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.

238 lines
8.3KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. //==============================================================================
  22. template <> struct ContainerDeletePolicy<UIDocumentPickerViewController> { static void destroy (NSObject* o) { [o release]; } };
  23. template <> struct ContainerDeletePolicy<NSObject<UIDocumentPickerDelegate>> { static void destroy (NSObject* o) { [o release]; } };
  24. class FileChooser::Native : private Component, public FileChooser::Pimpl
  25. {
  26. public:
  27. Native (FileChooser& fileChooser, int flags)
  28. : owner (fileChooser)
  29. {
  30. static FileChooserDelegateClass cls;
  31. delegate = [cls.createInstance() init];
  32. FileChooserDelegateClass::setOwner (delegate, this);
  33. auto utTypeArray = createNSArrayFromStringArray (getUTTypesForWildcards (owner.filters));
  34. if ((flags & FileBrowserComponent::saveMode) != 0)
  35. {
  36. auto currentFileOrDirectory = owner.startingFile;
  37. if (! currentFileOrDirectory.existsAsFile())
  38. {
  39. auto filename = (currentFileOrDirectory.isDirectory() ? "Untitled" : currentFileOrDirectory.getFileName());
  40. auto tmpDirectory = File::createTempFile ("iosDummyFiles");
  41. if (tmpDirectory.createDirectory().wasOk())
  42. {
  43. currentFileOrDirectory = tmpDirectory.getChildFile (filename);
  44. currentFileOrDirectory.replaceWithText ("");
  45. }
  46. }
  47. auto url = [[NSURL alloc] initFileURLWithPath:juceStringToNS (currentFileOrDirectory.getFullPathName())];
  48. controller = [[UIDocumentPickerViewController alloc] initWithURL:url
  49. inMode:UIDocumentPickerModeMoveToService];
  50. [url release];
  51. }
  52. else
  53. {
  54. controller = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:utTypeArray
  55. inMode:UIDocumentPickerModeOpen];
  56. }
  57. [controller setDelegate:delegate];
  58. [controller setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
  59. setOpaque (false);
  60. auto chooserBounds = Desktop::getInstance().getDisplays().getMainDisplay().userArea;
  61. setBounds (chooserBounds);
  62. setAlwaysOnTop (true);
  63. addToDesktop (0);
  64. }
  65. ~Native()
  66. {
  67. exitModalState (0);
  68. }
  69. void launch() override
  70. {
  71. enterModalState (true, nullptr, true);
  72. }
  73. void runModally() override
  74. {
  75. #if JUCE_MODAL_LOOPS_PERMITTED
  76. runModalLoop();
  77. #endif
  78. }
  79. private:
  80. //==============================================================================
  81. void parentHierarchyChanged() override
  82. {
  83. auto* newPeer = dynamic_cast<UIViewComponentPeer*> (getPeer());
  84. if (peer != newPeer)
  85. {
  86. peer = newPeer;
  87. if (auto* parentController = peer->controller)
  88. [parentController showViewController:controller sender:parentController];
  89. if (peer->view.window != nil)
  90. peer->view.window.autoresizesSubviews = YES;
  91. }
  92. }
  93. //==============================================================================
  94. static StringArray getUTTypesForWildcards (const String& filterWildcards)
  95. {
  96. auto filters = StringArray::fromTokens (filterWildcards, ";", "");
  97. StringArray result;
  98. if (! filters.contains ("*") && filters.size() > 0)
  99. {
  100. for (auto filter : filters)
  101. {
  102. // iOS only supports file extension wild cards
  103. jassert (filter.upToLastOccurrenceOf (".", true, false) == "*.");
  104. auto fileExtension = filter.fromLastOccurrenceOf (".", false, false);
  105. auto fileExtensionCF = fileExtension.toCFString();
  106. auto tag = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension, fileExtensionCF, nullptr);
  107. if (tag != nullptr)
  108. {
  109. result.add (String::fromCFString (tag));
  110. CFRelease (tag);
  111. }
  112. CFRelease (fileExtensionCF);
  113. }
  114. }
  115. else
  116. result.add ("public.data");
  117. return result;
  118. }
  119. //==============================================================================
  120. void didPickDocumentAtURL (NSURL* url)
  121. {
  122. Array<URL> chooserResults;
  123. chooserResults.add (URL (nsStringToJuce ([url absoluteString])));
  124. owner.finished (chooserResults);
  125. exitModalState (1);
  126. }
  127. void pickerWasCancelled()
  128. {
  129. Array<URL> chooserResults;
  130. owner.finished (chooserResults);
  131. exitModalState (0);
  132. }
  133. //==============================================================================
  134. struct FileChooserDelegateClass : public ObjCClass<NSObject<UIDocumentPickerDelegate>>
  135. {
  136. FileChooserDelegateClass() : ObjCClass<NSObject<UIDocumentPickerDelegate>> ("FileChooserDelegate_")
  137. {
  138. addIvar<Native*> ("owner");
  139. addMethod (@selector (documentPicker:didPickDocumentAtURL:), didPickDocumentAtURL, "v@:@@");
  140. addMethod (@selector (documentPickerWasCancelled:), documentPickerWasCancelled, "v@:@");
  141. addProtocol (@protocol (UIDocumentPickerDelegate));
  142. registerClass();
  143. }
  144. static void setOwner (id self, Native* owner) { object_setInstanceVariable (self, "owner", owner); }
  145. static Native* getOwner (id self) { return getIvar<Native*> (self, "owner"); }
  146. //==============================================================================
  147. static void didPickDocumentAtURL (id self, SEL, UIDocumentPickerViewController*, NSURL* url)
  148. {
  149. auto picker = getOwner (self);
  150. if (picker != nullptr)
  151. picker->didPickDocumentAtURL (url);
  152. }
  153. static void documentPickerWasCancelled (id self, SEL, UIDocumentPickerViewController*)
  154. {
  155. auto picker = getOwner (self);
  156. if (picker != nullptr)
  157. picker->pickerWasCancelled();
  158. }
  159. };
  160. //==============================================================================
  161. FileChooser& owner;
  162. ScopedPointer<NSObject<UIDocumentPickerDelegate>> delegate;
  163. ScopedPointer<UIDocumentPickerViewController> controller;
  164. UIViewComponentPeer* peer = nullptr;
  165. static FileChooserDelegateClass fileChooserDelegateClass;
  166. //==============================================================================
  167. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native)
  168. };
  169. //==============================================================================
  170. bool FileChooser::isPlatformDialogAvailable()
  171. {
  172. #if JUCE_DISABLE_NATIVE_FILECHOOSERS
  173. return false;
  174. #else
  175. return [[NSFileManager defaultManager] ubiquityIdentityToken] != nil;
  176. #endif
  177. }
  178. FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags,
  179. FilePreviewComponent*)
  180. {
  181. return new FileChooser::Native (owner, flags);
  182. }
  183. } // namespace juce