1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 module dfl.folderdialog; 5 6 import std.utf: toUTFz, toUTF8; 7 import core.sys.windows.windows; 8 import core.sys.windows.shlobj; // Fot ITEMIDLIST 9 import core.sys.windows.objidl; // Fot IMALLC 10 11 import dfl.application; 12 import dfl.base; 13 import dfl.commondialog; 14 import dfl.exception; 15 import dfl.internal.clib; 16 import dfl.internal.dlib; 17 import dfl.internal.utf; 18 19 private extern (Windows) nothrow { 20 alias SHBrowseForFolderWProc = LPITEMIDLIST function(LPBROWSEINFOW lpbi); 21 alias SHGetPathFromIDListWProc = BOOL function(LPCITEMIDLIST pidl, LPWSTR pszPath); 22 } 23 24 class FolderBrowserDialog : CommonDialog { 25 this() { 26 // Flag BIF_NEWDIALOGSTYLE requires OleInitialize(). 27 //OleInitialize(null); 28 29 Application.ppin(cast(void*) this); 30 31 bi.ulFlags = INIT_FLAGS; 32 bi.lParam = cast(typeof(bi.lParam)) cast(void*) this; 33 bi.lpfn = &fbdHookProc; 34 } 35 36 ~this() { 37 //OleUninitialize(); 38 } 39 40 override DialogResult showDialog() { 41 if (!runDialog(GetActiveWindow())) { 42 return DialogResult.CANCEL; 43 } 44 return DialogResult.OK; 45 } 46 47 override DialogResult showDialog(IWindow owner) { 48 if (!runDialog(owner ? owner.handle : GetActiveWindow())) { 49 return DialogResult.CANCEL; 50 } 51 return DialogResult.OK; 52 } 53 54 override void reset() { 55 bi.ulFlags = INIT_FLAGS; 56 _desc = null; 57 _selpath = null; 58 } 59 60 final @property void description(Dstring desc) { 61 // lpszTitle 62 _desc = desc; 63 } 64 65 final @property Dstring description() { 66 return _desc; 67 } 68 69 final @property void selectedPath(Dstring selpath) { 70 // pszDisplayName 71 _selpath = selpath; 72 } 73 74 final @property Dstring selectedPath() { 75 return _selpath; 76 } 77 78 // FIX: 79 /+ 80 // Currently only works for shell32.dll version 6.0+. 81 final @property void showNewFolderButton(bool byes) { 82 // BIF_NONEWFOLDERBUTTON exists with shell 6.0+. 83 // Might need to enum child windows looking for window title 84 // "&New Folder" and hide it, then shift "OK" and "Cancel" over. 85 86 if (byes) { 87 bi.ulFlags &= ~BIF_NONEWFOLDERBUTTON; 88 } else { 89 bi.ulFlags |= BIF_NONEWFOLDERBUTTON; 90 } 91 } 92 // 93 final @property bool showNewFolderButton() { 94 return (bi.ulFlags & BIF_NONEWFOLDERBUTTON) == 0; 95 } 96 +/ 97 98 // Currently only works for shell32.dll version 6.0+. 99 final @property void showNewStyleDialog(bool byes) { 100 // BIF_NONEWFOLDERBUTTON exists with shell 6.0+. 101 // Might need to enum child windows looking for window title 102 // "&New Folder" and hide it, then shift "OK" and "Cancel" over. 103 104 if (byes) { 105 bi.ulFlags |= BIF_NEWDIALOGSTYLE; 106 } else { 107 bi.ulFlags &= ~BIF_NEWDIALOGSTYLE; 108 } 109 } 110 111 final @property bool showNewStyleDialog() { 112 return (bi.ulFlags & BIF_NEWDIALOGSTYLE) != 0; 113 } 114 115 // Currently only works for shell32.dll version 6.0+. 116 final @property void showTextBox(bool byes) { 117 // BIF_NONEWFOLDERBUTTON exists with shell 6.0+. 118 // Might need to enum child windows looking for window title 119 // "&New Folder" and hide it, then shift "OK" and "Cancel" over. 120 121 if (byes) { 122 bi.ulFlags |= BIF_EDITBOX; 123 } else { 124 bi.ulFlags &= ~BIF_EDITBOX; 125 } 126 } 127 128 final @property bool showTextBox() { 129 return (bi.ulFlags & BIF_EDITBOX) != 0; 130 } 131 132 private void _errPathTooLong() { 133 throw new DflException("Path name is too long"); 134 } 135 136 private void _errNoGetPath() { 137 throw new DflException("Unable to obtain path"); 138 } 139 140 private void _errNoShMalloc() { 141 throw new DflException("Unable to get shell memory allocator"); 142 } 143 144 protected override bool runDialog(HWND owner) { 145 wchar[MAX_PATH + 1] buffer; 146 buffer[] = '\0'; 147 BROWSEINFOW dlgStruct; 148 dlgStruct.hwndOwner = owner; 149 dlgStruct.pszDisplayName = buffer.ptr; 150 dlgStruct.ulFlags = BIF_RETURNONLYFSDIRS; 151 dlgStruct.lpszTitle = toUTFz!(wchar*)(_desc); 152 153 ITEMIDLIST* pidl = SHBrowseForFolderW(&dlgStruct); 154 155 if (pidl) { 156 SHGetPathFromIDListW(pidl, buffer.ptr); //Get Full Path. 157 this.selectedPath = toUTF8(buffer); 158 return true; 159 } 160 return false; 161 } 162 163 protected: 164 165 /+ 166 override LRESULT hookProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 167 switch(msg) { 168 case WM_NOTIFY: { 169 NMHDR* nmhdr; 170 nmhdr = cast(NMHDR*)lparam; 171 switch(nmhdr.code) { 172 /+ 173 case CDN_FILEOK: 174 break; 175 +/ 176 177 default: 178 } 179 } 180 break; 181 182 default: 183 } 184 185 return super.hookProc(hwnd, msg, wparam, lparam); 186 } 187 +/ 188 189 private: 190 191 union { 192 BROWSEINFOW biw; 193 BROWSEINFOA bia; 194 alias bi = biw; 195 196 static assert(BROWSEINFOW.sizeof == BROWSEINFOA.sizeof); 197 static assert(BROWSEINFOW.ulFlags.offsetof == BROWSEINFOA.ulFlags.offsetof); 198 } 199 200 Dstring _desc; 201 Dstring _selpath; 202 203 enum UINT INIT_FLAGS = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; 204 } 205 206 private: 207 208 // FIX: 209 private /*extern (Windows)*/ int fbdHookProc(HWND hwnd, UINT msg, LPARAM lparam, LPARAM lpData) { 210 FolderBrowserDialog fd; 211 int result = 0; 212 213 try { 214 fd = cast(FolderBrowserDialog) cast(void*) lpData; 215 if (fd) { 216 Dstring s; 217 switch (msg) { 218 case BFFM_INITIALIZED: 219 s = fd.selectedPath; 220 if (s.length) { 221 if (dfl.internal.utf.useUnicode) { 222 SendMessageA(hwnd, BFFM_SETSELECTIONW, TRUE, 223 cast(LPARAM) dfl.internal.utf.toUnicodez(s)); 224 } else { 225 SendMessageA(hwnd, BFFM_SETSELECTIONA, TRUE, 226 cast(LPARAM) dfl.internal.utf.toAnsiz(s)); 227 } 228 } 229 break; 230 231 default: 232 } 233 } 234 } 235 catch (DThrowable e) { 236 Application.onThreadException(e); 237 } 238 239 return result; 240 }