popupMenu.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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/popupmenu.h"
  24. #include "core/util/safeDelete.h"
  25. #include "gui/core/guiCanvas.h"
  26. void PopupMenu::createPlatformPopupMenuData()
  27. {
  28. mData = new PlatformPopupMenuData;
  29. }
  30. void PopupMenu::deletePlatformPopupMenuData()
  31. {
  32. SAFE_DELETE(mData);
  33. }
  34. void PopupMenu::createPlatformMenu()
  35. {
  36. OSStatus err = CreateNewMenu( mData->tag, kMenuAttrAutoDisable,&(mData->mMenu));
  37. CFRetain(mData->mMenu);
  38. AssertFatal(err == noErr, "Could not create Carbon MenuRef");
  39. }
  40. static int _getModifierMask(const char* accel)
  41. {
  42. int ret = 0;
  43. if(dStrstr(accel, "ctrl"))
  44. ret |= kMenuControlModifier;
  45. if(dStrstr(accel, "shift"))
  46. ret |= kMenuShiftModifier;
  47. if(dStrstr(accel, "alt"))
  48. ret |= kMenuOptionModifier;
  49. if(!(dStrstr(accel, "cmd") || dStrstr(accel, "command")))
  50. ret |= kMenuNoCommandModifier;
  51. return ret;
  52. }
  53. static void _assignCommandKeys(const char* accel, MenuRef menu, MenuItemIndex item)
  54. {
  55. if(!(accel && *accel))
  56. return;
  57. // get the modifier keys
  58. String _accel = String::ToLower( accel );
  59. int mods = _getModifierMask(_accel);
  60. // accel is space or dash delimted.
  61. // the modifier key is either the last token in accel, or the first char in accel.
  62. const char* key = dStrrchr(_accel, ' ');
  63. if(!key)
  64. key = dStrrchr(_accel, '-');
  65. if(!key)
  66. key = _accel;
  67. else
  68. key++;
  69. if(dStrlen(key) <= 1)
  70. {
  71. char k = dToupper( key[0] );
  72. SetMenuItemCommandKey( menu, item, false, k );
  73. }
  74. else
  75. {
  76. SInt16 glyph = kMenuNullGlyph;
  77. //*** A lot of these mappings came from a listing at http://developer.apple.com/releasenotes/Carbon/HIToolboxOlderNotes.html
  78. if(!dStricmp(key, "DELETE"))
  79. glyph = kMenuDeleteRightGlyph;
  80. else if(!dStricmp(key, "HOME"))
  81. glyph = kMenuNorthwestArrowGlyph;
  82. else if(!dStricmp(key, "END"))
  83. glyph = kMenuSoutheastArrowGlyph;
  84. else if(!dStricmp(key, "BACKSPACE"))
  85. glyph = kMenuDeleteLeftGlyph;
  86. else if(!dStricmp(key, "TAB"))
  87. glyph = kMenuTabRightGlyph;
  88. else if(!dStricmp(key, "RETURN"))
  89. glyph = kMenuReturnGlyph;
  90. else if(!dStricmp(key, "ENTER"))
  91. glyph = kMenuEnterGlyph;
  92. else if(!dStricmp(key, "PG UP"))
  93. glyph = kMenuPageUpGlyph;
  94. else if(!dStricmp(key, "PG DOWN"))
  95. glyph = kMenuPageDownGlyph;
  96. else if(!dStricmp(key, "ESC"))
  97. glyph = kMenuEscapeGlyph;
  98. else if(!dStricmp(key, "LEFT"))
  99. glyph = kMenuLeftArrowGlyph;
  100. else if(!dStricmp(key, "RIGHT"))
  101. glyph = kMenuRightArrowGlyph;
  102. else if(!dStricmp(key, "UP"))
  103. glyph = kMenuUpArrowGlyph;
  104. else if(!dStricmp(key, "DOWN"))
  105. glyph = kMenuDownArrowGlyph;
  106. else if(!dStricmp(key, "SPACE"))
  107. glyph = kMenuSpaceGlyph;
  108. else if(!dStricmp(key, "F1"))
  109. glyph = kMenuF1Glyph;
  110. else if(!dStricmp(key, "F2"))
  111. glyph = kMenuF2Glyph;
  112. else if(!dStricmp(key, "F3"))
  113. glyph = kMenuF3Glyph;
  114. else if(!dStricmp(key, "F4"))
  115. glyph = kMenuF4Glyph;
  116. else if(!dStricmp(key, "F5"))
  117. glyph = kMenuF5Glyph;
  118. else if(!dStricmp(key, "F6"))
  119. glyph = kMenuF6Glyph;
  120. else if(!dStricmp(key, "F7"))
  121. glyph = kMenuF7Glyph;
  122. else if(!dStricmp(key, "F8"))
  123. glyph = kMenuF8Glyph;
  124. else if(!dStricmp(key, "F9"))
  125. glyph = kMenuF9Glyph;
  126. else if(!dStricmp(key, "F10"))
  127. glyph = kMenuF10Glyph;
  128. else if(!dStricmp(key, "F11"))
  129. glyph = kMenuF11Glyph;
  130. else if(!dStricmp(key, "F12"))
  131. glyph = kMenuF12Glyph;
  132. else if(!dStricmp(key, "F13"))
  133. glyph = kMenuF13Glyph;
  134. else if(!dStricmp(key, "F14"))
  135. glyph = kMenuF14Glyph;
  136. else if(!dStricmp(key, "F15"))
  137. glyph = kMenuF15Glyph;
  138. SetMenuItemKeyGlyph(menu, item, glyph);
  139. }
  140. SetMenuItemModifiers(menu, item, mods);
  141. }
  142. S32 PopupMenu::insertItem(S32 pos, const char *title, const char* accel)
  143. {
  144. MenuItemIndex item;
  145. CFStringRef cftitle;
  146. MenuItemAttributes attr = 0;
  147. bool needRelease = false;
  148. if(title && *title)
  149. {
  150. cftitle = CFStringCreateWithCString(NULL,title,kCFStringEncodingUTF8);
  151. needRelease = true;
  152. }
  153. else
  154. {
  155. cftitle = CFSTR("-");
  156. attr = kMenuItemAttrSeparator;
  157. }
  158. InsertMenuItemTextWithCFString(mData->mMenu, cftitle, pos, attr, kHICommandTorque + 1);
  159. if( needRelease )
  160. CFRelease( cftitle );
  161. // ensure that we have the correct index for the new menu item
  162. MenuRef outref;
  163. GetIndMenuItemWithCommandID(mData->mMenu, kHICommandTorque+1, 1, &outref, &item);
  164. SetMenuItemCommandID(mData->mMenu, item, kHICommandTorque);
  165. // save a ref to the PopupMenu that owns this item.
  166. PopupMenu* thisMenu = this;
  167. SetMenuItemProperty(mData->mMenu, item, 'GG2d', 'ownr', sizeof(PopupMenu*), &thisMenu);
  168. // construct the accelerator keys
  169. _assignCommandKeys(accel, mData->mMenu, item);
  170. S32 tag = PlatformPopupMenuData::getTag();
  171. SetMenuItemRefCon(mData->mMenu, item, tag);
  172. return tag;
  173. }
  174. S32 PopupMenu::insertSubMenu(S32 pos, const char *title, PopupMenu *submenu)
  175. {
  176. for(S32 i = 0;i < mSubmenus->size();i++)
  177. {
  178. if(submenu == (*mSubmenus)[i])
  179. {
  180. Con::errorf("PopupMenu::insertSubMenu - Attempting to add submenu twice");
  181. return -1;
  182. }
  183. }
  184. CFStringRef cftitle = CFStringCreateWithCString(NULL,title,kCFStringEncodingUTF8);
  185. InsertMenuItemTextWithCFString(mData->mMenu, cftitle, pos, 0, kHICommandTorque + 1);
  186. CFRelease( cftitle );
  187. // ensure that we have the correct index for the new menu item
  188. MenuRef outref;
  189. MenuItemIndex item;
  190. GetIndMenuItemWithCommandID(mData->mMenu, kHICommandTorque+1, 1, &outref, &item);
  191. SetMenuItemCommandID(mData->mMenu, item, 0);
  192. S32 tag = PlatformPopupMenuData::getTag();
  193. SetMenuItemRefCon( mData->mMenu, item, tag);
  194. // store a pointer to the PopupMenu this item represents. See PopupMenu::removeItem()
  195. SetMenuItemProperty(mData->mMenu, item, 'GG2d', 'subm', sizeof(PopupMenu*), submenu);
  196. SetMenuItemHierarchicalMenu( mData->mMenu, item, submenu->mData->mMenu);
  197. mSubmenus->addObject(submenu);
  198. return tag;
  199. }
  200. void PopupMenu::removeItem(S32 itemPos)
  201. {
  202. PopupMenu* submenu;
  203. itemPos++; // adjust torque -> mac menu index
  204. OSStatus err = GetMenuItemProperty(mData->mMenu, itemPos, 'GG2d', 'subm', sizeof(PopupMenu*),NULL,&submenu);
  205. if(err == noErr)
  206. mSubmenus->removeObject(submenu);
  207. // deleting the item decrements the ref count on the mac submenu.
  208. DeleteMenuItem(mData->mMenu, itemPos);
  209. }
  210. //////////////////////////////////////////////////////////////////////////
  211. void PopupMenu::enableItem(S32 pos, bool enable)
  212. {
  213. pos++; // adjust torque -> mac menu index.
  214. if(enable)
  215. EnableMenuItem(mData->mMenu, pos);
  216. else
  217. DisableMenuItem(mData->mMenu, pos);
  218. }
  219. void PopupMenu::checkItem(S32 pos, bool checked)
  220. {
  221. pos++;
  222. CheckMenuItem(mData->mMenu, pos, checked);
  223. }
  224. void PopupMenu::checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos)
  225. {
  226. // uncheck items
  227. for(int i = firstPos; i <= lastPos; i++)
  228. checkItem( i, false);
  229. // check the selected item
  230. checkItem( checkPos, true);
  231. }
  232. bool PopupMenu::isItemChecked(S32 pos)
  233. {
  234. CharParameter mark;
  235. GetItemMark(mData->mMenu, pos, &mark);
  236. return (mark == checkMark);
  237. }
  238. //////////////////////////////////////////////////////////////////////////
  239. // this method really isn't necessary for the mac implementation
  240. bool PopupMenu::canHandleID(U32 iD)
  241. {
  242. for(S32 i = 0;i < mSubmenus->size();i++)
  243. {
  244. PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
  245. if(subM == NULL)
  246. continue;
  247. if(subM->canHandleID(iD))
  248. return true;
  249. }
  250. UInt32 refcon;
  251. U32 nItems = CountMenuItems(mData->mMenu);
  252. for(int i = 1; i <= nItems; i++)
  253. {
  254. GetMenuItemRefCon(mData->mMenu, i, &refcon);
  255. if(refcon == iD)
  256. return true;
  257. }
  258. return false;
  259. }
  260. bool PopupMenu::handleSelect(U32 command, const char *text /* = NULL */)
  261. {
  262. // [tom, 8/20/2006] Pass off to a sub menu if it's for them
  263. for(S32 i = 0;i < mSubmenus->size();i++)
  264. {
  265. PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
  266. if(subM == NULL)
  267. continue;
  268. if(subM->canHandleID(command))
  269. {
  270. return subM->handleSelect(command, text);
  271. }
  272. }
  273. // ensure that this menu actually has an item with the specificed command / refcon.
  274. // this is not strictly necessary, we're just doing it here to keep the behavior
  275. // in line with the windows implementation.
  276. UInt32 refcon;
  277. U32 nItems = CountMenuItems(mData->mMenu);
  278. S32 pos = -1;
  279. for(int i = 1; i <= nItems; i++)
  280. {
  281. GetMenuItemRefCon(mData->mMenu, i, &refcon);
  282. if(refcon == command)
  283. pos = i;
  284. }
  285. if(pos == -1)
  286. {
  287. Con::errorf("PopupMenu::handleSelect - Could not find menu item position for ID %d ... this shouldn't happen!", command);
  288. return false;
  289. }
  290. char textbuf[1024];
  291. if(!text)
  292. {
  293. CFStringRef cfstr;
  294. CopyMenuItemTextAsCFString(mData->mMenu, pos, &cfstr);
  295. CFStringGetCString(cfstr,textbuf,sizeof(textbuf) - 1,kCFStringEncodingUTF8);
  296. CFRelease( cfstr );
  297. text = textbuf;
  298. }
  299. // [tom, 8/20/2006] Wasn't handled by a submenu, pass off to script
  300. return dAtob(Con::executef(this, "onSelectItem", Con::getIntArg(pos - 1), text ? text : ""));
  301. }
  302. //////////////////////////////////////////////////////////////////////////
  303. void PopupMenu::showPopup(GuiCanvas* canvas, S32 x /* = -1 */, S32 y /* = -1 */)
  304. {
  305. if(x < 0 || y < 0)
  306. {
  307. Point2I p = canvas->getCursorPos();
  308. x = p.x;
  309. y = p.y;
  310. }
  311. PopUpMenuSelect(mData->mMenu, y, x, 0);
  312. }
  313. //////////////////////////////////////////////////////////////////////////
  314. void PopupMenu::attachToMenuBar(GuiCanvas* canvas, S32 pos, const char *title)
  315. {
  316. CFStringRef cftitle = CFStringCreateWithCString(NULL,title,kCFStringEncodingUTF8);
  317. SetMenuTitleWithCFString(mData->mMenu, cftitle);
  318. CFRelease( cftitle );
  319. InsertMenu(mData->mMenu, pos);
  320. onAttachToMenuBar(canvas, pos, title);
  321. }
  322. void PopupMenu::removeFromMenuBar()
  323. {
  324. DeleteMenu(mData->tag);
  325. onRemoveFromMenuBar(mCanvas);
  326. }
  327. U32 PopupMenu::getItemCount()
  328. {
  329. return CountMenuItems( mData->mMenu );
  330. }
  331. bool PopupMenu::setItem(S32 pos, const char *title, const char *accelerator)
  332. {
  333. //TODO: update accelerator?
  334. pos += 1; // Torque to mac index
  335. CFStringRef cftitle = CFStringCreateWithCString( NULL, title, kCFStringEncodingUTF8 );
  336. SetMenuItemTextWithCFString( mData->mMenu, pos, cftitle );
  337. CFRelease( cftitle );
  338. return true;
  339. }
  340. S32 PopupMenu::getPosOnMenuBar()
  341. {
  342. return -1;
  343. }
  344. void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos)
  345. {
  346. }