|  | /*
  ==============================================================================
   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.
   JUCE is an open source library subject to commercial or open-source
   licensing.
   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).
   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy
   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.
  ==============================================================================
*/
namespace juce
{
//==============================================================================
template <> struct ContainerDeletePolicy<UIDocumentPickerViewController>                   { static void destroy (NSObject* o) { [o release]; } };
template <> struct ContainerDeletePolicy<NSObject<UIDocumentPickerDelegate>>               { static void destroy (NSObject* o) { [o release]; } };
class FileChooser::Native    : private Component, public FileChooser::Pimpl
{
public:
    Native (FileChooser& fileChooser, int flags)
        : owner (fileChooser)
    {
        static FileChooserDelegateClass cls;
        delegate = [cls.createInstance() init];
        FileChooserDelegateClass::setOwner (delegate, this);
        auto utTypeArray = createNSArrayFromStringArray (getUTTypesForWildcards (owner.filters));
        if ((flags & FileBrowserComponent::saveMode) != 0)
        {
            // You must specify the fileWhichShouldBeSaved parameter when using
            // the native save dialog on iOS!
            jassert (owner.fileToSave.existsAsFile());
            auto url = [[NSURL alloc] initFileURLWithPath:juceStringToNS (owner.fileToSave.getFullPathName())];
            controller = [[UIDocumentPickerViewController alloc] initWithURL:url
                                                                      inMode:UIDocumentPickerModeExportToService];
            [url release];
        }
        else
        {
            controller = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:utTypeArray
                                                                                inMode:UIDocumentPickerModeOpen];
        }
        [controller setDelegate:delegate];
        [controller setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
        setOpaque (false);
        auto chooserBounds = Desktop::getInstance().getDisplays().getMainDisplay().userArea;
        setBounds (chooserBounds);
        setAlwaysOnTop (true);
        addToDesktop (0);
    }
    ~Native()
    {
        exitModalState (0);
    }
    void launch() override
    {
        enterModalState (true, nullptr, true);
    }
    void runModally() override
    {
       #if JUCE_MODAL_LOOPS_PERMITTED
        runModalLoop();
       #endif
    }
private:
    //==============================================================================
    void parentHierarchyChanged() override
    {
        auto* newPeer = dynamic_cast<UIViewComponentPeer*> (getPeer());
        if (peer != newPeer)
        {
            peer = newPeer;
            if (auto* parentController = peer->controller)
                [parentController showViewController:controller sender:parentController];
            if (peer->view.window != nil)
                peer->view.window.autoresizesSubviews = YES;
        }
    }
    //==============================================================================
    static StringArray getUTTypesForWildcards (const String& filterWildcards)
    {
        auto filters = StringArray::fromTokens (filterWildcards, ";", "");
        StringArray result;
        if (! filters.contains ("*") && filters.size() > 0)
        {
            for (auto filter : filters)
            {
                // iOS only supports file extension wild cards
                jassert (filter.upToLastOccurrenceOf (".", true, false) == "*.");
                auto fileExtension = filter.fromLastOccurrenceOf (".", false, false);
                auto fileExtensionCF = fileExtension.toCFString();
                auto tag = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension, fileExtensionCF, nullptr);
                if (tag != nullptr)
                {
                    result.add (String::fromCFString (tag));
                    CFRelease (tag);
                }
                CFRelease (fileExtensionCF);
            }
        }
        else
            result.add ("public.data");
        return result;
    }
    //==============================================================================
    void didPickDocumentAtURL (NSURL* url)
    {
        Array<URL> chooserResults;
        chooserResults.add (URL (nsStringToJuce ([url absoluteString])));
        owner.finished (chooserResults, false);
        exitModalState (1);
    }
    void pickerWasCancelled()
    {
        Array<URL> chooserResults;
        owner.finished (chooserResults, false);
        exitModalState (0);
    }
    //==============================================================================
    struct FileChooserDelegateClass  : public ObjCClass<NSObject<UIDocumentPickerDelegate>>
    {
        FileChooserDelegateClass()  : ObjCClass<NSObject<UIDocumentPickerDelegate>> ("FileChooserDelegate_")
        {
            addIvar<Native*> ("owner");
            addMethod (@selector (documentPicker:didPickDocumentAtURL:), didPickDocumentAtURL,       "v@:@@");
            addMethod (@selector (documentPickerWasCancelled:),          documentPickerWasCancelled, "v@:@");
            addProtocol (@protocol (UIDocumentPickerDelegate));
            registerClass();
        }
        static void setOwner (id self, Native* owner)   { object_setInstanceVariable         (self, "owner", owner); }
        static Native* getOwner (id self)               { return getIvar<Native*> (self, "owner"); }
        //==============================================================================
        static void didPickDocumentAtURL (id self, SEL, UIDocumentPickerViewController*, NSURL* url)
        {
            auto picker = getOwner (self);
            if (picker != nullptr)
                picker->didPickDocumentAtURL (url);
        }
        static void documentPickerWasCancelled (id self, SEL, UIDocumentPickerViewController*)
        {
            auto picker = getOwner (self);
            if (picker != nullptr)
                picker->pickerWasCancelled();
        }
    };
    //==============================================================================
    FileChooser& owner;
    ScopedPointer<NSObject<UIDocumentPickerDelegate>> delegate;
    ScopedPointer<UIDocumentPickerViewController> controller;
    UIViewComponentPeer* peer = nullptr;
    static FileChooserDelegateClass fileChooserDelegateClass;
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native)
};
//==============================================================================
bool FileChooser::isPlatformDialogAvailable()
{
   #if JUCE_DISABLE_NATIVE_FILECHOOSERS
    return false;
   #else
    return [[NSFileManager defaultManager] ubiquityIdentityToken] != nil;
   #endif
}
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags,
                                                     FilePreviewComponent*)
{
    return new FileChooser::Native (owner, flags);
}
} // namespace juce
 |