guiListBoxCtrl.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  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. if( !mProfile )
  589. return;
  590. for ( S32 i = 0; i < mItems.size(); i++)
  591. {
  592. S32 colorBoxSize = 0;
  593. ColorI boxColor = ColorI(0, 0, 0);
  594. // Only render visible items
  595. if ((i + 1) * mItemSize.y + offset.y < updateRect.point.y)
  596. continue;
  597. // Break our once we're no longer in visible item range
  598. if( i * mItemSize.y + offset.y >= updateRect.point.y + updateRect.extent.y)
  599. break;
  600. // Render color box if needed
  601. if(mItems[i]->hasColor)
  602. {
  603. // Set the size of the color box to be drawn next to the item text
  604. colorBoxSize = 3;
  605. boxColor = ColorI(mItems[i]->color);
  606. // Draw the box first
  607. ColorI black = ColorI(0, 0, 0);
  608. drawBox( Point2I(offset.x + mProfile->mTextOffset.x + colorBoxSize, offset.y + ( i * mItemSize.y ) + 8), colorBoxSize, black, boxColor );
  609. }
  610. RectI itemRect = RectI( offset.x + mProfile->mTextOffset.x + (colorBoxSize * 2), offset.y + ( i * mItemSize.y ), mItemSize.x, mItemSize.y );
  611. // Render our item
  612. onRenderItem( itemRect, mItems[i] );
  613. }
  614. }
  615. void GuiListBoxCtrl::onRenderItem( RectI itemRect, LBItem *item )
  616. {
  617. if( item->isSelected )
  618. dglDrawRectFill( itemRect, mProfile->mFillColor );
  619. dglSetBitmapModulation(mProfile->mFontColor);
  620. renderText(itemRect.point + Point2I( 2, 0 ), itemRect.extent, item->itemText, mProfile);
  621. }
  622. void GuiListBoxCtrl::drawBox(const Point2I &box, S32 size, ColorI &outlineColor, ColorI &boxColor)
  623. {
  624. RectI r(box.x - size, box.y - size, 2 * size + 1, 2 * size + 1);
  625. r.inset(1, 1);
  626. dglDrawRectFill(r, boxColor);
  627. r.inset(-1, -1);
  628. dglDrawRect(r, outlineColor);
  629. }
  630. //////////////////////////////////////////////////////////////////////////
  631. // Mouse Events
  632. //////////////////////////////////////////////////////////////////////////
  633. void GuiListBoxCtrl::onTouchDragged(const GuiEvent &event)
  634. {
  635. Parent::onTouchDragged(event);
  636. if(isMethod("onTouchDragged"))
  637. Con::executef(this, 1, "onTouchDragged");
  638. }
  639. void GuiListBoxCtrl::onTouchDown( const GuiEvent &event )
  640. {
  641. Point2I localPoint = globalToLocalCoord(event.mousePoint);
  642. S32 itemHit = ( localPoint.y < 0 ) ? -1 : (S32)mFloor( (F32)localPoint.y / (F32)mItemSize.y );
  643. if ( itemHit >= mItems.size() || itemHit == -1 )
  644. return;
  645. LBItem *hitItem = mItems[ itemHit ];
  646. if ( hitItem == NULL )
  647. return;
  648. // If we're not a multiple selection listbox, we simply select/unselect an item
  649. if( !mMultipleSelections )
  650. {
  651. // No current selection? Just select the cell and move on
  652. S32 selItem = getSelectedItem();
  653. if ( selItem != itemHit && selItem != -1 )
  654. clearSelection();
  655. // Set the current selection
  656. setCurSel( itemHit );
  657. if( itemHit == selItem && event.mouseClickCount == 2 && isMethod("onDoubleClick") )
  658. Con::executef( this, 2, "onDoubleClick" );
  659. // Store the clicked item
  660. mLastClickItem = hitItem;
  661. // Evaluate the console command if we clicked the same item twice
  662. if( selItem == itemHit && event.mouseClickCount > 1 && mAltConsoleCommand[0] )
  663. Con::evaluate( mAltConsoleCommand, false );
  664. return;
  665. }
  666. // Deal with multiple selections
  667. if( event.modifier & SI_CTRL)
  668. {
  669. // Ctrl-Click toggles selection
  670. if( hitItem->isSelected )
  671. {
  672. removeSelection( hitItem, itemHit );
  673. // We return here when we deselect an item because we don't store last clicked when we deselect
  674. return;
  675. }
  676. else
  677. addSelection( hitItem, itemHit );
  678. }
  679. else if( event.modifier & SI_SHIFT )
  680. {
  681. if( !mLastClickItem )
  682. addSelection( hitItem, itemHit );
  683. else
  684. setCurSelRange( getItemIndex( mLastClickItem ), itemHit );
  685. }
  686. else
  687. {
  688. if( getSelCount() != 0 )
  689. {
  690. S32 selItem = getSelectedItem();
  691. if( selItem != -1 && mItems[selItem] != hitItem )
  692. clearSelection();
  693. }
  694. addSelection( hitItem, itemHit );
  695. }
  696. if( hitItem == mLastClickItem && event.mouseClickCount == 2 && isMethod("onDoubleClick") )
  697. Con::executef( this, 2, "onDoubleClick" );
  698. mLastClickItem = hitItem;
  699. }
  700. U32 GuiListBoxCtrl::getStringElementCount( const char* inString )
  701. {
  702. // Non-whitespace chars.
  703. static const char* set = " \t\n";
  704. // End of string.
  705. if ( *inString == 0 )
  706. return 0;
  707. U32 wordCount = 0;
  708. U8 search = 0;
  709. // Search String.
  710. while( *inString )
  711. {
  712. // Get string element.
  713. search = *inString;
  714. // End of string?
  715. if ( search == 0 )
  716. break;
  717. // Move to next element.
  718. inString++;
  719. // Search for seperators.
  720. for( U32 i = 0; set[i]; i++ )
  721. {
  722. // Found one?
  723. if( search == set[i] )
  724. {
  725. // Yes...
  726. search = 0;
  727. break;
  728. }
  729. }
  730. // Found a seperator?
  731. if ( search == 0 )
  732. continue;
  733. // We've found a non-seperator.
  734. wordCount++;
  735. // Search for end of non-seperator.
  736. while( 1 )
  737. {
  738. // Get string element.
  739. search = *inString;
  740. // End of string?
  741. if ( search == 0 )
  742. break;
  743. // Move to next element.
  744. inString++;
  745. // Search for seperators.
  746. for( U32 i = 0; set[i]; i++ )
  747. {
  748. // Found one?
  749. if( search == set[i] )
  750. {
  751. // Yes...
  752. search = 0;
  753. break;
  754. }
  755. }
  756. // Found Seperator?
  757. if ( search == 0 )
  758. break;
  759. }
  760. // End of string?
  761. if ( *inString == 0 )
  762. {
  763. // Bah!
  764. break;
  765. }
  766. }
  767. // We've finished.
  768. return wordCount;
  769. }
  770. //------------------------------------------------------------------------------
  771. // Get String Element.
  772. //------------------------------------------------------------------------------
  773. const char* GuiListBoxCtrl::getStringElement( const char* inString, const U32 index )
  774. {
  775. // Non-whitespace chars.
  776. static const char* set = " \t\n";
  777. U32 wordCount = 0;
  778. U8 search = 0;
  779. const char* pWordStart = NULL;
  780. // End of string?
  781. if ( *inString != 0 )
  782. {
  783. // No, so search string.
  784. while( *inString )
  785. {
  786. // Get string element.
  787. search = *inString;
  788. // End of string?
  789. if ( search == 0 )
  790. break;
  791. // Move to next element.
  792. inString++;
  793. // Search for seperators.
  794. for( U32 i = 0; set[i]; i++ )
  795. {
  796. // Found one?
  797. if( search == set[i] )
  798. {
  799. // Yes...
  800. search = 0;
  801. break;
  802. }
  803. }
  804. // Found a seperator?
  805. if ( search == 0 )
  806. continue;
  807. // Found are word?
  808. if ( wordCount == index )
  809. {
  810. // Yes, so mark it.
  811. pWordStart = inString-1;
  812. }
  813. // We've found a non-seperator.
  814. wordCount++;
  815. // Search for end of non-seperator.
  816. while( 1 )
  817. {
  818. // Get string element.
  819. search = *inString;
  820. // End of string?
  821. if ( search == 0 )
  822. break;
  823. // Move to next element.
  824. inString++;
  825. // Search for seperators.
  826. for( U32 i = 0; set[i]; i++ )
  827. {
  828. // Found one?
  829. if( search == set[i] )
  830. {
  831. // Yes...
  832. search = 0;
  833. break;
  834. }
  835. }
  836. // Found Seperator?
  837. if ( search == 0 )
  838. break;
  839. }
  840. // Have we found our word?
  841. if ( pWordStart )
  842. {
  843. // Yes, so we've got our word...
  844. // Result Buffer.
  845. static char buffer[4096];
  846. // Calculate word length.
  847. const U32 length = (const U32)(inString - pWordStart - ((*inString)?1:0));
  848. // Copy Word.
  849. dStrncpy( buffer, pWordStart, length);
  850. buffer[length] = '\0';
  851. // Return Word.
  852. return buffer;
  853. }
  854. // End of string?
  855. if ( *inString == 0 )
  856. {
  857. // Bah!
  858. break;
  859. }
  860. }
  861. }
  862. // Sanity!
  863. AssertFatal( false, "SceneObject::getStringElement() - Couldn't find specified string element!" );
  864. // Didn't find it
  865. return " ";
  866. }