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

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llflatlistview.cpp
  3.  * @brief LLFlatListView base class
  4.  *
  5.  * $LicenseInfo:firstyear=2009&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2009-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. #include "linden_common.h"
  33. #include "llpanel.h"
  34. #include "lltextbox.h"
  35. #include "llflatlistview.h"
  36. static const LLDefaultChildRegistry::Register<LLFlatListView> flat_list_view("flat_list_view");
  37. const LLSD SELECTED_EVENT = LLSD().with("selected", true);
  38. const LLSD UNSELECTED_EVENT = LLSD().with("selected", false);
  39. //forward declaration
  40. bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2);
  41. LLFlatListView::Params::Params()
  42. : item_pad("item_pad"),
  43. allow_select("allow_select"),
  44. multi_select("multi_select"),
  45. keep_one_selected("keep_one_selected"),
  46. no_items_text("no_items_text")
  47. {};
  48. void LLFlatListView::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
  49. {
  50. LLScrollContainer::reshape(width, height, called_from_parent);
  51. setItemsNoScrollWidth(width);
  52. rearrangeItems();
  53. }
  54. const LLRect& LLFlatListView::getItemsRect() const
  55. {
  56. return mItemsPanel->getRect(); 
  57. }
  58. bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/,bool rearrange /*= true*/)
  59. {
  60. if (!item) return false;
  61. if (value.isUndefined()) return false;
  62. //force uniqueness of items, easiest check but unreliable
  63. if (item->getParent() == mItemsPanel) return false;
  64. item_pair_t* new_pair = new item_pair_t(item, value);
  65. switch (pos)
  66. {
  67. case ADD_TOP:
  68. mItemPairs.push_front(new_pair);
  69. //in LLView::draw() children are iterated in backorder
  70. mItemsPanel->addChildInBack(item);
  71. break;
  72. case ADD_BOTTOM:
  73. mItemPairs.push_back(new_pair);
  74. mItemsPanel->addChild(item);
  75. break;
  76. default:
  77. break;
  78. }
  79. //_4 is for MASK
  80. item->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
  81. item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
  82. // Children don't accept the focus
  83. item->setTabStop(false);
  84. if (rearrange)
  85. {
  86. rearrangeItems();
  87. notifyParentItemsRectChanged();
  88. }
  89. return true;
  90. }
  91. bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value /*= LLUUID::null*/)
  92. {
  93. if (!after_item) return false;
  94. if (!item_to_add) return false;
  95. if (value.isUndefined()) return false;
  96. if (mItemPairs.empty()) return false;
  97. //force uniqueness of items, easiest check but unreliable
  98. if (item_to_add->getParent() == mItemsPanel) return false;
  99. item_pair_t* after_pair = getItemPair(after_item);
  100. if (!after_pair) return false;
  101. item_pair_t* new_pair = new item_pair_t(item_to_add, value);
  102. if (after_pair == mItemPairs.back())
  103. {
  104. mItemPairs.push_back(new_pair);
  105. mItemsPanel->addChild(item_to_add);
  106. }
  107. else
  108. {
  109. pairs_iterator_t it = mItemPairs.begin();
  110. for (; it != mItemPairs.end(); ++it)
  111. {
  112. if (*it == after_pair)
  113. {
  114. // insert new elements before the element at position of passed iterator.
  115. mItemPairs.insert(++it, new_pair);
  116. mItemsPanel->addChild(item_to_add);
  117. break;
  118. }
  119. }
  120. }
  121. //_4 is for MASK
  122. item_to_add->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
  123. item_to_add->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
  124. rearrangeItems();
  125. notifyParentItemsRectChanged();
  126. return true;
  127. }
  128. bool LLFlatListView::removeItem(LLPanel* item)
  129. {
  130. if (!item) return false;
  131. if (item->getParent() != mItemsPanel) return false;
  132. item_pair_t* item_pair = getItemPair(item);
  133. if (!item_pair) return false;
  134. return removeItemPair(item_pair);
  135. }
  136. bool LLFlatListView::removeItemByValue(const LLSD& value)
  137. {
  138. if (value.isUndefined()) return false;
  139. item_pair_t* item_pair = getItemPair(value);
  140. if (!item_pair) return false;
  141. return removeItemPair(item_pair);
  142. }
  143. bool LLFlatListView::removeItemByUUID(const LLUUID& uuid)
  144. {
  145. return removeItemByValue(LLSD(uuid));
  146. }
  147. LLPanel* LLFlatListView::getItemByValue(const LLSD& value) const
  148. {
  149. if (value.isUndefined()) return NULL;
  150. item_pair_t* pair = getItemPair(value);
  151. if (pair) return pair->first;
  152. return NULL;
  153. }
  154. bool LLFlatListView::selectItem(LLPanel* item, bool select /*= true*/)
  155. {
  156. if (!item) return false;
  157. if (item->getParent() != mItemsPanel) return false;
  158. item_pair_t* item_pair = getItemPair(item);
  159. if (!item_pair) return false;
  160. return selectItemPair(item_pair, select);
  161. }
  162. bool LLFlatListView::selectItemByValue(const LLSD& value, bool select /*= true*/)
  163. {
  164. if (value.isUndefined()) return false;
  165. item_pair_t* item_pair = getItemPair(value);
  166. if (!item_pair) return false;
  167. return selectItemPair(item_pair, select);
  168. }
  169. bool LLFlatListView::selectItemByUUID(const LLUUID& uuid, bool select /* = true*/)
  170. {
  171. return selectItemByValue(LLSD(uuid), select);
  172. }
  173. LLSD LLFlatListView::getSelectedValue() const
  174. {
  175. if (mSelectedItemPairs.empty()) return LLSD();
  176. item_pair_t* first_selected_pair = mSelectedItemPairs.front();
  177. return first_selected_pair->second;
  178. }
  179. void LLFlatListView::getSelectedValues(std::vector<LLSD>& selected_values) const
  180. {
  181. if (mSelectedItemPairs.empty()) return;
  182. for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
  183. {
  184. selected_values.push_back((*it)->second);
  185. }
  186. }
  187. LLUUID LLFlatListView::getSelectedUUID() const
  188. {
  189. const LLSD& value = getSelectedValue();
  190. if (value.isDefined() && value.isUUID())
  191. {
  192. return value.asUUID();
  193. }
  194. else 
  195. {
  196. return LLUUID::null;
  197. }
  198. }
  199. void LLFlatListView::getSelectedUUIDs(std::vector<LLUUID>& selected_uuids) const
  200. {
  201. if (mSelectedItemPairs.empty()) return;
  202. for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
  203. {
  204. selected_uuids.push_back((*it)->second.asUUID());
  205. }
  206. }
  207. LLPanel* LLFlatListView::getSelectedItem() const
  208. {
  209. if (mSelectedItemPairs.empty()) return NULL;
  210. return mSelectedItemPairs.front()->first;
  211. }
  212. void LLFlatListView::getSelectedItems(std::vector<LLPanel*>& selected_items) const
  213. {
  214. if (mSelectedItemPairs.empty()) return;
  215. for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
  216. {
  217. selected_items.push_back((*it)->first);
  218. }
  219. }
  220. void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/)
  221. {
  222. if (mSelectedItemPairs.empty()) return;
  223. for (pairs_iterator_t it= mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
  224. {
  225. item_pair_t* pair_to_deselect = *it;
  226. LLPanel* item = pair_to_deselect->first;
  227. item->setValue(UNSELECTED_EVENT);
  228. }
  229. mSelectedItemPairs.clear();
  230. if (mCommitOnSelectionChange && !no_commit_on_deselection)
  231. {
  232. onCommit();
  233. }
  234. // Stretch selected item rect to ensure it won't be clipped
  235. mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
  236. }
  237. void LLFlatListView::setNoItemsCommentText(const std::string& comment_text)
  238. {
  239. mNoItemsCommentTextbox->setValue(comment_text);
  240. }
  241. void LLFlatListView::clear()
  242. {
  243. // do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex.
  244. for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
  245. {
  246. mItemsPanel->removeChild((*it)->first);
  247. (*it)->first->die();
  248. delete *it;
  249. }
  250. mItemPairs.clear();
  251. mSelectedItemPairs.clear();
  252. // also set items panel height to zero. Reshape it to allow reshaping of non-item children
  253. LLRect rc = mItemsPanel->getRect();
  254. rc.mBottom = rc.mTop;
  255. mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
  256. mItemsPanel->setRect(rc);
  257. setNoItemsCommentVisible(true);
  258. notifyParentItemsRectChanged();
  259. }
  260. void LLFlatListView::sort()
  261. {
  262. if (!mItemComparator)
  263. {
  264. llwarns << "No comparator specified for sorting FlatListView items." << llendl;
  265. return;
  266. }
  267. mItemPairs.sort(ComparatorAdaptor(*mItemComparator));
  268. rearrangeItems();
  269. }
  270. bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value)
  271. {
  272. if (old_value.isUndefined() || new_value.isUndefined()) return false;
  273. if (llsds_are_equal(old_value, new_value)) return false;
  274. item_pair_t* item_pair = getItemPair(old_value);
  275. if (!item_pair) return false;
  276. item_pair->second = new_value;
  277. return true;
  278. }
  279. //////////////////////////////////////////////////////////////////////////
  280. // PROTECTED STUFF
  281. //////////////////////////////////////////////////////////////////////////
  282. LLFlatListView::LLFlatListView(const LLFlatListView::Params& p)
  283. : LLScrollContainer(p)
  284.   , mItemComparator(NULL)
  285.   , mItemsPanel(NULL)
  286.   , mItemPad(p.item_pad)
  287.   , mAllowSelection(p.allow_select)
  288.   , mMultipleSelection(p.multi_select)
  289.   , mKeepOneItemSelected(p.keep_one_selected)
  290.   , mCommitOnSelectionChange(false)
  291.   , mPrevNotifyParentRect(LLRect())
  292.   , mNoItemsCommentTextbox(NULL)
  293. {
  294. mBorderThickness = getBorderWidth();
  295. LLRect scroll_rect = getRect();
  296. LLRect items_rect;
  297. setItemsNoScrollWidth(scroll_rect.getWidth());
  298. items_rect.setLeftTopAndSize(mBorderThickness, scroll_rect.getHeight() - mBorderThickness, mItemsNoScrollWidth, 0);
  299. LLPanel::Params pp;
  300. pp.rect(items_rect);
  301. mItemsPanel = LLUICtrlFactory::create<LLPanel> (pp);
  302. addChild(mItemsPanel);
  303. //we don't need to stretch in vertical direction on reshaping by a parent
  304. //no bottom following!
  305. mItemsPanel->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
  306. LLViewBorder::Params params;
  307. params.name("scroll border");
  308. params.rect(getLastSelectedItemRect());
  309. params.visible(false);
  310. params.bevel_style(LLViewBorder::BEVEL_IN);
  311. mSelectedItemsBorder = LLUICtrlFactory::create<LLViewBorder> (params);
  312. mItemsPanel->addChild( mSelectedItemsBorder );
  313. {
  314. // create textbox for "No Items" comment text
  315. LLTextBox::Params text_p = p.no_items_text;
  316. if (!text_p.rect.isProvided())
  317. {
  318. LLRect comment_rect = getRect();
  319. comment_rect.setOriginAndSize(0, 0, comment_rect.getWidth(), comment_rect.getHeight());
  320. comment_rect.stretch(-getBorderWidth());
  321. text_p.rect(comment_rect);
  322. }
  323. text_p.border_visible(false);
  324. if (!text_p.follows.isProvided())
  325. {
  326. text_p.follows.flags(FOLLOWS_ALL);
  327. }
  328. mNoItemsCommentTextbox = LLUICtrlFactory::create<LLTextBox>(text_p, this);
  329. }
  330. };
  331. // virtual
  332. void LLFlatListView::draw()
  333. {
  334. // Highlight border if a child of this container has keyboard focus
  335. if( mSelectedItemsBorder->getVisible() )
  336. {
  337. mSelectedItemsBorder->setKeyboardFocusHighlight( hasFocus() );
  338. }
  339. LLScrollContainer::draw();
  340. }
  341. // virtual
  342. BOOL LLFlatListView::postBuild()
  343. {
  344. setTabStop(true);
  345. return LLScrollContainer::postBuild();
  346. }
  347. void LLFlatListView::rearrangeItems()
  348. {
  349. static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
  350. setNoItemsCommentVisible(mItemPairs.empty());
  351. if (mItemPairs.empty()) return;
  352. //calculating required height - assuming items can be of different height
  353. //list should accommodate all its items
  354. S32 height = 0;
  355. S32 invisible_children_count = 0;
  356. pairs_iterator_t it = mItemPairs.begin();
  357. for (; it != mItemPairs.end(); ++it)
  358. {
  359. LLPanel* item = (*it)->first;
  360. // skip invisible child
  361. if (!item->getVisible())
  362. {
  363. ++invisible_children_count;
  364. continue;
  365. }
  366. height += item->getRect().getHeight();
  367. }
  368. // add paddings between items, excluding invisible ones
  369. height += mItemPad * (mItemPairs.size() - invisible_children_count - 1);
  370. LLRect rc = mItemsPanel->getRect();
  371. S32 width = mItemsNoScrollWidth;
  372. // update width to avoid horizontal scrollbar
  373. if (height > getRect().getHeight() - 2 * mBorderThickness)
  374. width -= scrollbar_size;
  375. //changes the bottom, end of the list goes down in the scroll container
  376. rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height);
  377. mItemsPanel->setRect(rc);
  378. //reshaping items
  379. S32 item_new_top = height;
  380. pairs_iterator_t it2, first_it = mItemPairs.begin();
  381. for (it2 = first_it; it2 != mItemPairs.end(); ++it2)
  382. {
  383. LLPanel* item = (*it2)->first;
  384. // skip invisible child
  385. if (!item->getVisible())
  386. continue;
  387. LLRect rc = item->getRect();
  388. rc.setLeftTopAndSize(rc.mLeft, item_new_top, width, rc.getHeight());
  389. item->reshape(rc.getWidth(), rc.getHeight());
  390. item->setRect(rc);
  391. // move top for next item in list
  392. item_new_top -= (rc.getHeight() + mItemPad);
  393. }
  394. // Stretch selected item rect to ensure it won't be clipped
  395. mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
  396. }
  397. void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask)
  398. {
  399. if (!item_pair) return;
  400. setFocus(TRUE);
  401. bool select_item = !isSelected(item_pair);
  402. //*TODO find a better place for that enforcing stuff
  403. if (mKeepOneItemSelected && numSelected() == 1 && !select_item) return;
  404. if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection();
  405. selectItemPair(item_pair, select_item);
  406. }
  407. void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask)
  408. {
  409. if (!item_pair)
  410. return;
  411. // Forbid deselecting of items on right mouse button click if mMultipleSelection flag is set on,
  412. // because some of derived classes may have context menu and selected items must be kept.
  413. if ( !(mask & MASK_CONTROL) && mMultipleSelection && isSelected(item_pair) )
  414. return;
  415. // else got same behavior as at onItemMouseClick
  416. onItemMouseClick(item_pair, mask);
  417. }
  418. BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask)
  419. {
  420. BOOL reset_selection = (mask != MASK_SHIFT);
  421. BOOL handled = FALSE;
  422. switch (key)
  423. {
  424. case KEY_RETURN:
  425. {
  426. if (mSelectedItemPairs.size() && mask == MASK_NONE)
  427. {
  428. mOnReturnSignal(this, getValue());
  429. handled = TRUE;
  430. }
  431. break;
  432. }
  433. case KEY_UP:
  434. {
  435. if ( !selectNextItemPair(true, reset_selection) && reset_selection)
  436. {
  437. // If case we are in accordion tab notify parent to go to the previous accordion
  438. if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed
  439. resetSelection();
  440. }
  441. break;
  442. }
  443. case KEY_DOWN:
  444. {
  445. if ( !selectNextItemPair(false, reset_selection) && reset_selection)
  446. {
  447. // If case we are in accordion tab notify parent to go to the next accordion
  448. if( notifyParent(LLSD().with("action","select_next")) > 0 ) //message was processed
  449. resetSelection();
  450. }
  451. break;
  452. }
  453. case 'A':
  454. {
  455. if(MASK_CONTROL & mask)
  456. {
  457. selectAll();
  458. handled = TRUE;
  459. }
  460. break;
  461. }
  462. default:
  463. break;
  464. }
  465. if ( ( key == KEY_UP || key == KEY_DOWN ) && mSelectedItemPairs.size() )
  466. {
  467. ensureSelectedVisible();
  468. /*
  469. LLRect visible_rc = getVisibleContentRect();
  470. LLRect selected_rc = getLastSelectedItemRect();
  471. if ( !visible_rc.contains (selected_rc) )
  472. {
  473. // But scroll in Items panel coordinates
  474. scrollToShowRect(selected_rc);
  475. }
  476. // In case we are in accordion tab notify parent to show selected rectangle
  477. LLRect screen_rc;
  478. localRectToScreen(selected_rc, &screen_rc);
  479. notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/
  480. handled = TRUE;
  481. }
  482. return handled ? handled : LLScrollContainer::handleKeyHere(key, mask);
  483. }
  484. LLFlatListView::item_pair_t* LLFlatListView::getItemPair(LLPanel* item) const
  485. {
  486. llassert(item);
  487. for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
  488. {
  489. item_pair_t* item_pair = *it;
  490. if (item_pair->first == item) return item_pair;
  491. }
  492. return NULL;
  493. }
  494. //compares two LLSD's
  495. bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2)
  496. {
  497. llassert(llsd_1.isDefined());
  498. llassert(llsd_2.isDefined());
  499. if (llsd_1.type() != llsd_2.type()) return false;
  500. if (!llsd_1.isMap())
  501. {
  502. if (llsd_1.isUUID()) return llsd_1.asUUID() == llsd_2.asUUID();
  503. //assumptions that string representaion is enough for other types
  504. return llsd_1.asString() == llsd_2.asString();
  505. }
  506. if (llsd_1.size() != llsd_2.size()) return false;
  507. LLSD::map_const_iterator llsd_1_it = llsd_1.beginMap();
  508. LLSD::map_const_iterator llsd_2_it = llsd_2.beginMap();
  509. for (S32 i = 0; i < llsd_1.size(); ++i)
  510. {
  511. if ((*llsd_1_it).first != (*llsd_2_it).first) return false;
  512. if (!llsds_are_equal((*llsd_1_it).second, (*llsd_2_it).second)) return false;
  513. ++llsd_1_it;
  514. ++llsd_2_it;
  515. }
  516. return true;
  517. }
  518. LLFlatListView::item_pair_t* LLFlatListView::getItemPair(const LLSD& value) const
  519. {
  520. llassert(value.isDefined());
  521. for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
  522. {
  523. item_pair_t* item_pair = *it;
  524. if (llsds_are_equal(item_pair->second, value)) return item_pair;
  525. }
  526. return NULL;
  527. }
  528. bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select)
  529. {
  530. llassert(item_pair);
  531. if (!mAllowSelection && select) return false;
  532. if (isSelected(item_pair) == select) return true; //already in specified selection state
  533. if (select)
  534. {
  535. mSelectedItemPairs.push_back(item_pair);
  536. }
  537. else
  538. {
  539. mSelectedItemPairs.remove(item_pair);
  540. }
  541. //a way of notifying panel of selection state changes
  542. LLPanel* item = item_pair->first;
  543. item->setValue(select ? SELECTED_EVENT : UNSELECTED_EVENT);
  544. if (mCommitOnSelectionChange)
  545. {
  546. onCommit();
  547. }
  548. // Stretch selected item rect to ensure it won't be clipped
  549. mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
  550. return true;
  551. }
  552. LLRect LLFlatListView::getLastSelectedItemRect()
  553. {
  554. if (!mSelectedItemPairs.size())
  555. {
  556. return LLRect::null;
  557. }
  558. return mSelectedItemPairs.back()->first->getRect();
  559. }
  560. void LLFlatListView::selectFirstItem ()
  561. {
  562. selectItemPair(mItemPairs.front(), true);
  563. ensureSelectedVisible();
  564. }
  565. void LLFlatListView::selectLastItem ()
  566. {
  567. selectItemPair(mItemPairs.back(), true);
  568. ensureSelectedVisible();
  569. }
  570. void LLFlatListView::ensureSelectedVisible()
  571. {
  572. LLRect selected_rc = getLastSelectedItemRect();
  573. if ( selected_rc.isValid() )
  574. {
  575. scrollToShowRect(selected_rc);
  576. }
  577. }
  578. // virtual
  579. bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection)
  580. {
  581. // No items - no actions!
  582. if ( !mItemPairs.size() )
  583. return false;
  584. item_pair_t* to_sel_pair = NULL;
  585. item_pair_t* cur_sel_pair = NULL;
  586. if ( mSelectedItemPairs.size() )
  587. {
  588. // Take the last selected pair
  589. cur_sel_pair = mSelectedItemPairs.back();
  590. // Bases on given direction choose next item to select
  591. if ( is_up_direction )
  592. {
  593. // Find current selected item position in mItemPairs list
  594. pairs_list_t::reverse_iterator sel_it = std::find(mItemPairs.rbegin(), mItemPairs.rend(), cur_sel_pair);
  595. for (;++sel_it != mItemPairs.rend();)
  596. {
  597. // skip invisible items
  598. if ( (*sel_it)->first->getVisible() )
  599. {
  600. to_sel_pair = *sel_it;
  601. break;
  602. }
  603. }
  604. }
  605. else
  606. {
  607. // Find current selected item position in mItemPairs list
  608. pairs_list_t::iterator sel_it = std::find(mItemPairs.begin(), mItemPairs.end(), cur_sel_pair);
  609. for (;++sel_it != mItemPairs.end();)
  610. {
  611. // skip invisible items
  612. if ( (*sel_it)->first->getVisible() )
  613. {
  614. to_sel_pair = *sel_it;
  615. break;
  616. }
  617. }
  618. }
  619. }
  620. else
  621. {
  622. // If there weren't selected items then choose the first one bases on given direction
  623. cur_sel_pair = (is_up_direction) ? mItemPairs.back() : mItemPairs.front();
  624. // Force selection to first item
  625. to_sel_pair = cur_sel_pair;
  626. }
  627. if ( to_sel_pair )
  628. {
  629. bool select = true;
  630. if ( reset_selection )
  631. {
  632. // Reset current selection if we were asked about it
  633. resetSelection();
  634. }
  635. else
  636. {
  637. // If item already selected and no reset request than we should deselect last selected item.
  638. select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair));
  639. }
  640. // Select/Deselect next item
  641. selectItemPair(select ? to_sel_pair : cur_sel_pair, select);
  642. return true;
  643. }
  644. return false;
  645. }
  646. bool LLFlatListView::selectAll()
  647. {
  648. if (!mAllowSelection)
  649. return false;
  650. mSelectedItemPairs.clear();
  651. for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
  652. {
  653. item_pair_t* item_pair = *it;
  654. mSelectedItemPairs.push_back(item_pair);
  655. //a way of notifying panel of selection state changes
  656. LLPanel* item = item_pair->first;
  657. item->setValue(SELECTED_EVENT);
  658. }
  659. if (mCommitOnSelectionChange)
  660. {
  661. onCommit();
  662. }
  663. // Stretch selected item rect to ensure it won't be clipped
  664. mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
  665. return true;
  666. }
  667. bool LLFlatListView::isSelected(item_pair_t* item_pair) const
  668. {
  669. llassert(item_pair);
  670. pairs_const_iterator_t it_end = mSelectedItemPairs.end();
  671. return std::find(mSelectedItemPairs.begin(), it_end, item_pair) != it_end;
  672. }
  673. bool LLFlatListView::removeItemPair(item_pair_t* item_pair)
  674. {
  675. llassert(item_pair);
  676. bool deleted = false;
  677. for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
  678. {
  679. item_pair_t* _item_pair = *it;
  680. if (_item_pair == item_pair)
  681. {
  682. mItemPairs.erase(it);
  683. deleted = true;
  684. break;
  685. }
  686. }
  687. if (!deleted) return false;
  688. for (pairs_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
  689. {
  690. item_pair_t* selected_item_pair = *it;
  691. if (selected_item_pair == item_pair)
  692. {
  693. it = mSelectedItemPairs.erase(it);
  694. break;
  695. }
  696. }
  697. mItemsPanel->removeChild(item_pair->first);
  698. item_pair->first->die();
  699. delete item_pair;
  700. rearrangeItems();
  701. notifyParentItemsRectChanged();
  702. return true;
  703. }
  704. void LLFlatListView::notifyParentItemsRectChanged()
  705. {
  706. S32 comment_height = 0;
  707. // take into account comment text height if exists
  708. if (mNoItemsCommentTextbox && mNoItemsCommentTextbox->getVisible())
  709. {
  710. // top text padding inside the textbox is included into the height
  711. comment_height = mNoItemsCommentTextbox->getTextPixelHeight();
  712. // take into account a distance from parent's top border to textbox's top
  713. comment_height += getRect().getHeight() - mNoItemsCommentTextbox->getRect().mTop;
  714. }
  715. LLRect req_rect =  getItemsRect();
  716. // get maximum of items total height and comment text height
  717. req_rect.setOriginAndSize(req_rect.mLeft, req_rect.mBottom, req_rect.getWidth(), llmax(req_rect.getHeight(), comment_height));
  718. // take into account border size.
  719. req_rect.stretch(getBorderWidth());
  720. if (req_rect == mPrevNotifyParentRect)
  721. return;
  722. mPrevNotifyParentRect = req_rect;
  723. LLSD params;
  724. params["action"] = "size_changes";
  725. params["width"] = req_rect.getWidth();
  726. params["height"] = req_rect.getHeight();
  727. if (getParent()) // dummy widgets don't have a parent
  728. getParent()->notifyParent(params);
  729. }
  730. void LLFlatListView::setNoItemsCommentVisible(bool visible) const
  731. {
  732. if (mNoItemsCommentTextbox)
  733. {
  734. if (visible)
  735. {
  736. /*
  737. // *NOTE: MA 2010-02-04
  738. // Deprecated after params of the comment text box were moved into widget (flat_list_view.xml)
  739. // can be removed later if nothing happened.
  740. // We have to update child rect here because of issues with rect after reshaping while creating LLTextbox
  741. // It is possible to have invalid LLRect if Flat List is in LLAccordionTab
  742. LLRect comment_rect = getLocalRect();
  743. // To see comment correctly (EXT - 3244) in mNoItemsCommentTextbox we must get border width
  744. // of LLFlatListView (@see getBorderWidth()) and stretch mNoItemsCommentTextbox to this width
  745. // But getBorderWidth() returns 0 if LLFlatListView not visible. So we have to get border width
  746. // from 'scroll_border'
  747. LLViewBorder* scroll_border = getChild<LLViewBorder>("scroll border");
  748. comment_rect.stretch(-scroll_border->getBorderWidth());
  749. mNoItemsCommentTextbox->setRect(comment_rect);
  750. */
  751. }
  752. mNoItemsCommentTextbox->setVisible(visible);
  753. }
  754. }
  755. void LLFlatListView::getItems(std::vector<LLPanel*>& items) const
  756. {
  757. if (mItemPairs.empty()) return;
  758. items.clear();
  759. for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
  760. {
  761. items.push_back((*it)->first);
  762. }
  763. }
  764. void LLFlatListView::getValues(std::vector<LLSD>& values) const
  765. {
  766. if (mItemPairs.empty()) return;
  767. values.clear();
  768. for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
  769. {
  770. values.push_back((*it)->second);
  771. }
  772. }
  773. // virtual
  774. void LLFlatListView::onFocusReceived()
  775. {
  776. mSelectedItemsBorder->setVisible(TRUE);
  777. }
  778. // virtual
  779. void LLFlatListView::onFocusLost()
  780. {
  781. mSelectedItemsBorder->setVisible(FALSE);
  782. }
  783. //virtual 
  784. S32 LLFlatListView::notify(const LLSD& info)
  785. {
  786. if(info.has("action"))
  787. {
  788. std::string str_action = info["action"];
  789. if(str_action == "select_first")
  790. {
  791. setFocus(true);
  792. selectFirstItem();
  793. return 1;
  794. }
  795. else if(str_action == "select_last")
  796. {
  797. setFocus(true);
  798. selectLastItem();
  799. return 1;
  800. }
  801. }
  802. else if (info.has("rearrange"))
  803. {
  804. rearrangeItems();
  805. notifyParentItemsRectChanged();
  806. return 1;
  807. }
  808. return 0;
  809. }
  810. void LLFlatListView::detachItems(std::vector<LLPanel*>& detached_items)
  811. {
  812. LLSD action;
  813. action.with("detach", LLSD());
  814. // Clear detached_items list
  815. detached_items.clear();
  816. // Go through items and detach valid items, remove them from items panel
  817. // and add to detached_items.
  818. for (pairs_iterator_t
  819.  iter = mItemPairs.begin(),
  820.  iter_end = mItemPairs.end();
  821.  iter != iter_end; ++iter)
  822. {
  823. LLPanel* pItem = (*iter)->first;
  824. if (1 == pItem->notify(action))
  825. {
  826. selectItemPair((*iter), false);
  827. mItemsPanel->removeChild(pItem);
  828. detached_items.push_back(pItem);
  829. }
  830. }
  831. if (!detached_items.empty())
  832. {
  833. // Some items were detached, clean ourself from unusable memory
  834. if (detached_items.size() == mItemPairs.size())
  835. {
  836. // This way will be faster if all items were disconnected
  837. for (pairs_iterator_t
  838.  iter = mItemPairs.begin(),
  839.  iter_end = mItemPairs.end();
  840.  iter != iter_end; ++iter)
  841. {
  842. (*iter)->first = NULL;
  843. delete *iter;
  844. }
  845. mItemPairs.clear();
  846. // Also set items panel height to zero.
  847. // Reshape it to allow reshaping of non-item children.
  848. LLRect rc = mItemsPanel->getRect();
  849. rc.mBottom = rc.mTop;
  850. mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
  851. mItemsPanel->setRect(rc);
  852. setNoItemsCommentVisible(true);
  853. }
  854. else
  855. {
  856. for (std::vector<LLPanel*>::const_iterator
  857.  detached_iter = detached_items.begin(),
  858.  detached_iter_end = detached_items.end();
  859.  detached_iter != detached_iter_end; ++detached_iter)
  860. {
  861. LLPanel* pDetachedItem = *detached_iter;
  862. for (pairs_iterator_t
  863.  iter = mItemPairs.begin(),
  864.  iter_end = mItemPairs.end();
  865.  iter != iter_end; ++iter)
  866. {
  867. item_pair_t* item_pair = *iter;
  868. if (item_pair->first == pDetachedItem)
  869. {
  870. mItemPairs.erase(iter);
  871. item_pair->first = NULL;
  872. delete item_pair;
  873. break;
  874. }
  875. }
  876. }
  877. rearrangeItems();
  878. }
  879. notifyParentItemsRectChanged();
  880. }
  881. }
  882. //EOF