menuBarMac.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platformMac/platformMacCarb.h"
  23. #include "platform/menus/menuBar.h"
  24. #include "platform/menus/popupMenu.h"
  25. #include "gui/core/guiCanvas.h"
  26. #include "windowManager/platformWindowMgr.h"
  27. #include "windowManager/platformWindow.h"
  28. class PlatformMenuBarData
  29. {
  30. public:
  31. PlatformMenuBarData() :
  32. mMenuEventHandlerRef(NULL),
  33. mCommandEventHandlerRef(NULL),
  34. mMenuOpenCount( 0 ),
  35. mLastCloseTime( 0 )
  36. {}
  37. EventHandlerRef mMenuEventHandlerRef;
  38. EventHandlerRef mCommandEventHandlerRef;
  39. /// More evil hacking for OSX. There seems to be no way to disable menu shortcuts and
  40. /// they are automatically routed within that Cocoa thing outside of our control. Also,
  41. /// there's no way of telling what triggered a command event and thus no way of knowing
  42. /// whether it was a keyboard shortcut. Sigh.
  43. ///
  44. /// So what we do here is monitor the sequence of events leading to a command event. We
  45. /// capture the time the last open menu was closed and then, when we receive a command
  46. /// event (which are dished out after the menus are closed) and keyboard accelerators are
  47. /// disabled, we check whether we are a certain very short time away in the event stream
  48. /// from the menu close event. If so, we figure the event came from clicking in a menu.
  49. ///
  50. /// Utterly evil and dirty but seems to do the trick.
  51. U32 mMenuOpenCount;
  52. EventTime mLastCloseTime;
  53. };
  54. //-----------------------------------------------------------------------------
  55. #pragma mark -
  56. #pragma mark ---- menu event handler ----
  57. static OSStatus _OnMenuEvent(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData)
  58. {
  59. PlatformMenuBarData* mbData = ( PlatformMenuBarData* ) userData;
  60. MenuRef menu;
  61. GetEventParameter(theEvent, kEventParamDirectObject, typeMenuRef, NULL, sizeof(MenuRef), NULL, &menu);
  62. // Count open/close for the sake of hotkey disabling.
  63. UInt32 kind = GetEventKind( theEvent );
  64. if( kind == kEventMenuOpening )
  65. mbData->mMenuOpenCount ++;
  66. else
  67. {
  68. AssertWarn( mbData->mMenuOpenCount > 0, "Unbalanced menu open/close events in _OnMenuEvent" );
  69. if( mbData->mMenuOpenCount )
  70. mbData->mMenuOpenCount --;
  71. // Initial menu closed. Capture time.
  72. if( !mbData->mMenuOpenCount )
  73. mbData->mLastCloseTime = GetEventTime( theEvent );
  74. }
  75. OSStatus err = eventNotHandledErr;
  76. PopupMenu *torqueMenu;
  77. if( CountMenuItems( menu ) > 0 )
  78. {
  79. // I don't know of a way to get the Torque PopupMenu object from a Carbon MenuRef
  80. // other than going through its first menu item
  81. err = GetMenuItemProperty(menu, 1, 'GG2d', 'ownr', sizeof(PopupMenu*), NULL, &torqueMenu);
  82. if( err == noErr && torqueMenu != NULL )
  83. {
  84. torqueMenu->onMenuSelect();
  85. }
  86. }
  87. return err;
  88. }
  89. //-----------------------------------------------------------------------------
  90. #pragma mark -
  91. #pragma mark ---- menu command event handler ----
  92. static bool MacCarbHandleMenuCommand( void* hiCommand, PlatformMenuBarData* mbData )
  93. {
  94. HICommand *cmd = (HICommand*)hiCommand;
  95. if(cmd->commandID != kHICommandTorque)
  96. return false;
  97. MenuRef menu = cmd->menu.menuRef;
  98. MenuItemIndex item = cmd->menu.menuItemIndex;
  99. // Run the command handler.
  100. PopupMenu* torqueMenu;
  101. OSStatus err = GetMenuItemProperty(menu, item, 'GG2d', 'ownr', sizeof(PopupMenu*), NULL, &torqueMenu);
  102. AssertFatal(err == noErr, "Could not resolve the PopupMenu stored on a native menu item");
  103. UInt32 command;
  104. err = GetMenuItemRefCon(menu, item, &command);
  105. AssertFatal(err == noErr, "Could not find the tag of a native menu item");
  106. if(!torqueMenu->canHandleID(command))
  107. Con::errorf("menu claims it cannot handle that id. how odd.");
  108. // un-highlight currently selected menu
  109. HiliteMenu( 0 );
  110. return torqueMenu->handleSelect(command,NULL);
  111. }
  112. //-----------------------------------------------------------------------------
  113. #pragma mark -
  114. #pragma mark ---- Command Events ----
  115. static OSStatus _OnCommandEvent(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
  116. {
  117. PlatformMenuBarData* mbData = ( PlatformMenuBarData* ) userData;
  118. HICommand commandStruct;
  119. OSStatus result = eventNotHandledErr;
  120. GetEventParameter(theEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &commandStruct);
  121. // pass menu command events to a more specific handler.
  122. if(commandStruct.attributes & kHICommandFromMenu)
  123. {
  124. bool handleEvent = true;
  125. // Do menu-close check hack.
  126. PlatformWindow* window = PlatformWindowManager::get()->getFocusedWindow();
  127. if( !window || !window->getAcceleratorsEnabled() )
  128. {
  129. F32 deltaTime = mFabs( GetEventTime( theEvent ) - mbData->mLastCloseTime );
  130. if( deltaTime > 0.1f )
  131. handleEvent = false;
  132. }
  133. if( handleEvent && MacCarbHandleMenuCommand(&commandStruct, mbData) )
  134. result = noErr;
  135. }
  136. return result;
  137. }
  138. //-----------------------------------------------------------------------------
  139. // MenuBar Methods
  140. //-----------------------------------------------------------------------------
  141. void MenuBar::createPlatformPopupMenuData()
  142. {
  143. mData = new PlatformMenuBarData;
  144. }
  145. void MenuBar::deletePlatformPopupMenuData()
  146. {
  147. SAFE_DELETE(mData);
  148. }
  149. //-----------------------------------------------------------------------------
  150. void MenuBar::attachToCanvas(GuiCanvas *owner, S32 pos)
  151. {
  152. if(owner == NULL || isAttachedToCanvas())
  153. return;
  154. mCanvas = owner;
  155. // Add the items
  156. for(S32 i = 0;i < size();++i)
  157. {
  158. PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(i));
  159. if(mnu == NULL)
  160. {
  161. Con::warnf("MenuBar::attachToMenuBar - Non-PopupMenu object in set");
  162. continue;
  163. }
  164. if(mnu->isAttachedToMenuBar())
  165. mnu->removeFromMenuBar();
  166. mnu->attachToMenuBar(owner, pos + i, mnu->getBarTitle());
  167. }
  168. // register as listener for menu opening events
  169. static EventTypeSpec menuEventTypes[ 2 ];
  170. menuEventTypes[ 0 ].eventClass = kEventClassMenu;
  171. menuEventTypes[ 0 ].eventKind = kEventMenuOpening;
  172. menuEventTypes[ 1 ].eventClass = kEventClassMenu;
  173. menuEventTypes[ 1 ].eventKind = kEventMenuClosed;
  174. EventHandlerUPP menuEventHandlerUPP;
  175. menuEventHandlerUPP = NewEventHandlerUPP(_OnMenuEvent);
  176. InstallEventHandler(GetApplicationEventTarget(), menuEventHandlerUPP, 2, menuEventTypes, mData, &mData->mMenuEventHandlerRef);
  177. // register as listener for process command events
  178. static EventTypeSpec comEventTypes[1];
  179. comEventTypes[0].eventClass = kEventClassCommand;
  180. comEventTypes[0].eventKind = kEventCommandProcess;
  181. EventHandlerUPP commandEventHandlerUPP;
  182. commandEventHandlerUPP = NewEventHandlerUPP(_OnCommandEvent);
  183. InstallEventHandler(GetApplicationEventTarget(), commandEventHandlerUPP, 1, comEventTypes, mData, &mData->mCommandEventHandlerRef);
  184. }
  185. //-----------------------------------------------------------------------------
  186. void MenuBar::removeFromCanvas()
  187. {
  188. if(mCanvas == NULL || ! isAttachedToCanvas())
  189. return;
  190. if(mData->mCommandEventHandlerRef != NULL)
  191. RemoveEventHandler( mData->mCommandEventHandlerRef );
  192. mData->mCommandEventHandlerRef = NULL;
  193. if(mData->mMenuEventHandlerRef != NULL)
  194. RemoveEventHandler( mData->mMenuEventHandlerRef );
  195. mData->mMenuEventHandlerRef = NULL;
  196. // Add the items
  197. for(S32 i = 0;i < size();++i)
  198. {
  199. PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(i));
  200. if(mnu == NULL)
  201. {
  202. Con::warnf("MenuBar::removeFromMenuBar - Non-PopupMenu object in set");
  203. continue;
  204. }
  205. mnu->removeFromMenuBar();
  206. }
  207. mCanvas = NULL;
  208. }
  209. //-----------------------------------------------------------------------------
  210. void MenuBar::updateMenuBar(PopupMenu* menu)
  211. {
  212. if(! isAttachedToCanvas())
  213. return;
  214. menu->removeFromMenuBar();
  215. SimSet::iterator itr = find(begin(), end(), menu);
  216. if(itr == end())
  217. return;
  218. // Get the item currently at the position we want to add to
  219. S32 pos = itr - begin();
  220. S16 posID = 0;
  221. PopupMenu *nextMenu = NULL;
  222. for(S32 i = pos + 1; i < size(); i++)
  223. {
  224. PopupMenu *testMenu = dynamic_cast<PopupMenu *>(at(i));
  225. if (testMenu && testMenu->isAttachedToMenuBar())
  226. {
  227. nextMenu = testMenu;
  228. break;
  229. }
  230. }
  231. if(nextMenu)
  232. posID = GetMenuID(nextMenu->mData->mMenu);
  233. menu->attachToMenuBar(mCanvas, posID, menu->mBarTitle);
  234. }