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.

227 lines
7.9KB

  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. // You must specify the fileWhichShouldBeSaved parameter when using
  37. // the native save dialog on iOS!
  38. jassert (owner.fileToSave.existsAsFile());
  39. auto url = [[NSURL alloc] initFileURLWithPath:juceStringToNS (owner.fileToSave.getFullPathName())];
  40. controller = [[UIDocumentPickerViewController alloc] initWithURL:url
  41. inMode:UIDocumentPickerModeExportToService];
  42. [url release];
  43. }
  44. else
  45. {
  46. controller = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:utTypeArray
  47. inMode:UIDocumentPickerModeOpen];
  48. }
  49. [controller setDelegate:delegate];
  50. [controller setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
  51. setOpaque (false);
  52. auto chooserBounds = Desktop::getInstance().getDisplays().getMainDisplay().userArea;
  53. setBounds (chooserBounds);
  54. setAlwaysOnTop (true);
  55. addToDesktop (0);
  56. }
  57. ~Native()
  58. {
  59. exitModalState (0);
  60. }
  61. void launch() override
  62. {
  63. enterModalState (true, nullptr, true);
  64. }
  65. void runModally() override
  66. {
  67. #if JUCE_MODAL_LOOPS_PERMITTED
  68. runModalLoop();
  69. #endif
  70. }
  71. private:
  72. //==============================================================================
  73. void parentHierarchyChanged() override
  74. {
  75. auto* newPeer = dynamic_cast<UIViewComponentPeer*> (getPeer());
  76. if (peer != newPeer)
  77. {
  78. peer = newPeer;
  79. if (auto* parentController = peer->controller)
  80. [parentController showViewController:controller sender:parentController];
  81. if (peer->view.window != nil)
  82. peer->view.window.autoresizesSubviews = YES;
  83. }
  84. }
  85. //==============================================================================
  86. static StringArray getUTTypesForWildcards (const String& filterWildcards)
  87. {
  88. auto filters = StringArray::fromTokens (filterWildcards, ";", "");
  89. StringArray result;
  90. if (! filters.contains ("*") && filters.size() > 0)
  91. {
  92. for (auto filter : filters)
  93. {
  94. // iOS only supports file extension wild cards
  95. jassert (filter.upToLastOccurrenceOf (".", true, false) == "*.");
  96. auto fileExtension = filter.fromLastOccurrenceOf (".", false, false);
  97. auto fileExtensionCF = fileExtension.toCFString();
  98. auto tag = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension, fileExtensionCF, nullptr);
  99. if (tag != nullptr)
  100. {
  101. result.add (String::fromCFString (tag));
  102. CFRelease (tag);
  103. }
  104. CFRelease (fileExtensionCF);
  105. }
  106. }
  107. else
  108. result.add ("public.data");
  109. return result;
  110. }
  111. //==============================================================================
  112. void didPickDocumentAtURL (NSURL* url)
  113. {
  114. Array<URL> chooserResults;
  115. chooserResults.add (URL (nsStringToJuce ([url absoluteString])));
  116. owner.finished (chooserResults, false);
  117. exitModalState (1);
  118. }
  119. void pickerWasCancelled()
  120. {
  121. Array<URL> chooserResults;
  122. owner.finished (chooserResults, false);
  123. exitModalState (0);
  124. }
  125. //==============================================================================
  126. struct FileChooserDelegateClass : public ObjCClass<NSObject<UIDocumentPickerDelegate>>
  127. {
  128. FileChooserDelegateClass() : ObjCClass<NSObject<UIDocumentPickerDelegate>> ("FileChooserDelegate_")
  129. {
  130. addIvar<Native*> ("owner");
  131. addMethod (@selector (documentPicker:didPickDocumentAtURL:), didPickDocumentAtURL, "v@:@@");
  132. addMethod (@selector (documentPickerWasCancelled:), documentPickerWasCancelled, "v@:@");
  133. addProtocol (@protocol (UIDocumentPickerDelegate));
  134. registerClass();
  135. }
  136. static void setOwner (id self, Native* owner) { object_setInstanceVariable (self, "owner", owner); }
  137. static Native* getOwner (id self) { return getIvar<Native*> (self, "owner"); }
  138. //==============================================================================
  139. static void didPickDocumentAtURL (id self, SEL, UIDocumentPickerViewController*, NSURL* url)
  140. {
  141. auto picker = getOwner (self);
  142. if (picker != nullptr)
  143. picker->didPickDocumentAtURL (url);
  144. }
  145. static void documentPickerWasCancelled (id self, SEL, UIDocumentPickerViewController*)
  146. {
  147. auto picker = getOwner (self);
  148. if (picker != nullptr)
  149. picker->pickerWasCancelled();
  150. }
  151. };
  152. //==============================================================================
  153. FileChooser& owner;
  154. ScopedPointer<NSObject<UIDocumentPickerDelegate>> delegate;
  155. ScopedPointer<UIDocumentPickerViewController> controller;
  156. UIViewComponentPeer* peer = nullptr;
  157. static FileChooserDelegateClass fileChooserDelegateClass;
  158. //==============================================================================
  159. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native)
  160. };
  161. //==============================================================================
  162. bool FileChooser::isPlatformDialogAvailable()
  163. {
  164. #if JUCE_DISABLE_NATIVE_FILECHOOSERS
  165. return false;
  166. #else
  167. return [[NSFileManager defaultManager] ubiquityIdentityToken] != nil;
  168. #endif
  169. }
  170. FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags,
  171. FilePreviewComponent*)
  172. {
  173. return new FileChooser::Native (owner, flags);
  174. }
  175. } // namespace juce