The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

493 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #ifdef _MSC_VER
  24. #pragma warning (disable: 4514)
  25. #pragma warning (push)
  26. #endif
  27. #include "win32_headers.h"
  28. #include <shlobj.h>
  29. #include "../../../src/juce_core/basics/juce_StandardHeader.h"
  30. BEGIN_JUCE_NAMESPACE
  31. #include "../../../src/juce_appframework/gui/components/filebrowser/juce_FileChooser.h"
  32. #include "../../../src/juce_appframework/gui/components/juce_Desktop.h"
  33. #include "../../../src/juce_core/basics/juce_SystemStats.h"
  34. #ifdef _MSC_VER
  35. #pragma warning (pop)
  36. #endif
  37. //==============================================================================
  38. UNICODE_FUNCTION (SHBrowseForFolderW, LPITEMIDLIST, (LPBROWSEINFOW))
  39. UNICODE_FUNCTION (SHGetPathFromIDListW, BOOL, (LPCITEMIDLIST, LPWSTR))
  40. UNICODE_FUNCTION (GetSaveFileNameW, BOOL, (LPOPENFILENAMEW))
  41. UNICODE_FUNCTION (GetOpenFileNameW, BOOL, (LPOPENFILENAMEW))
  42. static void juce_initialiseUnicodeFileBrowserFunctions()
  43. {
  44. static bool initialised = false;
  45. if (! initialised)
  46. {
  47. initialised = true;
  48. HMODULE h = LoadLibraryA ("shell32.dll");
  49. UNICODE_FUNCTION_LOAD (SHBrowseForFolderW)
  50. UNICODE_FUNCTION_LOAD (SHGetPathFromIDListW)
  51. h = LoadLibraryA ("comdlg32.dll");
  52. UNICODE_FUNCTION_LOAD (GetSaveFileNameW)
  53. UNICODE_FUNCTION_LOAD (GetOpenFileNameW)
  54. }
  55. }
  56. //==============================================================================
  57. static const void* defaultDirPath = 0;
  58. static String returnedString; // need this to get non-existent pathnames from the directory chooser
  59. static Component* currentExtraFileWin = 0;
  60. static bool areThereAnyAlwaysOnTopWindows()
  61. {
  62. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  63. {
  64. Component* c = Desktop::getInstance().getComponent (i);
  65. if (c != 0 && c->isAlwaysOnTop() && c->isShowing())
  66. return true;
  67. }
  68. return false;
  69. }
  70. static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPARAM /*lpData*/)
  71. {
  72. if (msg == BFFM_INITIALIZED)
  73. {
  74. SendMessage (hWnd, (wSHBrowseForFolderW != 0) ? BFFM_SETSELECTIONW
  75. : BFFM_SETSELECTIONA,
  76. TRUE, (LPARAM) defaultDirPath);
  77. }
  78. else if (msg == BFFM_VALIDATEFAILEDW)
  79. {
  80. returnedString = (LPCWSTR) lParam;
  81. }
  82. else if (msg == BFFM_VALIDATEFAILEDA)
  83. {
  84. returnedString = (const char*) lParam;
  85. }
  86. return 0;
  87. }
  88. void juce_setWindowStyleBit (HWND h, int styleType, int feature, bool bitIsSet);
  89. static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam)
  90. {
  91. if (currentExtraFileWin != 0)
  92. {
  93. if (uiMsg == WM_INITDIALOG)
  94. {
  95. HWND dialogH = GetParent (hdlg);
  96. jassert (dialogH != 0);
  97. if (dialogH == 0)
  98. dialogH = hdlg;
  99. RECT r, cr;
  100. GetWindowRect (dialogH, &r);
  101. GetClientRect (dialogH, &cr);
  102. SetWindowPos (dialogH, 0,
  103. r.left, r.top,
  104. currentExtraFileWin->getWidth() + jmax (150, r.right - r.left),
  105. jmax (150, r.bottom - r.top),
  106. SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  107. currentExtraFileWin->setBounds (cr.right, cr.top, currentExtraFileWin->getWidth(), cr.bottom - cr.top);
  108. currentExtraFileWin->getChildComponent(0)->setBounds (0, 0, currentExtraFileWin->getWidth(), currentExtraFileWin->getHeight());
  109. SetParent ((HWND) currentExtraFileWin->getWindowHandle(), (HWND) dialogH);
  110. juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_CHILD, (dialogH != 0));
  111. juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_POPUP, (dialogH == 0));
  112. }
  113. else if (uiMsg == WM_NOTIFY)
  114. {
  115. LPOFNOTIFY ofn = (LPOFNOTIFY) lParam;
  116. if (ofn->hdr.code == CDN_SELCHANGE)
  117. {
  118. FilePreviewComponent* comp = (FilePreviewComponent*) currentExtraFileWin->getChildComponent(0);
  119. if (comp != 0)
  120. {
  121. TCHAR path [MAX_PATH * 2];
  122. path[0] = 0;
  123. CommDlg_OpenSave_GetFilePath (GetParent (hdlg), (LPARAM) &path, MAX_PATH);
  124. String fn;
  125. if (wGetOpenFileNameW != 0)
  126. fn = (const WCHAR*) path;
  127. else
  128. fn = path;
  129. comp->selectedFileChanged (File (fn));
  130. }
  131. }
  132. }
  133. }
  134. return 0;
  135. }
  136. class FPComponentHolder : public Component
  137. {
  138. public:
  139. FPComponentHolder()
  140. {
  141. setVisible (true);
  142. setOpaque (true);
  143. }
  144. ~FPComponentHolder()
  145. {
  146. }
  147. void paint (Graphics& g)
  148. {
  149. g.fillAll (Colours::lightgrey);
  150. }
  151. private:
  152. FPComponentHolder (const FPComponentHolder&);
  153. const FPComponentHolder& operator= (const FPComponentHolder&);
  154. };
  155. //==============================================================================
  156. void FileChooser::showPlatformDialog (OwnedArray<File>& results,
  157. const String& title,
  158. const File& currentFileOrDirectory,
  159. const String& filter,
  160. bool selectsDirectory,
  161. bool isSaveDialogue,
  162. bool warnAboutOverwritingExistingFiles,
  163. bool selectMultipleFiles,
  164. FilePreviewComponent* extraInfoComponent)
  165. {
  166. juce_initialiseUnicodeFileBrowserFunctions();
  167. const int numCharsAvailable = 32768;
  168. MemoryBlock filenameSpace ((numCharsAvailable + 1) * sizeof (WCHAR), true);
  169. WCHAR* const fname = (WCHAR*) filenameSpace.getData();
  170. int fnameIdx = 0;
  171. JUCE_TRY
  172. {
  173. // use a modal window as the parent for this dialog box
  174. // to block input from other app windows
  175. const Rectangle mainMon (Desktop::getInstance().getMainMonitorArea());
  176. Component w (String::empty);
  177. w.setBounds (mainMon.getX() + mainMon.getWidth() / 4,
  178. mainMon.getY() + mainMon.getHeight() / 4,
  179. 0, 0);
  180. w.setOpaque (true);
  181. w.setAlwaysOnTop (areThereAnyAlwaysOnTopWindows());
  182. w.addToDesktop (0);
  183. if (extraInfoComponent == 0)
  184. w.enterModalState();
  185. String initialDir;
  186. if (currentFileOrDirectory.isDirectory())
  187. {
  188. initialDir = currentFileOrDirectory.getFullPathName();
  189. }
  190. else
  191. {
  192. if (wSHBrowseForFolderW != 0)
  193. currentFileOrDirectory.getFileName().copyToBuffer (fname, numCharsAvailable);
  194. else
  195. currentFileOrDirectory.getFileName().copyToBuffer ((char*) fname, numCharsAvailable);
  196. initialDir = currentFileOrDirectory.getParentDirectory().getFullPathName();
  197. }
  198. if (currentExtraFileWin->isValidComponent())
  199. {
  200. jassertfalse
  201. return;
  202. }
  203. if (selectsDirectory)
  204. {
  205. LPITEMIDLIST list = 0;
  206. filenameSpace.fillWith (0);
  207. if (wSHBrowseForFolderW != 0)
  208. {
  209. BROWSEINFOW bi;
  210. zerostruct (bi);
  211. bi.hwndOwner = (HWND) w.getWindowHandle();
  212. bi.pszDisplayName = fname;
  213. bi.lpszTitle = title;
  214. bi.lpfn = browseCallbackProc;
  215. #ifdef BIF_USENEWUI
  216. bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE;
  217. #else
  218. bi.ulFlags = 0x50;
  219. #endif
  220. defaultDirPath = (const WCHAR*) initialDir;
  221. list = wSHBrowseForFolderW (&bi);
  222. if (! wSHGetPathFromIDListW (list, fname))
  223. {
  224. fname[0] = 0;
  225. returnedString = String::empty;
  226. }
  227. }
  228. else
  229. {
  230. BROWSEINFO bi;
  231. zerostruct (bi);
  232. bi.hwndOwner = (HWND) w.getWindowHandle();
  233. bi.pszDisplayName = (TCHAR*) fname;
  234. bi.lpszTitle = title;
  235. bi.lpfn = browseCallbackProc;
  236. #ifdef BIF_USENEWUI
  237. bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE;
  238. #else
  239. bi.ulFlags = 0x50;
  240. #endif
  241. defaultDirPath = (const char*) initialDir;
  242. list = SHBrowseForFolder (&bi);
  243. if (! SHGetPathFromIDList (list, (char*) fname))
  244. {
  245. fname[0] = 0;
  246. returnedString = String::empty;
  247. }
  248. }
  249. LPMALLOC al;
  250. if (list != 0 && SUCCEEDED (SHGetMalloc (&al)))
  251. al->Free (list);
  252. defaultDirPath = 0;
  253. if (returnedString.isNotEmpty())
  254. {
  255. String stringFName;
  256. if (wSHBrowseForFolderW != 0)
  257. stringFName = fname;
  258. else
  259. stringFName = (char*) fname;
  260. results.add (new File (File (stringFName).getSiblingFile (returnedString)));
  261. returnedString = String::empty;
  262. return;
  263. }
  264. }
  265. else
  266. {
  267. DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
  268. if (warnAboutOverwritingExistingFiles)
  269. flags |= OFN_OVERWRITEPROMPT;
  270. if (selectMultipleFiles)
  271. flags |= OFN_ALLOWMULTISELECT;
  272. if (extraInfoComponent != 0)
  273. {
  274. flags |= OFN_ENABLEHOOK;
  275. currentExtraFileWin = new FPComponentHolder();
  276. currentExtraFileWin->addAndMakeVisible (extraInfoComponent);
  277. currentExtraFileWin->setSize (jlimit (20, 800, extraInfoComponent->getWidth()),
  278. extraInfoComponent->getHeight());
  279. currentExtraFileWin->addToDesktop (0);
  280. currentExtraFileWin->enterModalState();
  281. }
  282. if (wGetSaveFileNameW != 0)
  283. {
  284. WCHAR filters [1024];
  285. zeromem (filters, sizeof (filters));
  286. filter.copyToBuffer (filters, 1024);
  287. filter.copyToBuffer (filters + filter.length() + 1,
  288. 1022 - filter.length());
  289. OPENFILENAMEW of;
  290. zerostruct (of);
  291. #ifdef OPENFILENAME_SIZE_VERSION_400W
  292. of.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
  293. #else
  294. of.lStructSize = sizeof (of);
  295. #endif
  296. of.hwndOwner = (HWND) w.getWindowHandle();
  297. of.lpstrFilter = filters;
  298. of.nFilterIndex = 1;
  299. of.lpstrFile = fname;
  300. of.nMaxFile = numCharsAvailable;
  301. of.lpstrInitialDir = initialDir;
  302. of.lpstrTitle = title;
  303. of.Flags = flags;
  304. if (extraInfoComponent != 0)
  305. of.lpfnHook = &openCallback;
  306. if (isSaveDialogue)
  307. {
  308. if (! wGetSaveFileNameW (&of))
  309. fname[0] = 0;
  310. else
  311. fnameIdx = of.nFileOffset;
  312. }
  313. else
  314. {
  315. if (! wGetOpenFileNameW (&of))
  316. fname[0] = 0;
  317. else
  318. fnameIdx = of.nFileOffset;
  319. }
  320. }
  321. else
  322. {
  323. TCHAR filters [1024];
  324. zeromem (filters, sizeof (filters));
  325. filter.copyToBuffer (filters, 1024);
  326. filter.copyToBuffer (filters + filter.length() + 1,
  327. 1022 - filter.length());
  328. OPENFILENAME of;
  329. zerostruct (of);
  330. #ifdef OPENFILENAME_SIZE_VERSION_400
  331. of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
  332. #else
  333. of.lStructSize = sizeof (of);
  334. #endif
  335. of.hwndOwner = (HWND) w.getWindowHandle();
  336. of.lpstrFilter = filters;
  337. of.nFilterIndex = 1;
  338. of.lpstrFile = (TCHAR*) fname;
  339. of.nMaxFile = numCharsAvailable;
  340. of.lpstrInitialDir = initialDir;
  341. of.lpstrTitle = title;
  342. of.Flags = flags;
  343. if (extraInfoComponent != 0)
  344. of.lpfnHook = &openCallback;
  345. if (isSaveDialogue)
  346. {
  347. if (! GetSaveFileName (&of))
  348. fname[0] = 0;
  349. else
  350. fnameIdx = of.nFileOffset;
  351. }
  352. else
  353. {
  354. if (! GetOpenFileName (&of))
  355. fname[0] = 0;
  356. else
  357. fnameIdx = of.nFileOffset;
  358. }
  359. }
  360. }
  361. }
  362. #if JUCE_CATCH_UNHANDLED_EXCEPTIONS
  363. catch (...)
  364. {
  365. fname[0] = 0;
  366. }
  367. #endif
  368. deleteAndZero (currentExtraFileWin);
  369. if (wGetSaveFileNameW != 0)
  370. {
  371. const WCHAR* const files = fname;
  372. if (selectMultipleFiles && fnameIdx > 0 && files [fnameIdx - 1] == 0)
  373. {
  374. const WCHAR* filename = files + fnameIdx;
  375. while (*filename != 0)
  376. {
  377. const String filepath (String (files) + T("\\") + String (filename));
  378. results.add (new File (filepath));
  379. filename += CharacterFunctions::length (filename) + 1;
  380. }
  381. }
  382. else if (files[0] != 0)
  383. {
  384. results.add (new File (files));
  385. }
  386. }
  387. else
  388. {
  389. const char* const files = (const char*) fname;
  390. if (selectMultipleFiles && fnameIdx > 0 && files [fnameIdx - 1] == 0)
  391. {
  392. const char* filename = files + fnameIdx;
  393. while (*filename != 0)
  394. {
  395. const String filepath (String (files) + T("\\") + String (filename));
  396. results.add (new File (filepath));
  397. filename += CharacterFunctions::length (filename) + 1;
  398. }
  399. }
  400. else if (files[0] != 0)
  401. {
  402. results.add (new File (files));
  403. }
  404. }
  405. }
  406. END_JUCE_NAMESPACE