The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. // (This file gets included by juce_mac_NativeCode.mm, rather than being
  24. // compiled on its own).
  25. #ifdef JUCE_INCLUDED_FILE
  26. //==============================================================================
  27. class JuceMainMenuHandler;
  28. END_JUCE_NAMESPACE
  29. using namespace JUCE_NAMESPACE;
  30. #define JuceMenuCallback MakeObjCClassName(JuceMenuCallback)
  31. @interface JuceMenuCallback : NSObject
  32. {
  33. JuceMainMenuHandler* owner;
  34. }
  35. - (JuceMenuCallback*) initWithOwner: (JuceMainMenuHandler*) owner_;
  36. - (void) dealloc;
  37. - (void) menuItemInvoked: (id) menu;
  38. @end
  39. BEGIN_JUCE_NAMESPACE
  40. //==============================================================================
  41. class JuceMainMenuHandler : private MenuBarModelListener,
  42. private DeletedAtShutdown
  43. {
  44. public:
  45. static JuceMainMenuHandler* instance;
  46. //==============================================================================
  47. JuceMainMenuHandler() throw()
  48. : currentModel (0)
  49. {
  50. callback = [[JuceMenuCallback alloc] initWithOwner: this];
  51. }
  52. ~JuceMainMenuHandler() throw()
  53. {
  54. setMenu (0);
  55. jassert (instance == this);
  56. instance = 0;
  57. [callback release];
  58. }
  59. void setMenu (MenuBarModel* const newMenuBarModel) throw()
  60. {
  61. if (currentModel != newMenuBarModel)
  62. {
  63. if (currentModel != 0)
  64. currentModel->removeListener (this);
  65. currentModel = newMenuBarModel;
  66. if (currentModel != 0)
  67. currentModel->addListener (this);
  68. menuBarItemsChanged (0);
  69. }
  70. }
  71. void addSubMenu (NSMenu* parent, const PopupMenu& child,
  72. const String& name, const int menuId, int& tag)
  73. {
  74. NSMenuItem* item = [parent addItemWithTitle: juceStringToNS (name)
  75. action: nil
  76. keyEquivalent: @""];
  77. [item setTag: tag];
  78. NSMenu* sub = createMenu (child, name, menuId, tag);
  79. [parent setSubmenu: sub forItem: item];
  80. [sub setAutoenablesItems: false];
  81. [sub release];
  82. }
  83. void menuBarItemsChanged (MenuBarModel*)
  84. {
  85. NSMenu* menuBar = [NSApp mainMenu];
  86. while ([menuBar numberOfItems] > 1)
  87. [menuBar removeItemAtIndex: 1];
  88. if (currentModel != 0)
  89. {
  90. const StringArray menuNames (currentModel->getMenuBarNames());
  91. int menuId = 1;
  92. for (int i = 0; i < menuNames.size(); ++i)
  93. {
  94. const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i]));
  95. addSubMenu (menuBar, menu, menuNames [i], menuId, i);
  96. }
  97. }
  98. }
  99. static void flashMenuBar (NSMenu* menu)
  100. {
  101. const unichar f35Key = NSF35FunctionKey;
  102. NSString* f35String = [NSString stringWithCharacters: &f35Key length: 1];
  103. NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"x"
  104. action: nil
  105. keyEquivalent: f35String];
  106. [item setTarget: nil];
  107. [menu insertItem: item atIndex: [menu numberOfItems]];
  108. [item release];
  109. NSEvent* f35Event = [NSEvent keyEventWithType: NSKeyDown
  110. location: NSZeroPoint
  111. modifierFlags: NSCommandKeyMask
  112. timestamp: 0
  113. windowNumber: 0
  114. context: [NSGraphicsContext currentContext]
  115. characters: f35String
  116. charactersIgnoringModifiers: f35String
  117. isARepeat: NO
  118. keyCode: 0];
  119. [menu performKeyEquivalent: f35Event];
  120. [menu removeItem: item];
  121. }
  122. void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info)
  123. {
  124. NSMenuItem* item = [[NSApp mainMenu] itemWithTag: info.commandID];
  125. if (item != 0)
  126. flashMenuBar ([item menu]);
  127. }
  128. void invoke (const int commandId, ApplicationCommandManager* const commandManager, const int topLevelIndex) const
  129. {
  130. if (currentModel != 0)
  131. {
  132. if (commandManager != 0)
  133. {
  134. ApplicationCommandTarget::InvocationInfo info (commandId);
  135. info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
  136. commandManager->invoke (info, true);
  137. }
  138. currentModel->menuItemSelected (commandId, topLevelIndex);
  139. }
  140. }
  141. MenuBarModel* currentModel;
  142. void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo,
  143. const int topLevelMenuId, const int topLevelIndex)
  144. {
  145. NSString* text = juceStringToNS (iter.itemName.upToFirstOccurrenceOf (T("<end>"), false, true));
  146. if (text == 0)
  147. text = @"";
  148. if (iter.isSeparator)
  149. {
  150. [menuToAddTo addItem: [NSMenuItem separatorItem]];
  151. }
  152. else if (iter.isSectionHeader)
  153. {
  154. NSMenuItem* item = [menuToAddTo addItemWithTitle: text
  155. action: nil
  156. keyEquivalent: @""];
  157. [item setEnabled: iter.isEnabled];
  158. }
  159. else if (iter.subMenu != 0)
  160. {
  161. NSMenuItem* item = [menuToAddTo addItemWithTitle: text
  162. action: nil
  163. keyEquivalent: @""];
  164. [item setTag: iter.itemId];
  165. [item setEnabled: iter.isEnabled];
  166. NSMenu* sub = createMenu (*iter.subMenu, iter.itemName, topLevelMenuId, topLevelIndex);
  167. [menuToAddTo setSubmenu: sub forItem: item];
  168. [sub release];
  169. }
  170. else
  171. {
  172. NSMenuItem* item = [menuToAddTo addItemWithTitle: text
  173. action: @selector (menuItemInvoked:)
  174. keyEquivalent: @""];
  175. [item setTag: iter.itemId];
  176. [item setEnabled: iter.isEnabled];
  177. [item setState: iter.isTicked ? NSOnState : NSOffState];
  178. [item setTarget: (id) callback];
  179. NSMutableArray* info = [NSMutableArray arrayWithObject: [NSNumber numberWithUnsignedLongLong: (pointer_sized_int) (void*) iter.commandManager]];
  180. [info addObject: [NSNumber numberWithInt: topLevelIndex]];
  181. [item setRepresentedObject: info];
  182. if (iter.commandManager != 0)
  183. {
  184. const Array <KeyPress> keyPresses (iter.commandManager->getKeyMappings()
  185. ->getKeyPressesAssignedToCommand (iter.itemId));
  186. if (keyPresses.size() > 0)
  187. {
  188. const KeyPress& kp = keyPresses.getReference(0);
  189. juce_wchar key = kp.getTextCharacter();
  190. if (kp.getKeyCode() == KeyPress::backspaceKey)
  191. key = NSBackspaceCharacter;
  192. else if (kp.getKeyCode() == KeyPress::deleteKey)
  193. key = NSDeleteCharacter;
  194. else if (key == 0)
  195. key = (juce_wchar) kp.getKeyCode();
  196. unsigned int mods = 0;
  197. if (kp.getModifiers().isShiftDown())
  198. mods |= NSShiftKeyMask;
  199. if (kp.getModifiers().isCtrlDown())
  200. mods |= NSControlKeyMask;
  201. if (kp.getModifiers().isAltDown())
  202. mods |= NSAlternateKeyMask;
  203. if (kp.getModifiers().isCommandDown())
  204. mods |= NSCommandKeyMask;
  205. [item setKeyEquivalent: juceStringToNS (String::charToString (key))];
  206. [item setKeyEquivalentModifierMask: mods];
  207. }
  208. }
  209. }
  210. }
  211. private:
  212. JuceMenuCallback* callback;
  213. NSMenu* createMenu (const PopupMenu menu,
  214. const String& menuName,
  215. const int topLevelMenuId,
  216. const int topLevelIndex)
  217. {
  218. NSMenu* m = [[NSMenu alloc] initWithTitle: juceStringToNS (menuName)];
  219. [m setAutoenablesItems: false];
  220. PopupMenu::MenuItemIterator iter (menu);
  221. while (iter.next())
  222. addMenuItem (iter, m, topLevelMenuId, topLevelIndex);
  223. [m update];
  224. return m;
  225. }
  226. };
  227. JuceMainMenuHandler* JuceMainMenuHandler::instance = 0;
  228. END_JUCE_NAMESPACE
  229. @implementation JuceMenuCallback
  230. - (JuceMenuCallback*) initWithOwner: (JuceMainMenuHandler*) owner_
  231. {
  232. [super init];
  233. owner = owner_;
  234. return self;
  235. }
  236. - (void) dealloc
  237. {
  238. [super dealloc];
  239. }
  240. - (void) menuItemInvoked: (id) menu
  241. {
  242. NSMenuItem* item = (NSMenuItem*) menu;
  243. if ([[item representedObject] isKindOfClass: [NSArray class]])
  244. {
  245. NSArray* info = (NSArray*) [item representedObject];
  246. owner->invoke ([item tag],
  247. (ApplicationCommandManager*) (pointer_sized_int)
  248. [((NSNumber*) [info objectAtIndex: 0]) unsignedLongLongValue],
  249. (int) [((NSNumber*) [info objectAtIndex: 1]) intValue]);
  250. }
  251. }
  252. @end
  253. BEGIN_JUCE_NAMESPACE
  254. //==============================================================================
  255. static NSMenu* createStandardAppMenu (NSMenu* menu, const String& appName,
  256. const PopupMenu* extraItems)
  257. {
  258. if (extraItems != 0 && JuceMainMenuHandler::instance != 0 && extraItems->getNumItems() > 0)
  259. {
  260. PopupMenu::MenuItemIterator iter (*extraItems);
  261. while (iter.next())
  262. JuceMainMenuHandler::instance->addMenuItem (iter, menu, 0, -1);
  263. [menu addItem: [NSMenuItem separatorItem]];
  264. }
  265. NSMenuItem* item;
  266. // Services...
  267. item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (@"Services", nil)
  268. action: nil keyEquivalent: @""];
  269. [menu addItem: item];
  270. [item release];
  271. NSMenu* servicesMenu = [[NSMenu alloc] initWithTitle: @"Services"];
  272. [menu setSubmenu: servicesMenu forItem: item];
  273. [NSApp setServicesMenu: servicesMenu];
  274. [servicesMenu release];
  275. [menu addItem: [NSMenuItem separatorItem]];
  276. // Hide + Show stuff...
  277. item = [[NSMenuItem alloc] initWithTitle: juceStringToNS ("Hide " + appName)
  278. action: @selector (hide:) keyEquivalent: @"h"];
  279. [item setTarget: NSApp];
  280. [menu addItem: item];
  281. [item release];
  282. item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (@"Hide Others", nil)
  283. action: @selector (hideOtherApplications:) keyEquivalent: @"h"];
  284. [item setKeyEquivalentModifierMask: NSCommandKeyMask | NSAlternateKeyMask];
  285. [item setTarget: NSApp];
  286. [menu addItem: item];
  287. [item release];
  288. item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (@"Show All", nil)
  289. action: @selector (unhideAllApplications:) keyEquivalent: @""];
  290. [item setTarget: NSApp];
  291. [menu addItem: item];
  292. [item release];
  293. [menu addItem: [NSMenuItem separatorItem]];
  294. // Quit item....
  295. item = [[NSMenuItem alloc] initWithTitle: juceStringToNS ("Quit " + appName)
  296. action: @selector (terminate:) keyEquivalent: @"q"];
  297. [item setTarget: NSApp];
  298. [menu addItem: item];
  299. [item release];
  300. return menu;
  301. }
  302. // Since our app has no NIB, this initialises a standard app menu...
  303. static void rebuildMainMenu (const PopupMenu* extraItems)
  304. {
  305. // this can't be used in a plugin!
  306. jassert (JUCEApplication::getInstance() != 0);
  307. if (JUCEApplication::getInstance() != 0)
  308. {
  309. const ScopedAutoReleasePool pool;
  310. NSMenu* mainMenu = [[NSMenu alloc] initWithTitle: @"MainMenu"];
  311. NSMenuItem* item = [mainMenu addItemWithTitle: @"Apple" action: nil keyEquivalent: @""];
  312. NSMenu* appMenu = [[NSMenu alloc] initWithTitle: @"Apple"];
  313. [NSApp performSelector: @selector (setAppleMenu:) withObject: appMenu];
  314. [mainMenu setSubmenu: appMenu forItem: item];
  315. [NSApp setMainMenu: mainMenu];
  316. createStandardAppMenu (appMenu, JUCEApplication::getInstance()->getApplicationName(), extraItems);
  317. [appMenu release];
  318. [mainMenu release];
  319. }
  320. }
  321. void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel,
  322. const PopupMenu* extraAppleMenuItems) throw()
  323. {
  324. if (getMacMainMenu() != newMenuBarModel)
  325. {
  326. if (newMenuBarModel == 0)
  327. {
  328. delete JuceMainMenuHandler::instance;
  329. jassert (JuceMainMenuHandler::instance == 0); // should be zeroed in the destructor
  330. jassert (extraAppleMenuItems == 0); // you can't specify some extra items without also supplying a model
  331. extraAppleMenuItems = 0;
  332. }
  333. else
  334. {
  335. if (JuceMainMenuHandler::instance == 0)
  336. JuceMainMenuHandler::instance = new JuceMainMenuHandler();
  337. JuceMainMenuHandler::instance->setMenu (newMenuBarModel);
  338. }
  339. }
  340. rebuildMainMenu (extraAppleMenuItems);
  341. if (newMenuBarModel != 0)
  342. newMenuBarModel->menuItemsChanged();
  343. }
  344. MenuBarModel* MenuBarModel::getMacMainMenu() throw()
  345. {
  346. return JuceMainMenuHandler::instance != 0
  347. ? JuceMainMenuHandler::instance->currentModel : 0;
  348. }
  349. void initialiseMainMenu()
  350. {
  351. if (JUCEApplication::getInstance() != 0) // only needed in an app
  352. rebuildMainMenu (0);
  353. }
  354. #endif