guiPopUpCtrlEx.cpp 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738
  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 "gui/core/guiCanvas.h"
  23. #include "gui/controls/guiPopUpCtrlEx.h"
  24. #include "console/consoleTypes.h"
  25. #include "console/engineAPI.h"
  26. #include "gui/core/guiDefaultControlRender.h"
  27. #include "gfx/primBuilder.h"
  28. #include "gfx/gfxDrawUtil.h"
  29. #include "console/engineAPI.h"
  30. ConsoleDocClass( GuiPopUpMenuCtrlEx,
  31. "@brief A control that allows to select a value from a drop-down list.\n\n"
  32. "This is essentially a GuiPopUpMenuCtrl, but with quite a few more features.\n\n"
  33. "@tsexample\n"
  34. "new GuiPopUpMenuCtrlEx()\n"
  35. "{\n"
  36. " maxPopupHeight = \"200\";\n"
  37. " sbUsesNAColor = \"0\";\n"
  38. " reverseTextList = \"0\";\n"
  39. " bitmapBounds = \"16 16\";\n"
  40. " hotTrackCallback = \"0\";\n"
  41. " extent = \"64 64\";\n"
  42. " profile = \"GuiDefaultProfile\";\n"
  43. " tooltipProfile = \"GuiToolTipProfile\";\n"
  44. "};\n"
  45. "@endtsexample\n\n"
  46. "@see GuiPopUpMenuCtrl\n"
  47. "@ingroup GuiControls\n");
  48. static ColorI colorWhite(255,255,255); // Added
  49. // Function to return the number of columns in 'string' given delimeters in 'set'
  50. static U32 getColumnCount(const char *string, const char *set)
  51. {
  52. U32 count = 0;
  53. U8 last = 0;
  54. while(*string)
  55. {
  56. last = *string++;
  57. for(U32 i =0; set[i]; i++)
  58. {
  59. if(last == set[i])
  60. {
  61. count++;
  62. last = 0;
  63. break;
  64. }
  65. }
  66. }
  67. if(last)
  68. count++;
  69. return count;
  70. }
  71. // Function to return the 'index' column from 'string' given delimeters in 'set'
  72. static const char *getColumn(const char *string, char* returnbuff, U32 index, const char *set)
  73. {
  74. U32 sz;
  75. while(index--)
  76. {
  77. if(!*string)
  78. return "";
  79. sz = dStrcspn(string, set);
  80. if (string[sz] == 0)
  81. return "";
  82. string += (sz + 1);
  83. }
  84. sz = dStrcspn(string, set);
  85. if (sz == 0)
  86. return "";
  87. char *ret = returnbuff;
  88. dStrncpy(ret, string, sz);
  89. ret[sz] = '\0';
  90. return ret;
  91. }
  92. GuiPopUpBackgroundCtrlEx::GuiPopUpBackgroundCtrlEx(GuiPopUpMenuCtrlEx *ctrl, GuiPopupTextListCtrlEx *textList)
  93. {
  94. mPopUpCtrl = ctrl;
  95. mTextList = textList;
  96. }
  97. void GuiPopUpBackgroundCtrlEx::onMouseDown(const GuiEvent &event)
  98. {
  99. // mTextList->setSelectedCell(Point2I(-1,-1)); // Removed
  100. mPopUpCtrl->mBackgroundCancel = true; // Set that the user didn't click within the text list. Replaces the line above.
  101. mPopUpCtrl->closePopUp();
  102. }
  103. //------------------------------------------------------------------------------
  104. GuiPopupTextListCtrlEx::GuiPopupTextListCtrlEx()
  105. {
  106. mPopUpCtrl = NULL;
  107. }
  108. //------------------------------------------------------------------------------
  109. GuiPopupTextListCtrlEx::GuiPopupTextListCtrlEx(GuiPopUpMenuCtrlEx *ctrl)
  110. {
  111. mPopUpCtrl = ctrl;
  112. }
  113. //------------------------------------------------------------------------------
  114. //------------------------------------------------------------------------------
  115. //void GuiPopUpTextListCtrl::onCellSelected( Point2I /*cell*/ )
  116. //{
  117. // // Do nothing, the parent control will take care of everything...
  118. //}
  119. void GuiPopupTextListCtrlEx::onCellSelected( Point2I cell )
  120. {
  121. // The old function is above. This new one will only call the the select
  122. // functions if we were not cancelled by a background click.
  123. // Check if we were cancelled by the user clicking on the Background ie: anywhere
  124. // other than within the text list.
  125. if(mPopUpCtrl->mBackgroundCancel)
  126. return;
  127. if( isMethod( "onSelect" ) )
  128. Con::executef(this, "onSelect", Con::getFloatArg(cell.x), Con::getFloatArg(cell.y));
  129. //call the console function
  130. execConsoleCallback();
  131. //if (mConsoleCommand[0])
  132. // Con::evaluate(mConsoleCommand, false);
  133. }
  134. bool GuiPopupTextListCtrlEx::hasCategories()
  135. {
  136. for( S32 i = 0; i < mList.size(); i++)
  137. {
  138. if( mList[i].id == -1)
  139. return true;
  140. }
  141. return false;
  142. }
  143. //------------------------------------------------------------------------------
  144. bool GuiPopupTextListCtrlEx::onKeyDown(const GuiEvent &event)
  145. {
  146. //if the control is a dead end, don't process the input:
  147. if ( !mVisible || !mActive || !mAwake )
  148. return false;
  149. //see if the key down is a <return> or not
  150. if ( event.modifier == 0 )
  151. {
  152. if ( event.keyCode == KEY_RETURN )
  153. {
  154. mPopUpCtrl->closePopUp();
  155. return true;
  156. }
  157. else if ( event.keyCode == KEY_ESCAPE )
  158. {
  159. mSelectedCell.set( -1, -1 );
  160. mPopUpCtrl->closePopUp();
  161. return true;
  162. }
  163. }
  164. //otherwise, pass the event to it's parent
  165. return Parent::onKeyDown(event);
  166. }
  167. void GuiPopupTextListCtrlEx::onMouseUp(const GuiEvent &event)
  168. {
  169. Point2I pt = globalToLocalCoord(event.mousePoint);
  170. pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
  171. Point2I cell(
  172. (pt.x < 0 ? -1 : pt.x / mCellSize.x),
  173. (pt.y < 0 ? -1 : pt.y / mCellSize.y)
  174. );
  175. if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
  176. {
  177. if (mList[cell.y].id == -1)
  178. return;
  179. }
  180. Parent::onMouseUp(event);
  181. mPopUpCtrl->closePopUp();
  182. }
  183. void GuiPopupTextListCtrlEx::onMouseMove( const GuiEvent &event )
  184. {
  185. if( !mPopUpCtrl || !mPopUpCtrl->isMethod("onHotTrackItem") )
  186. return Parent::onMouseMove( event );
  187. Point2I pt = globalToLocalCoord(event.mousePoint);
  188. pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
  189. Point2I cell( (pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y) );
  190. // Within Bounds?
  191. if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
  192. // Hot Track notification
  193. Con::executef( mPopUpCtrl, "onHotTrackItem", Con::getIntArg(mList[cell.y].id) );
  194. else
  195. // Hot Track -1
  196. Con::executef( mPopUpCtrl, "onHotTrackItem", Con::getIntArg(-1) );
  197. // Call Parent
  198. Parent::onMouseMove(event);
  199. }
  200. //------------------------------------------------------------------------------
  201. void GuiPopupTextListCtrlEx::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
  202. {
  203. Point2I size;
  204. getCellSize( size );
  205. // Render a background color for the cell
  206. if ( mouseOver )
  207. {
  208. RectI cellR( offset.x, offset.y, size.x, size.y );
  209. GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorHL );
  210. }
  211. else if ( selected )
  212. {
  213. RectI cellR( offset.x, offset.y, size.x, size.y );
  214. GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorSEL );
  215. }
  216. // Define the default x offset for the text
  217. U32 textXOffset = offset.x + mProfile->mTextOffset.x;
  218. // Do we also draw a colored box beside the text?
  219. ColorI boxColor;
  220. bool drawbox = mPopUpCtrl->getColoredBox( boxColor, mList[cell.y].id );
  221. if ( drawbox )
  222. {
  223. Point2I coloredboxsize( 15, 10 );
  224. RectI r( offset.x + mProfile->mTextOffset.x, offset.y + 2, coloredboxsize.x, coloredboxsize.y );
  225. GFX->getDrawUtil()->drawRectFill( r, boxColor );
  226. GFX->getDrawUtil()->drawRect( r, ColorI( 0, 0, 0 ) );
  227. textXOffset += coloredboxsize.x + mProfile->mTextOffset.x;
  228. }
  229. // Render 'Group' background
  230. if ( mList[cell.y].id == -1)
  231. {
  232. RectI cellR( offset.x, offset.y, size.x, size.y );
  233. GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorHL );
  234. }
  235. ColorI fontColor;
  236. mPopUpCtrl->getFontColor( fontColor, mList[cell.y].id, selected, mouseOver );
  237. GFX->getDrawUtil()->setBitmapModulation( fontColor );
  238. //GFX->drawText( mFont, Point2I( offset.x + 4, offset.y ), mList[cell.y].text );
  239. // Get the number of columns in the cell
  240. S32 colcount = getColumnCount(mList[cell.y].text, "\t");
  241. // Are there two or more columns?
  242. if(colcount >= 2)
  243. {
  244. char buff[256];
  245. // Draw the first column
  246. getColumn(mList[cell.y].text, buff, 0, "\t");
  247. GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset, offset.y ), buff ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
  248. // Draw the second column to the right
  249. getColumn(mList[cell.y].text, buff, 1, "\t");
  250. S32 txt_w = mFont->getStrWidth(buff);
  251. GFX->getDrawUtil()->drawText( mFont, Point2I( offset.x+size.x-mProfile->mTextOffset.x-txt_w, offset.y ), buff ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
  252. } else
  253. {
  254. if ((mList[cell.y].id == -1) || (!hasCategories()))
  255. GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset, offset.y ), mList[cell.y].text ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
  256. else
  257. GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset + 8, offset.y ), mList[cell.y].text ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
  258. }
  259. }
  260. //------------------------------------------------------------------------------
  261. //------------------------------------------------------------------------------
  262. IMPLEMENT_CONOBJECT(GuiPopUpMenuCtrlEx);
  263. GuiPopUpMenuCtrlEx::GuiPopUpMenuCtrlEx(void)
  264. {
  265. VECTOR_SET_ASSOCIATION(mEntries);
  266. VECTOR_SET_ASSOCIATION(mSchemes);
  267. mSelIndex = -1;
  268. mActive = true;
  269. mMaxPopupHeight = 200;
  270. mScrollDir = GuiScrollCtrl::None;
  271. mScrollCount = 0;
  272. mLastYvalue = 0;
  273. mIncValue = 0;
  274. mRevNum = 0;
  275. mInAction = false;
  276. mMouseOver = false; // Added
  277. mRenderScrollInNA = false; // Added
  278. mBackgroundCancel = false; // Added
  279. mReverseTextList = false; // Added - Don't reverse text list if displaying up
  280. INIT_IMAGEASSET_ARRAY(Bitmap, GFXDefaultGUIProfile, Normal);
  281. INIT_IMAGEASSET_ARRAY(Bitmap, GFXDefaultGUIProfile, Depressed);
  282. mBitmapBounds.set(16, 16); // Added
  283. mHotTrackItems = false;
  284. mIdMax = -1;
  285. mBackground = NULL;
  286. mTl = NULL;
  287. mSc = NULL;
  288. mReplaceText = false;
  289. }
  290. //------------------------------------------------------------------------------
  291. GuiPopUpMenuCtrlEx::~GuiPopUpMenuCtrlEx()
  292. {
  293. }
  294. //------------------------------------------------------------------------------
  295. void GuiPopUpMenuCtrlEx::initPersistFields(void)
  296. {
  297. addField("maxPopupHeight", TypeS32, Offset(mMaxPopupHeight, GuiPopUpMenuCtrlEx), "Length of menu when it extends");
  298. addField("sbUsesNAColor", TypeBool, Offset(mRenderScrollInNA, GuiPopUpMenuCtrlEx), "Deprecated" "@internal");
  299. addField("reverseTextList", TypeBool, Offset(mReverseTextList, GuiPopUpMenuCtrlEx), "Reverses text list if popup extends up, instead of down");
  300. addProtectedField("bitmap", TypeImageFilename, Offset(mBitmapName, GuiPopUpMenuCtrlEx), _setBitmaps, &defaultProtectedGetFn, "File name of bitmap to use");
  301. addProtectedField("bitmapAsset", TypeImageAssetId, Offset(mBitmapAssetId, GuiPopUpMenuCtrlEx), _setBitmaps, &defaultProtectedGetFn, "Name of bitmap asset to use");
  302. addField("bitmapBounds", TypePoint2I, Offset(mBitmapBounds, GuiPopUpMenuCtrlEx), "Boundaries of bitmap displayed");
  303. addField("hotTrackCallback", TypeBool, Offset(mHotTrackItems, GuiPopUpMenuCtrlEx),
  304. "Whether to provide a 'onHotTrackItem' callback when a list item is hovered over");
  305. Parent::initPersistFields();
  306. }
  307. bool GuiPopUpMenuCtrlEx::_setBitmaps(void* obj, const char* index, const char* data)
  308. {
  309. GuiPopUpMenuCtrlEx* object = static_cast<GuiPopUpMenuCtrlEx*>(obj);
  310. object->setBitmap(data);
  311. return true;
  312. }
  313. //------------------------------------------------------------------------------
  314. ConsoleDocFragment _GuiPopUpMenuCtrlExAdd(
  315. "@brief Adds an entry to the list\n\n"
  316. "@param name String containing the name of the entry\n"
  317. "@param idNum Numerical value assigned to the name\n"
  318. "@param scheme Optional ID associated with a scheme "
  319. "for font coloring, highlight coloring, and selection coloring\n\n",
  320. "GuiPopUpMenuCtrlEx",
  321. "void add(string name, S32 idNum, S32 scheme=0);"
  322. );
  323. DefineEngineMethod( GuiPopUpMenuCtrlEx, add, void, (const char * name, S32 idNum, U32 scheme), ("", -1, 0), "(string name, int idNum, int scheme=0)")
  324. {
  325. object->addEntry(name, idNum, scheme);
  326. }
  327. DefineEngineMethod( GuiPopUpMenuCtrlEx, addCategory, void, (const char* text),,
  328. "@brief Add a category to the list.\n\n"
  329. "Acts as a separator between entries, allowing for sub-lists\n\n"
  330. "@param text Name of the new category\n\n")
  331. {
  332. object->addEntry(text, -1, 0);
  333. }
  334. DefineEngineMethod( GuiPopUpMenuCtrlEx, addScheme, void, (S32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL),,
  335. "@brief Create a new scheme and add it to the list of choices for when a new text entry is added.\n\n"
  336. "@param id Numerical id associated with this scheme\n"
  337. "@param fontColor The base text font color. Formatted as \"Red Green Blue\", each a numerical between 0 and 255.\n"
  338. "@param fontColorHL Color of text when being highlighted. Formatted as \"Red Green Blue\", each a numerical between 0 and 255.\n"
  339. "@param fontColorSel Color of text when being selected. Formatted as \"Red Green Blue\", each a numerical between 0 and 255.\n")
  340. {
  341. /*ColorI fontColor, fontColorHL, fontColorSEL;
  342. U32 r, g, b;
  343. char buf[64];
  344. dStrcpy( buf, argv[3], 64 );
  345. char* temp = dStrtok( buf, " \0" );
  346. r = temp ? dAtoi( temp ) : 0;
  347. temp = dStrtok( NULL, " \0" );
  348. g = temp ? dAtoi( temp ) : 0;
  349. temp = dStrtok( NULL, " \0" );
  350. b = temp ? dAtoi( temp ) : 0;
  351. fontColor.set( r, g, b );
  352. dStrcpy( buf, argv[4], 64 );
  353. temp = dStrtok( buf, " \0" );
  354. r = temp ? dAtoi( temp ) : 0;
  355. temp = dStrtok( NULL, " \0" );
  356. g = temp ? dAtoi( temp ) : 0;
  357. temp = dStrtok( NULL, " \0" );
  358. b = temp ? dAtoi( temp ) : 0;
  359. fontColorHL.set( r, g, b );
  360. dStrcpy( buf, argv[5], 64 );
  361. temp = dStrtok( buf, " \0" );
  362. r = temp ? dAtoi( temp ) : 0;
  363. temp = dStrtok( NULL, " \0" );
  364. g = temp ? dAtoi( temp ) : 0;
  365. temp = dStrtok( NULL, " \0" );
  366. b = temp ? dAtoi( temp ) : 0;
  367. fontColorSEL.set( r, g, b );*/
  368. object->addScheme( id, fontColor, fontColorHL, fontColorSEL );
  369. }
  370. //ConsoleMethod( GuiPopUpMenuCtrlEx, addScheme, void, 6, 6, "(int id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL)")
  371. //{
  372. // ColorI fontColor, fontColorHL, fontColorSEL;
  373. // U32 r, g, b;
  374. // char buf[64];
  375. //
  376. // dStrcpy( buf, argv[3], 64 );
  377. // char* temp = dStrtok( buf, " \0" );
  378. // r = temp ? dAtoi( temp ) : 0;
  379. // temp = dStrtok( NULL, " \0" );
  380. // g = temp ? dAtoi( temp ) : 0;
  381. // temp = dStrtok( NULL, " \0" );
  382. // b = temp ? dAtoi( temp ) : 0;
  383. // fontColor.set( r, g, b );
  384. //
  385. // dStrcpy( buf, argv[4], 64 );
  386. // temp = dStrtok( buf, " \0" );
  387. // r = temp ? dAtoi( temp ) : 0;
  388. // temp = dStrtok( NULL, " \0" );
  389. // g = temp ? dAtoi( temp ) : 0;
  390. // temp = dStrtok( NULL, " \0" );
  391. // b = temp ? dAtoi( temp ) : 0;
  392. // fontColorHL.set( r, g, b );
  393. //
  394. // dStrcpy( buf, argv[5], 64 );
  395. // temp = dStrtok( buf, " \0" );
  396. // r = temp ? dAtoi( temp ) : 0;
  397. // temp = dStrtok( NULL, " \0" );
  398. // g = temp ? dAtoi( temp ) : 0;
  399. // temp = dStrtok( NULL, " \0" );
  400. // b = temp ? dAtoi( temp ) : 0;
  401. // fontColorSEL.set( r, g, b );
  402. //
  403. // object->addScheme( dAtoi( argv[2] ), fontColor, fontColorHL, fontColorSEL );
  404. //}
  405. DefineEngineMethod( GuiPopUpMenuCtrlEx, setText, void, ( const char* text),,
  406. "@brief Set the current text to a specified value.\n\n"
  407. "@param text String containing new text to set\n\n")
  408. {
  409. object->setText(text);
  410. }
  411. DefineEngineMethod( GuiPopUpMenuCtrlEx, getText, const char*, (),,
  412. "@brief Get the.\n\n"
  413. "Detailed description\n\n"
  414. "@param param Description\n\n"
  415. "@tsexample\n"
  416. "// Comment\n"
  417. "code();\n"
  418. "@endtsexample\n\n"
  419. "@return Returns current text in string format")
  420. {
  421. return object->getText();
  422. }
  423. DefineEngineMethod( GuiPopUpMenuCtrlEx, clear, void, (),,
  424. "@brief Clear the popup list.\n\n")
  425. {
  426. object->clear();
  427. }
  428. DefineEngineMethod( GuiPopUpMenuCtrlEx, sort, void, (),,
  429. "@brief Sort the list alphabetically.\n\n")
  430. {
  431. object->sort();
  432. }
  433. DefineEngineMethod( GuiPopUpMenuCtrlEx, sortID, void, (),,
  434. "@brief Sort the list by ID.\n\n")
  435. {
  436. object->sortID();
  437. }
  438. DefineEngineMethod( GuiPopUpMenuCtrlEx, forceOnAction, void, (),,
  439. "@brief Manually for the onAction function, which updates everything in this control.\n\n")
  440. {
  441. object->onAction();
  442. }
  443. DefineEngineMethod( GuiPopUpMenuCtrlEx, forceClose, void, (),,
  444. "@brief Manually force this control to collapse and close.\n\n")
  445. {
  446. object->closePopUp();
  447. }
  448. DefineEngineMethod( GuiPopUpMenuCtrlEx, getSelected, S32, (),,
  449. "@brief Get the current selection of the menu.\n\n"
  450. "@return Returns the ID of the currently selected entry")
  451. {
  452. return object->getSelected();
  453. }
  454. ConsoleDocFragment _GuiPopUpMenuCtrlExsetSelected(
  455. "brief Manually set an entry as selected int his control\n\n"
  456. "@param id The ID of the entry to select\n"
  457. "@param scripCallback Optional boolean that forces the script callback if true\n",
  458. "GuiPopUpMenuCtrlEx",
  459. "setSelected(int id, bool scriptCallback=true);"
  460. );
  461. DefineEngineMethod( GuiPopUpMenuCtrlEx, setSelected, void, (S32 id, bool scriptCallback), (true), "(int id, [scriptCallback=true])"
  462. "@hide")
  463. {
  464. object->setSelected( id, scriptCallback );
  465. }
  466. ConsoleDocFragment _GuiPopUpMenuCtrlExsetFirstSelected(
  467. "brief Manually set the selection to the first entry\n\n"
  468. "@param scripCallback Optional boolean that forces the script callback if true\n",
  469. "GuiPopUpMenuCtrlEx",
  470. "setSelected(bool scriptCallback=true);"
  471. );
  472. DefineEngineMethod( GuiPopUpMenuCtrlEx, setFirstSelected, void, (bool scriptCallback), (true), "([scriptCallback=true])"
  473. "@hide")
  474. {
  475. object->setFirstSelected( scriptCallback );
  476. }
  477. DefineEngineMethod( GuiPopUpMenuCtrlEx, setNoneSelected, void, ( S32 param),,
  478. "@brief Clears selection in the menu.\n\n")
  479. {
  480. object->setNoneSelected();
  481. }
  482. DefineEngineMethod( GuiPopUpMenuCtrlEx, getTextById, const char*, (S32 id),,
  483. "@brief Get the text of an entry based on an ID.\n\n"
  484. "@param id The ID assigned to the entry being queried\n\n"
  485. "@return String contained by the specified entry, NULL if empty or bad ID")
  486. {
  487. return(object->getTextById(id));
  488. }
  489. DefineEngineMethod( GuiPopUpMenuCtrlEx, getColorById, ColorI, (S32 id), ,
  490. "@brief Get color of an entry's box\n\n"
  491. "@param id ID number of entry to query\n\n"
  492. "@return ColorI in the format of \"Red Green Blue Alpha\", each of with is a value between 0 - 255")
  493. {
  494. ColorI color;
  495. object->getColoredBox(color, id);
  496. return color;
  497. }
  498. DefineEngineMethod( GuiPopUpMenuCtrlEx, setEnumContent, void, ( const char * className, const char * enumName ), ,
  499. "@brief This fills the popup with a classrep's field enumeration type info.\n\n"
  500. "More of a helper function than anything. If console access to the field list is added, "
  501. "at least for the enumerated types, then this should go away.\n\n"
  502. "@param class Name of the class containing the enum\n"
  503. "@param enum Name of the enum value to acces\n")
  504. {
  505. AbstractClassRep * classRep = AbstractClassRep::getClassList();
  506. // walk the class list to get our class
  507. while(classRep)
  508. {
  509. if(!dStricmp(classRep->getClassName(), className))
  510. break;
  511. classRep = classRep->getNextClass();
  512. }
  513. // get it?
  514. if(!classRep)
  515. {
  516. Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", className);
  517. return;
  518. }
  519. // walk the fields to check for this one (findField checks StringTableEntry ptrs...)
  520. U32 i;
  521. for(i = 0; i < classRep->mFieldList.size(); i++)
  522. if(!dStricmp(classRep->mFieldList[i].pFieldname, enumName))
  523. break;
  524. // found it?
  525. if(i == classRep->mFieldList.size())
  526. {
  527. Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", enumName, className);
  528. return;
  529. }
  530. const AbstractClassRep::Field & field = classRep->mFieldList[i];
  531. ConsoleBaseType* conType = ConsoleBaseType::getType( field.type );
  532. // check the type
  533. if( !conType->getEnumTable() )
  534. {
  535. Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", enumName, className);
  536. return;
  537. }
  538. // fill it
  539. const EngineEnumTable& table = *( conType->getEnumTable() );
  540. const U32 numValues = table.getNumValues();
  541. for(i = 0; i < numValues; i++)
  542. object->addEntry( table[i].getName(), table[i] );
  543. }
  544. //------------------------------------------------------------------------------
  545. DefineEngineMethod( GuiPopUpMenuCtrlEx, findText, S32, (const char * text), , "(string text)"
  546. "Returns the id of the first entry containing the specified text or -1 if not found."
  547. "@param text String value used for the query\n\n"
  548. "@return Numerical ID of entry containing the text.")
  549. {
  550. return( object->findText( text ) );
  551. }
  552. //------------------------------------------------------------------------------
  553. DefineEngineMethod( GuiPopUpMenuCtrlEx, size, S32, (), ,
  554. "@brief Get the size of the menu\n\n"
  555. "@return Number of entries in the menu\n")
  556. {
  557. return( object->getNumEntries() );
  558. }
  559. //------------------------------------------------------------------------------
  560. DefineEngineMethod( GuiPopUpMenuCtrlEx, replaceText, void, (S32 boolVal), ,
  561. "@brief Flag that causes each new text addition to replace the current entry\n\n"
  562. "@param True to turn on replacing, false to disable it")
  563. {
  564. object->replaceText(boolVal);
  565. }
  566. //------------------------------------------------------------------------------
  567. // Added
  568. bool GuiPopUpMenuCtrlEx::onWake()
  569. {
  570. if ( !Parent::onWake() )
  571. return false;
  572. // Set the bitmap for the popup.
  573. setBitmap(getBitmap(Normal));
  574. // Now update the Form Control's bitmap array, and possibly the child's too
  575. mProfile->constructBitmapArray();
  576. if ( mProfile->getChildrenProfile() )
  577. mProfile->getChildrenProfile()->constructBitmapArray();
  578. return true;
  579. }
  580. //------------------------------------------------------------------------------
  581. bool GuiPopUpMenuCtrlEx::onAdd()
  582. {
  583. if ( !Parent::onAdd() )
  584. return false;
  585. mSelIndex = -1;
  586. mReplaceText = true;
  587. return true;
  588. }
  589. //------------------------------------------------------------------------------
  590. void GuiPopUpMenuCtrlEx::onSleep()
  591. {
  592. Parent::onSleep();
  593. closePopUp(); // Tests in function.
  594. }
  595. //------------------------------------------------------------------------------
  596. void GuiPopUpMenuCtrlEx::clear()
  597. {
  598. mEntries.setSize(0);
  599. setText("");
  600. mSelIndex = -1;
  601. mRevNum = 0;
  602. mIdMax = -1;
  603. }
  604. //------------------------------------------------------------------------------
  605. void GuiPopUpMenuCtrlEx::clearEntry( S32 entry )
  606. {
  607. if( entry == -1 )
  608. return;
  609. U32 i = 0;
  610. for ( ; i < mEntries.size(); i++ )
  611. {
  612. if ( mEntries[i].id == entry )
  613. break;
  614. }
  615. mEntries.erase( i );
  616. if( mEntries.size() <= 0 )
  617. {
  618. mEntries.setSize(0);
  619. setText("");
  620. mSelIndex = -1;
  621. mRevNum = 0;
  622. }
  623. else
  624. {
  625. if( entry == mSelIndex )
  626. {
  627. setText("");
  628. mSelIndex = -1;
  629. }
  630. else
  631. {
  632. mSelIndex--;
  633. }
  634. }
  635. }
  636. //------------------------------------------------------------------------------
  637. DefineEngineMethod( GuiPopUpMenuCtrlEx, clearEntry, void, (S32 entry), , "(S32 entry)")
  638. {
  639. object->clearEntry(entry);
  640. }
  641. //------------------------------------------------------------------------------
  642. static S32 QSORT_CALLBACK textCompare(const void *a,const void *b)
  643. {
  644. GuiPopUpMenuCtrlEx::Entry *ea = (GuiPopUpMenuCtrlEx::Entry *) (a);
  645. GuiPopUpMenuCtrlEx::Entry *eb = (GuiPopUpMenuCtrlEx::Entry *) (b);
  646. return (dStrnatcasecmp(ea->buf, eb->buf));
  647. }
  648. // Added to sort by entry ID
  649. //------------------------------------------------------------------------------
  650. static S32 QSORT_CALLBACK idCompare(const void *a,const void *b)
  651. {
  652. GuiPopUpMenuCtrlEx::Entry *ea = (GuiPopUpMenuCtrlEx::Entry *) (a);
  653. GuiPopUpMenuCtrlEx::Entry *eb = (GuiPopUpMenuCtrlEx::Entry *) (b);
  654. return ( (ea->id < eb->id) ? -1 : ((ea->id > eb->id) ? 1 : 0) );
  655. }
  656. //------------------------------------------------------------------------------
  657. // Added
  658. void GuiPopUpMenuCtrlEx::setBitmap(const char *name)
  659. {
  660. StringTableEntry bitmapName = StringTable->insert(name);
  661. if (bitmapName != StringTable->EmptyString())
  662. {
  663. char buffer[1024];
  664. char* p;
  665. dStrcpy(buffer, bitmapName, 1024);
  666. p = buffer + dStrlen(buffer);
  667. S32 pLen = 1024 - dStrlen(buffer);
  668. dStrcpy(p, "_n", pLen);
  669. _setBitmap((StringTableEntry)buffer, Normal);
  670. dStrcpy(p, "_d", pLen);
  671. _setBitmap((StringTableEntry)buffer, Depressed);
  672. if (!mBitmap[Depressed])
  673. mBitmap[Depressed] = mBitmap[Normal];
  674. }
  675. else
  676. {
  677. _setBitmap(StringTable->EmptyString(), Normal);
  678. _setBitmap(StringTable->EmptyString(), Depressed);
  679. }
  680. setUpdate();
  681. }
  682. //------------------------------------------------------------------------------
  683. void GuiPopUpMenuCtrlEx::sort()
  684. {
  685. S32 size = mEntries.size();
  686. if( size > 0 )
  687. dQsort( mEntries.address(), size, sizeof(Entry), textCompare);
  688. // Entries need to re-Id themselves
  689. for( U32 i = 0; i < mEntries.size(); i++ )
  690. mEntries[i].id = i;
  691. }
  692. // Added to sort by entry ID
  693. //------------------------------------------------------------------------------
  694. void GuiPopUpMenuCtrlEx::sortID()
  695. {
  696. S32 size = mEntries.size();
  697. if( size > 0 )
  698. dQsort( mEntries.address(), size, sizeof(Entry), idCompare);
  699. // Entries need to re-Id themselves
  700. for( U32 i = 0; i < mEntries.size(); i++ )
  701. mEntries[i].id = i;
  702. }
  703. //------------------------------------------------------------------------------
  704. void GuiPopUpMenuCtrlEx::addEntry(const char *buf, S32 id, U32 scheme)
  705. {
  706. if( !buf )
  707. {
  708. //Con::printf( "GuiPopupMenuCtrlEx::addEntry - Invalid buffer!" );
  709. return;
  710. }
  711. // Ensure that there are no other entries with exactly the same name
  712. for ( U32 i = 0; i < mEntries.size(); i++ )
  713. {
  714. if ( String::compare( mEntries[i].buf, buf ) == 0 )
  715. return;
  716. }
  717. // If we don't give an id, create one from mIdMax
  718. if( id == -1 )
  719. id = mIdMax + 1;
  720. // Increase mIdMax when an id is greater than it
  721. if( id > mIdMax )
  722. mIdMax = id;
  723. Entry e;
  724. dStrcpy( e.buf, buf, 256 );
  725. e.id = id;
  726. e.scheme = scheme;
  727. // see if there is a shortcut key
  728. char * cp = dStrchr( e.buf, '~' );
  729. e.ascii = cp ? cp[1] : 0;
  730. // See if there is a colour box defined with the text
  731. char *cb = dStrchr( e.buf, '|' );
  732. if ( cb )
  733. {
  734. e.usesColorBox = true;
  735. cb[0] = '\0';
  736. char* red = &cb[1];
  737. cb = dStrchr(red, '|');
  738. cb[0] = '\0';
  739. char* green = &cb[1];
  740. cb = dStrchr(green, '|');
  741. cb[0] = '\0';
  742. char* blue = &cb[1];
  743. U32 r = dAtoi(red);
  744. U32 g = dAtoi(green);
  745. U32 b = dAtoi(blue);
  746. e.colorbox = ColorI(r,g,b);
  747. }
  748. else
  749. {
  750. e.usesColorBox = false;
  751. }
  752. mEntries.push_back(e);
  753. if ( mInAction && mTl )
  754. {
  755. // Add the new entry:
  756. mTl->addEntry( e.id, e.buf );
  757. repositionPopup();
  758. }
  759. }
  760. //------------------------------------------------------------------------------
  761. void GuiPopUpMenuCtrlEx::addScheme( U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL )
  762. {
  763. if ( !id )
  764. return;
  765. Scheme newScheme;
  766. newScheme.id = id;
  767. newScheme.fontColor = fontColor;
  768. newScheme.fontColorHL = fontColorHL;
  769. newScheme.fontColorSEL = fontColorSEL;
  770. mSchemes.push_back( newScheme );
  771. }
  772. //------------------------------------------------------------------------------
  773. S32 GuiPopUpMenuCtrlEx::getSelected()
  774. {
  775. if (mSelIndex == -1)
  776. return 0;
  777. return mEntries[mSelIndex].id;
  778. }
  779. //------------------------------------------------------------------------------
  780. const char* GuiPopUpMenuCtrlEx::getTextById(S32 id)
  781. {
  782. for ( U32 i = 0; i < mEntries.size(); i++ )
  783. {
  784. if ( mEntries[i].id == id )
  785. return( mEntries[i].buf );
  786. }
  787. return( "" );
  788. }
  789. //------------------------------------------------------------------------------
  790. S32 GuiPopUpMenuCtrlEx::findText( const char* text )
  791. {
  792. for ( U32 i = 0; i < mEntries.size(); i++ )
  793. {
  794. if ( String::compare( text, mEntries[i].buf ) == 0 )
  795. return( mEntries[i].id );
  796. }
  797. return( -1 );
  798. }
  799. //------------------------------------------------------------------------------
  800. void GuiPopUpMenuCtrlEx::setSelected(S32 id, bool bNotifyScript )
  801. {
  802. S32 i;
  803. for ( i = 0; U32(i) < mEntries.size(); i++ )
  804. {
  805. if ( id == mEntries[i].id )
  806. {
  807. i = ( mRevNum > i ) ? mRevNum - i : i;
  808. mSelIndex = i;
  809. if ( mReplaceText ) // Only change the displayed text if appropriate.
  810. {
  811. setText(mEntries[i].buf);
  812. }
  813. // Now perform the popup action:
  814. char idval[24];
  815. dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id );
  816. if ( isMethod( "onSelect" ) && bNotifyScript )
  817. Con::executef( this, "onSelect", idval, mEntries[mSelIndex].buf );
  818. return;
  819. }
  820. }
  821. if ( mReplaceText ) // Only change the displayed text if appropriate.
  822. {
  823. setText("");
  824. }
  825. mSelIndex = -1;
  826. if ( isMethod( "onCancel" ) && bNotifyScript )
  827. Con::executef( this, "onCancel" );
  828. if ( id == -1 )
  829. return;
  830. // Execute the popup console command:
  831. if ( bNotifyScript )
  832. execConsoleCallback();
  833. //if ( mConsoleCommand[0] && bNotifyScript )
  834. // Con::evaluate( mConsoleCommand, false );
  835. }
  836. //------------------------------------------------------------------------------
  837. // Added to set the first item as selected.
  838. void GuiPopUpMenuCtrlEx::setFirstSelected( bool bNotifyScript )
  839. {
  840. if ( mEntries.size() > 0 )
  841. {
  842. mSelIndex = 0;
  843. if ( mReplaceText ) // Only change the displayed text if appropriate.
  844. {
  845. setText( mEntries[0].buf );
  846. }
  847. // Now perform the popup action:
  848. char idval[24];
  849. dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id );
  850. if ( isMethod( "onSelect" ) )
  851. Con::executef( this, "onSelect", idval, mEntries[mSelIndex].buf );
  852. // Execute the popup console command:
  853. if ( bNotifyScript )
  854. execConsoleCallback();
  855. }
  856. else
  857. {
  858. if ( mReplaceText ) // Only change the displayed text if appropriate.
  859. setText("");
  860. mSelIndex = -1;
  861. if ( bNotifyScript )
  862. Con::executef( this, "onCancel" );
  863. }
  864. }
  865. //------------------------------------------------------------------------------
  866. // Added to set no items as selected.
  867. void GuiPopUpMenuCtrlEx::setNoneSelected()
  868. {
  869. if ( mReplaceText ) // Only change the displayed text if appropriate.
  870. {
  871. setText("");
  872. }
  873. mSelIndex = -1;
  874. }
  875. //------------------------------------------------------------------------------
  876. const char *GuiPopUpMenuCtrlEx::getScriptValue()
  877. {
  878. return getText();
  879. }
  880. //------------------------------------------------------------------------------
  881. void GuiPopUpMenuCtrlEx::onRender(Point2I offset, const RectI &updateRect)
  882. {
  883. TORQUE_UNUSED(updateRect);
  884. Point2I localStart;
  885. if ( mScrollDir != GuiScrollCtrl::None )
  886. autoScroll();
  887. GFXDrawUtil* drawUtil = GFX->getDrawUtil();
  888. RectI r( offset, getExtent() );
  889. if ( mInAction )
  890. {
  891. S32 l = r.point.x, r2 = r.point.x + r.extent.x - 1;
  892. S32 t = r.point.y, b = r.point.y + r.extent.y - 1;
  893. // Do we render a bitmap border or lines?
  894. if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
  895. {
  896. // Render the fixed, filled in border
  897. renderFixedBitmapBordersFilled(r, 3, mProfile );
  898. }
  899. else
  900. {
  901. //renderSlightlyLoweredBox(r, mProfile);
  902. drawUtil->drawRectFill( r, mProfile->mFillColor );
  903. }
  904. // Draw a bitmap over the background?
  905. if ( mBitmap[Depressed] )
  906. {
  907. RectI rect(offset, mBitmapBounds);
  908. drawUtil->clearBitmapModulation();
  909. drawUtil->drawBitmapStretch(mBitmap[Depressed], rect );
  910. }
  911. else if (mBitmap[Normal])
  912. {
  913. RectI rect(offset, mBitmapBounds);
  914. drawUtil->clearBitmapModulation();
  915. drawUtil->drawBitmapStretch(mBitmap[Normal], rect );
  916. }
  917. // Do we render a bitmap border or lines?
  918. if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) )
  919. {
  920. drawUtil->drawLine( l, t, l, b, colorWhite );
  921. drawUtil->drawLine( l, t, r2, t, colorWhite );
  922. drawUtil->drawLine( l + 1, b, r2, b, mProfile->mBorderColor );
  923. drawUtil->drawLine( r2, t + 1, r2, b - 1, mProfile->mBorderColor );
  924. }
  925. }
  926. else
  927. // TODO: Implement
  928. // TODO: Add onMouseEnter() and onMouseLeave() and a definition of mMouseOver (see guiButtonBaseCtrl) for this to work.
  929. if ( mMouseOver )
  930. {
  931. S32 l = r.point.x, r2 = r.point.x + r.extent.x - 1;
  932. S32 t = r.point.y, b = r.point.y + r.extent.y - 1;
  933. // Do we render a bitmap border or lines?
  934. if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
  935. {
  936. // Render the fixed, filled in border
  937. renderFixedBitmapBordersFilled( r, 2, mProfile );
  938. }
  939. else
  940. {
  941. drawUtil->drawRectFill( r, mProfile->mFillColorHL );
  942. }
  943. // Draw a bitmap over the background?
  944. if (mBitmap[Normal])
  945. {
  946. RectI rect( offset, mBitmapBounds );
  947. drawUtil->clearBitmapModulation();
  948. drawUtil->drawBitmapStretch(mBitmap[Normal], rect );
  949. }
  950. // Do we render a bitmap border or lines?
  951. if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) )
  952. {
  953. drawUtil->drawLine( l, t, l, b, colorWhite );
  954. drawUtil->drawLine( l, t, r2, t, colorWhite );
  955. drawUtil->drawLine( l + 1, b, r2, b, mProfile->mBorderColor );
  956. drawUtil->drawLine( r2, t + 1, r2, b - 1, mProfile->mBorderColor );
  957. }
  958. }
  959. else
  960. {
  961. // Do we render a bitmap border or lines?
  962. if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
  963. {
  964. // Render the fixed, filled in border
  965. renderFixedBitmapBordersFilled( r, 1, mProfile );
  966. }
  967. else
  968. {
  969. drawUtil->drawRectFill( r, mProfile->mFillColorNA );
  970. }
  971. // Draw a bitmap over the background?
  972. if (mBitmap[Normal])
  973. {
  974. RectI rect(offset, mBitmapBounds);
  975. drawUtil->clearBitmapModulation();
  976. drawUtil->drawBitmapStretch(mBitmap[Normal], rect );
  977. }
  978. // Do we render a bitmap border or lines?
  979. if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) )
  980. {
  981. drawUtil->drawRect( r, mProfile->mBorderColorNA );
  982. }
  983. }
  984. // renderSlightlyRaisedBox(r, mProfile); // Used to be the only 'else' condition to mInAction above.
  985. S32 txt_w = mProfile->mFont->getStrWidth(mText);
  986. localStart.x = 0;
  987. localStart.y = (getHeight() - (mProfile->mFont->getHeight())) / 2;
  988. // align the horizontal
  989. switch (mProfile->mAlignment)
  990. {
  991. case GuiControlProfile::RightJustify:
  992. if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
  993. {
  994. // We're making use of a bitmap border, so take into account the
  995. // right cap of the border.
  996. RectI* bitmapBounds = mProfile->mBitmapArrayRects.address();
  997. localStart.x = getWidth() - bitmapBounds[2].extent.x - txt_w;
  998. }
  999. else
  1000. {
  1001. localStart.x = getWidth() - txt_w;
  1002. }
  1003. break;
  1004. case GuiControlProfile::CenterJustify:
  1005. if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
  1006. {
  1007. // We're making use of a bitmap border, so take into account the
  1008. // right cap of the border.
  1009. RectI* bitmapBounds = mProfile->mBitmapArrayRects.address();
  1010. localStart.x = (getWidth() - bitmapBounds[2].extent.x - txt_w) / 2;
  1011. } else
  1012. {
  1013. localStart.x = (getWidth() - txt_w) / 2;
  1014. }
  1015. break;
  1016. default:
  1017. // GuiControlProfile::LeftJustify
  1018. if ( txt_w > getWidth() )
  1019. {
  1020. // The width of the text is greater than the width of the control.
  1021. // In this case we will right justify the text and leave some space
  1022. // for the down arrow.
  1023. if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
  1024. {
  1025. // We're making use of a bitmap border, so take into account the
  1026. // right cap of the border.
  1027. RectI* bitmapBounds = mProfile->mBitmapArrayRects.address();
  1028. localStart.x = getWidth() - bitmapBounds[2].extent.x - txt_w;
  1029. }
  1030. else
  1031. {
  1032. localStart.x = getWidth() - txt_w - 12;
  1033. }
  1034. }
  1035. else
  1036. {
  1037. localStart.x = mProfile->mTextOffset.x; // Use mProfile->mTextOffset as a controlable margin for the control's text.
  1038. }
  1039. break;
  1040. }
  1041. // Do we first draw a coloured box beside the text?
  1042. ColorI boxColor;
  1043. bool drawbox = getColoredBox( boxColor, mSelIndex);
  1044. if ( drawbox )
  1045. {
  1046. Point2I coloredboxsize( 15, 10 );
  1047. RectI boxBounds( offset.x + mProfile->mTextOffset.x, offset.y + ( (getHeight() - coloredboxsize.y ) / 2 ), coloredboxsize.x, coloredboxsize.y );
  1048. drawUtil->drawRectFill(boxBounds, boxColor);
  1049. drawUtil->drawRect(boxBounds, ColorI(0,0,0));
  1050. localStart.x += coloredboxsize.x + mProfile->mTextOffset.x;
  1051. }
  1052. // Draw the text
  1053. Point2I globalStart = localToGlobalCoord( localStart );
  1054. ColorI fontColor = mActive ? ( mInAction ? mProfile->mFontColor : mProfile->mFontColorNA ) : mProfile->mFontColorNA;
  1055. drawUtil->setBitmapModulation( fontColor ); // was: (mProfile->mFontColor);
  1056. // Get the number of columns in the text
  1057. S32 colcount = getColumnCount( mText, "\t" );
  1058. // Are there two or more columns?
  1059. if ( colcount >= 2 )
  1060. {
  1061. char buff[256];
  1062. // Draw the first column
  1063. getColumn( mText, buff, 0, "\t" );
  1064. drawUtil->drawText( mProfile->mFont, globalStart, buff, mProfile->mFontColors );
  1065. // Draw the second column to the right
  1066. getColumn( mText, buff, 1, "\t" );
  1067. S32 colTxt_w = mProfile->mFont->getStrWidth( buff );
  1068. if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
  1069. {
  1070. // We're making use of a bitmap border, so take into account the
  1071. // right cap of the border.
  1072. RectI* bitmapBounds = mProfile->mBitmapArrayRects.address();
  1073. Point2I textpos = localToGlobalCoord( Point2I( getWidth() - colTxt_w - bitmapBounds[2].extent.x, localStart.y ) );
  1074. drawUtil->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors );
  1075. } else
  1076. {
  1077. Point2I textpos = localToGlobalCoord( Point2I( getWidth() - colTxt_w - 12, localStart.y ) );
  1078. drawUtil->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors );
  1079. }
  1080. } else
  1081. {
  1082. drawUtil->drawText( mProfile->mFont, globalStart, mText, mProfile->mFontColors );
  1083. }
  1084. // If we're rendering a bitmap border, then it will take care of the arrow.
  1085. if ( !(mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size()) )
  1086. {
  1087. // Draw a triangle (down arrow)
  1088. S32 left = r.point.x + r.extent.x - 12;
  1089. S32 right = left + 8;
  1090. S32 middle = left + 4;
  1091. S32 top = r.extent.y / 2 + r.point.y - 4;
  1092. S32 bottom = top + 8;
  1093. PrimBuild::color( mProfile->mFontColor );
  1094. PrimBuild::begin( GFXTriangleList, 3 );
  1095. PrimBuild::vertex2fv( Point3F( (F32)left, (F32)top, 0.0f ) );
  1096. PrimBuild::vertex2fv( Point3F( (F32)right, (F32)top, 0.0f ) );
  1097. PrimBuild::vertex2fv( Point3F( (F32)middle, (F32)bottom, 0.0f ) );
  1098. PrimBuild::end();
  1099. }
  1100. }
  1101. //------------------------------------------------------------------------------
  1102. void GuiPopUpMenuCtrlEx::closePopUp()
  1103. {
  1104. if ( !mInAction )
  1105. return;
  1106. // Get the selection from the text list:
  1107. mSelIndex = mTl->getSelectedCell().y;
  1108. mSelIndex = ( mRevNum >= mSelIndex && mSelIndex != -1 ) ? mRevNum - mSelIndex : mSelIndex;
  1109. if ( mSelIndex != -1 )
  1110. {
  1111. if ( mReplaceText )
  1112. setText( mEntries[mSelIndex].buf );
  1113. setIntVariable( mEntries[mSelIndex].id );
  1114. }
  1115. // Release the mouse:
  1116. mInAction = false;
  1117. mTl->mouseUnlock();
  1118. // Commented out below and moved to the end of the function. See the
  1119. // note below for the reason why.
  1120. /*
  1121. // Pop the background:
  1122. getRoot()->popDialogControl(mBackground);
  1123. // Kill the popup:
  1124. mBackground->removeObject( mSc );
  1125. mTl->deleteObject();
  1126. mSc->deleteObject();
  1127. mBackground->deleteObject();
  1128. // Set this as the first responder:
  1129. setFocus();
  1130. */
  1131. // Now perform the popup action:
  1132. if ( mSelIndex != -1 )
  1133. {
  1134. char idval[24];
  1135. dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id );
  1136. if ( isMethod( "onSelect" ) )
  1137. Con::executef( this, "onSelect", idval, mEntries[mSelIndex].buf );
  1138. }
  1139. else if ( isMethod( "onCancel" ) )
  1140. Con::executef( this, "onCancel" );
  1141. // Execute the popup console command:
  1142. execConsoleCallback();
  1143. //if ( mConsoleCommand[0] )
  1144. // Con::evaluate( mConsoleCommand, false );
  1145. // Reordered this pop dialog to be after the script select callback. When the
  1146. // background was popped it was causing all sorts of focus issues when
  1147. // suddenly the first text edit control took the focus before it came back
  1148. // to this popup.
  1149. // Pop the background:
  1150. GuiCanvas *root = getRoot();
  1151. if ( root )
  1152. root->popDialogControl(mBackground);
  1153. // Kill the popup:
  1154. mBackground->removeObject( mSc );
  1155. mTl->deleteObject();
  1156. mSc->deleteObject();
  1157. mBackground->deleteObject();
  1158. // Set this as the first responder:
  1159. setFirstResponder();
  1160. }
  1161. //------------------------------------------------------------------------------
  1162. bool GuiPopUpMenuCtrlEx::onKeyDown(const GuiEvent &event)
  1163. {
  1164. //if the control is a dead end, don't process the input:
  1165. if ( !mVisible || !mActive || !mAwake )
  1166. return false;
  1167. //see if the key down is a <return> or not
  1168. if ( event.keyCode == KEY_RETURN && event.modifier == 0 )
  1169. {
  1170. onAction();
  1171. return true;
  1172. }
  1173. //otherwise, pass the event to its parent
  1174. return Parent::onKeyDown( event );
  1175. }
  1176. //------------------------------------------------------------------------------
  1177. void GuiPopUpMenuCtrlEx::onAction()
  1178. {
  1179. if (!mActive)
  1180. return;
  1181. GuiControl *canCtrl = getParent();
  1182. addChildren();
  1183. GuiCanvas *root = getRoot();
  1184. Point2I windowExt = root->getExtent();
  1185. mBackground->resize( Point2I(0,0), root->getExtent() );
  1186. S32 textWidth = 0, width = getWidth();
  1187. //const S32 menuSpace = 5; // Removed as no longer used.
  1188. const S32 textSpace = 2;
  1189. bool setScroll = false;
  1190. for ( U32 i = 0; i < mEntries.size(); ++i )
  1191. if ( S32(mProfile->mFont->getStrWidth( mEntries[i].buf )) > textWidth )
  1192. textWidth = mProfile->mFont->getStrWidth( mEntries[i].buf );
  1193. //if(textWidth > getWidth())
  1194. S32 sbWidth = mSc->getControlProfile()->mBorderThickness * 2 + mSc->scrollBarThickness(); // Calculate the scroll bar width
  1195. if ( textWidth > ( getWidth() - sbWidth-mProfile->mTextOffset.x - mSc->getChildMargin().x * 2 ) ) // 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.
  1196. {
  1197. //textWidth +=10;
  1198. textWidth +=sbWidth + mProfile->mTextOffset.x + mSc->getChildMargin().x * 2; // 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.
  1199. width = textWidth;
  1200. // If a child margin is not defined for the scroll control, let's add
  1201. // some space between the text and scroll control for readability
  1202. if(mSc->getChildMargin().x == 0)
  1203. width += textSpace;
  1204. }
  1205. //mTl->setCellSize(Point2I(width, mFont->getHeight()+3));
  1206. mTl->setCellSize(Point2I(width, mProfile->mFont->getHeight() + textSpace)); // Modified the above line to use textSpace rather than the '3' as this is what is used below.
  1207. for ( U32 j = 0; j < mEntries.size(); ++j )
  1208. mTl->addEntry( mEntries[j].id, mEntries[j].buf );
  1209. if ( mSelIndex >= 0 )
  1210. mTl->setSelectedCell( Point2I( 0, mSelIndex ) );
  1211. Point2I pointInGC = canCtrl->localToGlobalCoord( getPosition() );
  1212. Point2I scrollPoint( pointInGC.x, pointInGC.y + getHeight() );
  1213. //Calc max Y distance, so Scroll Ctrl will fit on window
  1214. //S32 maxYdis = windowExt.y - pointInGC.y - getHeight() - menuSpace;
  1215. S32 sbBorder = mSc->getControlProfile()->mBorderThickness * 2 + mSc->getChildMargin().y * 2; // 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
  1216. S32 maxYdis = windowExt.y - pointInGC.y - getHeight() - sbBorder; // - menuSpace; // Need to remove the border thickness from the contained control maximum extent and got rid of the 'menuspace' variable
  1217. //If scroll bars need to be added
  1218. mRevNum = 0; // Added here rather than within the following 'if' statements.
  1219. if ( maxYdis < mTl->getHeight() + sbBorder ) // Instead of adding sbBorder, it was: 'textSpace'
  1220. {
  1221. //Should we pop menu list above the button
  1222. if ( maxYdis < pointInGC.y ) // removed: '- menuSpace)' from check to see if there is more space above the control than below.
  1223. {
  1224. if(mReverseTextList) // Added this check if we should reverse the text list.
  1225. reverseTextList();
  1226. maxYdis = pointInGC.y; // Was at the end: '- menuSpace;'
  1227. //Does the menu need a scroll bar
  1228. if ( maxYdis < mTl->getHeight() + sbBorder ) // Instead of adding sbBorder, it was: 'textSpace'
  1229. {
  1230. // Removed width recalculation for scroll bar as the scroll bar is already being taken into account.
  1231. //Calc for the width of the scroll bar
  1232. // if(textWidth >= width)
  1233. // width += 20;
  1234. // mTl->setCellSize(Point2I(width,mFont->getHeight() + textSpace));
  1235. //Pop menu list above the button
  1236. // scrollPoint.set(pointInGC.x, menuSpace - 1); // Removed as calculated outside the 'if', and was wrong to begin with
  1237. setScroll = true;
  1238. }
  1239. //No scroll bar needed
  1240. else
  1241. {
  1242. maxYdis = mTl->getHeight() + sbBorder; // Instead of adding sbBorder, it was: 'textSpace'
  1243. // scrollPoint.set(pointInGC.x, pointInGC.y - maxYdis -1); // Removed as calculated outside the 'if' and the '-1' at the end is wrong
  1244. }
  1245. // Added the next two lines
  1246. scrollPoint.set(pointInGC.x, pointInGC.y - maxYdis); // Used to have the following on the end: '-1);'
  1247. }
  1248. //Scroll bar needed but Don't pop above button
  1249. else
  1250. {
  1251. //mRevNum = 0; // Commented out as it has been added at the beginning of this function
  1252. // Removed width recalculation for scroll bar as the scroll bar is already being taken into account.
  1253. //Calc for the width of the scroll bar
  1254. // if(textWidth >= width)
  1255. // width += 20;
  1256. // mTl->setCellSize(Point2I(width,mFont->getHeight() + textSpace));
  1257. setScroll = true;
  1258. }
  1259. }
  1260. //No scroll bar needed
  1261. else
  1262. {
  1263. //maxYdis = mTl->getHeight() + textSpace;
  1264. maxYdis = mTl->getHeight() + sbBorder; // Added in the border thickness of the scroll control and removed the addition of textSpace
  1265. }
  1266. RectI newBounds = mSc->getBounds();
  1267. //offset it from the background so it lines up properly
  1268. newBounds.point = mBackground->globalToLocalCoord( scrollPoint );
  1269. if ( newBounds.point.x + width > mBackground->getWidth() )
  1270. if ( width - getWidth() > 0 )
  1271. newBounds.point.x -= width - getWidth();
  1272. //mSc->getExtent().set(width-1, maxYdis);
  1273. newBounds.extent.set( width, maxYdis );
  1274. mSc->setBounds( newBounds ); // Not sure why the '-1' above.
  1275. mSc->registerObject();
  1276. mTl->registerObject();
  1277. mBackground->registerObject();
  1278. mSc->addObject( mTl );
  1279. mBackground->addObject( mSc );
  1280. mBackgroundCancel = false; // Setup check if user clicked on the background instead of the text list (ie: didn't want to change their current selection).
  1281. root->pushDialogControl( mBackground, 99 );
  1282. if ( setScroll )
  1283. {
  1284. // Resize the text list
  1285. Point2I cellSize;
  1286. mTl->getCellSize( cellSize );
  1287. cellSize.x = width - mSc->scrollBarThickness() - sbBorder;
  1288. mTl->setCellSize( cellSize );
  1289. mTl->setWidth( cellSize.x );
  1290. if ( mSelIndex )
  1291. mTl->scrollCellVisible( Point2I( 0, mSelIndex ) );
  1292. else
  1293. mTl->scrollCellVisible( Point2I( 0, 0 ) );
  1294. }
  1295. mTl->setFirstResponder();
  1296. mInAction = true;
  1297. }
  1298. //------------------------------------------------------------------------------
  1299. void GuiPopUpMenuCtrlEx::addChildren()
  1300. {
  1301. mTl = new GuiPopupTextListCtrlEx( this );
  1302. AssertFatal( mTl, "Failed to create the GuiPopUpTextListCtrlEx for the PopUpMenu" );
  1303. // Use the children's profile rather than the parent's profile, if it exists.
  1304. mTl->setControlProfile( mProfile->getChildrenProfile() ? mProfile->getChildrenProfile() : mProfile );
  1305. mTl->setField("noDuplicates", "false");
  1306. mSc = new GuiScrollCtrl;
  1307. AssertFatal( mSc, "Failed to create the GuiScrollCtrl for the PopUpMenu" );
  1308. GuiControlProfile *prof;
  1309. if ( Sim::findObject( "GuiScrollProfile", prof ) )
  1310. {
  1311. mSc->setControlProfile( prof );
  1312. }
  1313. else
  1314. {
  1315. // Use the children's profile rather than the parent's profile, if it exists.
  1316. mSc->setControlProfile( mProfile->getChildrenProfile() ? mProfile->getChildrenProfile() : mProfile );
  1317. }
  1318. mSc->setField( "hScrollBar", "AlwaysOff" );
  1319. mSc->setField( "vScrollBar", "dynamic" );
  1320. //if(mRenderScrollInNA) // Force the scroll control to render using fillColorNA rather than fillColor
  1321. // mSc->mUseNABackground = true;
  1322. mBackground = new GuiPopUpBackgroundCtrlEx( this, mTl );
  1323. AssertFatal( mBackground, "Failed to create the GuiBackgroundCtrlEx for the PopUpMenu" );
  1324. }
  1325. //------------------------------------------------------------------------------
  1326. void GuiPopUpMenuCtrlEx::repositionPopup()
  1327. {
  1328. if ( !mInAction || !mSc || !mTl )
  1329. return;
  1330. // I'm not concerned with this right now...
  1331. }
  1332. //------------------------------------------------------------------------------
  1333. void GuiPopUpMenuCtrlEx::reverseTextList()
  1334. {
  1335. mTl->clear();
  1336. for ( S32 i = mEntries.size()-1; i >= 0; --i )
  1337. mTl->addEntry( mEntries[i].id, mEntries[i].buf );
  1338. // Don't lose the selected cell:
  1339. if ( mSelIndex >= 0 )
  1340. mTl->setSelectedCell( Point2I( 0, mEntries.size() - mSelIndex - 1 ) );
  1341. mRevNum = mEntries.size() - 1;
  1342. }
  1343. //------------------------------------------------------------------------------
  1344. bool GuiPopUpMenuCtrlEx::getFontColor( ColorI &fontColor, S32 id, bool selected, bool mouseOver )
  1345. {
  1346. U32 i;
  1347. Entry* entry = NULL;
  1348. for ( i = 0; i < mEntries.size(); i++ )
  1349. {
  1350. if ( mEntries[i].id == id )
  1351. {
  1352. entry = &mEntries[i];
  1353. break;
  1354. }
  1355. }
  1356. if ( !entry )
  1357. return( false );
  1358. if ( entry->scheme != 0 )
  1359. {
  1360. // Find the entry's color scheme:
  1361. for ( i = 0; i < mSchemes.size(); i++ )
  1362. {
  1363. if ( mSchemes[i].id == entry->scheme )
  1364. {
  1365. fontColor = selected ? mSchemes[i].fontColorSEL : mouseOver ? mSchemes[i].fontColorHL : mSchemes[i].fontColor;
  1366. return( true );
  1367. }
  1368. }
  1369. }
  1370. if(id == -1)
  1371. fontColor = mProfile->mFontColorHL;
  1372. else
  1373. // Default color scheme...
  1374. fontColor = selected ? mProfile->mFontColorSEL : mouseOver ? mProfile->mFontColorHL : mProfile->mFontColorNA; // Modified the final color choice from mProfile->mFontColor to mProfile->mFontColorNA
  1375. return( true );
  1376. }
  1377. //------------------------------------------------------------------------------
  1378. // Added
  1379. bool GuiPopUpMenuCtrlEx::getColoredBox( ColorI &fontColor, S32 id )
  1380. {
  1381. U32 i;
  1382. Entry* entry = NULL;
  1383. for ( i = 0; i < mEntries.size(); i++ )
  1384. {
  1385. if ( mEntries[i].id == id )
  1386. {
  1387. entry = &mEntries[i];
  1388. break;
  1389. }
  1390. }
  1391. if ( !entry )
  1392. return false;
  1393. if ( entry->usesColorBox == false )
  1394. return false;
  1395. fontColor = entry->colorbox;
  1396. return true;
  1397. }
  1398. //------------------------------------------------------------------------------
  1399. void GuiPopUpMenuCtrlEx::onMouseDown(const GuiEvent &event)
  1400. {
  1401. TORQUE_UNUSED(event);
  1402. onAction();
  1403. }
  1404. //------------------------------------------------------------------------------
  1405. void GuiPopUpMenuCtrlEx::onMouseUp(const GuiEvent &event)
  1406. {
  1407. TORQUE_UNUSED(event);
  1408. }
  1409. //------------------------------------------------------------------------------
  1410. // Added
  1411. void GuiPopUpMenuCtrlEx::onMouseEnter(const GuiEvent &event)
  1412. {
  1413. mMouseOver = true;
  1414. }
  1415. //------------------------------------------------------------------------------
  1416. // Added
  1417. void GuiPopUpMenuCtrlEx::onMouseLeave(const GuiEvent &)
  1418. {
  1419. mMouseOver = false;
  1420. }
  1421. //------------------------------------------------------------------------------
  1422. void GuiPopUpMenuCtrlEx::setupAutoScroll(const GuiEvent &event)
  1423. {
  1424. GuiControl *parent = getParent();
  1425. if ( !parent )
  1426. return;
  1427. Point2I mousePt = mSc->globalToLocalCoord( event.mousePoint );
  1428. mEventSave = event;
  1429. if ( mLastYvalue != mousePt.y )
  1430. {
  1431. mScrollDir = GuiScrollCtrl::None;
  1432. if ( mousePt.y > mSc->getHeight() || mousePt.y < 0 )
  1433. {
  1434. S32 topOrBottom = ( mousePt.y > mSc->getHeight() ) ? 1 : 0;
  1435. mSc->scrollTo( 0, topOrBottom );
  1436. return;
  1437. }
  1438. F32 percent = (F32)mousePt.y / (F32)mSc->getHeight();
  1439. if ( percent > 0.7f && mousePt.y > mLastYvalue )
  1440. {
  1441. mIncValue = percent - 0.5f;
  1442. mScrollDir = GuiScrollCtrl::DownArrow;
  1443. }
  1444. else if ( percent < 0.3f && mousePt.y < mLastYvalue )
  1445. {
  1446. mIncValue = 0.5f - percent;
  1447. mScrollDir = GuiScrollCtrl::UpArrow;
  1448. }
  1449. mLastYvalue = mousePt.y;
  1450. }
  1451. }
  1452. //------------------------------------------------------------------------------
  1453. void GuiPopUpMenuCtrlEx::autoScroll()
  1454. {
  1455. mScrollCount += mIncValue;
  1456. while ( mScrollCount > 1 )
  1457. {
  1458. mSc->autoScroll( mScrollDir );
  1459. mScrollCount -= 1;
  1460. }
  1461. mTl->onMouseMove( mEventSave );
  1462. }
  1463. //------------------------------------------------------------------------------
  1464. void GuiPopUpMenuCtrlEx::replaceText(S32 boolVal)
  1465. {
  1466. mReplaceText = boolVal;
  1467. }