PageRenderTime 1771ms CodeModel.GetById 25ms app.highlight 455ms RepoModel.GetById 1ms 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

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

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

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