PageRenderTime 58ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llfolderviewitem.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2560 lines | 1993 code | 307 blank | 260 comment | 415 complexity | 76b1f7dcb039218bb6787a11107533c2 MD5 | raw file
Possible License(s): LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. /**
  2. * @file llfolderviewitem.cpp
  3. * @brief Items and folders that can appear in a hierarchical folder view
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llfolderviewitem.h"
  28. // viewer includes
  29. #include "llfolderview.h" // Items depend extensively on LLFolderViews
  30. #include "llfoldervieweventlistener.h"
  31. #include "llviewerfoldertype.h"
  32. #include "llinventorybridge.h" // for LLItemBridge in LLInventorySort::operator()
  33. #include "llinventoryfilter.h"
  34. #include "llinventoryfunctions.h"
  35. #include "llinventorymodelbackgroundfetch.h"
  36. #include "llpanel.h"
  37. #include "llviewercontrol.h" // gSavedSettings
  38. #include "llviewerwindow.h" // Argh, only for setCursor()
  39. // linden library includes
  40. #include "llfocusmgr.h" // gFocusMgr
  41. #include "lltrans.h"
  42. ///----------------------------------------------------------------------------
  43. /// Class LLFolderViewItem
  44. ///----------------------------------------------------------------------------
  45. static LLDefaultChildRegistry::Register<LLFolderViewItem> r("folder_view_item");
  46. // statics
  47. std::map<U8, LLFontGL*> LLFolderViewItem::sFonts; // map of styles to fonts
  48. // only integers can be initialized in header
  49. const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f;
  50. const F32 LLFolderViewItem::FOLDER_OPEN_TIME_CONSTANT = 0.03f;
  51. const LLColor4U DEFAULT_WHITE(255, 255, 255);
  52. //static
  53. LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style)
  54. {
  55. LLFontGL* rtn = sFonts[style];
  56. if (!rtn) // grab label font with this style, lazily
  57. {
  58. LLFontDescriptor labelfontdesc("SansSerif", "Small", style);
  59. rtn = LLFontGL::getFont(labelfontdesc);
  60. if (!rtn)
  61. {
  62. rtn = LLFontGL::getFontDefault();
  63. }
  64. sFonts[style] = rtn;
  65. }
  66. return rtn;
  67. }
  68. //static
  69. void LLFolderViewItem::initClass()
  70. {
  71. }
  72. //static
  73. void LLFolderViewItem::cleanupClass()
  74. {
  75. sFonts.clear();
  76. }
  77. // NOTE: Optimize this, we call it a *lot* when opening a large inventory
  78. LLFolderViewItem::Params::Params()
  79. : icon(),
  80. icon_open(),
  81. icon_overlay(),
  82. root(),
  83. listener(),
  84. folder_arrow_image("folder_arrow_image"),
  85. folder_indentation("folder_indentation"),
  86. selection_image("selection_image"),
  87. item_height("item_height"),
  88. item_top_pad("item_top_pad"),
  89. creation_date()
  90. {}
  91. // Default constructor
  92. LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)
  93. : LLView(p),
  94. mLabelWidth(0),
  95. mLabelWidthDirty(false),
  96. mParentFolder( NULL ),
  97. mIsSelected( FALSE ),
  98. mIsCurSelection( FALSE ),
  99. mSelectPending(FALSE),
  100. mLabelStyle( LLFontGL::NORMAL ),
  101. mHasVisibleChildren(FALSE),
  102. mIndentation(0),
  103. mItemHeight(p.item_height),
  104. mPassedFilter(FALSE),
  105. mLastFilterGeneration(-1),
  106. mStringMatchOffset(std::string::npos),
  107. mControlLabelRotation(0.f),
  108. mDragAndDropTarget(FALSE),
  109. mIsLoading(FALSE),
  110. mLabel(p.name),
  111. mRoot(p.root),
  112. mCreationDate(p.creation_date),
  113. mIcon(p.icon),
  114. mIconOpen(p.icon_open),
  115. mIconOverlay(p.icon_overlay),
  116. mListener(p.listener),
  117. mShowLoadStatus(false),
  118. mIsMouseOverTitle(false)
  119. {
  120. }
  121. BOOL LLFolderViewItem::postBuild()
  122. {
  123. refresh();
  124. return TRUE;
  125. }
  126. // Destroys the object
  127. LLFolderViewItem::~LLFolderViewItem( void )
  128. {
  129. delete mListener;
  130. mListener = NULL;
  131. }
  132. LLFolderView* LLFolderViewItem::getRoot()
  133. {
  134. return mRoot;
  135. }
  136. // Returns true if this object is a child (or grandchild, etc.) of potential_ancestor.
  137. BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor )
  138. {
  139. LLFolderViewItem* root = this;
  140. while( root->mParentFolder )
  141. {
  142. if( root->mParentFolder == potential_ancestor )
  143. {
  144. return TRUE;
  145. }
  146. root = root->mParentFolder;
  147. }
  148. return FALSE;
  149. }
  150. LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children)
  151. {
  152. if (!mParentFolder)
  153. {
  154. return NULL;
  155. }
  156. LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children );
  157. while(itemp && !itemp->getVisible())
  158. {
  159. LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children );
  160. if (itemp == next_itemp)
  161. {
  162. // hit last item
  163. return itemp->getVisible() ? itemp : this;
  164. }
  165. itemp = next_itemp;
  166. }
  167. return itemp;
  168. }
  169. LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children)
  170. {
  171. if (!mParentFolder)
  172. {
  173. return NULL;
  174. }
  175. LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children );
  176. // Skip over items that are invisible or are hidden from the UI.
  177. while(itemp && !itemp->getVisible())
  178. {
  179. LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children );
  180. if (itemp == next_itemp)
  181. {
  182. // hit first item
  183. return itemp->getVisible() ? itemp : this;
  184. }
  185. itemp = next_itemp;
  186. }
  187. return itemp;
  188. }
  189. // is this item something we think we should be showing?
  190. // for example, if we haven't gotten around to filtering it yet, then the answer is yes
  191. // until we find out otherwise
  192. BOOL LLFolderViewItem::potentiallyVisible()
  193. {
  194. // we haven't been checked against min required filter
  195. // or we have and we passed
  196. return getLastFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration() || getFiltered();
  197. }
  198. BOOL LLFolderViewItem::getFiltered()
  199. {
  200. return mPassedFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration();
  201. }
  202. BOOL LLFolderViewItem::getFiltered(S32 filter_generation)
  203. {
  204. return mPassedFilter && mLastFilterGeneration >= filter_generation;
  205. }
  206. void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation)
  207. {
  208. mPassedFilter = filtered;
  209. mLastFilterGeneration = filter_generation;
  210. }
  211. void LLFolderViewItem::setIcon(LLUIImagePtr icon)
  212. {
  213. mIcon = icon;
  214. }
  215. // refresh information from the listener
  216. void LLFolderViewItem::refreshFromListener()
  217. {
  218. if(mListener)
  219. {
  220. mLabel = mListener->getDisplayName();
  221. LLFolderType::EType preferred_type = mListener->getPreferredType();
  222. // *TODO: to be removed when database supports multi language. This is a
  223. // temporary attempt to display the inventory folder in the user locale.
  224. // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID
  225. // it uses the same way to find localized string
  226. // HACK: EXT - 6028 ([HARD CODED]? Inventory > Library > "Accessories" folder)
  227. // Translation of Accessories folder in Library inventory folder
  228. bool accessories = false;
  229. if(mLabel == std::string("Accessories"))
  230. {
  231. //To ensure that Accessories folder is in Library we have to check its parent folder.
  232. //Due to parent LLFolderViewFloder is not set to this item yet we have to check its parent via Inventory Model
  233. LLInventoryCategory* cat = gInventory.getCategory(mListener->getUUID());
  234. if(cat)
  235. {
  236. const LLUUID& parent_folder_id = cat->getParentUUID();
  237. accessories = (parent_folder_id == gInventory.getLibraryRootFolderID());
  238. }
  239. }
  240. //"Accessories" inventory category has folder type FT_NONE. So, this folder
  241. //can not be detected as protected with LLFolderType::lookupIsProtectedType
  242. if (accessories || LLFolderType::lookupIsProtectedType(preferred_type))
  243. {
  244. LLTrans::findString(mLabel, "InvFolder " + mLabel);
  245. };
  246. setToolTip(mLabel);
  247. setIcon(mListener->getIcon());
  248. time_t creation_date = mListener->getCreationDate();
  249. if ((creation_date > 0) && (mCreationDate != creation_date))
  250. {
  251. setCreationDate(creation_date);
  252. dirtyFilter();
  253. }
  254. if (mRoot->useLabelSuffix())
  255. {
  256. mLabelStyle = mListener->getLabelStyle();
  257. mLabelSuffix = mListener->getLabelSuffix();
  258. }
  259. }
  260. }
  261. void LLFolderViewItem::refresh()
  262. {
  263. refreshFromListener();
  264. std::string searchable_label(mLabel);
  265. searchable_label.append(mLabelSuffix);
  266. LLStringUtil::toUpper(searchable_label);
  267. if (mSearchableLabel.compare(searchable_label))
  268. {
  269. mSearchableLabel.assign(searchable_label);
  270. dirtyFilter();
  271. // some part of label has changed, so overall width has potentially changed, and sort order too
  272. if (mParentFolder)
  273. {
  274. mParentFolder->requestSort();
  275. mParentFolder->requestArrange();
  276. }
  277. }
  278. mLabelWidthDirty = true;
  279. }
  280. void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
  281. {
  282. functor(mListener);
  283. }
  284. // This function is called when items are added or view filters change. It's
  285. // implemented here but called by derived classes when folding the
  286. // views.
  287. void LLFolderViewItem::filterFromRoot( void )
  288. {
  289. LLFolderViewItem* root = getRoot();
  290. root->filter(*((LLFolderView*)root)->getFilter());
  291. }
  292. // This function is called when the folder view is dirty. It's
  293. // implemented here but called by derived classes when folding the
  294. // views.
  295. void LLFolderViewItem::arrangeFromRoot()
  296. {
  297. LLFolderViewItem* root = getRoot();
  298. S32 height = 0;
  299. S32 width = 0;
  300. S32 total_height = root->arrange( &width, &height, 0 );
  301. LLSD params;
  302. params["action"] = "size_changes";
  303. params["height"] = total_height;
  304. getParent()->notifyParent(params);
  305. }
  306. // Utility function for LLFolderView
  307. void LLFolderViewItem::arrangeAndSet(BOOL set_selection,
  308. BOOL take_keyboard_focus)
  309. {
  310. LLFolderView* root = getRoot();
  311. if (getParentFolder())
  312. {
  313. getParentFolder()->requestArrange();
  314. }
  315. if(set_selection)
  316. {
  317. setSelectionFromRoot(this, TRUE, take_keyboard_focus);
  318. if(root)
  319. {
  320. root->scrollToShowSelection();
  321. }
  322. }
  323. }
  324. // This function clears the currently selected item, and records the
  325. // specified selected item appropriately for display and use in the
  326. // UI. If open is TRUE, then folders are opened up along the way to
  327. // the selection.
  328. void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection,
  329. BOOL openitem,
  330. BOOL take_keyboard_focus)
  331. {
  332. getRoot()->setSelection(selection, openitem, take_keyboard_focus);
  333. }
  334. // helper function to change the selection from the root.
  335. void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected)
  336. {
  337. getRoot()->changeSelection(selection, selected);
  338. }
  339. std::set<LLUUID> LLFolderViewItem::getSelectionList() const
  340. {
  341. std::set<LLUUID> selection;
  342. return selection;
  343. }
  344. EInventorySortGroup LLFolderViewItem::getSortGroup() const
  345. {
  346. return SG_ITEM;
  347. }
  348. // addToFolder() returns TRUE if it succeeds. FALSE otherwise
  349. BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
  350. {
  351. if (!folder)
  352. {
  353. return FALSE;
  354. }
  355. mParentFolder = folder;
  356. root->addItemID(getListener()->getUUID(), this);
  357. return folder->addItem(this);
  358. }
  359. // Finds width and height of this object and it's children. Also
  360. // makes sure that this view and it's children are the right size.
  361. S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation)
  362. {
  363. const Params& p = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();
  364. S32 indentation = p.folder_indentation();
  365. // Only indent deeper items in hierarchy
  366. mIndentation = (getParentFolder()
  367. && getParentFolder()->getParentFolder() )
  368. ? mParentFolder->getIndentation() + indentation
  369. : 0;
  370. if (mLabelWidthDirty)
  371. {
  372. mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + getLabelFontForStyle(mLabelStyle)->getWidth(mSearchableLabel);
  373. mLabelWidthDirty = false;
  374. }
  375. *width = llmax(*width, mLabelWidth + mIndentation);
  376. // determine if we need to use ellipses to avoid horizontal scroll. EXT-719
  377. bool use_ellipses = getRoot()->getUseEllipses();
  378. if (use_ellipses)
  379. {
  380. // limit to set rect to avoid horizontal scrollbar
  381. *width = llmin(*width, getRoot()->getRect().getWidth());
  382. }
  383. *height = getItemHeight();
  384. return *height;
  385. }
  386. S32 LLFolderViewItem::getItemHeight()
  387. {
  388. return mItemHeight;
  389. }
  390. void LLFolderViewItem::filter( LLInventoryFilter& filter)
  391. {
  392. const BOOL previous_passed_filter = mPassedFilter;
  393. const BOOL passed_filter = filter.check(this);
  394. // If our visibility will change as a result of this filter, then
  395. // we need to be rearranged in our parent folder
  396. if (mParentFolder)
  397. {
  398. if (getVisible() != passed_filter
  399. || previous_passed_filter != passed_filter )
  400. mParentFolder->requestArrange();
  401. }
  402. setFiltered(passed_filter, filter.getCurrentGeneration());
  403. mStringMatchOffset = filter.getStringMatchOffset();
  404. filter.decrementFilterCount();
  405. if (getRoot()->getDebugFilters())
  406. {
  407. mStatusText = llformat("%d", mLastFilterGeneration);
  408. }
  409. }
  410. void LLFolderViewItem::dirtyFilter()
  411. {
  412. mLastFilterGeneration = -1;
  413. // bubble up dirty flag all the way to root
  414. if (getParentFolder())
  415. {
  416. getParentFolder()->setCompletedFilterGeneration(-1, TRUE);
  417. }
  418. }
  419. // *TODO: This can be optimized a lot by simply recording that it is
  420. // selected in the appropriate places, and assuming that set selection
  421. // means 'deselect' for a leaf item. Do this optimization after
  422. // multiple selection is implemented to make sure it all plays nice
  423. // together.
  424. BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus)
  425. {
  426. if (selection == this && !mIsSelected)
  427. {
  428. selectItem();
  429. }
  430. else if (mIsSelected) // Deselect everything else.
  431. {
  432. deselectItem();
  433. }
  434. return mIsSelected;
  435. }
  436. BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selected)
  437. {
  438. if (selection == this)
  439. {
  440. if (mIsSelected)
  441. {
  442. deselectItem();
  443. }
  444. else
  445. {
  446. selectItem();
  447. }
  448. return TRUE;
  449. }
  450. return FALSE;
  451. }
  452. void LLFolderViewItem::deselectItem(void)
  453. {
  454. mIsSelected = FALSE;
  455. }
  456. void LLFolderViewItem::selectItem(void)
  457. {
  458. if (mIsSelected == FALSE)
  459. {
  460. if (mListener)
  461. {
  462. mListener->selectItem();
  463. }
  464. mIsSelected = TRUE;
  465. }
  466. }
  467. BOOL LLFolderViewItem::isMovable()
  468. {
  469. if( mListener )
  470. {
  471. return mListener->isItemMovable();
  472. }
  473. else
  474. {
  475. return TRUE;
  476. }
  477. }
  478. BOOL LLFolderViewItem::isRemovable()
  479. {
  480. if( mListener )
  481. {
  482. return mListener->isItemRemovable();
  483. }
  484. else
  485. {
  486. return TRUE;
  487. }
  488. }
  489. void LLFolderViewItem::destroyView()
  490. {
  491. if (mParentFolder)
  492. {
  493. // removeView deletes me
  494. mParentFolder->removeView(this);
  495. }
  496. }
  497. // Call through to the viewed object and return true if it can be
  498. // removed.
  499. //BOOL LLFolderViewItem::removeRecursively(BOOL single_item)
  500. BOOL LLFolderViewItem::remove()
  501. {
  502. if(!isRemovable())
  503. {
  504. return FALSE;
  505. }
  506. if(mListener)
  507. {
  508. return mListener->removeItem();
  509. }
  510. return TRUE;
  511. }
  512. // Build an appropriate context menu for the item.
  513. void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags)
  514. {
  515. if(mListener)
  516. {
  517. mListener->buildContextMenu(menu, flags);
  518. }
  519. }
  520. void LLFolderViewItem::openItem( void )
  521. {
  522. if( mListener )
  523. {
  524. mListener->openItem();
  525. }
  526. }
  527. void LLFolderViewItem::preview( void )
  528. {
  529. if (mListener)
  530. {
  531. mListener->previewItem();
  532. }
  533. }
  534. void LLFolderViewItem::rename(const std::string& new_name)
  535. {
  536. if( !new_name.empty() )
  537. {
  538. if( mListener )
  539. {
  540. mListener->renameItem(new_name);
  541. if(mParentFolder)
  542. {
  543. mParentFolder->requestSort();
  544. }
  545. }
  546. }
  547. }
  548. const std::string& LLFolderViewItem::getSearchableLabel() const
  549. {
  550. return mSearchableLabel;
  551. }
  552. LLViewerInventoryItem * LLFolderViewItem::getInventoryItem(void)
  553. {
  554. if (!getListener()) return NULL;
  555. return gInventory.getItem(getListener()->getUUID());
  556. }
  557. const std::string& LLFolderViewItem::getName( void ) const
  558. {
  559. if(mListener)
  560. {
  561. return mListener->getName();
  562. }
  563. return mLabel;
  564. }
  565. // LLView functionality
  566. BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask )
  567. {
  568. if(!mIsSelected)
  569. {
  570. setSelectionFromRoot(this, FALSE);
  571. }
  572. make_ui_sound("UISndClick");
  573. return TRUE;
  574. }
  575. BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask )
  576. {
  577. if (LLView::childrenHandleMouseDown(x, y, mask))
  578. {
  579. return TRUE;
  580. }
  581. // No handler needed for focus lost since this class has no
  582. // state that depends on it.
  583. gFocusMgr.setMouseCapture( this );
  584. if (!mIsSelected)
  585. {
  586. if(mask & MASK_CONTROL)
  587. {
  588. changeSelectionFromRoot(this, !mIsSelected);
  589. }
  590. else if (mask & MASK_SHIFT)
  591. {
  592. getParentFolder()->extendSelectionTo(this);
  593. }
  594. else
  595. {
  596. setSelectionFromRoot(this, FALSE);
  597. }
  598. make_ui_sound("UISndClick");
  599. }
  600. else
  601. {
  602. mSelectPending = TRUE;
  603. }
  604. if( isMovable() )
  605. {
  606. S32 screen_x;
  607. S32 screen_y;
  608. localPointToScreen(x, y, &screen_x, &screen_y );
  609. LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y );
  610. }
  611. return TRUE;
  612. }
  613. BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
  614. {
  615. mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight));
  616. if( hasMouseCapture() && isMovable() )
  617. {
  618. S32 screen_x;
  619. S32 screen_y;
  620. localPointToScreen(x, y, &screen_x, &screen_y );
  621. BOOL can_drag = TRUE;
  622. if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) )
  623. {
  624. LLFolderView* root = getRoot();
  625. if(root->getCurSelectedItem())
  626. {
  627. LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD;
  628. // *TODO: push this into listener and remove
  629. // dependency on llagent
  630. if (mListener
  631. && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getRootFolderID()))
  632. {
  633. src = LLToolDragAndDrop::SOURCE_AGENT;
  634. }
  635. else if (mListener
  636. && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getLibraryRootFolderID()))
  637. {
  638. src = LLToolDragAndDrop::SOURCE_LIBRARY;
  639. }
  640. can_drag = root->startDrag(src);
  641. if (can_drag)
  642. {
  643. // if (mListener) mListener->startDrag();
  644. // RN: when starting drag and drop, clear out last auto-open
  645. root->autoOpenTest(NULL);
  646. root->setShowSelectionContext(TRUE);
  647. // Release keyboard focus, so that if stuff is dropped into the
  648. // world, pressing the delete key won't blow away the inventory
  649. // item.
  650. gFocusMgr.setKeyboardFocus(NULL);
  651. return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask );
  652. }
  653. }
  654. }
  655. if (can_drag)
  656. {
  657. gViewerWindow->setCursor(UI_CURSOR_ARROW);
  658. }
  659. else
  660. {
  661. gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
  662. }
  663. return TRUE;
  664. }
  665. else
  666. {
  667. getRoot()->setShowSelectionContext(FALSE);
  668. gViewerWindow->setCursor(UI_CURSOR_ARROW);
  669. // let parent handle this then...
  670. return FALSE;
  671. }
  672. }
  673. BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask )
  674. {
  675. preview();
  676. return TRUE;
  677. }
  678. BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )
  679. {
  680. if (LLView::childrenHandleMouseUp(x, y, mask))
  681. {
  682. return TRUE;
  683. }
  684. // if mouse hasn't moved since mouse down...
  685. if ( pointInView(x, y) && mSelectPending )
  686. {
  687. //...then select
  688. if(mask & MASK_CONTROL)
  689. {
  690. changeSelectionFromRoot(this, !mIsSelected);
  691. }
  692. else if (mask & MASK_SHIFT)
  693. {
  694. getParentFolder()->extendSelectionTo(this);
  695. }
  696. else
  697. {
  698. setSelectionFromRoot(this, FALSE);
  699. }
  700. }
  701. mSelectPending = FALSE;
  702. if( hasMouseCapture() )
  703. {
  704. getRoot()->setShowSelectionContext(FALSE);
  705. gFocusMgr.setMouseCapture( NULL );
  706. }
  707. return TRUE;
  708. }
  709. void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask)
  710. {
  711. mIsMouseOverTitle = false;
  712. }
  713. BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
  714. EDragAndDropType cargo_type,
  715. void* cargo_data,
  716. EAcceptance* accept,
  717. std::string& tooltip_msg)
  718. {
  719. BOOL accepted = FALSE;
  720. BOOL handled = FALSE;
  721. if(mListener)
  722. {
  723. accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg);
  724. handled = accepted;
  725. if (accepted)
  726. {
  727. mDragAndDropTarget = TRUE;
  728. *accept = ACCEPT_YES_MULTI;
  729. }
  730. else
  731. {
  732. *accept = ACCEPT_NO;
  733. }
  734. }
  735. if(mParentFolder && !handled)
  736. {
  737. // store this item to get it in LLFolderBridge::dragItemIntoFolder on drop event.
  738. mRoot->setDraggingOverItem(this);
  739. handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg);
  740. mRoot->setDraggingOverItem(NULL);
  741. }
  742. if (handled)
  743. {
  744. lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl;
  745. }
  746. return handled;
  747. }
  748. void LLFolderViewItem::draw()
  749. {
  750. static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
  751. static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);
  752. static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);
  753. static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);
  754. static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE);
  755. static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE);
  756. static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE);
  757. static LLUIColor sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE);
  758. static LLUIColor sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE);
  759. static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);
  760. static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE);
  761. const Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();
  762. const S32 TOP_PAD = default_params.item_top_pad;
  763. const S32 FOCUS_LEFT = 1;
  764. const LLFontGL* font = getLabelFontForStyle(mLabelStyle);
  765. const BOOL in_inventory = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getRootFolderID());
  766. const BOOL in_library = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getLibraryRootFolderID());
  767. //--------------------------------------------------------------------------------//
  768. // Draw open folder arrow
  769. //
  770. const bool up_to_date = mListener && mListener->isUpToDate();
  771. const bool possibly_has_children = ((up_to_date && hasVisibleChildren()) // we fetched our children and some of them have passed the filter...
  772. || (!up_to_date && mListener && mListener->hasChildren())); // ...or we know we have children but haven't fetched them (doesn't obey filter)
  773. if (possibly_has_children)
  774. {
  775. LLUIImage* arrow_image = default_params.folder_arrow_image;
  776. gl_draw_scaled_rotated_image(
  777. mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD - TOP_PAD,
  778. ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor);
  779. }
  780. //--------------------------------------------------------------------------------//
  781. // Draw highlight for selected items
  782. //
  783. const BOOL show_context = getRoot()->getShowSelectionContext();
  784. const BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); // If we have keyboard focus, draw selection filled
  785. const S32 focus_top = getRect().getHeight();
  786. const S32 focus_bottom = getRect().getHeight() - mItemHeight;
  787. const bool folder_open = (getRect().getHeight() > mItemHeight + 4);
  788. if (mIsSelected) // always render "current" item. Only render other selected items if mShowSingleSelection is FALSE
  789. {
  790. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  791. LLColor4 bg_color = sHighlightBgColor;
  792. if (!mIsCurSelection)
  793. {
  794. // do time-based fade of extra objects
  795. F32 fade_time = getRoot()->getSelectionFadeElapsedTime();
  796. if (getRoot()->getShowSingleSelection())
  797. {
  798. // fading out
  799. bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f);
  800. }
  801. else
  802. {
  803. // fading in
  804. bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);
  805. }
  806. }
  807. gl_rect_2d(FOCUS_LEFT,
  808. focus_top,
  809. getRect().getWidth() - 2,
  810. focus_bottom,
  811. bg_color, filled);
  812. if (mIsCurSelection)
  813. {
  814. gl_rect_2d(FOCUS_LEFT,
  815. focus_top,
  816. getRect().getWidth() - 2,
  817. focus_bottom,
  818. sFocusOutlineColor, FALSE);
  819. }
  820. if (folder_open)
  821. {
  822. gl_rect_2d(FOCUS_LEFT,
  823. focus_bottom + 1, // overlap with bottom edge of above rect
  824. getRect().getWidth() - 2,
  825. 0,
  826. sFocusOutlineColor, FALSE);
  827. if (show_context)
  828. {
  829. gl_rect_2d(FOCUS_LEFT,
  830. focus_bottom + 1,
  831. getRect().getWidth() - 2,
  832. 0,
  833. sHighlightBgColor, TRUE);
  834. }
  835. }
  836. }
  837. else if (mIsMouseOverTitle)
  838. {
  839. gl_rect_2d(FOCUS_LEFT,
  840. focus_top,
  841. getRect().getWidth() - 2,
  842. focus_bottom,
  843. sMouseOverColor, FALSE);
  844. }
  845. //--------------------------------------------------------------------------------//
  846. // Draw DragNDrop highlight
  847. //
  848. if (mDragAndDropTarget)
  849. {
  850. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  851. gl_rect_2d(FOCUS_LEFT,
  852. focus_top,
  853. getRect().getWidth() - 2,
  854. focus_bottom,
  855. sHighlightBgColor, FALSE);
  856. if (folder_open)
  857. {
  858. gl_rect_2d(FOCUS_LEFT,
  859. focus_bottom + 1, // overlap with bottom edge of above rect
  860. getRect().getWidth() - 2,
  861. 0,
  862. sHighlightBgColor, FALSE);
  863. }
  864. mDragAndDropTarget = FALSE;
  865. }
  866. const LLViewerInventoryItem *item = getInventoryItem();
  867. const BOOL highlight_link = mIconOverlay && item && item->getIsLinkType();
  868. //--------------------------------------------------------------------------------//
  869. // Draw open icon
  870. //
  871. const S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD;
  872. if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) // For open folders
  873. {
  874. mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1);
  875. }
  876. else if (mIcon)
  877. {
  878. mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1);
  879. }
  880. if (highlight_link)
  881. {
  882. mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1);
  883. }
  884. //--------------------------------------------------------------------------------//
  885. // Exit if no label to draw
  886. //
  887. if (mLabel.empty())
  888. {
  889. return;
  890. }
  891. LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor;
  892. if (highlight_link) color = sLinkColor;
  893. if (in_library) color = sLibraryColor;
  894. F32 right_x = 0;
  895. F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD;
  896. F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation);
  897. //--------------------------------------------------------------------------------//
  898. // Highlight filtered text
  899. //
  900. if (getRoot()->getDebugFilters())
  901. {
  902. if (!getFiltered() && !possibly_has_children)
  903. {
  904. color.mV[VALPHA] *= 0.5f;
  905. }
  906. LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ?
  907. LLColor4(0.5f, 0.8f, 0.5f, 1.f) :
  908. LLColor4(0.8f, 0.5f, 0.5f, 1.f);
  909. LLFontGL::getFontMonospace()->renderUTF8(mStatusText, 0, text_left, y, filter_color,
  910. LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
  911. S32_MAX, S32_MAX, &right_x, FALSE );
  912. text_left = right_x;
  913. }
  914. //--------------------------------------------------------------------------------//
  915. // Draw the actual label text
  916. //
  917. font->renderUTF8(mLabel, 0, text_left, y, color,
  918. LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
  919. S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE);
  920. //--------------------------------------------------------------------------------//
  921. // Draw "Loading..." text
  922. //
  923. bool root_is_loading = false;
  924. if (in_inventory)
  925. {
  926. root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress();
  927. }
  928. if (in_library)
  929. {
  930. root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress();
  931. }
  932. if ((mIsLoading
  933. && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime"))
  934. || (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive()
  935. && root_is_loading
  936. && mShowLoadStatus))
  937. {
  938. std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) ";
  939. font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor,
  940. LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
  941. S32_MAX, S32_MAX, &right_x, FALSE);
  942. }
  943. //--------------------------------------------------------------------------------//
  944. // Draw label suffix
  945. //
  946. if (!mLabelSuffix.empty())
  947. {
  948. font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor,
  949. LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
  950. S32_MAX, S32_MAX, &right_x, FALSE );
  951. }
  952. //--------------------------------------------------------------------------------//
  953. // Highlight string match
  954. //
  955. if (mStringMatchOffset != std::string::npos)
  956. {
  957. // don't draw backgrounds for zero-length strings
  958. S32 filter_string_length = getRoot()->getFilterSubString().size();
  959. if (filter_string_length > 0)
  960. {
  961. std::string combined_string = mLabel + mLabelSuffix;
  962. S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1;
  963. S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2;
  964. S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
  965. S32 top = getRect().getHeight() - TOP_PAD;
  966. LLUIImage* box_image = default_params.selection_image;
  967. LLRect box_rect(left, top, right, bottom);
  968. box_image->draw(box_rect, sFilterBGColor);
  969. F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset);
  970. F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD;
  971. font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy,
  972. sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
  973. filter_string_length, S32_MAX, &right_x, FALSE );
  974. }
  975. }
  976. }
  977. ///----------------------------------------------------------------------------
  978. /// Class LLFolderViewFolder
  979. ///----------------------------------------------------------------------------
  980. LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ):
  981. LLFolderViewItem( p ), // 0 = no create time
  982. mIsOpen(FALSE),
  983. mExpanderHighlighted(FALSE),
  984. mCurHeight(0.f),
  985. mTargetHeight(0.f),
  986. mAutoOpenCountdown(0.f),
  987. mSubtreeCreationDate(0),
  988. mAmTrash(LLFolderViewFolder::UNKNOWN),
  989. mLastArrangeGeneration( -1 ),
  990. mLastCalculatedWidth(0),
  991. mCompletedFilterGeneration(-1),
  992. mMostFilteredDescendantGeneration(-1),
  993. mNeedsSort(false),
  994. mPassedFolderFilter(FALSE)
  995. {
  996. }
  997. // Destroys the object
  998. LLFolderViewFolder::~LLFolderViewFolder( void )
  999. {
  1000. // The LLView base class takes care of object destruction. make sure that we
  1001. // don't have mouse or keyboard focus
  1002. gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
  1003. }
  1004. void LLFolderViewFolder::setFilteredFolder(bool filtered, S32 filter_generation)
  1005. {
  1006. mPassedFolderFilter = filtered;
  1007. mLastFilterGeneration = filter_generation;
  1008. }
  1009. bool LLFolderViewFolder::getFilteredFolder(S32 filter_generation)
  1010. {
  1011. return mPassedFolderFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration();
  1012. }
  1013. // addToFolder() returns TRUE if it succeeds. FALSE otherwise
  1014. BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
  1015. {
  1016. if (!folder)
  1017. {
  1018. return FALSE;
  1019. }
  1020. mParentFolder = folder;
  1021. root->addItemID(getListener()->getUUID(), this);
  1022. return folder->addFolder(this);
  1023. }
  1024. // Finds width and height of this object and its children. Also
  1025. // makes sure that this view and its children are the right size.
  1026. S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation)
  1027. {
  1028. // sort before laying out contents
  1029. if (mNeedsSort)
  1030. {
  1031. mFolders.sort(mSortFunction);
  1032. mItems.sort(mSortFunction);
  1033. mNeedsSort = false;
  1034. }
  1035. mHasVisibleChildren = hasFilteredDescendants(filter_generation);
  1036. // calculate height as a single item (without any children), and reshapes rectangle to match
  1037. LLFolderViewItem::arrange( width, height, filter_generation );
  1038. // clamp existing animated height so as to never get smaller than a single item
  1039. mCurHeight = llmax((F32)*height, mCurHeight);
  1040. // initialize running height value as height of single item in case we have no children
  1041. *height = getItemHeight();
  1042. F32 running_height = (F32)*height;
  1043. F32 target_height = (F32)*height;
  1044. // are my children visible?
  1045. if (needsArrange())
  1046. {
  1047. // set last arrange generation first, in case children are animating
  1048. // and need to be arranged again
  1049. mLastArrangeGeneration = getRoot()->getArrangeGeneration();
  1050. if (mIsOpen)
  1051. {
  1052. // Add sizes of children
  1053. S32 parent_item_height = getRect().getHeight();
  1054. for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit)
  1055. {
  1056. LLFolderViewFolder* folderp = (*fit);
  1057. if (getRoot()->getDebugFilters())
  1058. {
  1059. folderp->setVisible(TRUE);
  1060. }
  1061. else
  1062. {
  1063. folderp->setVisible( folderp->getListener()
  1064. && (folderp->getFiltered(filter_generation)
  1065. || (folderp->getFilteredFolder(filter_generation)
  1066. && folderp->hasFilteredDescendants(filter_generation)))); // passed filter or has descendants that passed filter
  1067. }
  1068. if (folderp->getVisible())
  1069. {
  1070. S32 child_width = *width;
  1071. S32 child_height = 0;
  1072. S32 child_top = parent_item_height - llround(running_height);
  1073. target_height += folderp->arrange( &child_width, &child_height, filter_generation );
  1074. running_height += (F32)child_height;
  1075. *width = llmax(*width, child_width);
  1076. folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() );
  1077. }
  1078. }
  1079. for(items_t::iterator iit = mItems.begin();
  1080. iit != mItems.end(); ++iit)
  1081. {
  1082. LLFolderViewItem* itemp = (*iit);
  1083. if (getRoot()->getDebugFilters())
  1084. {
  1085. itemp->setVisible(TRUE);
  1086. }
  1087. else
  1088. {
  1089. itemp->setVisible(itemp->getFiltered(filter_generation));
  1090. }
  1091. if (itemp->getVisible())
  1092. {
  1093. S32 child_width = *width;
  1094. S32 child_height = 0;
  1095. S32 child_top = parent_item_height - llround(running_height);
  1096. target_height += itemp->arrange( &child_width, &child_height, filter_generation );
  1097. // don't change width, as this item is as wide as its parent folder by construction
  1098. itemp->reshape( itemp->getRect().getWidth(), child_height);
  1099. running_height += (F32)child_height;
  1100. *width = llmax(*width, child_width);
  1101. itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() );
  1102. }
  1103. }
  1104. }
  1105. mTargetHeight = target_height;
  1106. // cache this width so next time we can just return it
  1107. mLastCalculatedWidth = *width;
  1108. }
  1109. else
  1110. {
  1111. // just use existing width
  1112. *width = mLastCalculatedWidth;
  1113. }
  1114. // animate current height towards target height
  1115. if (llabs(mCurHeight - mTargetHeight) > 1.f)
  1116. {
  1117. mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT));
  1118. requestArrange();
  1119. // hide child elements that fall out of current animated height
  1120. for (folders_t::iterator iter = mFolders.begin();
  1121. iter != mFolders.end();)
  1122. {
  1123. folders_t::iterator fit = iter++;
  1124. // number of pixels that bottom of folder label is from top of parent folder
  1125. if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight()
  1126. > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
  1127. {
  1128. // hide if beyond current folder height
  1129. (*fit)->setVisible(FALSE);
  1130. }
  1131. }
  1132. for (items_t::iterator iter = mItems.begin();
  1133. iter != mItems.end();)
  1134. {
  1135. items_t::iterator iit = iter++;
  1136. // number of pixels that bottom of item label is from top of parent folder
  1137. if (getRect().getHeight() - (*iit)->getRect().mBottom
  1138. > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
  1139. {
  1140. (*iit)->setVisible(FALSE);
  1141. }
  1142. }
  1143. }
  1144. else
  1145. {
  1146. mCurHeight = mTargetHeight;
  1147. }
  1148. // don't change width as this item is already as wide as its parent folder
  1149. reshape(getRect().getWidth(),llround(mCurHeight));
  1150. // pass current height value back to parent
  1151. *height = llround(mCurHeight);
  1152. return llround(mTargetHeight);
  1153. }
  1154. BOOL LLFolderViewFolder::needsArrange()
  1155. {
  1156. return mLastArrangeGeneration < getRoot()->getArrangeGeneration();
  1157. }
  1158. void LLFolderViewFolder::requestSort()
  1159. {
  1160. mNeedsSort = true;
  1161. // whenever item order changes, we need to lay things out again
  1162. requestArrange();
  1163. }
  1164. void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up)
  1165. {
  1166. mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation);
  1167. mCompletedFilterGeneration = generation;
  1168. // only aggregate up if we are a lower (older) value
  1169. if (recurse_up
  1170. && mParentFolder
  1171. && generation < mParentFolder->getCompletedFilterGeneration())
  1172. {
  1173. mParentFolder->setCompletedFilterGeneration(generation, TRUE);
  1174. }
  1175. }
  1176. void LLFolderViewFolder::filter( LLInventoryFilter& filter)
  1177. {
  1178. S32 filter_generation = filter.getCurrentGeneration();
  1179. // if failed to pass filter newer than must_pass_generation
  1180. // you will automatically fail this time, so we only
  1181. // check against items that have passed the filter
  1182. S32 must_pass_generation = filter.getMustPassGeneration();
  1183. bool autoopen_folders = (filter.hasFilterString());
  1184. // if we have already been filtered against this generation, skip out
  1185. if (getCompletedFilterGeneration() >= filter_generation)
  1186. {
  1187. return;
  1188. }
  1189. // filter folder itself
  1190. if (getLastFilterGeneration() < filter_generation)
  1191. {
  1192. if (getLastFilterGeneration() >= must_pass_generation // folder has been compared to a valid precursor filter
  1193. && !mPassedFilter) // and did not pass the filter
  1194. {
  1195. // go ahead and flag this folder as done
  1196. mLastFilterGeneration = filter_generation;
  1197. }
  1198. else // filter self only on first pass through
  1199. {
  1200. // filter against folder rules
  1201. filterFolder(filter);
  1202. // and then item rules
  1203. LLFolderViewItem::filter( filter );
  1204. }
  1205. }
  1206. if (getRoot()->getDebugFilters())
  1207. {
  1208. mStatusText = llformat("%d", mLastFilterGeneration);
  1209. mStatusText += llformat("(%d)", mCompletedFilterGeneration);
  1210. mStatusText += llformat("+%d", mMostFilteredDescendantGeneration);
  1211. }
  1212. // all descendants have been filtered later than must pass generation
  1213. // but none passed
  1214. if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation))
  1215. {
  1216. // don't traverse children if we've already filtered them since must_pass_generation
  1217. // and came back with nothing
  1218. return;
  1219. }
  1220. // we entered here with at least one filter iteration left
  1221. // check to see if we have any more before continuing on to children
  1222. if (filter.getFilterCount() < 0)
  1223. {
  1224. return;
  1225. }
  1226. // when applying a filter, matching folders get their contents downloaded first
  1227. if (filter.isNotDefault()
  1228. && getFiltered(filter.getMinRequiredGeneration())
  1229. && (mListener
  1230. && !gInventory.isCategoryComplete(mListener->getUUID())))
  1231. {
  1232. LLInventoryModelBackgroundFetch::instance().start(mListener->getUUID());
  1233. }
  1234. // now query children
  1235. for (folders_t::iterator iter = mFolders.begin();
  1236. iter != mFolders.end();
  1237. ++iter)
  1238. {
  1239. LLFolderViewFolder* folder = (*iter);
  1240. // have we run out of iterations this frame?
  1241. if (filter.getFilterCount() < 0)
  1242. {
  1243. break;
  1244. }
  1245. // mMostFilteredDescendantGeneration might have been reset
  1246. // in which case we need to update it even for folders that
  1247. // don't need to be filtered anymore
  1248. if (folder->getCompletedFilterGeneration() >= filter_generation)
  1249. {
  1250. // track latest generation to pass any child items
  1251. if (folder->getFiltered() || folder->hasFilteredDescendants(filter.getMinRequiredGeneration()))
  1252. {
  1253. mMostFilteredDescendantGeneration = filter_generation;
  1254. requestArrange();
  1255. }
  1256. // just skip it, it has already been filtered
  1257. continue;
  1258. }
  1259. // update this folders filter status (and children)
  1260. folder->filter( filter );
  1261. // track latest generation to pass any child items
  1262. if (folder->getFiltered() || folder->hasFilteredDescendants(filter_generation))
  1263. {
  1264. mMostFilteredDescendantGeneration = filter_generation;
  1265. requestArrange();
  1266. if (getRoot()->needsAutoSelect() && autoopen_folders)
  1267. {
  1268. folder->setOpenArrangeRecursively(TRUE);
  1269. }
  1270. }
  1271. }
  1272. for (items_t::iterator iter = mItems.begin();
  1273. iter != mItems.end();
  1274. ++iter)
  1275. {
  1276. LLFolderViewItem* item = (*iter);
  1277. if (filter.getFilterCount() < 0)
  1278. {
  1279. break;
  1280. }
  1281. if (item->getLastFilterGeneration() >= filter_generation)
  1282. {
  1283. if (item->getFiltered())
  1284. {
  1285. mMostFilteredDescendantGeneration = filter_generation;
  1286. requestArrange();
  1287. }
  1288. continue;
  1289. }
  1290. if (item->getLastFilterGeneration() >= must_pass_generation &&
  1291. !item->getFiltered(must_pass_generation))
  1292. {
  1293. // failed to pass an earlier filter that was a subset of the current one
  1294. // go ahead and flag this item as done
  1295. item->setFiltered(FALSE, filter_generation);
  1296. continue;
  1297. }
  1298. item->filter( filter );
  1299. if (item->getFiltered(filter.getMinRequiredGeneration()))
  1300. {
  1301. mMostFilteredDescendantGeneration = filter_generation;
  1302. requestArrange();
  1303. }
  1304. }
  1305. // if we didn't use all filter iterations
  1306. // that means we filtered all of our descendants
  1307. // instead of exhausting the filter count for this frame
  1308. if (filter.getFilterCount() > 0)
  1309. {
  1310. // flag this folder as having completed filter pass for all descendants
  1311. setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/);
  1312. }
  1313. }
  1314. void LLFolderViewFolder::filterFolder(LLInventoryFilter& filter)
  1315. {
  1316. const BOOL previous_passed_filter = mPassedFolderFilter;
  1317. const BOOL passed_filter = filter.checkFolder(this);
  1318. // If our visibility will change as a result of this filter, then
  1319. // we need to be rearranged in our parent folder
  1320. if (mParentFolder)
  1321. {
  1322. if (getVisible() != passed_filter
  1323. || previous_passed_filter != passed_filter )
  1324. {
  1325. mParentFolder->requestArrange();
  1326. }
  1327. }
  1328. setFilteredFolder(passed_filter, filter.getCurrentGeneration());
  1329. filter.decrementFilterCount();
  1330. if (getRoot()->getDebugFilters())
  1331. {
  1332. mStatusText = llformat("%d", mLastFilterGeneration);
  1333. }
  1334. }
  1335. void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation)
  1336. {
  1337. // if this folder is now filtered, but wasn't before
  1338. // (it just passed)
  1339. if (filtered && !mPassedFilter)
  1340. {
  1341. // reset current height, because last time we drew it
  1342. // it might have had more visible items than now
  1343. mCurHeight = 0.f;
  1344. }
  1345. LLFolderViewItem::setFiltered(filtered, filter_generation);
  1346. }
  1347. void LLFolderViewFolder::dirtyFilter()
  1348. {
  1349. // we're a folder, so invalidate our completed generation
  1350. setCompletedFilterGeneration(-1, FALSE);
  1351. LLFolderViewItem::dirtyFilter();
  1352. }
  1353. BOOL LLFolderViewFolder::getFiltered()
  1354. {
  1355. return getFilteredFolder(getRoot()->getFilter()->getMinRequiredGeneration())
  1356. && LLFolderViewItem::getFiltered();
  1357. }
  1358. BOOL LLFolderViewFolder::getFiltered(S32 filter_generation)
  1359. {
  1360. return getFilteredFolder(filter_generation) && LLFolderViewItem::getFiltered(filter_generation);
  1361. }
  1362. BOOL LLFolderViewFolder::hasFilteredDescendants(S32 filter_generation)
  1363. {
  1364. return mMostFilteredDescendantGeneration >= filter_generation;
  1365. }
  1366. BOOL LLFolderViewFolder::hasFilteredDescendants()
  1367. {
  1368. return mMostFilteredDescendantGeneration >= getRoot()->getFilter()->getCurrentGeneration();
  1369. }
  1370. // Passes selection information on to children and record selection
  1371. // information if necessary.
  1372. BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem,
  1373. BOOL take_keyboard_focus)
  1374. {
  1375. BOOL rv = FALSE;
  1376. if (selection == this)
  1377. {
  1378. if (!isSelected())
  1379. {
  1380. selectItem();
  1381. }
  1382. rv = TRUE;
  1383. }
  1384. else
  1385. {
  1386. if (isSelected())
  1387. {
  1388. deselectItem();
  1389. }
  1390. rv = FALSE;
  1391. }
  1392. BOOL child_selected = FALSE;
  1393. for (folders_t::iterator iter = mFolders.begin();
  1394. iter != mFolders.end();)
  1395. {
  1396. folders_t::iterator fit = iter++;
  1397. if((*fit)->setSelection(selection, openitem, take_keyboard_focus))
  1398. {
  1399. rv = TRUE;
  1400. child_selected = TRUE;
  1401. }
  1402. }
  1403. for (items_t::iterator iter = mItems.begin();
  1404. iter != mItems.end();)
  1405. {
  1406. items_t::iterator iit = iter++;
  1407. if((*iit)->setSelection(selection, openitem, take_keyboard_focus))
  1408. {
  1409. rv = TRUE;
  1410. child_selected = TRUE;
  1411. }
  1412. }
  1413. if(openitem && child_selected)
  1414. {
  1415. setOpenArrangeRecursively(TRUE);
  1416. }
  1417. return rv;
  1418. }
  1419. // This method is used to change the selection of an item.
  1420. // Recursively traverse all children; if 'selection' is 'this' then change
  1421. // the select status if necessary.
  1422. // Returns TRUE if the selection state of this folder, or of a child, was changed.
  1423. BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, BOOL selected)
  1424. {
  1425. BOOL rv = FALSE;
  1426. if(selection == this)
  1427. {
  1428. if (isSelected() != selected)
  1429. {
  1430. rv = TRUE;
  1431. if (selected)
  1432. {
  1433. selectItem();
  1434. }
  1435. else
  1436. {
  1437. deselectItem();
  1438. }
  1439. }
  1440. }
  1441. for (folders_t::iterator iter = mFolders.begin();
  1442. iter != mFolders.end();)
  1443. {
  1444. folders_t::iterator fit = iter++;
  1445. if((*fit)->changeSelection(selection, selected))
  1446. {
  1447. rv = TRUE;
  1448. }
  1449. }
  1450. for (items_t::iterator iter = mItems.begin();
  1451. iter != mItems.end();)
  1452. {
  1453. items_t::iterator iit = iter++;
  1454. if((*iit)->changeSelection(selection, selected))
  1455. {
  1456. rv = TRUE;
  1457. }
  1458. }
  1459. return rv;
  1460. }
  1461. LLFolderViewFolder* LLFolderViewFolder::getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse)
  1462. {
  1463. if (!item_a->getParentFolder() || !item_b->getParentFolder()) return NULL;
  1464. std::deque<LLFolderViewFolder*> item_a_ancestors;
  1465. LLFolderViewFolder* parent = item_a->getParentFolder();
  1466. while(parent)
  1467. {
  1468. item_a_ancestors.push_back(parent);
  1469. parent = parent->getParentFolder();
  1470. }
  1471. std::deque<LLFolderViewFolder*> item_b_ancestors;
  1472. parent = item_b->getParentFolder();
  1473. while(parent)
  1474. {
  1475. item_b_ancestors.push_back(parent);
  1476. parent = parent->getParentFolder();
  1477. }
  1478. LLFolderViewFolder* common_ancestor = item_a->getRoot();
  1479. while(item_a_ancestors.size() > item_b_ancestors.size())
  1480. {
  1481. item_a = item_a_ancestors.front();
  1482. item_a_ancestors.pop_front();
  1483. }
  1484. while(item_b_ancestors.size() > item_a_ancestors.size())
  1485. {
  1486. item_b = item_b_ancestors.front();
  1487. item_b_ancestors.pop_front();
  1488. }
  1489. while(item_a_ancestors.size())
  1490. {
  1491. common_ancestor = item_a_ancestors.front();
  1492. if (item_a_ancestors.front() == item_b_ancestors.front())
  1493. {
  1494. // which came first, sibling a or sibling b?
  1495. for (folders_t::iterator it = common_ancestor->mFolders.begin(), end_it = common_ancestor->mFolders.end();
  1496. it != end_it;
  1497. ++it)
  1498. {
  1499. LLFolderViewItem* item = *it;
  1500. if (item == item_a)
  1501. {
  1502. reverse = false;
  1503. return common_ancestor;
  1504. }
  1505. if (item == item_b)
  1506. {
  1507. reverse = true;
  1508. return common_ancestor;
  1509. }
  1510. }
  1511. for (items_t::iterator it = common_ancestor->mItems.begin(), end_it = common_ancestor->mItems.end();
  1512. it != end_it;
  1513. ++it)
  1514. {
  1515. LLFolderViewItem* item = *it;
  1516. if (item == item_a)
  1517. {
  1518. reverse = false;
  1519. return common_ancestor;
  1520. }
  1521. if (item == item_b)
  1522. {
  1523. reverse = true;
  1524. return common_ancestor;
  1525. }
  1526. }
  1527. break;
  1528. }
  1529. item_a = item_a_ancestors.front();
  1530. item_a_ancestors.pop_front();
  1531. item_b = item_b_ancestors.front();
  1532. item_b_ancestors.pop_front();
  1533. }
  1534. return NULL;
  1535. }
  1536. void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector<LLFolderViewItem*>& items)
  1537. {
  1538. bool selecting = start == NULL;
  1539. if (reverse)
  1540. {
  1541. for (items_t::reverse_iterator it = mItems.rbegin(), end_it = mItems.rend();
  1542. it != end_it;
  1543. ++it)
  1544. {
  1545. if (*it == end)
  1546. {
  1547. return;
  1548. }
  1549. if (selecting)
  1550. {
  1551. items.push_back(*it);
  1552. }
  1553. if (*it == start)
  1554. {
  1555. selecting = true;
  1556. }
  1557. }
  1558. for (folders_t::reverse_iterator it = mFolders.rbegin(), end_it = mFolders.rend();
  1559. it != end_it;
  1560. ++it)
  1561. {
  1562. if (*it == end)
  1563. {
  1564. return;
  1565. }
  1566. if (selecting)
  1567. {
  1568. items.push_back(*it);
  1569. }
  1570. if (*it == start)
  1571. {
  1572. selecting = true;
  1573. }
  1574. }
  1575. }
  1576. else
  1577. {
  1578. for (folders_t::iterator it = mFolders.begin(), end_it = mFolders.end();
  1579. it != end_it;
  1580. ++it)
  1581. {
  1582. if (*it == end)
  1583. {
  1584. return;
  1585. }
  1586. if (selecting)
  1587. {
  1588. items.push_back(*it);
  1589. }
  1590. if (*it == start)
  1591. {
  1592. selecting = true;
  1593. }
  1594. }
  1595. for (items_t::iterator it = mItems.begin(), end_it = mItems.end();
  1596. it != end_it;
  1597. ++it)
  1598. {
  1599. if (*it == end)
  1600. {
  1601. return;
  1602. }
  1603. if (selecting)
  1604. {
  1605. items.push_back(*it);
  1606. }
  1607. if (*it == start)
  1608. {
  1609. selecting = true;
  1610. }
  1611. }
  1612. }
  1613. }
  1614. void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection)
  1615. {
  1616. if (getRoot()->getAllowMultiSelect() == FALSE) return;
  1617. LLFolderViewItem* cur_selected_item = getRoot()->getCurSelectedItem();
  1618. if (cur_selected_item == NULL)
  1619. {
  1620. cur_selected_item = new_selection;
  1621. }
  1622. bool reverse = false;
  1623. LLFolderViewFolder* common_ancestor = getCommonAncestor(cur_selected_item, new_selection, reverse);
  1624. if (!common_ancestor) return;
  1625. LLFolderViewItem* last_selected_item_from_cur = cur_selected_item;
  1626. LLFolderViewFolder* cur_folder = cur_selected_item->getParentFolder();
  1627. std::vector<LLFolderViewItem*> items_to_select_forward;
  1628. while(cur_folder != common_ancestor)
  1629. {
  1630. cur_folder->gatherChildRangeExclusive(last_selected_item_from_cur, NULL, reverse, items_to_select_forward);
  1631. last_selected_item_from_cur = cur_folder;
  1632. cur_folder = cur_folder->getParentFolder();
  1633. }
  1634. std::vector<LLFolderViewItem*> items_to_select_reverse;
  1635. LLFolderViewItem* last_selected_item_from_new = new_selection;
  1636. cur_folder = new_selection->getParentFolder();
  1637. while(cur_folder != common_ancestor)
  1638. {
  1639. cur_folder->gatherChildRangeExclusive(last_selected_item_from_new, NULL, !reverse, items_to_select_reverse);
  1640. last_selected_item_from_new = cur_folder;
  1641. cur_folder = cur_folder->getParentFolder();
  1642. }
  1643. common_ancestor->gatherChildRangeExc

Large files files are truncated, but you can click here to view the full file