|  | /*
  ==============================================================================
   This file is part of the JUCE library.
   Copyright (c) 2013 - Raw Material Software Ltd.
   Permission is granted to use this software under the terms of either:
   a) the GPL v2 (or any later version)
   b) the Affero GPL v3
   Details of these licenses can be found at: www.gnu.org/licenses
   JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
   ------------------------------------------------------------------------------
   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.juce.com for more information.
  ==============================================================================
*/
void LookAndFeel::playAlertSound()
{
    NSBeep();
}
//==============================================================================
class OSXMessageBox  : private AsyncUpdater
{
public:
    OSXMessageBox (AlertWindow::AlertIconType type, const String& t, const String& m,
                   const char* b1, const char* b2, const char* b3,
                   ModalComponentManager::Callback* c, const bool runAsync)
        : iconType (type), title (t), message (m), callback (c),
          button1 (b1), button2 (b2), button3 (b3)
    {
        if (runAsync)
            triggerAsyncUpdate();
    }
    int getResult() const
    {
        switch (getRawResult())
        {
            case NSAlertDefaultReturn:  return 1;
            case NSAlertOtherReturn:    return 2;
            default:                    return 0;
        }
    }
    static int show (AlertWindow::AlertIconType iconType, const String& title, const String& message,
                     ModalComponentManager::Callback* callback, const char* b1, const char* b2, const char* b3,
                     bool runAsync)
    {
        ScopedPointer<OSXMessageBox> mb (new OSXMessageBox (iconType, title, message, b1, b2, b3,
                                                            callback, runAsync));
        if (! runAsync)
            return mb->getResult();
        mb.release();
        return 0;
    }
private:
    AlertWindow::AlertIconType iconType;
    String title, message;
    ScopedPointer<ModalComponentManager::Callback> callback;
    const char* button1;
    const char* button2;
    const char* button3;
    void handleAsyncUpdate() override
    {
        const int result = getResult();
        if (callback != nullptr)
            callback->modalStateFinished (result);
        delete this;
    }
    static NSString* translateIfNotNull (const char* s)
    {
        return s != nullptr ? juceStringToNS (TRANS (s)) : nil;
    }
    NSInteger getRawResult() const
    {
        NSString* msg = juceStringToNS (message);
        NSString* ttl = juceStringToNS (title);
        NSString* b1  = translateIfNotNull (button1);
        NSString* b2  = translateIfNotNull (button2);
        NSString* b3  = translateIfNotNull (button3);
        switch (iconType)
        {
            case AlertWindow::InfoIcon:     return NSRunInformationalAlertPanel (ttl, msg, b1, b2, b3);
            case AlertWindow::WarningIcon:  return NSRunCriticalAlertPanel      (ttl, msg, b1, b2, b3);
            default:                        return NSRunAlertPanel              (ttl, msg, b1, b2, b3);
        }
    }
};
#if JUCE_MODAL_LOOPS_PERMITTED
void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
                                                     const String& title, const String& message,
                                                     Component* /*associatedComponent*/)
{
    OSXMessageBox::show (iconType, title, message, nullptr, "OK", nullptr, nullptr, false);
}
#endif
void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
                                                          const String& title, const String& message,
                                                          Component* /*associatedComponent*/,
                                                          ModalComponentManager::Callback* callback)
{
    OSXMessageBox::show (iconType, title, message, callback, "OK", nullptr, nullptr, true);
}
bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
                                                      const String& title, const String& message,
                                                      Component* /*associatedComponent*/,
                                                      ModalComponentManager::Callback* callback)
{
    return OSXMessageBox::show (iconType, title, message, callback,
                                "OK", "Cancel", nullptr, callback != nullptr) == 1;
}
int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
                                                        const String& title, const String& message,
                                                        Component* /*associatedComponent*/,
                                                        ModalComponentManager::Callback* callback)
{
    return OSXMessageBox::show (iconType, title, message, callback,
                                "Yes", "Cancel", "No", callback != nullptr);
}
//==============================================================================
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool /*canMoveFiles*/)
{
    if (files.size() == 0)
        return false;
    MouseInputSource* draggingSource = Desktop::getInstance().getDraggingMouseSource(0);
    if (draggingSource == nullptr)
    {
        jassertfalse;  // This method must be called in response to a component's mouseDown or mouseDrag event!
        return false;
    }
    Component* sourceComp = draggingSource->getComponentUnderMouse();
    if (sourceComp == nullptr)
    {
        jassertfalse;  // This method must be called in response to a component's mouseDown or mouseDrag event!
        return false;
    }
    JUCE_AUTORELEASEPOOL
    {
        NSView* view = (NSView*) sourceComp->getWindowHandle();
        if (view == nil)
            return false;
        NSPasteboard* pboard = [NSPasteboard pasteboardWithName: NSDragPboard];
        [pboard declareTypes: [NSArray arrayWithObject: NSFilenamesPboardType]
                       owner: nil];
        NSMutableArray* filesArray = [NSMutableArray arrayWithCapacity: 4];
        for (int i = 0; i < files.size(); ++i)
            [filesArray addObject: juceStringToNS (files[i])];
        [pboard setPropertyList: filesArray
                        forType: NSFilenamesPboardType];
        NSPoint dragPosition = [view convertPoint: [[[view window] currentEvent] locationInWindow]
                                         fromView: nil];
        dragPosition.x -= 16;
        dragPosition.y -= 16;
        [view dragImage: [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (files[0])]
                     at: dragPosition
                 offset: NSMakeSize (0, 0)
                  event: [[view window] currentEvent]
             pasteboard: pboard
                 source: view
              slideBack: YES];
    }
    return true;
}
bool DragAndDropContainer::performExternalDragDropOfText (const String& /*text*/)
{
    jassertfalse;    // not implemented!
    return false;
}
//==============================================================================
bool Desktop::canUseSemiTransparentWindows() noexcept
{
    return true;
}
Point<float> MouseInputSource::getCurrentRawMousePosition()
{
    JUCE_AUTORELEASEPOOL
    {
        const NSPoint p ([NSEvent mouseLocation]);
        return Point<float> ((float) p.x, (float) (getMainScreenHeight() - p.y));
    }
}
void MouseInputSource::setRawMousePosition (Point<float> newPosition)
{
    // this rubbish needs to be done around the warp call, to avoid causing a
    // bizarre glitch..
    CGAssociateMouseAndMouseCursorPosition (false);
    CGWarpMouseCursorPosition (convertToCGPoint (newPosition));
    CGAssociateMouseAndMouseCursorPosition (true);
}
double Desktop::getDefaultMasterScale()
{
    return 1.0;
}
Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
{
    return upright;
}
//==============================================================================
#if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7)
 #define JUCE_USE_IOPM_SCREENSAVER_DEFEAT 1
#endif
#if ! (defined (JUCE_USE_IOPM_SCREENSAVER_DEFEAT) || defined (__POWER__))
 extern "C"  { extern OSErr UpdateSystemActivity (UInt8); } // Some versions of the SDK omit this function..
#endif
class ScreenSaverDefeater   : public Timer
{
public:
   #if JUCE_USE_IOPM_SCREENSAVER_DEFEAT
    ScreenSaverDefeater()
    {
        startTimer (5000);
        timerCallback();
    }
    void timerCallback() override
    {
        if (Process::isForegroundProcess())
        {
            if (assertion == nullptr)
                assertion = new PMAssertion();
        }
        else
        {
            assertion = nullptr;
        }
    }
    struct PMAssertion
    {
        PMAssertion()  : assertionID (kIOPMNullAssertionID)
        {
            IOReturn res = IOPMAssertionCreateWithName (kIOPMAssertionTypePreventUserIdleDisplaySleep,
                                                        kIOPMAssertionLevelOn,
                                                        CFSTR ("JUCE Playback"),
                                                        &assertionID);
            jassert (res == kIOReturnSuccess); (void) res;
        }
        ~PMAssertion()
        {
            if (assertionID != kIOPMNullAssertionID)
                IOPMAssertionRelease (assertionID);
        }
        IOPMAssertionID assertionID;
    };
    ScopedPointer<PMAssertion> assertion;
   #else
    ScreenSaverDefeater()
    {
        startTimer (10000);
        timerCallback();
    }
    void timerCallback() override
    {
        if (Process::isForegroundProcess())
            UpdateSystemActivity (1 /*UsrActivity*/);
    }
   #endif
};
static ScopedPointer<ScreenSaverDefeater> screenSaverDefeater;
void Desktop::setScreenSaverEnabled (const bool isEnabled)
{
    if (isEnabled)
        screenSaverDefeater = nullptr;
    else if (screenSaverDefeater == nullptr)
        screenSaverDefeater = new ScreenSaverDefeater();
}
bool Desktop::isScreenSaverEnabled()
{
    return screenSaverDefeater == nullptr;
}
//==============================================================================
class DisplaySettingsChangeCallback  : private DeletedAtShutdown
{
public:
    DisplaySettingsChangeCallback()
    {
        CGDisplayRegisterReconfigurationCallback (displayReconfigurationCallBack, 0);
    }
    ~DisplaySettingsChangeCallback()
    {
        CGDisplayRemoveReconfigurationCallback (displayReconfigurationCallBack, 0);
        clearSingletonInstance();
    }
    static void displayReconfigurationCallBack (CGDirectDisplayID, CGDisplayChangeSummaryFlags, void*)
    {
        const_cast <Desktop::Displays&> (Desktop::getInstance().getDisplays()).refresh();
    }
    juce_DeclareSingleton_SingleThreaded_Minimal (DisplaySettingsChangeCallback);
private:
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DisplaySettingsChangeCallback)
};
juce_ImplementSingleton_SingleThreaded (DisplaySettingsChangeCallback);
static Rectangle<int> convertDisplayRect (NSRect r, CGFloat mainScreenBottom)
{
    r.origin.y = mainScreenBottom - (r.origin.y + r.size.height);
    return convertToRectInt (r);
}
static Desktop::Displays::Display getDisplayFromScreen (NSScreen* s, CGFloat& mainScreenBottom, const float masterScale)
{
    Desktop::Displays::Display d;
    d.isMain = (mainScreenBottom == 0);
    if (d.isMain)
        mainScreenBottom = [s frame].size.height;
    d.userArea  = convertDisplayRect ([s visibleFrame], mainScreenBottom) / masterScale;
    d.totalArea = convertDisplayRect ([s frame], mainScreenBottom) / masterScale;
    d.scale = masterScale;
   #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
    if ([s respondsToSelector: @selector (backingScaleFactor)])
        d.scale *= s.backingScaleFactor;
   #endif
    NSSize dpi = [[[s deviceDescription] objectForKey: NSDeviceResolution] sizeValue];
    d.dpi = (dpi.width + dpi.height) / 2.0;
    return d;
}
void Desktop::Displays::findDisplays (const float masterScale)
{
    JUCE_AUTORELEASEPOOL
    {
        DisplaySettingsChangeCallback::getInstance();
        CGFloat mainScreenBottom = 0;
        for (NSScreen* s in [NSScreen screens])
            displays.add (getDisplayFromScreen (s, mainScreenBottom, masterScale));
    }
}
//==============================================================================
bool juce_areThereAnyAlwaysOnTopWindows()
{
    for (NSWindow* window in [NSApp windows])
        if ([window level] > NSNormalWindowLevel)
            return true;
    return false;
}
//==============================================================================
Image juce_createIconForFile (const File& file)
{
    JUCE_AUTORELEASEPOOL
    {
        NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (file.getFullPathName())];
        Image result (Image::ARGB, (int) [image size].width, (int) [image size].height, true);
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: juce_getImageContext (result) flipped: false]];
        [image drawAtPoint: NSMakePoint (0, 0)
                  fromRect: NSMakeRect (0, 0, [image size].width, [image size].height)
                 operation: NSCompositeSourceOver fraction: 1.0f];
        [[NSGraphicsContext currentContext] flushGraphics];
        [NSGraphicsContext restoreGraphicsState];
        return result;
    }
}
//==============================================================================
void SystemClipboard::copyTextToClipboard (const String& text)
{
    NSPasteboard* pb = [NSPasteboard generalPasteboard];
    [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType]
               owner: nil];
    [pb setString: juceStringToNS (text)
          forType: NSStringPboardType];
}
String SystemClipboard::getTextFromClipboard()
{
    NSString* text = [[NSPasteboard generalPasteboard] stringForType: NSStringPboardType];
    return text == nil ? String()
                       : nsStringToJuce (text);
}
void Process::setDockIconVisible (bool isVisible)
{
   #if defined (MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
    [NSApp setActivationPolicy: isVisible ? NSApplicationActivationPolicyRegular
                                          : NSApplicationActivationPolicyProhibited];
   #else
    (void) isVisible;
    jassertfalse; // sorry, not available in 10.5!
   #endif
}
 |