guiListBoxCtrl.cc 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "gui/guiListBoxCtrl.h"
  23. IMPLEMENT_CONOBJECT(GuiListBoxCtrl);
  24. GuiListBoxCtrl::GuiListBoxCtrl()
  25. {
  26. mItems.clear();
  27. mSelectedItems.clear();
  28. mMultipleSelections = true;
  29. mFitParentWidth = true;
  30. mItemSize = Point2I(10,20);
  31. mLastClickItem = NULL;
  32. }
  33. GuiListBoxCtrl::~GuiListBoxCtrl()
  34. {
  35. clearItems();
  36. }
  37. void GuiListBoxCtrl::initPersistFields()
  38. {
  39. Parent::initPersistFields();
  40. addField( "AllowMultipleSelections", TypeBool, Offset( mMultipleSelections, GuiListBoxCtrl) );
  41. addField( "FitParentWidth", TypeBool, Offset( mFitParentWidth, GuiListBoxCtrl) );
  42. }
  43. bool GuiListBoxCtrl::onWake()
  44. {
  45. if( !Parent::onWake() )
  46. return false;
  47. updateSize();
  48. return true;
  49. }
  50. //////////////////////////////////////////////////////////////////////////
  51. // Item Accessors
  52. //////////////////////////////////////////////////////////////////////////
  53. ConsoleMethod( GuiListBoxCtrl, setMultipleSelection, void, 3, 3, "(bool setMS) Set the MultipleSelection field.\n"
  54. "@return No return value.\n"
  55. "@note %listBox.setMultipleSelection([true/false])")
  56. {
  57. object->setMultipleSelection( dAtob( argv[2] ) );
  58. }
  59. ConsoleMethod( GuiListBoxCtrl, clearItems, void, 2, 2, "() Clears all the items in the listbox\n"
  60. "@return No return value.")
  61. {
  62. object->clearItems();
  63. }
  64. void GuiListBoxCtrl::clearItems()
  65. {
  66. // Free item list allocated memory
  67. while( mItems.size() )
  68. deleteItem( 0 );
  69. // Free our vector lists
  70. mItems.clear();
  71. mSelectedItems.clear();
  72. }
  73. ConsoleMethod( GuiListBoxCtrl, clearSelection, void, 2, 2, "() Sets all currently selected items to unselected\n"
  74. "@return No return value.")
  75. {
  76. object->clearSelection();
  77. }
  78. void GuiListBoxCtrl::clearSelection()
  79. {
  80. if( !mSelectedItems.size() )
  81. return;
  82. VectorPtr<LBItem*>::iterator i = mSelectedItems.begin();
  83. for( ; i != mSelectedItems.end(); i++ )
  84. (*i)->isSelected = false;
  85. mSelectedItems.clear();
  86. }
  87. ConsoleMethod( GuiListBoxCtrl, setSelected, void, 3, 4, "(S32 index, bool setting) Sets the item at the index specified to selected or not"
  88. "@param index The index of the item you wish to modify.\n"
  89. "@param setting A boolean value. True sets it as selected; false, not selected.\n"
  90. "@return No return value.")
  91. {
  92. bool value = true;
  93. if( argc == 4 )
  94. value = dAtob( argv[3] );
  95. if( value == true )
  96. object->addSelection( dAtoi( argv[2] ) );
  97. else
  98. object->removeSelection( dAtoi( argv[2] ) );
  99. }
  100. void GuiListBoxCtrl::removeSelection( S32 index )
  101. {
  102. // Range Check
  103. if( index >= mItems.size() || index < 0 )
  104. {
  105. Con::warnf("GuiListBoxCtrl::removeSelection - index out of range!" );
  106. return;
  107. }
  108. removeSelection( mItems[index], index );
  109. }
  110. void GuiListBoxCtrl::removeSelection( LBItem *item, S32 index )
  111. {
  112. if( !mSelectedItems.size() )
  113. return;
  114. if( !item )
  115. return;
  116. for( S32 i = 0 ; i < mSelectedItems.size(); i++ )
  117. {
  118. if( mSelectedItems[i] == item )
  119. {
  120. mSelectedItems.erase( &mSelectedItems[i] );
  121. item->isSelected = false;
  122. Con::executef(this, 3, "onUnSelect", Con::getIntArg( index ), item->itemText);
  123. return;
  124. }
  125. }
  126. }
  127. void GuiListBoxCtrl::addSelection( S32 index )
  128. {
  129. // Range Check
  130. if( index >= mItems.size() || index < 0 )
  131. {
  132. Con::warnf("GuiListBoxCtrl::addSelection- index out of range!" );
  133. return;
  134. }
  135. addSelection( mItems[index], index );
  136. }
  137. void GuiListBoxCtrl::addSelection( LBItem *item, S32 index )
  138. {
  139. if( !mMultipleSelections )
  140. {
  141. if( !mSelectedItems.empty() )
  142. {
  143. LBItem* selItem = mSelectedItems.front();
  144. if( selItem != item )
  145. clearSelection();
  146. else
  147. return;
  148. }
  149. }
  150. else
  151. {
  152. if( !mSelectedItems.empty() )
  153. {
  154. for( S32 i = 0; i < mSelectedItems.size(); i++ )
  155. {
  156. if( mSelectedItems[ i ] == item )
  157. return;
  158. }
  159. }
  160. }
  161. item->isSelected = true;
  162. mSelectedItems.push_front( item );
  163. Con::executef(this, 3, "onSelect", Con::getIntArg( index ), item->itemText);
  164. }
  165. S32 GuiListBoxCtrl::getItemIndex( LBItem *item )
  166. {
  167. if( mItems.empty() )
  168. return -1;
  169. // Lookup the index of an item in our list, by the pointer to the item
  170. for( S32 i = 0; i < mItems.size(); i++ )
  171. if( mItems[i] == item )
  172. return i;
  173. return -1;
  174. }
  175. ConsoleMethod( GuiListBoxCtrl, getItemCount, S32, 2, 2, "()\n @return Returns the number of items in the list")
  176. {
  177. return object->getItemCount();
  178. }
  179. S32 GuiListBoxCtrl::getItemCount()
  180. {
  181. return mItems.size();
  182. }
  183. ConsoleMethod( GuiListBoxCtrl, getSelCount, S32, 2, 2, "()\n @return Returns the number of items currently selected")
  184. {
  185. return object->getSelCount();
  186. }
  187. S32 GuiListBoxCtrl::getSelCount()
  188. {
  189. return mSelectedItems.size();
  190. }
  191. ConsoleMethod( GuiListBoxCtrl, getSelectedItem, S32, 2, 2, "()\n @return Returns the selected items index or -1 if none. "
  192. "If multiple selections exist it returns the first selected item" )
  193. {
  194. return object->getSelectedItem();
  195. }
  196. S32 GuiListBoxCtrl::getSelectedItem()
  197. {
  198. if( mSelectedItems.empty() || mItems.empty() )
  199. return -1;
  200. for( S32 i = 0 ; i < mItems.size(); i++ )
  201. if( mItems[i]->isSelected )
  202. return i;
  203. return -1;
  204. }
  205. ConsoleMethod( GuiListBoxCtrl, getSelectedItems, const char*, 2, 2, "()\n @return Returns a space delimited list "
  206. "of the selected items indexes in the list")
  207. {
  208. S32 selCount = object->getSelCount();
  209. if( selCount == -1 || selCount == 0 )
  210. return StringTable->lookup("-1");
  211. else if( selCount == 1 )
  212. return Con::getIntArg(object->getSelectedItem());
  213. Vector<S32> selItems;
  214. object->getSelectedItems( selItems );
  215. if( selItems.empty() )
  216. return StringTable->lookup("-1");
  217. UTF8 *retBuffer = Con::getReturnBuffer( selItems.size() * 4 );
  218. dMemset( retBuffer, 0, selItems.size() * 4 );
  219. Vector<S32>::iterator i = selItems.begin();
  220. for( ; i != selItems.end(); i++ )
  221. {
  222. UTF8 retFormat[12];
  223. dSprintf( retFormat, 12, "%d ", (*i) );
  224. dStrcat( retBuffer, retFormat );
  225. }
  226. return retBuffer;
  227. }
  228. void GuiListBoxCtrl::getSelectedItems( Vector<S32> &Items )
  229. {
  230. // Clear our return vector
  231. Items.clear();
  232. // If there are no selected items, return an empty vector
  233. if( mSelectedItems.empty() )
  234. return;
  235. for( S32 i = 0; i < mItems.size(); i++ )
  236. if( mItems[i]->isSelected )
  237. Items.push_back( i );
  238. }
  239. ConsoleMethod(GuiListBoxCtrl, findItemText, S32, 3, 4, " (string itemText, bool caseSensitive) Find the item with the given text.\n"
  240. "@param itemText The text to search for.\n"
  241. "@param caseSensitive Sets whether or not to ignore text in the search.\n"
  242. "@return Returns index of item with matching text or -1 if none"
  243. "@note %listBox.findItemText( myItemText, [?caseSensitive - false] )")
  244. {
  245. bool bCaseSensitive = false;
  246. if( argc == 4 )
  247. bCaseSensitive = dAtob( argv[3] );
  248. return object->findItemText( argv[2], bCaseSensitive );
  249. }
  250. S32 GuiListBoxCtrl::findItemText( StringTableEntry text, bool caseSensitive )
  251. {
  252. // Check Proper Arguments
  253. if( !text || !text[0] || text == StringTable->EmptyString )
  254. {
  255. Con::warnf("GuiListBoxCtrl::findItemText - No Text Specified!");
  256. return -1;
  257. }
  258. // Check Items Exist.
  259. if( mItems.empty() )
  260. return -1;
  261. // Lookup the index of an item in our list, by the pointer to the item
  262. for( S32 i = 0; i < mItems.size(); i++ )
  263. {
  264. // Case Sensitive Compare?
  265. if( caseSensitive && ( dStrcmp( mItems[i]->itemText, text ) == 0 ) )
  266. return i;
  267. else if (!caseSensitive && ( dStricmp( mItems[i]->itemText, text ) == 0 ))
  268. return i;
  269. }
  270. // Not Found!
  271. return -1;
  272. }
  273. ConsoleMethod(GuiListBoxCtrl, setCurSel, void, 3, 3, "(index) Sets the currently selected item at the specified index\n"
  274. "@return No return value.")
  275. {
  276. object->setCurSel( dAtoi( argv[2] ) );
  277. }
  278. void GuiListBoxCtrl::setCurSel( S32 index )
  279. {
  280. // Range Check
  281. if( index >= mItems.size() )
  282. {
  283. Con::warnf("GuiListBoxCtrl::setCurSel - index out of range!" );
  284. return;
  285. }
  286. // If index -1 is specified, we clear the selection
  287. if( index == -1 )
  288. {
  289. mSelectedItems.clear();
  290. return;
  291. }
  292. // Add the selection
  293. addSelection( mItems[ index ], index );
  294. }
  295. ConsoleMethod( GuiListBoxCtrl, setCurSelRange, void, 3, 4, "(start,[stop]) Sets the current selection range from"
  296. "index start to stop.\n"
  297. "@param start The start of the selection range.\n"
  298. "@param stop The stopping point of the selection range. If no stop is specified it sets from start index to the end of the list\n"
  299. "@return No return value.")
  300. {
  301. if( argc == 4 )
  302. object->setCurSelRange( dAtoi(argv[2]) , dAtoi( argv[3] ) );
  303. else
  304. object->setCurSelRange( dAtoi(argv[2]), 999999 );
  305. }
  306. void GuiListBoxCtrl::setCurSelRange( S32 start, S32 stop )
  307. {
  308. // Verify Selection Range
  309. if( start < 0 )
  310. start = 0;
  311. else if( start > mItems.size() )
  312. start = mItems.size();
  313. if( stop < 0 )
  314. stop = 0;
  315. else if( stop > mItems.size() )
  316. stop = mItems.size();
  317. S32 iterStart = ( start < stop ) ? start : stop;
  318. S32 iterStop = ( start < stop ) ? stop : start;
  319. for( ; iterStart <= iterStop; iterStart++ )
  320. addSelection( mItems[iterStart], iterStart );
  321. }
  322. ConsoleMethod( GuiListBoxCtrl, addItem, void, 3, 4, "(text, [color]) Adds an item to the end of the list with an optional color\n"
  323. "@param text The object text.\n"
  324. "@param color Optional color setting.\n"
  325. "@return No return value.")
  326. {
  327. if(argc == 3)
  328. {
  329. object->addItem( argv[2] );
  330. } else if(argc == 4)
  331. {
  332. U32 elementCount = GuiListBoxCtrl::getStringElementCount(argv[3]);
  333. if(elementCount == 3)
  334. {
  335. F32 red, green, blue;
  336. red = dAtof(GuiListBoxCtrl::getStringElement( argv[3], 0 ));
  337. green = dAtof(GuiListBoxCtrl::getStringElement( argv[3], 1 ));
  338. blue = dAtof(GuiListBoxCtrl::getStringElement( argv[3], 2 ));
  339. object->addItemWithColor( argv[2], ColorF(red, green, blue) );
  340. }
  341. else
  342. {
  343. Con::warnf("GuiListBoxCtrl::addItem() - Invalid number of parameters for the color!");
  344. }
  345. } else
  346. {
  347. Con::warnf("GuiListBoxCtrl::addItem() - Invalid number of parameters!");
  348. }
  349. }
  350. S32 GuiListBoxCtrl::addItem( StringTableEntry text, void *itemData )
  351. {
  352. // This just calls insert item at the end of the list
  353. return insertItem( mItems.size(), text, itemData );
  354. }
  355. S32 GuiListBoxCtrl::addItemWithColor( StringTableEntry text, ColorF color, void *itemData )
  356. {
  357. // This just calls insert item at the end of the list
  358. return insertItemWithColor( mItems.size(), text, color, itemData );
  359. }
  360. ConsoleMethod(GuiListBoxCtrl, setItemColor, void, 4, 4, "(index, color) Sets the color of the item at given index.\n"
  361. "@param index The index of the item you wish to modify.\n"
  362. "@param color The color you wish to set the object to.\n"
  363. "@return No return value.")
  364. {
  365. U32 elementCount = GuiListBoxCtrl::getStringElementCount(argv[3]);
  366. if(elementCount == 3)
  367. {
  368. F32 red = dAtof(GuiListBoxCtrl::getStringElement( argv[3], 0 ));
  369. F32 green = dAtof(GuiListBoxCtrl::getStringElement( argv[3], 1 ));
  370. F32 blue = dAtof(GuiListBoxCtrl::getStringElement( argv[3], 2 ));
  371. object->setItemColor( dAtoi(argv[2]), ColorF(red, green, blue) );
  372. }
  373. else
  374. Con::warnf("GuiListBoxCtrl::addItem() - Invalid number of parameters for the color!");
  375. }
  376. void GuiListBoxCtrl::setItemColor( S32 index, ColorF color )
  377. {
  378. if ((index >= mItems.size()) || index < 0)
  379. {
  380. Con::warnf("GuiListBoxCtrl::setItemColor - invalid index");
  381. return;
  382. }
  383. LBItem* item = mItems[index];
  384. item->hasColor = true;
  385. item->color = color;
  386. }
  387. ConsoleMethod(GuiListBoxCtrl, clearItemColor, void, 3, 3, "(index) Clears the color of the item at index.\n"
  388. "@param index The index of the item to modify.\n"
  389. "@return No return value.")
  390. {
  391. object->clearItemColor(dAtoi(argv[2]));
  392. }
  393. void GuiListBoxCtrl::clearItemColor( S32 index )
  394. {
  395. if ((index >= mItems.size()) || index < 0)
  396. {
  397. Con::warnf("GuiListBoxCtrl::setItemColor - invalid index");
  398. return;
  399. }
  400. LBItem* item = mItems[index];
  401. item->hasColor = false;
  402. }
  403. ConsoleMethod( GuiListBoxCtrl, insertItem, void, 4, 4, "( text, index ) Inserts an item into the list at the specified index.\n"
  404. "@param text The desired object text.\n"
  405. "@param index The index to set the object at.\n"
  406. "@return Returns the index assigned or -1 on error")
  407. {
  408. object->insertItem( dAtoi( argv[3] ), argv[2] );
  409. }
  410. S32 GuiListBoxCtrl::insertItem( S32 index, StringTableEntry text, void *itemData )
  411. {
  412. // If the index is greater than our list size, insert it at the end
  413. if( index >= mItems.size() )
  414. index = mItems.size();
  415. // Sanity checking
  416. if( !text )
  417. {
  418. Con::warnf("GuiListBoxCtrl::insertItem - cannot add NULL string" );
  419. return -1;
  420. }
  421. LBItem *newItem = new LBItem;
  422. if( !newItem )
  423. {
  424. Con::warnf("GuiListBoxCtrl::insertItem - error allocating item memory!" );
  425. return -1;
  426. }
  427. // Assign item data
  428. newItem->itemText = StringTable->insert(text);
  429. newItem->itemData = itemData;
  430. newItem->isSelected = false;
  431. newItem->hasColor = false;
  432. // Add to list
  433. mItems.insert(index);
  434. mItems[index] = newItem;
  435. // Resize our list to fit our items
  436. updateSize();
  437. // Return our index in list (last)
  438. return index;
  439. }
  440. S32 GuiListBoxCtrl::insertItemWithColor( S32 index, StringTableEntry text, ColorF color, void *itemData )
  441. {
  442. // If the index is greater than our list size, insert it at the end
  443. if( index >= mItems.size() )
  444. index = mItems.size();
  445. // Sanity checking
  446. if( !text )
  447. {
  448. Con::warnf("GuiListBoxCtrl::insertItem - cannot add NULL string" );
  449. return -1;
  450. }
  451. if( color == ColorF(-1, -1, -1) )
  452. {
  453. Con::warnf("GuiListBoxCtrl::insertItem - cannot add NULL color" );
  454. return -1;
  455. }
  456. LBItem *newItem = new LBItem;
  457. if( !newItem )
  458. {
  459. Con::warnf("GuiListBoxCtrl::insertItem - error allocating item memory!" );
  460. return -1;
  461. }
  462. // Assign item data
  463. newItem->itemText = StringTable->insert(text);
  464. newItem->itemData = itemData;
  465. newItem->isSelected = false;
  466. newItem->hasColor = true;
  467. newItem->color = color;
  468. // Add to list
  469. mItems.insert(index);
  470. mItems[index] = newItem;
  471. // Resize our list to fit our items
  472. updateSize();
  473. // Return our index in list (last)
  474. return index;
  475. }
  476. ConsoleMethod ( GuiListBoxCtrl, deleteItem, void, 3, 3, "(index) Deletes the item at the given index.\n"
  477. "@param index The index of the item to delete.\n"
  478. "@return No return value.")
  479. {
  480. object->deleteItem( dAtoi( argv[2] ) );
  481. }
  482. void GuiListBoxCtrl::deleteItem( S32 index )
  483. {
  484. // Range Check
  485. if( index >= mItems.size() || index < 0 )
  486. {
  487. Con::warnf("GuiListBoxCtrl::deleteItem - index out of range!" );
  488. return;
  489. }
  490. // Grab our item
  491. LBItem* item = mItems[ index ];
  492. if( !item )
  493. {
  494. Con::warnf("GuiListBoxCtrl::deleteItem - Bad Item Data!" );
  495. return;
  496. }
  497. // Remove it from the selected list.
  498. if( item->isSelected )
  499. {
  500. for( VectorPtr<LBItem*>::iterator i = mSelectedItems.begin(); i != mSelectedItems.end(); i++ )
  501. {
  502. if( item == *i )
  503. {
  504. mSelectedItems.erase_fast( i );
  505. break;
  506. }
  507. }
  508. }
  509. // Remove it from the list
  510. mItems.erase( &mItems[ index ] );
  511. // Free the memory associated with it
  512. delete item;
  513. }
  514. ConsoleMethod( GuiListBoxCtrl, getItemText, const char*, 3, 3, "(index) \n @return Returns the text of the item at the specified index")
  515. {
  516. return object->getItemText( dAtoi( argv[2] ) );
  517. }
  518. StringTableEntry GuiListBoxCtrl::getItemText( S32 index )
  519. {
  520. // Range Checking
  521. if( index > mItems.size() || index < 0 )
  522. {
  523. Con::warnf( "GuiListBoxCtrl::getItemText - index out of range!" );
  524. return StringTable->EmptyString;
  525. }
  526. return mItems[ index ]->itemText;
  527. }
  528. ConsoleMethod( GuiListBoxCtrl, setItemText, void, 4, 4, "(index, newtext) Sets the item's text at the specified index\n"
  529. "@param index The index of the item to modify.\n"
  530. "@param newtext The new text to set to the object.\n"
  531. "@return No return value.")
  532. {
  533. object->setItemText( dAtoi( argv[2] ), argv[3] );
  534. }
  535. void GuiListBoxCtrl::setItemText( S32 index, StringTableEntry text )
  536. {
  537. // Sanity Checking
  538. if( !text )
  539. {
  540. Con::warnf("GuiListBoxCtrl::setItemText - Invalid Text Specified!" );
  541. return;
  542. }
  543. // Range Checking
  544. if( index > mItems.size() || index < 0 )
  545. {
  546. Con::warnf( "GuiListBoxCtrl::getItemText - index out of range!" );
  547. return;
  548. }
  549. mItems[ index ]->itemText = StringTable->insert( text );
  550. }
  551. //////////////////////////////////////////////////////////////////////////
  552. // Sizing Functions
  553. //////////////////////////////////////////////////////////////////////////
  554. void GuiListBoxCtrl::updateSize()
  555. {
  556. if( !mProfile )
  557. return;
  558. GFont *font = mProfile->mFont;
  559. GuiScrollCtrl* parent = dynamic_cast<GuiScrollCtrl *>(getParent());
  560. if ( mFitParentWidth && parent )
  561. mItemSize.x = 100;//parent->getContentExtent().x;
  562. else
  563. {
  564. // Find the maximum width cell:
  565. S32 maxWidth = 1;
  566. for ( U32 i = 0; i < (U32)mItems.size(); i++ )
  567. {
  568. S32 width = font->getStrWidth( mItems[i]->itemText );
  569. if( width > maxWidth )
  570. maxWidth = width;
  571. }
  572. mItemSize.x = maxWidth + 6;
  573. }
  574. mItemSize.y = font->getHeight() + 2;
  575. Point2I newExtent( mItemSize.x, mItemSize.y * mItems.size() );
  576. resize( mBounds.point, newExtent );
  577. }
  578. void GuiListBoxCtrl::parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent)
  579. {
  580. Parent::parentResized( oldParentExtent, newParentExtent );
  581. updateSize();
  582. }
  583. //////////////////////////////////////////////////////////////////////////
  584. // Overrides
  585. //////////////////////////////////////////////////////////////////////////
  586. void GuiListBoxCtrl::onRender( Point2I offset, const RectI &updateRect )
  587. {
  588. RectI clipRect(updateRect.point, updateRect.extent);
  589. if( !mProfile )
  590. return;
  591. // Save our original clip rect
  592. RectI oldClipRect = clipRect;
  593. for ( S32 i = 0; i < mItems.size(); i++)
  594. {
  595. S32 colorBoxSize = 0;
  596. ColorI boxColor = ColorI(0, 0, 0);
  597. // Only render visible items
  598. if ((i + 1) * mItemSize.y + offset.y < updateRect.point.y)
  599. continue;
  600. // Break our once we're no longer in visible item range
  601. if( i * mItemSize.y + offset.y >= updateRect.point.y + updateRect.extent.y)
  602. break;
  603. // Render color box if needed
  604. if(mItems[i]->hasColor)
  605. {
  606. // Set the size of the color box to be drawn next to the item text
  607. colorBoxSize = 3;
  608. boxColor = ColorI(mItems[i]->color);
  609. // Draw the box first
  610. ColorI black = ColorI(0, 0, 0);
  611. drawBox( Point2I(offset.x + mProfile->mTextOffset.x + colorBoxSize, offset.y + ( i * mItemSize.y ) + 8), colorBoxSize, black, boxColor );
  612. }
  613. RectI itemRect = RectI( offset.x + mProfile->mTextOffset.x + (colorBoxSize * 2), offset.y + ( i * mItemSize.y ), mItemSize.x, mItemSize.y );
  614. // Render our item
  615. onRenderItem( itemRect, mItems[i] );
  616. }
  617. dglSetClipRect( oldClipRect );
  618. }
  619. void GuiListBoxCtrl::onRenderItem( RectI itemRect, LBItem *item )
  620. {
  621. if( item->isSelected )
  622. dglDrawRectFill( itemRect, mProfile->mFillColor );
  623. dglSetBitmapModulation(mProfile->mFontColor);
  624. renderText(itemRect.point + Point2I( 2, 0 ), itemRect.extent, item->itemText, mProfile);
  625. }
  626. void GuiListBoxCtrl::drawBox(const Point2I &box, S32 size, ColorI &outlineColor, ColorI &boxColor)
  627. {
  628. RectI r(box.x - size, box.y - size, 2 * size + 1, 2 * size + 1);
  629. r.inset(1, 1);
  630. dglDrawRectFill(r, boxColor);
  631. r.inset(-1, -1);
  632. dglDrawRect(r, outlineColor);
  633. }
  634. //////////////////////////////////////////////////////////////////////////
  635. // Mouse Events
  636. //////////////////////////////////////////////////////////////////////////
  637. void GuiListBoxCtrl::onTouchDragged(const GuiEvent &event)
  638. {
  639. Parent::onTouchDragged(event);
  640. if(isMethod("onTouchDragged"))
  641. Con::executef(this, 1, "onTouchDragged");
  642. }
  643. void GuiListBoxCtrl::onTouchDown( const GuiEvent &event )
  644. {
  645. Point2I localPoint = globalToLocalCoord(event.mousePoint);
  646. S32 itemHit = ( localPoint.y < 0 ) ? -1 : (S32)mFloor( (F32)localPoint.y / (F32)mItemSize.y );
  647. if ( itemHit >= mItems.size() || itemHit == -1 )
  648. return;
  649. LBItem *hitItem = mItems[ itemHit ];
  650. if ( hitItem == NULL )
  651. return;
  652. // If we're not a multiple selection listbox, we simply select/unselect an item
  653. if( !mMultipleSelections )
  654. {
  655. // No current selection? Just select the cell and move on
  656. S32 selItem = getSelectedItem();
  657. if ( selItem != itemHit && selItem != -1 )
  658. clearSelection();
  659. // Set the current selection
  660. setCurSel( itemHit );
  661. if( itemHit == selItem && event.mouseClickCount == 2 && isMethod("onDoubleClick") )
  662. Con::executef( this, 2, "onDoubleClick" );
  663. // Store the clicked item
  664. mLastClickItem = hitItem;
  665. // Evaluate the console command if we clicked the same item twice
  666. if( selItem == itemHit && event.mouseClickCount > 1 && mAltConsoleCommand[0] )
  667. Con::evaluate( mAltConsoleCommand, false );
  668. return;
  669. }
  670. // Deal with multiple selections
  671. if( event.modifier & SI_CTRL)
  672. {
  673. // Ctrl-Click toggles selection
  674. if( hitItem->isSelected )
  675. {
  676. removeSelection( hitItem, itemHit );
  677. // We return here when we deselect an item because we don't store last clicked when we deselect
  678. return;
  679. }
  680. else
  681. addSelection( hitItem, itemHit );
  682. }
  683. else if( event.modifier & SI_SHIFT )
  684. {
  685. if( !mLastClickItem )
  686. addSelection( hitItem, itemHit );
  687. else
  688. setCurSelRange( getItemIndex( mLastClickItem ), itemHit );
  689. }
  690. else
  691. {
  692. if( getSelCount() != 0 )
  693. {
  694. S32 selItem = getSelectedItem();
  695. if( selItem != -1 && mItems[selItem] != hitItem )
  696. clearSelection();
  697. }
  698. addSelection( hitItem, itemHit );
  699. }
  700. if( hitItem == mLastClickItem && event.mouseClickCount == 2 && isMethod("onDoubleClick") )
  701. Con::executef( this, 2, "onDoubleClick" );
  702. mLastClickItem = hitItem;
  703. }
  704. U32 GuiListBoxCtrl::getStringElementCount( const char* inString )
  705. {
  706. // Non-whitespace chars.
  707. static const char* set = " \t\n";
  708. // End of string.
  709. if ( *inString == 0 )
  710. return 0;
  711. U32 wordCount = 0;
  712. U8 search = 0;
  713. // Search String.
  714. while( *inString )
  715. {
  716. // Get string element.
  717. search = *inString;
  718. // End of string?
  719. if ( search == 0 )
  720. break;
  721. // Move to next element.
  722. inString++;
  723. // Search for seperators.
  724. for( U32 i = 0; set[i]; i++ )
  725. {
  726. // Found one?
  727. if( search == set[i] )
  728. {
  729. // Yes...
  730. search = 0;
  731. break;
  732. }
  733. }
  734. // Found a seperator?
  735. if ( search == 0 )
  736. continue;
  737. // We've found a non-seperator.
  738. wordCount++;
  739. // Search for end of non-seperator.
  740. while( 1 )
  741. {
  742. // Get string element.
  743. search = *inString;
  744. // End of string?
  745. if ( search == 0 )
  746. break;
  747. // Move to next element.
  748. inString++;
  749. // Search for seperators.
  750. for( U32 i = 0; set[i]; i++ )
  751. {
  752. // Found one?
  753. if( search == set[i] )
  754. {
  755. // Yes...
  756. search = 0;
  757. break;
  758. }
  759. }
  760. // Found Seperator?
  761. if ( search == 0 )
  762. break;
  763. }
  764. // End of string?
  765. if ( *inString == 0 )
  766. {
  767. // Bah!
  768. break;
  769. }
  770. }
  771. // We've finished.
  772. return wordCount;
  773. }
  774. //------------------------------------------------------------------------------
  775. // Get String Element.
  776. //------------------------------------------------------------------------------
  777. const char* GuiListBoxCtrl::getStringElement( const char* inString, const U32 index )
  778. {
  779. // Non-whitespace chars.
  780. static const char* set = " \t\n";
  781. U32 wordCount = 0;
  782. U8 search = 0;
  783. const char* pWordStart = NULL;
  784. // End of string?
  785. if ( *inString != 0 )
  786. {
  787. // No, so search string.
  788. while( *inString )
  789. {
  790. // Get string element.
  791. search = *inString;
  792. // End of string?
  793. if ( search == 0 )
  794. break;
  795. // Move to next element.
  796. inString++;
  797. // Search for seperators.
  798. for( U32 i = 0; set[i]; i++ )
  799. {
  800. // Found one?
  801. if( search == set[i] )
  802. {
  803. // Yes...
  804. search = 0;
  805. break;
  806. }
  807. }
  808. // Found a seperator?
  809. if ( search == 0 )
  810. continue;
  811. // Found are word?
  812. if ( wordCount == index )
  813. {
  814. // Yes, so mark it.
  815. pWordStart = inString-1;
  816. }
  817. // We've found a non-seperator.
  818. wordCount++;
  819. // Search for end of non-seperator.
  820. while( 1 )
  821. {
  822. // Get string element.
  823. search = *inString;
  824. // End of string?
  825. if ( search == 0 )
  826. break;
  827. // Move to next element.
  828. inString++;
  829. // Search for seperators.
  830. for( U32 i = 0; set[i]; i++ )
  831. {
  832. // Found one?
  833. if( search == set[i] )
  834. {
  835. // Yes...
  836. search = 0;
  837. break;
  838. }
  839. }
  840. // Found Seperator?
  841. if ( search == 0 )
  842. break;
  843. }
  844. // Have we found our word?
  845. if ( pWordStart )
  846. {
  847. // Yes, so we've got our word...
  848. // Result Buffer.
  849. static char buffer[4096];
  850. // Calculate word length.
  851. const U32 length = (const U32)(inString - pWordStart - ((*inString)?1:0));
  852. // Copy Word.
  853. dStrncpy( buffer, pWordStart, length);
  854. buffer[length] = '\0';
  855. // Return Word.
  856. return buffer;
  857. }
  858. // End of string?
  859. if ( *inString == 0 )
  860. {
  861. // Bah!
  862. break;
  863. }
  864. }
  865. }
  866. // Sanity!
  867. AssertFatal( false, "SceneObject::getStringElement() - Couldn't find specified string element!" );
  868. // Didn't find it
  869. return " ";
  870. }