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.

589 lines
19KB

  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. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  39. UNICODE_FUNCTION (SHBrowseForFolderW, LPITEMIDLIST, (LPBROWSEINFOW))
  40. UNICODE_FUNCTION (SHGetPathFromIDListW, BOOL, (LPCITEMIDLIST, LPWSTR))
  41. UNICODE_FUNCTION (GetSaveFileNameW, BOOL, (LPOPENFILENAMEW))
  42. UNICODE_FUNCTION (GetOpenFileNameW, BOOL, (LPOPENFILENAMEW))
  43. static void juce_initialiseUnicodeFileBrowserFunctions()
  44. {
  45. static bool initialised = false;
  46. if (! initialised)
  47. {
  48. initialised = true;
  49. HMODULE h = LoadLibraryA ("shell32.dll");
  50. UNICODE_FUNCTION_LOAD (SHBrowseForFolderW)
  51. UNICODE_FUNCTION_LOAD (SHGetPathFromIDListW)
  52. h = LoadLibraryA ("comdlg32.dll");
  53. UNICODE_FUNCTION_LOAD (GetSaveFileNameW)
  54. UNICODE_FUNCTION_LOAD (GetOpenFileNameW)
  55. }
  56. }
  57. #endif
  58. //==============================================================================
  59. static const void* defaultDirPath = 0;
  60. static String returnedString; // need this to get non-existent pathnames from the directory chooser
  61. static Component* currentExtraFileWin = 0;
  62. static bool areThereAnyAlwaysOnTopWindows()
  63. {
  64. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  65. {
  66. Component* c = Desktop::getInstance().getComponent (i);
  67. if (c != 0 && c->isAlwaysOnTop() && c->isShowing())
  68. return true;
  69. }
  70. return false;
  71. }
  72. static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPARAM /*lpData*/)
  73. {
  74. if (msg == BFFM_INITIALIZED)
  75. {
  76. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  77. SendMessage (hWnd, (wSHBrowseForFolderW != 0) ? BFFM_SETSELECTIONW
  78. : BFFM_SETSELECTIONA,
  79. TRUE, (LPARAM) defaultDirPath);
  80. #else
  81. SendMessage (hWnd, BFFM_SETSELECTIONW, TRUE, (LPARAM) defaultDirPath);
  82. #endif
  83. }
  84. else if (msg == BFFM_VALIDATEFAILEDW)
  85. {
  86. returnedString = (LPCWSTR) lParam;
  87. }
  88. else if (msg == BFFM_VALIDATEFAILEDA)
  89. {
  90. returnedString = (const char*) lParam;
  91. }
  92. return 0;
  93. }
  94. void juce_setWindowStyleBit (HWND h, int styleType, int feature, bool bitIsSet);
  95. static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam)
  96. {
  97. if (currentExtraFileWin != 0)
  98. {
  99. if (uiMsg == WM_INITDIALOG)
  100. {
  101. HWND dialogH = GetParent (hdlg);
  102. jassert (dialogH != 0);
  103. if (dialogH == 0)
  104. dialogH = hdlg;
  105. RECT r, cr;
  106. GetWindowRect (dialogH, &r);
  107. GetClientRect (dialogH, &cr);
  108. SetWindowPos (dialogH, 0,
  109. r.left, r.top,
  110. currentExtraFileWin->getWidth() + jmax (150, r.right - r.left),
  111. jmax (150, r.bottom - r.top),
  112. SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  113. currentExtraFileWin->setBounds (cr.right, cr.top, currentExtraFileWin->getWidth(), cr.bottom - cr.top);
  114. currentExtraFileWin->getChildComponent(0)->setBounds (0, 0, currentExtraFileWin->getWidth(), currentExtraFileWin->getHeight());
  115. SetParent ((HWND) currentExtraFileWin->getWindowHandle(), (HWND) dialogH);
  116. juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_CHILD, (dialogH != 0));
  117. juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_POPUP, (dialogH == 0));
  118. }
  119. else if (uiMsg == WM_NOTIFY)
  120. {
  121. LPOFNOTIFY ofn = (LPOFNOTIFY) lParam;
  122. if (ofn->hdr.code == CDN_SELCHANGE)
  123. {
  124. FilePreviewComponent* comp = (FilePreviewComponent*) currentExtraFileWin->getChildComponent(0);
  125. if (comp != 0)
  126. {
  127. TCHAR path [MAX_PATH * 2];
  128. path[0] = 0;
  129. CommDlg_OpenSave_GetFilePath (GetParent (hdlg), (LPARAM) &path, MAX_PATH);
  130. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  131. String fn;
  132. if (wGetOpenFileNameW != 0)
  133. fn = (const WCHAR*) path;
  134. else
  135. fn = path;
  136. #else
  137. const String fn ((const WCHAR*) path);
  138. #endif
  139. comp->selectedFileChanged (File (fn));
  140. }
  141. }
  142. }
  143. }
  144. return 0;
  145. }
  146. class FPComponentHolder : public Component
  147. {
  148. public:
  149. FPComponentHolder()
  150. {
  151. setVisible (true);
  152. setOpaque (true);
  153. }
  154. ~FPComponentHolder()
  155. {
  156. }
  157. void paint (Graphics& g)
  158. {
  159. g.fillAll (Colours::lightgrey);
  160. }
  161. private:
  162. FPComponentHolder (const FPComponentHolder&);
  163. const FPComponentHolder& operator= (const FPComponentHolder&);
  164. };
  165. //==============================================================================
  166. void FileChooser::showPlatformDialog (OwnedArray<File>& results,
  167. const String& title,
  168. const File& currentFileOrDirectory,
  169. const String& filter,
  170. bool selectsDirectory,
  171. bool isSaveDialogue,
  172. bool warnAboutOverwritingExistingFiles,
  173. bool selectMultipleFiles,
  174. FilePreviewComponent* extraInfoComponent)
  175. {
  176. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  177. juce_initialiseUnicodeFileBrowserFunctions();
  178. #endif
  179. const int numCharsAvailable = 32768;
  180. MemoryBlock filenameSpace ((numCharsAvailable + 1) * sizeof (WCHAR), true);
  181. WCHAR* const fname = (WCHAR*) filenameSpace.getData();
  182. int fnameIdx = 0;
  183. JUCE_TRY
  184. {
  185. // use a modal window as the parent for this dialog box
  186. // to block input from other app windows
  187. const Rectangle mainMon (Desktop::getInstance().getMainMonitorArea());
  188. Component w (String::empty);
  189. w.setBounds (mainMon.getX() + mainMon.getWidth() / 4,
  190. mainMon.getY() + mainMon.getHeight() / 4,
  191. 0, 0);
  192. w.setOpaque (true);
  193. w.setAlwaysOnTop (areThereAnyAlwaysOnTopWindows());
  194. w.addToDesktop (0);
  195. if (extraInfoComponent == 0)
  196. w.enterModalState();
  197. String initialDir;
  198. if (currentFileOrDirectory.isDirectory())
  199. {
  200. initialDir = currentFileOrDirectory.getFullPathName();
  201. }
  202. else
  203. {
  204. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  205. if (wSHBrowseForFolderW != 0)
  206. currentFileOrDirectory.getFileName().copyToBuffer (fname, numCharsAvailable);
  207. else
  208. currentFileOrDirectory.getFileName().copyToBuffer ((char*) fname, numCharsAvailable);
  209. #else
  210. currentFileOrDirectory.getFileName().copyToBuffer (fname, numCharsAvailable);
  211. #endif
  212. initialDir = currentFileOrDirectory.getParentDirectory().getFullPathName();
  213. }
  214. if (currentExtraFileWin->isValidComponent())
  215. {
  216. jassertfalse
  217. return;
  218. }
  219. if (selectsDirectory)
  220. {
  221. LPITEMIDLIST list = 0;
  222. filenameSpace.fillWith (0);
  223. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  224. if (wSHBrowseForFolderW != 0)
  225. {
  226. BROWSEINFOW bi;
  227. zerostruct (bi);
  228. bi.hwndOwner = (HWND) w.getWindowHandle();
  229. bi.pszDisplayName = fname;
  230. bi.lpszTitle = title;
  231. bi.lpfn = browseCallbackProc;
  232. #ifdef BIF_USENEWUI
  233. bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE;
  234. #else
  235. bi.ulFlags = 0x50;
  236. #endif
  237. defaultDirPath = (const WCHAR*) initialDir;
  238. list = wSHBrowseForFolderW (&bi);
  239. if (! wSHGetPathFromIDListW (list, fname))
  240. {
  241. fname[0] = 0;
  242. returnedString = String::empty;
  243. }
  244. }
  245. else
  246. {
  247. BROWSEINFO bi;
  248. zerostruct (bi);
  249. bi.hwndOwner = (HWND) w.getWindowHandle();
  250. bi.pszDisplayName = (TCHAR*) fname;
  251. bi.lpszTitle = title;
  252. bi.lpfn = browseCallbackProc;
  253. #ifdef BIF_USENEWUI
  254. bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE;
  255. #else
  256. bi.ulFlags = 0x50;
  257. #endif
  258. defaultDirPath = (const char*) initialDir;
  259. list = SHBrowseForFolder (&bi);
  260. if (! SHGetPathFromIDList (list, (char*) fname))
  261. {
  262. fname[0] = 0;
  263. returnedString = String::empty;
  264. }
  265. }
  266. #else
  267. {
  268. BROWSEINFOW bi;
  269. zerostruct (bi);
  270. bi.hwndOwner = (HWND) w.getWindowHandle();
  271. bi.pszDisplayName = fname;
  272. bi.lpszTitle = title;
  273. bi.lpfn = browseCallbackProc;
  274. #ifdef BIF_USENEWUI
  275. bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE;
  276. #else
  277. bi.ulFlags = 0x50;
  278. #endif
  279. defaultDirPath = (const WCHAR*) initialDir;
  280. list = SHBrowseForFolderW (&bi);
  281. if (! SHGetPathFromIDListW (list, fname))
  282. {
  283. fname[0] = 0;
  284. returnedString = String::empty;
  285. }
  286. }
  287. #endif
  288. LPMALLOC al;
  289. if (list != 0 && SUCCEEDED (SHGetMalloc (&al)))
  290. al->Free (list);
  291. defaultDirPath = 0;
  292. if (returnedString.isNotEmpty())
  293. {
  294. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  295. String stringFName;
  296. if (wSHBrowseForFolderW != 0)
  297. stringFName = fname;
  298. else
  299. stringFName = (char*) fname;
  300. #else
  301. const String stringFName (fname);
  302. #endif
  303. results.add (new File (File (stringFName).getSiblingFile (returnedString)));
  304. returnedString = String::empty;
  305. return;
  306. }
  307. }
  308. else
  309. {
  310. DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
  311. if (warnAboutOverwritingExistingFiles)
  312. flags |= OFN_OVERWRITEPROMPT;
  313. if (selectMultipleFiles)
  314. flags |= OFN_ALLOWMULTISELECT;
  315. if (extraInfoComponent != 0)
  316. {
  317. flags |= OFN_ENABLEHOOK;
  318. currentExtraFileWin = new FPComponentHolder();
  319. currentExtraFileWin->addAndMakeVisible (extraInfoComponent);
  320. currentExtraFileWin->setSize (jlimit (20, 800, extraInfoComponent->getWidth()),
  321. extraInfoComponent->getHeight());
  322. currentExtraFileWin->addToDesktop (0);
  323. currentExtraFileWin->enterModalState();
  324. }
  325. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  326. if (wGetSaveFileNameW != 0)
  327. {
  328. WCHAR filters [1024];
  329. zeromem (filters, sizeof (filters));
  330. filter.copyToBuffer (filters, 1024);
  331. filter.copyToBuffer (filters + filter.length() + 1,
  332. 1022 - filter.length());
  333. OPENFILENAMEW of;
  334. zerostruct (of);
  335. #ifdef OPENFILENAME_SIZE_VERSION_400W
  336. of.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
  337. #else
  338. of.lStructSize = sizeof (of);
  339. #endif
  340. of.hwndOwner = (HWND) w.getWindowHandle();
  341. of.lpstrFilter = filters;
  342. of.nFilterIndex = 1;
  343. of.lpstrFile = fname;
  344. of.nMaxFile = numCharsAvailable;
  345. of.lpstrInitialDir = initialDir;
  346. of.lpstrTitle = title;
  347. of.Flags = flags;
  348. if (extraInfoComponent != 0)
  349. of.lpfnHook = &openCallback;
  350. if (isSaveDialogue)
  351. {
  352. if (! wGetSaveFileNameW (&of))
  353. fname[0] = 0;
  354. else
  355. fnameIdx = of.nFileOffset;
  356. }
  357. else
  358. {
  359. if (! wGetOpenFileNameW (&of))
  360. fname[0] = 0;
  361. else
  362. fnameIdx = of.nFileOffset;
  363. }
  364. }
  365. else
  366. {
  367. TCHAR filters [1024];
  368. zeromem (filters, sizeof (filters));
  369. filter.copyToBuffer (filters, 1024);
  370. filter.copyToBuffer (filters + filter.length() + 1,
  371. 1022 - filter.length());
  372. OPENFILENAME of;
  373. zerostruct (of);
  374. #ifdef OPENFILENAME_SIZE_VERSION_400
  375. of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
  376. #else
  377. of.lStructSize = sizeof (of);
  378. #endif
  379. of.hwndOwner = (HWND) w.getWindowHandle();
  380. of.lpstrFilter = filters;
  381. of.nFilterIndex = 1;
  382. of.lpstrFile = (TCHAR*) fname;
  383. of.nMaxFile = numCharsAvailable;
  384. of.lpstrInitialDir = initialDir;
  385. of.lpstrTitle = title;
  386. of.Flags = flags;
  387. if (extraInfoComponent != 0)
  388. of.lpfnHook = &openCallback;
  389. if (isSaveDialogue)
  390. {
  391. if (! GetSaveFileName (&of))
  392. fname[0] = 0;
  393. else
  394. fnameIdx = of.nFileOffset;
  395. }
  396. else
  397. {
  398. if (! GetOpenFileName (&of))
  399. fname[0] = 0;
  400. else
  401. fnameIdx = of.nFileOffset;
  402. }
  403. }
  404. #else
  405. {
  406. WCHAR filters [1024];
  407. zeromem (filters, sizeof (filters));
  408. filter.copyToBuffer (filters, 1024);
  409. filter.copyToBuffer (filters + filter.length() + 1,
  410. 1022 - filter.length());
  411. OPENFILENAMEW of;
  412. zerostruct (of);
  413. #ifdef OPENFILENAME_SIZE_VERSION_400W
  414. of.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
  415. #else
  416. of.lStructSize = sizeof (of);
  417. #endif
  418. of.hwndOwner = (HWND) w.getWindowHandle();
  419. of.lpstrFilter = filters;
  420. of.nFilterIndex = 1;
  421. of.lpstrFile = fname;
  422. of.nMaxFile = numCharsAvailable;
  423. of.lpstrInitialDir = initialDir;
  424. of.lpstrTitle = title;
  425. of.Flags = flags;
  426. if (extraInfoComponent != 0)
  427. of.lpfnHook = &openCallback;
  428. if (isSaveDialogue)
  429. {
  430. if (! GetSaveFileNameW (&of))
  431. fname[0] = 0;
  432. else
  433. fnameIdx = of.nFileOffset;
  434. }
  435. else
  436. {
  437. if (! GetOpenFileNameW (&of))
  438. fname[0] = 0;
  439. else
  440. fnameIdx = of.nFileOffset;
  441. }
  442. }
  443. #endif
  444. }
  445. }
  446. #if JUCE_CATCH_UNHANDLED_EXCEPTIONS
  447. catch (...)
  448. {
  449. fname[0] = 0;
  450. }
  451. #endif
  452. deleteAndZero (currentExtraFileWin);
  453. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  454. if (wGetSaveFileNameW != 0)
  455. {
  456. #endif
  457. const WCHAR* const files = fname;
  458. if (selectMultipleFiles && fnameIdx > 0 && files [fnameIdx - 1] == 0)
  459. {
  460. const WCHAR* filename = files + fnameIdx;
  461. while (*filename != 0)
  462. {
  463. const String filepath (String (files) + T("\\") + String (filename));
  464. results.add (new File (filepath));
  465. filename += CharacterFunctions::length (filename) + 1;
  466. }
  467. }
  468. else if (files[0] != 0)
  469. {
  470. results.add (new File (files));
  471. }
  472. #if JUCE_ENABLE_WIN98_COMPATIBILITY
  473. }
  474. else
  475. {
  476. const char* const files = (const char*) fname;
  477. if (selectMultipleFiles && fnameIdx > 0 && files [fnameIdx - 1] == 0)
  478. {
  479. const char* filename = files + fnameIdx;
  480. while (*filename != 0)
  481. {
  482. const String filepath (String (files) + T("\\") + String (filename));
  483. results.add (new File (filepath));
  484. filename += CharacterFunctions::length (filename) + 1;
  485. }
  486. }
  487. else if (files[0] != 0)
  488. {
  489. results.add (new File (files));
  490. }
  491. }
  492. #endif
  493. }
  494. END_JUCE_NAMESPACE