guiPopUpCtrl.cc 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542
  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. // Revision History:
  23. // December 31, 2003 David Wyand Changed a bunch of stuff. Search for DAW below
  24. // and make better notes here and in the changes doc.
  25. // May 19, 2004 David Wyand Made changes to allow for a coloured rectangle to be
  26. // displayed to the left of the text in the popup.
  27. // May 27, 2004 David Wyand Added a check for mReverseTextList to see if we should
  28. // reverse the text list if we must render it above
  29. // the control. NOTE: there are some issues with setting
  30. // the selected ID using the 'setSelected' command externally
  31. // and the list is rendered in reverse. It seems that the
  32. // entry ID's also get reversed.
  33. // November 16, 2005 David Wyand Added the method setNoneSelected() to set none of the
  34. // items as selected. Use this over setSelected(-1); when
  35. // there are negative IDs in the item list. This also
  36. // includes the setNoneSelected console method.
  37. //
  38. #include "graphics/dgl.h"
  39. #include "gui/guiCanvas.h"
  40. #include "gui/guiPopUpCtrl.h"
  41. #include "console/consoleTypes.h"
  42. #include "gui/guiDefaultControlRender.h"
  43. static ColorI colorWhite(255,255,255); // DAW: Added
  44. // Function to return the number of columns in 'string' given delimeters in 'set'
  45. static U32 getColumnCount(const char *string, const char *set)
  46. {
  47. U32 count = 0;
  48. U8 last = 0;
  49. while(*string)
  50. {
  51. last = *string++;
  52. for(U32 i =0; set[i]; i++)
  53. {
  54. if(last == set[i])
  55. {
  56. count++;
  57. last = 0;
  58. break;
  59. }
  60. }
  61. }
  62. if(last)
  63. count++;
  64. return count;
  65. }
  66. // Function to return the 'index' column from 'string' given delimeters in 'set'
  67. static const char *getColumn(const char *string, char* returnbuff, U32 index, const char *set)
  68. {
  69. U32 sz;
  70. while(index--)
  71. {
  72. if(!*string)
  73. return "";
  74. sz = dStrcspn(string, set);
  75. if (string[sz] == 0)
  76. return "";
  77. string += (sz + 1);
  78. }
  79. sz = dStrcspn(string, set);
  80. if (sz == 0)
  81. return "";
  82. char *ret = returnbuff;
  83. dStrncpy(ret, string, sz);
  84. ret[sz] = '\0';
  85. return ret;
  86. }
  87. GuiPopUpBackgroundCtrl::GuiPopUpBackgroundCtrl(GuiPopUpMenuCtrl *ctrl, GuiPopupTextListCtrl *textList)
  88. {
  89. mPopUpCtrl = ctrl;
  90. mTextList = textList;
  91. }
  92. void GuiPopUpBackgroundCtrl::onMouseDown(const GuiEvent &event)
  93. {
  94. // mTextList->setSelectedCell(Point2I(-1,-1)); // DAW: Removed
  95. mPopUpCtrl->mBackgroundCancel = true; // DAW: Set that the user didn't click within the text list. Replaces the line above.
  96. mPopUpCtrl->closePopUp();
  97. }
  98. //------------------------------------------------------------------------------
  99. GuiPopupTextListCtrl::GuiPopupTextListCtrl()
  100. {
  101. mPopUpCtrl = NULL;
  102. }
  103. //------------------------------------------------------------------------------
  104. GuiPopupTextListCtrl::GuiPopupTextListCtrl(GuiPopUpMenuCtrl *ctrl)
  105. {
  106. mPopUpCtrl = ctrl;
  107. }
  108. //------------------------------------------------------------------------------
  109. //------------------------------------------------------------------------------
  110. //void GuiPopUpTextListCtrl::onCellSelected( Point2I /*cell*/ )
  111. //{
  112. // // Do nothing, the parent control will take care of everything...
  113. //}
  114. void GuiPopupTextListCtrl::onCellSelected( Point2I cell )
  115. {
  116. // DAW: The old function is above. This new one will only call the the select
  117. // functions if we were not cancelled by a background click.
  118. // DAW: Check if we were cancelled by the user clicking on the Background ie: anywhere
  119. // other than within the text list.
  120. if(mPopUpCtrl->mBackgroundCancel)
  121. return;
  122. if( isMethod( "onSelect" ) )
  123. Con::executef(this, 3, "onSelect", Con::getFloatArg(cell.x), Con::getFloatArg(cell.y));
  124. //call the console function
  125. if (mConsoleCommand[0])
  126. Con::evaluate(mConsoleCommand, false);
  127. }
  128. //------------------------------------------------------------------------------
  129. bool GuiPopupTextListCtrl::onKeyDown(const GuiEvent &event)
  130. {
  131. //if the control is a dead end, don't process the input:
  132. if ( !mVisible || !mActive || !mAwake )
  133. return false;
  134. //see if the key down is a <return> or not
  135. if ( event.modifier == 0 )
  136. {
  137. if ( event.keyCode == KEY_RETURN )
  138. {
  139. mPopUpCtrl->closePopUp();
  140. return true;
  141. }
  142. else if ( event.keyCode == KEY_ESCAPE )
  143. {
  144. mSelectedCell.set( -1, -1 );
  145. mPopUpCtrl->closePopUp();
  146. return true;
  147. }
  148. }
  149. //otherwise, pass the event to it's parent
  150. return Parent::onKeyDown(event);
  151. }
  152. void GuiPopupTextListCtrl::onMouseDown(const GuiEvent &event)
  153. {
  154. // Moved to onMouseUp in order to capture the mouse for the entirety of the click. ADL.
  155. // This also has the side effect of allowing the mouse to be held down and a selection made.
  156. //Parent::onMouseDown(event);
  157. //mPopUpCtrl->closePopUp();
  158. }
  159. void GuiPopupTextListCtrl::onMouseUp(const GuiEvent &event)
  160. {
  161. Parent::onMouseDown(event);
  162. mPopUpCtrl->closePopUp();
  163. Parent::onMouseUp(event);
  164. }
  165. //------------------------------------------------------------------------------
  166. void GuiPopupTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
  167. {
  168. // DAW: Get the size of the cells
  169. Point2I size;
  170. getCellSize(size);
  171. if(mouseOver)
  172. {
  173. // DAW: Render a background colour for the cell
  174. RectI cellR(offset.x, offset.y, size.x, size.y);
  175. ColorI color(0,0,0);
  176. dglDrawRectFill(cellR, color);
  177. } else if(selected)
  178. {
  179. // DAW: Render a background colour for the cell
  180. RectI cellR(offset.x, offset.y, size.x, size.y);
  181. ColorI color(128,128,128);
  182. dglDrawRectFill(cellR, color);
  183. }
  184. // DAW: Define the default x offset for the text
  185. U32 textXOffset = offset.x + mProfile->mTextOffset.x;
  186. // DAW: Do we also draw a coloured box beside the text?
  187. ColorI boxColor;
  188. bool drawbox = mPopUpCtrl->getColoredBox( boxColor, mList[cell.y].id);
  189. if(drawbox)
  190. {
  191. Point2I coloredboxsize(15,10);
  192. RectI r(offset.x + mProfile->mTextOffset.x, offset.y+2, coloredboxsize.x, coloredboxsize.y);
  193. dglDrawRectFill( r, boxColor);
  194. dglDrawRect( r, ColorI(0,0,0));
  195. textXOffset += coloredboxsize.x + mProfile->mTextOffset.x;
  196. }
  197. ColorI fontColor;
  198. mPopUpCtrl->getFontColor( fontColor, mList[cell.y].id, selected, mouseOver );
  199. dglSetBitmapModulation( fontColor );
  200. //dglDrawText( mFont, Point2I( offset.x + 4, offset.y ), mList[cell.y].text );
  201. // DAW: Get the number of columns in the cell
  202. S32 colcount = getColumnCount(mList[cell.y].text, "\t");
  203. // DAW: Are there two or more columns?
  204. if(colcount >= 2)
  205. {
  206. char buff[256];
  207. // Draw the first column
  208. getColumn(mList[cell.y].text, buff, 0, "\t");
  209. dglDrawText( mFont, Point2I( textXOffset, offset.y ), buff ); // DAW: Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
  210. // Draw the second column to the right
  211. getColumn(mList[cell.y].text, buff, 1, "\t");
  212. S32 txt_w = mFont->getStrWidth(buff);
  213. dglDrawText( mFont, Point2I( offset.x+size.x-mProfile->mTextOffset.x-txt_w, offset.y ), buff ); // DAW: Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
  214. } else
  215. {
  216. dglDrawText( mFont, Point2I( textXOffset, offset.y ), mList[cell.y].text ); // DAW: Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
  217. }
  218. }
  219. //------------------------------------------------------------------------------
  220. //------------------------------------------------------------------------------
  221. IMPLEMENT_CONOBJECT(GuiPopUpMenuCtrl);
  222. GuiPopUpMenuCtrl::GuiPopUpMenuCtrl(void)
  223. {
  224. VECTOR_SET_ASSOCIATION(mEntries);
  225. VECTOR_SET_ASSOCIATION(mSchemes);
  226. mSelIndex = -1;
  227. mActive = true;
  228. mMaxPopupHeight = 200;
  229. mScrollDir = GuiScrollCtrl::None;
  230. mScrollCount = 0;
  231. mLastYvalue = 0;
  232. mIncValue = 0;
  233. mRevNum = 0;
  234. mInAction = false;
  235. mMouseOver = false; // DAW: Added
  236. mRenderScrollInNA = false; // DAW: Added
  237. mBackgroundCancel = false; // DAW: Added
  238. mReverseTextList = false; // DAW: Added - Don't reverse text list if displaying up
  239. mBitmapName = StringTable->EmptyString; // DAW: Added
  240. mBitmapBounds.set(16, 16); // DAW: Added
  241. }
  242. //------------------------------------------------------------------------------
  243. GuiPopUpMenuCtrl::~GuiPopUpMenuCtrl()
  244. {
  245. }
  246. //------------------------------------------------------------------------------
  247. void GuiPopUpMenuCtrl::initPersistFields(void)
  248. {
  249. Parent::initPersistFields();
  250. addField("maxPopupHeight", TypeS32, Offset(mMaxPopupHeight, GuiPopUpMenuCtrl));
  251. addField("sbUsesNAColor", TypeBool, Offset(mRenderScrollInNA, GuiPopUpMenuCtrl));
  252. addField("reverseTextList", TypeBool, Offset(mReverseTextList, GuiPopUpMenuCtrl));
  253. addField("bitmap", TypeFilename, Offset(mBitmapName, GuiPopUpMenuCtrl));
  254. addField("bitmapBounds", TypePoint2I, Offset(mBitmapBounds, GuiPopUpMenuCtrl));
  255. }
  256. //------------------------------------------------------------------------------
  257. ConsoleMethod( GuiPopUpMenuCtrl, add, void, 4, 5, "( entryText , entryID [ , entryScheme ] ) Use the add method to add a new entry with text entryText, ID entryID, and using the scheme entryScheme.\n"
  258. "@param entryText Text to display in menu entry.\n"
  259. "@param entryID ID to assign to entry. This value may be 1 or greater.\n"
  260. "@param entryScheme An integer value representing the ID of an optional color scheme to be used for this entry.\n"
  261. "@return No return value.\n"
  262. "@sa addScheme, clear")
  263. {
  264. if ( argc > 4 )
  265. object->addEntry(argv[2],dAtoi(argv[3]),dAtoi(argv[4]));
  266. else
  267. object->addEntry(argv[2],dAtoi(argv[3]),0);
  268. }
  269. ConsoleMethod( GuiPopUpMenuCtrl, addScheme, void, 6, 6, "( entryScheme , fontColor , fontColorHL , fontColorSEL ) Use the addScheme method to create a new color scheme or to modify an existing one.\n"
  270. "An integer color vector contains three integer values, each between 0 and 255 and is organized in this order: 'R G B'.\n"
  271. "@param entryScheme An integer value representing the ID of the scheme, between 1 and inf.\n"
  272. "@param fontColor A vector containing an integer representation of the menu entry's normal color.\n"
  273. "@param fontColorHL A vector containing an integer representation of the menu entry's highlighted color.\n"
  274. "@param fontColorSEL A vector containing an integer representation of the menu entry's selected color.\n"
  275. "@return No return value.\n"
  276. "@sa add")
  277. {
  278. ColorI fontColor, fontColorHL, fontColorSEL;
  279. U32 r, g, b;
  280. char buf[64];
  281. dStrcpy( buf, argv[3] );
  282. char* temp = dStrtok( buf, " \0" );
  283. r = temp ? dAtoi( temp ) : 0;
  284. temp = dStrtok( NULL, " \0" );
  285. g = temp ? dAtoi( temp ) : 0;
  286. temp = dStrtok( NULL, " \0" );
  287. b = temp ? dAtoi( temp ) : 0;
  288. fontColor.set( r, g, b );
  289. dStrcpy( buf, argv[4] );
  290. temp = dStrtok( buf, " \0" );
  291. r = temp ? dAtoi( temp ) : 0;
  292. temp = dStrtok( NULL, " \0" );
  293. g = temp ? dAtoi( temp ) : 0;
  294. temp = dStrtok( NULL, " \0" );
  295. b = temp ? dAtoi( temp ) : 0;
  296. fontColorHL.set( r, g, b );
  297. dStrcpy( buf, argv[5] );
  298. temp = dStrtok( buf, " \0" );
  299. r = temp ? dAtoi( temp ) : 0;
  300. temp = dStrtok( NULL, " \0" );
  301. g = temp ? dAtoi( temp ) : 0;
  302. temp = dStrtok( NULL, " \0" );
  303. b = temp ? dAtoi( temp ) : 0;
  304. fontColorSEL.set( r, g, b );
  305. object->addScheme( dAtoi( argv[2] ), fontColor, fontColorHL, fontColorSEL );
  306. }
  307. ConsoleMethod( GuiPopUpMenuCtrl, setText, void, 3, 3, "( text ) Use the setText method to change the text displayed in the menu bar.\n"
  308. "Pass the NULL string to clear the menu bar text.\n"
  309. "@param text New text to display in the menu bar.\n"
  310. "@return No return value.\n"
  311. "@sa getText, replaceText")
  312. {
  313. object->setText(argv[2]);
  314. }
  315. ConsoleMethod( GuiPopUpMenuCtrl, getText, const char*, 2, 2, "() Use the getText method to get the text currently displayed in the menu bar.\n"
  316. "@return Returns the text in the menu bar or NULL if no text is present.\n"
  317. "@sa getSelected, setText")
  318. {
  319. return object->getText();
  320. }
  321. ConsoleMethod( GuiPopUpMenuCtrl, clear, void, 2, 2, "() Use the clear method to remove all entries and schemes from the menu.\n"
  322. "@return No return value.\n"
  323. "@sa add, addScheme")
  324. {
  325. object->clear();
  326. }
  327. ConsoleMethod(GuiPopUpMenuCtrl, sort, void, 2, 2, "() Use the sort method to sort the menu in ascending order.\n"
  328. "This is a lexicographic sort, so number (1,2,3,...) will come before letters (a,b,c,...)\n"
  329. "@return No return value.")
  330. {
  331. object->sort();
  332. }
  333. // DAW: Added to sort the entries by ID
  334. ConsoleMethod(GuiPopUpMenuCtrl, sortID, void, 2, 2, "() Sort the list by ID.\n"
  335. "@return No return value.")
  336. {
  337. object->sortID();
  338. }
  339. ConsoleMethod( GuiPopUpMenuCtrl, forceOnAction, void, 2, 2, "() Use the forceOnAction method to force the onAction callback to be triggered.\n"
  340. "@return No return value.\n"
  341. "@sa forceClose, onAction (GUIControl callback)")
  342. {
  343. object->onAction();
  344. }
  345. ConsoleMethod( GuiPopUpMenuCtrl, forceClose, void, 2, 2, "() Use the forceClose method to force the menu to close.\n"
  346. "This is useful for making menus that fold up after a short delay when the mouse leaves the menu area.\n"
  347. "@return No return value.\n"
  348. "@sa forceOnAction")
  349. {
  350. object->closePopUp();
  351. }
  352. ConsoleMethod( GuiPopUpMenuCtrl, getSelected, S32, 2, 2, "() Use the getSelected method to get the ID of the last selected entry.\n"
  353. "@return Returns the ID of the currently selected entry, or 0 meaning no menu was selected after the last menu open.")
  354. {
  355. return object->getSelected();
  356. }
  357. ConsoleMethod( GuiPopUpMenuCtrl, setSelected, void, 3, 4, "(int id, [scriptCallback=true]) Set the object status as selected\n"
  358. "@param id The object's ID.\n"
  359. "@param scriptCallback Flag whether to notify\n"
  360. "@return No return value.")
  361. {
  362. if( argc > 3 )
  363. object->setSelected( dAtoi( argv[2] ), dAtob( argv[3] ) );
  364. else
  365. object->setSelected( dAtoi( argv[2] ) );
  366. }
  367. ConsoleMethod( GuiPopUpMenuCtrl, setFirstSelected, void, 2, 2, "() \n"
  368. "@return No return value.")
  369. {
  370. object->setFirstSelected();
  371. }
  372. ConsoleMethod( GuiPopUpMenuCtrl, setNoneSelected, void, 2, 2, "() Deselects all popup menu controls\n"
  373. "@return No return value.")
  374. {
  375. object->setNoneSelected();
  376. }
  377. ConsoleMethod( GuiPopUpMenuCtrl, getTextById, const char*, 3, 3, "( ID ) Use the getTextById method to get the text value for the menu item represented by ID.\n"
  378. "@param ID An integer value representing the ID of a menu item.\n"
  379. "@return Returns a string containing the menu item corresponding to ID, or a NULL string if no menu item has the specified ID.\n"
  380. "@sa add, getText")
  381. {
  382. return(object->getTextById(dAtoi(argv[2])));
  383. }
  384. ConsoleMethod( GuiPopUpMenuCtrl, setEnumContent, void, 4, 4, "(string class, string enum)"
  385. "This fills the popup with a classrep's field enumeration type info.\n\n"
  386. "More of a helper function than anything. If console access to the field list is added, "
  387. "at least for the enumerated types, then this should go away.."
  388. "@param className The class name associated with this enum content.\n"
  389. "@param enumName The name of the enumerated entry to add to the menu. This value must match an enum string as exposed by the engine for the class. The menu item will have the same text as the enum string name, and the ID will be equal to the enumerated entries value.\n"
  390. "@return No return value")
  391. {
  392. AbstractClassRep * classRep = AbstractClassRep::getClassList();
  393. // walk the class list to get our class
  394. while(classRep)
  395. {
  396. if(!dStricmp(classRep->getClassName(), argv[2]))
  397. break;
  398. classRep = classRep->getNextClass();
  399. }
  400. // get it?
  401. if(!classRep)
  402. {
  403. Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", argv[2]);
  404. return;
  405. }
  406. // walk the fields to check for this one (findField checks StringTableEntry ptrs...)
  407. U32 i;
  408. for(i = 0; i < (U32)classRep->mFieldList.size(); i++)
  409. if(!dStricmp(classRep->mFieldList[i].pFieldname, argv[3]))
  410. break;
  411. // found it?
  412. if(i == classRep->mFieldList.size())
  413. {
  414. Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", argv[3], argv[2]);
  415. return;
  416. }
  417. const AbstractClassRep::Field & field = classRep->mFieldList[i];
  418. // check the type
  419. if(field.type != TypeEnum)
  420. {
  421. Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", argv[3], argv[2]);
  422. return;
  423. }
  424. AssertFatal(field.table, avar("enumeration '%s' for class '%s' with NULL ", argv[3], argv[2]));
  425. // fill it
  426. for(i = 0; i < (U32)field.table->size; i++)
  427. object->addEntry(field.table->table[i].label, field.table->table[i].index);
  428. }
  429. //------------------------------------------------------------------------------
  430. ConsoleMethod( GuiPopUpMenuCtrl, findText, S32, 3, 3, "( text ) Use the findText method to locate the string text in the list of menu items. It will return the ID of the first entry found.\n"
  431. "@param text A string containing the text to search for.\n"
  432. "@return Returns an integer value representing the ID of the menu item, or -1 if the text was not found.")
  433. {
  434. return( object->findText( argv[2] ) );
  435. }
  436. //------------------------------------------------------------------------------
  437. ConsoleMethod( GuiPopUpMenuCtrl, size, S32, 2, 2, "() Get the size of the menu, ie the number of entries in it.\n"
  438. "@return The numbers of entries as an integer.")
  439. {
  440. return( object->getNumEntries() );
  441. }
  442. //------------------------------------------------------------------------------
  443. ConsoleMethod( GuiPopUpMenuCtrl, replaceText, void, 3, 3, "( enable ) Use the replaceText method to enable the updating of the menu bar text when a menu item is selected.\n"
  444. "This does not prevent changing the menu bar text with setText.\n"
  445. "@param enable A boolean value enabling or disabling the automatic updating of the menu bar text when a selection is made.\n"
  446. "@return No return value.\n"
  447. "@sa getText, setText")
  448. {
  449. object->replaceText(dAtoi(argv[2]));
  450. }
  451. //------------------------------------------------------------------------------
  452. // DAW: Added
  453. bool GuiPopUpMenuCtrl::onWake()
  454. {
  455. if (! Parent::onWake())
  456. return false;
  457. // Set the bitmap for the popup.
  458. setBitmap(mBitmapName);
  459. // Now update the Form Control's bitmap array, and possibly the child's too
  460. mProfile->constructBitmapArray();
  461. if(mProfile->mProfileForChildren)
  462. mProfile->mProfileForChildren->constructBitmapArray();
  463. return true;
  464. }
  465. //------------------------------------------------------------------------------
  466. bool GuiPopUpMenuCtrl::onAdd()
  467. {
  468. if (!Parent::onAdd())
  469. return false;
  470. mSelIndex = -1;
  471. mReplaceText = true;
  472. return true;
  473. }
  474. //------------------------------------------------------------------------------
  475. void GuiPopUpMenuCtrl::onSleep()
  476. {
  477. mTextureNormal = NULL; // DAW: Added
  478. mTextureDepressed = NULL; // DAW: Added
  479. Parent::onSleep();
  480. closePopUp(); // Tests in function.
  481. }
  482. //------------------------------------------------------------------------------
  483. void GuiPopUpMenuCtrl::clear()
  484. {
  485. mEntries.setSize(0);
  486. setText("");
  487. mSelIndex = -1;
  488. mRevNum = 0;
  489. }
  490. //------------------------------------------------------------------------------
  491. static S32 QSORT_CALLBACK textCompare(const void *a,const void *b)
  492. {
  493. GuiPopUpMenuCtrl::Entry *ea = (GuiPopUpMenuCtrl::Entry *) (a);
  494. GuiPopUpMenuCtrl::Entry *eb = (GuiPopUpMenuCtrl::Entry *) (b);
  495. return (dStricmp(ea->buf, eb->buf));
  496. }
  497. // DAW: Added to sort by entry ID
  498. //------------------------------------------------------------------------------
  499. static S32 QSORT_CALLBACK idCompare(const void *a,const void *b)
  500. {
  501. GuiPopUpMenuCtrl::Entry *ea = (GuiPopUpMenuCtrl::Entry *) (a);
  502. GuiPopUpMenuCtrl::Entry *eb = (GuiPopUpMenuCtrl::Entry *) (b);
  503. return ( (ea->id < eb->id) ? -1 : ((ea->id > eb->id) ? 1 : 0) );
  504. }
  505. //------------------------------------------------------------------------------
  506. // DAW: Added
  507. void GuiPopUpMenuCtrl::setBitmap(const char *name)
  508. {
  509. mBitmapName = StringTable->insert(name);
  510. if(!isAwake())
  511. return;
  512. if (*mBitmapName)
  513. {
  514. char buffer[1024];
  515. char *p;
  516. dStrcpy(buffer, name);
  517. p = buffer + dStrlen(buffer);
  518. dStrcpy(p, "_n");
  519. mTextureNormal = TextureHandle(buffer, TextureHandle::BitmapTexture, true);
  520. dStrcpy(p, "_d");
  521. mTextureDepressed = TextureHandle(buffer, TextureHandle::BitmapTexture, true);
  522. if (!mTextureDepressed)
  523. mTextureDepressed = mTextureNormal;
  524. }
  525. else
  526. {
  527. mTextureNormal = NULL;
  528. mTextureDepressed = NULL;
  529. }
  530. setUpdate();
  531. }
  532. //------------------------------------------------------------------------------
  533. void GuiPopUpMenuCtrl::sort()
  534. {
  535. dQsort((void *)mEntries.address(), mEntries.size(), sizeof(Entry), textCompare);
  536. }
  537. // DAW: Added to sort by entry ID
  538. //------------------------------------------------------------------------------
  539. void GuiPopUpMenuCtrl::sortID()
  540. {
  541. dQsort((void *)mEntries.address(), mEntries.size(), sizeof(Entry), idCompare);
  542. }
  543. //------------------------------------------------------------------------------
  544. void GuiPopUpMenuCtrl::addEntry(const char *buf, S32 id, U32 scheme)
  545. {
  546. if( !buf )
  547. {
  548. //Con::printf( "GuiPopupMenuCtrlEx::addEntry - Invalid buffer!" );
  549. return;
  550. }
  551. Entry e;
  552. dStrcpy(e.buf, buf);
  553. e.id = id;
  554. e.scheme = scheme;
  555. // see if there is a shortcut key
  556. char * cp = dStrchr(e.buf, '~');
  557. e.ascii = cp ? cp[1] : 0;
  558. // DAW: See if there is a colour box defined with the text
  559. char* cb = dStrchr(e.buf, '|');
  560. if(cb)
  561. {
  562. e.usesColorBox = true;
  563. cb[0] = '\0';
  564. char* red = &cb[1];
  565. cb = dStrchr(red, '|');
  566. cb[0] = '\0';
  567. char* green = &cb[1];
  568. cb = dStrchr(green, '|');
  569. cb[0] = '\0';
  570. char* blue = &cb[1];
  571. U32 r = dAtoi(red);
  572. U32 g = dAtoi(green);
  573. U32 b = dAtoi(blue);
  574. e.colorbox = ColorI(r,g,b);
  575. } else
  576. {
  577. e.usesColorBox = false;
  578. }
  579. mEntries.push_back(e);
  580. if ( mInAction && mTl )
  581. {
  582. // Add the new entry:
  583. mTl->addEntry( e.id, e.buf );
  584. repositionPopup();
  585. }
  586. }
  587. //------------------------------------------------------------------------------
  588. void GuiPopUpMenuCtrl::addScheme( U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL )
  589. {
  590. if ( !id )
  591. return;
  592. Scheme newScheme;
  593. newScheme.id = id;
  594. newScheme.fontColor = fontColor;
  595. newScheme.fontColorHL = fontColorHL;
  596. newScheme.fontColorSEL = fontColorSEL;
  597. mSchemes.push_back( newScheme );
  598. }
  599. //------------------------------------------------------------------------------
  600. S32 GuiPopUpMenuCtrl::getSelected()
  601. {
  602. if (mSelIndex == -1)
  603. return 0;
  604. return mEntries[mSelIndex].id;
  605. }
  606. //------------------------------------------------------------------------------
  607. const char* GuiPopUpMenuCtrl::getTextById(S32 id)
  608. {
  609. for ( U32 i = 0; i < (U32)mEntries.size(); i++ )
  610. {
  611. if ( mEntries[i].id == id )
  612. return( mEntries[i].buf );
  613. }
  614. return( "" );
  615. }
  616. //------------------------------------------------------------------------------
  617. S32 GuiPopUpMenuCtrl::findText( const char* text )
  618. {
  619. for ( U32 i = 0; i < (U32)mEntries.size(); i++ )
  620. {
  621. if ( dStrcmp( text, mEntries[i].buf ) == 0 )
  622. return( mEntries[i].id );
  623. }
  624. return( -1 );
  625. }
  626. //------------------------------------------------------------------------------
  627. void GuiPopUpMenuCtrl::setSelected(S32 id, bool bNotifyScript )
  628. {
  629. S32 i;
  630. for (i = 0; U32(i) < (U32)mEntries.size(); i++)
  631. if (id == mEntries[i].id)
  632. {
  633. i = (mRevNum > i) ? mRevNum - i : i;
  634. mSelIndex = i;
  635. if(mReplaceText) // DAW: Only change the displayed text if appropriate.
  636. {
  637. setText(mEntries[i].buf);
  638. }
  639. // Now perform the popup action:
  640. char idval[24];
  641. dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id );
  642. if( isMethod( "onSelect" ) && bNotifyScript)
  643. Con::executef( this, 3, "onSelect", idval, mEntries[mSelIndex].buf );
  644. return;
  645. }
  646. if(mReplaceText) // DAW: Only change the displayed text if appropriate.
  647. {
  648. setText("");
  649. }
  650. mSelIndex = -1;
  651. if( isMethod( "onCancel" ) && bNotifyScript)
  652. Con::executef( this, 1, "onCancel" );
  653. if( id == -1 )
  654. return;
  655. // Execute the popup console command:
  656. if ( mConsoleCommand[0] && bNotifyScript )
  657. Con::evaluate( mConsoleCommand, false );
  658. }
  659. //------------------------------------------------------------------------------
  660. // DAW: Added to set the first item as selected.
  661. void GuiPopUpMenuCtrl::setFirstSelected()
  662. {
  663. if (mEntries.size() > 0)
  664. {
  665. mSelIndex = 0;
  666. if(mReplaceText) // DAW: Only change the displayed text if appropriate.
  667. {
  668. setText(mEntries[0].buf);
  669. }
  670. // Now perform the popup action:
  671. char idval[24];
  672. dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id );
  673. if(isMethod("onSelect"))
  674. Con::executef( this, 3, "onSelect", idval, mEntries[mSelIndex].buf );
  675. return;
  676. }
  677. if(mReplaceText) // DAW: Only change the displayed text if appropriate.
  678. {
  679. setText("");
  680. }
  681. mSelIndex = -1;
  682. Con::executef( this, 1, "onCancel" );
  683. // Execute the popup console command:
  684. if ( mConsoleCommand[0] )
  685. Con::evaluate( mConsoleCommand, false );
  686. }
  687. //------------------------------------------------------------------------------
  688. // DAW: Added to set no items as selected.
  689. void GuiPopUpMenuCtrl::setNoneSelected()
  690. {
  691. if(mReplaceText) // DAW: Only change the displayed text if appropriate.
  692. {
  693. setText("");
  694. }
  695. mSelIndex = -1;
  696. }
  697. //------------------------------------------------------------------------------
  698. const char *GuiPopUpMenuCtrl::getScriptValue()
  699. {
  700. return getText();
  701. }
  702. //------------------------------------------------------------------------------
  703. //-Mat untested
  704. void GuiPopUpMenuCtrl::onRender(Point2I offset, const RectI &updateRect)
  705. {
  706. Point2I localStart;
  707. if(mScrollDir != GuiScrollCtrl::None)
  708. autoScroll();
  709. S32 renderedBitmapIndex = 1;
  710. RectI r(offset, mBounds.extent);
  711. if(mProfile->mBitmapArrayRects.size() >= 4)
  712. {
  713. // if size >= 4 then the inactive state images should be present
  714. S32 index = 3;
  715. if(!mActive)
  716. {
  717. index = 4; // inactive state images are indexes 4 and 5.
  718. //S32 l = r.point.x, r2 = r.point.x + r.extent.x - 1;
  719. //S32 t = r.point.y, b = r.point.y + r.extent.y - 1;
  720. renderFixedBitmapBordersStretchYFilled(r, index, mProfile);
  721. return;
  722. }
  723. }
  724. if(mInAction)
  725. {
  726. S32 l = r.point.x, r2 = r.point.x + r.extent.x - 1;
  727. S32 t = r.point.y, b = r.point.y + r.extent.y - 1;
  728. // Do we render a bitmap border or lines?
  729. if(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size())
  730. {
  731. // Render the fixed, filled in border
  732. renderFixedBitmapBordersStretchYFilled(r, 3, mProfile);
  733. renderedBitmapIndex = 3;
  734. }
  735. else
  736. {
  737. //renderSlightlyLoweredBox(r, mProfile);
  738. dglDrawRectFill( r, mProfile->mFillColor);
  739. }
  740. // DAW: Draw a bitmap over the background?
  741. if(mTextureDepressed)
  742. {
  743. RectI rect(offset, mBitmapBounds);
  744. dglClearBitmapModulation();
  745. dglDrawBitmapStretch(mTextureDepressed, rect);
  746. } else if(mTextureNormal)
  747. {
  748. RectI rect(offset, mBitmapBounds);
  749. dglClearBitmapModulation();
  750. dglDrawBitmapStretch(mTextureNormal, rect);
  751. }
  752. // Do we render a bitmap border or lines?
  753. if(!(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size()))
  754. {
  755. dglDrawLine(l, t, l, b, colorWhite);
  756. dglDrawLine(l, t, r2, t, colorWhite);
  757. dglDrawLine(l + 1, b, r2, b, mProfile->mBorderColor);
  758. dglDrawLine(r2, t + 1, r2, b - 1, mProfile->mBorderColor);
  759. }
  760. }
  761. else
  762. // TODO: Implement
  763. if(mMouseOver) // TODO: Add onMouseEnter() and onMouseLeave() and a definition of mMouseOver (see guiButtonBaseCtrl) for this to work.
  764. {
  765. S32 l = r.point.x, r2 = r.point.x + r.extent.x - 1;
  766. S32 t = r.point.y, b = r.point.y + r.extent.y - 1;
  767. // Do we render a bitmap border or lines?
  768. if(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size())
  769. {
  770. // Render the fixed, filled in border
  771. renderFixedBitmapBordersStretchYFilled(r, 2, mProfile);
  772. renderedBitmapIndex = 2;
  773. } else
  774. {
  775. dglDrawRectFill( r, mProfile->mFillColorHL);
  776. }
  777. // DAW: Draw a bitmap over the background?
  778. if(mTextureNormal)
  779. {
  780. RectI rect(offset, mBitmapBounds);
  781. dglClearBitmapModulation();
  782. dglDrawBitmapStretch(mTextureNormal, rect);
  783. }
  784. // Do we render a bitmap border or lines?
  785. if(!(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size()))
  786. {
  787. dglDrawLine(l, t, l, b, colorWhite);
  788. dglDrawLine(l, t, r2, t, colorWhite);
  789. dglDrawLine(l + 1, b, r2, b, mProfile->mBorderColor);
  790. dglDrawLine(r2, t + 1, r2, b - 1, mProfile->mBorderColor);
  791. }
  792. }
  793. else
  794. {
  795. // Do we render a bitmap border or lines?
  796. if(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size())
  797. {
  798. // Render the fixed, filled in border
  799. renderFixedBitmapBordersStretchYFilled(r, 1, mProfile);
  800. renderedBitmapIndex = 1;
  801. } else
  802. {
  803. dglDrawRectFill( r, mProfile->mFillColorNA);
  804. }
  805. // DAW: Draw a bitmap over the background?
  806. if(mTextureNormal)
  807. {
  808. RectI rect(offset, mBitmapBounds);
  809. dglClearBitmapModulation();
  810. dglDrawBitmapStretch(mTextureNormal, rect);
  811. }
  812. // Do we render a bitmap border or lines?
  813. if(!(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size()))
  814. {
  815. dglDrawRect(r, mProfile->mBorderColorNA);
  816. }
  817. }
  818. // renderSlightlyRaisedBox(r, mProfile); // DAW: Used to be the only 'else' condition to mInAction above.
  819. S32 txt_w = mFont->getStrWidth(mText);
  820. localStart.x = 0;
  821. localStart.y = (mBounds.extent.y - (mFont->getHeight())) / 2;
  822. // DAW: Indices into the bitmap array
  823. const S32 NumBitmaps = 3;
  824. const S32 BorderLeft = NumBitmaps * renderedBitmapIndex - NumBitmaps;
  825. const S32 BorderRight = 2 + BorderLeft;
  826. // align the horizontal
  827. switch (mProfile->mAlignment)
  828. {
  829. case GuiControlProfile::RightJustify:
  830. if(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size())
  831. {
  832. // We're making use of a bitmap border, so take into account the
  833. // right cap of the border.
  834. RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address();
  835. localStart.x = mBounds.extent.x - mBitmapBounds[BorderRight].extent.x - txt_w;
  836. } else
  837. {
  838. localStart.x = mBounds.extent.x - txt_w;
  839. }
  840. break;
  841. case GuiControlProfile::CenterJustify:
  842. if(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size())
  843. {
  844. RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address();
  845. // GuiControlProfile::LeftJustify
  846. if(txt_w > (mBounds.extent.x - mBitmapBounds[BorderLeft].extent.x - mBitmapBounds[BorderRight].extent.x) )
  847. {
  848. // We're making use of a bitmap border, so take into account the
  849. // right cap of the border.
  850. localStart.x = mBounds.extent.x - mBitmapBounds[BorderRight].extent.x - txt_w;
  851. }
  852. else
  853. {
  854. // We're making use of a bitmap border, so take into account the
  855. // right cap of the border.
  856. localStart.x = mBitmapBounds[BorderLeft].extent.x + ((mBounds.extent.x - mBitmapBounds[BorderLeft].extent.x - mBitmapBounds[BorderRight].extent.x - txt_w) / 2);
  857. }
  858. } else
  859. {
  860. localStart.x = (mBounds.extent.x - txt_w) / 2;
  861. }
  862. break;
  863. default:
  864. // DAW: The width of the text is greater than the width of the control.
  865. // In this case we will right justify the text and leave some space
  866. // for the down arrow.
  867. if(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size())
  868. {
  869. RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address();
  870. // GuiControlProfile::LeftJustify
  871. if(txt_w > (mBounds.extent.x - mBitmapBounds[BorderLeft].extent.x - mBitmapBounds[BorderRight].extent.x) )
  872. {
  873. // We're making use of a bitmap border, so take into account the
  874. // right cap of the border.
  875. RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address();
  876. localStart.x = mBounds.extent.x - mBitmapBounds[BorderRight].extent.x - txt_w;
  877. }
  878. else
  879. {
  880. localStart.x = mBitmapBounds[BorderLeft].extent.x;
  881. }
  882. } else
  883. {
  884. localStart.x = mBounds.extent.x - txt_w - 12;
  885. }
  886. break;
  887. }
  888. // DAW: Do we first draw a coloured box beside the text?
  889. ColorI boxColor;
  890. bool drawbox = getColoredBox( boxColor, mSelIndex);
  891. if(drawbox)
  892. {
  893. Point2I coloredboxsize(15,10);
  894. RectI r(offset.x + mProfile->mTextOffset.x, offset.y + ((mBounds.extent.y - coloredboxsize.y) / 2), coloredboxsize.x, coloredboxsize.y);
  895. dglDrawRectFill( r, boxColor);
  896. dglDrawRect( r, ColorI(0,0,0));
  897. localStart.x += coloredboxsize.x + mProfile->mTextOffset.x;
  898. }
  899. // Draw the text
  900. Point2I globalStart = localToGlobalCoord(localStart);
  901. ColorI fontColor = mActive ? (mInAction ? mProfile->mFontColorNA : mProfile->mFontColor) : mProfile->mFontColorNA;
  902. dglSetBitmapModulation(fontColor); // DAW: was: (mProfile->mFontColor);
  903. // Save the clip rectangle.
  904. const RectI previousClipRect = dglGetClipRect();
  905. // Set a text rendering clip rectangle when we're using bitmap edges.
  906. if( mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size() )
  907. {
  908. RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address();
  909. dglSetClipRect( RectI( r.point.x + mBitmapBounds[BorderLeft].extent.x, r.point.y, r.extent.x - mBitmapBounds[BorderLeft].extent.x - mBitmapBounds[BorderRight].extent.x, r.extent.y ) );
  910. }
  911. // DAW: Get the number of columns in the text
  912. S32 colcount = getColumnCount(mText, "\t");
  913. // DAW: Are there two or more columns?
  914. if(colcount >= 2)
  915. {
  916. char buff[256];
  917. // Draw the first column
  918. getColumn(mText, buff, 0, "\t");
  919. dglDrawText(mFont, globalStart, buff, mProfile->mFontColors);
  920. // Draw the second column to the right
  921. getColumn(mText, buff, 1, "\t");
  922. S32 txt_w = mFont->getStrWidth(buff);
  923. if(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size())
  924. {
  925. // We're making use of a bitmap border, so take into account the
  926. // right cap of the border.
  927. RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address();
  928. Point2I textpos = localToGlobalCoord(Point2I(mBounds.extent.x - txt_w - mBitmapBounds[2].extent.x,localStart.y));
  929. dglDrawText(mFont, textpos, buff, mProfile->mFontColors);
  930. } else
  931. {
  932. Point2I textpos = localToGlobalCoord(Point2I(mBounds.extent.x - txt_w - 12,localStart.y));
  933. dglDrawText(mFont, textpos, buff, mProfile->mFontColors);
  934. }
  935. } else
  936. {
  937. dglDrawText(mFont, globalStart, mText, mProfile->mFontColors);
  938. }
  939. // Restore the clip rectangle.
  940. dglSetClipRect( previousClipRect );
  941. // If we're rendering a bitmap border, then it will take care of the arrow.
  942. if(!(mProfile->mProfileForChildren && mProfile->mBitmapArrayRects.size()))
  943. {
  944. // DAW: Draw a triangle (down arrow)
  945. F32 left = (F32)(r.point.x + r.extent.x - 12);
  946. F32 right = (F32)(left + 8);
  947. F32 middle = (F32)(left + 4);
  948. F32 top = (F32)(r.extent.y / 2 + r.point.y - 4);
  949. F32 bottom = (F32)(top + 8);
  950. glBegin(GL_TRIANGLES);
  951. glColor3i(mProfile->mFontColor.red,mProfile->mFontColor.green,mProfile->mFontColor.blue);
  952. glVertex2fv( Point3F(left,top,0) );
  953. glVertex2fv( Point3F(right,top,0) );
  954. glVertex2fv( Point3F(middle,bottom,0) );
  955. glEnd();
  956. }
  957. }
  958. //------------------------------------------------------------------------------
  959. void GuiPopUpMenuCtrl::closePopUp()
  960. {
  961. if ( !mInAction )
  962. return;
  963. // Get the selection from the text list:
  964. mSelIndex = mTl->getSelectedCell().y;
  965. mSelIndex = (mRevNum >= mSelIndex && mSelIndex != -1) ? mRevNum - mSelIndex : mSelIndex;
  966. if ( mSelIndex != -1 )
  967. {
  968. if(mReplaceText)
  969. setText( mEntries[mSelIndex].buf );
  970. setIntVariable( mEntries[mSelIndex].id );
  971. }
  972. // Release the mouse:
  973. mInAction = false;
  974. mTl->mouseUnlock();
  975. // DAW: Commented out below and moved to the end of the function. See the
  976. // note below for the reason why.
  977. /*
  978. // Pop the background:
  979. getRoot()->popDialogControl(mBackground);
  980. // Kill the popup:
  981. mBackground->removeObject( mSc );
  982. mTl->deleteObject();
  983. mSc->deleteObject();
  984. mBackground->deleteObject();
  985. // Set this as the first responder:
  986. setFocus();
  987. */
  988. // Now perform the popup action:
  989. if ( mSelIndex != -1 )
  990. {
  991. char idval[24];
  992. dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id );
  993. if(isMethod("onSelect"))
  994. Con::executef( this, 3, "onSelect", idval, mEntries[mSelIndex].buf );
  995. }
  996. else if(isMethod("onSelect"))
  997. Con::executef( this, 1, "onCancel" );
  998. // Execute the popup console command:
  999. if ( mConsoleCommand[0] )
  1000. Con::evaluate( mConsoleCommand, false );
  1001. // DAW: Reordered this pop dialog to be after the script select callback. When the
  1002. // background was popped it was causing all sorts of focus issues when
  1003. // suddenly the first text edit control took the focus before it came back
  1004. // to this popup.
  1005. // Pop the background:
  1006. getRoot()->popDialogControl(mBackground);
  1007. // Kill the popup:
  1008. mBackground->removeObject( mSc );
  1009. mTl->deleteObject();
  1010. mSc->deleteObject();
  1011. mBackground->deleteObject();
  1012. // Set this as the first responder:
  1013. setFirstResponder();
  1014. }
  1015. //------------------------------------------------------------------------------
  1016. bool GuiPopUpMenuCtrl::onKeyDown(const GuiEvent &event)
  1017. {
  1018. //if the control is a dead end, don't process the input:
  1019. if ( !mVisible || !mActive || !mAwake )
  1020. return false;
  1021. //see if the key down is a <return> or not
  1022. if ( event.keyCode == KEY_RETURN && event.modifier == 0 )
  1023. {
  1024. onAction();
  1025. return true;
  1026. }
  1027. //otherwise, pass the event to its parent
  1028. return Parent::onKeyDown( event );
  1029. }
  1030. //------------------------------------------------------------------------------
  1031. void GuiPopUpMenuCtrl::onAction()
  1032. {
  1033. if ( !mVisible || !mActive || !mAwake )
  1034. return;
  1035. GuiControl *canCtrl = getParent();
  1036. addChildren();
  1037. GuiCanvas *root = getRoot();
  1038. Point2I windowExt = root->mBounds.extent;
  1039. mBackground->mBounds.point.set(0,0);
  1040. mBackground->mBounds.extent = root->mBounds.extent;
  1041. S32 textWidth = 0, width = mBounds.extent.x;
  1042. //const S32 menuSpace = 5; // DAW: Removed as no longer used.
  1043. const S32 textSpace = 2;
  1044. bool setScroll = false;
  1045. for( U32 i=0; i < (U32)mEntries.size(); ++i )
  1046. if(S32(mFont->getStrWidth(mEntries[i].buf)) > textWidth)
  1047. textWidth = mFont->getStrWidth(mEntries[i].buf);
  1048. //if(textWidth > mBounds.extent.x)
  1049. S32 sbWidth = mSc->mProfile->mBorderThickness * 2 + mSc->scrollBarThickness(); // DAW: Calculate the scroll bar width
  1050. if(textWidth > (mBounds.extent.x - sbWidth-mProfile->mTextOffset.x - mSc->getChildMargin().x * 2)) // DAW: The text draw area to test against is the width of the drop-down minus the scroll bar width, the text margin and the scroll bar child margins.
  1051. {
  1052. //textWidth +=10;
  1053. textWidth +=sbWidth + mProfile->mTextOffset.x + mSc->getChildMargin().x * 2; // DAW: The new width is the width of the text plus the scroll bar width plus the text margin size plus the scroll bar child margins.
  1054. width = textWidth;
  1055. // DAW: If a child margin is not defined for the scroll control, let's add
  1056. // some space between the text and scroll control for readability
  1057. if(mSc->getChildMargin().x == 0)
  1058. width += 2;
  1059. }
  1060. //mTl->setCellSize(Point2I(width, mFont->getHeight()+3));
  1061. mTl->setCellSize(Point2I(width, mFont->getHeight() + textSpace)); // DAW: Modified the above line to use textSpace rather than the '3' as this is what is used below.
  1062. for( U32 j = 0; j < (U32)mEntries.size(); ++j )
  1063. mTl->addEntry(mEntries[j].id, mEntries[j].buf);
  1064. Point2I pointInGC = canCtrl->localToGlobalCoord(mBounds.point);
  1065. Point2I scrollPoint(pointInGC.x, pointInGC.y + mBounds.extent.y);
  1066. //Calc max Y distance, so Scroll Ctrl will fit on window
  1067. //S32 maxYdis = windowExt.y - pointInGC.y - mBounds.extent.y - menuSpace;
  1068. S32 sbBorder = mSc->mProfile->mBorderThickness * 2 + mSc->getChildMargin().y * 2; // DAW: Added to take into account the border thickness and the margin of the child controls of the scroll control when figuring out the size of the contained text list control
  1069. S32 maxYdis = windowExt.y - pointInGC.y - mBounds.extent.y - sbBorder; // - menuSpace; // DAW: Need to remove the border thickness from the contained control maximum extent and got rid of the 'menuspace' variable
  1070. //If scroll bars need to be added
  1071. mRevNum = 0; // DAW: Added here rather than within the following 'if' statements.
  1072. if(maxYdis < mTl->mBounds.extent.y + sbBorder) // DAW: Instead of adding sbBorder, it was: 'textSpace'
  1073. {
  1074. //Should we pop menu list above the button
  1075. if(maxYdis < pointInGC.y ) // DAW: removed: '- menuSpace)' from check to see if there is more space above the control than below.
  1076. {
  1077. if(mReverseTextList) // DAW: Added this check if we should reverse the text list.
  1078. reverseTextList();
  1079. maxYdis = pointInGC.y; // DAW: Was at the end: '- menuSpace;'
  1080. //Does the menu need a scroll bar
  1081. if(maxYdis < mTl->mBounds.extent.y + sbBorder) // DAW: Instead of adding sbBorder, it was: 'textSpace'
  1082. {
  1083. // DAW: Removed width recalculation for scroll bar as the scroll bar is already being taken into account.
  1084. //Calc for the width of the scroll bar
  1085. // if(textWidth >= width)
  1086. // width += 20;
  1087. // mTl->setCellSize(Point2I(width,mFont->getHeight() + textSpace));
  1088. //Pop menu list above the button
  1089. // scrollPoint.set(pointInGC.x, menuSpace - 1); // DAW: Removed as calculated outside the 'if', and was wrong to begin with
  1090. setScroll = true;
  1091. }
  1092. //No scroll bar needed
  1093. else
  1094. {
  1095. maxYdis = mTl->mBounds.extent.y + sbBorder; // DAW: Instead of adding sbBorder, it was: 'textSpace'
  1096. // scrollPoint.set(pointInGC.x, pointInGC.y - maxYdis -1); // DAW: Removed as calculated outside the 'if' and the '-1' at the end is wrong
  1097. }
  1098. // DAW: Added the next two lines
  1099. scrollPoint.set(pointInGC.x, pointInGC.y - maxYdis); // DAW: Used to have the following on the end: '-1);'
  1100. }
  1101. //Scroll bar needed but Don't pop above button
  1102. else
  1103. {
  1104. //mRevNum = 0; // DAW: Commented out as it has been added at the beginning of this function
  1105. if ( mSelIndex >= 0 )
  1106. mTl->setSelectedCell( Point2I( 0, mSelIndex ) ); // DAW: Added as we were not setting the selected cell if the list is displayed down.
  1107. // DAW: Removed width recalculation for scroll bar as the scroll bar is already being taken into account.
  1108. //Calc for the width of the scroll bar
  1109. // if(textWidth >= width)
  1110. // width += 20;
  1111. // mTl->setCellSize(Point2I(width,mFont->getHeight() + textSpace));
  1112. setScroll = true;
  1113. }
  1114. }
  1115. //No scroll bar needed
  1116. else
  1117. {
  1118. if ( mSelIndex >= 0 )
  1119. mTl->setSelectedCell( Point2I( 0, mSelIndex ) ); // DAW: Added as we were not setting the selected cell if the list is displayed down.
  1120. //maxYdis = mTl->mBounds.extent.y + textSpace;
  1121. maxYdis = mTl->mBounds.extent.y + sbBorder; // DAW: Added in the border thickness of the scroll control and removed the addition of textSpace
  1122. }
  1123. //offset it from the background so it lines up properly
  1124. mSc->mBounds.point = mBackground->globalToLocalCoord(scrollPoint);
  1125. if(mSc->mBounds.point.x + width > mBackground->mBounds.extent.x)
  1126. if(width - mBounds.extent.x > 0)
  1127. mSc->mBounds.point.x -= width - mBounds.extent.x;
  1128. //mSc->mBounds.extent.set(width-1, maxYdis);
  1129. mSc->mBounds.extent.set(width, maxYdis); // DAW: Not sure why the '-1' above.
  1130. mSc->registerObject();
  1131. mTl->registerObject();
  1132. mBackground->registerObject();
  1133. mSc->addObject( mTl );
  1134. mBackground->addObject( mSc );
  1135. mBackgroundCancel = false; // DAW: Setup check if user clicked on the background instead of the text list (ie: didn't want to change their current selection).
  1136. root->pushDialogControl(mBackground, 99);
  1137. if ( setScroll )
  1138. {
  1139. if ( mSelIndex )
  1140. mTl->scrollCellVisible( Point2I(0, mSelIndex));
  1141. else
  1142. mTl->scrollCellVisible( Point2I( 0, 0 ) );
  1143. }
  1144. mTl->setFirstResponder();
  1145. mInAction = true;
  1146. }
  1147. //------------------------------------------------------------------------------
  1148. void GuiPopUpMenuCtrl::addChildren()
  1149. {
  1150. mTl = new GuiPopupTextListCtrl(this);
  1151. AssertFatal(mTl, "Failed to create the GuiPopUpTextListCtrl for the PopUpMenu");
  1152. mTl->mProfile = mProfile->mProfileForChildren ? mProfile->mProfileForChildren : mProfile; // Use the children's profile rather than the parent's profile, if it exists.
  1153. mTl->setField("noDuplicates", "false");
  1154. mSc = new GuiScrollCtrl;
  1155. AssertFatal(mSc, "Failed to create the GuiScrollCtrl for the PopUpMenu");
  1156. mSc->mProfile = mProfile->mProfileForChildren ? mProfile->mProfileForChildren : mProfile; // Use the children's profile rather than the parent's profile, if it exists.
  1157. mSc->setField("hScrollBar","AlwaysOff");
  1158. mSc->setField("vScrollBar","dynamic");
  1159. //if(mRenderScrollInNA) // DAW: Force the scroll control to render using fillColorNA rather than fillColor
  1160. // mSc->mUseNABackground = true;
  1161. mBackground = new GuiPopUpBackgroundCtrl(this, mTl);
  1162. AssertFatal(mBackground, "Failed to create the GuiBackgroundCtrl for the PopUpMenu");
  1163. }
  1164. //------------------------------------------------------------------------------
  1165. void GuiPopUpMenuCtrl::repositionPopup()
  1166. {
  1167. if ( !mInAction || !mSc || !mTl )
  1168. return;
  1169. // I'm not concerned with this right now...
  1170. }
  1171. //------------------------------------------------------------------------------
  1172. void GuiPopUpMenuCtrl::reverseTextList()
  1173. {
  1174. mTl->clear();
  1175. for(S32 i=mEntries.size()-1; i >= 0; --i)
  1176. mTl->addEntry(mEntries[i].id, mEntries[i].buf);
  1177. // Don't lose the selected cell:
  1178. if ( mSelIndex >= 0 )
  1179. mTl->setSelectedCell( Point2I( 0, mEntries.size() - mSelIndex - 1 ) );
  1180. mRevNum = mEntries.size() - 1;
  1181. }
  1182. //------------------------------------------------------------------------------
  1183. bool GuiPopUpMenuCtrl::getFontColor( ColorI &fontColor, S32 id, bool selected, bool mouseOver )
  1184. {
  1185. U32 i;
  1186. Entry* entry = NULL;
  1187. for ( i = 0; i < (U32)mEntries.size(); i++ )
  1188. {
  1189. if ( mEntries[i].id == id )
  1190. {
  1191. entry = &mEntries[i];
  1192. break;
  1193. }
  1194. }
  1195. if ( !entry )
  1196. return( false );
  1197. if ( entry->scheme != 0 )
  1198. {
  1199. // Find the entry's color scheme:
  1200. for ( i = 0; i < (U32)mSchemes.size(); i++ )
  1201. {
  1202. if ( mSchemes[i].id == entry->scheme )
  1203. {
  1204. fontColor = selected ? mSchemes[i].fontColorSEL : mouseOver ? mSchemes[i].fontColorHL : mSchemes[i].fontColor;
  1205. return( true );
  1206. }
  1207. }
  1208. }
  1209. // Default color scheme...
  1210. fontColor = selected ? mProfile->mFontColorSEL : mouseOver ? mProfile->mFontColorHL : mProfile->mFontColorNA; // DAW: Modified the final colour choice from mProfile->mFontColor to mProfile->mFontColorNA
  1211. return( true );
  1212. }
  1213. //------------------------------------------------------------------------------
  1214. // DAW: Added
  1215. bool GuiPopUpMenuCtrl::getColoredBox( ColorI &fontColor, S32 id )
  1216. {
  1217. U32 i;
  1218. Entry* entry = NULL;
  1219. for ( i = 0; i < (U32)mEntries.size(); i++ )
  1220. {
  1221. if ( mEntries[i].id == id )
  1222. {
  1223. entry = &mEntries[i];
  1224. break;
  1225. }
  1226. }
  1227. if ( !entry )
  1228. return( false );
  1229. if( entry->usesColorBox == false)
  1230. return false;
  1231. fontColor = entry->colorbox;
  1232. return true;
  1233. }
  1234. //------------------------------------------------------------------------------
  1235. void GuiPopUpMenuCtrl::onMouseDown(const GuiEvent &event)
  1236. {
  1237. if ( !mVisible || !mActive || !mAwake )
  1238. return;
  1239. onAction();
  1240. }
  1241. //------------------------------------------------------------------------------
  1242. void GuiPopUpMenuCtrl::onMouseUp(const GuiEvent &event)
  1243. {
  1244. if ( !mVisible || !mActive || !mAwake )
  1245. return;
  1246. }
  1247. //------------------------------------------------------------------------------
  1248. // DAW: Added
  1249. void GuiPopUpMenuCtrl::onMouseEnter(const GuiEvent &event)
  1250. {
  1251. if ( !mVisible || !mActive || !mAwake )
  1252. return;
  1253. mMouseOver = true;
  1254. }
  1255. //------------------------------------------------------------------------------
  1256. // DAW: Added
  1257. void GuiPopUpMenuCtrl::onMouseLeave(const GuiEvent &)
  1258. {
  1259. if ( !mVisible || !mActive || !mAwake )
  1260. return;
  1261. mMouseOver = false;
  1262. }
  1263. //------------------------------------------------------------------------------
  1264. void GuiPopUpMenuCtrl::setupAutoScroll(const GuiEvent &event)
  1265. {
  1266. GuiControl *parent = getParent();
  1267. if (! parent) return;
  1268. Point2I mousePt = mSc->globalToLocalCoord(event.mousePoint);
  1269. mEventSave = event;
  1270. if(mLastYvalue != mousePt.y)
  1271. {
  1272. mScrollDir = GuiScrollCtrl::None;
  1273. if(mousePt.y > mSc->mBounds.extent.y || mousePt.y < 0)
  1274. {
  1275. S32 topOrBottom = (mousePt.y > mSc->mBounds.extent.y) ? 1 : 0;
  1276. mSc->scrollTo(0, topOrBottom);
  1277. return;
  1278. }
  1279. F32 percent = (F32)mousePt.y / (F32)mSc->mBounds.extent.y;
  1280. if(percent > 0.7f && mousePt.y > mLastYvalue)
  1281. {
  1282. mIncValue = percent - 0.5f;
  1283. mScrollDir = GuiScrollCtrl::DownArrow;
  1284. }
  1285. else if(percent < 0.3f && mousePt.y < mLastYvalue)
  1286. {
  1287. mIncValue = 0.5f - percent;
  1288. mScrollDir = GuiScrollCtrl::UpArrow;
  1289. }
  1290. mLastYvalue = mousePt.y;
  1291. }
  1292. }
  1293. //------------------------------------------------------------------------------
  1294. void GuiPopUpMenuCtrl::autoScroll()
  1295. {
  1296. mScrollCount += mIncValue;
  1297. while(mScrollCount > 1)
  1298. {
  1299. mSc->autoScroll(mScrollDir);
  1300. mScrollCount -= 1;
  1301. }
  1302. mTl->onMouseMove(mEventSave);
  1303. }
  1304. //------------------------------------------------------------------------------
  1305. void GuiPopUpMenuCtrl::replaceText(S32 boolVal)
  1306. {
  1307. mReplaceText = boolVal;
  1308. }