#if BF_PLATFORM_WINDOWS namespace System.IO { class FolderBrowserDialog : CommonDialog { public enum FolderKind { Open, Save } String mSelectedPath = new String() ~ delete _; public bool ShowNewFolderButton; String mDescriptionText = new String() ~ delete _; bool mSelectedPathNeedsCheck; static FolderBrowserDialog sCurrentThis; FolderKind mFolderKind; public this(FolderKind kind = .Open) { mFolderKind = kind; Reset(); } public StringView SelectedPath { get { return mSelectedPath; } set { mSelectedPath.Set(value); } } public StringView Description { get { return mDescriptionText; } set { mDescriptionText.Set(value); } } public void Reset() { mSelectedPath.Clear(); mDescriptionText.Clear(); mSelectedPathNeedsCheck = false; ShowNewFolderButton = true; } protected Result RunDialog_New(Windows.HWnd hWndOwner, FolderBrowserDialog.COM_IFileDialog* fileDialog) { //COM_IFileDialogEvents evts; /*COM_IFileDialogEvents.VTable funcs; funcs.QueryInterface = (self, riid, result) => { return .E_FAIL; }; funcs.AddRef = (self) => { return .OK; }; funcs.Release = (self) => { return .OK; }; funcs.OnFileOk = (self, fileDialog) => { return .OK; }; funcs.OnFolderChanging = (self, fileDialog, folder) => { return .OK; }; funcs.OnFolderChange = (self, fileDialog) => { return .OK; }; funcs.OnSelectionChange = (self, fileDialog) => { return .OK; }; funcs.OnShareViolation = (self, fileDialog, result) => { return .OK; }; funcs.OnTypeChange = (self, fileDialog) => { return .OK; }; funcs.OnOverwrite = (self, fileDialog, item, result) => { return .OK; }; evts.[Friend]mVT = &funcs; fileDialog.VT.Advise(fileDialog, &evts, var adviseCookie);*/ if (!mSelectedPath.IsEmpty) { COM_IShellItem* folderShellItem = null; Windows.SHCreateItemFromParsingName(mSelectedPath.ToScopedNativeWChar!(), null, COM_IShellItem.sIID, (void**)&folderShellItem); if (folderShellItem != null) { fileDialog.VT.SetDefaultFolder(fileDialog, folderShellItem); folderShellItem.VT.Release(folderShellItem); } } fileDialog.VT.SetOptions(fileDialog, .PICKFOLDERS); fileDialog.VT.Show(fileDialog, hWndOwner); DialogResult result = .Cancel; mSelectedPath.Clear(); COM_IShellItem* shellItem = null; fileDialog.VT.GetResult(fileDialog, out shellItem); if (shellItem != null) { char16* cStr = null; if (shellItem.VT.GetDisplayName(shellItem, .FILESYSPATH, out cStr) == .OK) { let str = scope String..Append(cStr); mSelectedPath.Append(str); Windows.COM_IUnknown.CoTaskMemFree(cStr); result = .OK; } shellItem.VT.Release(shellItem); } fileDialog.VT.Release(fileDialog); return .Ok(result); } protected override Result RunDialog(Windows.HWnd hWndOwner) { FolderBrowserDialog.COM_IFileDialog* fileDialog = null; Windows.COM_IUnknown.HResult hr; //if (mFolderKind == .Open) hr = Windows.COM_IUnknown.CoCreateInstance(ref FolderBrowserDialog.COM_IFileDialog.sCLSID, null, .INPROC_SERVER, ref FolderBrowserDialog.COM_IFileDialog.sIID, (void**)&fileDialog); //else //hr = Windows.COM_IUnknown.CoCreateInstance(ref FolderBrowserDialog.COM_FileSaveDialog.sCLSID, null, .INPROC_SERVER, ref FolderBrowserDialog.COM_FileSaveDialog.sIID, (void**)&fileDialog); if (hr == 0) return RunDialog_New(hWndOwner, fileDialog); int pidlRoot = 0; //Windows.SHGetSpecialFolderLocation(hWndOwner, (int32)mRootFolder, ref pidlRoot); if (pidlRoot == (int)0) { Windows.SHGetSpecialFolderLocation(hWndOwner, Windows.CSIDL_DESKTOP, ref pidlRoot); if (pidlRoot == (int)0) return .Err; } int32 mergedOptions = (int32)Windows.BrowseInfos.NewDialogStyle; if (!ShowNewFolderButton) mergedOptions |= (int32)Windows.BrowseInfos.HideNewFolderButton; String displayName = scope String(Windows.MAX_PATH); Windows.WndProc callback = => FolderBrowserDialog_BrowseCallbackProc; Windows.BrowseInfo bi; bi.mHWndOwner = hWndOwner; bi.mIdlRoot = pidlRoot; bi.mDisplayName = displayName; bi.mTitle = mDescriptionText; bi.mFlags = mergedOptions; bi.mCallback = callback; bi.mLParam = 0; bi.mImage = 0; sCurrentThis = this; int pidlRet = Windows.SHBrowseForFolder(ref bi); sCurrentThis = null; if (pidlRet == (int)0) return DialogResult.Cancel; char8* selectedPathCStr = scope char8[Windows.MAX_PATH]*; Windows.SHGetPathFromIDList(pidlRet, selectedPathCStr); mSelectedPath.Clear(); mSelectedPath.Append(selectedPathCStr); return DialogResult.OK; } public static int FolderBrowserDialog_BrowseCallbackProc(Windows.HWnd hWnd, int32 msg, int wParam, int lParam) { switch (msg) { case Windows.BFFM_INITIALIZED: // Indicates the browse dialog box has finished initializing. The lpData value is zero. if (sCurrentThis.mSelectedPath.Length != 0) { // Try to select the folder specified by selectedPath Windows.SendMessageW(hWnd, Windows.BFFM_SETSELECTIONA, 1, (int)(void*)sCurrentThis.mSelectedPath.ToScopedNativeWChar!()); } break; case Windows.BFFM_SELCHANGED: // Indicates the selection has changed. The lpData parameter points to the item identifier list for the newly selected item. int selectedPidl = lParam; if (selectedPidl != (int)0) { char8* pszSelectedPath = scope char8[Windows.MAX_PATH]*; // Try to retrieve the path from the IDList bool isFileSystemFolder = Windows.SHGetPathFromIDList(selectedPidl, pszSelectedPath); Windows.SendMessageW(hWnd, Windows.BFFM_ENABLEOK, 0, (int)(isFileSystemFolder ? 1 : 0)); } break; } return 0; } struct FDE_SHAREVIOLATION_RESPONSE; struct FDE_OVERWRITE_RESPONSE; struct COM_IFileDialogEvents : Windows.COM_IUnknown { public struct VTable : Windows.COM_IUnknown.VTable { public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnFileOk; public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog, COM_IShellItem* psiFolder) OnFolderChanging; public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnFolderChange; public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnSelectionChange; public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog, FDE_SHAREVIOLATION_RESPONSE* pResponse) OnShareViolation; public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnTypeChange; public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog, COM_IShellItem* shellItem, FDE_OVERWRITE_RESPONSE* response) OnOverwrite; } } struct COM_IShellItem : Windows.COM_IUnknown { public static Guid sIID = .(0x43826d1e, 0xe718, 0x42ee, 0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe); public enum SIGDN : uint32 { NORMALDISPLAY = 0x00000000, // SHGDN_NORMAL PARENTRELATIVEPARSING = 0x80018001, // SHGDN_INFOLDER | SHGDN_FORPARSING DESKTOPABSOLUTEPARSING = 0x80028000, // SHGDN_FORPARSING PARENTRELATIVEEDITING = 0x80031001, // SHGDN_INFOLDER | SHGDN_FOREDITING DESKTOPABSOLUTEEDITING = 0x8004c000, // SHGDN_FORPARSING | SHGDN_FORADDRESSBAR FILESYSPATH = 0x80058000, // SHGDN_FORPARSING URL = 0x80068000, // SHGDN_FORPARSING PARENTRELATIVEFORADDRESSBAR = 0x8007c001, // SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR PARENTRELATIVE = 0x80080001 // SHGDN_INFOLDER } public struct VTable : Windows.COM_IUnknown.VTable { public function HResult(COM_IShellItem* self, void* pbc, ref Guid bhid, ref Guid riid, void** ppv) BindToHandler; public function HResult(COM_IShellItem* self, out COM_IShellItem* ppsi) GetParent; public function HResult(COM_IShellItem* self, SIGDN sigdnName, out char16* ppszName) GetDisplayName; public function HResult(COM_IShellItem* self, uint sfgaoMask, out uint psfgaoAttribs) GetAttributes; public function HResult(COM_IShellItem* self, COM_IShellItem* psi, uint32 hint, out int32 piOrder) Compare; } public new VTable* VT { get { return (.)mVT; } } } struct COMDLG_FILTERSPEC { public char16* pszName; public char16* pszSpec; } enum FDAP : uint32 { FDAP_BOTTOM = 0x00000000, FDAP_TOP = 0x00000001, } public struct COM_IFileDialog : Windows.COM_IUnknown { public static Guid sIID = .(0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, 0x5d, 0x13, 0x5f, 0xc8); public static Guid sCLSID = .(0xdc1c5a9c, 0xe88a, 0x4dde, 0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7); ///s public enum FOS : uint32 { OVERWRITEPROMPT = 0x00000002, STRICTFILETYPES = 0x00000004, NOCHANGEDIR = 0x00000008, PICKFOLDERS = 0x00000020, FORCEFILESYSTEM = 0x00000040, ALLNONSTORAGEITEMS = 0x00000080, NOVALIDATE = 0x00000100, ALLOWMULTISELECT = 0x00000200, PATHMUSTEXIST = 0x00000800, FILEMUSTEXIST = 0x00001000, CREATEPROMPT = 0x00002000, SHAREAWARE = 0x00004000, NOREADONLYRETURN = 0x00008000, NOTESTFILECREATE = 0x00010000, HIDEMRUPLACES = 0x00020000, HIDEPINNEDPLACES = 0x00040000, NODEREFERENCELINKS = 0x00100000, DONTADDTORECENT = 0x02000000, FORCESHOWHIDDEN = 0x10000000, DEFAULTNOMINIMODE = 0x20000000 } public struct VTable : Windows.COM_IUnknown.VTable { public function HResult(COM_IFileDialog* self, Windows.HWnd parent) Show; public function HResult(COM_IFileDialog* self, uint cFileTypes, COMDLG_FILTERSPEC* rgFilterSpec) SetFileTypes; public function HResult(COM_IFileDialog* self, uint iFileType) SetFileTypeIndex; public function HResult(COM_IFileDialog* self, out uint piFileType) GetFileTypeIndex; public function HResult(COM_IFileDialog* self, COM_IFileDialogEvents* pfde, out uint pdwCookie) Advise; public function HResult(COM_IFileDialog* self, uint dwCookie) Unadvise; public function HResult(COM_IFileDialog* self, FOS fos) SetOptions; public function HResult(COM_IFileDialog* self, out FOS pfos) GetOptions; public function HResult(COM_IFileDialog* self, COM_IShellItem* psi) SetDefaultFolder; public function HResult(COM_IFileDialog* self, COM_IShellItem* psi) SetFolder; public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetFolder; public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetCurrentSelection; public function HResult(COM_IFileDialog* self, char16* pszName) SetFileName; public function HResult(COM_IFileDialog* self, out char16* pszName) GetFileName; public function HResult(COM_IFileDialog* self, char16* pszTitle) SetTitle; public function HResult(COM_IFileDialog* self, char16* pszText) SetOkButtonLabel; public function HResult(COM_IFileDialog* self, char16* pszLabel) SetFileNameLabel; public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetResult; public function HResult(COM_IFileDialog* self, COM_IShellItem* psi, FDAP fdap) AddPlace; public function HResult(COM_IFileDialog* self, char16* pszDefaultExtension) SetDefaultExtension; public function HResult(COM_IFileDialog* self, int hr) Close; public function HResult(COM_IFileDialog* self, ref Guid guid) SetClientGuid; public function HResult(COM_IFileDialog* self) ClearClientData; public function HResult(COM_IFileDialog* self, void* pFilter) SetFilter; } public new VTable* VT { get { return (.)mVT; } } } public struct COM_FileSaveDialog : COM_IFileDialog { public static new Guid sIID = .(0x84bccd23, 0x5fde, 0x4cdb, 0xae, 0xa4, 0xaf, 0x64, 0xb8, 0x3d, 0x78, 0xab); public static new Guid sCLSID = .(0xC0B4E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B); } } } #endif