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

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2. * @file llfolderviewitem.cpp
  3. * @brief Items and folders that can appear in a hierarchical folder view
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. * Copyright (c) 2001-2010, Linden Research, Inc.
  7. * Second Life Viewer Source Code
  8. * The source code in this file ("Source Code") is provided by Linden Lab
  9. * to you under the terms of the GNU General Public License, version 2.0
  10. * ("GPL"), unless you have obtained a separate licensing agreement
  11. * ("Other License"), formally executed by you and Linden Lab.  Terms of
  12. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  13. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  14. * There are special exceptions to the terms and conditions of the GPL as
  15. * it is applied to this Source Code. View the full text of the exception
  16. * in the file doc/FLOSS-exception.txt in this software distribution, or
  17. * online at
  18. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  19. * By copying, modifying or distributing this software, you acknowledge
  20. * that you have read and understood your obligations described above,
  21. * and agree to abide by those obligations.
  22. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  23. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  24. * COMPLETENESS OR PERFORMANCE.
  25. * $/LicenseInfo$
  26. */
  27. #include "llviewerprecompiledheaders.h"
  28. #include "llfolderviewitem.h"
  29. // viewer includes
  30. #include "llfolderview.h" // Items depend extensively on LLFolderViews
  31. #include "llfoldervieweventlistener.h"
  32. #include "llinventorybridge.h" // for LLItemBridge in LLInventorySort::operator()
  33. #include "llinventoryfilter.h"
  34. #include "llpanel.h"
  35. #include "llviewercontrol.h" // gSavedSettings
  36. #include "llviewerwindow.h" // Argh, only for setCursor()
  37. // linden library includes
  38. #include "llfocusmgr.h" // gFocusMgr
  39. #include "lltrans.h"
  40. ///----------------------------------------------------------------------------
  41. /// Class LLFolderViewItem
  42. ///----------------------------------------------------------------------------
  43. static LLDefaultChildRegistry::Register<LLFolderViewItem> r("folder_view_item");
  44. // statics 
  45. std::map<U8, LLFontGL*> LLFolderViewItem::sFonts; // map of styles to fonts
  46. // only integers can be initialized in header
  47. const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f;
  48. const F32 LLFolderViewItem::FOLDER_OPEN_TIME_CONSTANT = 0.03f;
  49. const LLColor4U DEFAULT_WHITE(255, 255, 255);
  50. //static
  51. LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style)
  52. {
  53. LLFontGL* rtn = sFonts[style];
  54. if (!rtn) // grab label font with this style, lazily
  55. {
  56. LLFontDescriptor labelfontdesc("SansSerif", "Small", style);
  57. rtn = LLFontGL::getFont(labelfontdesc);
  58. if (!rtn)
  59. {
  60. rtn = LLFontGL::getFontDefault();
  61. }
  62. sFonts[style] = rtn;
  63. }
  64. return rtn;
  65. }
  66. //static
  67. void LLFolderViewItem::initClass()
  68. {
  69. }
  70. //static
  71. void LLFolderViewItem::cleanupClass()
  72. {
  73. sFonts.clear();
  74. }
  75. // NOTE: Optimize this, we call it a *lot* when opening a large inventory
  76. LLFolderViewItem::Params::Params()
  77. : icon(),
  78. icon_open(),
  79. root(),
  80. listener(),
  81. folder_arrow_image("folder_arrow_image"),
  82. folder_indentation("folder_indentation"),
  83. selection_image("selection_image"),
  84. item_height("item_height"),
  85. item_top_pad("item_top_pad"),
  86. creation_date()
  87. {
  88. mouse_opaque(true);
  89. follows.flags(FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_RIGHT);
  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. mNumDescendantsSelected(0),
  105. mPassedFilter(FALSE),
  106. mLastFilterGeneration(-1),
  107. mStringMatchOffset(std::string::npos),
  108. mControlLabelRotation(0.f),
  109. mDragAndDropTarget(FALSE),
  110. mIsLoading(FALSE),
  111. mLabel(p.name),
  112. mRoot(p.root),
  113. mCreationDate(p.creation_date),
  114. mIcon(p.icon),
  115. mIconOpen(p.icon_open),
  116. mListener(p.listener),
  117. mHidden(false),
  118. mShowLoadStatus(false)
  119. {
  120. refresh();
  121. }
  122. // Destroys the object
  123. LLFolderViewItem::~LLFolderViewItem( void )
  124. {
  125. delete mListener;
  126. mListener = NULL;
  127. }
  128. LLFolderView* LLFolderViewItem::getRoot()
  129. {
  130. return mRoot;
  131. }
  132. // Returns true if this object is a child (or grandchild, etc.) of potential_ancestor.
  133. BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor )
  134. {
  135. LLFolderViewItem* root = this;
  136. while( root->mParentFolder )
  137. {
  138. if( root->mParentFolder == potential_ancestor )
  139. {
  140. return TRUE;
  141. }
  142. root = root->mParentFolder;
  143. }
  144. return FALSE;
  145. }
  146. LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children)
  147. {
  148. if (!mParentFolder)
  149. {
  150. return NULL;
  151. }
  152. LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children );
  153. while(itemp && !itemp->getVisible())
  154. {
  155. LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children );
  156. if (itemp == next_itemp) 
  157. {
  158. // hit last item
  159. return itemp->getVisible() ? itemp : this;
  160. }
  161. itemp = next_itemp;
  162. }
  163. return itemp;
  164. }
  165. LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children)
  166. {
  167. if (!mParentFolder)
  168. {
  169. return NULL;
  170. }
  171. LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children );
  172. // Skip over items that are invisible or are hidden from the UI.
  173. while(itemp && (!itemp->getVisible() || itemp->getHidden()))
  174. {
  175. LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children );
  176. if (itemp == next_itemp) 
  177. {
  178. // hit first item
  179. return itemp->getVisible() ? itemp : this;
  180. }
  181. itemp = next_itemp;
  182. }
  183. return itemp;
  184. }
  185. // is this item something we think we should be showing?
  186. // for example, if we haven't gotten around to filtering it yet, then the answer is yes
  187. // until we find out otherwise
  188. BOOL LLFolderViewItem::potentiallyVisible()
  189. {
  190. // we haven't been checked against min required filter
  191. // or we have and we passed
  192. return getLastFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration() || getFiltered();
  193. }
  194. BOOL LLFolderViewItem::getFiltered() 
  195. return mPassedFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); 
  196. }
  197. BOOL LLFolderViewItem::getFiltered(S32 filter_generation) 
  198. {
  199. return mPassedFilter && mLastFilterGeneration >= filter_generation;
  200. }
  201. void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation)
  202. {
  203. mPassedFilter = filtered;
  204. mLastFilterGeneration = filter_generation;
  205. }
  206. void LLFolderViewItem::setIcon(LLUIImagePtr icon)
  207. {
  208. mIcon = icon;
  209. }
  210. // refresh information from the listener
  211. void LLFolderViewItem::refreshFromListener()
  212. {
  213. if(mListener)
  214. {
  215. mLabel = mListener->getDisplayName();
  216. LLFolderType::EType preferred_type = mListener->getPreferredType();
  217. // *TODO: to be removed when database supports multi language. This is a
  218. // temporary attempt to display the inventory folder in the user locale.
  219. // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID
  220. // it uses the same way to find localized string
  221. if (LLFolderType::lookupIsProtectedType(preferred_type))
  222. {
  223. LLTrans::findString(mLabel, "InvFolder " + mLabel);
  224. };
  225. setIcon(mListener->getIcon());
  226. time_t creation_date = mListener->getCreationDate();
  227. if (mCreationDate != creation_date)
  228. {
  229. mCreationDate = mListener->getCreationDate();
  230. dirtyFilter();
  231. }
  232. mLabelStyle = mListener->getLabelStyle();
  233. mLabelSuffix = mListener->getLabelSuffix();
  234. }
  235. }
  236. void LLFolderViewItem::refresh()
  237. {
  238. refreshFromListener();
  239. std::string searchable_label(mLabel);
  240. searchable_label.append(mLabelSuffix);
  241. LLStringUtil::toUpper(searchable_label);
  242. if (mSearchableLabel.compare(searchable_label))
  243. {
  244. mSearchableLabel.assign(searchable_label);
  245. dirtyFilter();
  246. // some part of label has changed, so overall width has potentially changed, and sort order too
  247. if (mParentFolder)
  248. {
  249. mParentFolder->requestSort();
  250. mParentFolder->requestArrange();
  251. }
  252. }
  253. mLabelWidthDirty = true;
  254. }
  255. void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
  256. {
  257. functor(mListener);
  258. }
  259. // This function is called when items are added or view filters change. It's
  260. // implemented here but called by derived classes when folding the
  261. // views.
  262. void LLFolderViewItem::filterFromRoot( void )
  263. {
  264. LLFolderViewItem* root = getRoot();
  265. root->filter(*((LLFolderView*)root)->getFilter());
  266. }
  267. // This function is called when the folder view is dirty. It's
  268. // implemented here but called by derived classes when folding the
  269. // views.
  270. void LLFolderViewItem::arrangeFromRoot()
  271. {
  272. LLFolderViewItem* root = getRoot();
  273. S32 height = 0;
  274. S32 width = 0;
  275. S32 total_height = root->arrange( &width, &height, 0 );
  276. LLSD params;
  277. params["action"] = "size_changes";
  278. params["height"] = total_height;
  279. getParent()->notifyParent(params);
  280. }
  281. // Utility function for LLFolderView
  282. void LLFolderViewItem::arrangeAndSet(BOOL set_selection,
  283.  BOOL take_keyboard_focus)
  284. {
  285. LLFolderView* root = getRoot();
  286. getParentFolder()->requestArrange();
  287. if(set_selection)
  288. {
  289. setSelectionFromRoot(this, TRUE, take_keyboard_focus);
  290. if(root)
  291. {
  292. root->scrollToShowSelection();
  293. }
  294. }
  295. }
  296. // This function clears the currently selected item, and records the
  297. // specified selected item appropriately for display and use in the
  298. // UI. If open is TRUE, then folders are opened up along the way to
  299. // the selection.
  300. void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection,
  301. BOOL openitem,
  302. BOOL take_keyboard_focus)
  303. {
  304. getRoot()->setSelection(selection, openitem, take_keyboard_focus);
  305. }
  306. // helper function to change the selection from the root.
  307. void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected)
  308. {
  309. getRoot()->changeSelection(selection, selected);
  310. }
  311. void LLFolderViewItem::extendSelectionFromRoot(LLFolderViewItem* selection)
  312. {
  313. LLDynamicArray<LLFolderViewItem*> selected_items;
  314. getRoot()->extendSelection(selection, NULL, selected_items);
  315. }
  316. EInventorySortGroup LLFolderViewItem::getSortGroup()  const
  317. return SG_ITEM; 
  318. }
  319. // addToFolder() returns TRUE if it succeeds. FALSE otherwise
  320. BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
  321. {
  322. if (!folder)
  323. {
  324. return FALSE;
  325. }
  326. mParentFolder = folder;
  327. root->addItemID(getListener()->getUUID(), this);
  328. return folder->addItem(this);
  329. }
  330. // Finds width and height of this object and it's children.  Also
  331. // makes sure that this view and it's children are the right size.
  332. S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation)
  333. {
  334. const Params& p = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();
  335. S32 indentation = p.folder_indentation();
  336. // Only indent deeper items in hierarchy
  337. mIndentation = (getParentFolder() 
  338. && getParentFolder()->getParentFolder() )
  339. ? mParentFolder->getIndentation() + indentation
  340. : 0;
  341. if (mLabelWidthDirty)
  342. {
  343. mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + getLabelFontForStyle(mLabelStyle)->getWidth(mSearchableLabel); 
  344. mLabelWidthDirty = false;
  345. }
  346. *width = llmax(*width, mLabelWidth + mIndentation); 
  347. // determine if we need to use ellipses to avoid horizontal scroll. EXT-719
  348. bool use_ellipses = getRoot()->getUseEllipses();
  349. if (use_ellipses)
  350. {
  351. // limit to set rect to avoid horizontal scrollbar
  352. *width = llmin(*width, getRoot()->getRect().getWidth());
  353. }
  354. *height = getItemHeight();
  355. return *height;
  356. }
  357. S32 LLFolderViewItem::getItemHeight()
  358. {
  359. if (mHidden) return 0;
  360. //S32 icon_height = mIcon->getHeight();
  361. //S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight());
  362. //return llmax( icon_height, label_height ) + ICON_PAD;
  363. return mItemHeight;
  364. }
  365. void LLFolderViewItem::filter( LLInventoryFilter& filter)
  366. {
  367. const BOOL previous_passed_filter = mPassedFilter;
  368. const BOOL passed_filter = mListener && filter.check(this);
  369. // If our visibility will change as a result of this filter, then
  370. // we need to be rearranged in our parent folder
  371. if (mParentFolder)
  372. {
  373. if (getVisible() != passed_filter)
  374. mParentFolder->requestArrange();
  375. if (passed_filter != previous_passed_filter)
  376. mParentFolder->requestArrange();
  377. }
  378. setFiltered(passed_filter, filter.getCurrentGeneration());
  379. mStringMatchOffset = filter.getStringMatchOffset();
  380. filter.decrementFilterCount();
  381. if (getRoot()->getDebugFilters())
  382. {
  383. mStatusText = llformat("%d", mLastFilterGeneration);
  384. }
  385. }
  386. void LLFolderViewItem::dirtyFilter()
  387. {
  388. mLastFilterGeneration = -1;
  389. // bubble up dirty flag all the way to root
  390. if (getParentFolder())
  391. {
  392. getParentFolder()->setCompletedFilterGeneration(-1, TRUE);
  393. }
  394. }
  395. // *TODO: This can be optimized a lot by simply recording that it is
  396. // selected in the appropriate places, and assuming that set selection
  397. // means 'deselect' for a leaf item. Do this optimization after
  398. // multiple selection is implemented to make sure it all plays nice
  399. // together.
  400. BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus)
  401. {
  402. if( selection == this )
  403. {
  404. mIsSelected = TRUE;
  405. if(mListener)
  406. {
  407. mListener->selectItem();
  408. }
  409. }
  410. else
  411. {
  412. mIsSelected = FALSE;
  413. }
  414. return mIsSelected;
  415. }
  416. BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selected)
  417. {
  418. if(selection == this && mIsSelected != selected)
  419. {
  420. mIsSelected = selected;
  421. if(mListener)
  422. {
  423. mListener->selectItem();
  424. }
  425. return TRUE;
  426. }
  427. return FALSE;
  428. }
  429. void LLFolderViewItem::recursiveDeselect(BOOL deselect_self)
  430. {
  431. if (mIsSelected && deselect_self)
  432. {
  433. mIsSelected = FALSE;
  434. // update ancestors' count of selected descendents
  435. LLFolderViewFolder* parent_folder = getParentFolder();
  436. while(parent_folder)
  437. {
  438. parent_folder->mNumDescendantsSelected--;
  439. parent_folder = parent_folder->getParentFolder();
  440. }
  441. }
  442. }
  443. BOOL LLFolderViewItem::isMovable()
  444. {
  445. if( mListener )
  446. {
  447. return mListener->isItemMovable();
  448. }
  449. else
  450. {
  451. return TRUE;
  452. }
  453. }
  454. BOOL LLFolderViewItem::isRemovable()
  455. {
  456. if( mListener )
  457. {
  458. return mListener->isItemRemovable();
  459. }
  460. else
  461. {
  462. return TRUE;
  463. }
  464. }
  465. void LLFolderViewItem::destroyView()
  466. {
  467. if (mParentFolder)
  468. {
  469. // removeView deletes me
  470. mParentFolder->removeView(this);
  471. }
  472. }
  473. // Call through to the viewed object and return true if it can be
  474. // removed.
  475. //BOOL LLFolderViewItem::removeRecursively(BOOL single_item)
  476. BOOL LLFolderViewItem::remove()
  477. {
  478. if(!isRemovable())
  479. {
  480. return FALSE;
  481. }
  482. if(mListener)
  483. {
  484. return mListener->removeItem();
  485. }
  486. return TRUE;
  487. }
  488. // Build an appropriate context menu for the item.
  489. void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags)
  490. {
  491. if(mListener)
  492. {
  493. mListener->buildContextMenu(menu, flags);
  494. }
  495. }
  496. void LLFolderViewItem::openItem( void )
  497. {
  498. if( mListener )
  499. {
  500. mListener->openItem();
  501. }
  502. }
  503. void LLFolderViewItem::preview( void )
  504. {
  505. if (mListener)
  506. {
  507. mListener->previewItem();
  508. }
  509. }
  510. void LLFolderViewItem::rename(const std::string& new_name)
  511. {
  512. if( !new_name.empty() )
  513. {
  514. if( mListener )
  515. {
  516. mListener->renameItem(new_name);
  517. if(mParentFolder)
  518. {
  519. mParentFolder->requestSort();
  520. }
  521. }
  522. }
  523. }
  524. const std::string& LLFolderViewItem::getSearchableLabel() const
  525. {
  526. return mSearchableLabel;
  527. }
  528. LLViewerInventoryItem * LLFolderViewItem::getInventoryItem(void)
  529. {
  530. return gInventory.getItem(getListener()->getUUID());
  531. }
  532. std::string LLFolderViewItem::getName( void ) const
  533. {
  534. if(mListener)
  535. {
  536. return mListener->getName();
  537. }
  538. return mLabel;
  539. }
  540. // LLView functionality
  541. BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask )
  542. {
  543. if(!mIsSelected)
  544. {
  545. setSelectionFromRoot(this, FALSE);
  546. }
  547. make_ui_sound("UISndClick");
  548. return TRUE;
  549. }
  550. BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask )
  551. {
  552. if (LLView::childrenHandleMouseDown(x, y, mask))
  553. {
  554. return TRUE;
  555. }
  556. // No handler needed for focus lost since this class has no
  557. // state that depends on it.
  558. gFocusMgr.setMouseCapture( this );
  559. if (!mIsSelected)
  560. {
  561. if(mask & MASK_CONTROL)
  562. {
  563. changeSelectionFromRoot(this, !mIsSelected);
  564. }
  565. else if (mask & MASK_SHIFT)
  566. {
  567. extendSelectionFromRoot(this);
  568. }
  569. else
  570. {
  571. setSelectionFromRoot(this, FALSE);
  572. }
  573. make_ui_sound("UISndClick");
  574. }
  575. else
  576. {
  577. mSelectPending = TRUE;
  578. }
  579. if( isMovable() )
  580. {
  581. S32 screen_x;
  582. S32 screen_y;
  583. localPointToScreen(x, y, &screen_x, &screen_y );
  584. LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y );
  585. }
  586. return TRUE;
  587. }
  588. BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
  589. {
  590. if( hasMouseCapture() && isMovable() )
  591. {
  592. S32 screen_x;
  593. S32 screen_y;
  594. localPointToScreen(x, y, &screen_x, &screen_y );
  595. BOOL can_drag = TRUE;
  596. if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) )
  597. {
  598. LLFolderView* root = getRoot();
  599. if(root->getCurSelectedItem())
  600. {
  601. LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD;
  602. // *TODO: push this into listener and remove
  603. // dependency on llagent
  604. if (mListener
  605. && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getRootFolderID()))
  606. {
  607. src = LLToolDragAndDrop::SOURCE_AGENT;
  608. }
  609. else if (mListener
  610. && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getLibraryRootFolderID()))
  611. {
  612. src = LLToolDragAndDrop::SOURCE_LIBRARY;
  613. }
  614. can_drag = root->startDrag(src);
  615. if (can_drag)
  616. {
  617. // if (mListener) mListener->startDrag();
  618. // RN: when starting drag and drop, clear out last auto-open
  619. root->autoOpenTest(NULL);
  620. root->setShowSelectionContext(TRUE);
  621. // Release keyboard focus, so that if stuff is dropped into the
  622. // world, pressing the delete key won't blow away the inventory
  623. // item.
  624. gFocusMgr.setKeyboardFocus(NULL);
  625. return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask );
  626. }
  627. }
  628. }
  629. if (can_drag)
  630. {
  631. gViewerWindow->setCursor(UI_CURSOR_ARROW);
  632. }
  633. else
  634. {
  635. gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
  636. }
  637. return TRUE;
  638. }
  639. else
  640. {
  641. getRoot()->setShowSelectionContext(FALSE);
  642. gViewerWindow->setCursor(UI_CURSOR_ARROW);
  643. // let parent handle this then...
  644. return FALSE;
  645. }
  646. }
  647. BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask )
  648. {
  649. preview();
  650. return TRUE;
  651. }
  652. BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )
  653. {
  654. if (LLView::childrenHandleMouseUp(x, y, mask))
  655. {
  656. return TRUE;
  657. }
  658. // if mouse hasn't moved since mouse down...
  659. if ( pointInView(x, y) && mSelectPending )
  660. {
  661. //...then select
  662. if(mask & MASK_CONTROL)
  663. {
  664. changeSelectionFromRoot(this, !mIsSelected);
  665. }
  666. else if (mask & MASK_SHIFT)
  667. {
  668. extendSelectionFromRoot(this);
  669. }
  670. else
  671. {
  672. setSelectionFromRoot(this, FALSE);
  673. }
  674. }
  675. mSelectPending = FALSE;
  676. if( hasMouseCapture() )
  677. {
  678. getRoot()->setShowSelectionContext(FALSE);
  679. gFocusMgr.setMouseCapture( NULL );
  680. }
  681. return TRUE;
  682. }
  683. BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
  684.  EDragAndDropType cargo_type,
  685.  void* cargo_data,
  686.  EAcceptance* accept,
  687.  std::string& tooltip_msg)
  688. {
  689. BOOL accepted = FALSE;
  690. BOOL handled = FALSE;
  691. if(mListener)
  692. {
  693. accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data);
  694. handled = accepted;
  695. if (accepted)
  696. {
  697. mDragAndDropTarget = TRUE;
  698. *accept = ACCEPT_YES_MULTI;
  699. }
  700. else
  701. {
  702. *accept = ACCEPT_NO;
  703. }
  704. }
  705. if(mParentFolder && !handled)
  706. {
  707. // store this item to get it in LLFolderBridge::dragItemIntoFolder on drop event.
  708. mRoot->setDraggingOverItem(this);
  709. handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg);
  710. mRoot->setDraggingOverItem(NULL);
  711. }
  712. if (handled)
  713. {
  714. lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl;
  715. }
  716. return handled;
  717. }
  718. void LLFolderViewItem::draw()
  719. {
  720. if (mHidden) return;
  721. static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
  722. static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);
  723. static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);
  724. static LLUIColor sFocusOutlineColor =
  725. LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);
  726. static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE);
  727. static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE);
  728. static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemSuffixColor", DEFAULT_WHITE);
  729. static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);
  730. const Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();
  731. const S32 TOP_PAD = default_params.item_top_pad;
  732. bool possibly_has_children = false;
  733. bool up_to_date = mListener && mListener->isUpToDate();
  734. if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter...
  735. (!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter)
  736. {
  737. possibly_has_children = true;
  738. }
  739. if(/*mControlLabel[0] != '' && */possibly_has_children)
  740. {
  741. LLUIImage* arrow_image = default_params.folder_arrow_image;
  742. gl_draw_scaled_rotated_image(
  743. mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD - TOP_PAD,
  744. ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor);
  745. }
  746. // See also LLFolderView::updateRenamerPosition()
  747. F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation);
  748. LLFontGL* font = getLabelFontForStyle(mLabelStyle);
  749. // If we have keyboard focus, draw selection filled
  750. BOOL show_context = getRoot()->getShowSelectionContext();
  751. BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus());
  752. const S32 FOCUS_LEFT = 1;
  753. S32 focus_top = getRect().getHeight();
  754. S32 focus_bottom = getRect().getHeight() - mItemHeight;
  755. bool folder_open = (getRect().getHeight() > mItemHeight + 4);
  756. // always render "current" item, only render other selected items if
  757. // mShowSingleSelection is FALSE
  758. if( mIsSelected )
  759. {
  760. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  761. LLColor4 bg_color = sHighlightBgColor;
  762. if (!mIsCurSelection)
  763. {
  764. // do time-based fade of extra objects
  765. F32 fade_time = getRoot()->getSelectionFadeElapsedTime();
  766. if (getRoot()->getShowSingleSelection())
  767. {
  768. // fading out
  769. bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f);
  770. }
  771. else
  772. {
  773. // fading in
  774. bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);
  775. }
  776. }
  777. gl_rect_2d(
  778. FOCUS_LEFT,
  779. focus_top, 
  780. getRect().getWidth() - 2,
  781. focus_bottom,
  782. bg_color, filled);
  783. if (mIsCurSelection)
  784. {
  785. gl_rect_2d(
  786. FOCUS_LEFT, 
  787. focus_top, 
  788. getRect().getWidth() - 2,
  789. focus_bottom,
  790. sFocusOutlineColor, FALSE);
  791. }
  792. if (folder_open)
  793. {
  794. gl_rect_2d(
  795. FOCUS_LEFT,
  796. focus_bottom + 1, // overlap with bottom edge of above rect
  797. getRect().getWidth() - 2,
  798. 0,
  799. sFocusOutlineColor, FALSE);
  800. if (show_context)
  801. {
  802. gl_rect_2d(
  803. FOCUS_LEFT,
  804. focus_bottom + 1,
  805. getRect().getWidth() - 2,
  806. 0,
  807. sHighlightBgColor, TRUE);
  808. }
  809. }
  810. }
  811. if (mDragAndDropTarget)
  812. {
  813. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  814. gl_rect_2d(
  815. FOCUS_LEFT, 
  816. focus_top, 
  817. getRect().getWidth() - 2,
  818. focus_bottom,
  819. sHighlightBgColor, FALSE);
  820. if (folder_open)
  821. {
  822. gl_rect_2d(
  823. FOCUS_LEFT,
  824. focus_bottom + 1, // overlap with bottom edge of above rect
  825. getRect().getWidth() - 2,
  826. 0,
  827. sHighlightBgColor, FALSE);
  828. }
  829. mDragAndDropTarget = FALSE;
  830. }
  831. S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD;
  832. // First case is used for open folders
  833. if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80))
  834.   {
  835. mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1);
  836. }
  837. else if(mIcon)
  838. {
  839.   mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1);
  840.   }
  841. if (!mLabel.empty())
  842. {
  843. // highlight filtered text
  844. BOOL debug_filters = getRoot()->getDebugFilters();
  845. LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor );
  846. F32 right_x;
  847. F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD;
  848. if (debug_filters)
  849. {
  850. if (!getFiltered() && !possibly_has_children)
  851. {
  852. color.mV[VALPHA] *= 0.5f;
  853. }
  854. LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? LLColor4(0.5f, 0.8f, 0.5f, 1.f) : LLColor4(0.8f, 0.5f, 0.5f, 1.f);
  855. LLFontGL::getFontMonospace()->renderUTF8(
  856. mStatusText, 0, text_left, y, filter_color,
  857. LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
  858. S32_MAX, S32_MAX, &right_x, FALSE );
  859. text_left = right_x;
  860. }
  861. font->renderUTF8( mLabel, 0, text_left, y, color,
  862.   LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
  863.   S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE);
  864. // LLViewerInventoryCategory *item = 0;
  865. // if (getListener())
  866. // item = gInventory.getCategory(getListener()->getUUID());
  867. bool root_is_loading = false;
  868. if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),gInventory.getRootFolderID()))
  869. {
  870. // Descendent of my inventory.
  871. root_is_loading = gInventory.myInventoryFetchInProgress();
  872. }
  873. if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),gInventory.getLibraryRootFolderID()))
  874. {
  875. // Descendent of library
  876. root_is_loading = gInventory.libraryFetchInProgress();
  877. }
  878. if ( (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime"))
  879. || (LLInventoryModel::backgroundFetchActive() && root_is_loading && mShowLoadStatus) )
  880. {
  881. std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) ";
  882. font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor,
  883.   LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &right_x, FALSE);
  884. }
  885. if (!mLabelSuffix.empty())
  886. {
  887. font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor,
  888.    LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
  889. S32_MAX, S32_MAX, &right_x, FALSE );
  890. }
  891. if (mStringMatchOffset != std::string::npos)
  892. {
  893. // don't draw backgrounds for zero-length strings
  894. S32 filter_string_length = getRoot()->getFilterSubString().size();
  895. if (filter_string_length > 0)
  896. {
  897. std::string combined_string = mLabel + mLabelSuffix;
  898. S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1;
  899. S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2;
  900. S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
  901. S32 top = getRect().getHeight() - TOP_PAD;
  902. LLUIImage* box_image = default_params.selection_image;
  903. LLRect box_rect(left, top, right, bottom);
  904. box_image->draw(box_rect, sFilterBGColor);
  905. F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset);
  906. F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD;
  907. font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy,
  908.   sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
  909.   filter_string_length, S32_MAX, &right_x, FALSE );
  910. }
  911. }
  912. }
  913. }
  914. ///----------------------------------------------------------------------------
  915. /// Class LLFolderViewFolder
  916. ///----------------------------------------------------------------------------
  917. LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): 
  918. LLFolderViewItem( p ), // 0 = no create time
  919. mIsOpen(FALSE),
  920. mExpanderHighlighted(FALSE),
  921. mCurHeight(0.f),
  922. mTargetHeight(0.f),
  923. mAutoOpenCountdown(0.f),
  924. mSubtreeCreationDate(0),
  925. mAmTrash(LLFolderViewFolder::UNKNOWN),
  926. mLastArrangeGeneration( -1 ),
  927. mLastCalculatedWidth(0),
  928. mCompletedFilterGeneration(-1),
  929. mMostFilteredDescendantGeneration(-1),
  930. mNeedsSort(false)
  931. {}
  932. // Destroys the object
  933. LLFolderViewFolder::~LLFolderViewFolder( void )
  934. {
  935. // The LLView base class takes care of object destruction. make sure that we
  936. // don't have mouse or keyboard focus
  937. gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
  938. }
  939. // addToFolder() returns TRUE if it succeeds. FALSE otherwise
  940. BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
  941. {
  942. if (!folder)
  943. {
  944. return FALSE;
  945. }
  946. mParentFolder = folder;
  947. root->addItemID(getListener()->getUUID(), this);
  948. return folder->addFolder(this);
  949. }
  950. // Finds width and height of this object and it's children.  Also
  951. // makes sure that this view and it's children are the right size.
  952. S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation)
  953. {
  954. // sort before laying out contents
  955. if (mNeedsSort)
  956. {
  957. mFolders.sort(mSortFunction);
  958. mItems.sort(mSortFunction);
  959. mNeedsSort = false;
  960. }
  961. mHasVisibleChildren = hasFilteredDescendants(filter_generation);
  962. LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getFilter()->getShowFolderState();
  963. // calculate height as a single item (without any children), and reshapes rectangle to match
  964. LLFolderViewItem::arrange( width, height, filter_generation );
  965. // clamp existing animated height so as to never get smaller than a single item
  966. mCurHeight = llmax((F32)*height, mCurHeight);
  967. // initialize running height value as height of single item in case we have no children
  968. *height = getItemHeight();
  969. F32 running_height = (F32)*height;
  970. F32 target_height = (F32)*height;
  971. // are my children visible?
  972. if (needsArrange())
  973. {
  974. // set last arrange generation first, in case children are animating
  975. // and need to be arranged again
  976. mLastArrangeGeneration = getRoot()->getArrangeGeneration();
  977. if (mIsOpen)
  978. {
  979. // Add sizes of children
  980. S32 parent_item_height = getRect().getHeight();
  981. for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit)
  982. {
  983. LLFolderViewFolder* folderp = (*fit);
  984. if (getRoot()->getDebugFilters())
  985. {
  986. folderp->setVisible(TRUE);
  987. }
  988. else
  989. {
  990. folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
  991. (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
  992. }
  993. if (folderp->getVisible())
  994. {
  995. S32 child_width = *width;
  996. S32 child_height = 0;
  997. S32 child_top = parent_item_height - llround(running_height);
  998. target_height += folderp->arrange( &child_width, &child_height, filter_generation );
  999. running_height += (F32)child_height;
  1000. *width = llmax(*width, child_width);
  1001. folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() );
  1002. }
  1003. }
  1004. for(items_t::iterator iit = mItems.begin();
  1005. iit != mItems.end(); ++iit)
  1006. {
  1007. LLFolderViewItem* itemp = (*iit);
  1008. if (getRoot()->getDebugFilters())
  1009. {
  1010. itemp->setVisible(TRUE);
  1011. }
  1012. else
  1013. {
  1014. itemp->setVisible(itemp->getFiltered(filter_generation));
  1015. }
  1016. if (itemp->getVisible())
  1017. {
  1018. S32 child_width = *width;
  1019. S32 child_height = 0;
  1020. S32 child_top = parent_item_height - llround(running_height);
  1021. target_height += itemp->arrange( &child_width, &child_height, filter_generation );
  1022. // don't change width, as this item is as wide as its parent folder by construction
  1023. itemp->reshape( itemp->getRect().getWidth(), child_height);
  1024. running_height += (F32)child_height;
  1025. *width = llmax(*width, child_width);
  1026. itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() );
  1027. }
  1028. }
  1029. }
  1030. mTargetHeight = target_height;
  1031. // cache this width so next time we can just return it
  1032. mLastCalculatedWidth = *width;
  1033. }
  1034. else
  1035. {
  1036. // just use existing width
  1037. *width = mLastCalculatedWidth;
  1038. }
  1039. // animate current height towards target height
  1040. if (llabs(mCurHeight - mTargetHeight) > 1.f)
  1041. {
  1042. mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT));
  1043. requestArrange();
  1044. // hide child elements that fall out of current animated height
  1045. for (folders_t::iterator iter = mFolders.begin();
  1046. iter != mFolders.end();)
  1047. {
  1048. folders_t::iterator fit = iter++;
  1049. // number of pixels that bottom of folder label is from top of parent folder
  1050. if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() 
  1051. > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
  1052. {
  1053. // hide if beyond current folder height
  1054. (*fit)->setVisible(FALSE);
  1055. }
  1056. }
  1057. for (items_t::iterator iter = mItems.begin();
  1058. iter != mItems.end();)
  1059. {
  1060. items_t::iterator iit = iter++;
  1061. // number of pixels that bottom of item label is from top of parent folder
  1062. if (getRect().getHeight() - (*iit)->getRect().mBottom
  1063. > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
  1064. {
  1065. (*iit)->setVisible(FALSE);
  1066. }
  1067. }
  1068. }
  1069. else
  1070. {
  1071. mCurHeight = mTargetHeight;
  1072. }
  1073. // don't change width as this item is already as wide as its parent folder
  1074. reshape(getRect().getWidth(),llround(mCurHeight));
  1075. // pass current height value back to parent
  1076. *height = llround(mCurHeight);
  1077. return llround(mTargetHeight);
  1078. }
  1079. BOOL LLFolderViewFolder::needsArrange()
  1080. {
  1081. return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); 
  1082. }
  1083. void LLFolderViewFolder::requestSort()
  1084. {
  1085. mNeedsSort = true;
  1086. // whenever item order changes, we need to lay things out again
  1087. requestArrange();
  1088. }
  1089. void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up)
  1090. {
  1091. mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation);
  1092. mCompletedFilterGeneration = generation;
  1093. // only aggregate up if we are a lower (older) value
  1094. if (recurse_up && mParentFolder && generation < mParentFolder->getCompletedFilterGeneration())
  1095. {
  1096. mParentFolder->setCompletedFilterGeneration(generation, TRUE);
  1097. }
  1098. }
  1099. void LLFolderViewFolder::filter( LLInventoryFilter& filter)
  1100. {
  1101. S32 filter_generation = filter.getCurrentGeneration();
  1102. // if failed to pass filter newer than must_pass_generation
  1103. // you will automatically fail this time, so we only
  1104. // check against items that have passed the filter
  1105. S32 must_pass_generation = filter.getMustPassGeneration();
  1106. bool autoopen_folders = (filter.hasFilterString());
  1107. // if we have already been filtered against this generation, skip out
  1108. if (getCompletedFilterGeneration() >= filter_generation)
  1109. {
  1110. return;
  1111. }
  1112. // filter folder itself
  1113. if (getLastFilterGeneration() < filter_generation)
  1114. {
  1115. if (getLastFilterGeneration() >= must_pass_generation && // folder has been compared to a valid precursor filter
  1116. !mPassedFilter) // and did not pass the filter
  1117. {
  1118. // go ahead and flag this folder as done
  1119. mLastFilterGeneration = filter_generation;
  1120. }
  1121. else
  1122. {
  1123. // filter self only on first pass through
  1124. LLFolderViewItem::filter( filter );
  1125. }
  1126. if (mHidden)
  1127. {
  1128. setOpen();
  1129. }
  1130. }
  1131. if (getRoot()->getDebugFilters())
  1132. {
  1133. mStatusText = llformat("%d", mLastFilterGeneration);
  1134. mStatusText += llformat("(%d)", mCompletedFilterGeneration);
  1135. mStatusText += llformat("+%d", mMostFilteredDescendantGeneration);
  1136. }
  1137. // all descendants have been filtered later than must pass generation
  1138. // but none passed
  1139. if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation))
  1140. {
  1141. // don't traverse children if we've already filtered them since must_pass_generation
  1142. // and came back with nothing
  1143. return;
  1144. }
  1145. // we entered here with at least one filter iteration left
  1146. // check to see if we have any more before continuing on to children
  1147. if (filter.getFilterCount() < 0)
  1148. {
  1149. return;
  1150. }
  1151. // when applying a filter, matching folders get their contents downloaded first
  1152. if (filter.isNotDefault() && getFiltered(filter.getMinRequiredGeneration()) && (mListener && !gInventory.isCategoryComplete(mListener->getUUID())))
  1153. {
  1154. gInventory.startBackgroundFetch(mListener->getUUID());
  1155. }
  1156. // now query children
  1157. for (folders_t::iterator iter = mFolders.begin();
  1158.  iter != mFolders.end();
  1159.  ++iter)
  1160. {
  1161. LLFolderViewFolder* folder = (*iter);
  1162. // have we run out of iterations this frame?
  1163. if (filter.getFilterCount() < 0)
  1164. {
  1165. break;
  1166. }
  1167. // mMostFilteredDescendantGeneration might have been reset
  1168. // in which case we need to update it even for folders that
  1169. // don't need to be filtered anymore
  1170. if (folder->getCompletedFilterGeneration() >= filter_generation)
  1171. {
  1172. // track latest generation to pass any child items
  1173. if (folder->getFiltered() || folder->hasFilteredDescendants(filter.getMinRequiredGeneration()))
  1174. {
  1175. mMostFilteredDescendantGeneration = filter_generation;
  1176. }
  1177. // just skip it, it has already been filtered
  1178. continue;
  1179. }
  1180. // update this folders filter status (and children)
  1181. folder->filter( filter );
  1182. // track latest generation to pass any child items
  1183. if (folder->getFiltered() || folder->hasFilteredDescendants(filter_generation))
  1184. {
  1185. mMostFilteredDescendantGeneration = filter_generation;
  1186. if (getRoot()->needsAutoSelect() && autoopen_folders)
  1187. {
  1188. folder->setOpenArrangeRecursively(TRUE);
  1189. }
  1190. }
  1191. }
  1192. for (items_t::iterator iter = mItems.begin();
  1193.  iter != mItems.end();
  1194.  ++iter)
  1195. {
  1196. LLFolderViewItem* item = (*iter);
  1197. if (filter.getFilterCount() < 0)
  1198. {
  1199. break;
  1200. }
  1201. if (item->getLastFilterGeneration() >= filter_generation)
  1202. {
  1203. if (item->getFiltered())
  1204. {
  1205. mMostFilteredDescendantGeneration = filter_generation;
  1206. }
  1207. continue;
  1208. }
  1209. if (item->getLastFilterGeneration() >= must_pass_generation && 
  1210. !item->getFiltered(must_pass_generation))
  1211. {
  1212. // failed to pass an earlier filter that was a subset of the current one
  1213. // go ahead and flag this item as done
  1214. item->setFiltered(FALSE, filter_generation);
  1215. continue;
  1216. }
  1217. item->filter( filter );
  1218. if (item->getFiltered(filter.getMinRequiredGeneration()))
  1219. {
  1220. mMostFilteredDescendantGeneration = filter_generation;
  1221. }
  1222. }
  1223. // if we didn't use all filter iterations
  1224. // that means we filtered all of our descendants
  1225. // instead of exhausting the filter count for this frame
  1226. if (filter.getFilterCount() > 0)
  1227. {
  1228. // flag this folder as having completed filter pass for all descendants
  1229. setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/);
  1230. }
  1231. }
  1232. void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation)
  1233. {
  1234. // if this folder is now filtered, but wasn't before
  1235. // (it just passed)
  1236. if (filtered && !mPassedFilter)
  1237. {
  1238. // reset current height, because last time we drew it
  1239. // it might have had more visible items than now
  1240. mCurHeight = 0.f;
  1241. }
  1242. LLFolderViewItem::setFiltered(filtered, filter_generation);
  1243. }
  1244. void LLFolderViewFolder::dirtyFilter()
  1245. {
  1246. // we're a folder, so invalidate our completed generation
  1247. setCompletedFilterGeneration(-1, FALSE);
  1248. LLFolderViewItem::dirtyFilter();
  1249. }
  1250. BOOL LLFolderViewFolder::hasFilteredDescendants()
  1251. {
  1252. return mMostFilteredDescendantGeneration >= getRoot()->getFilter()->getCurrentGeneration();
  1253. }
  1254. // Passes selection information on to children and record selection
  1255. // information if necessary.
  1256. BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem,
  1257.   BOOL take_keyboard_focus)
  1258. {
  1259. BOOL rv = FALSE;
  1260. if( selection == this )
  1261. {
  1262. mIsSelected = TRUE;
  1263. if(mListener)
  1264. {
  1265. mListener->selectItem();
  1266. }
  1267. rv = TRUE;
  1268. }
  1269. else
  1270. {
  1271. mIsSelected = FALSE;
  1272. rv = FALSE;
  1273. }
  1274. BOOL child_selected = FALSE;
  1275. for (folders_t::iterator iter = mFolders.begin();
  1276. iter != mFolders.end();)
  1277. {
  1278. folders_t::iterator fit = iter++;
  1279. if((*fit)->setSelection(selection, openitem, take_keyboard_focus))
  1280. {
  1281. rv = TRUE;
  1282. child_selected = TRUE;
  1283. mNumDescendantsSelected++;
  1284. }
  1285. }
  1286. for (items_t::iterator iter = mItems.begin();
  1287. iter != mItems.end();)
  1288. {
  1289. items_t::iterator iit = iter++;
  1290. if((*iit)->setSelection(selection, openitem, take_keyboard_focus))
  1291. {
  1292. rv = TRUE;
  1293. child_selected = TRUE;
  1294. mNumDescendantsSelected++;
  1295. }
  1296. }
  1297. if(openitem && child_selected)
  1298. {
  1299. setOpenArrangeRecursively(TRUE);
  1300. }
  1301. return rv;
  1302. }
  1303. // This method is used to change the selection of an item. If
  1304. // selection is 'this', then note selection as true. Returns TRUE
  1305. // if this or a child is now selected.
  1306. BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection,
  1307.  BOOL selected)
  1308. {
  1309. BOOL rv = FALSE;
  1310. if(selection == this)
  1311. {
  1312. mIsSelected = selected;
  1313. if(mListener && selected)
  1314. {
  1315. mListener->selectItem();
  1316. }
  1317. rv = TRUE;
  1318. }
  1319. for (folders_t::iterator iter = mFolders.begin();
  1320. iter != mFolders.end();)
  1321. {
  1322. folders_t::iterator fit = iter++;
  1323. if((*fit)->changeSelection(selection, selected))
  1324. {
  1325. if (selected)
  1326. {
  1327. mNumDescendantsSelected++;
  1328. }
  1329. else
  1330. {
  1331. mNumDescendantsSelected--;
  1332. }
  1333. rv = TRUE;
  1334. }
  1335. }
  1336. for (items_t::iterator iter = mItems.begin();
  1337. iter != mItems.end();)
  1338. {
  1339. items_t::iterator iit = iter++;
  1340. if((*iit)->changeSelection(selection, selected))
  1341. {
  1342. if (selected)
  1343. {
  1344. mNumDescendantsSelected++;
  1345. }
  1346. else
  1347. {
  1348. mNumDescendantsSelected--;
  1349. }
  1350. rv = TRUE;
  1351. }
  1352. }
  1353. return rv;
  1354. }
  1355. S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& selected_items)
  1356. {
  1357. S32 num_selected = 0;
  1358. // pass on to child folders first
  1359. for (folders_t::iterator iter = mFolders.begin();
  1360. iter != mFolders.end();)
  1361. {
  1362. folders_t::iterator fit = iter++;
  1363. num_selected += (*fit)->extendSelection(selection, last_selected, selected_items);
  1364. mNumDescendantsSelected += num_selected;
  1365. }
  1366. // handle selection of our immediate children...
  1367. BOOL reverse_select = FALSE;
  1368. BOOL found_last_selected = FALSE;
  1369. BOOL found_selection = FALSE;
  1370. LLDynamicArray<LLFolderViewItem*> items_to_select;
  1371. LLFolderViewItem* item;
  1372. //...folders first...
  1373. for (folders_t::iterator iter = mFolders.begin();
  1374. iter != mFolders.end();)
  1375. {
  1376. folders_t::iterator fit = iter++;
  1377. item = (*fit);
  1378. if(item == selection)
  1379. {
  1380. found_selection = TRUE;
  1381. }
  1382. else if (item == last_selected)
  1383. {
  1384. found_last_selected = TRUE;
  1385. if (found_selection)
  1386. {
  1387. reverse_select = TRUE;
  1388. }
  1389. }
  1390. if (found_selection || found_last_selected)
  1391. {
  1392. // deselect currently selected items so they can be pushed back on queue
  1393. if (item->isSelected())
  1394. {
  1395. item->changeSelection(item, FALSE);
  1396. }
  1397. items_to_select.put(item);
  1398. }
  1399. if (found_selection && found_last_selected)
  1400. {
  1401. break;
  1402. }
  1403. }
  1404. if (!(found_selection && found_last_selected))
  1405. {
  1406. //,,,then items
  1407. for (items_t::iterator iter = mItems.begin();
  1408. iter != mItems.end();)
  1409. {
  1410. items_t::iterator iit = iter++;
  1411. item = (*iit);
  1412. if(item == selection)
  1413. {
  1414. found_selection = TRUE;
  1415. }
  1416. else if (item == last_selected)
  1417. {
  1418. found_last_selected = TRUE;
  1419. if (found_selection)
  1420. {
  1421. reverse_select = TRUE;
  1422. }
  1423. }
  1424. if (found_selection || found_last_selected)
  1425. {
  1426. // deselect currently selected items so they can be pushed back on queue
  1427. if (item->isSelected())
  1428. {
  1429. item->changeSelection(item, FALSE);
  1430. }
  1431. items_to_select.put(item);
  1432. }
  1433. if (found_selection && found_last_selected)
  1434. {
  1435. break;
  1436. }
  1437. }
  1438. }
  1439. if (found_last_selected && found_selection)
  1440. {
  1441. // we have a complete selection inside this folder
  1442. for (S32 index = reverse_select ? items_to_select.getLength() - 1 : 0; 
  1443. reverse_select ? index >= 0 : index < items_to_select.getLength(); reverse_select ? index-- : index++)
  1444. {
  1445. LLFolderViewItem* item = items_to_select[index];
  1446. if (item->changeSelection(item, TRUE))
  1447. {
  1448. selected_items.put(item);
  1449. mNumDescendantsSelected++;
  1450. num_selected++;
  1451. }
  1452. }
  1453. }
  1454. else if (found_selection)
  1455. {
  1456. // last selection was not in this folder....go ahead and select just the new item
  1457. if (selection->changeSelection(selection, TRUE))
  1458. {
  1459. selected_items.put(selection);
  1460. mNumDescendantsSelected++;
  1461. num_selected++;
  1462. }
  1463. }
  1464. return num_selected;
  1465. }
  1466. void LLFolderViewFolder::recursiveDeselect(BOOL deselect_self)
  1467. {
  1468. // make sure we don't have negative values
  1469. llassert(mNumDescendantsSelected >= 0);
  1470. if (mIsSelected && deselect_self)
  1471. {
  1472. mIsSelected = FALSE;
  1473. // update ancestors' count of selected descendents
  1474. LLFolderViewFolder* parent_folder = getParentFolder();
  1475. while(parent_folder)
  1476. {
  1477. parent_folder->mNumDescendantsSelected--;
  1478. parent_folder = parent_folder->getParentFolder();
  1479. }
  1480. }
  1481. if (0 == mNumDescendantsSelected)
  1482. {
  1483. return;
  1484. }
  1485. for (items_t::iterator iter = mItems.begin();
  1486. iter != mItems.end();)
  1487. {
  1488. items_t::iterator iit = iter++;
  1489. LLFolderViewItem* item = (*iit);
  1490. item->recursiveDeselect(TRUE);
  1491. }
  1492. for (folders_t::iterator iter = mFolders.begin();
  1493. iter != mFolders.end();)
  1494. {
  1495. folders_t::iterator fit = iter++;
  1496. LLFolderViewFolder* folder = (*fit);
  1497. folder->recursiveDeselect(TRUE);
  1498. }
  1499. }
  1500. void LLFolderViewFolder::destroyView()
  1501. {
  1502. for (items_t::iterator iter = mItems.begin();
  1503. iter != mItems.end();)
  1504. {
  1505. items_t::iterator iit = iter++;
  1506. LLFolderViewItem* item = (*iit);
  1507. getRoot()->removeItemID(item->getListener()->getUUID());
  1508. }
  1509. std::for_each(mItems.begin(), mItems.end(), DeletePointer());
  1510. mItems.clear();
  1511. while (!mFolders.empty())
  1512. {
  1513. LLFolderViewFolder *folderp = mFolders.back();
  1514. folderp->destroyView(); // removes entry from mFolders
  1515. }
  1516. deleteAllChildren();
  1517. if (mParentFolder)
  1518. {
  1519. mParentFolder->removeView(this);
  1520. }
  1521. }
  1522. // remove the specified item (and any children) if possible. Return
  1523. // TRUE if the item was deleted.
  1524. BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item)
  1525. {
  1526. if(item->remove())
  1527. {
  1528. //RN: this seem unneccessary as remove() moves to trash
  1529. //removeView(item);
  1530. return TRUE;
  1531. }
  1532. return FALSE;
  1533. }
  1534. // simply remove the view (and any children) Don't bother telling the
  1535. // listeners.
  1536. void LLFolderViewFolder::removeView(LLFolderViewItem* item)
  1537. {
  1538. if (!item || item->getParentFolder() != this)
  1539. {
  1540. return;
  1541. }
  1542. // deselect without traversing hierarchy
  1543. item->recursiveDeselect(TRUE);
  1544. getRoot()->removeFromSelectionList(item);
  1545. extractItem(item);
  1546. delete item;
  1547. }
  1548. // extractItem() removes the specified item from the folder, but
  1549. // doesn't delete it.
  1550. void LLFolderViewFolder::extractItem( LLFolderViewItem* item )
  1551. {
  1552. items_t::iterator it = std::find(mItems.begin(), mItems.end(), item);
  1553. if(it == mItems.end())
  1554. {
  1555. // This is an evil downcast. However, it's only doing
  1556. // pointer comparison to find if (which it should be ) the
  1557. // item is in the container, so it's pretty safe.
  1558. LLFolderViewFolder* f = reinterpret_cast<LLFolderViewFolder*>(item);
  1559. folders_t::iterator ft;
  1560. ft = std::find(mFolders.begin(), mFolders.end(), f);
  1561. if(ft != mFolders.end())
  1562. {
  1563. mFolders.erase(ft);
  1564. }
  1565. }
  1566. else
  1567. {
  1568. mItems.erase(it);
  1569. }
  1570. //item has been removed, need to update filter
  1571. dirtyFilter();
  1572. //because an item is going away regardless of filter status, force rearrange
  1573. requestArrange();
  1574. getRoot()->removeItemID(item->getListener()->getUUID());
  1575. removeChild(item);
  1576. }
  1577. bool LLFolderViewFolder::isTrash() const
  1578. {
  1579. if (mAmTrash == LLFolderViewFolder::UNKNOWN)
  1580. {
  1581. mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH, false) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH;
  1582. }
  1583. return mAmTrash == LLFolderViewFolder::TRASH;
  1584. }
  1585. void LLFolderViewFolder::sortBy(U32 order)
  1586. {
  1587. if (!mSortFunction.updateSort(order))
  1588. {
  1589. // No changes.
  1590. return;
  1591. }
  1592. // Propegate this change to sub folders
  1593. for (folders_t::iterator iter = mFolders.begin();
  1594. iter != mFolders.end();)
  1595. {
  1596. folders_t::iterator fit = iter++;
  1597. (*fit)->sortBy(order);
  1598. }
  1599. mFolders.sort(mSortFunction);
  1600. mItems.sort(mSortFunction);
  1601. if (order & LLInventoryFilter::SO_DATE)
  1602. {
  1603. time_t latest = 0;
  1604. if (!mItems.empty())
  1605. {
  1606. LLFolderViewItem* item = *(mItems.begin());
  1607. latest = item->getCreationDate();
  1608. }
  1609. if (!mFolders.empty())
  1610. {
  1611. LLFolderViewFolder* folder = *(mFolders.begin());
  1612. if (folder->getCreationDate() > latest)
  1613. {
  1614. latest = folder->getCreationDate();
  1615. }
  1616. }
  1617. mSubtreeCreationDate = latest;
  1618. }
  1619. }
  1620. void LLFolderViewFolder::setItemSortOrder(U32 ordering)
  1621. {
  1622. if (mSortFunction.updateSort(ordering))
  1623. {
  1624. for (folders_t::iterator iter = mFolders.begin();
  1625. iter != mFolders.end();)
  1626. {
  1627. folders_t::iterator fit = iter++;
  1628. (*fit)->setItemSortOrder(ordering);
  1629. }
  1630. mFolders.sort(mSortFunction);
  1631. mItems.sort(mSortFunction);
  1632. }
  1633. }
  1634. EInventorySortGroup LLFolderViewFolder::getSortGroup() const
  1635. {
  1636. if (isTrash())
  1637. {
  1638. return SG_TRASH_FOLDER;
  1639. }
  1640. if( mListener )
  1641. {
  1642. if(LLFolderType::lookupIsProtectedType(mListener->getPreferredType()))
  1643. {
  1644. return SG_SYSTEM_FOLDER;
  1645. }
  1646. }
  1647. return SG_NORMAL_FOLDER;
  1648. }
  1649. BOOL LLFolderViewFolder::isMovable()
  1650. {
  1651. if( mListener )
  1652. {
  1653. if( !(mListener->isItemMovable()) )
  1654. {
  1655. return FALSE;
  1656. }
  1657. for (items_t::iterator iter = mItems.begin();
  1658. iter != mItems.end();)
  1659. {
  1660. items_t::iterator iit = iter++;
  1661. if(!(*iit)->isMovable())
  1662. {
  1663. return FALSE;
  1664. }
  1665. }
  1666. for (folders_t::iterator iter = mFolders.begin();
  1667. iter != mFolders.end();)
  1668. {
  1669. folders_t::iterator fit = iter++;
  1670. if(!(*fit)->isMovable())
  1671. {
  1672. return FALSE;
  1673. }
  1674. }
  1675. }
  1676. return TRUE;
  1677. }
  1678. BOOL LLFolderViewFolder::isRemovable()
  1679. {
  1680. if( mListener )
  1681. {
  1682. if( !(mListener->isItemRemovable()) )
  1683. {
  1684. return FALSE;
  1685. }
  1686. for (items_t::iterator iter = mItems.begin();
  1687. iter != mItems.end();)
  1688. {
  1689. items_t::iterator iit = iter++;
  1690. if(!(*iit)->isRemovable())
  1691. {
  1692. return FALSE;
  1693. }
  1694. }
  1695. for (folders_t::iterator iter = mFolders.begin();
  1696. iter != mFolders.end();)
  1697. {
  1698. folders_t::iterator fit = iter++;
  1699. if(!(*fit)->isRemovable())
  1700. {
  1701. return FALSE;
  1702. }
  1703. }
  1704. }
  1705. return TRUE;
  1706. }
  1707. // this is an internal method used for adding items to folders. 
  1708. BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item)
  1709. {
  1710. mItems.push_back(item);
  1711. item->setRect(LLRect(0, 0, getRect().getWidth(), 0));
  1712. item->setVisible(FALSE);
  1713. addChild( item );
  1714. item->dirtyFilter();
  1715. requestArrange();
  1716. requestSort();
  1717. return TRUE;
  1718. }
  1719. // this is an internal method used for adding items to folders. 
  1720. BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder)
  1721. {
  1722. mFolders.push_back(folder);
  1723. folder->setOrigin(0, 0);
  1724. folder->reshape(getRect().getWidth(), 0);
  1725. folder->setVisible(FALSE);
  1726. addChild( folder );
  1727. folder->dirtyFilter();
  1728. // rearrange all descendants too, as our indentation level might have changed
  1729. folder->requestArrange(TRUE);
  1730. requestSort();
  1731. return TRUE;
  1732. }
  1733. void LLFolderViewFolder::requestArrange(BOOL include_descendants)
  1734. mLastArrangeGeneration = -1; 
  1735. // flag all items up to root
  1736. if (mParentFolder)
  1737. {
  1738. mParentFolder->requestArrange();
  1739. }
  1740. if (include_descendants)
  1741. {
  1742. for (folders_t::iterator iter = mFolders.begin();
  1743. iter != mFolders.end();
  1744. ++iter)
  1745. {
  1746. (*iter)->requestArrange(TRUE);
  1747. }
  1748. }
  1749. }
  1750. void LLFolderViewFolder::toggleOpen()
  1751. {
  1752. setOpen(!mIsOpen);
  1753. }
  1754. // Force a folder open or closed
  1755. void LLFolderViewFolder::setOpen(BOOL openitem)
  1756. {
  1757. setOpenArrangeRecursively(openitem);
  1758. }
  1759. void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse)
  1760. {
  1761. BOOL was_open = mIsOpen;
  1762. mIsOpen = openitem;
  1763. if (mListener)
  1764. {
  1765. if(!was_open && openitem)
  1766. {
  1767. mListener->openItem();
  1768. }
  1769. else if(was_open && !openitem)
  1770. {
  1771. mListener->closeItem();
  1772. }
  1773. }
  1774. if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN)
  1775. {
  1776. for (folders_t::iterator iter = mFolders.begin();
  1777. iter != mFolders.end();)
  1778. {
  1779. folders_t::iterator fit = iter++;
  1780. (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN); /* Flawfinder: ignore */
  1781. }
  1782. }
  1783. if (mParentFolder && (recurse == RECURSE_UP || recurse == RECURSE_UP_DOWN))
  1784. {
  1785. mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP);
  1786. }
  1787. if (was_open != mIsOpen)
  1788. {
  1789. requestArrange();
  1790. }
  1791. }
  1792. BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask,
  1793. BOOL drop,
  1794. EDragAndDropType c_type,
  1795. void* cargo_data,
  1796. EAcceptance* accept,
  1797. std::string& tooltip_msg)
  1798. {
  1799. BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data);
  1800. if (accepted) 
  1801. {
  1802. mDragAndDropTarget = TRUE;
  1803. *accept = ACCEPT_YES_MULTI;
  1804. }
  1805. else 
  1806. {
  1807. *accept = ACCEPT_NO;
  1808. }
  1809. // drag and drop to child item, so clear pending auto-opens
  1810. getRoot()->autoOpenTest(NULL);
  1811. return TRUE;
  1812. }
  1813. void LLFolderViewFolder::openItem( void )
  1814. {
  1815. toggleOpen();
  1816. }
  1817. void LLFolderViewFolder::applyFunctorToChildren(LLFolderViewFunctor& functor)
  1818. {
  1819. for (folders_t::iterator iter = mFolders.begin();
  1820. iter != mFolders.end();)
  1821. {
  1822. folders_t::iterator fit = iter++;
  1823. functor.doItem((*fit));
  1824. }
  1825. for (items_t::iterator iter = mItems.begin();
  1826. iter != mItems.end();)
  1827. {
  1828. items_t::iterator iit = iter++;
  1829. functor.doItem((*iit));
  1830. }
  1831. }
  1832. void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor)
  1833. {
  1834. functor.doFolder(this);
  1835. for (folders_t::iterator iter = mFolders.begin();
  1836. iter != mFolders.end();)
  1837. {
  1838. folders_t::iterator fit = iter++;
  1839. (*fit)->applyFunctorRecursively(functor);
  1840. }
  1841. for (items_t::iterator iter = mItems.begin();
  1842. iter != mItems.end();)
  1843. {
  1844. items_t::iterator iit = iter++;
  1845. functor.doItem((*iit));
  1846. }
  1847. }
  1848. void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
  1849. {
  1850. functor(mListener);
  1851. for (folders_t::iterator iter = mFolders.begin();
  1852. iter != mFolders.end();)
  1853. {
  1854. folders_t::iterator fit = iter++;
  1855. (*fit)->applyListenerFunctorRecursively(functor);
  1856. }
  1857. for (items_t::iterator iter = mItems.begin();
  1858. iter != mItems.end();)
  1859. {
  1860. items_t::iterator iit = iter++;
  1861. (*iit)->applyListenerFunctorRecursively(functor);
  1862. }
  1863. }
  1864. // LLView functionality
  1865. BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask,
  1866.    BOOL drop,
  1867.    EDragAndDropType cargo_type,
  1868.    void* cargo_data,
  1869.    EAcceptance* accept,
  1870.    std::string& tooltip_msg)
  1871. {
  1872. LLFolderView* root_view = getRoot();
  1873. BOOL handled = FALSE;
  1874. if(mIsOpen)
  1875. {
  1876. handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
  1877. cargo_data, accept, tooltip_msg) != NULL;
  1878. }
  1879. if (!handled)
  1880. {
  1881. BOOL accepted = mListener && mListener->dragOrDrop(mask, drop,cargo_type,cargo_data);
  1882. if (accepted) 
  1883. {
  1884. mDragAndDropTarget = TRUE;
  1885. *accept = ACCEPT_YES_MULTI;
  1886. }
  1887. else 
  1888. {
  1889. *accept = ACCEPT_NO;
  1890. }
  1891. if (!drop && accepted)
  1892. {
  1893. root_view->autoOpenTest(this);
  1894. }
  1895. lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl;
  1896. }
  1897. return TRUE;
  1898. }
  1899. BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask )
  1900. {
  1901. BOOL handled = FALSE;
  1902. // fetch contents of this folder, as context menu can depend on contents
  1903. // still, user would have to open context menu again to see the changes
  1904. gInventory.fetchDescendentsOf(mListener->getUUID());
  1905. if( mIsOpen )
  1906. {
  1907. handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
  1908. }
  1909. if (!handled)
  1910. {
  1911. handled = LLFolderViewItem::handleRightMouseDown( x, y, mask );
  1912. }
  1913. return handled;
  1914. }
  1915. BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask)
  1916. {
  1917. BOOL handled = LLView::handleHover(x, y, mask);
  1918. if (!handled)
  1919. {
  1920. // this doesn't do child processing
  1921. handled = LLFolderViewItem::handleHover(x, y, mask);
  1922. }
  1923. return handled;
  1924. }
  1925. BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )
  1926. {
  1927. BOOL handled = FALSE;
  1928. if( mIsOpen )
  1929. {
  1930. handled = childrenHandleMouseDown(x,y,mask) != NULL;
  1931. }
  1932. if( !handled )
  1933. {
  1934. if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD)
  1935. {
  1936. toggleOpen();
  1937. handled = TRUE;
  1938. }
  1939. else
  1940. {
  1941. // do normal selection logic
  1942. handled = LLFolderViewItem::handleMouseDown(x, y, mask);
  1943. }
  1944. }
  1945. return handled;
  1946. }
  1947. BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask )
  1948. {
  1949. /* Disable outfit double click to wear
  1950. const LLUUID &cat_uuid = getListener()->getUUID();
  1951. const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid);
  1952. if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT)
  1953. {
  1954. getListener()->performAction(NULL, NULL,"replaceoutfit");
  1955. return TRUE;
  1956. }
  1957. */
  1958. BOOL handled = FALSE;
  1959. if( mIsOpen )
  1960. {
  1961. handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
  1962. }
  1963. if( !handled )
  1964. {
  1965. if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD)
  1966. {
  1967. // don't select when user double-clicks plus sign
  1968. // so as not to contradict single-click behavior
  1969. toggleOpen();
  1970. }
  1971. else
  1972. {
  1973. setSelectionFromRoot(this, FALSE);
  1974. toggleOpen();
  1975. }
  1976. handled = TRUE;
  1977. }
  1978. return handled;
  1979. }
  1980. void LLFolderViewFolder::draw()
  1981. {
  1982. if (mAutoOpenCountdown != 0.f)
  1983. {
  1984. mControlLabelRotation = mAutoOpenCountdown * -90.f;
  1985. }
  1986. else if (mIsOpen)
  1987. {
  1988. mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f));
  1989. }
  1990. else
  1991. {
  1992. mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f));
  1993. }
  1994. bool possibly_has_children = false;
  1995. bool up_to_date = mListener && mListener->isUpToDate();
  1996. if(!up_to_date && mListener && mListener->hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter)
  1997. {
  1998. possibly_has_children = true;
  1999. }
  2000. BOOL loading = ( mIsOpen && possibly_has_children && !up_to_date );
  2001. if ( loading && !mIsLoading )
  2002. {
  2003. // Measure how long we've been in the loading state
  2004. mTimeSinceRequestStart.reset();
  2005. }
  2006. mIsLoading = loading;
  2007. LLFolderViewItem::draw();
  2008. // draw children if root folder, or any other folder that is open or animating to closed state
  2009. if( getRoot() == this || (mIsOpen || mCurHeight != mTargetHeight ))
  2010. {
  2011. LLView::draw();
  2012. }
  2013. mExpanderHighlighted = FALSE;
  2014. }
  2015. time_t LLFolderViewFolder::getCreationDate() const
  2016. {
  2017. return llmax<time_t>(mCreationDate, mSubtreeCreationDate);
  2018. }
  2019. BOOL LLFolderViewFolder::potentiallyVisible()
  2020. {
  2021. // folder should be visible by it's own filter status
  2022. return LLFolderViewItem::potentiallyVisible() 
  2023. // or one or more of its descendants have passed the minimum filter requirement
  2024. || hasFilteredDescendants(getRoot()->getFilter()->getMinRequiredGeneration())
  2025. // or not all of its descendants have been checked against minimum filter requirement
  2026. || getCompletedFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration(); 
  2027. }
  2028. // this does prefix traversal, as folders are listed above their contents
  2029. LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children )
  2030. {
  2031. BOOL found_item = FALSE;
  2032. LLFolderViewItem* result = NULL;
  2033. // when not starting from a given item, start at beginning
  2034. if(item == NULL)
  2035. {
  2036. found_item = TRUE;
  2037. }
  2038. // find current item among children
  2039. folders_t::iterator fit = mFolders.begin();
  2040. folders_t::iterator fend = mFolders.end();
  2041. items_t::iterator iit = mItems.begin();
  2042. items_t::iterator iend = mItems.end();
  2043. // if not trivially starting at the beginning, we have to find the current item
  2044. if (!found_item)
  2045. {
  2046. // first, look among folders, since they are always above items
  2047. for(; fit != fend; ++fit)
  2048. {
  2049. if(item == (*fit))
  2050. {
  2051. found_item = TRUE;
  2052. // if we are on downwards traversal
  2053. if (include_children && (*fit)->isOpen())
  2054. {
  2055. // look for first descendant
  2056. return (*fit)->getNextFromChild(NULL, TRUE);
  2057. }
  2058. // otherwise advance to next folder
  2059. ++fit;
  2060. include_children = TRUE;
  2061. break;
  2062. }
  2063. }
  2064. // didn't find in folders?  Check items...
  2065. if (!found_item)
  2066. {
  2067. for(; iit != iend; ++iit)
  2068. {
  2069. if(item == (*iit))
  2070. {
  2071. found_item = TRUE;
  2072. // point to next item
  2073. ++iit;
  2074. break;
  2075. }
  2076. }
  2077. }
  2078. }
  2079. if (!found_item)
  2080. {
  2081. // you should never call this method with an item that isn't a child
  2082. // so we should always find something
  2083. llassert(FALSE);
  2084. return NULL;
  2085. }
  2086. // at this point, either iit or fit point to a candidate "next" item
  2087. // if both are out of range, we need to punt up to our parent
  2088. // now, starting from found folder, continue through folders
  2089. // searching for next visible folder
  2090. while(fit != fend && !(*fit)->getVisible())
  2091. {
  2092. // turn on downwards traversal for next folder
  2093. ++fit;
  2094. if (fit != fend)
  2095. {
  2096. result = (*fit);
  2097. }
  2098. else
  2099. {
  2100. // otherwise, scan for next visible item
  2101. while(iit != iend && !(*iit)->getVisible())
  2102. {
  2103. ++iit;
  2104. // check to see if we have a valid item
  2105. if (iit != iend)
  2106. {
  2107. result = (*iit);
  2108. }
  2109. }
  2110. if( !result && mParentFolder )
  2111. {
  2112. // If there are no siblings or children to go to, recurse up one level in the tree
  2113. // and skip children for this folder, as we've already discounted them
  2114. result = mParentFolder->getNextFromChild(this, FALSE);
  2115. }
  2116. return result;
  2117. }
  2118. // this does postfix traversal, as folders are listed above their contents
  2119. LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children )
  2120. {
  2121. BOOL found_item = FALSE;
  2122. LLFolderViewItem* result = NULL;
  2123. // when not starting from a given item, start at end
  2124. if(item == NULL)
  2125. {
  2126. found_item = TRUE;
  2127. }
  2128. // find current item among children
  2129. folders_t::reverse_iterator fit = mFolders.rbegin();
  2130. folders_t::reverse_iterator fend = mFolders.rend();
  2131. items_t::reverse_iterator iit = mItems.rbegin();
  2132. items_t::reverse_iterator iend = mItems.rend();
  2133. // if not trivially starting at the end, we have to find the current item
  2134. if (!found_item)
  2135. {
  2136. // first, look among items, since they are always below the folders
  2137. for(; iit != iend; ++iit)
  2138. {
  2139. if(item == (*iit))
  2140. {
  2141. found_item = TRUE;
  2142. // point to next item
  2143. ++iit;
  2144. break;
  2145. }
  2146. }
  2147. // didn't find in items?  Check folders...
  2148. if (!found_item)
  2149. {
  2150. for(; fit != fend; ++fit)
  2151. {
  2152. if(item == (*fit))
  2153. {
  2154. found_item = TRUE;
  2155. // point to next folder
  2156. ++fit;
  2157. break;
  2158. }
  2159. }
  2160. }
  2161. }
  2162. if (!found_item)
  2163. {
  2164. // you should never call this method with an item that isn't a child
  2165. // so we should always find something
  2166. llassert(FALSE);
  2167. return NULL;
  2168. }
  2169. // at this point, either iit or fit point to a candidate "next" item
  2170. // if both are out of range, we need to punt up to our parent
  2171. // now, starting from found item, continue through items
  2172. // searching for next visible item
  2173. while(iit != iend && !(*iit)->getVisible())
  2174. {
  2175. ++iit;
  2176. if (iit != iend)
  2177. {
  2178. // we found an appropriate item
  2179. result = (*iit);
  2180. }
  2181. else
  2182. {
  2183. // otherwise, scan for next visible folder
  2184. while(fit != fend && !(*fit)->getVisible())
  2185. {
  2186. ++fit;
  2187. // check to see if we have a valid folder
  2188. if (fit != fend)
  2189. {
  2190. // try selecting child element of this folder
  2191. if ((*fit)->isOpen())
  2192. {
  2193. result = (*fit)->getPreviousFromChild(NULL);
  2194. }
  2195. else
  2196. {
  2197. result = (*fit);
  2198. }
  2199. }
  2200. }
  2201. if( !result )
  2202. {
  2203. // If there are no siblings or children to go to, recurse up one level in the tree
  2204. // which gets back to this folder, which will only be visited if it is a valid, visible item
  2205. result = this;
  2206. }
  2207. return result;
  2208. }
  2209. bool LLInventorySort::updateSort(U32 order)
  2210. {
  2211. if (order != mSortOrder)
  2212. {
  2213. mSortOrder = order;
  2214. mByDate = (order & LLInventoryFilter::SO_DATE);
  2215. mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP);
  2216. mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME);
  2217. return true;
  2218. }
  2219. return false;
  2220. }
  2221. bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b)
  2222. {
  2223. // ignore sort order for landmarks in the Favorites folder.
  2224. // they should be always sorted as in Favorites bar. See EXT-719
  2225. if (a->getSortGroup() == SG_ITEM && b->getSortGroup() == SG_ITEM
  2226. && a->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK
  2227. && b->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK)
  2228. {
  2229. static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
  2230. LLUUID a_uuid = a->getParentFolder()->getListener()->getUUID();
  2231. LLUUID b_uuid = b->getParentFolder()->getListener()->getUUID();
  2232. if ((a_uuid == favorites_folder_id && b_uuid == favorites_folder_id))
  2233. {
  2234. // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem
  2235. // or to LLInvFVBridge
  2236. LLViewerInventoryItem* aitem = (static_cast<const LLItemBridge*>(a->getListener()))->getItem();
  2237. LLViewerInventoryItem* bitem = (static_cast<const LLItemBridge*>(b->getListener()))->getItem();
  2238. if (!aitem || !bitem)
  2239. return false;
  2240. S32 a_sort = aitem->getSortField();
  2241. S32 b_sort = bitem->getSortField();
  2242. return a_sort < b_sort;
  2243. }
  2244. }
  2245. // We sort by name if we aren't sorting by date
  2246. // OR if these are folders and we are sorting folders by name.
  2247. bool by_name = (!mByDate 
  2248. || (mFoldersByName 
  2249. && (a->getSortGroup() != SG_ITEM)));
  2250. if (a->getSortGroup() != b->getSortGroup())
  2251. {
  2252. if (mSystemToTop)
  2253. {
  2254. // Group order is System Folders, Trash, Normal Folders, Items
  2255. return (a->getSortGroup() < b->getSortGroup());
  2256. }
  2257. else if (mByDate)
  2258. {
  2259. // Trash needs to go to the bottom if we are sorting by date
  2260. if ( (a->getSortGroup() == SG_TRASH_FOLDER)
  2261. || (b->getSortGroup() == SG_TRASH_FOLDER))
  2262. {
  2263. return (b->getSortGroup() == SG_TRASH_FOLDER);
  2264. }
  2265. }
  2266. }
  2267. if (by_name)
  2268. {
  2269. S32 compare = LLStringUtil::compareDict(a->getLabel(), b->getLabel());
  2270. if (0 == compare)
  2271. {
  2272. return (a->getCreationDate() > b->getCreationDate());
  2273. }
  2274. else
  2275. {
  2276. return (compare < 0);
  2277. }
  2278. }
  2279. else
  2280. {
  2281. // BUG: This is very very slow.  The getCreationDate() is log n in number
  2282. // of inventory items.
  2283. time_t first_create = a->getCreationDate();
  2284. time_t second_create = b->getCreationDate();
  2285. if (first_create == second_create)
  2286. {
  2287. return (LLStringUtil::compareDict(a->getLabel(), b->getLabel()) < 0);
  2288. }
  2289. else
  2290. {
  2291. return (first_create > second_create);
  2292. }
  2293. }
  2294. }