Skip to content
Snippets Groups Projects
llfolderview.cpp 53.6 KiB
Newer Older
James Cook's avatar
James Cook committed
/** 
 * @file llfolderview.cpp
 * @brief Implementation of the folder view collection of classes.
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
James Cook's avatar
James Cook committed
 */

#include "linden_common.h"
James Cook's avatar
James Cook committed

#include "llfolderview.h"
#include "llfolderviewmodel.h"
#include "llclipboard.h" // *TODO: remove this once hack below gone.
James Cook's avatar
James Cook committed
#include "llkeyboard.h"
#include "lllineeditor.h"
#include "llmenugl.h"
James Cook's avatar
James Cook committed
#include "llscrollcontainer.h" // hack to allow scrolling
#include "lltextbox.h"
James Cook's avatar
James Cook committed
#include "llui.h"
#include "lluictrlfactory.h"
// Linden library includes
#include "lldbstrings.h"
#include "llfocusmgr.h"
#include "llfontgl.h"
#include "llgl.h" 
#include "llrender.h"

// Third-party library includes
#include <algorithm>
Rye Mutt's avatar
Rye Mutt committed
#include <boost/range/adaptor/reversed.hpp>
James Cook's avatar
James Cook committed

///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------

const S32 RENAME_HEIGHT_PAD = 1;
James Cook's avatar
James Cook committed
const S32 AUTO_OPEN_STACK_DEPTH = 16;
James Cook's avatar
James Cook committed
const S32 MINIMUM_RENAMER_WIDTH = 80;

// *TODO: move in params in xml if necessary. Requires modification of LLFolderView & LLInventoryPanel Params.
const S32 STATUS_TEXT_HPAD = 6;
const S32 STATUS_TEXT_VPAD = 8;

enum {
	SIGNAL_NO_KEYBOARD_FOCUS = 1,
	SIGNAL_KEYBOARD_FOCUS = 2
};

James Cook's avatar
James Cook committed
F32 LLFolderView::sAutoOpenTime = 1.f;

//---------------------------------------------------------------------------
James Cook's avatar
James Cook committed

// Tells all folders in a folderview to close themselves
// For efficiency, calls setOpenArrangeRecursively().
// The calling function must then call:
//	LLFolderView* root = getRoot();
//	if( root )
//	{
//		root->arrange( NULL, NULL );
//		root->scrollToShowSelection();
//	}
// to patch things up.
class LLCloseAllFoldersFunctor : public LLFolderViewFunctor
James Cook's avatar
James Cook committed
{
public:
	LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; }
	virtual ~LLCloseAllFoldersFunctor() {}
	virtual void doFolder(LLFolderViewFolder* folder);
	virtual void doItem(LLFolderViewItem* item);
James Cook's avatar
James Cook committed

void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder)
	folder->setOpenArrangeRecursively(mOpen);
// Do nothing.
void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item)
{ }
James Cook's avatar
James Cook committed

//---------------------------------------------------------------------------

void LLAllDescendentsPassedFilter::doFolder(LLFolderViewFolder* folder)
{
	mAllDescendentsPassedFilter &= (folder) && (folder->passedFilter()) && (folder->descendantsPassedFilter());
}

void LLAllDescendentsPassedFilter::doItem(LLFolderViewItem* item)
{
	mAllDescendentsPassedFilter &= (item) && (item->passedFilter());
}

///----------------------------------------------------------------------------
/// Class LLFolderViewScrollContainer
///----------------------------------------------------------------------------

// virtual
const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const
{
	LLRect rect = LLRect::null;
	if (mScrolledView)
	{
		LLFolderView* folder_view = dynamic_cast<LLFolderView*>(mScrolledView);
		if (folder_view)
		{
			S32 height = folder_view->getRect().getHeight();

			rect = mScrolledView->getRect();
			rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), height);
		}
	}

	return rect;
}

LLFolderViewScrollContainer::LLFolderViewScrollContainer(const LLScrollContainer::Params& p)
:	LLScrollContainer(p)
{}

///----------------------------------------------------------------------------
/// Class LLFolderView
///----------------------------------------------------------------------------
LLFolderView::Params::Params()
	use_label_suffix("use_label_suffix"),
	allow_multiselect("allow_multiselect", true),
	allow_drag("allow_drag", true),
	suppress_folder_menu("suppress_folder_menu", false),
	use_ellipses("use_ellipses", false),
    options_menu("options_menu", "")
	folder_indentation = -4;
James Cook's avatar
James Cook committed

// Default constructor
LLFolderView::LLFolderView(const Params& p)
:	LLFolderViewFolder(p),
	mScrollContainer( NULL ),
	mPopupMenuHandle(),
	mMenuFileName(p.options_menu),
	mAllowMultiSelect(p.allow_multiselect),
	mShowFolderHierarchy(FALSE),
	mRenameItem( NULL ),
	mNeedsScroll( FALSE ),
	mUseLabelSuffix(p.use_label_suffix),
	mSuppressFolderMenu(p.suppress_folder_menu),
	mPinningSelectedItem(FALSE),
	mNeedsAutoSelect( FALSE ),
	mAutoSelectOverride(FALSE),
	mNeedsAutoRename(FALSE),
	mShowSelectionContext(FALSE),
	mShowSingleSelection(FALSE),
	mArrangeGeneration(0),
	mSignalSelectCallback(0),
	mMinWidth(0),
	mDragAndDropThisFrame(FALSE),
	mCallbackRegistrar(NULL),
	mEnableRegistrar(NULL),
	mStatusTextBox(NULL),
	mShowItemLinkOverlays(p.show_item_link_overlays),
	mViewModel(p.view_model),
    mGroupedItemModel(p.grouped_item_model)
James Cook's avatar
James Cook committed
{
    LLPanel* panel = p.parent_panel;
    mParentPanel = panel->getHandle();
	mViewModel->setFolderView(this);
	LLRect rect = p.rect;
	LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom);
	setRect( rect );
	reshape(rect.getWidth(), rect.getHeight());
	mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH);
	mAutoOpenCandidate = NULL;
	mAutoOpenTimer.stop();
	mKeyboardSelection = FALSE;
	mIndentation = 	getParentFolder() ? getParentFolder()->getIndentation() + mLocalIndentation : 0;  
James Cook's avatar
James Cook committed

	//clear label
	// go ahead and render root folder as usual
	// just make sure the label ("Inventory Folder") never shows up
	mLabel = LLStringUtil::null;
	// Escape is handled by reverting the rename, not commiting it (default behavior)
	LLLineEditor::Params params;
	params.name("ren");
	params.font(getLabelFontForStyle(LLFontGL::NORMAL));
	params.max_length.bytes(DB_INV_ITEM_NAME_STR_LEN);
	params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2));
	params.prevalidate_callback(&LLTextValidate::validateASCIIPrintableNoPipe);
	params.commit_on_focus_lost(true);
	params.visible(false);
	mRenamer = LLUICtrlFactory::create<LLLineEditor> (params);
	addChild(mRenamer);
	LLFontGL* font = getLabelFontForStyle(mLabelStyle);
    //mIconPad, mTextPad are set in folder_view_item.xml
	LLRect new_r = LLRect(rect.mLeft + mIconPad,
			      rect.mTop - mTextPad,
			      rect.mTop - mTextPad - font->getLineHeight());
	text_p.name(std::string(p.name));
	text_p.font(font);
	text_p.parse_urls(true);
	text_p.wrap(true); // allow multiline text. See EXT-7564, EXT-7047
	// set text padding the same as in People panel. EXT-7047, EXT-4837
	text_p.h_pad(STATUS_TEXT_HPAD);
	text_p.v_pad(STATUS_TEXT_VPAD);
	mStatusTextBox = LLUICtrlFactory::create<LLTextBox> (text_p);
	mStatusTextBox->setFollowsLeft();
	mStatusTextBox->setFollowsTop();
	mViewModelItem->openItem();
James Cook's avatar
James Cook committed

// Destroys the object
LLFolderView::~LLFolderView( void )
	closeRenamer();

	// The release focus call can potentially call the
	// scrollcontainer, which can potentially be called with a partly
	// destroyed scollcontainer. Just null it out here, and no worries
	// about calling into the invalid scroll container.
	// Same with the renamer.
	mScrollContainer = NULL;
	mRenameItem = NULL;
	mRenamer = NULL;
James Cook's avatar
James Cook committed

	if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
	mPopupMenuHandle.markDead();
James Cook's avatar
James Cook committed

	mAutoOpenItems.removeAllNodes();
	clearSelection();
	mItems.clear();
	mFolders.clear();
James Cook's avatar
James Cook committed

	//mViewModel->setFolderView(NULL);
	mViewModel = NULL;
BOOL LLFolderView::canFocusChildren() const
James Cook's avatar
James Cook committed
{
void LLFolderView::addFolder( LLFolderViewFolder* folder)
James Cook's avatar
James Cook committed
{
	LLFolderViewFolder::addFolder(folder);
Xenhat Hex's avatar
Xenhat Hex committed
void LLFolderView::openAllFolders()
James Cook's avatar
James Cook committed
{
Xenhat Hex's avatar
Xenhat Hex committed
	// Open all the folders
	setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_DOWN);
Xenhat Hex's avatar
Xenhat Hex committed
void LLFolderView::closeAllFolders()
Xenhat Hex's avatar
Xenhat Hex committed
	// Close all the folders
	setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN);
void LLFolderView::openTopLevelFolders()
{
	for (folders_t::iterator iter = mFolders.begin();
		 iter != mFolders.end();)
	{
		folders_t::iterator fit = iter++;
		(*fit)->setOpen(TRUE);
	}
}

// This view grows and shrinks to enclose all of its children items and folders.
// *width should be 0
// conform show folder state works
S32 LLFolderView::arrange( S32* unused_width, S32* unused_height )
	S32 target_height;
James Cook's avatar
James Cook committed

	LLFolderViewFolder::arrange(&mMinWidth, &target_height);
	LLRect scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
	reshape( llmax(scroll_rect.getWidth(), mMinWidth), ll_round(mCurHeight) );
	LLRect new_scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
	if (new_scroll_rect.getWidth() != scroll_rect.getWidth())
James Cook's avatar
James Cook committed
	{
		reshape( llmax(scroll_rect.getWidth(), mMinWidth), ll_round(mCurHeight) );
James Cook's avatar
James Cook committed
	}

	// move item renamer text field to item's new position
	updateRenamerPosition();

static LLTrace::BlockTimerStatHandle FTM_FILTER("Filter Folder View");
void LLFolderView::filter( LLFolderViewFilter& filter )
James Cook's avatar
James Cook committed
{
	LL_RECORD_BLOCK_TIME(FTM_FILTER);
    static LLUICachedControl<S32> time_visible("FilterItemsMaxTimePerFrameVisible", 10);
    static LLUICachedControl<S32> time_invisible("FilterItemsMaxTimePerFrameUnvisible", 1);
Rye Mutt's avatar
Rye Mutt committed
    filter.resetTime(llclamp(mParentPanel.get()->getVisible() ? time_visible() : time_invisible(), 1, 100));
    // Note: we filter the model, not the view
	getViewModelItem()->filter(filter);
void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent)
James Cook's avatar
James Cook committed
{
James Cook's avatar
James Cook committed
	{
		LLView::reshape(width, height, called_from_parent);
		scroll_rect = mScrollContainer->getContentWindowRect();
James Cook's avatar
James Cook committed
	}
	width  = llmax(mMinWidth, scroll_rect.getWidth());
	height = llmax(ll_round(mCurHeight), scroll_rect.getHeight());
	// Restrict width within scroll container's width
	if (mUseEllipses && mScrollContainer)
	{
		width = scroll_rect.getWidth();
	LLView::reshape(width, height, called_from_parent);
	mReshapeSignal(mSelectedItems, FALSE);
void LLFolderView::addToSelectionList(LLFolderViewItem* item)
James Cook's avatar
James Cook committed
{
James Cook's avatar
James Cook committed
	{
James Cook's avatar
James Cook committed
	}
James Cook's avatar
James Cook committed
	{
		mSelectedItems.back()->setIsCurSelection(FALSE);
James Cook's avatar
James Cook committed
	}
	item->setIsCurSelection(TRUE);
	mSelectedItems.push_back(item);
void LLFolderView::removeFromSelectionList(LLFolderViewItem* item)
James Cook's avatar
James Cook committed
{
	if (mSelectedItems.size())
	{
		mSelectedItems.back()->setIsCurSelection(FALSE);
	}
James Cook's avatar
James Cook committed

	selected_items_t::iterator item_iter;
	for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();)
James Cook's avatar
James Cook committed
	{
James Cook's avatar
James Cook committed
		{
			item_iter = mSelectedItems.erase(item_iter);
James Cook's avatar
James Cook committed
		}
		else
		{
James Cook's avatar
James Cook committed
		}
	}
James Cook's avatar
James Cook committed
	{
		mSelectedItems.back()->setIsCurSelection(TRUE);
James Cook's avatar
James Cook committed
	}
James Cook's avatar
James Cook committed

LLFolderViewItem* LLFolderView::getCurSelectedItem( void )
{
	if(mSelectedItems.size())
James Cook's avatar
James Cook committed
	{
		LLFolderViewItem* itemp = mSelectedItems.back();
		llassert(itemp->getIsCurSelection());
		return itemp;
James Cook's avatar
James Cook committed
	}
LLFolderView::selected_items_t& LLFolderView::getSelectedItems( void )
{
    return mSelectedItems;
}

// Record the selected item and pass it down the hierachy.
BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem,
								BOOL take_keyboard_focus)
James Cook's avatar
James Cook committed
{
	mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS;

James Cook's avatar
James Cook committed
	{
James Cook's avatar
James Cook committed

		mParentPanel.get()->setFocus(TRUE);
James Cook's avatar
James Cook committed

	// clear selection down here because change of keyboard focus can potentially
	// affect selection
	clearSelection();
James Cook's avatar
James Cook committed

James Cook's avatar
James Cook committed
	}

	BOOL rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus);
	if(openitem && selection)
James Cook's avatar
James Cook committed
	{
		selection->getParentFolder()->requestArrange();
James Cook's avatar
James Cook committed

BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected)
{
	BOOL rv = FALSE;

	// can't select root folder
	if(!selection || selection == this)
	{
		return FALSE;
James Cook's avatar
James Cook committed
	{
	selected_items_t::iterator item_iter;
	for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
James Cook's avatar
James Cook committed
	{
James Cook's avatar
James Cook committed
		{
James Cook's avatar
James Cook committed
		}
	}

	BOOL on_list = (item_iter != mSelectedItems.end());

	if(selected && !on_list)
James Cook's avatar
James Cook committed
	{
James Cook's avatar
James Cook committed
	}
James Cook's avatar
James Cook committed
	{
	rv = LLFolderViewFolder::changeSelection(selection, selected);
James Cook's avatar
James Cook committed

	mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS;
	
	return rv;
}
James Cook's avatar
James Cook committed

static LLTrace::BlockTimerStatHandle FTM_SANITIZE_SELECTION("Sanitize Selection");
	LL_RECORD_BLOCK_TIME(FTM_SANITIZE_SELECTION);
	// store off current item in case it is automatically deselected
	// and we want to preserve context
	LLFolderViewItem* original_selected_item = getCurSelectedItem();
James Cook's avatar
James Cook committed

	std::vector<LLFolderViewItem*> items_to_remove;
	selected_items_t::iterator item_iter;
	for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
		LLFolderViewItem* item = *item_iter;

		// ensure that each ancestor is open and potentially passes filtering
		if(item->getViewModelItem() != NULL)
		{
			visible = item->getViewModelItem()->potentiallyVisible(); // initialize from filter state for this item
		}
		// modify with parent open and filters states
		LLFolderViewFolder* parent_folder = item->getParentFolder();
		// Move up through parent folders and see what's visible
			visible = visible && parent_folder->isOpen() && parent_folder->getViewModelItem()->potentiallyVisible();
					parent_folder = parent_folder->getParentFolder();
				}
James Cook's avatar
James Cook committed

		//  deselect item if any ancestor is closed or didn't pass filter requirements.
		if (!visible)
James Cook's avatar
James Cook committed
		}

		// disallow nested selections (i.e. folder items plus one or more ancestors)
		// could check cached mum selections count and only iterate if there are any
		// but that may be a premature optimization.
		selected_items_t::iterator other_item_iter;
		for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter)
James Cook's avatar
James Cook committed
		{
			LLFolderViewItem* other_item = *other_item_iter;
			for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder())
James Cook's avatar
James Cook committed
			{
				if (parent_folder == item)
				{
					// this is a descendent of the current folder, remove from list
					items_to_remove.push_back(other_item);
					break;
				}
James Cook's avatar
James Cook committed
			}
		}

		// Don't allow invisible items (such as root folders) to be selected.
James Cook's avatar
James Cook committed

	std::vector<LLFolderViewItem*>::iterator item_it;
	for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it )
		changeSelection(*item_it, FALSE); // toggle selection (also removes from list)
James Cook's avatar
James Cook committed

	// if nothing selected after prior constraints...
	if (mSelectedItems.empty())
		// ...select first available parent of original selection
		LLFolderViewItem* new_selection = NULL;
		if (original_selected_item)
			for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder();
				parent_folder;
				parent_folder = parent_folder->getParentFolder())
James Cook's avatar
James Cook committed
			{
				if (parent_folder->getViewModelItem() && parent_folder->getViewModelItem()->potentiallyVisible())
				{
					// give initial selection to first ancestor folder that potentially passes the filter
					if (!new_selection)
					{
						new_selection = parent_folder;
					}

					// if any ancestor folder of original item is closed, move the selection up 
					// to the highest closed
					if (!parent_folder->isOpen())
					{	
						new_selection = parent_folder;
					}
				}
James Cook's avatar
James Cook committed
			}
		}
Josh Bell's avatar
Josh Bell committed
		{
			new_selection = NULL;
James Cook's avatar
James Cook committed
		{
			setSelection(new_selection, FALSE, FALSE);
	for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); 
		 item_it != mSelectedItems.end(); 
		 ++item_it)
James Cook's avatar
James Cook committed
	}
std::set<LLFolderViewItem*> LLFolderView::getSelectionList() const
	std::set<LLFolderViewItem*> selection;
	std::copy(mSelectedItems.begin(), mSelectedItems.end(), std::inserter(selection, selection.begin()));
bool LLFolderView::startDrag()
	std::vector<LLFolderViewModelItem*> selected_items;
	if (!mSelectedItems.empty())
	{
		for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
		{
			selected_items.push_back((*item_it)->getViewModelItem());
James Cook's avatar
James Cook committed

		return getFolderViewModel()->startDrag(selected_items);
James Cook's avatar
James Cook committed

void LLFolderView::commitRename( const LLSD& data )
James Cook's avatar
James Cook committed
{
James Cook's avatar
James Cook committed
{
	//LLFontGL* font = getLabelFontForStyle(mLabelStyle);
	// if cursor has moved off of me during drag and drop
	// close all auto opened folders
	if (!mDragAndDropThisFrame)
	static LLUICachedControl<F32> type_ahead_timeout("TypeAheadTimeout", 1.5f);
Rye Mutt's avatar
Rye Mutt committed
	if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout || mSearchString.empty())
James Cook's avatar
James Cook committed
	{
	if (hasVisibleChildren())
James Cook's avatar
James Cook committed
	{
		mStatusTextBox->setVisible( FALSE );
James Cook's avatar
James Cook committed
	}
James Cook's avatar
James Cook committed
	{
		mStatusTextBox->setValue(getFolderViewModel()->getStatusText());
		// firstly reshape message textbox with current size. This is necessary to
		// LLTextBox::getTextPixelHeight works properly
		const LLRect local_rect = getLocalRect();
		mStatusTextBox->setShape(local_rect);

		// get preferable text height...
		S32 pixel_height = mStatusTextBox->getTextPixelHeight();
		bool height_changed = (local_rect.getHeight() < pixel_height);
		if (height_changed)
		{
			// ... if it does not match current height, lets rearrange current view.
			// This will indirectly call ::arrange and reshape of the status textbox.
			// We should call this method to also notify parent about required rect.
			// See EXT-7564, EXT-7047.
			S32 height = 0;
			S32 width = 0;
			S32 total_height = arrange( &width, &height );
			notifyParent(LLSD().with("action", "size_changes").with("height", total_height));

			LLUI::popMatrix();
			LLUI::pushMatrix();
			LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom);
	if (mRenameItem && mRenamer && mRenamer->getVisible() && !getVisibleRect().overlaps(mRenamer->getRect()))
	{
		// renamer is not connected to the item we are renaming in any form so manage it manually
		// TODO: consider stopping on any scroll action instead of when out of visible area
		finishRenamingItem();
	// skip over LLFolderViewFolder::draw since we don't want the folder icon, label, 
	// and arrow for the root folder
	LLView::draw();
James Cook's avatar
James Cook committed

void LLFolderView::finishRenamingItem( void )
{
	if(!mRenamer)
James Cook's avatar
James Cook committed
	{
		return;
	}
	if( mRenameItem )
	{
		mRenameItem->rename( mRenamer->getText() );
	}

	closeRenamer();
James Cook's avatar
James Cook committed

	// This is moved to an inventory observer in llinventorybridge.cpp, to handle updating after operation completed in AISv3 (SH-4611).
	// List is re-sorted alphabetically, so scroll to make sure the selected item is visible.
	if (mRenamer && mRenamer->getVisible())
James Cook's avatar
James Cook committed
	{
		// Triggers onRenamerLost() that actually closes the renamer.
		LLUI::getInstance()->removePopup(mRenamer);
James Cook's avatar
James Cook committed
	}
James Cook's avatar
James Cook committed

void LLFolderView::removeSelectedItems()
James Cook's avatar
James Cook committed
	{
		// just in case we're removing the renaming item.
		mRenameItem = NULL;

		// create a temporary structure which we will use to remove
		// items, since the removal will futz with internal data
		// structures.
		std::vector<LLFolderViewItem*> items;
		S32 count = mSelectedItems.size();
		if(count <= 0) return;
		LLFolderViewItem* item = NULL;
		selected_items_t::iterator item_it;
		for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
James Cook's avatar
James Cook committed
		{
				LL_INFOS() << "Cannot delete " << item->getName() << LL_ENDL;
		// iterate through the new container.
		count = items.size();
		LLFolderViewItem* item_to_select = getNextUnselectedItem();

James Cook's avatar
James Cook committed
		{
			LLFolderViewItem* item_to_delete = items[0];
			LLFolderViewFolder* parent = item_to_delete->getParentFolder();
			if(parent)
James Cook's avatar
James Cook committed
			{
				if (item_to_delete->remove())
James Cook's avatar
James Cook committed
				{
					// change selection on successful delete
					setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus());
James Cook's avatar
James Cook committed
				}
James Cook's avatar
James Cook committed
		}
James Cook's avatar
James Cook committed
		{
			std::vector<LLFolderViewModelItem*> listeners;
			LLFolderViewModelItem* listener;
			setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus());
				listener = items[i]->getViewModelItem();
				if(listener && (std::find(listeners.begin(), listeners.end(), listener) == listeners.end()))
			listener = static_cast<LLFolderViewModelItem*>(listeners.at(0));
James Cook's avatar
James Cook committed
			{
James Cook's avatar
James Cook committed
			}
		}
James Cook's avatar
James Cook committed
	}
James Cook's avatar
James Cook committed

void LLFolderView::autoOpenItem( LLFolderViewFolder* item )
James Cook's avatar
James Cook committed
{
	if ((mAutoOpenItems.check() == item) || 
		(mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) ||
		item->isOpen())
James Cook's avatar
James Cook committed
	{
	// close auto-opened folders
	LLFolderViewFolder* close_item = mAutoOpenItems.check();
	while (close_item && close_item != item->getParentFolder())
	{
		mAutoOpenItems.pop();
		close_item->setOpenArrangeRecursively(FALSE);
		close_item = mAutoOpenItems.check();
	}
James Cook's avatar
James Cook committed

James Cook's avatar
James Cook committed

	mAutoOpenItems.push(item);
	
	item->setOpen(TRUE);
	LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
	LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0);
	scrollToShowItem(item, constraint_rect);
void LLFolderView::closeAutoOpenedFolders()
James Cook's avatar
James Cook committed
{
James Cook's avatar
James Cook committed
	{
		LLFolderViewFolder* close_item = mAutoOpenItems.pop();
		close_item->setOpen(FALSE);
James Cook's avatar
James Cook committed
	}
James Cook's avatar
James Cook committed
	{
		mAutoOpenCandidate->setAutoOpenCountdown(0.f);
James Cook's avatar
James Cook committed
	}
	mAutoOpenCandidate = NULL;
	mAutoOpenTimer.stop();
BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder)
James Cook's avatar
James Cook committed
{
	if (folder && mAutoOpenCandidate == folder)
James Cook's avatar
James Cook committed
	{
James Cook's avatar
James Cook committed
		{
James Cook's avatar
James Cook committed
			{
				mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f));
James Cook's avatar
James Cook committed
			}
			if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime)
James Cook's avatar
James Cook committed
			{
				autoOpenItem(folder);
				mAutoOpenTimer.stop();
				return TRUE;
James Cook's avatar
James Cook committed
			}
		}
James Cook's avatar
James Cook committed
	}

	// otherwise new candidate, restart timer
	if (mAutoOpenCandidate)
James Cook's avatar
James Cook committed
	{
		mAutoOpenCandidate->setAutoOpenCountdown(0.f);
James Cook's avatar
James Cook committed
	}
	mAutoOpenCandidate = folder;
	mAutoOpenTimer.start();
	return FALSE;
James Cook's avatar
James Cook committed
{
	if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0)))
James Cook's avatar
James Cook committed
	{
James Cook's avatar
James Cook committed
	}
	
	for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
James Cook's avatar
James Cook committed
	{
		const LLFolderViewItem* item = *selected_it;
		if (!item->getViewModelItem()->isItemCopyable())
James Cook's avatar
James Cook committed
		{
James Cook's avatar
James Cook committed
		}
James Cook's avatar
James Cook committed

// copy selected item
void LLFolderView::copy()
{
	// *NOTE: total hack to clear the inventory clipboard
	S32 count = mSelectedItems.size();
	if(getVisible() && getEnabled() && (count > 0))
	{
		LLFolderViewModelItem* listener = NULL;
		selected_items_t::iterator item_it;
		for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
James Cook's avatar
James Cook committed
		{
			listener = (*item_it)->getViewModelItem();
James Cook's avatar
James Cook committed
			{
James Cook's avatar
James Cook committed
			}
		}
James Cook's avatar
James Cook committed

	if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0)))
	{
		return FALSE;
	}
	
	for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
	{
		const LLFolderViewItem* item = *selected_it;
		const LLFolderViewModelItem* listener = item->getViewModelItem();
		if (!listener || !listener->isItemRemovable())
		{
			return FALSE;
		}
	}
	return TRUE;
	// clear the inventory clipboard
	if(getVisible() && getEnabled() && (mSelectedItems.size() > 0))
		// Find out which item will be selected once the selection will be cut
		LLFolderViewItem* item_to_select = getNextUnselectedItem();
		
		// Get the selection: removeItem() modified mSelectedItems and makes iterating on it unwise
		std::set<LLFolderViewItem*> inventory_selected = getSelectionList();
		// Move each item to the clipboard and out of their folder
		for (std::set<LLFolderViewItem*>::iterator item_it = inventory_selected.begin(); item_it != inventory_selected.end(); ++item_it)
			LLFolderViewModelItem* listener = item_to_cut->getViewModelItem();
			{
				listener->cutToClipboard();
			}
		}
		setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus());
	}
	mSearchString.clear();
}

BOOL LLFolderView::canPaste() const
{
	if (mSelectedItems.empty())
	{
		return FALSE;
James Cook's avatar
James Cook committed
	{
		for (selected_items_t::const_iterator item_it = mSelectedItems.begin();
			 item_it != mSelectedItems.end(); ++item_it)
James Cook's avatar
James Cook committed
		{
			// *TODO: only check folders and parent folders of items
			const LLFolderViewItem* item = (*item_it);
			const LLFolderViewModelItem* listener = item->getViewModelItem();
			if(!listener || !listener->isClipboardPasteable())
James Cook's avatar
James Cook committed
			{