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.

359 lines
12KB

  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. static const void* defaultDirPath = 0;
  39. static String returnedString; // need this to get non-existent pathnames from the directory chooser
  40. static Component* currentExtraFileWin = 0;
  41. static bool areThereAnyAlwaysOnTopWindows()
  42. {
  43. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  44. {
  45. Component* c = Desktop::getInstance().getComponent (i);
  46. if (c != 0 && c->isAlwaysOnTop() && c->isShowing())
  47. return true;
  48. }
  49. return false;
  50. }
  51. static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPARAM /*lpData*/)
  52. {
  53. if (msg == BFFM_INITIALIZED)
  54. {
  55. SendMessage (hWnd, BFFM_SETSELECTIONW, TRUE, (LPARAM) defaultDirPath);
  56. }
  57. else if (msg == BFFM_VALIDATEFAILEDW)
  58. {
  59. returnedString = (LPCWSTR) lParam;
  60. }
  61. else if (msg == BFFM_VALIDATEFAILEDA)
  62. {
  63. returnedString = (const char*) lParam;
  64. }
  65. return 0;
  66. }
  67. void juce_setWindowStyleBit (HWND h, int styleType, int feature, bool bitIsSet);
  68. static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam)
  69. {
  70. if (currentExtraFileWin != 0)
  71. {
  72. if (uiMsg == WM_INITDIALOG)
  73. {
  74. HWND dialogH = GetParent (hdlg);
  75. jassert (dialogH != 0);
  76. if (dialogH == 0)
  77. dialogH = hdlg;
  78. RECT r, cr;
  79. GetWindowRect (dialogH, &r);
  80. GetClientRect (dialogH, &cr);
  81. SetWindowPos (dialogH, 0,
  82. r.left, r.top,
  83. currentExtraFileWin->getWidth() + jmax (150, r.right - r.left),
  84. jmax (150, r.bottom - r.top),
  85. SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  86. currentExtraFileWin->setBounds (cr.right, cr.top, currentExtraFileWin->getWidth(), cr.bottom - cr.top);
  87. currentExtraFileWin->getChildComponent(0)->setBounds (0, 0, currentExtraFileWin->getWidth(), currentExtraFileWin->getHeight());
  88. SetParent ((HWND) currentExtraFileWin->getWindowHandle(), (HWND) dialogH);
  89. juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_CHILD, (dialogH != 0));
  90. juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_POPUP, (dialogH == 0));
  91. }
  92. else if (uiMsg == WM_NOTIFY)
  93. {
  94. LPOFNOTIFY ofn = (LPOFNOTIFY) lParam;
  95. if (ofn->hdr.code == CDN_SELCHANGE)
  96. {
  97. FilePreviewComponent* comp = (FilePreviewComponent*) currentExtraFileWin->getChildComponent(0);
  98. if (comp != 0)
  99. {
  100. TCHAR path [MAX_PATH * 2];
  101. path[0] = 0;
  102. CommDlg_OpenSave_GetFilePath (GetParent (hdlg), (LPARAM) &path, MAX_PATH);
  103. const String fn ((const WCHAR*) path);
  104. comp->selectedFileChanged (File (fn));
  105. }
  106. }
  107. }
  108. }
  109. return 0;
  110. }
  111. class FPComponentHolder : public Component
  112. {
  113. public:
  114. FPComponentHolder()
  115. {
  116. setVisible (true);
  117. setOpaque (true);
  118. }
  119. ~FPComponentHolder()
  120. {
  121. }
  122. void paint (Graphics& g)
  123. {
  124. g.fillAll (Colours::lightgrey);
  125. }
  126. private:
  127. FPComponentHolder (const FPComponentHolder&);
  128. const FPComponentHolder& operator= (const FPComponentHolder&);
  129. };
  130. //==============================================================================
  131. void FileChooser::showPlatformDialog (OwnedArray<File>& results,
  132. const String& title,
  133. const File& currentFileOrDirectory,
  134. const String& filter,
  135. bool selectsDirectory,
  136. bool isSaveDialogue,
  137. bool warnAboutOverwritingExistingFiles,
  138. bool selectMultipleFiles,
  139. FilePreviewComponent* extraInfoComponent)
  140. {
  141. const int numCharsAvailable = 32768;
  142. MemoryBlock filenameSpace ((numCharsAvailable + 1) * sizeof (WCHAR), true);
  143. WCHAR* const fname = (WCHAR*) filenameSpace.getData();
  144. int fnameIdx = 0;
  145. JUCE_TRY
  146. {
  147. // use a modal window as the parent for this dialog box
  148. // to block input from other app windows
  149. const Rectangle mainMon (Desktop::getInstance().getMainMonitorArea());
  150. Component w (String::empty);
  151. w.setBounds (mainMon.getX() + mainMon.getWidth() / 4,
  152. mainMon.getY() + mainMon.getHeight() / 4,
  153. 0, 0);
  154. w.setOpaque (true);
  155. w.setAlwaysOnTop (areThereAnyAlwaysOnTopWindows());
  156. w.addToDesktop (0);
  157. if (extraInfoComponent == 0)
  158. w.enterModalState();
  159. String initialDir;
  160. if (currentFileOrDirectory.isDirectory())
  161. {
  162. initialDir = currentFileOrDirectory.getFullPathName();
  163. }
  164. else
  165. {
  166. currentFileOrDirectory.getFileName().copyToBuffer (fname, numCharsAvailable);
  167. initialDir = currentFileOrDirectory.getParentDirectory().getFullPathName();
  168. }
  169. if (currentExtraFileWin->isValidComponent())
  170. {
  171. jassertfalse
  172. return;
  173. }
  174. if (selectsDirectory)
  175. {
  176. LPITEMIDLIST list = 0;
  177. filenameSpace.fillWith (0);
  178. {
  179. BROWSEINFO bi;
  180. zerostruct (bi);
  181. bi.hwndOwner = (HWND) w.getWindowHandle();
  182. bi.pszDisplayName = fname;
  183. bi.lpszTitle = title;
  184. bi.lpfn = browseCallbackProc;
  185. #ifdef BIF_USENEWUI
  186. bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE;
  187. #else
  188. bi.ulFlags = 0x50;
  189. #endif
  190. defaultDirPath = (const WCHAR*) initialDir;
  191. list = SHBrowseForFolder (&bi);
  192. if (! SHGetPathFromIDListW (list, fname))
  193. {
  194. fname[0] = 0;
  195. returnedString = String::empty;
  196. }
  197. }
  198. LPMALLOC al;
  199. if (list != 0 && SUCCEEDED (SHGetMalloc (&al)))
  200. al->Free (list);
  201. defaultDirPath = 0;
  202. if (returnedString.isNotEmpty())
  203. {
  204. const String stringFName (fname);
  205. results.add (new File (File (stringFName).getSiblingFile (returnedString)));
  206. returnedString = String::empty;
  207. return;
  208. }
  209. }
  210. else
  211. {
  212. DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
  213. if (warnAboutOverwritingExistingFiles)
  214. flags |= OFN_OVERWRITEPROMPT;
  215. if (selectMultipleFiles)
  216. flags |= OFN_ALLOWMULTISELECT;
  217. if (extraInfoComponent != 0)
  218. {
  219. flags |= OFN_ENABLEHOOK;
  220. currentExtraFileWin = new FPComponentHolder();
  221. currentExtraFileWin->addAndMakeVisible (extraInfoComponent);
  222. currentExtraFileWin->setSize (jlimit (20, 800, extraInfoComponent->getWidth()),
  223. extraInfoComponent->getHeight());
  224. currentExtraFileWin->addToDesktop (0);
  225. currentExtraFileWin->enterModalState();
  226. }
  227. {
  228. WCHAR filters [1024];
  229. zeromem (filters, sizeof (filters));
  230. filter.copyToBuffer (filters, 1024);
  231. filter.copyToBuffer (filters + filter.length() + 1,
  232. 1022 - filter.length());
  233. OPENFILENAMEW of;
  234. zerostruct (of);
  235. #ifdef OPENFILENAME_SIZE_VERSION_400W
  236. of.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
  237. #else
  238. of.lStructSize = sizeof (of);
  239. #endif
  240. of.hwndOwner = (HWND) w.getWindowHandle();
  241. of.lpstrFilter = filters;
  242. of.nFilterIndex = 1;
  243. of.lpstrFile = fname;
  244. of.nMaxFile = numCharsAvailable;
  245. of.lpstrInitialDir = initialDir;
  246. of.lpstrTitle = title;
  247. of.Flags = flags;
  248. if (extraInfoComponent != 0)
  249. of.lpfnHook = &openCallback;
  250. if (isSaveDialogue)
  251. {
  252. if (! GetSaveFileName (&of))
  253. fname[0] = 0;
  254. else
  255. fnameIdx = of.nFileOffset;
  256. }
  257. else
  258. {
  259. if (! GetOpenFileName (&of))
  260. fname[0] = 0;
  261. else
  262. fnameIdx = of.nFileOffset;
  263. }
  264. }
  265. }
  266. }
  267. #if JUCE_CATCH_UNHANDLED_EXCEPTIONS
  268. catch (...)
  269. {
  270. fname[0] = 0;
  271. }
  272. #endif
  273. deleteAndZero (currentExtraFileWin);
  274. const WCHAR* const files = fname;
  275. if (selectMultipleFiles && fnameIdx > 0 && files [fnameIdx - 1] == 0)
  276. {
  277. const WCHAR* filename = files + fnameIdx;
  278. while (*filename != 0)
  279. {
  280. const String filepath (String (files) + T("\\") + String (filename));
  281. results.add (new File (filepath));
  282. filename += CharacterFunctions::length (filename) + 1;
  283. }
  284. }
  285. else if (files[0] != 0)
  286. {
  287. results.add (new File (files));
  288. }
  289. }
  290. END_JUCE_NAMESPACE