guiMenuBar.cpp 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993
  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 "platform/platform.h"
  23. #include "gui/editor/guiMenuBar.h"
  24. #include "console/consoleTypes.h"
  25. #include "console/console.h"
  26. #include "gui/core/guiCanvas.h"
  27. #include "gui/core/guiDefaultControlRender.h"
  28. #include "gui/controls/guiTextListCtrl.h"
  29. #include "sim/actionMap.h"
  30. #include "gfx/gfxDevice.h"
  31. #include "gfx/gfxDrawUtil.h"
  32. #include "gfx/primBuilder.h"
  33. #include "console/engineAPI.h"
  34. // menu bar:
  35. // basic idea - fixed height control bar at the top of a window, placed and sized in gui editor
  36. // menu text for menus or menu items should not begin with a digit
  37. // all menus can be removed via the clearMenus() console command
  38. // each menu is added via the addMenu(menuText, menuId) console command
  39. // each menu is added with a menu id
  40. // menu items are added to menus via that addMenuItem(menu, menuItemText, menuItemId, accelerator, checkGroup) console command
  41. // each menu item is added with a menu item id and an optional accelerator
  42. // menu items are initially enabled, but can be disabled/re-enabled via the setMenuItemEnable(menu,menuItem,bool)
  43. // menu text can be set via the setMenuText(menu, newMenuText) console method
  44. // menu item text can be set via the setMenuItemText console method
  45. // menu items can be removed via the removeMenuItem(menu, menuItem) console command
  46. // menu items can be cleared via the clearMenuItems(menu) console command
  47. // menus can be hidden or shown via the setMenuVisible(menu, bool) console command
  48. // menu items can be hidden or shown via the setMenuItemVisible(menu, menuItem, bool) console command
  49. // menu items can be check'd via the setMenuItemChecked(menu, menuItem, bool) console command
  50. // if the bool is true, any other items in that menu item's check group become unchecked.
  51. //
  52. // menu items can have a bitmap set on them via the setMenuItemBitmap(menu, menuItem, bitmapIndex)
  53. // passing -1 for the bitmap index will result in no bitmap being shown
  54. // the index paramater is an index into the bitmap array of the associated profile
  55. // this can be used, for example, to display a check next to a selected menu item
  56. // bitmap indices are actually multiplied by 3 when indexing into the bitmap
  57. // since bitmaps have normal, selected and disabled states.
  58. //
  59. // menus can be removed via the removeMenu console command
  60. // specification arguments for menus and menu items can be either the id or the text of the menu or menu item
  61. // adding the menu item "-" will add an un-selectable seperator to the menu
  62. // callbacks:
  63. // when a menu is clicked, before it is displayed, the menu calls its onMenuSelect(menuId, menuText) method -
  64. // this allows the callback to enable/disable menu items, or add menu items in a context-sensitive way
  65. // when a menu item is clicked, the menu removes itself from display, then calls onMenuItemSelect(menuId, menuText, menuItemId, menuItemText)
  66. // the initial implementation does not support:
  67. // hierarchal menus
  68. // keyboard accelerators on menu text (i.e. via alt-key combos)
  69. //------------------------------------------------------------------------------
  70. IMPLEMENT_CONOBJECT(GuiMenuBar);
  71. ConsoleDocClass( GuiMenuBar,
  72. "@brief GUI Control which displays a horizontal bar with individual drop-down menu items. Each menu item may also have submenu items.\n\n"
  73. "@tsexample\n"
  74. "new GuiMenuBar(newMenuBar)\n"
  75. "{\n"
  76. " Padding = \"0\";\n"
  77. " //Properties not specific to this control have been omitted from this example.\n"
  78. "};\n\n"
  79. "// Add a menu to the menu bar\n"
  80. "newMenuBar.addMenu(0,\"New Menu\");\n\n"
  81. "// Add a menu item to the New Menu\n"
  82. "newMenuBar.addMenuItem(0,\"New Menu Item\",0,\"n\",-1);\n\n"
  83. "// Add a submenu item to the New Menu Item\n"
  84. "newMenuBar.addSubmenuItem(0,1,\"New Submenu Item\",0,\"s\",-1);\n"
  85. "@endtsexample\n\n"
  86. "@see GuiTickCtrl\n\n"
  87. "@ingroup GuiCore\n"
  88. );
  89. IMPLEMENT_CALLBACK( GuiMenuBar, onMouseInMenu, void, (bool isInMenu),( isInMenu ),
  90. "@brief Called whenever the mouse enters, or persists is in the menu.\n\n"
  91. "@param isInMenu True if the mouse has entered the menu, otherwise is false.\n"
  92. "@note To receive this callback, call setProcessTicks(true) on the menu bar.\n"
  93. "@tsexample\n"
  94. "// Mouse enters or persists within the menu, causing the callback to occur.\n"
  95. "GuiMenuBar::onMouseInMenu(%this,%hasLeftMenu)\n"
  96. "{\n"
  97. " // Code to run when the callback occurs\n"
  98. "}\n"
  99. "@endtsexample\n\n"
  100. "@see GuiTickCtrl\n\n"
  101. );
  102. IMPLEMENT_CALLBACK( GuiMenuBar, onMenuSelect, void, ( S32 menuId, const char* menuText ),( menuId , menuText ),
  103. "@brief Called whenever a menu is selected.\n\n"
  104. "@param menuId Index id of the clicked menu\n"
  105. "@param menuText Text of the clicked menu\n\n"
  106. "@tsexample\n"
  107. "// A menu has been selected, causing the callback to occur.\n"
  108. "GuiMenuBar::onMenuSelect(%this,%menuId,%menuText)\n"
  109. "{\n"
  110. " // Code to run when the callback occurs\n"
  111. "}\n"
  112. "@endtsexample\n\n"
  113. "@see GuiTickCtrl\n\n"
  114. );
  115. IMPLEMENT_CALLBACK( GuiMenuBar, onMenuItemSelect, void, ( S32 menuId, const char* menuText, S32 menuItemId, const char* menuItemText ),
  116. ( menuId, menuText, menuItemId, menuItemText ),
  117. "@brief Called whenever an item in a menu is selected.\n\n"
  118. "@param menuId Index id of the menu which contains the selected menu item\n"
  119. "@param menuText Text of the menu which contains the selected menu item\n\n"
  120. "@param menuItemId Index id of the selected menu item\n"
  121. "@param menuItemText Text of the selected menu item\n\n"
  122. "@tsexample\n"
  123. "// A menu item has been selected, causing the callback to occur.\n"
  124. "GuiMenuBar::onMenuItemSelect(%this,%menuId,%menuText,%menuItemId,%menuItemText)\n"
  125. "{\n"
  126. " // Code to run when the callback occurs\n"
  127. "}\n"
  128. "@endtsexample\n\n"
  129. "@see GuiTickCtrl\n\n"
  130. );
  131. IMPLEMENT_CALLBACK( GuiMenuBar, onSubmenuSelect, void, ( S32 submenuId, const char* submenuText ),( submenuId, submenuText ),
  132. "@brief Called whenever a submenu is selected.\n\n"
  133. "@param submenuId Id of the selected submenu\n"
  134. "@param submenuText Text of the selected submenu\n\n"
  135. "@tsexample\n"
  136. "GuiMenuBar::onSubmenuSelect(%this,%submenuId,%submenuText)\n"
  137. "{\n"
  138. " // Code to run when the callback occurs\n"
  139. "}\n"
  140. "@endtsexample\n\n"
  141. "@see GuiTickCtrl\n\n"
  142. );
  143. //------------------------------------------------------------------------------
  144. // console methods
  145. //------------------------------------------------------------------------------
  146. DefineEngineMethod( GuiMenuBar, clearMenus, void, (),,
  147. "@brief Clears all the menus from the menu bar.\n\n"
  148. "@tsexample\n"
  149. "// Inform the GuiMenuBar control to clear all menus from itself.\n"
  150. "%thisGuiMenuBar.clearMenus();\n"
  151. "@endtsexample\n\n"
  152. "@see GuiTickCtrl")
  153. {
  154. object->clearMenus();
  155. }
  156. DefineEngineMethod( GuiMenuBar, setMenuMargins, void, (S32 horizontalMargin, S32 verticalMargin, S32 bitmapToTextSpacing),,
  157. "@brief Sets the menu rendering margins: horizontal, vertical, bitmap spacing.\n\n"
  158. "Detailed description\n\n"
  159. "@param horizontalMargin Number of pixels on the left and right side of a menu's text.\n"
  160. "@param verticalMargin Number of pixels on the top and bottom of a menu's text.\n"
  161. "@param bitmapToTextSpacing Number of pixels between a menu's bitmap and text.\n"
  162. "@tsexample\n"
  163. "// Define the horizontalMargin\n"
  164. "%horizontalMargin = \"5\";\n\n"
  165. "// Define the verticalMargin\n"
  166. "%verticalMargin = \"5\";\n\n"
  167. "// Define the bitmapToTextSpacing\n"
  168. "%bitmapToTextSpacing = \"12\";\n\n"
  169. "// Inform the GuiMenuBar control to set its margins based on the defined values.\n"
  170. "%thisGuiMenuBar.setMenuMargins(%horizontalMargin,%verticalMargin,%bitmapToTextSpacing);\n"
  171. "@endtsexample\n\n"
  172. "@see GuiTickCtrl")
  173. {
  174. object->mHorizontalMargin = horizontalMargin;
  175. object->mVerticalMargin = verticalMargin;
  176. object->mBitmapMargin = bitmapToTextSpacing;
  177. }
  178. DefineEngineMethod(GuiMenuBar, addMenu, void, (const char* menuText, S32 menuId),,
  179. "@brief Adds a new menu to the menu bar.\n\n"
  180. "@param menuText Text to display for the new menu item.\n"
  181. "@param menuId ID for the new menu item.\n"
  182. "@tsexample\n"
  183. "// Define the menu text\n"
  184. "%menuText = \"New Menu\";\n\n"
  185. "// Define the menu ID.\n"
  186. "%menuId = \"2\";\n\n"
  187. "// Inform the GuiMenuBar control to add the new menu\n"
  188. "%thisGuiMenuBar.addMenu(%menuText,%menuId);\n"
  189. "@endtsexample\n\n"
  190. "@see GuiTickCtrl")
  191. {
  192. if(dIsdigit(menuText[0]))
  193. {
  194. Con::errorf("Cannot add menu %s (id = %s). First character of a menu's text cannot be a digit.", menuText, menuId);
  195. return;
  196. }
  197. object->addMenu(menuText, menuId);
  198. }
  199. DefineEngineMethod(GuiMenuBar, addMenuItem, void, (const char* targetMenu, const char* menuItemText, S32 menuItemId, const char* accelerator, int checkGroup, const char *cmd),
  200. ("","",0,nullAsType<const char*>(),-1,""),
  201. "@brief Adds a menu item to the specified menu. The menu argument can be either the text of a menu or its id.\n\n"
  202. "@param menu Menu name or menu Id to add the new item to.\n"
  203. "@param menuItemText Text for the new menu item.\n"
  204. "@param menuItemId Id for the new menu item.\n"
  205. "@param accelerator Accelerator key for the new menu item.\n"
  206. "@param checkGroup Check group to include this menu item in.\n"
  207. "@tsexample\n"
  208. "// Define the menu we wish to add the item to\n"
  209. "%targetMenu = \"New Menu\"; or %menu = \"4\";\n\n"
  210. "// Define the text for the new menu item\n"
  211. "%menuItemText = \"Menu Item\";\n\n"
  212. "// Define the id for the new menu item\n"
  213. "%menuItemId = \"3\";\n\n"
  214. "// Set the accelerator key to toggle this menu item with\n"
  215. "%accelerator = \"n\";\n\n"
  216. "// Define the Check Group that this menu item will be in, if we want it to be in a check group. -1 sets it in no check group.\n"
  217. "%checkGroup = \"4\";\n\n"
  218. "// Inform the GuiMenuBar control to add the new menu item with the defined fields\n"
  219. "%thisGuiMenuBar.addMenuItem(%menu,%menuItemText,%menuItemId,%accelerator,%checkGroup);\n"
  220. "@endtsexample\n\n"
  221. "@see GuiTickCtrl")
  222. {
  223. if(dIsdigit(menuItemText[0]))
  224. {
  225. Con::errorf("Cannot add menu item %s (id = %s). First character of a menu item's text cannot be a digit.", menuItemText, menuItemId);
  226. return;
  227. }
  228. GuiMenuBar::Menu *menu = object->findMenu(targetMenu);
  229. if(!menu)
  230. {
  231. Con::errorf("Cannot find menu %s for addMenuItem.", targetMenu);
  232. return;
  233. }
  234. object->addMenuItem(menu, menuItemText, menuItemId, accelerator != NULL ? accelerator : "", checkGroup == -1 ? -1 : checkGroup, cmd);
  235. }
  236. DefineEngineMethod(GuiMenuBar, setMenuItemEnable, void, (const char* menuTarget, const char* menuItemTarget, bool enabled),,
  237. "@brief sets the menu item to enabled or disabled based on the enable parameter.\n"
  238. "The specified menu and menu item can either be text or ids.\n\n"
  239. "Detailed description\n\n"
  240. "@param menuTarget Menu to work in\n"
  241. "@param menuItemTarget The menu item inside of the menu to enable or disable\n"
  242. "@param enabled Boolean enable / disable value.\n"
  243. "@tsexample\n"
  244. "// Define the menu\n"
  245. "%menu = \"New Menu\"; or %menu = \"4\";\n\n"
  246. "// Define the menu item\n"
  247. "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n"
  248. "// Define the enabled state\n"
  249. "%enabled = \"true\";\n\n"
  250. "// Inform the GuiMenuBar control to set the enabled state of the requested menu item\n"
  251. "%thisGuiMenuBar.setMenuItemEnable(%menu,%menuItme,%enabled);\n"
  252. "@endtsexample\n\n"
  253. "@see GuiTickCtrl")
  254. {
  255. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  256. if(!menu)
  257. {
  258. Con::errorf("Cannot find menu %s for setMenuItemEnable.", menuTarget);
  259. return;
  260. }
  261. GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget);
  262. if(!menuItem)
  263. {
  264. Con::errorf("Cannot find menu item %s for setMenuItemEnable.", menuItemTarget);
  265. return;
  266. }
  267. menuItem->enabled = enabled;
  268. }
  269. DefineEngineMethod(GuiMenuBar, setCheckmarkBitmapIndex, void, (S32 bitmapindex),,
  270. "@brief Sets the menu bitmap index for the check mark image.\n\n"
  271. "@param bitmapIndex Bitmap index for the check mark image.\n"
  272. "@tsexample\n"
  273. "// Define the bitmap index\n"
  274. "%bitmapIndex = \"2\";\n\n"
  275. "// Inform the GuiMenuBar control of the proper bitmap index for the check mark image\n"
  276. "%thisGuiMenuBar.setCheckmarkBitmapIndex(%bitmapIndex);\n"
  277. "@endtsexample\n\n"
  278. "@see GuiTickCtrl")
  279. {
  280. object->mCheckmarkBitmapIndex = bitmapindex;
  281. }
  282. DefineEngineMethod(GuiMenuBar, setMenuItemChecked, void, (const char* menuTarget, const char* menuItemTarget, bool checked),,
  283. "@brief Sets the menu item bitmap to a check mark, which by default is the first element in\n"
  284. "the bitmap array (although this may be changed with setCheckmarkBitmapIndex()).\n"
  285. "Any other menu items in the menu with the same check group become unchecked if they are checked.\n\n"
  286. "@param menuTarget Menu to work in\n"
  287. "@param menuItem Menu item to affect\n"
  288. "@param checked Whether we are setting it to checked or not\n"
  289. "@tsexample\n"
  290. ""
  291. "@endtsexample\n\n"
  292. "@return If not void, return value and description\n\n"
  293. "@see References")
  294. {
  295. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  296. if(!menu)
  297. {
  298. Con::errorf("Cannot find menu %s for setMenuItemChecked.", menuTarget);
  299. return;
  300. }
  301. GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget);
  302. if(!menuItem)
  303. {
  304. Con::errorf("Cannot find menu item %s for setMenuItemChecked.", menuItemTarget);
  305. return;
  306. }
  307. if(checked && menuItem->checkGroup != -1)
  308. {
  309. // first, uncheck everything in the group:
  310. for(GuiMenuBar::MenuItem *itemWalk = menu->firstMenuItem; itemWalk; itemWalk = itemWalk->nextMenuItem)
  311. if(itemWalk->checkGroup == menuItem->checkGroup && itemWalk->bitmapIndex == object->mCheckmarkBitmapIndex)
  312. itemWalk->bitmapIndex = -1;
  313. }
  314. menuItem->bitmapIndex = checked ? object->mCheckmarkBitmapIndex : -1;
  315. }
  316. DefineEngineMethod(GuiMenuBar, setMenuText, void, (const char* menuTarget, const char* newMenuText),,
  317. "@brief Sets the text of the specified menu to the new string.\n\n"
  318. "@param menuTarget Menu to affect\n"
  319. "@param newMenuText New menu text\n"
  320. "@tsexample\n"
  321. "// Define the menu to affect"
  322. "%menu = \"New Menu\"; or %menu = \"3\";\n\n"
  323. "// Define the text to change the menu to\n"
  324. "%newMenuText = \"Still a New Menu\";\n\n"
  325. "// Inform the GuiMenuBar control to change the defined menu to the defined text\n"
  326. "%thisGuiMenuBar.setMenuText(%menu,%newMenuText);\n"
  327. "@endtsexample\n\n"
  328. "@see GuiTickCtrl")
  329. {
  330. if(dIsdigit(menuTarget[0]))
  331. {
  332. Con::errorf("Cannot name menu %s to %s. First character of a menu's text cannot be a digit.", menuTarget, newMenuText);
  333. return;
  334. }
  335. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  336. if(!menu)
  337. {
  338. Con::errorf("Cannot find menu %s for setMenuText.", menuTarget);
  339. return;
  340. }
  341. dFree(menu->text);
  342. menu->text = dStrdup(newMenuText);
  343. object->menuBarDirty = true;
  344. }
  345. DefineEngineMethod(GuiMenuBar, setMenuBitmapIndex, void, (const char* menuTarget, S32 bitmapindex, bool bitmaponly, bool drawborder),,
  346. "@brief Sets the bitmap index for the menu and toggles rendering only the bitmap.\n\n"
  347. "@param menuTarget Menu to affect\n"
  348. "@param bitmapindex Bitmap index to set for the menu\n"
  349. "@param bitmaponly If true, only the bitmap will be rendered\n"
  350. "@param drawborder If true, a border will be drawn around the menu.\n"
  351. "@tsexample\n"
  352. "// Define the menuTarget to affect\n"
  353. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  354. "// Set the bitmap index\n"
  355. "%bitmapIndex = \"5\";\n\n"
  356. "// Set if we are only to render the bitmap or not\n"
  357. "%bitmaponly = \"true\";\n\n"
  358. "// Set if we are rendering a border or not\n"
  359. "%drawborder = \"true\";\n\n"
  360. "// Inform the GuiMenuBar of the bitmap and rendering changes\n"
  361. "%thisGuiMenuBar.setMenuBitmapIndex(%menuTarget,%bitmapIndex,%bitmapOnly,%drawBorder);\n"
  362. "@endtsexample\n\n"
  363. "@see GuiTickCtrl")
  364. {
  365. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  366. if(!menu)
  367. {
  368. Con::errorf("Cannot find menu %s for setMenuBitmapIndex.", menuTarget);
  369. return;
  370. }
  371. menu->bitmapIndex = bitmapindex;
  372. menu->drawBitmapOnly = bitmaponly;
  373. menu->drawBorder = drawborder;
  374. object->menuBarDirty = true;
  375. }
  376. DefineEngineMethod(GuiMenuBar, setMenuVisible, void, (const char* menuTarget, bool visible),,
  377. "@brief Sets the whether or not to display the specified menu.\n\n"
  378. "@param menuTarget Menu item to affect\n"
  379. "@param visible Whether the menu item will be visible or not\n"
  380. "@tsexample\n"
  381. "// Define the menu to work with\n"
  382. "%menuTarget = \"New Menu\"; or %menuTarget = \"4\";\n\n"
  383. "// Define if the menu should be visible or not\n"
  384. "%visible = \"true\";\n\n"
  385. "// Inform the GuiMenuBar control of the new visibility state for the defined menu\n"
  386. "%thisGuiMenuBar.setMenuVisible(%menuTarget,%visible);\n"
  387. "@endtsexample\n\n"
  388. "@see GuiTickCtrl")
  389. {
  390. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  391. if(!menu)
  392. {
  393. Con::errorf("Cannot find menu %s for setMenuVisible.", menuTarget);
  394. return;
  395. }
  396. menu->visible = visible;
  397. object->menuBarDirty = true;
  398. object->setUpdate();
  399. }
  400. DefineEngineMethod(GuiMenuBar, setMenuItemText, void, (const char* menuTarget, const char* menuItemTarget, const char* newMenuItemText),,
  401. "@brief Sets the text of the specified menu item to the new string.\n\n"
  402. "@param menuTarget Menu to affect\n"
  403. "@param menuItem Menu item in the menu to change the text at\n"
  404. "@param newMenuItemText New menu text\n"
  405. "@tsexample\n"
  406. "// Define the menuTarget\n"
  407. "%menuTarget = \"New Menu\"; or %menuTarget = \"4\";\n\n"
  408. "// Define the menuItem\n"
  409. "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n"
  410. "// Define the new text for the menu item\n"
  411. "%newMenuItemText = \"Very New Menu Item\";\n\n"
  412. "// Inform the GuiMenuBar control to change the defined menu item with the new text\n"
  413. "%thisGuiMenuBar.setMenuItemText(%menuTarget,%menuItem,%newMenuItemText);\n"
  414. "@endtsexample\n\n"
  415. "@see GuiTickCtrl")
  416. {
  417. if(dIsdigit(newMenuItemText[0]))
  418. {
  419. Con::errorf("Cannot name menu item %s to %s. First character of a menu item's text cannot be a digit.", menuItemTarget, newMenuItemText);
  420. return;
  421. }
  422. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  423. if(!menu)
  424. {
  425. Con::errorf("Cannot find menu %s for setMenuItemText.", menuTarget);
  426. return;
  427. }
  428. GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget);
  429. if(!menuItem)
  430. {
  431. Con::errorf("Cannot find menu item %s for setMenuItemText.", menuItemTarget);
  432. return;
  433. }
  434. dFree(menuItem->text);
  435. menuItem->text = dStrdup(newMenuItemText);
  436. }
  437. DefineEngineMethod(GuiMenuBar, setMenuItemVisible, void, (const char* menuTarget, const char* menuItemTarget, bool isVisible),,
  438. "@brief Brief Description.\n\n"
  439. "Detailed description\n\n"
  440. "@param menuTarget Menu to affect the menu item in\n"
  441. "@param menuItem Menu item to affect\n"
  442. "@param isVisible Visible state to set the menu item to.\n"
  443. "@tsexample\n"
  444. "// Define the menuTarget\n"
  445. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  446. "// Define the menuItem\n"
  447. "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n"
  448. "// Define the visibility state\n"
  449. "%isVisible = \"true\";\n\n"
  450. "// Inform the GuiMenuBarControl of the visibility state of the defined menu item\n"
  451. "%thisGuiMenuBar.setMenuItemVisible(%menuTarget,%menuItem,%isVisible);\n"
  452. "@endtsexample\n\n"
  453. "@see GuiTickCtrl")
  454. {
  455. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  456. if(!menu)
  457. {
  458. Con::errorf("Cannot find menu %s for setMenuItemVisible.", menuTarget);
  459. return;
  460. }
  461. GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget);
  462. if(!menuItem)
  463. {
  464. Con::errorf("Cannot find menu item %s for setMenuItemVisible.", menuItemTarget);
  465. return;
  466. }
  467. menuItem->visible = isVisible;
  468. }
  469. DefineEngineMethod(GuiMenuBar, setMenuItemBitmap, void, (const char* menuTarget, const char* menuItemTarget, S32 bitmapIndex),,
  470. "@brief Sets the specified menu item bitmap index in the bitmap array. Setting the item's index to -1 will remove any bitmap.\n\n"
  471. "@param menuTarget Menu to affect the menuItem in\n"
  472. "@param menuItem Menu item to affect\n"
  473. "@param bitmapIndex Bitmap index to set the menu item to\n"
  474. "@tsexample\n"
  475. "// Define the menuTarget\n"
  476. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  477. "// Define the menuItem\"\n"
  478. "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n"
  479. "// Define the bitmapIndex\n"
  480. "%bitmapIndex = \"6\";\n\n"
  481. "// Inform the GuiMenuBar control to set the menu item to the defined bitmap\n"
  482. "%thisGuiMenuBar.setMenuItemBitmap(%menuTarget,%menuItem,%bitmapIndex);\n"
  483. "@endtsexample\n\n"
  484. "@see GuiTickCtrl")
  485. {
  486. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  487. if(!menu)
  488. {
  489. Con::errorf("Cannot find menu %s for setMenuItemBitmap.", menuTarget);
  490. return;
  491. }
  492. GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget);
  493. if(!menuItem)
  494. {
  495. Con::errorf("Cannot find menu item %s for setMenuItemBitmap.", menuItemTarget);
  496. return;
  497. }
  498. menuItem->bitmapIndex = bitmapIndex;
  499. }
  500. DefineEngineMethod(GuiMenuBar, removeMenuItem, void, (const char* menuTarget, const char* menuItemTarget),,
  501. "@brief Removes the specified menu item from the menu.\n\n"
  502. "@param menuTarget Menu to affect the menu item in\n"
  503. "@param menuItem Menu item to affect\n"
  504. "@tsexample\n"
  505. "// Define the menuTarget\n"
  506. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  507. "// Define the menuItem\n"
  508. "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n"
  509. "// Request the GuiMenuBar control to remove the define menu item\n"
  510. "%thisGuiMenuBar.removeMenuItem(%menuTarget,%menuItem);\n\n"
  511. "@endtsexample\n\n"
  512. "@see GuiTickCtrl")
  513. {
  514. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  515. if(!menu)
  516. {
  517. Con::errorf("Cannot find menu %s for removeMenuItem.", menuTarget);
  518. return;
  519. }
  520. GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget);
  521. if(!menuItem)
  522. {
  523. Con::errorf("Cannot find menu item %s for removeMenuItem.", menuItemTarget);
  524. return;
  525. }
  526. object->removeMenuItem(menu, menuItem);
  527. }
  528. DefineEngineMethod(GuiMenuBar, clearMenuItems, void, (const char* menuTarget),,
  529. "@brief Removes all the menu items from the specified menu.\n\n"
  530. "@param menuTarget Menu to remove all items from\n"
  531. "@tsexample\n"
  532. "// Define the menuTarget\n"
  533. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  534. "// Inform the GuiMenuBar control to clear all menu items from the defined menu\n"
  535. "%thisGuiMenuBar.clearMenuItems(%menuTarget);\n"
  536. "@endtsexample\n\n"
  537. "@see GuiTickCtrl")
  538. {
  539. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  540. if(!menu)
  541. {
  542. //Con::errorf("Cannot find menu %s for clearMenuItems.", menuTarget);
  543. return;
  544. }
  545. object->clearMenuItems(menu);
  546. }
  547. DefineEngineMethod( GuiMenuBar, removeMenu, void, (const char* menuTarget),,
  548. "@brief Removes the specified menu from the menu bar.\n\n"
  549. "@param menuTarget Menu to remove from the menu bar\n"
  550. "@tsexample\n"
  551. "// Define the menuTarget\n"
  552. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  553. "// Inform the GuiMenuBar to remove the defined menu from the menu bar\n"
  554. "%thisGuiMenuBar.removeMenu(%menuTarget);\n"
  555. "@endtsexample\n\n"
  556. "@see GuiTickCtrl")
  557. {
  558. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  559. if(!menu)
  560. {
  561. //Con::errorf("Cannot find menu %s for removeMenu.", menuTarget);
  562. return;
  563. }
  564. object->clearMenuItems(menu);
  565. object->menuBarDirty = true;
  566. }
  567. //------------------------------------------------------------------------------
  568. // Submenu console methods
  569. //------------------------------------------------------------------------------
  570. DefineEngineMethod(GuiMenuBar, setMenuItemSubmenuState, void, (const char* menuTarget, const char* menuItem, bool isSubmenu),,
  571. "@brief Sets the given menu item to be a submenu.\n\n"
  572. "@param menuTarget Menu to affect a submenu in\n"
  573. "@param menuItem Menu item to affect\n"
  574. "@param isSubmenu Whether or not the menuItem will become a subMenu or not\n"
  575. "@tsexample\n"
  576. "// Define the menuTarget\n"
  577. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  578. "// Define the menuItem\n"
  579. "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n"
  580. "// Define whether or not the Menu Item is a sub menu or not\n"
  581. "%isSubmenu = \"true\";\n\n"
  582. "// Inform the GuiMenuBar control to set the defined menu item to be a submenu or not.\n"
  583. "%thisGuiMenuBar.setMenuItemSubmenuState(%menuTarget,%menuItem,%isSubmenu);\n"
  584. "@endtsexample\n\n"
  585. "@see GuiTickCtrl")
  586. {
  587. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  588. if(!menu)
  589. {
  590. Con::errorf("Cannot find menu %s for setMenuItemSubmenuState.", menuTarget);
  591. return;
  592. }
  593. GuiMenuBar::MenuItem *menuitem = object->findMenuItem(menu, menuItem);
  594. if(!menuitem)
  595. {
  596. Con::errorf("Cannot find menuitem %s for setMenuItemSubmenuState.", menuItem);
  597. return;
  598. }
  599. menuitem->isSubmenu = isSubmenu;
  600. }
  601. DefineEngineMethod(GuiMenuBar, addSubmenuItem, void, (const char* menuTarget, const char* menuItem, const char* submenuItemText,
  602. int submenuItemId, const char* accelerator, int checkGroup),,
  603. "@brief Adds a menu item to the specified menu. The menu argument can be either the text of a menu or its id.\n\n"
  604. "@param menuTarget Menu to affect a submenu in\n"
  605. "@param menuItem Menu item to affect\n"
  606. "@param submenuItemText Text to show for the new submenu\n"
  607. "@param submenuItemId Id for the new submenu\n"
  608. "@param accelerator Accelerator key for the new submenu\n"
  609. "@param checkGroup Which check group the new submenu should be in, or -1 for none.\n"
  610. "@tsexample\n"
  611. "// Define the menuTarget\n"
  612. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  613. "// Define the menuItem\n"
  614. "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n"
  615. "// Define the text for the new submenu\n"
  616. "%submenuItemText = \"New Submenu Item\";\n\n"
  617. "// Define the id for the new submenu\n"
  618. "%submenuItemId = \"4\";\n\n"
  619. "// Define the accelerator key for the new submenu\n"
  620. "%accelerator = \"n\";\n\n"
  621. "// Define the checkgroup for the new submenu\n"
  622. "%checkgroup = \"7\";\n\n"
  623. "// Request the GuiMenuBar control to add the new submenu with the defined information\n"
  624. "%thisGuiMenuBar.addSubmenuItem(%menuTarget,%menuItem,%submenuItemText,%submenuItemId,%accelerator,%checkgroup);\n"
  625. "@endtsexample\n\n"
  626. "@see GuiTickCtrl\n")
  627. {
  628. if(dIsdigit(submenuItemText[0]))
  629. {
  630. Con::errorf("Cannot add submenu item %s (id = %s). First character of a menu item's text cannot be a digit.", submenuItemText, submenuItemId);
  631. return;
  632. }
  633. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  634. if(!menu)
  635. {
  636. Con::errorf("Cannot find menu %s for addMenuItem.", menuTarget);
  637. return;
  638. }
  639. GuiMenuBar::MenuItem *menuitem = object->findMenuItem(menu, menuItem);
  640. if(!menuitem)
  641. {
  642. Con::errorf("Cannot find menuitem %s for addSubmenuItem.", menuItem);
  643. return;
  644. }
  645. object->addSubmenuItem(menu, menuitem, submenuItemText, submenuItemId, !accelerator ? "" : accelerator, checkGroup == -1 ? -1 : checkGroup);
  646. }
  647. DefineEngineMethod(GuiMenuBar, clearSubmenuItems, void, (const char* menuTarget, const char* menuItem),,
  648. "@brief Removes all the menu items from the specified submenu.\n\n"
  649. "@param menuTarget Menu to affect a submenu in\n"
  650. "@param menuItem Menu item to affect\n"
  651. "@tsexample\n"
  652. "// Define the menuTarget\n"
  653. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  654. "// Define the menuItem\n"
  655. "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n"
  656. "// Inform the GuiMenuBar to remove all submenu items from the defined menu item\n"
  657. "%thisGuiMenuBar.clearSubmenuItems(%menuTarget,%menuItem);\n\n"
  658. "@endtsexample\n\n"
  659. "@see GuiControl")
  660. {
  661. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  662. if(!menu)
  663. {
  664. Con::errorf("Cannot find menu %s for clearSubmenuItems.", menuTarget);
  665. return;
  666. }
  667. GuiMenuBar::MenuItem *menuitem = object->findMenuItem(menu, menuItem);
  668. if(!menuitem)
  669. {
  670. Con::errorf("Cannot find menuitem %s for clearSubmenuItems.", menuItem);
  671. return;
  672. }
  673. object->clearSubmenuItems(menuitem);
  674. }
  675. DefineEngineMethod(GuiMenuBar, setSubmenuItemChecked, void, (const char* menuTarget, const char* menuItemTarget, const char* submenuItemText, bool checked),,
  676. "@brief Sets the menu item bitmap to a check mark, which by default is the first element in the\n"
  677. "bitmap array (although this may be changed with setCheckmarkBitmapIndex()).\n"
  678. "Any other menu items in the menu with the same check group become unchecked if they are checked.\n\n"
  679. "@param menuTarget Menu to affect a submenu in\n"
  680. "@param menuItem Menu item to affect\n"
  681. "@param submenuItemText Text to show for submenu\n"
  682. "@param checked Whether or not this submenu item will be checked.\n"
  683. "@tsexample\n"
  684. "// Define the menuTarget\n"
  685. "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n"
  686. "// Define the menuItem\n"
  687. "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n"
  688. "// Define the text for the new submenu\n"
  689. "%submenuItemText = \"Submenu Item\";\n\n"
  690. "// Define if this submenu item should be checked or not\n"
  691. "%checked = \"true\";\n\n"
  692. "// Inform the GuiMenuBar control to set the checked state of the defined submenu item\n"
  693. "%thisGuiMenuBar.setSubmenuItemChecked(%menuTarget,%menuItem,%submenuItemText,%checked);\n"
  694. "@endtsexample\n\n"
  695. "@return If not void, return value and description\n\n"
  696. "@see References")
  697. {
  698. // Find the parent menu
  699. GuiMenuBar::Menu *menu = object->findMenu(menuTarget);
  700. if(!menu)
  701. {
  702. Con::errorf("Cannot find menu %s for setSubmenuItemChecked.", menuTarget);
  703. return;
  704. }
  705. // Find the parent menu item
  706. GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget);
  707. if(!menuItem)
  708. {
  709. Con::errorf("Cannot find menu item %s for setSubmenuItemChecked.", menuItemTarget);
  710. return;
  711. }
  712. // Find the submenu item
  713. GuiMenuBar::MenuItem *submenuItem = object->findSubmenuItem(menu, menuItemTarget, submenuItemText);
  714. if(!submenuItem)
  715. {
  716. Con::errorf("Cannot find submenu item %s for setSubmenuItemChecked.", submenuItemText);
  717. return;
  718. }
  719. if(checked && submenuItem->checkGroup != -1)
  720. {
  721. // first, uncheck everything in the group:
  722. for(GuiMenuBar::MenuItem *itemWalk = menuItem->submenu->firstMenuItem; itemWalk; itemWalk = itemWalk->nextMenuItem)
  723. if(itemWalk->checkGroup == submenuItem->checkGroup && itemWalk->bitmapIndex == object->mCheckmarkBitmapIndex)
  724. itemWalk->bitmapIndex = -1;
  725. }
  726. submenuItem->bitmapIndex = checked ? object->mCheckmarkBitmapIndex : -1;
  727. }
  728. //------------------------------------------------------------------------------
  729. // menu management methods
  730. //------------------------------------------------------------------------------
  731. GuiMenuBar::Menu* GuiMenuBar::sCreateMenu(const char *menuText, U32 menuId)
  732. {
  733. // allocate the menu
  734. Menu *newMenu = new Menu;
  735. newMenu->text = dStrdup(menuText);
  736. newMenu->id = menuId;
  737. newMenu->nextMenu = NULL;
  738. newMenu->firstMenuItem = NULL;
  739. newMenu->visible = true;
  740. // Menu bitmap variables
  741. newMenu->bitmapIndex = -1;
  742. newMenu->drawBitmapOnly = false;
  743. newMenu->drawBorder = true;
  744. return newMenu;
  745. }
  746. void GuiMenuBar::addMenu(GuiMenuBar::Menu *newMenu, S32 pos)
  747. {
  748. // add it to the menu list
  749. menuBarDirty = true;
  750. if (pos == -1)
  751. mMenuList.push_back(newMenu);
  752. else
  753. mMenuList.insert(pos, newMenu);
  754. }
  755. void GuiMenuBar::addMenu(const char *menuText, U32 menuId)
  756. {
  757. Menu *newMenu = sCreateMenu(menuText, menuId);
  758. addMenu(newMenu);
  759. }
  760. GuiMenuBar::Menu *GuiMenuBar::findMenu(const char *menu)
  761. {
  762. if(dIsdigit(menu[0]))
  763. {
  764. U32 id = dAtoi(menu);
  765. for (U32 i = 0; i < mMenuList.size(); ++i)
  766. if (id == mMenuList[i]->id)
  767. return mMenuList[i];
  768. return NULL;
  769. }
  770. else
  771. {
  772. for (U32 i = 0; i < mMenuList.size(); ++i)
  773. if (!dStricmp(menu, mMenuList[i]->text))
  774. return mMenuList[i];
  775. return NULL;
  776. }
  777. }
  778. GuiMenuBar::MenuItem *GuiMenuBar::findMenuItem(Menu *menu, const char *menuItem)
  779. {
  780. if(dIsdigit(menuItem[0]))
  781. {
  782. U32 id = dAtoi(menuItem);
  783. for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem)
  784. if(id == walk->id)
  785. return walk;
  786. return NULL;
  787. }
  788. else
  789. {
  790. for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem)
  791. if(!dStricmp(menuItem, walk->text))
  792. return walk;
  793. return NULL;
  794. }
  795. }
  796. void GuiMenuBar::removeMenu(Menu *menu)
  797. {
  798. menuBarDirty = true;
  799. clearMenuItems(menu);
  800. for (U32 i = 0; i < mMenuList.size(); ++i)
  801. {
  802. if (mMenuList[i] == menu)
  803. {
  804. mMenuList.erase(i);
  805. break;
  806. }
  807. }
  808. }
  809. void GuiMenuBar::removeMenuItem(Menu *menu, MenuItem *menuItem)
  810. {
  811. for(MenuItem **walk = &menu->firstMenuItem; *walk; walk = &(*walk)->nextMenuItem)
  812. {
  813. if(*walk == menuItem)
  814. {
  815. *walk = menuItem->nextMenuItem;
  816. break;
  817. }
  818. }
  819. // If this is a submenu, then be sure to clear the submenu's items
  820. if(menuItem->isSubmenu)
  821. {
  822. clearSubmenuItems(menuItem);
  823. }
  824. dFree(menuItem->text);
  825. dFree(menuItem->accelerator);
  826. delete menuItem;
  827. }
  828. GuiMenuBar::MenuItem* GuiMenuBar::addMenuItem(Menu *menu, const char *text, U32 id, const char *accelerator, S32 checkGroup, const char *cmd )
  829. {
  830. // allocate the new menu item
  831. MenuItem *newMenuItem = new MenuItem;
  832. newMenuItem->text = dStrdup(text);
  833. if(accelerator[0])
  834. newMenuItem->accelerator = dStrdup(accelerator);
  835. else
  836. newMenuItem->accelerator = NULL;
  837. newMenuItem->cmd = cmd;
  838. newMenuItem->id = id;
  839. newMenuItem->checkGroup = checkGroup;
  840. newMenuItem->nextMenuItem = NULL;
  841. newMenuItem->acceleratorIndex = 0;
  842. newMenuItem->enabled = text[0] != '-';
  843. newMenuItem->visible = true;
  844. newMenuItem->bitmapIndex = -1;
  845. // Default to not having a submenu
  846. newMenuItem->isSubmenu = false;
  847. newMenuItem->submenu = NULL;
  848. newMenuItem->submenuParentMenu = NULL;
  849. // link it into the menu's menu item list
  850. if(menu)
  851. {
  852. MenuItem **walk = &menu->firstMenuItem;
  853. while(*walk)
  854. walk = &(*walk)->nextMenuItem;
  855. *walk = newMenuItem;
  856. }
  857. return newMenuItem;
  858. }
  859. GuiMenuBar::MenuItem* GuiMenuBar::addMenuItem(Menu *menu, MenuItem* newMenuItem)
  860. {
  861. // link it into the menu's menu item list
  862. if(menu)
  863. {
  864. MenuItem **walk = &menu->firstMenuItem;
  865. while(*walk)
  866. walk = &(*walk)->nextMenuItem;
  867. *walk = newMenuItem;
  868. }
  869. return newMenuItem;
  870. }
  871. void GuiMenuBar::clearMenuItems(Menu *menu)
  872. {
  873. while(menu->firstMenuItem)
  874. removeMenuItem(menu, menu->firstMenuItem);
  875. }
  876. void GuiMenuBar::clearMenus()
  877. {
  878. mMenuList.clear();
  879. }
  880. void GuiMenuBar::attachToMenuBar(Menu* menu, S32 pos)
  881. {
  882. addMenu(menu, pos);
  883. }
  884. void GuiMenuBar::removeFromMenuBar(Menu* menu)
  885. {
  886. menuBarDirty = true;
  887. for (U32 i = 0; i < mMenuList.size(); ++i)
  888. {
  889. if (mMenuList[i] == menu)
  890. {
  891. mMenuList.erase(i);
  892. break;
  893. }
  894. }
  895. }
  896. //------------------------------------------------------------------------------
  897. // Submenu methods
  898. //------------------------------------------------------------------------------
  899. // This method will return the MenuItem class of of a submenu's menu item given
  900. // its parent menu and parent menuitem. If the menuitem ID is used, then the submenu
  901. // ID must also be used.
  902. GuiMenuBar::MenuItem *GuiMenuBar::findSubmenuItem(Menu *menu, const char *menuItem, const char *submenuItem)
  903. {
  904. if(dIsdigit(menuItem[0]))
  905. {
  906. // Search by ID
  907. U32 id = dAtoi(menuItem);
  908. for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem)
  909. if(id == walk->id)
  910. {
  911. if(walk->isSubmenu && walk->submenu)
  912. {
  913. return GuiMenuBar::findMenuItem(walk->submenu, submenuItem);
  914. }
  915. return NULL;
  916. }
  917. return NULL;
  918. }
  919. else
  920. {
  921. // Search by name
  922. for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem)
  923. if(!dStricmp(menuItem, walk->text))
  924. {
  925. if(walk->isSubmenu && walk->submenu)
  926. {
  927. return GuiMenuBar::findMenuItem(walk->submenu, submenuItem);
  928. }
  929. return NULL;
  930. }
  931. return NULL;
  932. }
  933. }
  934. GuiMenuBar::MenuItem* GuiMenuBar::findSubmenuItem(MenuItem *menuItem, const char *submenuItem)
  935. {
  936. if( !menuItem->isSubmenu )
  937. return NULL;
  938. return GuiMenuBar::findMenuItem( menuItem->submenu, submenuItem );
  939. }
  940. // Add a menuitem to the given submenu
  941. void GuiMenuBar::addSubmenuItem(Menu *menu, MenuItem *submenu, const char *text, U32 id, const char *accelerator, S32 checkGroup)
  942. {
  943. // Check that the given menu item supports a submenu
  944. if(submenu && !submenu->isSubmenu)
  945. {
  946. Con::errorf("GuiMenuBar::addSubmenuItem: Attempting to add menuitem '%s' to an invalid submenu",text);
  947. return;
  948. }
  949. // allocate the new menu item
  950. MenuItem *newMenuItem = new MenuItem;
  951. newMenuItem->text = dStrdup(text);
  952. if(accelerator[0])
  953. newMenuItem->accelerator = dStrdup(accelerator);
  954. else
  955. newMenuItem->accelerator = NULL;
  956. newMenuItem->id = id;
  957. newMenuItem->checkGroup = checkGroup;
  958. newMenuItem->nextMenuItem = NULL;
  959. newMenuItem->acceleratorIndex = 0;
  960. newMenuItem->enabled = (dStrlen(text) > 1 || text[0] != '-');
  961. newMenuItem->visible = true;
  962. newMenuItem->bitmapIndex = -1;
  963. // Default to not having a submenu
  964. newMenuItem->isSubmenu = false;
  965. newMenuItem->submenu = NULL;
  966. // Point back to the submenu's menu
  967. newMenuItem->submenuParentMenu = menu;
  968. // link it into the menu's menu item list
  969. MenuItem **walk = &submenu->submenu->firstMenuItem;
  970. while(*walk)
  971. walk = &(*walk)->nextMenuItem;
  972. *walk = newMenuItem;
  973. }
  974. void GuiMenuBar::addSubmenuItem(Menu *menu, MenuItem *submenu, MenuItem *newMenuItem )
  975. {
  976. AssertFatal( submenu && newMenuItem, "");
  977. // Point back to the submenu's menu
  978. newMenuItem->submenuParentMenu = menu;
  979. // link it into the menu's menu item list
  980. MenuItem **walk = &submenu->submenu->firstMenuItem;
  981. while(*walk)
  982. walk = &(*walk)->nextMenuItem;
  983. *walk = newMenuItem;
  984. }
  985. // Remove a submenu item
  986. void GuiMenuBar::removeSubmenuItem(MenuItem *menuItem, MenuItem *submenuItem)
  987. {
  988. // Check that the given menu item supports a submenu
  989. if(menuItem && !menuItem->isSubmenu)
  990. {
  991. Con::errorf("GuiMenuBar::removeSubmenuItem: Attempting to remove submenuitem '%s' from an invalid submenu",submenuItem->text);
  992. return;
  993. }
  994. GuiMenuBar::removeMenuItem(menuItem->submenu, submenuItem);
  995. }
  996. // Clear all menuitems from a submenu
  997. void GuiMenuBar::clearSubmenuItems(MenuItem *menuitem)
  998. {
  999. // Check that the given menu item supports a submenu
  1000. if(menuitem && !menuitem->isSubmenu)
  1001. {
  1002. Con::errorf("GuiMenuBar::clearSubmenuItems: Attempting to clear an invalid submenu");
  1003. return;
  1004. }
  1005. while(menuitem->submenu->firstMenuItem)
  1006. removeSubmenuItem(menuitem, menuitem->submenu->firstMenuItem);
  1007. }
  1008. //------------------------------------------------------------------------------
  1009. // initialization, input and render methods
  1010. //------------------------------------------------------------------------------
  1011. GuiMenuBar::GuiMenuBar()
  1012. {
  1013. mMenuList.clear();
  1014. menuBarDirty = true;
  1015. mouseDownMenu = NULL;
  1016. mouseOverMenu = NULL;
  1017. mCurAcceleratorIndex = 0;
  1018. mBackground = NULL;
  1019. mPadding = 0;
  1020. mCheckmarkBitmapIndex = 0; // Default to the first image in the bitmap array for the check mark
  1021. mHorizontalMargin = 6; // Default number of pixels on the left and right side of a manu's text
  1022. mVerticalMargin = 1; // Default number of pixels on the top and bottom of a menu's text
  1023. mBitmapMargin = 2; // Default number of pixels between a menu's bitmap and text
  1024. // Added:
  1025. mouseDownSubmenu = NULL;
  1026. mouseOverSubmenu = NULL;
  1027. mSubmenuBackground = NULL;
  1028. mSubmenuTextList = NULL;
  1029. mMouseOverCounter = 0;
  1030. mCountMouseOver = false;
  1031. mMouseHoverAmount = 30;
  1032. setProcessTicks(false);
  1033. }
  1034. void GuiMenuBar::initPersistFields()
  1035. {
  1036. addField("padding", TypeS32, Offset( mPadding, GuiMenuBar ),"Extra padding to add to the bounds of the control.\n");
  1037. Parent::initPersistFields();
  1038. }
  1039. bool GuiMenuBar::onWake()
  1040. {
  1041. if(!Parent::onWake())
  1042. return false;
  1043. mProfile->constructBitmapArray(); // if a bitmap was specified...
  1044. maxBitmapSize.set(0,0);
  1045. S32 numBitmaps = mProfile->mBitmapArrayRects.size();
  1046. if(numBitmaps)
  1047. {
  1048. RectI *bitmapBounds = mProfile->mBitmapArrayRects.address();
  1049. for(S32 i = 0; i < numBitmaps; i++)
  1050. {
  1051. if(bitmapBounds[i].extent.x > maxBitmapSize.x)
  1052. maxBitmapSize.x = bitmapBounds[i].extent.x;
  1053. if(bitmapBounds[i].extent.y > maxBitmapSize.y)
  1054. maxBitmapSize.y = bitmapBounds[i].extent.y;
  1055. }
  1056. }
  1057. return true;
  1058. }
  1059. GuiMenuBar::Menu *GuiMenuBar::findHitMenu(Point2I mousePoint)
  1060. {
  1061. Point2I pos = globalToLocalCoord(mousePoint);
  1062. for (U32 i = 0; i < mMenuList.size(); ++i)
  1063. if (mMenuList[i]->visible && mMenuList[i]->bounds.pointInRect(pos))
  1064. return mMenuList[i];
  1065. return NULL;
  1066. }
  1067. void GuiMenuBar::onPreRender()
  1068. {
  1069. Parent::onPreRender();
  1070. if(menuBarDirty)
  1071. {
  1072. menuBarDirty = false;
  1073. U32 curX = mPadding;
  1074. for (U32 i = 0; i < mMenuList.size(); ++i)
  1075. {
  1076. if (!mMenuList[i]->visible)
  1077. continue;
  1078. // Bounds depends on if there is a bitmap to be drawn or not
  1079. if (mMenuList[i]->bitmapIndex == -1)
  1080. {
  1081. // Text only
  1082. mMenuList[i]->bounds.set(curX, 0, mProfile->mFont->getStrWidth(mMenuList[i]->text) + (mHorizontalMargin * 2), getHeight() - (mVerticalMargin * 2));
  1083. } else
  1084. {
  1085. // Will the bitmap and text be draw?
  1086. if (!mMenuList[i]->drawBitmapOnly)
  1087. {
  1088. // Draw the bitmap and the text
  1089. RectI *bitmapBounds = mProfile->mBitmapArrayRects.address();
  1090. mMenuList[i]->bounds.set(curX, 0, bitmapBounds[mMenuList[i]->bitmapIndex].extent.x + mProfile->mFont->getStrWidth(mMenuList[i]->text) + (mHorizontalMargin * 2), getHeight() + (mVerticalMargin * 2));
  1091. } else
  1092. {
  1093. // Only the bitmap will be drawn
  1094. RectI *bitmapBounds = mProfile->mBitmapArrayRects.address();
  1095. mMenuList[i]->bounds.set(curX, 0, bitmapBounds[mMenuList[i]->bitmapIndex].extent.x + mBitmapMargin + (mHorizontalMargin * 2), getHeight() + (mVerticalMargin * 2));
  1096. }
  1097. }
  1098. curX += mMenuList[i]->bounds.extent.x;
  1099. }
  1100. mouseOverMenu = NULL;
  1101. mouseDownMenu = NULL;
  1102. }
  1103. }
  1104. void GuiMenuBar::checkMenuMouseMove(const GuiEvent &event)
  1105. {
  1106. Menu *hit = findHitMenu(event.mousePoint);
  1107. if(hit && hit != mouseDownMenu)
  1108. {
  1109. // gotta close out the current menu...
  1110. mTextList->setSelectedCell(Point2I(-1, -1));
  1111. closeMenu();
  1112. mouseOverMenu = mouseDownMenu = hit;
  1113. setUpdate();
  1114. onAction();
  1115. }
  1116. }
  1117. void GuiMenuBar::onMouseMove(const GuiEvent &event)
  1118. {
  1119. Menu *hit = findHitMenu(event.mousePoint);
  1120. if(hit != mouseOverMenu)
  1121. {
  1122. // If we need to, reset the mouse over menu counter and indicate
  1123. // that we should track it.
  1124. if(hit)
  1125. mMouseOverCounter = 0;
  1126. if(!mCountMouseOver)
  1127. {
  1128. // We've never started the counter, so start it.
  1129. if(hit)
  1130. mCountMouseOver = true;
  1131. }
  1132. mouseOverMenu = hit;
  1133. setUpdate();
  1134. }
  1135. }
  1136. void GuiMenuBar::onMouseLeave(const GuiEvent &event)
  1137. {
  1138. if(mouseOverMenu)
  1139. setUpdate();
  1140. mouseOverMenu = NULL;
  1141. // As we've left the control, don't track how long the mouse has been
  1142. // within it.
  1143. if(mCountMouseOver && mMouseOverCounter >= mMouseHoverAmount)
  1144. {
  1145. onMouseInMenu_callback(false); // Last parameter indicates if we've entered or left the menu
  1146. }
  1147. mCountMouseOver = false;
  1148. mMouseOverCounter = 0;
  1149. }
  1150. void GuiMenuBar::onMouseDragged(const GuiEvent &event)
  1151. {
  1152. Menu *hit = findHitMenu(event.mousePoint);
  1153. if(hit != mouseOverMenu)
  1154. {
  1155. // If we need to, reset the mouse over menu counter and indicate
  1156. // that we should track it.
  1157. if(hit)
  1158. mMouseOverCounter = 0;
  1159. if(!mCountMouseOver)
  1160. {
  1161. // We've never started the counter, so start it.
  1162. if(hit)
  1163. mCountMouseOver = true;
  1164. }
  1165. mouseOverMenu = hit;
  1166. mouseDownMenu = hit;
  1167. setUpdate();
  1168. onAction();
  1169. }
  1170. }
  1171. void GuiMenuBar::onMouseDown(const GuiEvent &event)
  1172. {
  1173. mouseDownMenu = mouseOverMenu = findHitMenu(event.mousePoint);
  1174. setUpdate();
  1175. onAction();
  1176. }
  1177. void GuiMenuBar::onMouseUp(const GuiEvent &event)
  1178. {
  1179. mouseDownMenu = NULL;
  1180. setUpdate();
  1181. }
  1182. void GuiMenuBar::onRender(Point2I offset, const RectI &updateRect)
  1183. {
  1184. RectI ctrlRect(offset, getExtent());
  1185. GFXDrawUtil* drawUtil = GFX->getDrawUtil();
  1186. //if opaque, fill the update rect with the fill color
  1187. if (mProfile->mOpaque)
  1188. drawUtil->drawRectFill(RectI(offset, getExtent()), mProfile->mFillColor);
  1189. //if there's a border, draw the border
  1190. if (mProfile->mBorder)
  1191. renderBorder(ctrlRect, mProfile);
  1192. for (U32 i = 0; i < mMenuList.size(); ++i)
  1193. {
  1194. if (!mMenuList[i]->visible)
  1195. continue;
  1196. ColorI fontColor = mProfile->mFontColor;
  1197. RectI bounds = mMenuList[i]->bounds;
  1198. bounds.point += offset;
  1199. Point2I start;
  1200. start.x = mMenuList[i]->bounds.point.x + mHorizontalMargin;
  1201. start.y = mMenuList[i]->bounds.point.y + (mMenuList[i]->bounds.extent.y - mProfile->mFont->getHeight()) / 2;
  1202. // Draw the border
  1203. if (mMenuList[i]->drawBorder)
  1204. {
  1205. RectI highlightBounds = bounds;
  1206. highlightBounds.inset(1,1);
  1207. if (mMenuList[i] == mouseDownMenu)
  1208. renderFilledBorder(highlightBounds, mProfile->mBorderColorHL, mProfile->mFillColorHL );
  1209. else if (mMenuList[i] == mouseOverMenu && mouseDownMenu == NULL)
  1210. renderFilledBorder(highlightBounds, mProfile->mBorderColorHL, mProfile->mFillColorHL);
  1211. }
  1212. // Do we draw a bitmap?
  1213. if (mMenuList[i]->bitmapIndex != -1)
  1214. {
  1215. S32 index = mMenuList[i]->bitmapIndex * 3;
  1216. if (mMenuList[i] == mouseDownMenu)
  1217. ++index;
  1218. else if (mMenuList[i] == mouseOverMenu && mouseDownMenu == NULL)
  1219. index += 2;
  1220. RectI rect = mProfile->mBitmapArrayRects[index];
  1221. Point2I bitmapstart(start);
  1222. bitmapstart.y = mMenuList[i]->bounds.point.y + (mMenuList[i]->bounds.extent.y - rect.extent.y) / 2;
  1223. drawUtil->clearBitmapModulation();
  1224. drawUtil->drawBitmapSR( mProfile->mTextureObject, offset + bitmapstart, rect);
  1225. // Should we also draw the text?
  1226. if (!mMenuList[i]->drawBitmapOnly)
  1227. {
  1228. start.x += mBitmapMargin;
  1229. drawUtil->setBitmapModulation( fontColor );
  1230. drawUtil->drawText(mProfile->mFont, start + offset, mMenuList[i]->text, mProfile->mFontColors);
  1231. }
  1232. } else
  1233. {
  1234. drawUtil->setBitmapModulation( fontColor );
  1235. drawUtil->drawText(mProfile->mFont, start + offset, mMenuList[i]->text, mProfile->mFontColors);
  1236. }
  1237. }
  1238. renderChildControls( offset, updateRect );
  1239. }
  1240. void GuiMenuBar::buildWindowAcceleratorMap( WindowInputGenerator &inputGenerator )
  1241. {
  1242. // ok, accelerator map is cleared...
  1243. // add all our keys:
  1244. mCurAcceleratorIndex = 1;
  1245. for (U32 i = 0; i < mMenuList.size(); ++i)
  1246. {
  1247. for (MenuItem *item = mMenuList[i]->firstMenuItem; item; item = item->nextMenuItem)
  1248. {
  1249. if(!item->accelerator)
  1250. {
  1251. item->accelerator = 0;
  1252. continue;
  1253. }
  1254. EventDescriptor accelEvent;
  1255. ActionMap::createEventDescriptor(item->accelerator, &accelEvent);
  1256. //now we have a modifier, and a key, add them to the canvas
  1257. inputGenerator.addAcceleratorKey( this, item->cmd, accelEvent.eventCode, accelEvent.flags);
  1258. item->acceleratorIndex = mCurAcceleratorIndex;
  1259. mCurAcceleratorIndex++;
  1260. }
  1261. }
  1262. }
  1263. void GuiMenuBar::removeWindowAcceleratorMap( WindowInputGenerator &inputGenerator )
  1264. {
  1265. inputGenerator.removeAcceleratorKeys( this );
  1266. }
  1267. void GuiMenuBar::acceleratorKeyPress(U32 index)
  1268. {
  1269. // loop through all the menus
  1270. // and find the item that corresponds to the accelerator index
  1271. for (U32 i = 0; i < mMenuList.size(); ++i)
  1272. {
  1273. if (!mMenuList[i]->visible)
  1274. continue;
  1275. for (MenuItem *item = mMenuList[i]->firstMenuItem; item; item = item->nextMenuItem)
  1276. {
  1277. if(item->acceleratorIndex == index)
  1278. {
  1279. // first, call the script callback for menu selection:
  1280. onMenuSelect_callback(mMenuList[i]->id, mMenuList[i]->text);
  1281. if(item->visible)
  1282. menuItemSelected(mMenuList[i], item);
  1283. return;
  1284. }
  1285. }
  1286. }
  1287. }
  1288. //------------------------------------------------------------------------------
  1289. // Menu display class methods
  1290. //------------------------------------------------------------------------------
  1291. GuiMenuBackgroundCtrl::GuiMenuBackgroundCtrl(GuiMenuBar *ctrl, GuiMenuTextListCtrl *textList)
  1292. {
  1293. mMenuBarCtrl = ctrl;
  1294. mTextList = textList;
  1295. }
  1296. void GuiMenuBackgroundCtrl::onMouseDown(const GuiEvent &event)
  1297. {
  1298. mTextList->setSelectedCell(Point2I(-1,-1));
  1299. mMenuBarCtrl->closeMenu();
  1300. }
  1301. void GuiMenuBackgroundCtrl::onMouseMove(const GuiEvent &event)
  1302. {
  1303. GuiCanvas *root = getRoot();
  1304. GuiControl *ctrlHit = root->findHitControl(event.mousePoint, mLayer - 1);
  1305. if(ctrlHit == mMenuBarCtrl) // see if the current mouse over menu is right...
  1306. mMenuBarCtrl->checkMenuMouseMove(event);
  1307. }
  1308. void GuiMenuBackgroundCtrl::onMouseDragged(const GuiEvent &event)
  1309. {
  1310. GuiCanvas *root = getRoot();
  1311. GuiControl *ctrlHit = root->findHitControl(event.mousePoint, mLayer - 1);
  1312. if(ctrlHit == mMenuBarCtrl) // see if the current mouse over menu is right...
  1313. mMenuBarCtrl->checkMenuMouseMove(event);
  1314. }
  1315. GuiMenuTextListCtrl::GuiMenuTextListCtrl(GuiMenuBar *ctrl)
  1316. {
  1317. mMenuBarCtrl = ctrl;
  1318. isSubMenu = false; // Added
  1319. }
  1320. void GuiMenuTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
  1321. {
  1322. if(dStrcmp(mList[cell.y].text + 3, "-\t")) // Was: dStrcmp(mList[cell.y].text + 2, "-\t")) but has been changed to take into account the submenu flag
  1323. Parent::onRenderCell(offset, cell, selected, mouseOver);
  1324. else
  1325. {
  1326. S32 yp = offset.y + mCellSize.y / 2;
  1327. GFX->getDrawUtil()->drawLine(offset.x, yp, offset.x + mCellSize.x, yp, ColorI(128,128,128));
  1328. GFX->getDrawUtil()->drawLine(offset.x, yp+1, offset.x + mCellSize.x, yp+1, ColorI(255,255,255));
  1329. }
  1330. // now see if there's a bitmap...
  1331. U8 idx = mList[cell.y].text[0];
  1332. if(idx != 1)
  1333. {
  1334. // there's a bitmap...
  1335. U32 index = U32(idx - 2) * 3;
  1336. if(!mList[cell.y].active)
  1337. index += 2;
  1338. else if(selected || mouseOver)
  1339. index ++;
  1340. RectI rect = mProfile->mBitmapArrayRects[index];
  1341. Point2I off = mMenuBarCtrl->maxBitmapSize - rect.extent;
  1342. off /= 2;
  1343. GFX->getDrawUtil()->clearBitmapModulation();
  1344. GFX->getDrawUtil()->drawBitmapSR(mProfile->mTextureObject, offset + off, rect);
  1345. }
  1346. // Check if this is a submenu
  1347. idx = mList[cell.y].text[1];
  1348. if(idx != 1)
  1349. {
  1350. // This is a submenu, so draw an arrow
  1351. S32 left = offset.x + mCellSize.x - 12;
  1352. S32 right = left + 8;
  1353. S32 top = mCellSize.y / 2 + offset.y - 4;
  1354. S32 bottom = top + 8;
  1355. S32 middle = top + 4;
  1356. PrimBuild::begin( GFXTriangleList, 3 );
  1357. if( selected || mouseOver )
  1358. PrimBuild::color( mProfile->mFontColorHL );
  1359. else
  1360. PrimBuild::color( mProfile->mFontColor );
  1361. PrimBuild::vertex2i( left, top );
  1362. PrimBuild::vertex2i( right, middle );
  1363. PrimBuild::vertex2i( left, bottom );
  1364. PrimBuild::end();
  1365. }
  1366. }
  1367. bool GuiMenuTextListCtrl::onKeyDown(const GuiEvent &event)
  1368. {
  1369. //if the control is a dead end, don't process the input:
  1370. if ( !mVisible || !mActive || !mAwake )
  1371. return false;
  1372. //see if the key down is a <return> or not
  1373. if ( event.modifier == 0 )
  1374. {
  1375. if ( event.keyCode == KEY_RETURN )
  1376. {
  1377. mMenuBarCtrl->closeMenu();
  1378. return true;
  1379. }
  1380. else if ( event.keyCode == KEY_ESCAPE )
  1381. {
  1382. mSelectedCell.set( -1, -1 );
  1383. mMenuBarCtrl->closeMenu();
  1384. return true;
  1385. }
  1386. }
  1387. //otherwise, pass the event to it's parent
  1388. return Parent::onKeyDown(event);
  1389. }
  1390. void GuiMenuTextListCtrl::onMouseDown(const GuiEvent &event)
  1391. {
  1392. Parent::onMouseDown(event);
  1393. }
  1394. void GuiMenuTextListCtrl::onMouseUp(const GuiEvent &event)
  1395. {
  1396. Parent::onMouseUp(event);
  1397. mMenuBarCtrl->closeMenu();
  1398. }
  1399. void GuiMenuTextListCtrl::onCellHighlighted(Point2I cell)
  1400. {
  1401. // If this text list control is part of a submenu, then don't worry about
  1402. // passing this along
  1403. if(!isSubMenu)
  1404. {
  1405. RectI globalbounds(getBounds());
  1406. Point2I globalpoint = localToGlobalCoord(globalbounds.point);
  1407. globalbounds.point = globalpoint;
  1408. mMenuBarCtrl->highlightedMenuItem(cell.y, globalbounds, mCellSize);
  1409. }
  1410. }
  1411. //------------------------------------------------------------------------------
  1412. // Submenu display class methods
  1413. //------------------------------------------------------------------------------
  1414. GuiSubmenuBackgroundCtrl::GuiSubmenuBackgroundCtrl(GuiMenuBar *ctrl, GuiMenuTextListCtrl *textList) : GuiMenuBackgroundCtrl(ctrl, textList)
  1415. {
  1416. }
  1417. void GuiSubmenuBackgroundCtrl::onMouseDown(const GuiEvent &event)
  1418. {
  1419. mTextList->setSelectedCell(Point2I(-1,-1));
  1420. mMenuBarCtrl->closeMenu();
  1421. }
  1422. bool GuiSubmenuBackgroundCtrl::pointInControl(const Point2I& parentCoordPoint)
  1423. {
  1424. S32 xt = parentCoordPoint.x - getLeft();
  1425. S32 yt = parentCoordPoint.y - getTop();
  1426. if(findHitControl(Point2I(xt,yt)) == this)
  1427. return false;
  1428. else
  1429. return true;
  1430. // return xt >= 0 && yt >= 0 && xt < getWidth() && yt < getHeight();
  1431. }
  1432. //------------------------------------------------------------------------------
  1433. void GuiMenuBar::menuItemSelected(GuiMenuBar::Menu *menu, GuiMenuBar::MenuItem *item)
  1434. {
  1435. if(item->enabled)
  1436. onMenuItemSelect_callback(menu->id, menu->text, item->id, item->text);
  1437. }
  1438. void GuiMenuBar::onSleep()
  1439. {
  1440. if(mBackground) // a menu is up?
  1441. {
  1442. mTextList->setSelectedCell(Point2I(-1, -1));
  1443. closeMenu();
  1444. }
  1445. Parent::onSleep();
  1446. }
  1447. void GuiMenuBar::closeMenu()
  1448. {
  1449. // First close any open submenu
  1450. closeSubmenu();
  1451. // Get the selection from the text list:
  1452. S32 selectionIndex = mTextList->getSelectedCell().y;
  1453. // Pop the background:
  1454. if( getRoot() )
  1455. getRoot()->popDialogControl(mBackground);
  1456. else
  1457. return;
  1458. // Kill the popup:
  1459. mBackground->deleteObject();
  1460. mBackground = NULL;
  1461. // Now perform the popup action:
  1462. if ( selectionIndex != -1 )
  1463. {
  1464. MenuItem *list = mouseDownMenu->firstMenuItem;
  1465. while(selectionIndex && list)
  1466. {
  1467. list = list->nextMenuItem;
  1468. selectionIndex--;
  1469. }
  1470. if(list)
  1471. menuItemSelected(mouseDownMenu, list);
  1472. }
  1473. mouseDownMenu = NULL;
  1474. }
  1475. // Called when a menu item is highlighted by the mouse
  1476. void GuiMenuBar::highlightedMenuItem(S32 selectionIndex, const RectI& bounds, Point2I cellSize)
  1477. {
  1478. S32 selstore = selectionIndex;
  1479. // Now perform the popup action:
  1480. if ( selectionIndex != -1 )
  1481. {
  1482. MenuItem *list = mouseDownMenu->firstMenuItem;
  1483. while(selectionIndex && list)
  1484. {
  1485. list = list->nextMenuItem;
  1486. selectionIndex--;
  1487. }
  1488. if(list)
  1489. {
  1490. // If the highlighted item has changed...
  1491. if(mouseOverSubmenu != list)
  1492. {
  1493. closeSubmenu();
  1494. mouseOverSubmenu = NULL;
  1495. // Check if this is a submenu. If so, open the submenu.
  1496. if(list->isSubmenu)
  1497. {
  1498. // If there are submenu items, then open the submenu
  1499. if(list->submenu->firstMenuItem)
  1500. {
  1501. mouseOverSubmenu = list;
  1502. onSubmenuAction(selstore, bounds, cellSize);
  1503. }
  1504. }
  1505. }
  1506. }
  1507. }
  1508. }
  1509. //------------------------------------------------------------------------------
  1510. void GuiMenuBar::onAction()
  1511. {
  1512. if(!mouseDownMenu)
  1513. return;
  1514. // first, call the script callback for menu selection:
  1515. onMenuSelect_callback(mouseDownMenu->id, mouseDownMenu->text);
  1516. MenuItem *visWalk = mouseDownMenu->firstMenuItem;
  1517. while(visWalk)
  1518. {
  1519. if(visWalk->visible)
  1520. break;
  1521. visWalk = visWalk->nextMenuItem;
  1522. }
  1523. if(!visWalk)
  1524. {
  1525. mouseDownMenu = NULL;
  1526. return;
  1527. }
  1528. mTextList = new GuiMenuTextListCtrl(this);
  1529. mTextList->setControlProfile(mProfile);
  1530. mBackground = new GuiMenuBackgroundCtrl(this, mTextList);
  1531. GuiCanvas *root = getRoot();
  1532. Point2I windowExt = root->getExtent();
  1533. mBackground->resize( Point2I(0,0), root->getExtent());
  1534. S32 textWidth = 0, width = 0;
  1535. S32 acceleratorWidth = 0;
  1536. GFont *font = mProfile->mFont;
  1537. for(MenuItem *walk = mouseDownMenu->firstMenuItem; walk; walk = walk->nextMenuItem)
  1538. {
  1539. if(!walk->visible)
  1540. continue;
  1541. S32 iTextWidth = font->getStrWidth(walk->text);
  1542. S32 iAcceleratorWidth = walk->accelerator ? font->getStrWidth(walk->accelerator) : 0;
  1543. if(iTextWidth > textWidth)
  1544. textWidth = iTextWidth;
  1545. if(iAcceleratorWidth > acceleratorWidth)
  1546. acceleratorWidth = iAcceleratorWidth;
  1547. }
  1548. width = textWidth + acceleratorWidth + maxBitmapSize.x * 2 + 2 + 4;
  1549. mTextList->setCellSize(Point2I(width, font->getHeight()+2));
  1550. mTextList->clearColumnOffsets();
  1551. mTextList->addColumnOffset(-1); // add an empty column in for the bitmap index.
  1552. mTextList->addColumnOffset(maxBitmapSize.x + 1);
  1553. mTextList->addColumnOffset(maxBitmapSize.x + 1 + textWidth + 4);
  1554. U32 entryCount = 0;
  1555. for(MenuItem *walk = mouseDownMenu->firstMenuItem; walk; walk = walk->nextMenuItem)
  1556. {
  1557. if(!walk->visible)
  1558. continue;
  1559. char buf[512];
  1560. // If this menu item is a submenu, then set the isSubmenu to 2 to indicate
  1561. // an arrow should be drawn. Otherwise set the isSubmenu normally.
  1562. char isSubmenu = 1;
  1563. if(walk->isSubmenu)
  1564. isSubmenu = 2;
  1565. char bitmapIndex = 1;
  1566. if(walk->bitmapIndex >= 0 && (walk->bitmapIndex * 3 <= mProfile->mBitmapArrayRects.size()))
  1567. bitmapIndex = walk->bitmapIndex + 2;
  1568. dSprintf(buf, sizeof(buf), "%c%c\t%s\t%s", bitmapIndex, isSubmenu, walk->text, walk->accelerator ? walk->accelerator : "");
  1569. mTextList->addEntry(entryCount, buf);
  1570. if(!walk->enabled)
  1571. mTextList->setEntryActive(entryCount, false);
  1572. entryCount++;
  1573. }
  1574. Point2I menuPoint = localToGlobalCoord(mouseDownMenu->bounds.point);
  1575. menuPoint.y += mouseDownMenu->bounds.extent.y; // Used to have this at the end: + 2;
  1576. GuiControl *ctrl = new GuiControl;
  1577. RectI ctrlBounds( menuPoint, mTextList->getExtent() + Point2I(6, 6));
  1578. ctrl->setControlProfile(mProfile);
  1579. mTextList->setPosition( mTextList->getPosition() + Point2I(3,3) );
  1580. // Make sure the menu doesn't go beyond the Canvas' bottom edge.
  1581. if((ctrlBounds.point.y + ctrlBounds.extent.y) > windowExt.y)
  1582. {
  1583. // Pop the menu above the menu bar
  1584. Point2I menuBar = localToGlobalCoord(mouseDownMenu->bounds.point);
  1585. ctrlBounds.point.y = menuBar.y - ctrl->getHeight();
  1586. }
  1587. ctrl->resize(ctrlBounds.point, ctrlBounds.extent);
  1588. //mTextList->setPosition(Point2I(3,3));
  1589. mTextList->registerObject();
  1590. mBackground->registerObject();
  1591. ctrl->registerObject();
  1592. mBackground->addObject( ctrl );
  1593. ctrl->addObject( mTextList );
  1594. root->pushDialogControl(mBackground, mLayer + 1);
  1595. mTextList->setFirstResponder();
  1596. }
  1597. //------------------------------------------------------------------------------
  1598. // Performs an action when a menu item that is a submenu is selected/highlighted
  1599. void GuiMenuBar::onSubmenuAction(S32 selectionIndex, const RectI& bounds, Point2I cellSize)
  1600. {
  1601. if(!mouseOverSubmenu)
  1602. return;
  1603. // first, call the script callback for menu selection:
  1604. onSubmenuSelect_callback(mouseOverSubmenu->id, mouseOverSubmenu->text);
  1605. MenuItem *visWalk = mouseOverSubmenu->submenu->firstMenuItem;
  1606. while(visWalk)
  1607. {
  1608. if(visWalk->visible)
  1609. break;
  1610. visWalk = visWalk->nextMenuItem;
  1611. }
  1612. if(!visWalk)
  1613. {
  1614. mouseOverSubmenu = NULL;
  1615. return;
  1616. }
  1617. mSubmenuTextList = new GuiMenuTextListCtrl(this);
  1618. mSubmenuTextList->setControlProfile(mProfile);
  1619. mSubmenuTextList->isSubMenu = true; // Indicate that this text list is part of a submenu
  1620. mSubmenuBackground = new GuiSubmenuBackgroundCtrl(this, mSubmenuTextList);
  1621. GuiCanvas *root = getRoot();
  1622. Point2I windowExt = root->getExtent();
  1623. mSubmenuBackground->resize( Point2I(0,0), root->getExtent());
  1624. S32 textWidth = 0, width = 0;
  1625. S32 acceleratorWidth = 0;
  1626. GFont *font = mProfile->mFont;
  1627. for(MenuItem *walk = mouseOverSubmenu->submenu->firstMenuItem; walk; walk = walk->nextMenuItem)
  1628. {
  1629. if(!walk->visible)
  1630. continue;
  1631. S32 iTextWidth = font->getStrWidth(walk->text);
  1632. S32 iAcceleratorWidth = walk->accelerator ? font->getStrWidth(walk->accelerator) : 0;
  1633. if(iTextWidth > textWidth)
  1634. textWidth = iTextWidth;
  1635. if(iAcceleratorWidth > acceleratorWidth)
  1636. acceleratorWidth = iAcceleratorWidth;
  1637. }
  1638. width = textWidth + acceleratorWidth + maxBitmapSize.x * 2 + 2 + 4;
  1639. mSubmenuTextList->setCellSize(Point2I(width, font->getHeight()+3));
  1640. mSubmenuTextList->clearColumnOffsets();
  1641. mSubmenuTextList->addColumnOffset(-1); // add an empty column in for the bitmap index.
  1642. mSubmenuTextList->addColumnOffset(maxBitmapSize.x + 1);
  1643. mSubmenuTextList->addColumnOffset(maxBitmapSize.x + 1 + textWidth + 4);
  1644. U32 entryCount = 0;
  1645. for(MenuItem *walk = mouseOverSubmenu->submenu->firstMenuItem; walk; walk = walk->nextMenuItem)
  1646. {
  1647. if(!walk->visible)
  1648. continue;
  1649. char buf[512];
  1650. // Can't have submenus within submenus.
  1651. char isSubmenu = 1;
  1652. char bitmapIndex = 1;
  1653. if(walk->bitmapIndex >= 0 && (walk->bitmapIndex * 3 <= mProfile->mBitmapArrayRects.size()))
  1654. bitmapIndex = walk->bitmapIndex + 2;
  1655. dSprintf(buf, sizeof(buf), "%c%c\t%s\t%s", bitmapIndex, isSubmenu, walk->text, walk->accelerator ? walk->accelerator : "");
  1656. mSubmenuTextList->addEntry(entryCount, buf);
  1657. if(!walk->enabled)
  1658. mSubmenuTextList->setEntryActive(entryCount, false);
  1659. entryCount++;
  1660. }
  1661. Point2I menuPoint = bounds.point; //localToGlobalCoord(bounds.point);
  1662. menuPoint.x += bounds.extent.x;
  1663. menuPoint.y += cellSize.y * selectionIndex - 6;
  1664. GuiControl *ctrl = new GuiControl;
  1665. RectI ctrlBounds(menuPoint, mSubmenuTextList->getExtent() + Point2I(6, 6));
  1666. ctrl->setControlProfile(mProfile);
  1667. mSubmenuTextList->setPosition( getPosition() + Point2I(3,3));
  1668. // Make sure the menu doesn't go beyond the Canvas' bottom edge.
  1669. if((ctrlBounds.point.y + ctrlBounds.extent.y ) > windowExt.y)
  1670. {
  1671. // Pop the menu above the menu bar
  1672. ctrlBounds.point.y -= mSubmenuTextList->getHeight() - cellSize.y - 6 - 3;
  1673. }
  1674. // And the same for the right edge
  1675. if((ctrlBounds.point.x + ctrlBounds.extent.x) > windowExt.x)
  1676. {
  1677. // Pop the submenu to the left of the menu
  1678. ctrlBounds.point.x -= mSubmenuTextList->getWidth() + cellSize.x + 6;
  1679. }
  1680. ctrl->resize(ctrlBounds.point, ctrlBounds.extent);
  1681. //mSubmenuTextList->setPosition(Point2I(3,3));
  1682. mSubmenuTextList->registerObject();
  1683. mSubmenuBackground->registerObject();
  1684. ctrl->registerObject();
  1685. mSubmenuBackground->addObject( ctrl );
  1686. ctrl->addObject( mSubmenuTextList );
  1687. root->pushDialogControl(mSubmenuBackground, mLayer + 1);
  1688. mSubmenuTextList->setFirstResponder();
  1689. }
  1690. // Close down the submenu controls
  1691. void GuiMenuBar::closeSubmenu()
  1692. {
  1693. if(!mSubmenuBackground || !mSubmenuTextList)
  1694. return;
  1695. // Get the selection from the text list:
  1696. S32 selectionIndex = mSubmenuTextList->getSelectedCell().y;
  1697. // Pop the background:
  1698. if( getRoot() )
  1699. getRoot()->popDialogControl(mSubmenuBackground);
  1700. // Kill the popup:
  1701. mSubmenuBackground->deleteObject();
  1702. mSubmenuBackground = NULL;
  1703. mSubmenuTextList = NULL;
  1704. // Now perform the popup action:
  1705. if ( selectionIndex != -1 )
  1706. {
  1707. MenuItem *list = NULL;
  1708. if(mouseOverSubmenu)
  1709. {
  1710. list = mouseOverSubmenu->submenu->firstMenuItem;
  1711. while(selectionIndex && list)
  1712. {
  1713. list = list->nextMenuItem;
  1714. selectionIndex--;
  1715. }
  1716. }
  1717. if(list)
  1718. menuItemSelected(list->submenuParentMenu, list);
  1719. }
  1720. mouseOverSubmenu = NULL;
  1721. }
  1722. // Find if the mouse pointer is within a menu item
  1723. GuiMenuBar::MenuItem *GuiMenuBar::findHitMenuItem(Point2I mousePoint)
  1724. {
  1725. // for(Menu *walk = menuList; walk; walk = walk->nextMenu)
  1726. // if(walk->visible && walk->bounds.pointInRect(pos))
  1727. // return walk;
  1728. return NULL;
  1729. }
  1730. // Checks if the mouse has been moved to a new menu item
  1731. void GuiMenuBar::checkSubmenuMouseMove(const GuiEvent &event)
  1732. {
  1733. MenuItem *hit = findHitMenuItem(event.mousePoint);
  1734. if(hit && hit != mouseOverSubmenu)
  1735. {
  1736. // gotta close out the current menu...
  1737. mSubmenuTextList->setSelectedCell(Point2I(-1, -1));
  1738. // closeSubmenu();
  1739. setUpdate();
  1740. }
  1741. }
  1742. // Process a tick
  1743. void GuiMenuBar::processTick()
  1744. {
  1745. // If we are to track a tick, then do so.
  1746. if(mCountMouseOver)
  1747. {
  1748. // If we're at a particular number of ticks, notify the script function
  1749. if(mMouseOverCounter < mMouseHoverAmount)
  1750. {
  1751. ++mMouseOverCounter;
  1752. } else if(mMouseOverCounter == mMouseHoverAmount)
  1753. {
  1754. ++mMouseOverCounter;
  1755. onMouseInMenu_callback(true); // Last parameter indicates if we've entered or left the menu
  1756. }
  1757. }
  1758. }