popupMenu.cpp 14 KB

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