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.

300 lines
11KB

  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. #include <Carbon/Carbon.h>
  24. #include <fnmatch.h>
  25. #include "../../../src/juce_core/basics/juce_StandardHeader.h"
  26. BEGIN_JUCE_NAMESPACE
  27. #include "../../../src/juce_appframework/gui/components/filebrowser/juce_FileChooser.h"
  28. #include "../../../src/juce_appframework/gui/components/juce_Desktop.h"
  29. #include "../../../src/juce_appframework/application/juce_Application.h"
  30. #include "../../../src/juce_appframework/events/juce_MessageManager.h"
  31. #include "../../../src/juce_core/misc/juce_PlatformUtilities.h"
  32. #include "../../../src/juce_core/text/juce_LocalisedStrings.h"
  33. //==============================================================================
  34. struct JuceNavInfo
  35. {
  36. StringArray filters;
  37. AEDesc defaultLocation;
  38. bool defaultLocationValid;
  39. };
  40. static void pascal juceNavEventProc (NavEventCallbackMessage callbackSelector,
  41. NavCBRecPtr callbackParms,
  42. void *callBackUD)
  43. {
  44. if (callbackSelector == kNavCBStart)
  45. {
  46. if (((JuceNavInfo*) callBackUD)->defaultLocationValid)
  47. {
  48. NavCustomControl (callbackParms->context,
  49. kNavCtlSetLocation,
  50. (void*) &((JuceNavInfo*) callBackUD)->defaultLocation);
  51. }
  52. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  53. {
  54. Component* const c = Desktop::getInstance().getComponent (i);
  55. if (c != 0 && c->isAlwaysOnTop() && c->isVisible())
  56. {
  57. SetWindowGroup (callbackParms->window,
  58. GetWindowGroup ((WindowRef) c->getWindowHandle()));
  59. break;
  60. }
  61. }
  62. BringToFront (callbackParms->window);
  63. SelectWindow (callbackParms->window);
  64. SetUserFocusWindow (callbackParms->window);
  65. }
  66. }
  67. static Boolean pascal juceNavFilterProc (AEDesc* theItem,
  68. void*,
  69. void* callBackUD,
  70. NavFilterModes filterMode)
  71. {
  72. // must return true if we don't understand the object
  73. bool result = true;
  74. if (filterMode == kNavFilteringBrowserList)
  75. {
  76. AEDesc desc;
  77. if (AECoerceDesc (theItem, typeFSRef, &desc) == noErr)
  78. {
  79. Size size = AEGetDescDataSize (&desc);
  80. if (size > 0)
  81. {
  82. void* data = juce_calloc (size);
  83. if (AEGetDescData (&desc, data, size) == noErr)
  84. {
  85. const String path (PlatformUtilities::makePathFromFSRef ((FSRef*) data));
  86. if (path.isNotEmpty())
  87. {
  88. const File file (path);
  89. if ((! file.isDirectory()) || PlatformUtilities::isBundle (path))
  90. {
  91. const String filename (file.getFileName().toLowerCase());
  92. const char* const filenameUTF8 = filename.toUTF8();
  93. const JuceNavInfo* const info = (const JuceNavInfo*) callBackUD;
  94. if (info != 0)
  95. {
  96. result = false;
  97. for (int i = info->filters.size(); --i >= 0;)
  98. {
  99. const String wildcard (info->filters[i].toLowerCase());
  100. if (fnmatch (wildcard.toUTF8(), filenameUTF8, 0) == 0)
  101. {
  102. result = true;
  103. break;
  104. }
  105. }
  106. }
  107. }
  108. }
  109. }
  110. juce_free (data);
  111. }
  112. AEDisposeDesc (&desc);
  113. }
  114. }
  115. return result;
  116. }
  117. void FileChooser::showPlatformDialog (OwnedArray<File>& results,
  118. const String& title,
  119. const File& currentFileOrDirectory,
  120. const String& filter,
  121. bool selectsDirectory,
  122. bool isSaveDialogue,
  123. bool warnAboutOverwritingExistingFiles,
  124. bool selectMultipleFiles,
  125. FilePreviewComponent* extraInfoComponent)
  126. {
  127. JuceNavInfo userInfo;
  128. userInfo.filters.addTokens (filter.replaceCharacters (T(",:"), T(";;")), T(";"), 0);
  129. userInfo.filters.trim();
  130. userInfo.filters.removeEmptyStrings();
  131. userInfo.defaultLocationValid = false;
  132. void* const userInfoPtr = (void*) &userInfo;
  133. const int oldTimeBeforeWaitCursor = MessageManager::getInstance()->getTimeBeforeShowingWaitCursor();
  134. MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0);
  135. NavEventUPP eventProc = NewNavEventUPP (juceNavEventProc);
  136. NavObjectFilterUPP filterProc = NewNavObjectFilterUPP (juceNavFilterProc);
  137. FSRef defaultRef;
  138. if ((currentFileOrDirectory.isOnHardDisk()
  139. && PlatformUtilities::makeFSRefFromPath (&defaultRef,
  140. currentFileOrDirectory.getFullPathName()))
  141. || (currentFileOrDirectory.getParentDirectory().isOnHardDisk()
  142. && PlatformUtilities::makeFSRefFromPath (&defaultRef,
  143. currentFileOrDirectory.getParentDirectory().getFullPathName())))
  144. {
  145. if (AECreateDesc (typeFSRef, &defaultRef, sizeof (defaultRef), &userInfo.defaultLocation) == noErr)
  146. {
  147. userInfo.defaultLocationValid = true;
  148. }
  149. }
  150. WindowRef lastFocused = GetUserFocusWindow();
  151. NavDialogCreationOptions options;
  152. if (NavGetDefaultDialogCreationOptions (&options) == noErr)
  153. {
  154. options.optionFlags |= kNavSelectDefaultLocation
  155. | kNavSupportPackages
  156. | kNavAllowPreviews;
  157. if (! warnAboutOverwritingExistingFiles)
  158. options.optionFlags |= kNavDontConfirmReplacement;
  159. if (selectMultipleFiles)
  160. options.optionFlags |= kNavAllowMultipleFiles;
  161. const String name (selectsDirectory ? TRANS("Choose folder")
  162. : TRANS("Choose file"));
  163. options.clientName = PlatformUtilities::juceStringToCFString (name);
  164. CFStringRef message = PlatformUtilities::juceStringToCFString (title);
  165. // nasty layout bug if the message text is set for a directory browser..
  166. if (selectsDirectory)
  167. options.windowTitle = message;
  168. else
  169. options.message = message;
  170. NavDialogRef dialog = 0;
  171. bool ok = false;
  172. if (selectsDirectory)
  173. {
  174. ok = (NavCreateChooseFolderDialog (&options, eventProc, 0, userInfoPtr, &dialog) == noErr);
  175. }
  176. else if (isSaveDialogue)
  177. {
  178. ok = (NavCreatePutFileDialog (&options, 0, 0, eventProc, userInfoPtr, &dialog) == noErr);
  179. }
  180. else
  181. {
  182. ok = (NavCreateGetFileDialog (&options, 0, eventProc, 0, filterProc, userInfoPtr, &dialog) == noErr);
  183. }
  184. if (ok && (NavDialogRun (dialog) == noErr))
  185. {
  186. NavReplyRecord reply;
  187. if (NavDialogGetReply (dialog, &reply) == noErr)
  188. {
  189. if (reply.validRecord)
  190. {
  191. long count;
  192. if (AECountItems (&(reply.selection), &count) == noErr
  193. && count > 0)
  194. {
  195. AEKeyword theKeyword;
  196. DescType actualType;
  197. Size actualSize;
  198. FSRef file;
  199. for (int i = 1; i <= count; ++i)
  200. {
  201. // Get a pointer to selected file
  202. if (AEGetNthPtr (&(reply.selection),
  203. i,
  204. typeFSRef,
  205. &theKeyword,
  206. &actualType,
  207. &file,
  208. sizeof (file),
  209. &actualSize) == noErr)
  210. {
  211. String result (PlatformUtilities::makePathFromFSRef (&file));
  212. if (result.isNotEmpty() && isSaveDialogue && ! selectsDirectory)
  213. {
  214. CFStringRef saveName = NavDialogGetSaveFileName (dialog);
  215. result = File (result)
  216. .getChildFile (PlatformUtilities::convertToPrecomposedUnicode (PlatformUtilities::cfStringToJuceString (saveName)))
  217. .getFullPathName();
  218. }
  219. results.add (new File (result));
  220. }
  221. }
  222. }
  223. }
  224. NavDisposeReply (&reply);
  225. }
  226. }
  227. if (dialog != 0)
  228. NavDialogDispose (dialog);
  229. CFRelease (message);
  230. CFRelease (options.clientName);
  231. }
  232. if (userInfo.defaultLocationValid)
  233. AEDisposeDesc (&userInfo.defaultLocation);
  234. DisposeNavEventUPP (eventProc);
  235. DisposeNavObjectFilterUPP (filterProc);
  236. MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (oldTimeBeforeWaitCursor);
  237. SetUserFocusWindow (lastFocused);
  238. }
  239. END_JUCE_NAMESPACE