diff --git a/build/macosx/platform_specific_code/juce_mac_MainMenu.mm b/build/macosx/platform_specific_code/juce_mac_MainMenu.mm index 9ffed2be35..3ce120cd2d 100644 --- a/build/macosx/platform_specific_code/juce_mac_MainMenu.mm +++ b/build/macosx/platform_specific_code/juce_mac_MainMenu.mm @@ -49,7 +49,7 @@ using namespace JUCE_NAMESPACE; - (JuceMenuCallback*) initWithOwner: (JuceMainMenuHandler*) owner_; - (void) dealloc; - (void) menuItemInvoked: (id) menu; - +- (void) menuNeedsUpdate: (NSMenu*) menu; @end BEGIN_JUCE_NAMESPACE @@ -63,7 +63,8 @@ public: //============================================================================== JuceMainMenuHandler() throw() - : currentModel (0) + : currentModel (0), + lastUpdateTime (0) { callback = [[JuceMenuCallback alloc] initWithOwner: this]; } @@ -95,7 +96,7 @@ public: } void addSubMenu (NSMenu* parent, const PopupMenu& child, - const String& name, const int menuId, int& tag) + const String& name, const int menuId, const int tag) { NSMenuItem* item = [parent addItemWithTitle: juceStringToNS (name) action: nil @@ -109,22 +110,48 @@ public: [sub release]; } + void updateSubMenu (NSMenuItem* parentItem, const PopupMenu& menuToCopy, + const String& name, const int menuId, const int tag) + { + [parentItem setTag: tag]; + NSMenu* menu = [parentItem submenu]; + + [menu setTitle: juceStringToNS (name)]; + + while ([menu numberOfItems] > 0) + [menu removeItemAtIndex: 0]; + + PopupMenu::MenuItemIterator iter (menuToCopy); + + while (iter.next()) + addMenuItem (iter, menu, menuId, tag); + + [menu setAutoenablesItems: false]; + [menu update]; + } + void menuBarItemsChanged (MenuBarModel*) { - NSMenu* menuBar = [NSApp mainMenu]; - while ([menuBar numberOfItems] > 1) - [menuBar removeItemAtIndex: 1]; + lastUpdateTime = Time::getMillisecondCounter(); + StringArray menuNames; if (currentModel != 0) + menuNames = currentModel->getMenuBarNames(); + + NSMenu* menuBar = [NSApp mainMenu]; + while ([menuBar numberOfItems] > 1 + menuNames.size()) + [menuBar removeItemAtIndex: [menuBar numberOfItems] - 1]; + + int menuId = 1; + + for (int i = 0; i < menuNames.size(); ++i) { - const StringArray menuNames (currentModel->getMenuBarNames()); - int menuId = 1; + const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); - for (int i = 0; i < menuNames.size(); ++i) - { - const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); - addSubMenu (menuBar, menu, menuNames [i], menuId, i); - } + if (i >= [menuBar numberOfItems] - 1) + addSubMenu (menuBar, menu, menuNames[i], menuId, i); + else + updateSubMenu ([menuBar itemAtIndex: 1 + i], menu, menuNames[i], menuId, i); } } @@ -163,6 +190,12 @@ public: flashMenuBar ([item menu]); } + void updateMenus() + { + if (Time::getMillisecondCounter() > lastUpdateTime + 500) + menuBarItemsChanged (0); + } + void invoke (const int commandId, ApplicationCommandManager* const commandManager, const int topLevelIndex) const { if (currentModel != 0) @@ -180,6 +213,7 @@ public: } MenuBarModel* currentModel; + uint32 lastUpdateTime; void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, const int topLevelMenuId, const int topLevelIndex) @@ -199,7 +233,8 @@ public: action: nil keyEquivalent: @""]; - [item setEnabled: iter.isEnabled]; + [item setEnabled: false]; + [item setIndentationLevel: 5]; } else if (iter.subMenu != 0) { @@ -264,8 +299,8 @@ public: } } -private: JuceMenuCallback* callback; +private: NSMenu* createMenu (const PopupMenu menu, const String& menuName, @@ -275,6 +310,7 @@ private: NSMenu* m = [[NSMenu alloc] initWithTitle: juceStringToNS (menuName)]; [m setAutoenablesItems: false]; + [m setDelegate: callback]; PopupMenu::MenuItemIterator iter (menu); @@ -318,8 +354,13 @@ END_JUCE_NAMESPACE } } -@end +- (void) menuNeedsUpdate: (NSMenu*) menu; +{ + if (JuceMainMenuHandler::instance != 0) + JuceMainMenuHandler::instance->updateMenus(); +} +@end BEGIN_JUCE_NAMESPACE //============================================================================== diff --git a/build/macosx/platform_specific_code/juce_mac_MiscUtilities.mm b/build/macosx/platform_specific_code/juce_mac_MiscUtilities.mm index ed40023475..2bd976b83c 100644 --- a/build/macosx/platform_specific_code/juce_mac_MiscUtilities.mm +++ b/build/macosx/platform_specific_code/juce_mac_MiscUtilities.mm @@ -1,244 +1,244 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - 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. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -// (This file gets included by juce_mac_NativeCode.mm, rather than being -// compiled on its own). -#ifdef JUCE_INCLUDED_FILE - - -//============================================================================== -ScopedAutoReleasePool::ScopedAutoReleasePool() -{ - pool = [[NSAutoreleasePool alloc] init]; -} - -ScopedAutoReleasePool::~ScopedAutoReleasePool() -{ - [((NSAutoreleasePool*) pool) release]; -} - -//============================================================================== -void PlatformUtilities::beep() -{ - NSBeep(); -} - -//============================================================================== -bool AlertWindow::showNativeDialogBox (const String& title, - const String& bodyText, - bool isOkCancel) -{ - const ScopedAutoReleasePool pool; - return NSRunAlertPanel (juceStringToNS (title), - juceStringToNS (bodyText), - @"Ok", - isOkCancel ? @"Cancel" : nil, - nil) == 0; -} - -//============================================================================== -bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) -{ - if (files.size() == 0) - return false; - - Component* sourceComp = Component::getComponentUnderMouse(); - - if (sourceComp == 0) - { - jassertfalse // this method must be called in response to a - // component's mouseDrag event! - return false; - } - - const ScopedAutoReleasePool pool; - - NSView* view = (NSView*) sourceComp->getWindowHandle(); - - if (view == 0) - 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() throw() -{ - return true; -} - -void Desktop::getMousePosition (int& x, int& y) throw() -{ - const ScopedAutoReleasePool pool; - const NSPoint p ([NSEvent mouseLocation]); - x = roundFloatToInt (p.x); - y = roundFloatToInt ([[[NSScreen screens] objectAtIndex: 0] frame].size.height - p.y); -} - -void Desktop::setMousePosition (int x, int y) throw() -{ - // this rubbish needs to be done around the warp call, to avoid causing a - // bizarre glitch.. - CGAssociateMouseAndMouseCursorPosition (false); - CGSetLocalEventsSuppressionInterval (0); - - CGPoint pos = { x, y }; - CGWarpMouseCursorPosition (pos); - - CGAssociateMouseAndMouseCursorPosition (true); -} - -//============================================================================== -#if MACOS_10_4_OR_EARLIER -class ScreenSaverDefeater : public Timer, - public DeletedAtShutdown -{ -public: - ScreenSaverDefeater() throw() - { - startTimer (10000); - timerCallback(); - } - - ~ScreenSaverDefeater() {} - - void timerCallback() - { - if (Process::isForegroundProcess()) - UpdateSystemActivity (UsrActivity); - } -}; - -static ScreenSaverDefeater* screenSaverDefeater = 0; - -void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() -{ - if (isEnabled) - { - deleteAndZero (screenSaverDefeater); - } - else if (screenSaverDefeater == 0) - { - screenSaverDefeater = new ScreenSaverDefeater(); - } -} - -bool Desktop::isScreenSaverEnabled() throw() -{ - return screenSaverDefeater == 0; -} - -#else -//============================================================================== -static IOPMAssertionID screenSaverDisablerID = 0; - -void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() -{ - if (isEnabled) - { - if (screenSaverDisablerID != 0) - { - IOPMAssertionRelease (screenSaverDisablerID); - screenSaverDisablerID = 0; - } - } - else - { - if (screenSaverDisablerID == 0) - { - IOPMAssertionCreate (kIOPMAssertionTypeNoIdleSleep, - kIOPMAssertionLevelOn, &screenSaverDisablerID); - } - } -} - -bool Desktop::isScreenSaverEnabled() throw() -{ - return screenSaverDisablerID == 0; -} - -#endif - -//============================================================================== -void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() -{ - const ScopedAutoReleasePool pool; - monitorCoords.clear(); - NSArray* screens = [NSScreen screens]; - const float mainScreenBottom = [[[NSScreen screens] objectAtIndex: 0] frame].size.height; - - for (unsigned int i = 0; i < [screens count]; ++i) - { - NSScreen* s = (NSScreen*) [screens objectAtIndex: i]; - - NSRect r = clipToWorkArea ? [s visibleFrame] - : [s frame]; - - monitorCoords.add (Rectangle ((int) r.origin.x, - (int) (mainScreenBottom - (r.origin.y + r.size.height)), - (int) r.size.width, - (int) r.size.height)); - } - - jassert (monitorCoords.size() > 0); -} - -#endif +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + + +//============================================================================== +ScopedAutoReleasePool::ScopedAutoReleasePool() +{ + pool = [[NSAutoreleasePool alloc] init]; +} + +ScopedAutoReleasePool::~ScopedAutoReleasePool() +{ + [((NSAutoreleasePool*) pool) release]; +} + +//============================================================================== +void PlatformUtilities::beep() +{ + NSBeep(); +} + +//============================================================================== +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + const ScopedAutoReleasePool pool; + return NSRunAlertPanel (juceStringToNS (title), + juceStringToNS (bodyText), + @"Ok", + isOkCancel ? @"Cancel" : nil, + nil) == 0; +} + +//============================================================================== +bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) +{ + if (files.size() == 0) + return false; + + Component* sourceComp = Component::getComponentUnderMouse(); + + if (sourceComp == 0) + { + jassertfalse // this method must be called in response to a + // component's mouseDrag event! + return false; + } + + const ScopedAutoReleasePool pool; + + NSView* view = (NSView*) sourceComp->getWindowHandle(); + + if (view == 0) + 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() throw() +{ + return true; +} + +void Desktop::getMousePosition (int& x, int& y) throw() +{ + const ScopedAutoReleasePool pool; + const NSPoint p ([NSEvent mouseLocation]); + x = roundFloatToInt (p.x); + y = roundFloatToInt ([[[NSScreen screens] objectAtIndex: 0] frame].size.height - p.y); +} + +void Desktop::setMousePosition (int x, int y) throw() +{ + // this rubbish needs to be done around the warp call, to avoid causing a + // bizarre glitch.. + CGAssociateMouseAndMouseCursorPosition (false); + CGSetLocalEventsSuppressionInterval (0); + + CGPoint pos = { x, y }; + CGWarpMouseCursorPosition (pos); + + CGAssociateMouseAndMouseCursorPosition (true); +} + +//============================================================================== +#if MACOS_10_4_OR_EARLIER +class ScreenSaverDefeater : public Timer, + public DeletedAtShutdown +{ +public: + ScreenSaverDefeater() throw() + { + startTimer (10000); + timerCallback(); + } + + ~ScreenSaverDefeater() {} + + void timerCallback() + { + if (Process::isForegroundProcess()) + UpdateSystemActivity (UsrActivity); + } +}; + +static ScreenSaverDefeater* screenSaverDefeater = 0; + +void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() +{ + if (isEnabled) + { + deleteAndZero (screenSaverDefeater); + } + else if (screenSaverDefeater == 0) + { + screenSaverDefeater = new ScreenSaverDefeater(); + } +} + +bool Desktop::isScreenSaverEnabled() throw() +{ + return screenSaverDefeater == 0; +} + +#else +//============================================================================== +static IOPMAssertionID screenSaverDisablerID = 0; + +void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() +{ + if (isEnabled) + { + if (screenSaverDisablerID != 0) + { + IOPMAssertionRelease (screenSaverDisablerID); + screenSaverDisablerID = 0; + } + } + else + { + if (screenSaverDisablerID == 0) + { + IOPMAssertionCreate (kIOPMAssertionTypeNoIdleSleep, + kIOPMAssertionLevelOn, &screenSaverDisablerID); + } + } +} + +bool Desktop::isScreenSaverEnabled() throw() +{ + return screenSaverDisablerID == 0; +} + +#endif + +//============================================================================== +void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() +{ + const ScopedAutoReleasePool pool; + monitorCoords.clear(); + NSArray* screens = [NSScreen screens]; + const float mainScreenBottom = [[[NSScreen screens] objectAtIndex: 0] frame].size.height; + + for (unsigned int i = 0; i < [screens count]; ++i) + { + NSScreen* s = (NSScreen*) [screens objectAtIndex: i]; + + NSRect r = clipToWorkArea ? [s visibleFrame] + : [s frame]; + + monitorCoords.add (Rectangle ((int) r.origin.x, + (int) (mainScreenBottom - (r.origin.y + r.size.height)), + (int) r.size.width, + (int) r.size.height)); + } + + jassert (monitorCoords.size() > 0); +} + +#endif diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 6f11fd6328..9fc1a5f21a 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -268045,7 +268045,7 @@ using namespace JUCE_NAMESPACE; - (JuceMenuCallback*) initWithOwner: (JuceMainMenuHandler*) owner_; - (void) dealloc; - (void) menuItemInvoked: (id) menu; - +- (void) menuNeedsUpdate: (NSMenu*) menu; @end BEGIN_JUCE_NAMESPACE @@ -268056,7 +268056,8 @@ public: static JuceMainMenuHandler* instance; JuceMainMenuHandler() throw() - : currentModel (0) + : currentModel (0), + lastUpdateTime (0) { callback = [[JuceMenuCallback alloc] initWithOwner: this]; } @@ -268088,7 +268089,7 @@ public: } void addSubMenu (NSMenu* parent, const PopupMenu& child, - const String& name, const int menuId, int& tag) + const String& name, const int menuId, const int tag) { NSMenuItem* item = [parent addItemWithTitle: juceStringToNS (name) action: nil @@ -268102,22 +268103,48 @@ public: [sub release]; } + void updateSubMenu (NSMenuItem* parentItem, const PopupMenu& menuToCopy, + const String& name, const int menuId, const int tag) + { + [parentItem setTag: tag]; + NSMenu* menu = [parentItem submenu]; + + [menu setTitle: juceStringToNS (name)]; + + while ([menu numberOfItems] > 0) + [menu removeItemAtIndex: 0]; + + PopupMenu::MenuItemIterator iter (menuToCopy); + + while (iter.next()) + addMenuItem (iter, menu, menuId, tag); + + [menu setAutoenablesItems: false]; + [menu update]; + } + void menuBarItemsChanged (MenuBarModel*) { - NSMenu* menuBar = [NSApp mainMenu]; - while ([menuBar numberOfItems] > 1) - [menuBar removeItemAtIndex: 1]; + lastUpdateTime = Time::getMillisecondCounter(); + StringArray menuNames; if (currentModel != 0) + menuNames = currentModel->getMenuBarNames(); + + NSMenu* menuBar = [NSApp mainMenu]; + while ([menuBar numberOfItems] > 1 + menuNames.size()) + [menuBar removeItemAtIndex: [menuBar numberOfItems] - 1]; + + int menuId = 1; + + for (int i = 0; i < menuNames.size(); ++i) { - const StringArray menuNames (currentModel->getMenuBarNames()); - int menuId = 1; + const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); - for (int i = 0; i < menuNames.size(); ++i) - { - const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); - addSubMenu (menuBar, menu, menuNames [i], menuId, i); - } + if (i >= [menuBar numberOfItems] - 1) + addSubMenu (menuBar, menu, menuNames[i], menuId, i); + else + updateSubMenu ([menuBar itemAtIndex: 1 + i], menu, menuNames[i], menuId, i); } } @@ -268156,6 +268183,12 @@ public: flashMenuBar ([item menu]); } + void updateMenus() + { + if (Time::getMillisecondCounter() > lastUpdateTime + 500) + menuBarItemsChanged (0); + } + void invoke (const int commandId, ApplicationCommandManager* const commandManager, const int topLevelIndex) const { if (currentModel != 0) @@ -268173,6 +268206,7 @@ public: } MenuBarModel* currentModel; + uint32 lastUpdateTime; void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, const int topLevelMenuId, const int topLevelIndex) @@ -268192,7 +268226,8 @@ public: action: nil keyEquivalent: @""]; - [item setEnabled: iter.isEnabled]; + [item setEnabled: false]; + [item setIndentationLevel: 5]; } else if (iter.subMenu != 0) { @@ -268257,8 +268292,8 @@ public: } } -private: JuceMenuCallback* callback; +private: NSMenu* createMenu (const PopupMenu menu, const String& menuName, @@ -268268,6 +268303,7 @@ private: NSMenu* m = [[NSMenu alloc] initWithTitle: juceStringToNS (menuName)]; [m setAutoenablesItems: false]; + [m setDelegate: callback]; PopupMenu::MenuItemIterator iter (menu); @@ -268311,8 +268347,13 @@ END_JUCE_NAMESPACE } } -@end +- (void) menuNeedsUpdate: (NSMenu*) menu; +{ + if (JuceMainMenuHandler::instance != 0) + JuceMainMenuHandler::instance->updateMenus(); +} +@end BEGIN_JUCE_NAMESPACE static NSMenu* createStandardAppMenu (NSMenu* menu, const String& appName,