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.

430 lines
14KB

  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, 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. private:
  143. JuceMenuCallback* callback;
  144. NSMenu* createMenu (const PopupMenu menu,
  145. const String& menuName,
  146. int& id,
  147. const int topLevelIndex)
  148. {
  149. NSMenu* m = [[NSMenu alloc] initWithTitle: juceStringToNS (menuName)];
  150. [m setAutoenablesItems: false];
  151. PopupMenu::MenuItemIterator iter (menu);
  152. while (iter.next())
  153. {
  154. NSString* text = juceStringToNS (iter.itemName.upToFirstOccurrenceOf (T("<end>"), false, true));
  155. if (iter.isSeparator)
  156. {
  157. [m addItem: [NSMenuItem separatorItem]];
  158. }
  159. else if (iter.isSectionHeader)
  160. {
  161. NSMenuItem* item = [m addItemWithTitle: text
  162. action: nil
  163. keyEquivalent: @""];
  164. [item setEnabled: iter.isEnabled];
  165. }
  166. else if (iter.subMenu != 0)
  167. {
  168. NSMenuItem* item = [m addItemWithTitle: text
  169. action: nil
  170. keyEquivalent: @""];
  171. [item setTag: iter.itemId];
  172. [item setEnabled: iter.isEnabled];
  173. NSMenu* sub = createMenu (*iter.subMenu, iter.itemName, id, topLevelIndex);
  174. [m setSubmenu: sub forItem: item];
  175. [sub release];
  176. }
  177. else
  178. {
  179. NSMenuItem* item = [m addItemWithTitle: text
  180. action: @selector (menuItemInvoked:)
  181. keyEquivalent: @""];
  182. [item setTag: iter.itemId];
  183. [item setEnabled: iter.isEnabled];
  184. [item setState: iter.isTicked ? NSOnState : NSOffState];
  185. [item setTarget: (id) callback];
  186. NSMutableArray* info = [NSMutableArray arrayWithObject: [NSNumber numberWithUnsignedLongLong: (pointer_sized_int) (void*) iter.commandManager]];
  187. [info addObject: [NSNumber numberWithInt: topLevelIndex]];
  188. [item setRepresentedObject: info];
  189. if (iter.commandManager != 0)
  190. {
  191. const Array <KeyPress> keyPresses (iter.commandManager->getKeyMappings()
  192. ->getKeyPressesAssignedToCommand (iter.itemId));
  193. if (keyPresses.size() > 0)
  194. {
  195. const KeyPress& kp = keyPresses.getReference(0);
  196. juce_wchar key = kp.getTextCharacter();
  197. if (kp.getKeyCode() == KeyPress::backspaceKey)
  198. key = NSBackspaceCharacter;
  199. else if (kp.getKeyCode() == KeyPress::deleteKey)
  200. key = NSDeleteCharacter;
  201. else if (key == 0)
  202. key = (juce_wchar) kp.getKeyCode();
  203. unsigned int mods = 0;
  204. if (kp.getModifiers().isShiftDown())
  205. mods |= NSShiftKeyMask;
  206. if (kp.getModifiers().isCtrlDown())
  207. mods |= NSControlKeyMask;
  208. if (kp.getModifiers().isAltDown())
  209. mods |= NSAlternateKeyMask;
  210. if (kp.getModifiers().isCommandDown())
  211. mods |= NSCommandKeyMask;
  212. [item setKeyEquivalent: juceStringToNS (String::charToString (key))];
  213. [item setKeyEquivalentModifierMask: mods];
  214. }
  215. }
  216. }
  217. }
  218. [m update];
  219. return m;
  220. }
  221. };
  222. JuceMainMenuHandler* JuceMainMenuHandler::instance = 0;
  223. END_JUCE_NAMESPACE
  224. @implementation JuceMenuCallback
  225. - (JuceMenuCallback*) initWithOwner: (JuceMainMenuHandler*) owner_
  226. {
  227. [super init];
  228. owner = owner_;
  229. return self;
  230. }
  231. - (void) dealloc
  232. {
  233. [super dealloc];
  234. }
  235. - (void) menuItemInvoked: (id) menu
  236. {
  237. NSMenuItem* item = (NSMenuItem*) menu;
  238. if ([[item representedObject] isKindOfClass: [NSArray class]])
  239. {
  240. NSArray* info = (NSArray*) [item representedObject];
  241. owner->invoke ([item tag],
  242. (ApplicationCommandManager*) (pointer_sized_int)
  243. [((NSNumber*) [info objectAtIndex: 0]) unsignedLongLongValue],
  244. (int) [((NSNumber*) [info objectAtIndex: 1]) intValue]);
  245. }
  246. }
  247. @end
  248. BEGIN_JUCE_NAMESPACE
  249. void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel) throw()
  250. {
  251. if (getMacMainMenu() != newMenuBarModel)
  252. {
  253. if (newMenuBarModel == 0)
  254. {
  255. delete JuceMainMenuHandler::instance;
  256. jassert (JuceMainMenuHandler::instance == 0); // should be zeroed in the destructor
  257. }
  258. else
  259. {
  260. if (JuceMainMenuHandler::instance == 0)
  261. JuceMainMenuHandler::instance = new JuceMainMenuHandler();
  262. JuceMainMenuHandler::instance->setMenu (newMenuBarModel);
  263. }
  264. }
  265. }
  266. MenuBarModel* MenuBarModel::getMacMainMenu() throw()
  267. {
  268. return JuceMainMenuHandler::instance != 0
  269. ? JuceMainMenuHandler::instance->currentModel : 0;
  270. }
  271. //==============================================================================
  272. static NSMenu* createStandardAppMenu (NSMenu* menu, const String& appName)
  273. {
  274. NSMenuItem* item;
  275. // xxx should allow the 'about' and 'preferences' items to be turned on programatically...
  276. /* item = [menu addItemWithTitle: juceStringToNS ("About " + appName)]
  277. action: @selector(orderFrontStandardAboutPanel:) keyEquivalent: @""];
  278. [item setTarget: NSApp];
  279. [menu addItem: [NSMenuItem separatorItem]];
  280. */
  281. /* item = [menu addItemWithTitle: NSLocalizedString (@"Preferences...", nil)
  282. action: nil keyEquivalent: @","];
  283. [menu addItem: [NSMenuItem separatorItem]];
  284. */
  285. // Services...
  286. item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (@"Services", nil)
  287. action: nil keyEquivalent: @""];
  288. [menu addItem: item];
  289. [item release];
  290. NSMenu* servicesMenu = [[NSMenu alloc] initWithTitle: @"Services"];
  291. [menu setSubmenu: servicesMenu forItem: item];
  292. [NSApp setServicesMenu: servicesMenu];
  293. [servicesMenu release];
  294. [menu addItem: [NSMenuItem separatorItem]];
  295. // Hide + Show stuff...
  296. item = [[NSMenuItem alloc] initWithTitle: juceStringToNS ("Hide " + appName)
  297. action: @selector (hide:) keyEquivalent: @"h"];
  298. [item setTarget: NSApp];
  299. [menu addItem: item];
  300. [item release];
  301. item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (@"Hide Others", nil)
  302. action: @selector (hideOtherApplications:) keyEquivalent: @"h"];
  303. [item setKeyEquivalentModifierMask: NSCommandKeyMask | NSAlternateKeyMask];
  304. [item setTarget: NSApp];
  305. [menu addItem: item];
  306. [item release];
  307. item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (@"Show All", nil)
  308. action: @selector (unhideAllApplications:) keyEquivalent: @""];
  309. [item setTarget: NSApp];
  310. [menu addItem: item];
  311. [item release];
  312. [menu addItem: [NSMenuItem separatorItem]];
  313. // Quit item....
  314. item = [[NSMenuItem alloc] initWithTitle: juceStringToNS ("Quit " + appName)
  315. action: @selector (terminate:) keyEquivalent: @"q"];
  316. [item setTarget: NSApp];
  317. [menu addItem: item];
  318. [item release];
  319. return menu;
  320. }
  321. // Since our app has no NIB, this initialises a standard app menu...
  322. void initialiseMainMenu()
  323. {
  324. if (JUCEApplication::getInstance() != 0) // only needed in an app
  325. {
  326. const ScopedAutoReleasePool pool;
  327. NSMenu* mainMenu = [[NSMenu alloc] initWithTitle: @"MainMenu"];
  328. NSMenuItem* item = [mainMenu addItemWithTitle: @"Apple" action: nil keyEquivalent: @""];
  329. NSMenu* appMenu = [[NSMenu alloc] initWithTitle: @"Apple"];
  330. [NSApp performSelector: @selector (setAppleMenu:) withObject: appMenu];
  331. [mainMenu setSubmenu: appMenu forItem: item];
  332. [NSApp setMainMenu: mainMenu];
  333. createStandardAppMenu (appMenu, JUCEApplication::getInstance()->getApplicationName());
  334. [appMenu release];
  335. [mainMenu release];
  336. }
  337. }
  338. #endif