diff --git a/extras/JuceDemo/Source/MainDemoWindow.cpp b/extras/JuceDemo/Source/MainDemoWindow.cpp index f3cbcc4430..361fa1bf78 100644 --- a/extras/JuceDemo/Source/MainDemoWindow.cpp +++ b/extras/JuceDemo/Source/MainDemoWindow.cpp @@ -565,7 +565,7 @@ private: }; //============================================================================== -#if JUCE_WINDOWS || JUCE_LINUX +#if JUCE_WINDOWS || JUCE_LINUX || JUCE_MAC // Just add a simple icon to the Window system tray area.. class DemoTaskbarComponent : public SystemTrayIconComponent @@ -633,7 +633,7 @@ MainDemoWindow::MainDemoWindow() setVisible (true); - #if JUCE_WINDOWS || JUCE_LINUX + #if JUCE_WINDOWS || JUCE_LINUX || JUCE_MAC taskbarIcon = new DemoTaskbarComponent(); #endif } diff --git a/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm b/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm index ad5d061469..52c030a885 100644 --- a/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm +++ b/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm @@ -28,7 +28,7 @@ //============================================================================== namespace MouseCursorHelpers { - static NSImage* createNSImage (const Image& image) + NSImage* createNSImage (const Image& image) { JUCE_AUTORELEASEPOOL { diff --git a/modules/juce_gui_extra/juce_gui_extra.cpp b/modules/juce_gui_extra/juce_gui_extra.cpp index c030c12e4d..e3f8868649 100644 --- a/modules/juce_gui_extra/juce_gui_extra.cpp +++ b/modules/juce_gui_extra/juce_gui_extra.cpp @@ -111,6 +111,7 @@ namespace juce #if JUCE_MAC #include "native/juce_mac_NSViewComponent.mm" #include "native/juce_mac_AppleRemote.mm" + #include "native/juce_mac_SystemTrayIcon.cpp" #endif #if JUCE_IOS diff --git a/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.cpp b/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.cpp index 615e122c51..1c62f40183 100644 --- a/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.cpp +++ b/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.cpp @@ -23,7 +23,7 @@ ============================================================================== */ -#if JUCE_WINDOWS || JUCE_LINUX +#if JUCE_WINDOWS || JUCE_LINUX || JUCE_MAC SystemTrayIconComponent::SystemTrayIconComponent() { diff --git a/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h b/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h index 2d944027f6..fe635d08dc 100644 --- a/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h +++ b/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h @@ -26,7 +26,7 @@ #ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ #define __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ -#if JUCE_WINDOWS || JUCE_LINUX || DOXYGEN +#if JUCE_WINDOWS || JUCE_LINUX || JUCE_MAC || DOXYGEN //============================================================================== diff --git a/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp b/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp new file mode 100644 index 0000000000..7ae583d68b --- /dev/null +++ b/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp @@ -0,0 +1,173 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-11 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online 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.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +namespace MouseCursorHelpers +{ + extern NSImage* createNSImage (const Image&); +} + +class SystemTrayIconComponent::Pimpl +{ +public: + Pimpl (SystemTrayIconComponent& iconComp, const Image& im) + : owner (iconComp), statusItem (nil), + statusIcon (MouseCursorHelpers::createNSImage (im)) + { + static SystemTrayCallbackClass cls; + callback = [cls.createInstance() init]; + SystemTrayCallbackClass::setOwner (callback, this); + + statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain]; + + [statusItem setHighlightMode: YES]; + + setIconSize(); + [statusItem setImage: statusIcon]; + [statusItem setTarget: callback]; + [statusItem setAction: @selector (statusItemAction:)]; + } + + ~Pimpl() + { + [statusItem release]; + [statusIcon release]; + [callback release]; + } + + void updateIcon (const Image& newImage) + { + [statusIcon release]; + statusIcon = MouseCursorHelpers::createNSImage (newImage); + setIconSize(); + } + + void handleStatusItemAction (NSEvent* e) + { + NSEventType type = [e type]; + + const bool isLeft = (type == NSLeftMouseDown || type == NSLeftMouseUp); + const bool isRight = (type == NSRightMouseDown || type == NSRightMouseUp); + + if (owner.isCurrentlyBlockedByAnotherModalComponent()) + { + if (isLeft || isRight) + if (Component* const current = Component::getCurrentlyModalComponent()) + current->inputAttemptWhenModal(); + } + else + { + ModifierKeys eventMods (ModifierKeys::getCurrentModifiersRealtime()); + + if (([e modifierFlags] & NSCommandKeyMask) != 0) + eventMods = eventMods.withFlags (ModifierKeys::commandModifier); + + NSRect r = [[e window] frame]; + r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.origin.y - r.size.height; + owner.setBounds (convertToRectInt (r)); + + const Time now (Time::getCurrentTime()); + + if (isLeft || isRight) // Only mouse up is sent by the OS, so simulate a down/up + { + owner.mouseDown (MouseEvent (Desktop::getInstance().getMainMouseSource(), + Point(), + eventMods.withFlags (isLeft ? ModifierKeys::leftButtonModifier + : ModifierKeys::rightButtonModifier), + &owner, &owner, now, + Point(), now, 1, false)); + + owner.mouseUp (MouseEvent (Desktop::getInstance().getMainMouseSource(), + Point(), eventMods.withoutMouseButtons(), + &owner, &owner, now, + Point(), now, 1, false)); + } + else if (type == NSMouseMoved) + { + owner.mouseMove (MouseEvent (Desktop::getInstance().getMainMouseSource(), + Point(), eventMods, + &owner, &owner, now, + Point(), now, 1, false)); + } + } + } + +private: + SystemTrayIconComponent& owner; + NSStatusItem* statusItem; + NSImage* statusIcon; + NSObject* callback; + + void setIconSize() + { + [statusIcon setSize: NSMakeSize (20.0f, 20.0f)]; + } + + struct SystemTrayCallbackClass : public ObjCClass + { + SystemTrayCallbackClass() : ObjCClass ("JUCESystemTray_") + { + addIvar ("owner"); + addMethod (@selector (statusItemAction:), statusItemAction, "v@:@"); + + registerClass(); + } + + static void setOwner (id self, SystemTrayIconComponent::Pimpl* owner) + { + object_setInstanceVariable (self, "owner", owner); + } + + private: + static void statusItemAction (id self, SEL, id /*sender*/) + { + if (SystemTrayIconComponent::Pimpl* const owner = getIvar (self, "owner")) + owner->handleStatusItemAction ([NSApp currentEvent]); + } + }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) +}; + + +//============================================================================== +void SystemTrayIconComponent::setIconImage (const Image& newImage) +{ + if (newImage.isValid()) + { + if (pimpl == nullptr) + pimpl = new Pimpl (*this, newImage); + else + pimpl->updateIcon (newImage); + } + else + { + pimpl = nullptr; + } +} + +void SystemTrayIconComponent::setIconTooltip (const String& /* tooltip */) +{ + // xxx not yet implemented! +} diff --git a/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp b/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp index 206fa6cda6..7adde76b3a 100644 --- a/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp +++ b/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp @@ -55,10 +55,9 @@ public: // In order to receive the "TaskbarCreated" message, we need to request that it's not filtered out. // (Need to load dynamically, as ChangeWindowMessageFilter is only available in Vista and later) typedef BOOL (WINAPI* ChangeWindowMessageFilterType) (UINT, DWORD); - ChangeWindowMessageFilterType changeWindowMessageFilter - = (ChangeWindowMessageFilterType) getUser32Function ("ChangeWindowMessageFilter"); - if (changeWindowMessageFilter != nullptr) + if (ChangeWindowMessageFilterType changeWindowMessageFilter + = (ChangeWindowMessageFilterType) getUser32Function ("ChangeWindowMessageFilter")) changeWindowMessageFilter (taskbarCreatedMessage, 1 /* MSGFLT_ADD */); } @@ -96,9 +95,7 @@ public: if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK) { - Component* const current = Component::getCurrentlyModalComponent(); - - if (current != nullptr) + if (Component* const current = Component::getCurrentlyModalComponent()) current->inputAttemptWhenModal(); } } @@ -113,9 +110,11 @@ public: else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP) eventMods = eventMods.withoutMouseButtons(); + const Time eventTime (getMouseEventTime()); + const MouseEvent e (Desktop::getInstance().getMainMouseSource(), - Point(), eventMods, &owner, &owner, Time (getMouseEventTime()), - Point(), Time (getMouseEventTime()), 1, false); + Point(), eventMods, &owner, &owner, eventTime, + Point(), eventTime, 1, false); if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN) { @@ -141,17 +140,9 @@ public: static Pimpl* getPimpl (HWND hwnd) { if (JuceWindowIdentifier::isJUCEWindow (hwnd)) - { - ComponentPeer* peer = (ComponentPeer*) GetWindowLongPtr (hwnd, 8); - - if (peer != nullptr) - { - SystemTrayIconComponent* const iconComp = dynamic_cast (&(peer->getComponent())); - - if (iconComp != nullptr) + if (ComponentPeer* peer = (ComponentPeer*) GetWindowLongPtr (hwnd, 8)) + if (SystemTrayIconComponent* const iconComp = dynamic_cast (&(peer->getComponent()))) return iconComp->pimpl; - } - } return nullptr; }