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.

281 lines
9.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. namespace FileChooserHelpers
  19. {
  20. static bool areThereAnyAlwaysOnTopWindows()
  21. {
  22. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  23. {
  24. Component* const c = Desktop::getInstance().getComponent (i);
  25. if (c != nullptr && c->isAlwaysOnTop() && c->isShowing())
  26. return true;
  27. }
  28. return false;
  29. }
  30. struct FileChooserCallbackInfo
  31. {
  32. String initialPath;
  33. String returnedString; // need this to get non-existent pathnames from the directory chooser
  34. ScopedPointer<Component> customComponent;
  35. };
  36. static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPARAM lpData)
  37. {
  38. FileChooserCallbackInfo* info = (FileChooserCallbackInfo*) lpData;
  39. if (msg == BFFM_INITIALIZED)
  40. SendMessage (hWnd, BFFM_SETSELECTIONW, TRUE, (LPARAM) info->initialPath.toWideCharPointer());
  41. else if (msg == BFFM_VALIDATEFAILEDW)
  42. info->returnedString = (LPCWSTR) lParam;
  43. else if (msg == BFFM_VALIDATEFAILEDA)
  44. info->returnedString = (const char*) lParam;
  45. return 0;
  46. }
  47. static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam)
  48. {
  49. if (uiMsg == WM_INITDIALOG)
  50. {
  51. Component* customComp = ((FileChooserCallbackInfo*) (((OPENFILENAMEW*) lParam)->lCustData))->customComponent;
  52. HWND dialogH = GetParent (hdlg);
  53. jassert (dialogH != 0);
  54. if (dialogH == 0)
  55. dialogH = hdlg;
  56. RECT r, cr;
  57. GetWindowRect (dialogH, &r);
  58. GetClientRect (dialogH, &cr);
  59. SetWindowPos (dialogH, 0,
  60. r.left, r.top,
  61. customComp->getWidth() + jmax (150, (int) (r.right - r.left)),
  62. jmax (150, (int) (r.bottom - r.top)),
  63. SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  64. customComp->setBounds (cr.right, cr.top, customComp->getWidth(), cr.bottom - cr.top);
  65. customComp->addToDesktop (0, dialogH);
  66. }
  67. else if (uiMsg == WM_NOTIFY)
  68. {
  69. LPOFNOTIFY ofn = (LPOFNOTIFY) lParam;
  70. if (ofn->hdr.code == CDN_SELCHANGE)
  71. {
  72. FileChooserCallbackInfo* info = (FileChooserCallbackInfo*) ofn->lpOFN->lCustData;
  73. FilePreviewComponent* comp = dynamic_cast<FilePreviewComponent*> (info->customComponent->getChildComponent(0));
  74. if (comp != nullptr)
  75. {
  76. WCHAR path [MAX_PATH * 2] = { 0 };
  77. CommDlg_OpenSave_GetFilePath (GetParent (hdlg), (LPARAM) &path, MAX_PATH);
  78. comp->selectedFileChanged (File (path));
  79. }
  80. }
  81. }
  82. return 0;
  83. }
  84. class CustomComponentHolder : public Component
  85. {
  86. public:
  87. CustomComponentHolder (Component* const customComp)
  88. {
  89. setVisible (true);
  90. setOpaque (true);
  91. addAndMakeVisible (customComp);
  92. setSize (jlimit (20, 800, customComp->getWidth()), customComp->getHeight());
  93. }
  94. void paint (Graphics& g)
  95. {
  96. g.fillAll (Colours::lightgrey);
  97. }
  98. void resized()
  99. {
  100. Component* const c = getChildComponent(0);
  101. if (c != nullptr)
  102. c->setBounds (getLocalBounds());
  103. }
  104. private:
  105. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomComponentHolder);
  106. };
  107. }
  108. //==============================================================================
  109. bool FileChooser::isPlatformDialogAvailable()
  110. {
  111. return true;
  112. }
  113. void FileChooser::showPlatformDialog (Array<File>& results, const String& title_, const File& currentFileOrDirectory,
  114. const String& filter, bool selectsDirectory, bool /*selectsFiles*/,
  115. bool isSaveDialogue, bool warnAboutOverwritingExistingFiles,
  116. bool selectMultipleFiles, FilePreviewComponent* extraInfoComponent)
  117. {
  118. using namespace FileChooserHelpers;
  119. const String title (title_);
  120. HeapBlock<WCHAR> files;
  121. const int charsAvailableForResult = 32768;
  122. files.calloc (charsAvailableForResult + 1);
  123. int filenameOffset = 0;
  124. FileChooserCallbackInfo info;
  125. // use a modal window as the parent for this dialog box
  126. // to block input from other app windows
  127. Component parentWindow (String::empty);
  128. const Rectangle<int> mainMon (Desktop::getInstance().getDisplays().getMainDisplay().userArea);
  129. parentWindow.setBounds (mainMon.getX() + mainMon.getWidth() / 4,
  130. mainMon.getY() + mainMon.getHeight() / 4,
  131. 0, 0);
  132. parentWindow.setOpaque (true);
  133. parentWindow.setAlwaysOnTop (areThereAnyAlwaysOnTopWindows());
  134. parentWindow.addToDesktop (0);
  135. if (extraInfoComponent == nullptr)
  136. parentWindow.enterModalState();
  137. if (currentFileOrDirectory.isDirectory())
  138. {
  139. info.initialPath = currentFileOrDirectory.getFullPathName();
  140. }
  141. else
  142. {
  143. currentFileOrDirectory.getFileName().copyToUTF16 (files, charsAvailableForResult * sizeof (WCHAR));
  144. info.initialPath = currentFileOrDirectory.getParentDirectory().getFullPathName();
  145. }
  146. if (selectsDirectory)
  147. {
  148. BROWSEINFO bi = { 0 };
  149. bi.hwndOwner = (HWND) parentWindow.getWindowHandle();
  150. bi.pszDisplayName = files;
  151. bi.lpszTitle = title.toWideCharPointer();
  152. bi.lParam = (LPARAM) &info;
  153. bi.lpfn = browseCallbackProc;
  154. #ifdef BIF_USENEWUI
  155. bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE;
  156. #else
  157. bi.ulFlags = 0x50;
  158. #endif
  159. LPITEMIDLIST list = SHBrowseForFolder (&bi);
  160. if (! SHGetPathFromIDListW (list, files))
  161. {
  162. files[0] = 0;
  163. info.returnedString = String::empty;
  164. }
  165. LPMALLOC al;
  166. if (list != 0 && SUCCEEDED (SHGetMalloc (&al)))
  167. al->Free (list);
  168. if (info.returnedString.isNotEmpty())
  169. {
  170. results.add (File (String (files)).getSiblingFile (info.returnedString));
  171. return;
  172. }
  173. }
  174. else
  175. {
  176. DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
  177. if (warnAboutOverwritingExistingFiles)
  178. flags |= OFN_OVERWRITEPROMPT;
  179. if (selectMultipleFiles)
  180. flags |= OFN_ALLOWMULTISELECT;
  181. if (extraInfoComponent != nullptr)
  182. {
  183. flags |= OFN_ENABLEHOOK;
  184. info.customComponent = new CustomComponentHolder (extraInfoComponent);
  185. info.customComponent->enterModalState();
  186. }
  187. const int filterSpaceNumChars = 2048;
  188. HeapBlock<WCHAR> filters;
  189. filters.calloc (filterSpaceNumChars);
  190. const int bytesWritten = filter.copyToUTF16 (filters.getData(), filterSpaceNumChars * sizeof (WCHAR));
  191. filter.copyToUTF16 (filters + (bytesWritten / sizeof (WCHAR)),
  192. (int) ((filterSpaceNumChars - 1) * sizeof (WCHAR) - bytesWritten));
  193. OPENFILENAMEW of = { 0 };
  194. String localPath (info.initialPath);
  195. #ifdef OPENFILENAME_SIZE_VERSION_400W
  196. of.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
  197. #else
  198. of.lStructSize = sizeof (of);
  199. #endif
  200. of.hwndOwner = (HWND) parentWindow.getWindowHandle();
  201. of.lpstrFilter = filters.getData();
  202. of.nFilterIndex = 1;
  203. of.lpstrFile = files;
  204. of.nMaxFile = charsAvailableForResult;
  205. of.lpstrInitialDir = localPath.toWideCharPointer();
  206. of.lpstrTitle = title.toWideCharPointer();
  207. of.Flags = flags;
  208. of.lCustData = (LPARAM) &info;
  209. if (extraInfoComponent != nullptr)
  210. of.lpfnHook = &openCallback;
  211. if (! (isSaveDialogue ? GetSaveFileName (&of)
  212. : GetOpenFileName (&of)))
  213. return;
  214. filenameOffset = of.nFileOffset;
  215. }
  216. if (selectMultipleFiles && filenameOffset > 0 && files [filenameOffset - 1] == 0)
  217. {
  218. const WCHAR* filename = files + filenameOffset;
  219. while (*filename != 0)
  220. {
  221. results.add (File (String (files) + "\\" + String (filename)));
  222. filename += wcslen (filename) + 1;
  223. }
  224. }
  225. else if (files[0] != 0)
  226. {
  227. results.add (File (String (files)));
  228. }
  229. }