|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2015 - ROLI 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.
-
- ==============================================================================
- */
-
- class JuceMainMenuHandler : private MenuBarModel::Listener,
- private DeletedAtShutdown
- {
- public:
- JuceMainMenuHandler()
- : currentModel (nullptr),
- lastUpdateTime (0),
- isOpen (false),
- defferedUpdateRequested (false)
- {
- static JuceMenuCallbackClass cls;
- callback = [cls.createInstance() init];
- JuceMenuCallbackClass::setOwner (callback, this);
- }
-
- ~JuceMainMenuHandler()
- {
- setMenu (nullptr, nullptr, String::empty);
-
- jassert (instance == this);
- instance = nullptr;
-
- [callback release];
- }
-
- void setMenu (MenuBarModel* const newMenuBarModel,
- const PopupMenu* newExtraAppleMenuItems,
- const String& recentItemsName)
- {
- recentItemsMenuName = recentItemsName;
-
- if (currentModel != newMenuBarModel)
- {
- if (currentModel != nullptr)
- currentModel->removeListener (this);
-
- currentModel = newMenuBarModel;
-
- if (currentModel != nullptr)
- currentModel->addListener (this);
-
- menuBarItemsChanged (nullptr);
- }
-
- extraAppleMenuItems = createCopyIfNotNull (newExtraAppleMenuItems);
- }
-
- void addTopLevelMenu (NSMenu* parent, const PopupMenu& child,
- const String& name, const int menuId, const int tag)
- {
- NSMenuItem* item = [parent addItemWithTitle: juceStringToNS (name)
- action: nil
- keyEquivalent: nsEmptyString()];
- [item setTag: tag];
-
- NSMenu* sub = createMenu (child, name, menuId, tag, true);
-
- [parent setSubmenu: sub forItem: item];
- [sub setAutoenablesItems: false];
- [sub release];
- }
-
- void updateTopLevelMenu (NSMenuItem* parentItem, const PopupMenu& menuToCopy,
- const String& name, const int menuId, const int tag)
- {
- // Note: This method used to update the contents of the existing menu in-place, but that caused
- // weird side-effects which messed-up keyboard focus when switching between windows. By creating
- // a new menu and replacing the old one with it, that problem seems to be avoided..
- NSMenu* menu = [[NSMenu alloc] initWithTitle: juceStringToNS (name)];
-
- for (PopupMenu::MenuItemIterator iter (menuToCopy); iter.next();)
- addMenuItem (iter, menu, menuId, tag);
-
- [menu setAutoenablesItems: false];
- [menu update];
- [parentItem setTag: tag];
- [parentItem setSubmenu: menu];
- [menu release];
- }
-
- void menuBarItemsChanged (MenuBarModel*)
- {
- if (isOpen)
- {
- defferedUpdateRequested = true;
- return;
- }
-
- lastUpdateTime = Time::getMillisecondCounter();
-
- StringArray menuNames;
- if (currentModel != nullptr)
- menuNames = currentModel->getMenuBarNames();
-
- NSMenu* menuBar = [[NSApp mainMenu] retain];
-
- while ([menuBar numberOfItems] > 1 + menuNames.size())
- [menuBar removeItemAtIndex: [menuBar numberOfItems] - 1];
-
- int menuId = 1;
-
- for (int i = 0; i < menuNames.size(); ++i)
- {
- const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i]));
-
- if (i >= [menuBar numberOfItems] - 1)
- addTopLevelMenu (menuBar, menu, menuNames[i], menuId, i);
- else
- updateTopLevelMenu ([menuBar itemAtIndex: 1 + i], menu, menuNames[i], menuId, i);
- }
-
- [menuBar release];
- }
-
- void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info)
- {
- if (NSMenuItem* item = findMenuItem ([NSApp mainMenu], info))
- flashMenuBar ([item menu]);
- }
-
- void updateMenus (NSMenu* menu)
- {
- if (PopupMenu::dismissAllActiveMenus())
- {
- // If we were running a juce menu, then we should let that modal loop finish before allowing
- // the OS menus to start their own modal loop - so cancel the menu that was being opened..
- if ([menu respondsToSelector: @selector (cancelTracking)])
- [menu performSelector: @selector (cancelTracking)];
- }
-
- if (Time::getMillisecondCounter() > lastUpdateTime + 100)
- (new AsyncMenuUpdater())->post();
- }
-
- void invoke (const int commandId, ApplicationCommandManager* const commandManager, const int topLevelIndex) const
- {
- if (currentModel != nullptr)
- {
- if (commandManager != nullptr)
- {
- ApplicationCommandTarget::InvocationInfo info (commandId);
- info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
-
- commandManager->invoke (info, true);
- }
-
- (new AsyncCommandInvoker (commandId, topLevelIndex))->post();
- }
- }
-
- void invokeDirectly (const int commandId, const int topLevelIndex)
- {
- if (currentModel != nullptr)
- currentModel->menuItemSelected (commandId, topLevelIndex);
- }
-
- void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo,
- const int topLevelMenuId, const int topLevelIndex)
- {
- NSString* text = juceStringToNS (iter.itemName.upToFirstOccurrenceOf ("<end>", false, true));
-
- if (text == nil)
- text = nsEmptyString();
-
- if (iter.isSeparator)
- {
- [menuToAddTo addItem: [NSMenuItem separatorItem]];
- }
- else if (iter.isSectionHeader)
- {
- NSMenuItem* item = [menuToAddTo addItemWithTitle: text
- action: nil
- keyEquivalent: nsEmptyString()];
-
- [item setEnabled: false];
- }
- else if (iter.subMenu != nullptr)
- {
- if (iter.itemName == recentItemsMenuName)
- {
- if (recent == nullptr)
- recent = new RecentFilesMenuItem();
-
- if (recent->recentItem != nil)
- {
- if (NSMenu* parent = [recent->recentItem menu])
- [parent removeItem: recent->recentItem];
-
- [menuToAddTo addItem: recent->recentItem];
- return;
- }
- }
-
- NSMenuItem* item = [menuToAddTo addItemWithTitle: text
- action: nil
- keyEquivalent: nsEmptyString()];
-
- [item setTag: iter.itemId];
- [item setEnabled: iter.isEnabled];
-
- NSMenu* sub = createMenu (*iter.subMenu, iter.itemName, topLevelMenuId, topLevelIndex, false);
- [menuToAddTo setSubmenu: sub forItem: item];
- [sub release];
- }
- else
- {
- NSMenuItem* item = [menuToAddTo addItemWithTitle: text
- action: @selector (menuItemInvoked:)
- keyEquivalent: nsEmptyString()];
-
- [item setTag: iter.itemId];
- [item setEnabled: iter.isEnabled];
- [item setState: iter.isTicked ? NSOnState : NSOffState];
- [item setTarget: (id) callback];
-
- NSMutableArray* info = [NSMutableArray arrayWithObject: [NSNumber numberWithUnsignedLongLong: (pointer_sized_uint) (void*) iter.commandManager]];
- [info addObject: [NSNumber numberWithInt: topLevelIndex]];
- [item setRepresentedObject: info];
-
- if (iter.commandManager != nullptr)
- {
- const Array<KeyPress> keyPresses (iter.commandManager->getKeyMappings()
- ->getKeyPressesAssignedToCommand (iter.itemId));
-
- if (keyPresses.size() > 0)
- {
- const KeyPress& kp = keyPresses.getReference(0);
-
- if (kp != KeyPress::backspaceKey // (adding these is annoying because it flashes the menu bar
- && kp != KeyPress::deleteKey) // every time you press the key while editing text)
- {
- juce_wchar key = kp.getTextCharacter();
- if (key == 0)
- key = (juce_wchar) kp.getKeyCode();
-
- [item setKeyEquivalent: juceStringToNS (String::charToString (key).toLowerCase())];
- [item setKeyEquivalentModifierMask: juceModsToNSMods (kp.getModifiers())];
- }
- }
- }
- }
- }
-
- NSMenu* createMenu (const PopupMenu menu,
- const String& menuName,
- const int topLevelMenuId,
- const int topLevelIndex,
- const bool addDelegate)
- {
- NSMenu* m = [[NSMenu alloc] initWithTitle: juceStringToNS (menuName)];
-
- [m setAutoenablesItems: false];
-
- if (addDelegate)
- {
- #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
- [m setDelegate: (id<NSMenuDelegate>) callback];
- #else
- [m setDelegate: callback];
- #endif
- }
-
- for (PopupMenu::MenuItemIterator iter (menu); iter.next();)
- addMenuItem (iter, m, topLevelMenuId, topLevelIndex);
-
- [m update];
- return m;
- }
-
- static JuceMainMenuHandler* instance;
-
- MenuBarModel* currentModel;
- ScopedPointer<PopupMenu> extraAppleMenuItems;
- uint32 lastUpdateTime;
- NSObject* callback;
- String recentItemsMenuName;
- bool isOpen, defferedUpdateRequested;
-
- private:
- struct RecentFilesMenuItem
- {
- RecentFilesMenuItem() : recentItem (nil)
- {
- if (NSNib* menuNib = [[[NSNib alloc] initWithNibNamed: @"RecentFilesMenuTemplate" bundle: nil] autorelease])
- {
- NSArray* array = nil;
-
- #if (! defined (MAC_OS_X_VERSION_10_8)) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
- [menuNib instantiateNibWithOwner: NSApp topLevelObjects: &array];
- #else
- [menuNib instantiateWithOwner: NSApp topLevelObjects: &array];
- #endif
-
- for (id object in array)
- {
- if ([object isKindOfClass: [NSMenu class]])
- {
- if (NSArray* items = [object itemArray])
- {
- if (NSMenuItem* item = findRecentFilesItem (items))
- {
- recentItem = [item retain];
- break;
- }
- }
- }
- }
- }
- }
-
- ~RecentFilesMenuItem()
- {
- [recentItem release];
- }
-
- static NSMenuItem* findRecentFilesItem (NSArray* const items)
- {
- for (id object in items)
- if (NSArray* subMenuItems = [[object submenu] itemArray])
- for (id subObject in subMenuItems)
- if ([subObject isKindOfClass: [NSMenuItem class]])
- return subObject;
- return nil;
- }
-
- NSMenuItem* recentItem;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RecentFilesMenuItem)
- };
-
- ScopedPointer<RecentFilesMenuItem> recent;
-
- //==============================================================================
- static NSMenuItem* findMenuItem (NSMenu* const menu, const ApplicationCommandTarget::InvocationInfo& info)
- {
- for (NSInteger i = [menu numberOfItems]; --i >= 0;)
- {
- NSMenuItem* m = [menu itemAtIndex: i];
- if ([m tag] == info.commandID)
- return m;
-
- if (NSMenu* sub = [m submenu])
- if (NSMenuItem* found = findMenuItem (sub, info))
- return found;
- }
-
- return nil;
- }
-
- static void flashMenuBar (NSMenu* menu)
- {
- if ([[menu title] isEqualToString: nsStringLiteral ("Apple")])
- return;
-
- [menu retain];
-
- const unichar f35Key = NSF35FunctionKey;
- NSString* f35String = [NSString stringWithCharacters: &f35Key length: 1];
-
- NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: nsStringLiteral ("x")
- action: nil
- keyEquivalent: f35String];
- [item setTarget: nil];
- [menu insertItem: item atIndex: [menu numberOfItems]];
- [item release];
-
- if ([menu indexOfItem: item] >= 0)
- {
- NSEvent* f35Event = [NSEvent keyEventWithType: NSKeyDown
- location: NSZeroPoint
- modifierFlags: NSCommandKeyMask
- timestamp: 0
- windowNumber: 0
- context: [NSGraphicsContext currentContext]
- characters: f35String
- charactersIgnoringModifiers: f35String
- isARepeat: NO
- keyCode: 0];
-
- [menu performKeyEquivalent: f35Event];
-
- if ([menu indexOfItem: item] >= 0)
- [menu removeItem: item]; // (this throws if the item isn't actually in the menu)
- }
-
- [menu release];
- }
-
- static unsigned int juceModsToNSMods (const ModifierKeys mods)
- {
- unsigned int m = 0;
- if (mods.isShiftDown()) m |= NSShiftKeyMask;
- if (mods.isCtrlDown()) m |= NSControlKeyMask;
- if (mods.isAltDown()) m |= NSAlternateKeyMask;
- if (mods.isCommandDown()) m |= NSCommandKeyMask;
- return m;
- }
-
- class AsyncMenuUpdater : public CallbackMessage
- {
- public:
- AsyncMenuUpdater() {}
-
- void messageCallback() override
- {
- if (instance != nullptr)
- instance->menuBarItemsChanged (nullptr);
- }
-
- private:
- JUCE_DECLARE_NON_COPYABLE (AsyncMenuUpdater)
- };
-
- class AsyncCommandInvoker : public CallbackMessage
- {
- public:
- AsyncCommandInvoker (const int commandId_, const int topLevelIndex_)
- : commandId (commandId_), topLevelIndex (topLevelIndex_)
- {}
-
- void messageCallback() override
- {
- if (instance != nullptr)
- instance->invokeDirectly (commandId, topLevelIndex);
- }
-
- private:
- const int commandId, topLevelIndex;
-
- JUCE_DECLARE_NON_COPYABLE (AsyncCommandInvoker)
- };
-
- //==============================================================================
- struct JuceMenuCallbackClass : public ObjCClass <NSObject>
- {
- JuceMenuCallbackClass() : ObjCClass <NSObject> ("JUCEMainMenu_")
- {
- addIvar<JuceMainMenuHandler*> ("owner");
-
- addMethod (@selector (menuItemInvoked:), menuItemInvoked, "v@:@");
- addMethod (@selector (menuNeedsUpdate:), menuNeedsUpdate, "v@:@");
-
- #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
- addProtocol (@protocol (NSMenuDelegate));
- #endif
-
- registerClass();
- }
-
- static void setOwner (id self, JuceMainMenuHandler* owner)
- {
- object_setInstanceVariable (self, "owner", owner);
- }
-
- private:
- static void menuItemInvoked (id self, SEL, NSMenuItem* item)
- {
- JuceMainMenuHandler* const owner = getIvar<JuceMainMenuHandler*> (self, "owner");
-
- if ([[item representedObject] isKindOfClass: [NSArray class]])
- {
- // If the menu is being triggered by a keypress, the OS will have picked it up before we had a chance to offer it to
- // our own components, which may have wanted to intercept it. So, rather than dispatching directly, we'll feed it back
- // into the focused component and let it trigger the menu item indirectly.
- NSEvent* e = [NSApp currentEvent];
- if ([e type] == NSKeyDown || [e type] == NSKeyUp)
- {
- if (juce::Component* focused = juce::Component::getCurrentlyFocusedComponent())
- {
- if (juce::NSViewComponentPeer* peer = dynamic_cast<juce::NSViewComponentPeer*> (focused->getPeer()))
- {
- if ([e type] == NSKeyDown)
- peer->redirectKeyDown (e);
- else
- peer->redirectKeyUp (e);
-
- return;
- }
- }
- }
-
- NSArray* info = (NSArray*) [item representedObject];
-
- owner->invoke ((int) [item tag],
- (ApplicationCommandManager*) (pointer_sized_int)
- [((NSNumber*) [info objectAtIndex: 0]) unsignedLongLongValue],
- (int) [((NSNumber*) [info objectAtIndex: 1]) intValue]);
- }
- }
-
- static void menuNeedsUpdate (id, SEL, NSMenu* menu)
- {
- if (instance != nullptr)
- instance->updateMenus (menu);
- }
- };
- };
-
- JuceMainMenuHandler* JuceMainMenuHandler::instance = nullptr;
-
- //==============================================================================
- class TemporaryMainMenuWithStandardCommands
- {
- public:
- TemporaryMainMenuWithStandardCommands()
- : oldMenu (MenuBarModel::getMacMainMenu()), oldAppleMenu (nullptr)
- {
- if (const PopupMenu* appleMenu = MenuBarModel::getMacExtraAppleItemsMenu())
- oldAppleMenu = new PopupMenu (*appleMenu);
-
- if (JuceMainMenuHandler::instance != nullptr)
- oldRecentItems = JuceMainMenuHandler::instance->recentItemsMenuName;
-
- MenuBarModel::setMacMainMenu (nullptr);
-
- NSMenu* menu = [[NSMenu alloc] initWithTitle: nsStringLiteral ("Edit")];
- NSMenuItem* item;
-
- item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (nsStringLiteral ("Cut"), nil)
- action: @selector (cut:) keyEquivalent: nsStringLiteral ("x")];
- [menu addItem: item];
- [item release];
-
- item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (nsStringLiteral ("Copy"), nil)
- action: @selector (copy:) keyEquivalent: nsStringLiteral ("c")];
- [menu addItem: item];
- [item release];
-
- item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (nsStringLiteral ("Paste"), nil)
- action: @selector (paste:) keyEquivalent: nsStringLiteral ("v")];
- [menu addItem: item];
- [item release];
-
- item = [[NSApp mainMenu] addItemWithTitle: NSLocalizedString (nsStringLiteral ("Edit"), nil)
- action: nil keyEquivalent: nsEmptyString()];
- [[NSApp mainMenu] setSubmenu: menu forItem: item];
- [menu release];
-
- // use a dummy modal component so that apps can tell that something is currently modal.
- dummyModalComponent.enterModalState();
- }
-
- ~TemporaryMainMenuWithStandardCommands()
- {
- MenuBarModel::setMacMainMenu (oldMenu, oldAppleMenu, oldRecentItems);
- }
-
- private:
- MenuBarModel* oldMenu;
- ScopedPointer<PopupMenu> oldAppleMenu;
- String oldRecentItems;
-
- // The OS view already plays an alert when clicking outside
- // the modal comp, so this override avoids adding extra
- // inappropriate noises when the cancel button is pressed.
- // This override is also important because it stops the base class
- // calling ModalComponentManager::bringToFront, which can get
- // recursive when file dialogs are involved
- class SilentDummyModalComp : public Component
- {
- public:
- SilentDummyModalComp() {}
- void inputAttemptWhenModal() override {}
- };
-
- SilentDummyModalComp dummyModalComponent;
- };
-
- //==============================================================================
- namespace MainMenuHelpers
- {
- static NSString* translateMenuName (const String& name)
- {
- return NSLocalizedString (juceStringToNS (TRANS (name)), nil);
- }
-
- static NSMenuItem* createMenuItem (NSMenu* menu, const String& name, SEL sel, NSString* key)
- {
- NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle: translateMenuName (name)
- action: sel
- keyEquivalent: key] autorelease];
- [item setTarget: NSApp];
- [menu addItem: item];
- return item;
- }
-
- static void createStandardAppMenu (NSMenu* menu, const String& appName, const PopupMenu* extraItems)
- {
- if (extraItems != nullptr && JuceMainMenuHandler::instance != nullptr && extraItems->getNumItems() > 0)
- {
- for (PopupMenu::MenuItemIterator iter (*extraItems); iter.next();)
- JuceMainMenuHandler::instance->addMenuItem (iter, menu, 0, -1);
-
- [menu addItem: [NSMenuItem separatorItem]];
- }
-
- // Services...
- NSMenuItem* services = [[[NSMenuItem alloc] initWithTitle: translateMenuName ("Services")
- action: nil keyEquivalent: nsEmptyString()] autorelease];
- [menu addItem: services];
-
- NSMenu* servicesMenu = [[[NSMenu alloc] initWithTitle: translateMenuName ("Services")] autorelease];
- [menu setSubmenu: servicesMenu forItem: services];
- [NSApp setServicesMenu: servicesMenu];
- [menu addItem: [NSMenuItem separatorItem]];
-
- createMenuItem (menu, "Hide " + appName, @selector (hide:), nsStringLiteral ("h"));
-
- [createMenuItem (menu, "Hide Others", @selector (hideOtherApplications:), nsStringLiteral ("h"))
- setKeyEquivalentModifierMask: NSCommandKeyMask | NSAlternateKeyMask];
-
- createMenuItem (menu, "Show All", @selector (unhideAllApplications:), nsEmptyString());
-
- [menu addItem: [NSMenuItem separatorItem]];
-
- createMenuItem (menu, "Quit " + appName, @selector (terminate:), nsStringLiteral ("q"));
- }
-
- // Since our app has no NIB, this initialises a standard app menu...
- static void rebuildMainMenu (const PopupMenu* extraItems)
- {
- // this can't be used in a plugin!
- jassert (JUCEApplicationBase::isStandaloneApp());
-
- if (JUCEApplicationBase* app = JUCEApplicationBase::getInstance())
- {
- JUCE_AUTORELEASEPOOL
- {
- NSMenu* mainMenu = [[NSMenu alloc] initWithTitle: nsStringLiteral ("MainMenu")];
- NSMenuItem* item = [mainMenu addItemWithTitle: nsStringLiteral ("Apple") action: nil keyEquivalent: nsEmptyString()];
-
- NSMenu* appMenu = [[NSMenu alloc] initWithTitle: nsStringLiteral ("Apple")];
-
- [NSApp performSelector: @selector (setAppleMenu:) withObject: appMenu];
- [mainMenu setSubmenu: appMenu forItem: item];
-
- [NSApp setMainMenu: mainMenu];
- MainMenuHelpers::createStandardAppMenu (appMenu, app->getApplicationName(), extraItems);
-
- [appMenu release];
- [mainMenu release];
- }
- }
- }
- }
-
- void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel,
- const PopupMenu* extraAppleMenuItems,
- const String& recentItemsMenuName)
- {
- if (getMacMainMenu() != newMenuBarModel)
- {
- JUCE_AUTORELEASEPOOL
- {
- if (newMenuBarModel == nullptr)
- {
- delete JuceMainMenuHandler::instance;
- jassert (JuceMainMenuHandler::instance == nullptr); // should be zeroed in the destructor
- jassert (extraAppleMenuItems == nullptr); // you can't specify some extra items without also supplying a model
-
- extraAppleMenuItems = nullptr;
- }
- else
- {
- if (JuceMainMenuHandler::instance == nullptr)
- JuceMainMenuHandler::instance = new JuceMainMenuHandler();
-
- JuceMainMenuHandler::instance->setMenu (newMenuBarModel, extraAppleMenuItems, recentItemsMenuName);
- }
- }
- }
-
- MainMenuHelpers::rebuildMainMenu (extraAppleMenuItems);
-
- if (newMenuBarModel != nullptr)
- newMenuBarModel->menuItemsChanged();
- }
-
- MenuBarModel* MenuBarModel::getMacMainMenu()
- {
- if (JuceMainMenuHandler* mm = JuceMainMenuHandler::instance)
- return mm->currentModel;
-
- return nullptr;
- }
-
- const PopupMenu* MenuBarModel::getMacExtraAppleItemsMenu()
- {
- if (JuceMainMenuHandler* mm = JuceMainMenuHandler::instance)
- return mm->extraAppleMenuItems.get();
-
- return nullptr;
- }
-
- typedef void (*MenuTrackingChangedCallback) (bool);
- extern MenuTrackingChangedCallback menuTrackingChangedCallback;
-
- static void mainMenuTrackingChanged (bool isTracking)
- {
- PopupMenu::dismissAllActiveMenus();
-
- if (JuceMainMenuHandler* menuHandler = JuceMainMenuHandler::instance)
- {
- menuHandler->isOpen = isTracking;
-
- if (menuHandler->defferedUpdateRequested && ! isTracking)
- {
- menuHandler->defferedUpdateRequested = false;
- menuHandler->menuBarItemsChanged (menuHandler->currentModel);
- }
- }
- }
-
- void juce_initialiseMacMainMenu()
- {
- menuTrackingChangedCallback = mainMenuTrackingChanged;
-
- if (JuceMainMenuHandler::instance == nullptr)
- MainMenuHelpers::rebuildMainMenu (nullptr);
- }
-
- // (used from other modules that need to create an NSMenu)
- NSMenu* createNSMenu (const PopupMenu& menu, const String& name,
- int topLevelMenuId, int topLevelIndex, bool addDelegate)
- {
- juce_initialiseMacMainMenu();
-
- if (JuceMainMenuHandler* mm = JuceMainMenuHandler::instance)
- return mm->createMenu (menu, name, topLevelMenuId, topLevelIndex, addDelegate);
-
- jassertfalse; // calling this before making sure the OSX main menu stuff was initialised?
- return nil;
- }
|