llcombobox.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:25k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llcombobox.cpp
  3.  * @brief LLComboBox base class
  4.  *
  5.  * $LicenseInfo:firstyear=2001&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2001-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. // A control that displays the name of the chosen item, which when
  33. // clicked shows a scrolling box of options.
  34. #include "linden_common.h"
  35. // file includes
  36. #include "llcombobox.h"
  37. // common includes
  38. #include "llstring.h"
  39. // newview includes
  40. #include "llbutton.h"
  41. #include "llkeyboard.h"
  42. #include "llscrolllistctrl.h"
  43. #include "llwindow.h"
  44. #include "llfloater.h"
  45. #include "llscrollbar.h"
  46. #include "llscrolllistcell.h"
  47. #include "llscrolllistitem.h"
  48. #include "llcontrol.h"
  49. #include "llfocusmgr.h"
  50. #include "lllineeditor.h"
  51. #include "v2math.h"
  52. #include "lluictrlfactory.h"
  53. #include "lltooltip.h"
  54. // Globals
  55. S32 LLCOMBOBOX_HEIGHT = 0;
  56. S32 LLCOMBOBOX_WIDTH = 0;
  57. S32 MAX_COMBO_WIDTH = 500;
  58. static LLDefaultChildRegistry::Register<LLComboBox> register_combo_box("combo_box");
  59. void LLComboBox::PreferredPositionValues::declareValues()
  60. {
  61. declare("above", ABOVE);
  62. declare("below", BELOW);
  63. }
  64. LLComboBox::ItemParams::ItemParams()
  65. : label("label")
  66. {
  67. }
  68. LLComboBox::Params::Params()
  69. : allow_text_entry("allow_text_entry", false),
  70. allow_new_values("allow_new_values", false),
  71. show_text_as_tentative("show_text_as_tentative", true),
  72. max_chars("max_chars", 20),
  73. list_position("list_position", BELOW),
  74. items("item"),
  75. combo_button("combo_button"),
  76. combo_list("combo_list"),
  77. combo_editor("combo_editor"),
  78. drop_down_button("drop_down_button")
  79. {
  80. addSynonym(items, "combo_item");
  81. }
  82. LLComboBox::LLComboBox(const LLComboBox::Params& p)
  83. : LLUICtrl(p),
  84. mTextEntry(NULL),
  85. mTextEntryTentative(p.show_text_as_tentative),
  86. mHasAutocompletedText(false),
  87. mAllowTextEntry(p.allow_text_entry),
  88. mAllowNewValues(p.allow_new_values),
  89. mMaxChars(p.max_chars),
  90. mPrearrangeCallback(p.prearrange_callback()),
  91. mTextEntryCallback(p.text_entry_callback()),
  92. mListPosition(p.list_position),
  93. mLastSelectedIndex(-1),
  94. mLabel(p.label)
  95. {
  96. // Text label button
  97. LLButton::Params button_params = (mAllowTextEntry ? p.combo_button : p.drop_down_button);
  98. button_params.mouse_down_callback.function(
  99. boost::bind(&LLComboBox::onButtonMouseDown, this));
  100. button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT);
  101. button_params.rect(p.rect);
  102. if(mAllowTextEntry)
  103. {
  104. button_params.pad_right(2);
  105. }
  106. mArrowImage = button_params.image_unselected;
  107. mButton = LLUICtrlFactory::create<LLButton>(button_params);
  108. if(mAllowTextEntry)
  109. {
  110. //redo to compensate for button hack that leaves space for a character
  111. //unless it is a "minimal combobox"(drop down)
  112. mButton->setRightHPad(2);
  113. }
  114. addChild(mButton);
  115. LLScrollListCtrl::Params params = p.combo_list;
  116. params.name("ComboBox");
  117. params.commit_callback.function(boost::bind(&LLComboBox::onItemSelected, this, _2));
  118. params.visible(false);
  119. params.commit_on_keyboard_movement(false);
  120. mList = LLUICtrlFactory::create<LLScrollListCtrl>(params);
  121. addChild(mList);
  122. // Mouse-down on button will transfer mouse focus to the list
  123. // Grab the mouse-up event and make sure the button state is correct
  124. mList->setMouseUpCallback(boost::bind(&LLComboBox::onListMouseUp, this));
  125. for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items().begin();
  126. it != p.items().end();
  127. ++it)
  128. {
  129. LLScrollListItem::Params item_params = *it;
  130. if (it->label.isProvided())
  131. {
  132. item_params.columns.add().value(it->label());
  133. }
  134. mList->addRow(item_params);
  135. }
  136. createLineEditor(p);
  137. setTopLostCallback(boost::bind(&LLComboBox::hideList, this));
  138. }
  139. void LLComboBox::initFromParams(const LLComboBox::Params& p)
  140. {
  141. LLUICtrl::initFromParams(p);
  142. if (!acceptsTextInput() && mLabel.empty())
  143. {
  144. selectFirstItem();
  145. }
  146. }
  147. // virtual
  148. BOOL LLComboBox::postBuild()
  149. {
  150. if (mControlVariable)
  151. {
  152. setValue(mControlVariable->getValue()); // selects the appropriate item
  153. }
  154. return TRUE;
  155. }
  156. LLComboBox::~LLComboBox()
  157. {
  158. // children automatically deleted, including mMenu, mButton
  159. }
  160. void LLComboBox::clear()
  161. if (mTextEntry)
  162. {
  163. mTextEntry->setText(LLStringUtil::null);
  164. }
  165. mButton->setLabelSelected(LLStringUtil::null);
  166. mButton->setLabelUnselected(LLStringUtil::null);
  167. mList->deselectAllItems();
  168. mLastSelectedIndex = -1;
  169. }
  170. void LLComboBox::onCommit()
  171. {
  172. if (mAllowTextEntry && getCurrentIndex() != -1)
  173. {
  174. // we have selected an existing item, blitz the manual text entry with
  175. // the properly capitalized item
  176. mTextEntry->setValue(getSimple());
  177. mTextEntry->setTentative(FALSE);
  178. }
  179. setControlValue(getValue());
  180. LLUICtrl::onCommit();
  181. }
  182. // virtual
  183. BOOL LLComboBox::isDirty() const
  184. {
  185. BOOL grubby = FALSE;
  186. if ( mList )
  187. {
  188. grubby = mList->isDirty();
  189. }
  190. return grubby;
  191. }
  192. // virtual   Clear dirty state
  193. void LLComboBox::resetDirty()
  194. {
  195. if ( mList )
  196. {
  197. mList->resetDirty();
  198. }
  199. }
  200. // add item "name" to menu
  201. LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, BOOL enabled)
  202. {
  203. LLScrollListItem* item = mList->addSimpleElement(name, pos);
  204. item->setEnabled(enabled);
  205. if (!mAllowTextEntry && mLabel.empty())
  206. {
  207. selectFirstItem();
  208. }
  209. return item;
  210. }
  211. // add item "name" with a unique id to menu
  212. LLScrollListItem* LLComboBox::add(const std::string& name, const LLUUID& id, EAddPosition pos, BOOL enabled )
  213. {
  214. LLScrollListItem* item = mList->addSimpleElement(name, pos, id);
  215. item->setEnabled(enabled);
  216. if (!mAllowTextEntry && mLabel.empty())
  217. {
  218. selectFirstItem();
  219. }
  220. return item;
  221. }
  222. // add item "name" with attached userdata
  223. LLScrollListItem* LLComboBox::add(const std::string& name, void* userdata, EAddPosition pos, BOOL enabled )
  224. {
  225. LLScrollListItem* item = mList->addSimpleElement(name, pos);
  226. item->setEnabled(enabled);
  227. item->setUserdata( userdata );
  228. if (!mAllowTextEntry && mLabel.empty())
  229. {
  230. selectFirstItem();
  231. }
  232. return item;
  233. }
  234. // add item "name" with attached generic data
  235. LLScrollListItem* LLComboBox::add(const std::string& name, LLSD value, EAddPosition pos, BOOL enabled )
  236. {
  237. LLScrollListItem* item = mList->addSimpleElement(name, pos, value);
  238. item->setEnabled(enabled);
  239. if (!mAllowTextEntry && mLabel.empty())
  240. {
  241. selectFirstItem();
  242. }
  243. return item;
  244. }
  245. LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos)
  246. {
  247. return mList->addSeparator(pos);
  248. }
  249. void LLComboBox::sortByName(BOOL ascending)
  250. {
  251. mList->sortOnce(0, ascending);
  252. }
  253. // Choose an item with a given name in the menu.
  254. // Returns TRUE if the item was found.
  255. BOOL LLComboBox::setSimple(const LLStringExplicit& name)
  256. {
  257. BOOL found = mList->selectItemByLabel(name, FALSE);
  258. if (found)
  259. {
  260. setLabel(name);
  261. mLastSelectedIndex = mList->getFirstSelectedIndex();
  262. }
  263. return found;
  264. }
  265. // virtual
  266. void LLComboBox::setValue(const LLSD& value)
  267. {
  268. BOOL found = mList->selectByValue(value);
  269. if (found)
  270. {
  271. LLScrollListItem* item = mList->getFirstSelected();
  272. if (item)
  273. {
  274. setLabel( mList->getSelectedItemLabel() );
  275. }
  276. mLastSelectedIndex = mList->getFirstSelectedIndex();
  277. }
  278. }
  279. const std::string LLComboBox::getSimple() const
  280. {
  281. const std::string res = mList->getSelectedItemLabel();
  282. if (res.empty() && mAllowTextEntry)
  283. {
  284. return mTextEntry->getText();
  285. }
  286. else
  287. {
  288. return res;
  289. }
  290. }
  291. const std::string LLComboBox::getSelectedItemLabel(S32 column) const
  292. {
  293. return mList->getSelectedItemLabel(column);
  294. }
  295. // virtual
  296. LLSD LLComboBox::getValue() const
  297. {
  298. LLScrollListItem* item = mList->getFirstSelected();
  299. if( item )
  300. {
  301. return item->getValue();
  302. }
  303. else if (mAllowTextEntry)
  304. {
  305. return mTextEntry->getValue();
  306. }
  307. else
  308. {
  309. return LLSD();
  310. }
  311. }
  312. void LLComboBox::setLabel(const LLStringExplicit& name)
  313. {
  314. if ( mTextEntry )
  315. {
  316. mTextEntry->setText(name);
  317. if (mList->selectItemByLabel(name, FALSE))
  318. {
  319. mTextEntry->setTentative(FALSE);
  320. mLastSelectedIndex = mList->getFirstSelectedIndex();
  321. }
  322. else
  323. {
  324. mTextEntry->setTentative(mTextEntryTentative);
  325. }
  326. }
  327. if (!mAllowTextEntry)
  328. {
  329. mButton->setLabel(name);
  330. }
  331. }
  332. BOOL LLComboBox::remove(const std::string& name)
  333. {
  334. BOOL found = mList->selectItemByLabel(name);
  335. if (found)
  336. {
  337. LLScrollListItem* item = mList->getFirstSelected();
  338. if (item)
  339. {
  340. mList->deleteSingleItem(mList->getItemIndex(item));
  341. }
  342. mLastSelectedIndex = mList->getFirstSelectedIndex();
  343. }
  344. return found;
  345. }
  346. BOOL LLComboBox::remove(S32 index)
  347. {
  348. if (index < mList->getItemCount())
  349. {
  350. mList->deleteSingleItem(index);
  351. setLabel(mList->getSelectedItemLabel());
  352. return TRUE;
  353. }
  354. return FALSE;
  355. }
  356. // Keyboard focus lost.
  357. void LLComboBox::onFocusLost()
  358. {
  359. hideList();
  360. // if valid selection
  361. if (mAllowTextEntry && getCurrentIndex() != -1)
  362. {
  363. mTextEntry->selectAll();
  364. }
  365. LLUICtrl::onFocusLost();
  366. }
  367. void LLComboBox::setButtonVisible(BOOL visible)
  368. {
  369. static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0);
  370. mButton->setVisible(visible);
  371. if (mTextEntry)
  372. {
  373. LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
  374. if (visible)
  375. {
  376. S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
  377. text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
  378. }
  379. //mTextEntry->setRect(text_entry_rect);
  380. mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE);
  381. }
  382. }
  383. BOOL LLComboBox::setCurrentByIndex( S32 index )
  384. {
  385. BOOL found = mList->selectNthItem( index );
  386. if (found)
  387. {
  388. setLabel(mList->getSelectedItemLabel());
  389. mLastSelectedIndex = index;
  390. }
  391. return found;
  392. }
  393. S32 LLComboBox::getCurrentIndex() const
  394. {
  395. LLScrollListItem* item = mList->getFirstSelected();
  396. if( item )
  397. {
  398. return mList->getItemIndex( item );
  399. }
  400. return -1;
  401. }
  402. void LLComboBox::createLineEditor(const LLComboBox::Params& p)
  403. {
  404. static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0);
  405. LLRect rect = getLocalRect();
  406. if (mAllowTextEntry)
  407. {
  408. S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
  409. S32 shadow_size = drop_shadow_button;
  410. mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size,
  411. rect.mTop, rect.mRight, rect.mBottom));
  412. mButton->setTabStop(FALSE);
  413. mButton->setHAlign(LLFontGL::HCENTER);
  414. LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
  415. text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
  416. // clear label on button
  417. std::string cur_label = mButton->getLabelSelected();
  418. LLLineEditor::Params params = p.combo_editor;
  419. params.rect(text_entry_rect);
  420. params.default_text(LLStringUtil::null);
  421. params.max_length_bytes(mMaxChars);
  422. params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2));
  423. params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1));
  424. params.handle_edit_keys_directly(true);
  425. params.commit_on_focus_lost(false);
  426. params.follows.flags(FOLLOWS_ALL);
  427. params.label(mLabel);
  428. mTextEntry = LLUICtrlFactory::create<LLLineEditor> (params);
  429. mTextEntry->setText(cur_label);
  430. mTextEntry->setIgnoreTab(TRUE);
  431. addChild(mTextEntry);
  432. // clear label on button
  433. setLabel(LLStringUtil::null);
  434. mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT);
  435. }
  436. else
  437. {
  438. mButton->setRect(rect);
  439. mButton->setTabStop(TRUE);
  440. mButton->setHAlign(LLFontGL::LEFT);
  441. mButton->setLabel(mLabel.getString());
  442. if (mTextEntry)
  443. {
  444. mTextEntry->setVisible(FALSE);
  445. }
  446. mButton->setFollowsAll();
  447. }
  448. }
  449. void* LLComboBox::getCurrentUserdata()
  450. {
  451. LLScrollListItem* item = mList->getFirstSelected();
  452. if( item )
  453. {
  454. return item->getUserdata();
  455. }
  456. return NULL;
  457. }
  458. void LLComboBox::showList()
  459. {
  460. // Make sure we don't go off top of screen.
  461. LLCoordWindow window_size;
  462. getWindow()->getSize(&window_size);
  463. //HACK: shouldn't have to know about scale here
  464. mList->fitContents( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 );
  465. // Make sure that we can see the whole list
  466. LLRect root_view_local;
  467. LLView* root_view = getRootView();
  468. root_view->localRectToOtherView(root_view->getLocalRect(), &root_view_local, this);
  469. LLRect rect = mList->getRect();
  470. S32 min_width = getRect().getWidth();
  471. S32 max_width = llmax(min_width, MAX_COMBO_WIDTH);
  472. // make sure we have up to date content width metrics
  473. mList->calcColumnWidths();
  474. S32 list_width = llclamp(mList->getMaxContentWidth(), min_width, max_width);
  475. if (mListPosition == BELOW)
  476. {
  477. if (rect.getHeight() <= -root_view_local.mBottom)
  478. {
  479. // Move rect so it hangs off the bottom of this view
  480. rect.setLeftTopAndSize(0, 0, list_width, rect.getHeight() );
  481. }
  482. else
  483. {
  484. // stack on top or bottom, depending on which has more room
  485. if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight())
  486. {
  487. // Move rect so it hangs off the bottom of this view
  488. rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
  489. }
  490. else
  491. {
  492. // move rect so it stacks on top of this view (clipped to size of screen)
  493. rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
  494. }
  495. }
  496. }
  497. else // ABOVE
  498. {
  499. if (rect.getHeight() <= root_view_local.mTop - getRect().getHeight())
  500. {
  501. // move rect so it stacks on top of this view (clipped to size of screen)
  502. rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
  503. }
  504. else
  505. {
  506. // stack on top or bottom, depending on which has more room
  507. if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight())
  508. {
  509. // Move rect so it hangs off the bottom of this view
  510. rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
  511. }
  512. else
  513. {
  514. // move rect so it stacks on top of this view (clipped to size of screen)
  515. rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
  516. }
  517. }
  518. }
  519. mList->setOrigin(rect.mLeft, rect.mBottom);
  520. mList->reshape(rect.getWidth(), rect.getHeight());
  521. mList->translateIntoRect(root_view_local, FALSE);
  522. // Make sure we didn't go off bottom of screen
  523. S32 x, y;
  524. mList->localPointToScreen(0, 0, &x, &y);
  525. if (y < 0)
  526. {
  527. mList->translate(0, -y);
  528. }
  529. // NB: this call will trigger the focuslost callback which will hide the list, so do it first
  530. // before finally showing the list
  531. mList->setFocus(TRUE);
  532. // register ourselves as a "top" control
  533. // effectively putting us into a special draw layer
  534. // and not affecting the bounding rectangle calculation
  535. gFocusMgr.setTopCtrl(this);
  536. // Show the list and push the button down
  537. mButton->setToggleState(TRUE);
  538. mList->setVisible(TRUE);
  539. setUseBoundingRect(TRUE);
  540. }
  541. void LLComboBox::hideList()
  542. {
  543. if (mList->getVisible())
  544. {
  545. // assert selection in list
  546. if(mAllowNewValues)
  547. {
  548. // mLastSelectedIndex = -1 means that we entered a new value, don't select
  549. // any of existing items in this case.
  550. if(mLastSelectedIndex >= 0)
  551. mList->selectNthItem(mLastSelectedIndex);
  552. }
  553. else if(mLastSelectedIndex >= 0)
  554. mList->selectNthItem(mLastSelectedIndex);
  555. mButton->setToggleState(FALSE);
  556. mList->setVisible(FALSE);
  557. mList->mouseOverHighlightNthItem(-1);
  558. setUseBoundingRect(FALSE);
  559. if( gFocusMgr.getTopCtrl() == this )
  560. {
  561. gFocusMgr.setTopCtrl(NULL);
  562. }
  563. }
  564. }
  565. void LLComboBox::onButtonMouseDown()
  566. {
  567. if (!mList->getVisible())
  568. {
  569. // this might change selection, so do it first
  570. prearrangeList();
  571. // highlight the last selected item from the original selection before potentially selecting a new item
  572. // as visual cue to original value of combo box
  573. LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
  574. if (last_selected_item)
  575. {
  576. mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
  577. }
  578. if (mList->getItemCount() != 0)
  579. {
  580. showList();
  581. }
  582. setFocus( TRUE );
  583. // pass mouse capture on to list if button is depressed
  584. if (mButton->hasMouseCapture())
  585. {
  586. gFocusMgr.setMouseCapture(mList);
  587. // But keep the "pressed" look, which buttons normally lose when they
  588. // lose focus
  589. mButton->setForcePressedState(true);
  590. }
  591. }
  592. else
  593. {
  594. hideList();
  595. }
  596. void LLComboBox::onListMouseUp()
  597. {
  598. // In some cases this is the termination of a mouse click that started on
  599. // the button, so clear its pressed state
  600. mButton->setForcePressedState(false);
  601. }
  602. //------------------------------------------------------------------
  603. // static functions
  604. //------------------------------------------------------------------
  605. void LLComboBox::onItemSelected(const LLSD& data)
  606. {
  607. const std::string name = mList->getSelectedItemLabel();
  608. S32 cur_id = getCurrentIndex();
  609. mLastSelectedIndex = cur_id;
  610. if (cur_id != -1)
  611. {
  612. setLabel(name);
  613. if (mAllowTextEntry)
  614. {
  615. gFocusMgr.setKeyboardFocus(mTextEntry);
  616. mTextEntry->selectAll();
  617. }
  618. }
  619. // hiding the list reasserts the old value stored in the text editor/dropdown button
  620. hideList();
  621. // commit does the reverse, asserting the value in the list
  622. onCommit();
  623. }
  624. BOOL LLComboBox::handleToolTip(S32 x, S32 y, MASK mask)
  625. {
  626.     std::string tool_tip;
  627. if(LLUICtrl::handleToolTip(x, y, mask))
  628. {
  629. return TRUE;
  630. }
  631. tool_tip = getToolTip();
  632. if (tool_tip.empty())
  633. {
  634. tool_tip = getSelectedItemLabel();
  635. }
  636. if( !tool_tip.empty() )
  637. {
  638. LLToolTipMgr::instance().show(LLToolTip::Params()
  639. .message(tool_tip)
  640. .sticky_rect(calcScreenRect()));
  641. }
  642. return TRUE;
  643. }
  644. BOOL LLComboBox::handleKeyHere(KEY key, MASK mask)
  645. {
  646. BOOL result = FALSE;
  647. if (hasFocus())
  648. {
  649. if (mList->getVisible() 
  650. && key == KEY_ESCAPE && mask == MASK_NONE)
  651. {
  652. hideList();
  653. return TRUE;
  654. }
  655. //give list a chance to pop up and handle key
  656. LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
  657. if (last_selected_item)
  658. {
  659. // highlight the original selection before potentially selecting a new item
  660. mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
  661. }
  662. result = mList->handleKeyHere(key, mask);
  663. // will only see return key if it is originating from line editor
  664. // since the dropdown button eats the key
  665. if (key == KEY_RETURN)
  666. {
  667. // don't show list and don't eat key input when committing
  668. // free-form text entry with RETURN since user already knows
  669.             // what they are trying to select
  670. return FALSE;
  671. }
  672. // if selection has changed, pop open list
  673. else if (mList->getLastSelectedItem() != last_selected_item)
  674. {
  675. showList();
  676. }
  677. }
  678. return result;
  679. }
  680. BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char)
  681. {
  682. BOOL result = FALSE;
  683. if (gFocusMgr.childHasKeyboardFocus(this))
  684. {
  685. // space bar just shows the list
  686. if (' ' != uni_char )
  687. {
  688. LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
  689. if (last_selected_item)
  690. {
  691. // highlight the original selection before potentially selecting a new item
  692. mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
  693. }
  694. result = mList->handleUnicodeCharHere(uni_char);
  695. if (mList->getLastSelectedItem() != last_selected_item)
  696. {
  697. showList();
  698. }
  699. }
  700. }
  701. return result;
  702. }
  703. void LLComboBox::setTextEntry(const LLStringExplicit& text)
  704. {
  705. if (mTextEntry)
  706. {
  707. mTextEntry->setText(text);
  708. mHasAutocompletedText = FALSE;
  709. updateSelection();
  710. }
  711. }
  712. void LLComboBox::onTextEntry(LLLineEditor* line_editor)
  713. {
  714. if (mTextEntryCallback != NULL)
  715. {
  716. (mTextEntryCallback)(line_editor, LLSD());
  717. }
  718. KEY key = gKeyboard->currentKey();
  719. if (key == KEY_BACKSPACE || 
  720. key == KEY_DELETE)
  721. {
  722. if (mList->selectItemByLabel(line_editor->getText(), FALSE))
  723. {
  724. line_editor->setTentative(FALSE);
  725. mLastSelectedIndex = mList->getFirstSelectedIndex();
  726. }
  727. else
  728. {
  729. line_editor->setTentative(mTextEntryTentative);
  730. mList->deselectAllItems();
  731. mLastSelectedIndex = -1;
  732. }
  733. return;
  734. }
  735. if (key == KEY_LEFT || 
  736. key == KEY_RIGHT)
  737. {
  738. return;
  739. }
  740. if (key == KEY_DOWN)
  741. {
  742. setCurrentByIndex(llmin(getItemCount() - 1, getCurrentIndex() + 1));
  743. if (!mList->getVisible())
  744. {
  745. prearrangeList();
  746. if (mList->getItemCount() != 0)
  747. {
  748. showList();
  749. }
  750. }
  751. line_editor->selectAll();
  752. line_editor->setTentative(FALSE);
  753. }
  754. else if (key == KEY_UP)
  755. {
  756. setCurrentByIndex(llmax(0, getCurrentIndex() - 1));
  757. if (!mList->getVisible())
  758. {
  759. prearrangeList();
  760. if (mList->getItemCount() != 0)
  761. {
  762. showList();
  763. }
  764. }
  765. line_editor->selectAll();
  766. line_editor->setTentative(FALSE);
  767. }
  768. else
  769. {
  770. // RN: presumably text entry
  771. updateSelection();
  772. }
  773. }
  774. void LLComboBox::updateSelection()
  775. {
  776. LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor());
  777. // user-entered portion of string, based on assumption that any selected
  778.     // text was a result of auto-completion
  779. LLWString user_wstring = mHasAutocompletedText ? left_wstring : mTextEntry->getWText();
  780. std::string full_string = mTextEntry->getText();
  781. // go ahead and arrange drop down list on first typed character, even
  782. // though we aren't showing it... some code relies on prearrange
  783. // callback to populate content
  784. if( mTextEntry->getWText().size() == 1 )
  785. {
  786. prearrangeList(mTextEntry->getText());
  787. }
  788. if (mList->selectItemByLabel(full_string, FALSE))
  789. {
  790. mTextEntry->setTentative(FALSE);
  791. mLastSelectedIndex = mList->getFirstSelectedIndex();
  792. }
  793. else if (mList->selectItemByPrefix(left_wstring, FALSE))
  794. {
  795. LLWString selected_item = utf8str_to_wstring(mList->getSelectedItemLabel());
  796. LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size());
  797. mTextEntry->setText(wstring_to_utf8str(wtext));
  798. mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size());
  799. mTextEntry->endSelection();
  800. mTextEntry->setTentative(FALSE);
  801. mHasAutocompletedText = TRUE;
  802. mLastSelectedIndex = mList->getFirstSelectedIndex();
  803. }
  804. else // no matching items found
  805. {
  806. mList->deselectAllItems();
  807. mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion
  808. mTextEntry->setTentative(mTextEntryTentative);
  809. mHasAutocompletedText = FALSE;
  810. mLastSelectedIndex = -1;
  811. }
  812. }
  813. void LLComboBox::onTextCommit(const LLSD& data)
  814. {
  815. std::string text = mTextEntry->getText();
  816. setSimple(text);
  817. onCommit();
  818. mTextEntry->selectAll();
  819. }
  820. void LLComboBox::setFocus(BOOL b)
  821. {
  822. LLUICtrl::setFocus(b);
  823. if (b)
  824. {
  825. mList->clearSearchString();
  826. if (mList->getVisible())
  827. {
  828. mList->setFocus(TRUE);
  829. }
  830. }
  831. }
  832. void LLComboBox::prearrangeList(std::string filter)
  833. {
  834. if (mPrearrangeCallback)
  835. {
  836. mPrearrangeCallback(this, LLSD(filter));
  837. }
  838. }
  839. //============================================================================
  840. // LLCtrlListInterface functions
  841. S32 LLComboBox::getItemCount() const
  842. {
  843. return mList->getItemCount();
  844. }
  845. void LLComboBox::addColumn(const LLSD& column, EAddPosition pos)
  846. {
  847. mList->clearColumns();
  848. mList->addColumn(column, pos);
  849. }
  850. void LLComboBox::clearColumns()
  851. {
  852. mList->clearColumns();
  853. }
  854. void LLComboBox::setColumnLabel(const std::string& column, const std::string& label)
  855. {
  856. mList->setColumnLabel(column, label);
  857. }
  858. LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, void* userdata)
  859. {
  860. return mList->addElement(value, pos, userdata);
  861. }
  862. LLScrollListItem* LLComboBox::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id)
  863. {
  864. return mList->addSimpleElement(value, pos, id);
  865. }
  866. void LLComboBox::clearRows()
  867. {
  868. mList->clearRows();
  869. }
  870. void LLComboBox::sortByColumn(const std::string& name, BOOL ascending)
  871. {
  872. mList->sortByColumn(name, ascending);
  873. }
  874. //============================================================================
  875. //LLCtrlSelectionInterface functions
  876. BOOL LLComboBox::setCurrentByID(const LLUUID& id)
  877. {
  878. BOOL found = mList->selectByID( id );
  879. if (found)
  880. {
  881. setLabel(mList->getSelectedItemLabel());
  882. mLastSelectedIndex = mList->getFirstSelectedIndex();
  883. }
  884. return found;
  885. }
  886. LLUUID LLComboBox::getCurrentID() const
  887. {
  888. return mList->getStringUUIDSelectedItem();
  889. }
  890. BOOL LLComboBox::setSelectedByValue(const LLSD& value, BOOL selected)
  891. {
  892. BOOL found = mList->setSelectedByValue(value, selected);
  893. if (found)
  894. {
  895. setLabel(mList->getSelectedItemLabel());
  896. }
  897. return found;
  898. }
  899. LLSD LLComboBox::getSelectedValue()
  900. {
  901. return mList->getSelectedValue();
  902. }
  903. BOOL LLComboBox::isSelected(const LLSD& value) const
  904. {
  905. return mList->isSelected(value);
  906. }
  907. BOOL LLComboBox::operateOnSelection(EOperation op)
  908. {
  909. if (op == OP_DELETE)
  910. {
  911. mList->deleteSelectedItems();
  912. return TRUE;
  913. }
  914. return FALSE;
  915. }
  916. BOOL LLComboBox::operateOnAll(EOperation op)
  917. {
  918. if (op == OP_DELETE)
  919. {
  920. clearRows();
  921. return TRUE;
  922. }
  923. return FALSE;
  924. }
  925. BOOL LLComboBox::selectItemRange( S32 first, S32 last )
  926. {
  927. return mList->selectItemRange(first, last);
  928. }