popupMenu.mm 14 KB

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