/* ============================================================================== 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 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 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 MouseInputSource::getCurrentRawMousePosition() { JUCE_AUTORELEASEPOOL { const NSPoint p ([NSEvent mouseLocation]); return Point (p.x, getMainScreenHeight() - p.y); } } void MouseInputSource::setRawMousePosition (Point 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 assertion; #else ScreenSaverDefeater() { startTimer (10000); timerCallback(); } void timerCallback() override { if (Process::isForegroundProcess()) UpdateSystemActivity (1 /*UsrActivity*/); } #endif }; static ScopedPointer 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::getInstance().getDisplays()).refresh(); } juce_DeclareSingleton_SingleThreaded_Minimal (DisplaySettingsChangeCallback); private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DisplaySettingsChangeCallback) }; juce_ImplementSingleton_SingleThreaded (DisplaySettingsChangeCallback); static Rectangle 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 }