PageRenderTime 32ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/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
  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->gatherChildRangeExclusive(last_selected_item_from_cur, last_selected_item_from_new, reverse, items_to_select_forward);
  1644. for (std::vector<LLFolderViewItem*>::reverse_iterator it = items_to_select_reverse.rbegin(), end_it = items_to_select_reverse.rend();
  1645. it != end_it;
  1646. ++it)
  1647. {
  1648. items_to_select_forward.push_back(*it);
  1649. }
  1650. LLFolderView* root = getRoot();
  1651. for (std::vector<LLFolderViewItem*>::iterator it = items_to_select_forward.begin(), end_it = items_to_select_forward.end();
  1652. it != end_it;
  1653. ++it)
  1654. {
  1655. LLFolderViewItem* item = *it;
  1656. if (item->isSelected())
  1657. {
  1658. root->removeFromSelectionList(item);
  1659. }
  1660. else
  1661. {
  1662. item->selectItem();
  1663. }
  1664. root->addToSelectionList(item);
  1665. }
  1666. if (new_selection->isSelected())
  1667. {
  1668. root->removeFromSelectionList(new_selection);
  1669. }
  1670. else
  1671. {
  1672. new_selection->selectItem();
  1673. }
  1674. root->addToSelectionList(new_selection);
  1675. }
  1676. void LLFolderViewFolder::destroyView()
  1677. {
  1678. for (items_t::iterator iter = mItems.begin();
  1679. iter != mItems.end();)
  1680. {
  1681. items_t::iterator iit = iter++;
  1682. LLFolderViewItem* item = (*iit);
  1683. getRoot()->removeItemID(item->getListener()->getUUID());
  1684. }
  1685. std::for_each(mItems.begin(), mItems.end(), DeletePointer());
  1686. mItems.clear();
  1687. while (!mFolders.empty())
  1688. {
  1689. LLFolderViewFolder *folderp = mFolders.back();
  1690. folderp->destroyView(); // removes entry from mFolders
  1691. }
  1692. //deleteAllChildren();
  1693. if (mParentFolder)
  1694. {
  1695. mParentFolder->removeView(this);
  1696. }
  1697. }
  1698. // remove the specified item (and any children) if possible. Return
  1699. // TRUE if the item was deleted.
  1700. BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item)
  1701. {
  1702. if(item->remove())
  1703. {
  1704. return TRUE;
  1705. }
  1706. return FALSE;
  1707. }
  1708. // simply remove the view (and any children) Don't bother telling the
  1709. // listeners.
  1710. void LLFolderViewFolder::removeView(LLFolderViewItem* item)
  1711. {
  1712. if (!item || item->getParentFolder() != this)
  1713. {
  1714. return;
  1715. }
  1716. // deselect without traversing hierarchy
  1717. if (item->isSelected())
  1718. {
  1719. item->deselectItem();
  1720. }
  1721. getRoot()->removeFromSelectionList(item);
  1722. extractItem(item);
  1723. delete item;
  1724. }
  1725. // extractItem() removes the specified item from the folder, but
  1726. // doesn't delete it.
  1727. void LLFolderViewFolder::extractItem( LLFolderViewItem* item )
  1728. {
  1729. items_t::iterator it = std::find(mItems.begin(), mItems.end(), item);
  1730. if(it == mItems.end())
  1731. {
  1732. // This is an evil downcast. However, it's only doing
  1733. // pointer comparison to find if (which it should be ) the
  1734. // item is in the container, so it's pretty safe.
  1735. LLFolderViewFolder* f = static_cast<LLFolderViewFolder*>(item);
  1736. folders_t::iterator ft;
  1737. ft = std::find(mFolders.begin(), mFolders.end(), f);
  1738. if (ft != mFolders.end())
  1739. {
  1740. mFolders.erase(ft);
  1741. }
  1742. }
  1743. else
  1744. {
  1745. mItems.erase(it);
  1746. }
  1747. //item has been removed, need to update filter
  1748. dirtyFilter();
  1749. //because an item is going away regardless of filter status, force rearrange
  1750. requestArrange();
  1751. getRoot()->removeItemID(item->getListener()->getUUID());
  1752. removeChild(item);
  1753. }
  1754. bool LLFolderViewFolder::isTrash() const
  1755. {
  1756. if (mAmTrash == LLFolderViewFolder::UNKNOWN)
  1757. {
  1758. mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH, false) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH;
  1759. }
  1760. return mAmTrash == LLFolderViewFolder::TRASH;
  1761. }
  1762. void LLFolderViewFolder::sortBy(U32 order)
  1763. {
  1764. if (!mSortFunction.updateSort(order))
  1765. {
  1766. // No changes.
  1767. return;
  1768. }
  1769. // Propagate this change to sub folders
  1770. for (folders_t::iterator iter = mFolders.begin();
  1771. iter != mFolders.end();)
  1772. {
  1773. folders_t::iterator fit = iter++;
  1774. (*fit)->sortBy(order);
  1775. }
  1776. // Don't sort the topmost folders (My Inventory and Library)
  1777. if (mListener->getUUID().notNull())
  1778. {
  1779. mFolders.sort(mSortFunction);
  1780. mItems.sort(mSortFunction);
  1781. }
  1782. if (order & LLInventoryFilter::SO_DATE)
  1783. {
  1784. time_t latest = 0;
  1785. if (!mItems.empty())
  1786. {
  1787. LLFolderViewItem* item = *(mItems.begin());
  1788. latest = item->getCreationDate();
  1789. }
  1790. if (!mFolders.empty())
  1791. {
  1792. LLFolderViewFolder* folder = *(mFolders.begin());
  1793. if (folder->getCreationDate() > latest)
  1794. {
  1795. latest = folder->getCreationDate();
  1796. }
  1797. }
  1798. mSubtreeCreationDate = latest;
  1799. }
  1800. }
  1801. void LLFolderViewFolder::setItemSortOrder(U32 ordering)
  1802. {
  1803. if (mSortFunction.updateSort(ordering))
  1804. {
  1805. for (folders_t::iterator iter = mFolders.begin();
  1806. iter != mFolders.end();)
  1807. {
  1808. folders_t::iterator fit = iter++;
  1809. (*fit)->setItemSortOrder(ordering);
  1810. }
  1811. mFolders.sort(mSortFunction);
  1812. mItems.sort(mSortFunction);
  1813. }
  1814. }
  1815. EInventorySortGroup LLFolderViewFolder::getSortGroup() const
  1816. {
  1817. if (isTrash())
  1818. {
  1819. return SG_TRASH_FOLDER;
  1820. }
  1821. if( mListener )
  1822. {
  1823. if(LLFolderType::lookupIsProtectedType(mListener->getPreferredType()))
  1824. {
  1825. return SG_SYSTEM_FOLDER;
  1826. }
  1827. }
  1828. return SG_NORMAL_FOLDER;
  1829. }
  1830. BOOL LLFolderViewFolder::isMovable()
  1831. {
  1832. if( mListener )
  1833. {
  1834. if( !(mListener->isItemMovable()) )
  1835. {
  1836. return FALSE;
  1837. }
  1838. for (items_t::iterator iter = mItems.begin();
  1839. iter != mItems.end();)
  1840. {
  1841. items_t::iterator iit = iter++;
  1842. if(!(*iit)->isMovable())
  1843. {
  1844. return FALSE;
  1845. }
  1846. }
  1847. for (folders_t::iterator iter = mFolders.begin();
  1848. iter != mFolders.end();)
  1849. {
  1850. folders_t::iterator fit = iter++;
  1851. if(!(*fit)->isMovable())
  1852. {
  1853. return FALSE;
  1854. }
  1855. }
  1856. }
  1857. return TRUE;
  1858. }
  1859. BOOL LLFolderViewFolder::isRemovable()
  1860. {
  1861. if( mListener )
  1862. {
  1863. if( !(mListener->isItemRemovable()) )
  1864. {
  1865. return FALSE;
  1866. }
  1867. for (items_t::iterator iter = mItems.begin();
  1868. iter != mItems.end();)
  1869. {
  1870. items_t::iterator iit = iter++;
  1871. if(!(*iit)->isRemovable())
  1872. {
  1873. return FALSE;
  1874. }
  1875. }
  1876. for (folders_t::iterator iter = mFolders.begin();
  1877. iter != mFolders.end();)
  1878. {
  1879. folders_t::iterator fit = iter++;
  1880. if(!(*fit)->isRemovable())
  1881. {
  1882. return FALSE;
  1883. }
  1884. }
  1885. }
  1886. return TRUE;
  1887. }
  1888. // this is an internal method used for adding items to folders.
  1889. BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item)
  1890. {
  1891. mItems.push_back(item);
  1892. item->setRect(LLRect(0, 0, getRect().getWidth(), 0));
  1893. item->setVisible(FALSE);
  1894. addChild(item);
  1895. item->dirtyFilter();
  1896. // Update the folder creation date if the folder has no creation date
  1897. bool setting_date = false;
  1898. const time_t item_creation_date = item->getCreationDate();
  1899. if ((item_creation_date > 0) && (mCreationDate == 0))
  1900. {
  1901. setCreationDate(item_creation_date);
  1902. setting_date = true;
  1903. }
  1904. // Handle sorting
  1905. requestArrange();
  1906. requestSort();
  1907. // Traverse parent folders and update creation date and resort, if necessary
  1908. LLFolderViewFolder* parentp = getParentFolder();
  1909. while (parentp)
  1910. {
  1911. // Update the parent folder creation date
  1912. if (setting_date && (parentp->mCreationDate == 0))
  1913. {
  1914. parentp->setCreationDate(item_creation_date);
  1915. }
  1916. if (parentp->mSortFunction.isByDate())
  1917. {
  1918. // parent folder doesn't have a time stamp yet, so get it from us
  1919. parentp->requestSort();
  1920. }
  1921. parentp = parentp->getParentFolder();
  1922. }
  1923. return TRUE;
  1924. }
  1925. // this is an internal method used for adding items to folders.
  1926. BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder)
  1927. {
  1928. mFolders.push_back(folder);
  1929. folder->setOrigin(0, 0);
  1930. folder->reshape(getRect().getWidth(), 0);
  1931. folder->setVisible(FALSE);
  1932. addChild( folder );
  1933. folder->dirtyFilter();
  1934. // rearrange all descendants too, as our indentation level might have changed
  1935. folder->requestArrange(TRUE);
  1936. requestSort();
  1937. LLFolderViewFolder* parentp = getParentFolder();
  1938. while (parentp && !parentp->mSortFunction.isByDate())
  1939. {
  1940. // parent folder doesn't have a time stamp yet, so get it from us
  1941. parentp->requestSort();
  1942. parentp = parentp->getParentFolder();
  1943. }
  1944. return TRUE;
  1945. }
  1946. void LLFolderViewFolder::requestArrange(BOOL include_descendants)
  1947. {
  1948. mLastArrangeGeneration = -1;
  1949. // flag all items up to root
  1950. if (mParentFolder)
  1951. {
  1952. mParentFolder->requestArrange();
  1953. }
  1954. if (include_descendants)
  1955. {
  1956. for (folders_t::iterator iter = mFolders.begin();
  1957. iter != mFolders.end();
  1958. ++iter)
  1959. {
  1960. (*iter)->requestArrange(TRUE);
  1961. }
  1962. }
  1963. }
  1964. void LLFolderViewFolder::toggleOpen()
  1965. {
  1966. setOpen(!mIsOpen);
  1967. }
  1968. // Force a folder open or closed
  1969. void LLFolderViewFolder::setOpen(BOOL openitem)
  1970. {
  1971. setOpenArrangeRecursively(openitem);
  1972. }
  1973. void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse)
  1974. {
  1975. BOOL was_open = mIsOpen;
  1976. mIsOpen = openitem;
  1977. if (mListener)
  1978. {
  1979. if(!was_open && openitem)
  1980. {
  1981. mListener->openItem();
  1982. }
  1983. else if(was_open && !openitem)
  1984. {
  1985. mListener->closeItem();
  1986. }
  1987. }
  1988. if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN)
  1989. {
  1990. for (folders_t::iterator iter = mFolders.begin();
  1991. iter != mFolders.end();)
  1992. {
  1993. folders_t::iterator fit = iter++;
  1994. (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN); /* Flawfinder: ignore */
  1995. }
  1996. }
  1997. if (mParentFolder
  1998. && (recurse == RECURSE_UP
  1999. || recurse == RECURSE_UP_DOWN))
  2000. {
  2001. mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP);
  2002. }
  2003. if (was_open != mIsOpen)
  2004. {
  2005. requestArrange();
  2006. }
  2007. }
  2008. BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask,
  2009. BOOL drop,
  2010. EDragAndDropType c_type,
  2011. void* cargo_data,
  2012. EAcceptance* accept,
  2013. std::string& tooltip_msg)
  2014. {
  2015. BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg);
  2016. if (accepted)
  2017. {
  2018. mDragAndDropTarget = TRUE;
  2019. *accept = ACCEPT_YES_MULTI;
  2020. }
  2021. else
  2022. {
  2023. *accept = ACCEPT_NO;
  2024. }
  2025. // drag and drop to child item, so clear pending auto-opens
  2026. getRoot()->autoOpenTest(NULL);
  2027. return TRUE;
  2028. }
  2029. void LLFolderViewFolder::openItem( void )
  2030. {
  2031. toggleOpen();
  2032. }
  2033. void LLFolderViewFolder::applyFunctorToChildren(LLFolderViewFunctor& functor)
  2034. {
  2035. for (folders_t::iterator iter = mFolders.begin();
  2036. iter != mFolders.end();)
  2037. {
  2038. folders_t::iterator fit = iter++;
  2039. functor.doItem((*fit));
  2040. }
  2041. for (items_t::iterator iter = mItems.begin();
  2042. iter != mItems.end();)
  2043. {
  2044. items_t::iterator iit = iter++;
  2045. functor.doItem((*iit));
  2046. }
  2047. }
  2048. void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor)
  2049. {
  2050. functor.doFolder(this);
  2051. for (folders_t::iterator iter = mFolders.begin();
  2052. iter != mFolders.end();)
  2053. {
  2054. folders_t::iterator fit = iter++;
  2055. (*fit)->applyFunctorRecursively(functor);
  2056. }
  2057. for (items_t::iterator iter = mItems.begin();
  2058. iter != mItems.end();)
  2059. {
  2060. items_t::iterator iit = iter++;
  2061. functor.doItem((*iit));
  2062. }
  2063. }
  2064. void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
  2065. {
  2066. functor(mListener);
  2067. for (folders_t::iterator iter = mFolders.begin();
  2068. iter != mFolders.end();)
  2069. {
  2070. folders_t::iterator fit = iter++;
  2071. (*fit)->applyListenerFunctorRecursively(functor);
  2072. }
  2073. for (items_t::iterator iter = mItems.begin();
  2074. iter != mItems.end();)
  2075. {
  2076. items_t::iterator iit = iter++;
  2077. (*iit)->applyListenerFunctorRecursively(functor);
  2078. }
  2079. }
  2080. // LLView functionality
  2081. BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask,
  2082. BOOL drop,
  2083. EDragAndDropType cargo_type,
  2084. void* cargo_data,
  2085. EAcceptance* accept,
  2086. std::string& tooltip_msg)
  2087. {
  2088. BOOL handled = FALSE;
  2089. if (mIsOpen)
  2090. {
  2091. handled = (childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL);
  2092. }
  2093. if (!handled)
  2094. {
  2095. handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
  2096. lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl;
  2097. }
  2098. return TRUE;
  2099. }
  2100. BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask,
  2101. BOOL drop,
  2102. EDragAndDropType cargo_type,
  2103. void* cargo_data,
  2104. EAcceptance* accept,
  2105. std::string& tooltip_msg)
  2106. {
  2107. BOOL accepted = mListener && mListener->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg);
  2108. if (accepted)
  2109. {
  2110. mDragAndDropTarget = TRUE;
  2111. *accept = ACCEPT_YES_MULTI;
  2112. }
  2113. else
  2114. {
  2115. *accept = ACCEPT_NO;
  2116. }
  2117. if (!drop && accepted)
  2118. {
  2119. getRoot()->autoOpenTest(this);
  2120. }
  2121. return TRUE;
  2122. }
  2123. BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask )
  2124. {
  2125. BOOL handled = FALSE;
  2126. // fetch contents of this folder, as context menu can depend on contents
  2127. // still, user would have to open context menu again to see the changes
  2128. gInventory.fetchDescendentsOf(mListener->getUUID());
  2129. if( mIsOpen )
  2130. {
  2131. handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
  2132. }
  2133. if (!handled)
  2134. {
  2135. handled = LLFolderViewItem::handleRightMouseDown( x, y, mask );
  2136. }
  2137. return handled;
  2138. }
  2139. BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask)
  2140. {
  2141. mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight));
  2142. BOOL handled = LLView::handleHover(x, y, mask);
  2143. if (!handled)
  2144. {
  2145. // this doesn't do child processing
  2146. handled = LLFolderViewItem::handleHover(x, y, mask);
  2147. }
  2148. return handled;
  2149. }
  2150. BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )
  2151. {
  2152. BOOL handled = FALSE;
  2153. if( mIsOpen )
  2154. {
  2155. handled = childrenHandleMouseDown(x,y,mask) != NULL;
  2156. }
  2157. if( !handled )
  2158. {
  2159. if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD)
  2160. {
  2161. toggleOpen();
  2162. handled = TRUE;
  2163. }
  2164. else
  2165. {
  2166. // do normal selection logic
  2167. handled = LLFolderViewItem::handleMouseDown(x, y, mask);
  2168. }
  2169. }
  2170. return handled;
  2171. }
  2172. BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask )
  2173. {
  2174. /* Disable outfit double click to wear
  2175. const LLUUID &cat_uuid = getListener()->getUUID();
  2176. const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid);
  2177. if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT)
  2178. {
  2179. getListener()->performAction(NULL, NULL,"replaceoutfit");
  2180. return TRUE;
  2181. }
  2182. */
  2183. BOOL handled = FALSE;
  2184. if( mIsOpen )
  2185. {
  2186. handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
  2187. }
  2188. if( !handled )
  2189. {
  2190. if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD)
  2191. {
  2192. // don't select when user double-clicks plus sign
  2193. // so as not to contradict single-click behavior
  2194. toggleOpen();
  2195. }
  2196. else
  2197. {
  2198. setSelectionFromRoot(this, FALSE);
  2199. toggleOpen();
  2200. }
  2201. handled = TRUE;
  2202. }
  2203. return handled;
  2204. }
  2205. void LLFolderViewFolder::draw()
  2206. {
  2207. if (mAutoOpenCountdown != 0.f)
  2208. {
  2209. mControlLabelRotation = mAutoOpenCountdown * -90.f;
  2210. }
  2211. else if (mIsOpen)
  2212. {
  2213. mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f));
  2214. }
  2215. else
  2216. {
  2217. mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f));
  2218. }
  2219. bool possibly_has_children = false;
  2220. bool up_to_date = mListener && mListener->isUpToDate();
  2221. if(!up_to_date
  2222. && mListener->hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter)
  2223. {
  2224. possibly_has_children = true;
  2225. }
  2226. BOOL loading = (mIsOpen
  2227. && possibly_has_children
  2228. && !up_to_date );
  2229. if ( loading && !mIsLoading )
  2230. {
  2231. // Measure how long we've been in the loading state
  2232. mTimeSinceRequestStart.reset();
  2233. }
  2234. mIsLoading = loading;
  2235. LLFolderViewItem::draw();
  2236. // draw children if root folder, or any other folder that is open or animating to closed state
  2237. if( getRoot() == this || (mIsOpen || mCurHeight != mTargetHeight ))
  2238. {
  2239. LLView::draw();
  2240. }
  2241. mExpanderHighlighted = FALSE;
  2242. }
  2243. time_t LLFolderViewFolder::getCreationDate() const
  2244. {
  2245. return llmax<time_t>(mCreationDate, mSubtreeCreationDate);
  2246. }
  2247. BOOL LLFolderViewFolder::potentiallyVisible()
  2248. {
  2249. // folder should be visible by it's own filter status
  2250. return LLFolderViewItem::potentiallyVisible()
  2251. // or one or more of its descendants have passed the minimum filter requirement
  2252. || hasFilteredDescendants(getRoot()->getFilter()->getMinRequiredGeneration())
  2253. // or not all of its descendants