popupMenuWin32.cc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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 "platform/menus/popupMenu.h"
  23. #include "platformWin32/platformWin32.h"
  24. #include "platformWin32/winWindow.h"
  25. #include "memory/safeDelete.h"
  26. //////////////////////////////////////////////////////////////////////////
  27. // Platform Menu Data
  28. //////////////////////////////////////////////////////////////////////////
  29. struct PlatformPopupMenuData
  30. {
  31. static U32 mLastPopupMenuID;
  32. static const U32 PopupMenuIDRange;
  33. HMENU mMenu;
  34. U32 mMenuID;
  35. U32 mLastID;
  36. PlatformPopupMenuData()
  37. {
  38. mMenu = NULL;
  39. mMenuID = mLastPopupMenuID++;
  40. mLastID = 0;
  41. }
  42. ~PlatformPopupMenuData()
  43. {
  44. if(mMenu)
  45. DestroyMenu(mMenu);
  46. }
  47. };
  48. U32 PlatformPopupMenuData::mLastPopupMenuID = 0;
  49. const U32 PlatformPopupMenuData::PopupMenuIDRange = 100;
  50. void PopupMenu::createPlatformPopupMenuData()
  51. {
  52. mData = new PlatformPopupMenuData;
  53. }
  54. void PopupMenu::deletePlatformPopupMenuData()
  55. {
  56. SAFE_DELETE(mData);
  57. }
  58. void PopupMenu::createPlatformMenu()
  59. {
  60. mData->mMenu = mIsPopup ? CreatePopupMenu() : CreateMenu();
  61. }
  62. //////////////////////////////////////////////////////////////////////////
  63. // Public Methods
  64. //////////////////////////////////////////////////////////////////////////
  65. S32 PopupMenu::insertItem(S32 pos, const char *title, const char* accelerator)
  66. {
  67. MENUITEMINFOA mi;
  68. mi.cbSize = sizeof(mi);
  69. mi.fMask = MIIM_ID|MIIM_TYPE;
  70. mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1;
  71. mData->mLastID++;
  72. if(title && *title)
  73. mi.fType = MFT_STRING;
  74. else
  75. mi.fType = MFT_SEPARATOR;
  76. char buf[1024];
  77. dSprintf(buf, sizeof(buf), "%s\t%s", title, accelerator);
  78. mi.dwTypeData = (LPSTR)buf;
  79. if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi))
  80. {
  81. DrawMenuBar(winState.appWindow);
  82. return mi.wID;
  83. }
  84. return -1;
  85. }
  86. S32 PopupMenu::insertSubMenu(S32 pos, const char *title, PopupMenu *submenu)
  87. {
  88. for(S32 i = 0;i < mSubmenus->size();i++)
  89. {
  90. if(submenu == (*mSubmenus)[i])
  91. {
  92. Con::errorf("PopupMenu::insertSubMenu - Attempting to add submenu twice");
  93. return -1;
  94. }
  95. }
  96. MENUITEMINFOA mi;
  97. mi.cbSize = sizeof(mi);
  98. mi.fMask = MIIM_ID|MIIM_TYPE|MIIM_SUBMENU|MIIM_DATA;
  99. mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1;
  100. if(title && *title)
  101. mi.fType = MFT_STRING;
  102. else
  103. mi.fType = MFT_SEPARATOR;
  104. mi.dwTypeData = (LPSTR)title;
  105. mi.hSubMenu = submenu->mData->mMenu;
  106. mi.dwItemData = (ULONG_PTR)submenu;
  107. if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi))
  108. {
  109. mSubmenus->addObject(submenu);
  110. DrawMenuBar(winState.appWindow);
  111. return mi.wID;
  112. }
  113. return -1;
  114. }
  115. void PopupMenu::removeItem(S32 itemPos)
  116. {
  117. MENUITEMINFOA mi;
  118. mi.cbSize = sizeof(mi);
  119. mi.fMask = MIIM_DATA;
  120. if(GetMenuItemInfoA(mData->mMenu, itemPos, TRUE, &mi))
  121. {
  122. if(mi.fMask & MIIM_DATA)
  123. {
  124. PopupMenu *mnu = (PopupMenu *)mi.dwItemData;
  125. if (mnu)
  126. mSubmenus->removeObject(mnu);
  127. }
  128. }
  129. RemoveMenu(mData->mMenu, itemPos, MF_BYPOSITION);
  130. DrawMenuBar(winState.appWindow);
  131. }
  132. //////////////////////////////////////////////////////////////////////////
  133. void PopupMenu::enableItem(S32 pos, bool enable)
  134. {
  135. U32 flags = enable ? MF_ENABLED : MF_GRAYED;
  136. EnableMenuItem(mData->mMenu, pos, MF_BYPOSITION|flags);
  137. }
  138. void PopupMenu::checkItem(S32 pos, bool checked)
  139. {
  140. // U32 flags = checked ? MF_CHECKED : MF_UNCHECKED;
  141. // CheckMenuItem(mData->mMenu, pos, MF_BYPOSITION|flags);
  142. MENUITEMINFOA mi;
  143. mi.cbSize = sizeof(mi);
  144. mi.fMask = MIIM_STATE;
  145. mi.fState = checked ? MFS_CHECKED : MFS_UNCHECKED;
  146. SetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi);
  147. }
  148. void PopupMenu::checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos)
  149. {
  150. CheckMenuRadioItem(mData->mMenu, firstPos, lastPos, checkPos, MF_BYPOSITION);
  151. }
  152. bool PopupMenu::isItemChecked(S32 pos)
  153. {
  154. MENUITEMINFOA mi;
  155. mi.cbSize = sizeof(mi);
  156. mi.fMask = MIIM_STATE;
  157. if(GetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi) && (mi.fState & MFS_CHECKED))
  158. return true;
  159. return false;
  160. }
  161. //////////////////////////////////////////////////////////////////////////
  162. bool PopupMenu::canHandleID(U32 iD)
  163. {
  164. for(S32 i = 0;i < mSubmenus->size();i++)
  165. {
  166. PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
  167. if(subM == NULL)
  168. continue;
  169. if(subM->canHandleID(iD))
  170. return true;
  171. }
  172. if(iD >= mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange &&
  173. iD < (mData->mMenuID+1) * PlatformPopupMenuData::PopupMenuIDRange)
  174. {
  175. return true;
  176. }
  177. return false;
  178. }
  179. bool PopupMenu::handleSelect(U32 command, const char *text /* = NULL */)
  180. {
  181. // [tom, 8/20/2006] Pass off to a sub menu if it's for them
  182. for(S32 i = 0;i < mSubmenus->size();i++)
  183. {
  184. PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
  185. if(subM == NULL)
  186. continue;
  187. if(subM->canHandleID(command))
  188. {
  189. return subM->handleSelect(command, text);
  190. }
  191. }
  192. // [tom, 8/21/2006] Cheesey hack to find the position based on ID
  193. char buf[512];
  194. MENUITEMINFOA mi;
  195. mi.cbSize = sizeof(mi);
  196. mi.dwTypeData = NULL;
  197. S32 numItems = GetMenuItemCount(mData->mMenu);
  198. S32 pos = -1;
  199. for(S32 i = 0;i < numItems;i++)
  200. {
  201. mi.fMask = MIIM_ID|MIIM_STRING|MIIM_STATE;
  202. if(GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi))
  203. {
  204. if(mi.wID == command)
  205. {
  206. if(text == NULL)
  207. {
  208. mi.dwTypeData = buf;
  209. mi.cch++;
  210. GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi);
  211. // [tom, 5/11/2007] Don't do anything if the menu item is disabled
  212. if(mi.fState & MFS_DISABLED)
  213. return false;
  214. text = StringTable->insert(mi.dwTypeData);
  215. }
  216. pos = i;
  217. break;
  218. }
  219. }
  220. }
  221. if(pos == -1)
  222. {
  223. Con::errorf("PopupMenu::handleSelect - Could not find menu item position for ID %d ... this shouldn't happen!", command);
  224. return false;
  225. }
  226. // [tom, 8/20/2006] Wasn't handled by a submenu, pass off to script
  227. return dAtob(Con::executef(this, 4, "onSelectItem", Con::getIntArg(pos), text ? text : ""));
  228. }
  229. //////////////////////////////////////////////////////////////////////////
  230. void PopupMenu::showPopup(S32 x /* = -1 */, S32 y /* = -1 */)
  231. {
  232. POINT p;
  233. if(x == -1 && y == -1)
  234. GetCursorPos(&p);
  235. else
  236. {
  237. p.x = x;
  238. p.y = y;
  239. ClientToScreen(winState.appWindow, &p);
  240. }
  241. winState.renderThreadBlocked = true;
  242. U32 opt = (int)TrackPopupMenu(mData->mMenu, TPM_NONOTIFY|TPM_RETURNCMD, p.x, p.y, 0, winState.appWindow, NULL);
  243. if(opt > 0)
  244. handleSelect(opt, NULL);
  245. winState.renderThreadBlocked = false;
  246. }
  247. //////////////////////////////////////////////////////////////////////////
  248. void PopupMenu::attachToMenuBar(S32 pos, const char *title)
  249. {
  250. if(winState.appMenu == NULL)
  251. {
  252. CreateWin32MenuBar();
  253. }
  254. MENUITEMINFOA mii;
  255. mii.cbSize = sizeof(MENUITEMINFOA);
  256. mii.fMask = MIIM_STRING|MIIM_DATA;
  257. mii.dwTypeData = (LPSTR)title;
  258. mii.fMask |= MIIM_ID;
  259. mii.wID = mData->mMenuID;
  260. mii.fMask |= MIIM_SUBMENU;
  261. mii.hSubMenu = mData->mMenu;
  262. mii.dwItemData = (ULONG_PTR)this;
  263. InsertMenuItemA(winState.appMenu, pos, TRUE, &mii);
  264. DrawMenuBar(winState.appWindow);
  265. }
  266. void PopupMenu::removeFromMenuBar()
  267. {
  268. // [tom, 8/21/2006] Find us on the menu bar (lame)
  269. S32 numItems = GetMenuItemCount(winState.appMenu);
  270. S32 pos = -1;
  271. for(S32 i = 0;i < numItems;i++)
  272. {
  273. MENUITEMINFOA mi;
  274. mi.cbSize = sizeof(mi);
  275. mi.fMask = MIIM_DATA;
  276. if(GetMenuItemInfoA(winState.appMenu, i, TRUE, &mi))
  277. {
  278. if(mi.fMask & MIIM_DATA)
  279. {
  280. PopupMenu *mnu = (PopupMenu *)mi.dwItemData;
  281. if(mnu == this)
  282. {
  283. pos = i;
  284. break;
  285. }
  286. }
  287. }
  288. }
  289. if(pos == -1)
  290. return;
  291. RemoveMenu(winState.appMenu, pos, MF_BYPOSITION);
  292. DrawMenuBar(winState.appWindow);
  293. }