PageRenderTime 1247ms CodeModel.GetById 221ms app.highlight 843ms RepoModel.GetById 163ms app.codeStats 1ms

/indra/newview/llfolderview.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2514 lines | 1981 code | 324 blank | 209 comment | 438 complexity | 95cc07bce5ed868c6b0471bfadbacf43 MD5 | raw file

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

   1/** 
   2 * @file llfolderview.cpp
   3 * @brief Implementation of the folder view collection of classes.
   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
  27#include "llviewerprecompiledheaders.h"
  28
  29#include "llfolderview.h"
  30
  31#include "llcallbacklist.h"
  32#include "llinventorybridge.h"
  33#include "llinventoryclipboard.h" // *TODO: remove this once hack below gone.
  34#include "llinventoryfilter.h"
  35#include "llinventoryfunctions.h"
  36#include "llinventorymodelbackgroundfetch.h"
  37#include "llinventorypanel.h"
  38#include "llfoldertype.h"
  39#include "llfloaterinventory.h"// hacked in for the bonus context menu items.
  40#include "llkeyboard.h"
  41#include "lllineeditor.h"
  42#include "llmenugl.h"
  43#include "llpanel.h"
  44#include "llpreview.h"
  45#include "llscrollcontainer.h" // hack to allow scrolling
  46#include "lltooldraganddrop.h"
  47#include "lltrans.h"
  48#include "llui.h"
  49#include "llviewertexture.h"
  50#include "llviewertexturelist.h"
  51#include "llviewerjointattachment.h"
  52#include "llviewermenu.h"
  53#include "lluictrlfactory.h"
  54#include "llviewercontrol.h"
  55#include "llviewerfoldertype.h"
  56#include "llviewerwindow.h"
  57#include "llvoavatar.h"
  58#include "llfloaterproperties.h"
  59#include "llnotificationsutil.h"
  60
  61// Linden library includes
  62#include "lldbstrings.h"
  63#include "llfocusmgr.h"
  64#include "llfontgl.h"
  65#include "llgl.h" 
  66#include "llrender.h"
  67#include "llinventory.h"
  68
  69// Third-party library includes
  70#include <algorithm>
  71
  72///----------------------------------------------------------------------------
  73/// Local function declarations, constants, enums, and typedefs
  74///----------------------------------------------------------------------------
  75
  76const S32 RENAME_WIDTH_PAD = 4;
  77const S32 RENAME_HEIGHT_PAD = 1;
  78const S32 AUTO_OPEN_STACK_DEPTH = 16;
  79const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH
  80			+ LLFolderViewItem::ICON_PAD 
  81			+ LLFolderViewItem::ARROW_SIZE 
  82			+ LLFolderViewItem::TEXT_PAD 
  83			+ /*first few characters*/ 40;
  84const S32 MINIMUM_RENAMER_WIDTH = 80;
  85
  86// *TODO: move in params in xml if necessary. Requires modification of LLFolderView & LLInventoryPanel Params.
  87const S32 STATUS_TEXT_HPAD = 6;
  88const S32 STATUS_TEXT_VPAD = 8;
  89
  90enum {
  91	SIGNAL_NO_KEYBOARD_FOCUS = 1,
  92	SIGNAL_KEYBOARD_FOCUS = 2
  93};
  94
  95F32 LLFolderView::sAutoOpenTime = 1.f;
  96
  97void delete_selected_item(void* user_data);
  98void copy_selected_item(void* user_data);
  99void open_selected_items(void* user_data);
 100void properties_selected_items(void* user_data);
 101void paste_items(void* user_data);
 102
 103
 104//---------------------------------------------------------------------------
 105
 106// Tells all folders in a folderview to sort their items
 107// (and only their items, not folders) by a certain function.
 108class LLSetItemSortFunction : public LLFolderViewFunctor
 109{
 110public:
 111	LLSetItemSortFunction(U32 ordering)
 112		: mSortOrder(ordering) {}
 113	virtual ~LLSetItemSortFunction() {}
 114	virtual void doFolder(LLFolderViewFolder* folder);
 115	virtual void doItem(LLFolderViewItem* item);
 116
 117	U32 mSortOrder;
 118};
 119
 120
 121// Set the sort order.
 122void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder)
 123{
 124	folder->setItemSortOrder(mSortOrder);
 125}
 126
 127// Do nothing.
 128void LLSetItemSortFunction::doItem(LLFolderViewItem* item)
 129{
 130	return;
 131}
 132
 133//---------------------------------------------------------------------------
 134
 135// Tells all folders in a folderview to close themselves
 136// For efficiency, calls setOpenArrangeRecursively().
 137// The calling function must then call:
 138//	LLFolderView* root = getRoot();
 139//	if( root )
 140//	{
 141//		root->arrange( NULL, NULL );
 142//		root->scrollToShowSelection();
 143//	}
 144// to patch things up.
 145class LLCloseAllFoldersFunctor : public LLFolderViewFunctor
 146{
 147public:
 148	LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; }
 149	virtual ~LLCloseAllFoldersFunctor() {}
 150	virtual void doFolder(LLFolderViewFolder* folder);
 151	virtual void doItem(LLFolderViewItem* item);
 152
 153	BOOL mOpen;
 154};
 155
 156
 157// Set the sort order.
 158void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder)
 159{
 160	folder->setOpenArrangeRecursively(mOpen);
 161}
 162
 163// Do nothing.
 164void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item)
 165{ }
 166
 167///----------------------------------------------------------------------------
 168/// Class LLFolderView
 169///----------------------------------------------------------------------------
 170LLFolderView::Params::Params()
 171:	task_id("task_id"),
 172	title("title"),
 173	use_label_suffix("use_label_suffix"),
 174	allow_multiselect("allow_multiselect", true),
 175	show_empty_message("show_empty_message", true),
 176	show_load_status("show_load_status", true),
 177	use_ellipses("use_ellipses", false)
 178{
 179}
 180
 181
 182// Default constructor
 183LLFolderView::LLFolderView(const Params& p)
 184:	LLFolderViewFolder(p),
 185	mRunningHeight(0),
 186	mScrollContainer( NULL ),
 187	mPopupMenuHandle(),
 188	mAllowMultiSelect(p.allow_multiselect),
 189	mShowEmptyMessage(p.show_empty_message),
 190	mShowFolderHierarchy(FALSE),
 191	mSourceID(p.task_id),
 192	mRenameItem( NULL ),
 193	mNeedsScroll( FALSE ),
 194	mUseLabelSuffix(p.use_label_suffix),
 195	mPinningSelectedItem(FALSE),
 196	mNeedsAutoSelect( FALSE ),
 197	mAutoSelectOverride(FALSE),
 198	mNeedsAutoRename(FALSE),
 199	mDebugFilters(FALSE),
 200	mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME),	// This gets overridden by a pref immediately
 201	mFilter( new LLInventoryFilter(p.title) ),
 202	mShowSelectionContext(FALSE),
 203	mShowSingleSelection(FALSE),
 204	mArrangeGeneration(0),
 205	mSignalSelectCallback(0),
 206	mMinWidth(0),
 207	mDragAndDropThisFrame(FALSE),
 208	mCallbackRegistrar(NULL),
 209	mParentPanel(p.parent_panel),
 210	mUseEllipses(p.use_ellipses),
 211	mDraggingOverItem(NULL),
 212	mStatusTextBox(NULL)
 213{
 214	mRoot = this;
 215
 216	mShowLoadStatus = p.show_load_status();
 217
 218	LLRect rect = p.rect;
 219	LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom);
 220	setRect( rect );
 221	reshape(rect.getWidth(), rect.getHeight());
 222	mIsOpen = TRUE; // this view is always open.
 223	mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH);
 224	mAutoOpenCandidate = NULL;
 225	mAutoOpenTimer.stop();
 226	mKeyboardSelection = FALSE;
 227	const LLFolderViewItem::Params& item_params =
 228		LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();
 229	S32 indentation = item_params.folder_indentation();
 230	mIndentation = -indentation; // children start at indentation 0
 231	gIdleCallbacks.addFunction(idle, this);
 232
 233	//clear label
 234	// go ahead and render root folder as usual
 235	// just make sure the label ("Inventory Folder") never shows up
 236	mLabel = LLStringUtil::null;
 237
 238	//mRenamer->setWriteableBgColor(LLColor4::white);
 239	// Escape is handled by reverting the rename, not commiting it (default behavior)
 240	LLLineEditor::Params params;
 241	params.name("ren");
 242	params.rect(rect);
 243	params.font(getLabelFontForStyle(LLFontGL::NORMAL));
 244	params.max_length.bytes(DB_INV_ITEM_NAME_STR_LEN);
 245	params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2));
 246	params.prevalidate_callback(&LLTextValidate::validateASCIIPrintableNoPipe);
 247	params.commit_on_focus_lost(true);
 248	params.visible(false);
 249	mRenamer = LLUICtrlFactory::create<LLLineEditor> (params);
 250	addChild(mRenamer);
 251
 252	// Textbox
 253	LLTextBox::Params text_p;
 254	LLFontGL* font = getLabelFontForStyle(mLabelStyle);
 255	LLRect new_r = LLRect(rect.mLeft + ICON_PAD,
 256			      rect.mTop - TEXT_PAD,
 257			      rect.mRight,
 258			      rect.mTop - TEXT_PAD - llfloor(font->getLineHeight()));
 259	text_p.rect(new_r);
 260	text_p.name(std::string(p.name));
 261	text_p.font(font);
 262	text_p.visible(false);
 263	text_p.parse_urls(true);
 264	text_p.wrap(true); // allow multiline text. See EXT-7564, EXT-7047
 265	// set text padding the same as in People panel. EXT-7047, EXT-4837
 266	text_p.h_pad(STATUS_TEXT_HPAD);
 267	text_p.v_pad(STATUS_TEXT_VPAD);
 268	mStatusTextBox = LLUICtrlFactory::create<LLTextBox> (text_p);
 269	mStatusTextBox->setFollowsLeft();
 270	mStatusTextBox->setFollowsTop();
 271	//addChild(mStatusTextBox);
 272
 273
 274	// make the popup menu available
 275	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 276	if (!menu)
 277	{
 278		menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu");
 279	}
 280	menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
 281	mPopupMenuHandle = menu->getHandle();
 282
 283	mListener->openItem();
 284}
 285
 286// Destroys the object
 287LLFolderView::~LLFolderView( void )
 288{
 289	closeRenamer();
 290
 291	// The release focus call can potentially call the
 292	// scrollcontainer, which can potentially be called with a partly
 293	// destroyed scollcontainer. Just null it out here, and no worries
 294	// about calling into the invalid scroll container.
 295	// Same with the renamer.
 296	mScrollContainer = NULL;
 297	mRenameItem = NULL;
 298	mRenamer = NULL;
 299	mStatusTextBox = NULL;
 300
 301	mAutoOpenItems.removeAllNodes();
 302	gIdleCallbacks.deleteFunction(idle, this);
 303
 304	if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
 305
 306	mAutoOpenItems.removeAllNodes();
 307	clearSelection();
 308	mItems.clear();
 309	mFolders.clear();
 310
 311	mItemMap.clear();
 312
 313	delete mFilter;
 314	mFilter = NULL;
 315}
 316
 317BOOL LLFolderView::canFocusChildren() const
 318{
 319	return FALSE;
 320}
 321
 322static LLFastTimer::DeclareTimer FTM_SORT("Sort Inventory");
 323
 324void LLFolderView::setSortOrder(U32 order)
 325{
 326	if (order != mSortOrder)
 327	{
 328		LLFastTimer t(FTM_SORT);
 329		
 330		mSortOrder = order;
 331
 332		sortBy(order);
 333		arrangeAll();
 334	}
 335}
 336
 337
 338U32 LLFolderView::getSortOrder() const
 339{
 340	return mSortOrder;
 341}
 342
 343BOOL LLFolderView::addFolder( LLFolderViewFolder* folder)
 344{
 345	// enforce sort order of My Inventory followed by Library
 346	if (folder->getListener()->getUUID() == gInventory.getLibraryRootFolderID())
 347	{
 348		mFolders.push_back(folder);
 349	}
 350	else
 351	{
 352		mFolders.insert(mFolders.begin(), folder);
 353	}
 354	folder->setShowLoadStatus(mShowLoadStatus);
 355	folder->setOrigin(0, 0);
 356	folder->reshape(getRect().getWidth(), 0);
 357	folder->setVisible(FALSE);
 358	addChild( folder );
 359	folder->dirtyFilter();
 360	folder->requestArrange();
 361	return TRUE;
 362}
 363
 364void LLFolderView::closeAllFolders()
 365{
 366	// Close all the folders
 367	setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN);
 368	arrangeAll();
 369}
 370
 371void LLFolderView::openTopLevelFolders()
 372{
 373	for (folders_t::iterator iter = mFolders.begin();
 374		 iter != mFolders.end();)
 375	{
 376		folders_t::iterator fit = iter++;
 377		(*fit)->setOpen(TRUE);
 378	}
 379}
 380
 381void LLFolderView::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse)
 382{
 383	// call base class to do proper recursion
 384	LLFolderViewFolder::setOpenArrangeRecursively(openitem, recurse);
 385	// make sure root folder is always open
 386	mIsOpen = TRUE;
 387}
 388
 389static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange");
 390
 391// This view grows and shinks to enclose all of its children items and folders.
 392S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation )
 393{
 394	if (getListener()->getUUID().notNull())
 395	{
 396		if (mNeedsSort)
 397		{
 398			mFolders.sort(mSortFunction);
 399			mItems.sort(mSortFunction);
 400			mNeedsSort = false;
 401		}
 402	}
 403
 404	LLFastTimer t2(FTM_ARRANGE);
 405
 406	filter_generation = mFilter->getMinRequiredGeneration();
 407	mMinWidth = 0;
 408
 409	mHasVisibleChildren = hasFilteredDescendants(filter_generation);
 410	// arrange always finishes, so optimistically set the arrange generation to the most current
 411	mLastArrangeGeneration = getRoot()->getArrangeGeneration();
 412
 413	LLInventoryFilter::EFolderShow show_folder_state =
 414		getRoot()->getFilter()->getShowFolderState();
 415
 416	S32 total_width = LEFT_PAD;
 417	S32 running_height = mDebugFilters ? llceil(LLFontGL::getFontMonospace()->getLineHeight()) : 0;
 418	S32 target_height = running_height;
 419	S32 parent_item_height = getRect().getHeight();
 420
 421	for (folders_t::iterator iter = mFolders.begin();
 422		 iter != mFolders.end();)
 423	{
 424		folders_t::iterator fit = iter++;
 425		LLFolderViewFolder* folderp = (*fit);
 426		if (getDebugFilters())
 427		{
 428			folderp->setVisible(TRUE);
 429		}
 430		else
 431		{
 432			folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
 433									(folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
 434		}
 435
 436		if (folderp->getVisible())
 437		{
 438			S32 child_height = 0;
 439			S32 child_width = 0;
 440			S32 child_top = parent_item_height - running_height;
 441			
 442			target_height += folderp->arrange( &child_width, &child_height, filter_generation );
 443
 444			mMinWidth = llmax(mMinWidth, child_width);
 445			total_width = llmax( total_width, child_width );
 446			running_height += child_height;
 447			folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() );
 448		}
 449	}
 450
 451	for (items_t::iterator iter = mItems.begin();
 452		 iter != mItems.end();)
 453	{
 454		items_t::iterator iit = iter++;
 455		LLFolderViewItem* itemp = (*iit);
 456		itemp->setVisible(itemp->getFiltered(filter_generation));
 457
 458		if (itemp->getVisible())
 459		{
 460			S32 child_width = 0;
 461			S32 child_height = 0;
 462			S32 child_top = parent_item_height - running_height;
 463			
 464			target_height += itemp->arrange( &child_width, &child_height, filter_generation );
 465			itemp->reshape(itemp->getRect().getWidth(), child_height);
 466
 467			mMinWidth = llmax(mMinWidth, child_width);
 468			total_width = llmax( total_width, child_width );
 469			running_height += child_height;
 470			itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() );
 471		}
 472	}
 473
 474	if(!mHasVisibleChildren)// is there any filtered items ?
 475	{
 476		//Nope. We need to display status textbox, let's reserve some place for it
 477		running_height = mStatusTextBox->getTextPixelHeight();
 478		target_height = running_height;
 479	}
 480
 481	mRunningHeight = running_height;
 482	LLRect scroll_rect = mScrollContainer->getContentWindowRect();
 483	reshape( llmax(scroll_rect.getWidth(), total_width), running_height );
 484
 485	LLRect new_scroll_rect = mScrollContainer->getContentWindowRect();
 486	if (new_scroll_rect.getWidth() != scroll_rect.getWidth())
 487	{
 488		reshape( llmax(scroll_rect.getWidth(), total_width), running_height );
 489	}
 490
 491	// move item renamer text field to item's new position
 492	updateRenamerPosition();
 493
 494	mTargetHeight = (F32)target_height;
 495	return llround(mTargetHeight);
 496}
 497
 498const std::string LLFolderView::getFilterSubString(BOOL trim)
 499{
 500	return mFilter->getFilterSubString(trim);
 501}
 502
 503static LLFastTimer::DeclareTimer FTM_FILTER("Filter Inventory");
 504
 505void LLFolderView::filter( LLInventoryFilter& filter )
 506{
 507	LLFastTimer t2(FTM_FILTER);
 508	filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000));
 509
 510	if (getCompletedFilterGeneration() < filter.getCurrentGeneration())
 511	{
 512		mPassedFilter = FALSE;
 513		mMinWidth = 0;
 514		LLFolderViewFolder::filter(filter);
 515	}
 516	else
 517	{
 518		mPassedFilter = TRUE;
 519	}
 520}
 521
 522void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent)
 523{
 524	LLRect scroll_rect;
 525	if (mScrollContainer)
 526	{
 527		LLView::reshape(width, height, called_from_parent);
 528		scroll_rect = mScrollContainer->getContentWindowRect();
 529	}
 530	width = llmax(mMinWidth, scroll_rect.getWidth());
 531	height = llmax(mRunningHeight, scroll_rect.getHeight());
 532
 533	// restrict width with scroll container's width
 534	if (mUseEllipses)
 535		width = scroll_rect.getWidth();
 536
 537	LLView::reshape(width, height, called_from_parent);
 538
 539	mReshapeSignal(mSelectedItems, FALSE);
 540}
 541
 542void LLFolderView::addToSelectionList(LLFolderViewItem* item)
 543{
 544	if (item->isSelected())
 545	{
 546		removeFromSelectionList(item);
 547	}
 548	if (mSelectedItems.size())
 549	{
 550		mSelectedItems.back()->setIsCurSelection(FALSE);
 551	}
 552	item->setIsCurSelection(TRUE);
 553	mSelectedItems.push_back(item);
 554}
 555
 556void LLFolderView::removeFromSelectionList(LLFolderViewItem* item)
 557{
 558	if (mSelectedItems.size())
 559	{
 560		mSelectedItems.back()->setIsCurSelection(FALSE);
 561	}
 562
 563	selected_items_t::iterator item_iter;
 564	for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();)
 565	{
 566		if (*item_iter == item)
 567		{
 568			item_iter = mSelectedItems.erase(item_iter);
 569		}
 570		else
 571		{
 572			++item_iter;
 573		}
 574	}
 575	if (mSelectedItems.size())
 576	{
 577		mSelectedItems.back()->setIsCurSelection(TRUE);
 578	}
 579}
 580
 581LLFolderViewItem* LLFolderView::getCurSelectedItem( void )
 582{
 583	if(mSelectedItems.size())
 584	{
 585		LLFolderViewItem* itemp = mSelectedItems.back();
 586		llassert(itemp->getIsCurSelection());
 587		return itemp;
 588	}
 589	return NULL;
 590}
 591
 592
 593// Record the selected item and pass it down the hierachy.
 594BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem,
 595								BOOL take_keyboard_focus)
 596{
 597	mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS;
 598
 599	if( selection == this )
 600	{
 601		return FALSE;
 602	}
 603
 604	if( selection && take_keyboard_focus)
 605	{
 606		mParentPanel->setFocus(TRUE);
 607	}
 608
 609	// clear selection down here because change of keyboard focus can potentially
 610	// affect selection
 611	clearSelection();
 612
 613	if(selection)
 614	{
 615		addToSelectionList(selection);
 616	}
 617
 618	BOOL rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus);
 619	if(openitem && selection)
 620	{
 621		selection->getParentFolder()->requestArrange();
 622	}
 623
 624	llassert(mSelectedItems.size() <= 1);
 625
 626	return rv;
 627}
 628
 629void LLFolderView::setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus)
 630{
 631	LLFolderViewItem* itemp = getItemByID(obj_id);
 632	if(itemp && itemp->getListener())
 633	{
 634		itemp->arrangeAndSet(TRUE, take_keyboard_focus);
 635		mSelectThisID.setNull();
 636		return;
 637	}
 638	else
 639	{
 640		// save the desired item to be selected later (if/when ready)
 641		mSelectThisID = obj_id;
 642	}
 643}
 644
 645void LLFolderView::updateSelection()
 646{
 647	if (mSelectThisID.notNull())
 648	{
 649		setSelectionByID(mSelectThisID, false);
 650	}
 651}
 652
 653BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected)
 654{
 655	BOOL rv = FALSE;
 656
 657	// can't select root folder
 658	if(!selection || selection == this)
 659	{
 660		return FALSE;
 661	}
 662
 663	if (!mAllowMultiSelect)
 664	{
 665		clearSelection();
 666	}
 667
 668	selected_items_t::iterator item_iter;
 669	for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
 670	{
 671		if (*item_iter == selection)
 672		{
 673			break;
 674		}
 675	}
 676
 677	BOOL on_list = (item_iter != mSelectedItems.end());
 678
 679	if(selected && !on_list)
 680	{
 681		addToSelectionList(selection);
 682	}
 683	if(!selected && on_list)
 684	{
 685		removeFromSelectionList(selection);
 686	}
 687
 688	rv = LLFolderViewFolder::changeSelection(selection, selected);
 689
 690	mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS;
 691	
 692	return rv;
 693}
 694
 695static LLFastTimer::DeclareTimer FTM_SANITIZE_SELECTION("Sanitize Selection");
 696void LLFolderView::sanitizeSelection()
 697{
 698	LLFastTimer _(FTM_SANITIZE_SELECTION);
 699	// store off current item in case it is automatically deselected
 700	// and we want to preserve context
 701	LLFolderViewItem* original_selected_item = getCurSelectedItem();
 702
 703	// Cache "Show all folders" filter setting
 704	BOOL show_all_folders = (getRoot()->getFilter()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS);
 705
 706	std::vector<LLFolderViewItem*> items_to_remove;
 707	selected_items_t::iterator item_iter;
 708	for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
 709	{
 710		LLFolderViewItem* item = *item_iter;
 711
 712		// ensure that each ancestor is open and potentially passes filtering
 713		BOOL visible = item->potentiallyVisible(); // initialize from filter state for this item
 714		// modify with parent open and filters states
 715		LLFolderViewFolder* parent_folder = item->getParentFolder();
 716		if ( parent_folder )
 717		{
 718			if ( show_all_folders )
 719			{	// "Show all folders" is on, so this folder is visible
 720				visible = TRUE;
 721			}
 722			else
 723			{	// Move up through parent folders and see what's visible
 724				while(parent_folder)
 725				{
 726					visible = visible && parent_folder->isOpen() && parent_folder->potentiallyVisible();
 727					parent_folder = parent_folder->getParentFolder();
 728				}
 729			}
 730		}
 731
 732		//  deselect item if any ancestor is closed or didn't pass filter requirements.
 733		if (!visible)
 734		{
 735			items_to_remove.push_back(item);
 736		}
 737
 738		// disallow nested selections (i.e. folder items plus one or more ancestors)
 739		// could check cached mum selections count and only iterate if there are any
 740		// but that may be a premature optimization.
 741		selected_items_t::iterator other_item_iter;
 742		for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter)
 743		{
 744			LLFolderViewItem* other_item = *other_item_iter;
 745			for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder())
 746			{
 747				if (parent_folder == item)
 748				{
 749					// this is a descendent of the current folder, remove from list
 750					items_to_remove.push_back(other_item);
 751					break;
 752				}
 753			}
 754		}
 755
 756		// Don't allow invisible items (such as root folders) to be selected.
 757		if (item == getRoot())
 758		{
 759			items_to_remove.push_back(item);
 760		}
 761	}
 762
 763	std::vector<LLFolderViewItem*>::iterator item_it;
 764	for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it )
 765	{
 766		changeSelection(*item_it, FALSE); // toggle selection (also removes from list)
 767	}
 768
 769	// if nothing selected after prior constraints...
 770	if (mSelectedItems.empty())
 771	{
 772		// ...select first available parent of original selection, or "My Inventory" otherwise
 773		LLFolderViewItem* new_selection = NULL;
 774		if (original_selected_item)
 775		{
 776			for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder();
 777				parent_folder;
 778				parent_folder = parent_folder->getParentFolder())
 779			{
 780				if (parent_folder->potentiallyVisible())
 781				{
 782					// give initial selection to first ancestor folder that potentially passes the filter
 783					if (!new_selection)
 784					{
 785						new_selection = parent_folder;
 786					}
 787
 788					// if any ancestor folder of original item is closed, move the selection up 
 789					// to the highest closed
 790					if (!parent_folder->isOpen())
 791					{	
 792						new_selection = parent_folder;
 793					}
 794				}
 795			}
 796		}
 797		else
 798		{
 799			new_selection = NULL;
 800		}
 801
 802		if (new_selection)
 803		{
 804			setSelection(new_selection, FALSE, FALSE);
 805		}
 806	}
 807}
 808
 809void LLFolderView::clearSelection()
 810{
 811	for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); 
 812		 item_it != mSelectedItems.end(); 
 813		 ++item_it)
 814	{
 815		(*item_it)->setUnselected();
 816	}
 817
 818	mSelectedItems.clear();
 819	mSelectThisID.setNull();
 820}
 821
 822std::set<LLUUID> LLFolderView::getSelectionList() const
 823{
 824	std::set<LLUUID> selection;
 825	for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); 
 826		 item_it != mSelectedItems.end(); 
 827		 ++item_it)
 828	{
 829		selection.insert((*item_it)->getListener()->getUUID());
 830	}
 831	return selection;
 832}
 833
 834BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source)
 835{
 836	std::vector<EDragAndDropType> types;
 837	uuid_vec_t cargo_ids;
 838	selected_items_t::iterator item_it;
 839	BOOL can_drag = TRUE;
 840	if (!mSelectedItems.empty())
 841	{
 842		for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
 843		{
 844			EDragAndDropType type = DAD_NONE;
 845			LLUUID id = LLUUID::null;
 846			can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id);
 847
 848			types.push_back(type);
 849			cargo_ids.push_back(id);
 850		}
 851
 852		LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, source, mSourceID); 
 853	}
 854	return can_drag;
 855}
 856
 857void LLFolderView::commitRename( const LLSD& data )
 858{
 859	finishRenamingItem();
 860}
 861
 862void LLFolderView::draw()
 863{
 864	static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", LLColor4::white);
 865	if (mDebugFilters)
 866	{
 867		std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d",
 868										mFilter->getCurrentGeneration(), mFilter->getMinRequiredGeneration(), mFilter->getMustPassGeneration());
 869		LLFontGL::getFontMonospace()->renderUTF8(current_filter_string, 0, 2, 
 870			getRect().getHeight() - LLFontGL::getFontMonospace()->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), 
 871			LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
 872	}
 873
 874	//LLFontGL* font = getLabelFontForStyle(mLabelStyle);
 875
 876	// if cursor has moved off of me during drag and drop
 877	// close all auto opened folders
 878	if (!mDragAndDropThisFrame)
 879	{
 880		closeAutoOpenedFolders();
 881	}
 882
 883	// while dragging, update selection rendering to reflect single/multi drag status
 884	if (LLToolDragAndDrop::getInstance()->hasMouseCapture())
 885	{
 886		EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept();
 887		if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE)
 888		{
 889			setShowSingleSelection(TRUE);
 890		}
 891		else
 892		{
 893			setShowSingleSelection(FALSE);
 894		}
 895	}
 896	else
 897	{
 898		setShowSingleSelection(FALSE);
 899	}
 900
 901
 902	if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size())
 903	{
 904		mSearchString.clear();
 905	}
 906
 907	if (hasVisibleChildren()
 908		|| mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS)
 909	{
 910		mStatusText.clear();
 911		mStatusTextBox->setVisible( FALSE );
 912	}
 913	else if (mShowEmptyMessage)
 914	{
 915		if (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() || mCompletedFilterGeneration < mFilter->getMinRequiredGeneration())
 916		{
 917			mStatusText = LLTrans::getString("Searching");
 918		}
 919		else
 920		{
 921			if (getFilter())
 922			{
 923				LLStringUtil::format_map_t args;
 924				args["[SEARCH_TERM]"] = LLURI::escape(getFilter()->getFilterSubStringOrig());
 925				mStatusText = LLTrans::getString(getFilter()->getEmptyLookupMessage(), args);
 926			}
 927		}
 928		mStatusTextBox->setValue(mStatusText);
 929		mStatusTextBox->setVisible( TRUE );
 930		
 931		// firstly reshape message textbox with current size. This is necessary to
 932		// LLTextBox::getTextPixelHeight works properly
 933		const LLRect local_rect = getLocalRect();
 934		mStatusTextBox->setShape(local_rect);
 935
 936		// get preferable text height...
 937		S32 pixel_height = mStatusTextBox->getTextPixelHeight();
 938		bool height_changed = local_rect.getHeight() != pixel_height;
 939		if (height_changed)
 940		{
 941			// ... if it does not match current height, lets rearrange current view.
 942			// This will indirectly call ::arrange and reshape of the status textbox.
 943			// We should call this method to also notify parent about required rect.
 944			// See EXT-7564, EXT-7047.
 945			arrangeFromRoot();
 946		}
 947	}
 948
 949	// skip over LLFolderViewFolder::draw since we don't want the folder icon, label, 
 950	// and arrow for the root folder
 951	LLView::draw();
 952
 953	mDragAndDropThisFrame = FALSE;
 954}
 955
 956void LLFolderView::finishRenamingItem( void )
 957{
 958	if(!mRenamer)
 959	{
 960		return;
 961	}
 962	if( mRenameItem )
 963	{
 964		mRenameItem->rename( mRenamer->getText() );
 965	}
 966
 967	closeRenamer();
 968
 969	// List is re-sorted alphabeticly, so scroll to make sure the selected item is visible.
 970	scrollToShowSelection();
 971}
 972
 973void LLFolderView::closeRenamer( void )
 974{
 975	if (mRenamer && mRenamer->getVisible())
 976	{
 977		// Triggers onRenamerLost() that actually closes the renamer.
 978		gViewerWindow->removePopup(mRenamer);
 979	}
 980}
 981
 982void LLFolderView::removeSelectedItems( void )
 983{
 984	if (mSelectedItems.empty()) return;
 985	LLSD args;
 986	args["QUESTION"] = LLTrans::getString(mSelectedItems.size() > 1 ? "DeleteItems" :  "DeleteItem");
 987	LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLFolderView::onItemsRemovalConfirmation, this, _1, _2));
 988}
 989
 990bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector<LLFolderViewItem*>& selectedItems)
 991{
 992	LLFolderViewItem* item_parent = dynamic_cast<LLFolderViewItem*>(item->getParent());
 993
 994	if (item_parent)
 995	{
 996		for(std::vector<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it)
 997		{
 998			const LLFolderViewItem* const selected_item = (*it);
 999
1000			LLFolderViewItem* parent = item_parent;
1001
1002			while (parent)
1003			{
1004				if (selected_item == parent)
1005				{
1006					return true;
1007				}
1008
1009				parent = dynamic_cast<LLFolderViewItem*>(parent->getParent());
1010			}
1011		}
1012	}
1013
1014	return false;
1015}
1016
1017void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response)
1018{
1019	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
1020	if (option != 0) return; // canceled
1021
1022	if(getVisible() && getEnabled())
1023	{
1024		// just in case we're removing the renaming item.
1025		mRenameItem = NULL;
1026
1027		// create a temporary structure which we will use to remove
1028		// items, since the removal will futz with internal data
1029		// structures.
1030		std::vector<LLFolderViewItem*> items;
1031		S32 count = mSelectedItems.size();
1032		if(count == 0) return;
1033		LLFolderViewItem* item = NULL;
1034		selected_items_t::iterator item_it;
1035		for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
1036		{
1037			item = *item_it;
1038			if (item && item->isRemovable())
1039			{
1040				items.push_back(item);
1041			}
1042			else
1043			{
1044				llinfos << "Cannot delete " << item->getName() << llendl;
1045				return;
1046			}
1047		}
1048
1049		// iterate through the new container.
1050		count = items.size();
1051		LLUUID new_selection_id;
1052		if(count == 1)
1053		{
1054			LLFolderViewItem* item_to_delete = items[0];
1055			LLFolderViewFolder* parent = item_to_delete->getParentFolder();
1056			LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE);
1057			if (!new_selection)
1058			{
1059				new_selection = item_to_delete->getPreviousOpenNode(FALSE);
1060			}
1061			if(parent)
1062			{
1063				if (parent->removeItem(item_to_delete))
1064				{
1065					// change selection on successful delete
1066					if (new_selection)
1067					{
1068						setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus());
1069					}
1070					else
1071					{
1072						setSelectionFromRoot(NULL, mParentPanel->hasFocus());
1073					}
1074				}
1075			}
1076			arrangeAll();
1077		}
1078		else if (count > 1)
1079		{
1080			LLDynamicArray<LLFolderViewEventListener*> listeners;
1081			LLFolderViewEventListener* listener;
1082			LLFolderViewItem* last_item = items[count - 1];
1083			LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE);
1084			while(new_selection && new_selection->isSelected())
1085			{
1086				new_selection = new_selection->getNextOpenNode(FALSE);
1087			}
1088			if (!new_selection)
1089			{
1090				new_selection = last_item->getPreviousOpenNode(FALSE);
1091				while (new_selection && (new_selection->isSelected() || isDescendantOfASelectedItem(new_selection, items)))
1092				{
1093					new_selection = new_selection->getPreviousOpenNode(FALSE);
1094				}
1095			}
1096			if (new_selection)
1097			{
1098				setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus());
1099			}
1100			else
1101			{
1102				setSelectionFromRoot(NULL, mParentPanel->hasFocus());
1103			}
1104
1105			for(S32 i = 0; i < count; ++i)
1106			{
1107				listener = items[i]->getListener();
1108				if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::FAIL))
1109				{
1110					listeners.put(listener);
1111				}
1112			}
1113			listener = listeners.get(0);
1114			if(listener)
1115			{
1116				listener->removeBatch(listeners);
1117			}
1118		}
1119		arrangeAll();
1120		scrollToShowSelection();
1121	}
1122}
1123
1124// open the selected item.
1125void LLFolderView::openSelectedItems( void )
1126{
1127	if(getVisible() && getEnabled())
1128	{
1129		if (mSelectedItems.size() == 1)
1130		{
1131			mSelectedItems.front()->openItem();
1132		}
1133		else
1134		{
1135			LLMultiPreview* multi_previewp = new LLMultiPreview();
1136			LLMultiProperties* multi_propertiesp = new LLMultiProperties();
1137
1138			selected_items_t::iterator item_it;
1139			for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
1140			{
1141				// IT_{OBJECT,ATTACHMENT} creates LLProperties
1142				// floaters; others create LLPreviews.  Put
1143				// each one in the right type of container.
1144				LLFolderViewEventListener* listener = (*item_it)->getListener();
1145				bool is_prop = listener && (listener->getInventoryType() == LLInventoryType::IT_OBJECT || listener->getInventoryType() == LLInventoryType::IT_ATTACHMENT);
1146				if (is_prop)
1147					LLFloater::setFloaterHost(multi_propertiesp);
1148				else
1149					LLFloater::setFloaterHost(multi_previewp);
1150				(*item_it)->openItem();
1151			}
1152
1153			LLFloater::setFloaterHost(NULL);
1154			// *NOTE: LLMulti* will safely auto-delete when open'd
1155			// without any children.
1156			multi_previewp->openFloater(LLSD());
1157			multi_propertiesp->openFloater(LLSD());
1158		}
1159	}
1160}
1161
1162void LLFolderView::propertiesSelectedItems( void )
1163{
1164	if(getVisible() && getEnabled())
1165	{
1166		if (mSelectedItems.size() == 1)
1167		{
1168			LLFolderViewItem* folder_item = mSelectedItems.front();
1169			if(!folder_item) return;
1170			folder_item->getListener()->showProperties();
1171		}
1172		else
1173		{
1174			LLMultiProperties* multi_propertiesp = new LLMultiProperties();
1175
1176			LLFloater::setFloaterHost(multi_propertiesp);
1177
1178			selected_items_t::iterator item_it;
1179			for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
1180			{
1181				(*item_it)->getListener()->showProperties();
1182			}
1183
1184			LLFloater::setFloaterHost(NULL);
1185			multi_propertiesp->openFloater(LLSD());
1186		}
1187	}
1188}
1189
1190void LLFolderView::changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type)
1191{
1192	LLFolderBridge *folder_bridge = LLFolderBridge::sSelf.get();
1193
1194	if (!folder_bridge) return;
1195	LLViewerInventoryCategory *cat = folder_bridge->getCategory();
1196	if (!cat) return;
1197	cat->changeType(new_folder_type);
1198}
1199
1200void LLFolderView::autoOpenItem( LLFolderViewFolder* item )
1201{
1202	if ((mAutoOpenItems.check() == item) || 
1203		(mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) ||
1204		item->isOpen())
1205	{
1206		return;
1207	}
1208
1209	// close auto-opened folders
1210	LLFolderViewFolder* close_item = mAutoOpenItems.check();
1211	while (close_item && close_item != item->getParentFolder())
1212	{
1213		mAutoOpenItems.pop();
1214		close_item->setOpenArrangeRecursively(FALSE);
1215		close_item = mAutoOpenItems.check();
1216	}
1217
1218	item->requestArrange();
1219
1220	mAutoOpenItems.push(item);
1221	
1222	item->setOpen(TRUE);
1223	LLRect content_rect = mScrollContainer->getContentWindowRect();
1224	LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0);
1225	scrollToShowItem(item, constraint_rect);
1226}
1227
1228void LLFolderView::closeAutoOpenedFolders()
1229{
1230	while (mAutoOpenItems.check())
1231	{
1232		LLFolderViewFolder* close_item = mAutoOpenItems.pop();
1233		close_item->setOpen(FALSE);
1234	}
1235
1236	if (mAutoOpenCandidate)
1237	{
1238		mAutoOpenCandidate->setAutoOpenCountdown(0.f);
1239	}
1240	mAutoOpenCandidate = NULL;
1241	mAutoOpenTimer.stop();
1242}
1243
1244BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder)
1245{
1246	if (folder && mAutoOpenCandidate == folder)
1247	{
1248		if (mAutoOpenTimer.getStarted())
1249		{
1250			if (!mAutoOpenCandidate->isOpen())
1251			{
1252				mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f));
1253			}
1254			if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime)
1255			{
1256				autoOpenItem(folder);
1257				mAutoOpenTimer.stop();
1258				return TRUE;
1259			}
1260		}
1261		return FALSE;
1262	}
1263
1264	// otherwise new candidate, restart timer
1265	if (mAutoOpenCandidate)
1266	{
1267		mAutoOpenCandidate->setAutoOpenCountdown(0.f);
1268	}
1269	mAutoOpenCandidate = folder;
1270	mAutoOpenTimer.start();
1271	return FALSE;
1272}
1273
1274BOOL LLFolderView::canCopy() const
1275{
1276	if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0)))
1277	{
1278		return FALSE;
1279	}
1280	
1281	for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
1282	{
1283		const LLFolderViewItem* item = *selected_it;
1284		if (!item->getListener()->isItemCopyable())
1285		{
1286			return FALSE;
1287		}
1288	}
1289	return TRUE;
1290}
1291
1292// copy selected item
1293void LLFolderView::copy()
1294{
1295	// *NOTE: total hack to clear the inventory clipboard
1296	LLInventoryClipboard::instance().reset();
1297	S32 count = mSelectedItems.size();
1298	if(getVisible() && getEnabled() && (count > 0))
1299	{
1300		LLFolderViewEventListener* listener = NULL;
1301		selected_items_t::iterator item_it;
1302		for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
1303		{
1304			listener = (*item_it)->getListener();
1305			if(listener)
1306			{
1307				listener->copyToClipboard();
1308			}
1309		}
1310	}
1311	mSearchString.clear();
1312}
1313
1314BOOL LLFolderView::canCut() const
1315{
1316	if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0)))
1317	{
1318		return FALSE;
1319	}
1320	
1321	for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
1322	{
1323		const LLFolderViewItem* item = *selected_it;
1324		const LLFolderViewEventListener* listener = item->getListener();
1325
1326		if (!listener || !listener->isItemRemovable())
1327		{
1328			return FALSE;
1329		}
1330	}
1331	return TRUE;
1332}
1333
1334void LLFolderView::cut()
1335{
1336	// clear the inventory clipboard
1337	LLInventoryClipboard::instance().reset();
1338	S32 count = mSelectedItems.size();
1339	if(getVisible() && getEnabled() && (count > 0))
1340	{
1341		LLFolderViewEventListener* listener = NULL;
1342		selected_items_t::iterator item_it;
1343		for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
1344		{
1345			listener = (*item_it)->getListener();
1346			if(listener)
1347			{
1348				listener->cutToClipboard();
1349			}
1350		}
1351	}
1352	mSearchString.clear();
1353}
1354
1355BOOL LLFolderView::canPaste() const
1356{
1357	if (mSelectedItems.empty())
1358	{
1359		return FALSE;
1360	}
1361
1362	if(getVisible() && getEnabled())
1363	{
1364		for (selected_items_t::const_iterator item_it = mSelectedItems.begin();
1365			 item_it != mSelectedItems.end(); ++item_it)
1366		{
1367			// *TODO: only check folders and parent folders of items
1368			const LLFolderViewItem* item = (*item_it);
1369			const LLFolderViewEventListener* listener = item->getListener();
1370			if(!listener || !listener->isClipboardPasteable())
1371			{
1372				const LLFolderViewFolder* folderp = item->getParentFolder();
1373				listener = folderp->getListener();
1374				if (!listener || !listener->isClipboardPasteable())
1375				{
1376					return FALSE;
1377				}
1378			}
1379		}
1380		return TRUE;
1381	}
1382	return FALSE;
1383}
1384
1385// paste selected item
1386void LLFolderView::paste()
1387{
1388	if(getVisible() && getEnabled())
1389	{
1390		// find set of unique folders to paste into
1391		std::set<LLFolderViewItem*> folder_set;
1392
1393		selected_items_t::iterator selected_it;
1394		for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
1395		{
1396			LLFolderViewItem* item = *selected_it;
1397			LLFolderViewEventListener* listener = item->getListener();
1398			if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY)
1399			{
1400				item = item->getParentFolder();
1401			}
1402			folder_set.insert(item);
1403		}
1404
1405		std::set<LLFolderViewItem*>::iterator set_iter;
1406		for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter)
1407		{
1408			LLFolderViewEventListener* listener = (*set_iter)->getListener();
1409			if(listener && listener->isClipboardPasteable())
1410			{
1411				listener->pasteFromClipboard();
1412			}
1413		}
1414	}
1415	mSearchString.clear();
1416}
1417
1418// public rename functionality - can only start the process
1419void LLFolderView::startRenamingSelectedItem( void )
1420{
1421	// make sure selection is visible
1422	scrollToShowSelection();
1423
1424	S32 count = mSelectedItems.size();
1425	LLFolderViewItem* item = NULL;
1426	if(count > 0)
1427	{
1428		item = mSelectedItems.front();
1429	}
1430	if(getVisible() && getEnabled() && (count == 1) && item && item->getListener() &&
1431	   item->getListener()->isItemRenameable())
1432	{
1433		mRenameItem = item;
1434
1435		updateRenamerPosition();
1436
1437
1438		mRenamer->setText(item->getName());
1439		mRenamer->selectAll();
1440		mRenamer->setVisible( TRUE );
1441		// set focus will fail unless item is visible
1442		mRenamer->setFocus( TRUE );
1443		mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this));
1444		gViewerWindow->addPopup(mRenamer);
1445	}
1446}
1447
1448BOOL LLFolderView::handleKeyHere( KEY key, MASK mask )
1449{
1450	BOOL handled = FALSE;
1451
1452	// SL-51858: Key presses are not being passed to the Popup menu.
1453	// A proper fix is non-trivial so instead just close the menu.
1454	LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
1455	if (menu && menu->isOpen())
1456	{
1457		LLMenuGL::sMenuContainer->hideMenus();
1458	}
1459
1460	LLView *item = NULL;
1461	if (getChildCount() > 0)
1462	{
1463		item = *(getChildList()->begin());
1464	}
1465
1466	switch( key )
1467	{
1468	case KEY_F2:
1469		mSearchString.clear();
1470		startRenamingSelectedItem();
1471		handled = TRUE;
1472		break;
1473
1474	case KEY_RETURN:
1475		if (mask == MASK_NONE)
1476		{
1477			if( mRenameItem && mRenamer->getVisible() )
1478			{
1479				finishRenamingItem();
1480				mSearchString.clear();
1481				handled = TRUE;
1482			}
1483			else
1484			{
1485				LLFolderView::openSelectedItems();
1486				handled = TRUE;
1487			}
1488		}
1489		break;
1490
1491	case KEY_ESCAPE:
1492		if( mRenameItem && mRenamer->getVisible() )
1493		{
1494			closeRenamer();
1495			handled = TRUE;
1496		}
1497		mSearchString.clear();
1498		break;
1499
1500	case KEY_PAGE_UP:
1501		mSearchString.clear();
1502		mScrollContainer->pageUp(30);
1503		handled = TRUE;
1504		break;
1505
1506	case KEY_PAGE_DOWN:
1507		mSearchString.clear();
1508		mScrollContainer->pageDown(30);
1509		handled = TRUE;
1510		break;
1511
1512	case KEY_HOME:
1513		mSearchString.clear();
1514		mScrollContainer->goToTop();
1515		handled = TRUE;
1516		break;
1517
1518	case KEY_END:
1519		mSearchString.clear();
1520		mScrollContainer->goToBottom();
1521		break;
1522
1523	case KEY_DOWN:
1524		if((mSelectedItems.size() > 0) && mScrollContainer)
1525		{
1526			LLFolderViewItem* last_selected = getCurSelectedItem();
1527
1528			if (!mKeyboardSelection)
1529			{
1530				setSelection(last_selected, FALSE, TRUE);
1531				mKeyboardSelection = TRUE;
1532			}
1533
1534			LLFolderViewItem* next = NULL;
1535			if (mask & MASK_SHIFT)
1536			{
1537				// don't shift select down to children of folders (they are implicitly selected through parent)
1538				next = last_selected->getNextOpenNode(FALSE);
1539				if (next)
1540				{
1541					if (next->isSelected())
1542					{
1543						// shrink selection
1544						changeSelectionFromRoot(last_selected, FALSE);
1545					}
1546					else if (last_selected->getParentFolder() == next->getParentFolder())
1547					{
1548						// grow selection
1549						changeSelectionFromRoot(next, TRUE);
1550					}
1551				}
1552			}
1553			else
1554			{
1555				next = last_selected->getNextOpenNode();
1556				if( next )
1557				{
1558					if (next == last_selected)
1559					{
1560						//special case for LLAccordionCtrl
1561						if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed
1562						{
1563							clearSelection();
1564							return TRUE;
1565						}
1566						return FALSE;
1567					}
1568					setSelection( next, FALSE, TRUE );
1569				}
1570				else
1571				{
1572					//special case for LLAccordionCtrl
1573					if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed
1574					{
1575						clearSelection();
1576						return TRUE;
1577					}
1578					return FALSE;
1579				}
1580			}
1581			scrollToShowSelection();
1582			mSearchString.clear();
1583			handled = TRUE;
1584		}
1585		break;
1586
1587	case KEY_UP:
1588		if((mSelectedItems.size() > 0) && mScrollContainer)
1589		{
1590			LLFolderViewItem* last_selected = mSelectedItems.back();
1591
1592			if (!mKeyboardSelection)
1593			{
1594				setSelection(last_selected, FALSE, TRUE);
1595				mKeyboardSelection = TRUE;
1596			}
1597
1598			LLFolderViewItem* prev = NULL;
1599			if (mask & MASK_SHIFT)
1600			{
1601				// don't shift select down to children of folders (they are implicitly selected through parent)
1602				prev = last_selected->getPreviousOpenNode(FALSE);
1603				if (prev)
1604				{
1605					if (prev->isSelected())
1606					{
1607						// shrink selection
1608						changeSelectionFromRoot(last_selected, FALSE);
1609					}
1610					else if (last_selected->getParentFolder() == prev->getParentFolder())
1611					{
1612						// grow selection
1613						changeSelectionFromRoot(prev, TRUE);
1614					}
1615				}
1616			}
1617			else
1618			{
1619				prev = last_selected->getPreviousOpenNode();
1620				if( prev )
1621				{
1622					if (prev == this)
1623					{
1624						// If case we are in accordion tab notify parent to go to the previous accordion
1625						if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed
1626						{
1627							clearSelection();
1628							return TRUE;
1629						}
1630
1631						return FALSE;
1632					}
1633					setSelection( prev, FALSE, TRUE );
1634				}
1635			}
1636			scrollToShowSelection();
1637			mSearchString.clear();
1638
1639			handled = TRUE;
1640		}
1641		break;
1642
1643	case KEY_RIGHT:
1644		if(mSelectedItems.size())
1645		{
1646			LLFolderViewItem* last_selected = getCurSelectedItem();
1647			last_selected->setOpen( TRUE );
1648			mSearchString.clear();
1649			handled = TRUE;
1650		}
1651		break;
1652
1653	case KEY_LEFT:
1654		if(mSelectedItems.size())
1655		{
1656			LLFolderViewItem* last_selected = getCurSelectedItem();
1657			LLFolderViewItem* parent_folder = last_selected->getParentFolder();
1658			if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder())
1659			{
1660				setSelection(parent_folder, FALSE, TRUE);
1661			}
1662			else
1663			{
1664				last_selected->setOpen( FALSE );
1665			}
1666			mSearchString.clear();
1667			scrollToShowSelection();
1668			handled = TRUE;
1669		}
1670		break;
1671	}
1672
1673	if (!handled && mParentPanel->hasFocus())
1674	{
1675		if (key == KEY_BACKSPACE)
1676		{
1677			mSearchTimer.reset();
1678			if (mSearchString.size())
1679			{
1680				mSearchString.erase(mSearchString.size() - 1, 1);
1681			}
1682			search(getCurSelectedItem(), mSearchString, FALSE);
1683			handled = TRUE;
1684		}
1685	}
1686
1687	return handled;
1688}
1689
1690
1691BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char)
1692{
1693	if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
1694	{
1695		return FALSE;
1696	}
1697
1698	if (uni_char > 0x7f)
1699	{
1700		llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl;
1701		return FALSE;
1702	}
1703
1704	BOOL handled = FALSE;
1705	if (mParentPanel->hasFocus())
1706	{
1707		// SL-51858: Key presses are not being passed to the Popup menu.
1708		// A proper fix is non-trivial so instead just close the menu.
1709		LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
1710		if (menu && menu->isOpen())
1711		{
1712			LLMenuGL::sMenuContainer->hideMenus();
1713		}
1714
1715		//do text search
1716		if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout"))
1717		{
1718			mSearchString.clear();
1719		}
1720		mSearchTimer.reset();
1721		if (mSearchString.size() < 128)
1722		{
1723			mSearchString += uni_char;
1724		}
1725		search(getCurSelectedItem(), mSearchString, FALSE);
1726
1727		handled = TRUE;
1728	}
1729
1730	return handled;
1731}
1732
1733
1734BOOL LLFolderView::canDoDelete() const
1735{
1736	if (mSelectedItems.size() == 0) return FALSE;
1737
1738	for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
1739	{
1740		if (!(*item_it)->getListener()->isItemRemovable())
1741		{
1742			return FALSE;
1743		}
1744	}
1745	return TRUE;
1746}
1747
1748void LLFolderView::doDelete()
1749{
1750	if(mSelectedItems.size() > 0)
1751	{				
1752		removeSelectedItems();
1753	}
1754}
1755
1756
1757BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask )
1758{
1759	mKeyboardSelection = FALSE;
1760	mSearchString.clear();
1761
1762	mParentPanel->setFocus(TRUE);
1763
1764	LLEditMenuHandler::gEditMenuHandler = this;
1765
1766	return LLView::handleMouseDown( x, y, mask );
1767}
1768
1769BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward)
1770{
1771	// get first selected item
1772	LLFolderViewItem* search_item = first_item;
1773
1774	// make sure search string is upper case
1775	std::string upper_case_string = search_string;
1776	LLStringUtil::toUpper(upper_case_string);
1777
1778	// if nothing selected, select first item in folder
1779	if (!search_item)
1780	{
1781		// start from first item
1782		search_item = getNextFromChild(NULL);
1783	}
1784
1785	// search over all open nodes for first substring match (with wrapping)
1786	BOOL found = FALSE;
1787	LLFolderViewItem* original_search_item = search_item;
1788	do
1789	{
1790		// wrap at end
1791		if (!search_item)
1792		{
1793			if (backward)
1794			{
1795				search_item = getPreviousFromChild(NULL);
1796			}
1797			else
1798			{
1799				search_item = getNextFromChild(NULL);
1800			}
1801			if (!search_item || search_item == original_search_item)
1802			{
1803				break;
1804			}
1805		}
1806
1807		const std::string current_item_label(search_item->getSearchableLabel());
1808		S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size());
1809		if (!current_item_label.compare(0, search_string_length, upper_case_string))
1810		{
1811			found = TRUE;
1812			break;
1813		}
1814		if (backward)
1815		{
1816			search_item = search_item->getPreviousOpenNode();
1817		}
1818		else
1819		{
1820			search_item = search_item->getNextOpenNode();
1821		}
1822
1823	} while(search_item != original_search_item);
1824	
1825
1826	if (found)
1827	{
1828		setSelection(search_item, FALSE, TRUE);
1829		scrollToShowSelection();
1830	}
1831
1832	return found;
1833}
1834
1835BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask )
1836{
1837	// skip LLFolderViewFolder::handleDoubleClick()
1838	return LLView::handleDoubleClick( x, y, mask );
1839}
1840
1841BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
1842{
1843	// all user operations move keyboard focus to inventory
1844	// this way, we know when to stop auto-updating a search
1845	mParentPanel->setFocus(TRUE);
1846
1847	BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
1848	S32 count = mSelectedItems.size();
1849	LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
1850	if (   handled
1851		&& ( count > 0 && (hasVisibleChildren() || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) ) // show menu only if selected items are visible
1852		&& menu )
1853	{
1854		if (mCallbackRegistrar)
1855			mCallbackRegistrar->pushScope();
1856
1857		updateMenuOptions(menu);
1858	   
1859		menu->updateParent(LLMenuGL::sMenuContainer);
1860		LLMenuGL::showPopup(this, menu, x, y);
1861		if (mCallbackRegistrar)
1862			mCallbackRegistrar->popScope();
1863	}
1864	else
1865	{
1866		if (menu && menu->getVisible())
1867		{
1868			menu->setVisible(FALSE);
1869		}
1870		setSelection(NULL, FALSE, TRUE);
1871	}
1872	return handled;
1873}
1874
1875// Add "--no options--" if the menu is completely blank.
1876BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const
1877{
1878	const std::string nooptions_str = "--no options--";
1879	LLView *nooptions_item = NULL;
1880	
1881	const LLView::child_list_t *list = menu->getChildList();
1882	for (LLView::child_list_t::const_iterator itor = list->begin(); 
1883		 itor != list->end(); 
1884		 ++itor)
1885	{
1886		LLView *menu_item = (*itor);
1887		if (menu_item->getVisible())
1888		{
1889			return FALSE;
1890		}
1891		std::string name = menu_item->getName();
1892		if (menu_item->getName() == nooptions_str)
1893		{
1894			nooptions_item = menu_item;
1895		}
1896	}
1897	if (nooptions_item)
1898	{
1899		nooptions_item->setVisible(TRUE);
1900		nooptions_item->setEnabled(FALSE);
1901		return TRUE;
1902	}
1903	return FALSE;
1904}
1905
1906BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask )
1907{
1908	return LLView::handleHover( x, y, mask );
1909}
1910
1911BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
1912									 EDragAndDropType cargo_type,
1913									 void* cargo_data, 
1914									 EAcceptance* accept,
1915									 std::string& tooltip_msg)
1916{
1917	mDragAndDropThisFrame = TRUE;
1918	// have children handle it first
1919	BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data,
1920											 accept, tooltip_msg);
1921
1922	// when drop is not handled by child, it should be handled
1923	// by the folder which is the hierarchy root.
1924	if (!handled)
1925	{
1926		if (getListener()->getUUID().notNull())
1927		{
1928			handled = LLFolderViewFolder::handleDragAndDrop(x, y

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