From d6101558a171dbd2390792ac1e78d09fc2c27711 Mon Sep 17 00:00:00 2001 From: James Cook Date: Mon, 6 Jul 2009 21:58:04 +0000 Subject: Merge xui-army-5 to viewer-2, includes layout, art, and color changes, also UI color refactoring and new FreeType font library on Linux. svn merge -r126038:126164 svn+ssh://svn.lindenlab.com/svn/linden/branches/skinning/xui-army-5 --- indra/newview/llfolderview.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'indra/newview/llfolderview.cpp') diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index fd8c22b8e5..ebda8b25fd 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -793,13 +793,13 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, void LLFolderViewItem::draw() { - static LLCachedControl sFgColor(gSavedSkinSettings, "MenuItemEnabledColor", DEFAULT_WHITE); - static LLCachedControl sHighlightBgColor(gSavedSkinSettings, "MenuItemHighlightBgColor", DEFAULT_WHITE); - static LLCachedControl sHighlightFgColor(gSavedSkinSettings, "MenuItemHighlightFgColor", DEFAULT_WHITE); - static LLCachedControl sFilterBGColor(gSavedSkinSettings, "FilterBackgroundColor", DEFAULT_WHITE); - static LLCachedControl sFilterTextColor(gSavedSkinSettings, "FilterTextColor", DEFAULT_WHITE); - static LLCachedControl sSuffixColor(gSavedSkinSettings, "InventoryItemSuffixColor", DEFAULT_WHITE); - static LLCachedControl sSearchStatusColor(gSavedSkinSettings, "InventorySearchStatusColor", DEFAULT_WHITE); + static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); + static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); + static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); + static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); + static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemSuffixColor", DEFAULT_WHITE); + static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); bool possibly_has_children = false; bool up_to_date = mListener && mListener->isUpToDate(); @@ -2562,7 +2562,7 @@ LLFolderView::LLFolderView(const Params& p) { menu = LLUICtrlFactory::getDefaultWidget("inventory_menu"); } - menu->setBackgroundColor(gSavedSkinSettings.getColor("MenuPopupBgColor")); + menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); mPopupMenuHandle = menu->getHandle(); } @@ -3148,7 +3148,7 @@ void LLFolderView::commitRename( const LLSD& data ) void LLFolderView::draw() { - static LLCachedControl sSearchStatusColor(gSavedSkinSettings, "InventorySearchStatusColor", DEFAULT_WHITE); + static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); if (mDebugFilters) { std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", -- cgit v1.2.3 From 52aeaa32841e7d0b37abab0a2a2540c2be2f16b7 Mon Sep 17 00:00:00 2001 From: James Cook Date: Tue, 7 Jul 2009 00:53:05 +0000 Subject: Merge skinning-14 to viewer-2, including refactoring many floaters to register them with LLFloaterReg, support for introspection of ParamBlock based UI widgets to dump XML schema, splitting llfolderview.cpp into three separate files to unravel dependencies and skeleton for for LLListView widget. Resolved conflicts in these files: lldraghandle.h, lluictrl.h, llchiclet.cpp, llfolderview.h/cpp, lliinventorybridge.cpp, llpanelpicks.cpp, llviewermenu.cpp, floater_mute.xml, floater_preferences.xml, notifications.xml, panel_preferences_audio.xml, panel_preferences_graphics1.xml, panel_region_general.xml svn merge -r124961:126284 svn+ssh://svn.lindenlab.com/svn/linden/branches/skinning/skinning-14 --- indra/newview/llfolderview.cpp | 5975 ++++++++++------------------------------ 1 file changed, 1504 insertions(+), 4471 deletions(-) (limited to 'indra/newview/llfolderview.cpp') diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index ebda8b25fd..d0a82f283c 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -34,20 +34,11 @@ #include "llfolderview.h" -#include - -#include "llviewercontrol.h" -#include "lldbstrings.h" -#include "llfocusmgr.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llrender.h" -#include "llinventory.h" - #include "llcallbacklist.h" #include "llinventorybridge.h" #include "llinventoryclipboard.h" // *TODO: remove this once hack below gone. -#include "llinventoryview.h"// hacked in for the bonus context menu items. +#include "llinventoryfilter.h" +#include "llfloaterinventory.h"// hacked in for the bonus context menu items. #include "llkeyboard.h" #include "lllineeditor.h" #include "llmenugl.h" @@ -61,34 +52,35 @@ #include "llviewerjointattachment.h" #include "llviewermenu.h" #include "lluictrlfactory.h" +#include "llviewercontrol.h" #include "llviewerwindow.h" #include "llvoavatar.h" #include "llfloaterproperties.h" -// RN: HACK -// We need these because some of the code below relies on things like -// gAgent root folder. Remove them once the abstraction leak is fixed. -#include "llagent.h" -#include "llappviewer.h" +// Linden library includes +#include "lldbstrings.h" +#include "llfocusmgr.h" +#include "llfontgl.h" +#include "llgl.h" +#include "llrender.h" +#include "llinventory.h" + +// Third-party library includes +#include ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- -const S32 LEFT_PAD = 5; -const S32 LEFT_INDENTATION = 13; -const S32 ICON_PAD = 2; -const S32 ICON_WIDTH = 16; -const S32 TEXT_PAD = 1; -const S32 ARROW_SIZE = 12; const S32 RENAME_WIDTH_PAD = 4; const S32 RENAME_HEIGHT_PAD = 2; const S32 AUTO_OPEN_STACK_DEPTH = 16; -const S32 MIN_ITEM_WIDTH_VISIBLE = ICON_WIDTH + ICON_PAD + ARROW_SIZE + TEXT_PAD + /*first few characters*/ 40; +const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH + + LLFolderViewItem::ICON_PAD + + LLFolderViewItem::ARROW_SIZE + + LLFolderViewItem::TEXT_PAD + + /*first few characters*/ 40; const S32 MINIMUM_RENAMER_WIDTH = 80; -const F32 FOLDER_CLOSE_TIME_CONSTANT = 0.02f; -const F32 FOLDER_OPEN_TIME_CONSTANT = 0.03f; -const S32 MAX_FOLDER_ITEM_OVERLAP = 2; enum { SIGNAL_NO_KEYBOARD_FOCUS = 1, @@ -104,5030 +96,2071 @@ void properties_selected_items(void* user_data); void paste_items(void* user_data); void renamer_focus_lost( LLFocusableElement* handler, void* user_data ); -///---------------------------------------------------------------------------- -/// Class LLFolderViewItem -///---------------------------------------------------------------------------- - -// statics -const LLFontGL* LLFolderViewItem::sFont = NULL; -const LLFontGL* LLFolderViewItem::sSmallFont = NULL; -LLUIImagePtr LLFolderViewItem::sArrowImage; -LLUIImagePtr LLFolderViewItem::sBoxImage; -const LLColor4U DEFAULT_WHITE(255, 255, 255); - -//static -void LLFolderViewItem::initClass() -{ - sFont = LLFontGL::getFontSansSerifSmall(); - sSmallFont = LLFontGL::getFontMonospace(); - sArrowImage = LLUI::getUIImage("folder_arrow.tga"); - sBoxImage = LLUI::getUIImage("rounded_square.tga"); -} +//--------------------------------------------------------------------------- -//static -void LLFolderViewItem::cleanupClass() +// Tells all folders in a folderview to sort their items +// (and only their items, not folders) by a certain function. +class LLSetItemSortFunction : public LLFolderViewFunctor { - sArrowImage = NULL; - sBoxImage = NULL; -} +public: + LLSetItemSortFunction(U32 ordering) + : mSortOrder(ordering) {} + virtual ~LLSetItemSortFunction() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); -// NOTE: Optimize this, we call it a *lot* when opening a large inventory + U32 mSortOrder; +}; -// Default constructor -LLFolderViewItem::LLFolderViewItem(LLFolderViewItem::Params p) -: LLView(p), - mLabelWidth(0), - mLabelWidthDirty(false), - mParentFolder( NULL ), - mIsSelected( FALSE ), - mIsCurSelection( FALSE ), - mSelectPending(FALSE), - mLabelStyle( LLFontGL::NORMAL ), - mHasVisibleChildren(FALSE), - mIndentation(0), - mNumDescendantsSelected(0), - mFiltered(FALSE), - mLastFilterGeneration(-1), - mStringMatchOffset(std::string::npos), - mControlLabelRotation(0.f), - mDragAndDropTarget(FALSE), - mIsLoading(FALSE), - mLabel(p.name), - mRoot(p.root), - mCreationDate(p.creation_date), - mListener(p.listener), - mArrowImage(p.folder_arrow_image), - mBoxImage(p.selection_image) -{ - refresh(); -} -// Destroys the object -LLFolderViewItem::~LLFolderViewItem( void ) +// Set the sort order. +void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder) { - delete mListener; - mListener = NULL; + folder->setItemSortOrder(mSortOrder); } -LLFolderView* LLFolderViewItem::getRoot() +// Do nothing. +void LLSetItemSortFunction::doItem(LLFolderViewItem* item) { - return mRoot; + return; } -// Returns true if this object is a child (or grandchild, etc.) of potential_ancestor. -BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) -{ - LLFolderViewItem* root = this; - while( root->mParentFolder ) - { - if( root->mParentFolder == potential_ancestor ) - { - return TRUE; - } - root = root->mParentFolder; - } - return FALSE; -} +//--------------------------------------------------------------------------- -LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children) +// 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 { - if (!mParentFolder) - { - return NULL; - } - - LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children ); - while(itemp && !itemp->getVisible()) - { - LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children ); - if (itemp == next_itemp) - { - // hit last item - return itemp->getVisible() ? itemp : this; - } - itemp = next_itemp; - } - - return itemp; -} +public: + LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; } + virtual ~LLCloseAllFoldersFunctor() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); -LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children) -{ - if (!mParentFolder) - { - return NULL; - } - - LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children ); - while(itemp && !itemp->getVisible()) - { - LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children ); - if (itemp == next_itemp) - { - // hit first item - return itemp->getVisible() ? itemp : this; - } - itemp = next_itemp; - } + BOOL mOpen; +}; - return itemp; -} -// is this item something we think we should be showing? -// for example, if we haven't gotten around to filtering it yet, then the answer is yes -// until we find out otherwise -BOOL LLFolderViewItem::potentiallyVisible() +// Set the sort order. +void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder) { - // we haven't been checked against min required filter - // or we have and we passed - return getLastFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration() || getFiltered(); + folder->setOpenArrangeRecursively(mOpen); } -BOOL LLFolderViewItem::getFiltered() -{ - return mFiltered && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); -} +// Do nothing. +void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item) +{ } -BOOL LLFolderViewItem::getFiltered(S32 filter_generation) -{ - return mFiltered && mLastFilterGeneration >= filter_generation; -} +///---------------------------------------------------------------------------- +/// Class LLFolderView +///---------------------------------------------------------------------------- -void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation) -{ - mFiltered = filtered; - mLastFilterGeneration = filter_generation; -} +// Default constructor +LLFolderView::LLFolderView(const Params& p) +: LLFolderViewFolder(p), + mScrollContainer( NULL ), + mPopupMenuHandle(), + mAllowMultiSelect(TRUE), + mShowFolderHierarchy(FALSE), + mSourceID(p.task_id), + mRenameItem( NULL ), + mNeedsScroll( FALSE ), + mLastScrollItem( NULL ), + mNeedsAutoSelect( FALSE ), + mAutoSelectOverride(FALSE), + mNeedsAutoRename(FALSE), + mDebugFilters(FALSE), + mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately + mFilter( new LLInventoryFilter(p.name) ), + mShowSelectionContext(FALSE), + mShowSingleSelection(FALSE), + mArrangeGeneration(0), + mSignalSelectCallback(0), + mMinWidth(0), + mDragAndDropThisFrame(FALSE), + mCallbackRegistrar(NULL), + mParentPanel(p.parent_panel) -void LLFolderViewItem::setIcon(LLUIImagePtr icon) { - mIcon = icon; -} + 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()); + mIsOpen = TRUE; // this view is always open. + mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH); + mAutoOpenCandidate = NULL; + mAutoOpenTimer.stop(); + mKeyboardSelection = FALSE; + mIndentation = -LEFT_INDENTATION; // children start at indentation 0 + gIdleCallbacks.addFunction(idle, this); -// refresh information from the listener -void LLFolderViewItem::refreshFromListener() -{ - if(mListener) - { - mLabel = mListener->getDisplayName(); - LLAssetType::EType preferred_type = mListener->getPreferredType(); + //clear label + // go ahead and render root folder as usual + // just make sure the label ("Inventory Folder") never shows up + mLabel = LLStringUtil::null; - // *TODO: to be removed when database supports multi language. This is a - // temporary attempt to display the inventory folder in the user locale. - if (preferred_type != LLAssetType::AT_NONE) - { - mLabel = LLTrans::getString("InvFolder " + mLabel); - }; + //mRenamer->setWriteableBgColor(LLColor4::white); + // Escape is handled by reverting the rename, not commiting it (default behavior) + LLLineEditor::Params params; + params.name("ren"); + params.rect(getRect()); + params.font(sFont); + params.max_length_bytes(DB_INV_ITEM_NAME_STR_LEN); + params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2)); + params.prevalidate_callback(&LLLineEditor::prevalidatePrintableNotPipe); + params.commit_on_focus_lost(true); + params.visible(false); + mRenamer = LLUICtrlFactory::create (params); + addChild(mRenamer); - setIcon(mListener->getIcon()); - time_t creation_date = mListener->getCreationDate(); - if (mCreationDate != creation_date) - { - mCreationDate = mListener->getCreationDate(); - dirtyFilter(); - } - mLabelStyle = mListener->getLabelStyle(); - mLabelSuffix = mListener->getLabelSuffix(); + // make the popup menu available + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_inventory.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if (!menu) + { + menu = LLUICtrlFactory::getDefaultWidget("inventory_menu"); } + menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); + mPopupMenuHandle = menu->getHandle(); + } -void LLFolderViewItem::refresh() +// Destroys the object +LLFolderView::~LLFolderView( void ) { - refreshFromListener(); - - std::string searchable_label(mLabel); - searchable_label.append(mLabelSuffix); - LLStringUtil::toUpper(searchable_label); + // 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; - if (mSearchableLabel.compare(searchable_label)) + if( gEditMenuHandler == this ) { - mSearchableLabel.assign(searchable_label); - dirtyFilter(); - // some part of label has changed, so overall width has potentially changed, and sort order too - if (mParentFolder) - { - mParentFolder->requestSort(); - mParentFolder->requestArrange(); - } + gEditMenuHandler = NULL; } - mLabelWidthDirty = true; -} - -void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); -} - -// This function is called when items are added or view filters change. It's -// implemented here but called by derived classes when folding the -// views. -void LLFolderViewItem::filterFromRoot( void ) -{ - LLFolderViewItem* root = getRoot(); + mAutoOpenItems.removeAllNodes(); + gIdleCallbacks.deleteFunction(idle, this); - root->filter(*((LLFolderView*)root)->getFilter()); -} + LLView::deleteViewByHandle(mPopupMenuHandle); -// This function is called when the folder view is dirty. It's -// implemented here but called by derived classes when folding the -// views. -void LLFolderViewItem::arrangeFromRoot() -{ - LLFolderViewItem* root = getRoot(); + if(mRenamer == gFocusMgr.getTopCtrl()) + { + gFocusMgr.setTopCtrl(NULL); + } - S32 height = 0; - S32 width = 0; - root->arrange( &width, &height, 0 ); -} + mAutoOpenItems.removeAllNodes(); + clearSelection(); + mItems.clear(); + mFolders.clear(); -// This function clears the currently selected item, and records the -// specified selected item appropriately for display and use in the -// UI. If open is TRUE, then folders are opened up along the way to -// the selection. -void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection, - BOOL openitem, - BOOL take_keyboard_focus) -{ - getRoot()->setSelection(selection, openitem, take_keyboard_focus); -} + mItemMap.clear(); -// helper function to change the selection from the root. -void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected) -{ - getRoot()->changeSelection(selection, selected); + delete mFilter; + mFilter = NULL; } -void LLFolderViewItem::extendSelectionFromRoot(LLFolderViewItem* selection) +BOOL LLFolderView::canFocusChildren() const { - LLDynamicArray selected_items; - - getRoot()->extendSelection(selection, NULL, selected_items); -} - -EInventorySortGroup LLFolderViewItem::getSortGroup() const -{ - return SG_ITEM; + return FALSE; } -// addToFolder() returns TRUE if it succeeds. FALSE otherwise -BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) +void LLFolderView::checkTreeResortForModelChanged() { - if (!folder) + if (mSortOrder & LLInventoryFilter::SO_DATE && !(mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME)) { - return FALSE; + // This is the case where something got added or removed. If we are date sorting + // everything including folders, then we need to rebuild the whole tree. + // Just set to something not SO_DATE to force the folder most resent date resort. + mSortOrder = mSortOrder & ~LLInventoryFilter::SO_DATE; + setSortOrder(mSortOrder | LLInventoryFilter::SO_DATE); } - mParentFolder = folder; - root->addItemID(getListener()->getUUID(), this); - return folder->addItem(this); } - -// Finds width and height of this object and it's children. Also -// makes sure that this view and it's children are the right size. -S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation) +void LLFolderView::setSortOrder(U32 order) { - mIndentation = mParentFolder ? mParentFolder->getIndentation() + LEFT_INDENTATION : 0; - if (mLabelWidthDirty) + if (order != mSortOrder) { - mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + sFont->getWidth(mSearchableLabel); - mLabelWidthDirty = false; - } - - *width = llmax(*width, mLabelWidth + mIndentation); - *height = getItemHeight(); - return *height; -} - -S32 LLFolderViewItem::getItemHeight() -{ - S32 icon_height = mIcon->getHeight(); - S32 label_height = llround(sFont->getLineHeight()); - return llmax( icon_height, label_height ) + ICON_PAD; -} + LLFastTimer t(LLFastTimer::FTM_SORT); + mSortOrder = order; -void LLFolderViewItem::filter( LLInventoryFilter& filter) -{ - BOOL filtered = mListener && filter.check(this); - - // if our visibility will change as a result of this filter, then - // we need to be rearranged in our parent folder - if (getVisible() != filtered) - { - if (mParentFolder) + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) { - mParentFolder->requestArrange(); + folders_t::iterator fit = iter++; + (*fit)->sortBy(order); } - } - setFiltered(filtered, filter.getCurrentGeneration()); - mStringMatchOffset = filter.getStringMatchOffset(); - filter.decrementFilterCount(); - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); + arrangeAll(); } } -void LLFolderViewItem::dirtyFilter() + +U32 LLFolderView::getSortOrder() const { - mLastFilterGeneration = -1; - // bubble up dirty flag all the way to root - if (getParentFolder()) - { - getParentFolder()->setCompletedFilterGeneration(-1, TRUE); - } + return mSortOrder; } -// *TODO: This can be optimized a lot by simply recording that it is -// selected in the appropriate places, and assuming that set selection -// means 'deselect' for a leaf item. Do this optimization after -// multiple selection is implemented to make sure it all plays nice -// together. -BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus) +BOOL LLFolderView::addFolder( LLFolderViewFolder* folder) { - if( selection == this ) + // enforce sort order of My Inventory followed by Library + if (folder->getListener()->getUUID() == gInventory.getLibraryRootFolderID()) { - mIsSelected = TRUE; - if(mListener) - { - mListener->selectItem(); - } + mFolders.push_back(folder); } else { - mIsSelected = FALSE; + mFolders.insert(mFolders.begin(), folder); } - return mIsSelected; + folder->setOrigin(0, 0); + folder->reshape(getRect().getWidth(), 0); + folder->setVisible(FALSE); + addChild( folder ); + folder->dirtyFilter(); + folder->requestArrange(); + return TRUE; } -BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selected) +void LLFolderView::closeAllFolders() { - if(selection == this && mIsSelected != selected) - { - mIsSelected = selected; - if(mListener) - { - mListener->selectItem(); - } - return TRUE; - } - return FALSE; + // Close all the folders + setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); + arrangeAll(); } -void LLFolderViewItem::recursiveDeselect(BOOL deselect_self) +void LLFolderView::openFolder(const std::string& foldername) { - if (mIsSelected && deselect_self) + LLFolderViewFolder* inv = findChild(foldername); + if (inv) { - mIsSelected = FALSE; - - // update ancestors' count of selected descendents - LLFolderViewFolder* parent_folder = getParentFolder(); - while(parent_folder) - { - parent_folder->mNumDescendantsSelected--; - parent_folder = parent_folder->getParentFolder(); - } + setSelection(inv, FALSE, FALSE); + inv->setOpen(TRUE); } } - -BOOL LLFolderViewItem::isMovable() +void LLFolderView::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) { - if( mListener ) - { - return mListener->isItemMovable(); - } - else - { - return TRUE; - } + // call base class to do proper recursion + LLFolderViewFolder::setOpenArrangeRecursively(openitem, recurse); + // make sure root folder is always open + mIsOpen = TRUE; } -BOOL LLFolderViewItem::isRemovable() +// This view grows and shinks to enclose all of its children items and folders. +S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation ) { - if( mListener ) - { - return mListener->isItemRemovable(); - } - else - { - return TRUE; - } -} + LLFastTimer t2(LLFastTimer::FTM_ARRANGE); -void LLFolderViewItem::destroyView() -{ - if (mParentFolder) - { - // removeView deletes me - mParentFolder->removeView(this); - } -} + filter_generation = mFilter->getMinRequiredGeneration(); + mMinWidth = 0; -// Call through to the viewed object and return true if it can be -// removed. -//BOOL LLFolderViewItem::removeRecursively(BOOL single_item) -BOOL LLFolderViewItem::remove() -{ - if(!isRemovable()) + mHasVisibleChildren = hasFilteredDescendants(filter_generation); + // arrange always finishes, so optimistically set the arrange generation to the most current + mLastArrangeGeneration = getRoot()->getArrangeGeneration(); + + LLInventoryFilter::EFolderShow show_folder_state = + getRoot()->getFilter()->getShowFolderState(); + + S32 total_width = LEFT_PAD; + S32 running_height = mDebugFilters ? llceil(sSmallFont->getLineHeight()) : 0; + S32 target_height = running_height; + S32 parent_item_height = getRect().getHeight(); + + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) { - return FALSE; + folders_t::iterator fit = iter++; + LLFolderViewFolder* folderp = (*fit); + if (getDebugFilters()) + { + folderp->setVisible(TRUE); + } + else + { + folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? + (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter + } + if (folderp->getVisible()) + { + S32 child_height = 0; + S32 child_width = 0; + S32 child_top = parent_item_height - running_height; + + target_height += folderp->arrange( &child_width, &child_height, filter_generation ); + + mMinWidth = llmax(mMinWidth, child_width); + total_width = llmax( total_width, child_width ); + running_height += child_height; + folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() ); + } } - if(mListener) + + for (items_t::iterator iter = mItems.begin(); + iter != mItems.end();) { - return mListener->removeItem(); + items_t::iterator iit = iter++; + LLFolderViewItem* itemp = (*iit); + itemp->setVisible(itemp->getFiltered(filter_generation)); + + if (itemp->getVisible()) + { + S32 child_width = 0; + S32 child_height = 0; + S32 child_top = parent_item_height - running_height; + + target_height += itemp->arrange( &child_width, &child_height, filter_generation ); + itemp->reshape(itemp->getRect().getWidth(), child_height); + + mMinWidth = llmax(mMinWidth, child_width); + total_width = llmax( total_width, child_width ); + running_height += child_height; + itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() ); + } } - return TRUE; -} -// Build an appropriate context menu for the item. -void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - if(mListener) + S32 dummy_s32; + BOOL dummy_bool; + S32 min_width; + mScrollContainer->calcVisibleSize( &min_width, &dummy_s32, &dummy_bool, &dummy_bool); + reshape( llmax(min_width, total_width), running_height ); + + S32 new_min_width; + mScrollContainer->calcVisibleSize( &new_min_width, &dummy_s32, &dummy_bool, &dummy_bool); + if (new_min_width != min_width) { - mListener->buildContextMenu(menu, flags); + reshape( llmax(min_width, total_width), running_height ); } + + // move item renamer text field to item's new position + updateRenamerPosition(); + + mTargetHeight = (F32)target_height; + return llround(mTargetHeight); } -void LLFolderViewItem::openItem( void ) +const std::string LLFolderView::getFilterSubString(BOOL trim) { - if( mListener ) - { - mListener->openItem(); - } + return mFilter->getFilterSubString(trim); } -void LLFolderViewItem::preview( void ) +void LLFolderView::filter( LLInventoryFilter& filter ) { - if (mListener) + LLFastTimer t2(LLFastTimer::FTM_FILTER); + filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000)); + + if (getCompletedFilterGeneration() < filter.getCurrentGeneration()) { - mListener->previewItem(); + mFiltered = FALSE; + mMinWidth = 0; + LLFolderViewFolder::filter(filter); } } -void LLFolderViewItem::rename(const std::string& new_name) +void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) { - if( !new_name.empty() ) + S32 min_width = 0; + S32 dummy_height; + BOOL dummy_bool; + if (mScrollContainer) { - if( mListener ) - { - mListener->renameItem(new_name); - - if(mParentFolder) - { - mParentFolder->requestSort(); - } - } + mScrollContainer->calcVisibleSize( &min_width, &dummy_height, &dummy_bool, &dummy_bool); } -} + width = llmax(mMinWidth, min_width); + LLView::reshape(width, height, called_from_parent); -const std::string& LLFolderViewItem::getSearchableLabel() const -{ - return mSearchableLabel; + mReshapeSignal(mSelectedItems, FALSE); } -const std::string& LLFolderViewItem::getName( void ) const +void LLFolderView::addToSelectionList(LLFolderViewItem* item) { - if(mListener) + if (item->isSelected()) { - return mListener->getName(); + removeFromSelectionList(item); } - return mLabel; -} - -// LLView functionality -BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - if(!mIsSelected) + if (mSelectedItems.size()) { - setSelectionFromRoot(this, FALSE); + mSelectedItems.back()->setIsCurSelection(FALSE); } - make_ui_sound("UISndClick"); - return TRUE; + item->setIsCurSelection(TRUE); + mSelectedItems.push_back(item); } -BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) +void LLFolderView::removeFromSelectionList(LLFolderViewItem* item) { - // No handler needed for focus lost since this class has no - // state that depends on it. - gFocusMgr.setMouseCapture( this ); + if (mSelectedItems.size()) + { + mSelectedItems.back()->setIsCurSelection(FALSE); + } - if (!mIsSelected) + selected_items_t::iterator item_iter; + for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();) { - if(mask & MASK_CONTROL) - { - changeSelectionFromRoot(this, !mIsSelected); - } - else if (mask & MASK_SHIFT) + if (*item_iter == item) { - extendSelectionFromRoot(this); + item_iter = mSelectedItems.erase(item_iter); } else { - setSelectionFromRoot(this, FALSE); + ++item_iter; } - make_ui_sound("UISndClick"); } - else + if (mSelectedItems.size()) { - mSelectPending = TRUE; + mSelectedItems.back()->setIsCurSelection(TRUE); } +} - if( isMovable() ) +LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) +{ + if(mSelectedItems.size()) { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y ); + LLFolderViewItem* itemp = mSelectedItems.back(); + llassert(itemp->getIsCurSelection()); + return itemp; } - return TRUE; + return NULL; } -BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) + +// Record the selected item and pass it down the hierachy. +BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, + BOOL take_keyboard_focus) { - if( hasMouseCapture() && isMovable() ) + if( selection == this ) { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - BOOL can_drag = TRUE; - if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) - { - LLFolderView* root = getRoot(); - - if(root->getCurSelectedItem()) - { - LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD; - - // *TODO: push this into listener and remove - // dependency on llagent - if(mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gAgent.getInventoryRootID())) - { - src = LLToolDragAndDrop::SOURCE_AGENT; - } - else if (mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventoryLibraryRoot)) - { - src = LLToolDragAndDrop::SOURCE_LIBRARY; - } - - can_drag = root->startDrag(src); - if (can_drag) - { - // if (mListener) mListener->startDrag(); - // RN: when starting drag and drop, clear out last auto-open - root->autoOpenTest(NULL); - root->setShowSelectionContext(TRUE); + return FALSE; + } - // Release keyboard focus, so that if stuff is dropped into the - // world, pressing the delete key won't blow away the inventory - // item. - gFocusMgr.setKeyboardFocus(NULL); + if( selection && take_keyboard_focus) + { + mParentPanel->setFocus(TRUE); + } - return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); - } - } - } + // clear selection down here because change of keyboard focus can potentially + // affect selection + clearSelection(); - if (can_drag) - { - gViewerWindow->setCursor(UI_CURSOR_ARROW); - } - else - { - gViewerWindow->setCursor(UI_CURSOR_NOLOCKED); - } - return TRUE; + if(selection) + { + addToSelectionList(selection); } - else + + BOOL rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus); + if(openitem && selection) { - getRoot()->setShowSelectionContext(FALSE); - gViewerWindow->setCursor(UI_CURSOR_ARROW); - // let parent handle this then... - return FALSE; + selection->getParentFolder()->requestArrange(); } -} + llassert(mSelectedItems.size() <= 1); -BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - preview(); - return TRUE; + mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS; + + return rv; } -BOOL LLFolderViewItem::handleScrollWheel(S32 x, S32 y, S32 clicks) +void LLFolderView::setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus) { - if (getParent()) + LLFolderViewItem* itemp = getItemByID(obj_id); + if(itemp && itemp->getListener()) + { + itemp->arrangeAndSet(TRUE, take_keyboard_focus); + mSelectThisID.setNull(); + return; + } + else { - return getParent()->handleScrollWheel(x, y, clicks); + // save the desired item to be selected later (if/when ready) + mSelectThisID = obj_id; } - return FALSE; } -BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) +void LLFolderView::updateSelection() { - // if mouse hasn't moved since mouse down... - if ( pointInView(x, y) && mSelectPending ) + if (mSelectThisID.notNull()) { - //...then select - if(mask & MASK_CONTROL) - { - changeSelectionFromRoot(this, !mIsSelected); - } - else if (mask & MASK_SHIFT) - { - extendSelectionFromRoot(this); - } - else - { - setSelectionFromRoot(this, FALSE); - } + setSelectionByID(mSelectThisID, false); + } +} + +BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) +{ + BOOL rv = FALSE; + + // can't select root folder + if(!selection || selection == this) + { + return FALSE; } - - mSelectPending = FALSE; - if( hasMouseCapture() ) + if (!mAllowMultiSelect) { - getRoot()->setShowSelectionContext(FALSE); - gFocusMgr.setMouseCapture( NULL ); + clearSelection(); } - return TRUE; -} -BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = FALSE; - BOOL handled = FALSE; - if(mListener) + selected_items_t::iterator item_iter; + for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) { - accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data); - handled = accepted; - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else + if (*item_iter == selection) { - *accept = ACCEPT_NO; + break; } } - if(mParentFolder && !handled) + + BOOL on_list = (item_iter != mSelectedItems.end()); + + if(selected && !on_list) { - handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); + addToSelectionList(selection); } - if (handled) + if(!selected && on_list) { - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl; + removeFromSelectionList(selection); } - return handled; -} + rv = LLFolderViewFolder::changeSelection(selection, selected); + mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS; + + return rv; +} -void LLFolderViewItem::draw() +S32 LLFolderView::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& items) { - static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); - static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); - static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); - static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); - static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); - static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemSuffixColor", DEFAULT_WHITE); - static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); - - bool possibly_has_children = false; - bool up_to_date = mListener && mListener->isUpToDate(); - if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter... - (!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter) - { - possibly_has_children = true; - } - if(/*mControlLabel[0] != '\0' && */possibly_has_children) + S32 rv = 0; + + // now store resulting selection + if (mAllowMultiSelect) { - if (sArrowImage) + LLFolderViewItem *cur_selection = getCurSelectedItem(); + rv = LLFolderViewFolder::extendSelection(selection, cur_selection, items); + for (S32 i = 0; i < items.count(); i++) { - gl_draw_scaled_rotated_image(mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD, - ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, sArrowImage->getImage(), sFgColor); + addToSelectionList(items[i]); + rv++; } } + else + { + setSelection(selection, FALSE, FALSE); + rv++; + } + + mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS; + return rv; +} - F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); +void LLFolderView::sanitizeSelection() +{ + // store off current item in case it is automatically deselected + // and we want to preserve context + LLFolderViewItem* original_selected_item = getCurSelectedItem(); - // If we have keyboard focus, draw selection filled - BOOL show_context = getRoot()->getShowSelectionContext(); - BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); + // Cache "Show all folders" filter setting + BOOL show_all_folders = (getRoot()->getFilter()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS); - // always render "current" item, only render other selected items if - // mShowSingleSelection is FALSE - if( mIsSelected ) + std::vector items_to_remove; + selected_items_t::iterator item_iter; + for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLColor4 bg_color = sHighlightBgColor; - //const S32 TRAILING_PAD = 5; // It just looks better with this. - if (!mIsCurSelection) + LLFolderViewItem* item = *item_iter; + + // ensure that each ancestor is open and potentially passes filtering + BOOL visible = item->potentiallyVisible(); // initialize from filter state for this item + // modify with parent open and filters states + LLFolderViewFolder* parent_folder = item->getParentFolder(); + if ( parent_folder ) { - // do time-based fade of extra objects - F32 fade_time = getRoot()->getSelectionFadeElapsedTime(); - if (getRoot()->getShowSingleSelection()) - { - // fading out - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f); + if ( show_all_folders ) + { // "Show all folders" is on, so this folder is visible + visible = TRUE; } else - { - // fading in - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]); + { // Move up through parent folders and see what's visible + while(parent_folder) + { + visible = visible && parent_folder->isOpen() && parent_folder->potentiallyVisible(); + parent_folder = parent_folder->getParentFolder(); + } } } - gl_rect_2d( - 0, - getRect().getHeight(), - getRect().getWidth() - 2, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD), - bg_color, filled); - if (mIsCurSelection) + // deselect item if any ancestor is closed or didn't pass filter requirements. + if (!visible) { - gl_rect_2d( - 0, - getRect().getHeight(), - getRect().getWidth() - 2, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD), - sHighlightFgColor, FALSE); + items_to_remove.push_back(item); } - if (getRect().getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2) + + // 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) { - gl_rect_2d( - 0, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, - getRect().getWidth() - 2, - 2, - sHighlightFgColor, FALSE); - if (show_context) + LLFolderViewItem* other_item = *other_item_iter; + for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder()) { - gl_rect_2d( - 0, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, - getRect().getWidth() - 2, - 2, - sHighlightBgColor, TRUE); + if (parent_folder == item) + { + // this is a descendent of the current folder, remove from list + items_to_remove.push_back(other_item); + break; + } } } } - if (mDragAndDropTarget) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d( - 0, - getRect().getHeight(), - getRect().getWidth() - 2, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD), - sHighlightBgColor, FALSE); - - if (getRect().getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2) - { - gl_rect_2d( - 0, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, - getRect().getWidth() - 2, - 2, - sHighlightBgColor, FALSE); - } - mDragAndDropTarget = FALSE; - } - - if(mIcon) + std::vector::iterator item_it; + for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it ) { - mIcon->draw(mIndentation + ARROW_SIZE + TEXT_PAD, getRect().getHeight() - mIcon->getHeight()); + changeSelection(*item_it, FALSE); // toggle selection (also removes from list) } - if (!mLabel.empty()) + // if nothing selected after prior constraints... + if (mSelectedItems.empty()) { - // highlight filtered text - BOOL debug_filters = getRoot()->getDebugFilters(); - LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor ); - F32 right_x; - F32 y = (F32)getRect().getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD; - - if (debug_filters) + // ...select first available parent of original selection, or "My Inventory" otherwise + LLFolderViewItem* new_selection = NULL; + if (original_selected_item) { - if (!getFiltered() && !possibly_has_children) + for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder(); + parent_folder; + parent_folder = parent_folder->getParentFolder()) { - color.mV[VALPHA] *= 0.5f; + if (parent_folder->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; + } + } } - - LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? LLColor4(0.5f, 0.8f, 0.5f, 1.f) : LLColor4(0.8f, 0.5f, 0.5f, 1.f); - sSmallFont->renderUTF8(mStatusText, 0, text_left, y, filter_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - text_left = right_x; } - - - if ( mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime") ) + else { - sFont->renderUTF8(LLTrans::getString("LoadingData"), 0, text_left, y, sSearchStatusColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &right_x, FALSE); - text_left = right_x; + // nothing selected to start with, so pick "My Inventory" as best guess + new_selection = getItemByID(gInventory.getRootFolderID()); } - sFont->renderUTF8( mLabel, 0, text_left, y, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - if (!mLabelSuffix.empty()) + if (new_selection) { - sFont->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); + setSelection(new_selection, FALSE, FALSE); } + } +} - if (sBoxImage.notNull() && mStringMatchOffset != std::string::npos) - { - // don't draw backgrounds for zero-length strings - S32 filter_string_length = getRoot()->getFilterSubString().size(); - if (filter_string_length > 0) - { - std::string combined_string = mLabel + mLabelSuffix; - S32 left = llround(text_left) + sFont->getWidth(combined_string, 0, mStringMatchOffset) - 1; - S32 right = left + sFont->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; - S32 bottom = llfloor(getRect().getHeight() - sFont->getLineHeight() - 3); - S32 top = getRect().getHeight(); - - LLRect box_rect(left, top, right, bottom); - sBoxImage->draw(box_rect, sFilterBGColor); - F32 match_string_left = text_left + sFont->getWidthF32(combined_string, 0, mStringMatchOffset); - F32 y = (F32)getRect().getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD; - sFont->renderUTF8( combined_string, mStringMatchOffset, match_string_left, y, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, - filter_string_length, S32_MAX, &right_x, FALSE ); - } - } +void LLFolderView::clearSelection() +{ + if (mSelectedItems.size() > 0) + { + recursiveDeselect(FALSE); + mSelectedItems.clear(); } + mSelectThisID.setNull(); +} - if( sDebugRects ) +BOOL LLFolderView::getSelectionList(std::set &selection) +{ + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - drawDebugRect(); + selection.insert((*item_it)->getListener()->getUUID()); } - //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview) - //std::set::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); - //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights)) - //{ - // drawDebugRect(); - //} + return (selection.size() != 0); } +BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source) +{ + std::vector types; + std::vector cargo_ids; + selected_items_t::iterator item_it; + BOOL can_drag = TRUE; + if (!mSelectedItems.empty()) + { + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + { + EDragAndDropType type = DAD_NONE; + LLUUID id = LLUUID::null; + can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id); -///---------------------------------------------------------------------------- -/// Class LLFolderViewFolder -///---------------------------------------------------------------------------- + types.push_back(type); + cargo_ids.push_back(id); + } -LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): - LLFolderViewItem( p ), // 0 = no create time - mIsOpen(FALSE), - mExpanderHighlighted(FALSE), - mCurHeight(0.f), - mTargetHeight(0.f), - mAutoOpenCountdown(0.f), - mSubtreeCreationDate(0), - mAmTrash(LLFolderViewFolder::UNKNOWN), - mLastArrangeGeneration( -1 ), - mLastCalculatedWidth(0), - mCompletedFilterGeneration(-1), - mMostFilteredDescendantGeneration(-1), - mNeedsSort(false) -{} + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, source, mSourceID); + } + return can_drag; +} -// Destroys the object -LLFolderViewFolder::~LLFolderViewFolder( void ) +void LLFolderView::commitRename( const LLSD& data ) { - // The LLView base class takes care of object destruction. make sure that we - // don't have mouse or keyboard focus - gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() + finishRenamingItem(); } -// addToFolder() returns TRUE if it succeeds. FALSE otherwise -BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) +void LLFolderView::draw() { - if (!folder) + static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", LLColor4::white); + if (mDebugFilters) { - return FALSE; + std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", + mFilter->getCurrentGeneration(), mFilter->getMinRequiredGeneration(), mFilter->getMustPassGeneration()); + sSmallFont->renderUTF8(current_filter_string, 0, 2, + getRect().getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } - mParentFolder = folder; - root->addItemID(getListener()->getUUID(), this); - return folder->addFolder(this); -} -// Finds width and height of this object and it's children. Also -// makes sure that this view and it's children are the right size. -S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) -{ - // sort before laying out contents - if (mNeedsSort) + // if cursor has moved off of me during drag and drop + // close all auto opened folders + if (!mDragAndDropThisFrame) { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; + closeAutoOpenedFolders(); } - mHasVisibleChildren = hasFilteredDescendants(filter_generation); - - LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState(); - - // calculate height as a single item (without any children), and reshapes rectangle to match - LLFolderViewItem::arrange( width, height, filter_generation ); - - // clamp existing animated height so as to never get smaller than a single item - mCurHeight = llmax((F32)*height, mCurHeight); - - // initialize running height value as height of single item in case we have no children - *height = getItemHeight(); - F32 running_height = (F32)*height; - F32 target_height = (F32)*height; - - // are my children visible? - if (needsArrange()) + // while dragging, update selection rendering to reflect single/multi drag status + if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) { - // set last arrange generation first, in case children are animating - // and need to be arranged again - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - if (mIsOpen) + EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept(); + if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) { - // Add sizes of children - S32 parent_item_height = getRect().getHeight(); - - for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - if (getRoot()->getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? - (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter - } - - if (folderp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() ); - } - } - for(items_t::iterator iit = mItems.begin(); - iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - if (getRoot()->getDebugFilters()) - { - itemp->setVisible(TRUE); - } - else - { - itemp->setVisible(itemp->getFiltered(filter_generation)); - } - - if (itemp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); - // don't change width, as this item is as wide as its parent folder by construction - itemp->reshape( itemp->getRect().getWidth(), child_height); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() ); - } - } + setShowSingleSelection(TRUE); + } + else + { + setShowSingleSelection(FALSE); } - - mTargetHeight = target_height; - // cache this width so next time we can just return it - mLastCalculatedWidth = *width; } else { - // just use existing width - *width = mLastCalculatedWidth; + setShowSingleSelection(FALSE); } - // animate current height towards target height - if (llabs(mCurHeight - mTargetHeight) > 1.f) - { - mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT)); - - requestArrange(); - // hide child elements that fall out of current animated height - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - // number of pixels that bottom of folder label is from top of parent folder - if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - // hide if beyond current folder height - (*fit)->setVisible(FALSE); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - // number of pixels that bottom of item label is from top of parent folder - if (getRect().getHeight() - (*iit)->getRect().mBottom - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - (*iit)->setVisible(FALSE); - } - } - } - else - { - mCurHeight = mTargetHeight; - } - - // don't change width as this item is already as wide as its parent folder - reshape(getRect().getWidth(),llround(mCurHeight)); - - // pass current height value back to parent - *height = llround(mCurHeight); - - return llround(mTargetHeight); -} - -BOOL LLFolderViewFolder::needsArrange() -{ - return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); -} - -void LLFolderViewFolder::requestSort() -{ - mNeedsSort = true; - // whenever item order changes, we need to lay things out again - requestArrange(); -} - -void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up) -{ - mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation); - mCompletedFilterGeneration = generation; - // only aggregate up if we are a lower (older) value - if (recurse_up && mParentFolder && generation < mParentFolder->getCompletedFilterGeneration()) + if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size()) { - mParentFolder->setCompletedFilterGeneration(generation, TRUE); + mSearchString.clear(); } -} -void LLFolderViewFolder::filter( LLInventoryFilter& filter) -{ - S32 filter_generation = filter.getCurrentGeneration(); - // if failed to pass filter newer than must_pass_generation - // you will automatically fail this time, so we only - // check against items that have passed the filter - S32 must_pass_generation = filter.getMustPassGeneration(); - - // if we have already been filtered against this generation, skip out - if (getCompletedFilterGeneration() >= filter_generation) + if (hasVisibleChildren() + || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) { - return; + mStatusText.clear(); } - - // filter folder itself - if (getLastFilterGeneration() < filter_generation) + else { - if (getLastFilterGeneration() >= must_pass_generation && // folder has been compared to a valid precursor filter - !mFiltered) // and did not pass the filter + if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter->getMinRequiredGeneration()) { - // go ahead and flag this folder as done - mLastFilterGeneration = filter_generation; + mStatusText = LLTrans::getString("Searching"); + sFont->renderUTF8(mStatusText, 0, 2, 1, sSearchStatusColor, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } else { - // filter self only on first pass through - LLFolderViewItem::filter( filter ); + mStatusText = LLTrans::getString("InventoryNoMatchingItems"); + sFont->renderUTF8(mStatusText, 0, 2, 1, sSearchStatusColor, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } } - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - mStatusText += llformat("(%d)", mCompletedFilterGeneration); - mStatusText += llformat("+%d", mMostFilteredDescendantGeneration); - } + LLFolderViewFolder::draw(); + + mDragAndDropThisFrame = FALSE; +} - // all descendants have been filtered later than must pass generation - // but none passed - if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation)) +void LLFolderView::finishRenamingItem( void ) +{ + if(!mRenamer) { - // don't traverse children if we've already filtered them since must_pass_generation - // and came back with nothing return; } + if( mRenameItem ) + { + mRenameItem->rename( mRenamer->getText() ); + } + + mRenamer->setCommitOnFocusLost( FALSE ); + mRenamer->setFocus( FALSE ); + mRenamer->setVisible( FALSE ); + mRenamer->setCommitOnFocusLost( TRUE ); + gFocusMgr.setTopCtrl( NULL ); - // we entered here with at least one filter iteration left - // check to see if we have any more before continuing on to children - if (filter.getFilterCount() < 0) + if( mRenameItem ) { - return; + setSelectionFromRoot( mRenameItem, TRUE ); + mRenameItem = NULL; } - // when applying a filter, matching folders get their contents downloaded first - if (filter.isNotDefault() && getFiltered(filter.getMinRequiredGeneration()) && (mListener && !gInventory.isCategoryComplete(mListener->getUUID()))) + // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible. + scrollToShowSelection(); +} + +void LLFolderView::closeRenamer( void ) +{ + // will commit current name (which could be same as original name) + mRenamer->setFocus( FALSE ); + mRenamer->setVisible( FALSE ); + gFocusMgr.setTopCtrl( NULL ); + + if( mRenameItem ) { - gInventory.startBackgroundFetch(mListener->getUUID()); + setSelectionFromRoot( mRenameItem, TRUE ); + mRenameItem = NULL; } +} - // now query children - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) +void LLFolderView::removeSelectedItems( void ) +{ + if(getVisible() && getEnabled()) { - folders_t::iterator fit = iter++; - // have we run out of iterations this frame? - if (filter.getFilterCount() < 0) + // 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 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) { - break; + item = *item_it; + if(item->isRemovable()) + { + items.push_back(item); + } + else + { + llinfos << "Cannot delete " << item->getName() << llendl; + return; + } } - // mMostFilteredDescendantGeneration might have been reset - // in which case we need to update it even for folders that - // don't need to be filtered anymore - if ((*fit)->getCompletedFilterGeneration() >= filter_generation) + // iterate through the new container. + count = items.size(); + LLUUID new_selection_id; + if(count == 1) { - // track latest generation to pass any child items - if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter.getMinRequiredGeneration())) + LLFolderViewItem* item_to_delete = items[0]; + LLFolderViewFolder* parent = item_to_delete->getParentFolder(); + LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE); + if (!new_selection) + { + new_selection = item_to_delete->getPreviousOpenNode(FALSE); + } + if(parent) { - mMostFilteredDescendantGeneration = filter_generation; - if (getRoot()->needsAutoSelect()) + if (parent->removeItem(item_to_delete)) { - (*fit)->setOpenArrangeRecursively(TRUE); + // change selection on successful delete + if (new_selection) + { + setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); + } + else + { + setSelectionFromRoot(NULL, mParentPanel->hasFocus()); + } } } - // just skip it, it has already been filtered - continue; + arrangeAll(); } - - // update this folders filter status (and children) - (*fit)->filter( filter ); - - // track latest generation to pass any child items - if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter_generation)) + else if (count > 1) { - mMostFilteredDescendantGeneration = filter_generation; - if (getRoot()->needsAutoSelect()) + LLDynamicArray listeners; + LLFolderViewEventListener* listener; + LLFolderViewItem* last_item = items[count - 1]; + LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE); + while(new_selection && new_selection->isSelected()) + { + new_selection = new_selection->getNextOpenNode(FALSE); + } + if (!new_selection) + { + new_selection = last_item->getPreviousOpenNode(FALSE); + while (new_selection && new_selection->isSelected()) + { + new_selection = new_selection->getPreviousOpenNode(FALSE); + } + } + if (new_selection) + { + setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); + } + else + { + setSelectionFromRoot(NULL, mParentPanel->hasFocus()); + } + + for(S32 i = 0; i < count; ++i) + { + listener = items[i]->getListener(); + if(listener && (listeners.find(listener) == LLDynamicArray::FAIL)) + { + listeners.put(listener); + } + } + listener = listeners.get(0); + if(listener) { - (*fit)->setOpenArrangeRecursively(TRUE); + listener->removeBatch(listeners); } } + arrangeAll(); + scrollToShowSelection(); } +} - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) +// open the selected item. +void LLFolderView::openSelectedItems( void ) +{ + if(getVisible() && getEnabled()) { - items_t::iterator iit = iter++; - if (filter.getFilterCount() < 0) + if (mSelectedItems.size() == 1) { - break; + mSelectedItems.front()->openItem(); } - if ((*iit)->getLastFilterGeneration() >= filter_generation) + else { - if ((*iit)->getFiltered()) + LLMultiPreview* multi_previewp = new LLMultiPreview(); + LLMultiProperties* multi_propertiesp = new LLMultiProperties(); + + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - mMostFilteredDescendantGeneration = filter_generation; + // IT_{OBJECT,ATTACHMENT} creates LLProperties + // floaters; others create LLPreviews. Put + // each one in the right type of container. + LLFolderViewEventListener* listener = (*item_it)->getListener(); + bool is_prop = listener && (listener->getInventoryType() == LLInventoryType::IT_OBJECT || listener->getInventoryType() == LLInventoryType::IT_ATTACHMENT); + if (is_prop) + LLFloater::setFloaterHost(multi_propertiesp); + else + LLFloater::setFloaterHost(multi_previewp); + (*item_it)->openItem(); } - continue; + + LLFloater::setFloaterHost(NULL); + // *NOTE: LLMulti* will safely auto-delete when open'd + // without any children. + multi_previewp->openFloater(LLSD()); + multi_propertiesp->openFloater(LLSD()); } + } +} - if ((*iit)->getLastFilterGeneration() >= must_pass_generation && - !(*iit)->getFiltered(must_pass_generation)) +void LLFolderView::propertiesSelectedItems( void ) +{ + if(getVisible() && getEnabled()) + { + if (mSelectedItems.size() == 1) { - // failed to pass an earlier filter that was a subset of the current one - // go ahead and flag this item as done - (*iit)->setFiltered(FALSE, filter_generation); - continue; + LLFolderViewItem* folder_item = mSelectedItems.front(); + if(!folder_item) return; + folder_item->getListener()->showProperties(); } - - (*iit)->filter( filter ); - - if ((*iit)->getFiltered(filter.getMinRequiredGeneration())) + else { - mMostFilteredDescendantGeneration = filter_generation; + LLMultiProperties* multi_propertiesp = new LLMultiProperties(); + + LLFloater::setFloaterHost(multi_propertiesp); + + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + { + (*item_it)->getListener()->showProperties(); + } + + LLFloater::setFloaterHost(NULL); + multi_propertiesp->openFloater(LLSD()); } } - - // if we didn't use all filter iterations - // that means we filtered all of our descendants - // instead of exhausting the filter count for this frame - if (filter.getFilterCount() > 0) - { - // flag this folder as having completed filter pass for all descendants - setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/); - } } -void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation) +void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) { - // if this folder is now filtered, but wasn't before - // (it just passed) - if (filtered && !mFiltered) + if (mAutoOpenItems.check() == item || mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) { - // reset current height, because last time we drew it - // it might have had more visible items than now - mCurHeight = 0.f; + return; } - LLFolderViewItem::setFiltered(filtered, filter_generation); -} + // 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(); + } -void LLFolderViewFolder::dirtyFilter() -{ - // we're a folder, so invalidate our completed generation - setCompletedFilterGeneration(-1, FALSE); - LLFolderViewItem::dirtyFilter(); -} + item->requestArrange(); -BOOL LLFolderViewFolder::hasFilteredDescendants() -{ - return mMostFilteredDescendantGeneration >= getRoot()->getFilter()->getCurrentGeneration(); + mAutoOpenItems.push(item); + + item->setOpen(TRUE); + scrollToShowItem(item); } -// Passes selection information on to children and record selection -// information if necessary. -BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus) +void LLFolderView::closeAutoOpenedFolders() { - BOOL rv = FALSE; - if( selection == this ) + while (mAutoOpenItems.check()) { - mIsSelected = TRUE; - if(mListener) - { - mListener->selectItem(); - } - rv = TRUE; - } - else - { - mIsSelected = FALSE; - rv = FALSE; - } - BOOL child_selected = FALSE; - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if((*fit)->setSelection(selection, openitem, take_keyboard_focus)) - { - rv = TRUE; - child_selected = TRUE; - mNumDescendantsSelected++; - } - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if((*iit)->setSelection(selection, openitem, take_keyboard_focus)) - { - rv = TRUE; - child_selected = TRUE; - mNumDescendantsSelected++; - } + LLFolderViewFolder* close_item = mAutoOpenItems.pop(); + close_item->setOpen(FALSE); } - if(openitem && child_selected) + + if (mAutoOpenCandidate) { - setOpenArrangeRecursively(TRUE); + mAutoOpenCandidate->setAutoOpenCountdown(0.f); } - return rv; + mAutoOpenCandidate = NULL; + mAutoOpenTimer.stop(); } -// This method is used to change the selection of an item. If -// selection is 'this', then note selection as true. Returns TRUE -// if this or a child is now selected. -BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, - BOOL selected) +BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder) { - BOOL rv = FALSE; - if(selection == this) - { - mIsSelected = selected; - if(mListener && selected) - { - mListener->selectItem(); - } - rv = TRUE; - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + if (folder && mAutoOpenCandidate == folder) { - folders_t::iterator fit = iter++; - if((*fit)->changeSelection(selection, selected)) + if (mAutoOpenTimer.getStarted()) { - if (selected) + if (!mAutoOpenCandidate->isOpen()) { - mNumDescendantsSelected++; + mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f)); } - else + if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime) { - mNumDescendantsSelected--; + autoOpenItem(folder); + mAutoOpenTimer.stop(); + return TRUE; } - rv = TRUE; } + return FALSE; } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + + // otherwise new candidate, restart timer + if (mAutoOpenCandidate) { - items_t::iterator iit = iter++; - if((*iit)->changeSelection(selection, selected)) - { - if (selected) - { - mNumDescendantsSelected++; - } - else - { - mNumDescendantsSelected--; - } - rv = TRUE; - } + mAutoOpenCandidate->setAutoOpenCountdown(0.f); } - return rv; + mAutoOpenCandidate = folder; + mAutoOpenTimer.start(); + return FALSE; } -S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& selected_items) +BOOL LLFolderView::canCopy() const { - S32 num_selected = 0; - - // pass on to child folders first - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0))) { - folders_t::iterator fit = iter++; - num_selected += (*fit)->extendSelection(selection, last_selected, selected_items); - mNumDescendantsSelected += num_selected; + return FALSE; } - - // handle selection of our immediate children... - BOOL reverse_select = FALSE; - BOOL found_last_selected = FALSE; - BOOL found_selection = FALSE; - LLDynamicArray items_to_select; - LLFolderViewItem* item; - - //...folders first... - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + + for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) { - folders_t::iterator fit = iter++; - item = (*fit); - if(item == selection) - { - found_selection = TRUE; - } - else if (item == last_selected) + const LLFolderViewItem* item = *selected_it; + if (!item->getListener()->isItemCopyable()) { - found_last_selected = TRUE; - if (found_selection) - { - reverse_select = TRUE; - } + return FALSE; } + } + return TRUE; +} - if (found_selection || found_last_selected) +// copy selected item +void LLFolderView::copy() +{ + // *NOTE: total hack to clear the inventory clipboard + LLInventoryClipboard::instance().reset(); + S32 count = mSelectedItems.size(); + if(getVisible() && getEnabled() && (count > 0)) + { + LLFolderViewEventListener* listener = NULL; + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - // deselect currently selected items so they can be pushed back on queue - if (item->isSelected()) + listener = (*item_it)->getListener(); + if(listener) { - item->changeSelection(item, FALSE); + listener->copyToClipboard(); } - items_to_select.put(item); } + } + mSearchString.clear(); +} - if (found_selection && found_last_selected) - { - break; - } +BOOL LLFolderView::canCut() const +{ + return FALSE; +} + +void LLFolderView::cut() +{ + // implement Windows-style cut-and-leave +} + +BOOL LLFolderView::canPaste() const +{ + if (mSelectedItems.empty()) + { + return FALSE; } - if (!(found_selection && found_last_selected)) + if(getVisible() && getEnabled()) { - //,,,then items - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); + item_it != mSelectedItems.end(); ++item_it) { - items_t::iterator iit = iter++; - item = (*iit); - if(item == selection) - { - found_selection = TRUE; - } - else if (item == last_selected) - { - found_last_selected = TRUE; - if (found_selection) - { - reverse_select = TRUE; - } - } - - if (found_selection || found_last_selected) + // *TODO: only check folders and parent folders of items + const LLFolderViewItem* item = (*item_it); + const LLFolderViewEventListener* listener = item->getListener(); + if(!listener || !listener->isClipboardPasteable()) { - // deselect currently selected items so they can be pushed back on queue - if (item->isSelected()) + const LLFolderViewFolder* folderp = item->getParentFolder(); + listener = folderp->getListener(); + if (!listener || !listener->isClipboardPasteable()) { - item->changeSelection(item, FALSE); + return FALSE; } - items_to_select.put(item); - } - - if (found_selection && found_last_selected) - { - break; } } + return TRUE; } + return FALSE; +} - if (found_last_selected && found_selection) +// paste selected item +void LLFolderView::paste() +{ + if(getVisible() && getEnabled()) { - // we have a complete selection inside this folder - for (S32 index = reverse_select ? items_to_select.getLength() - 1 : 0; - reverse_select ? index >= 0 : index < items_to_select.getLength(); reverse_select ? index-- : index++) + // find set of unique folders to paste into + std::set folder_set; + + selected_items_t::iterator selected_it; + for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) { - LLFolderViewItem* item = items_to_select[index]; - if (item->changeSelection(item, TRUE)) + LLFolderViewItem* item = *selected_it; + LLFolderViewEventListener* listener = item->getListener(); + if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY) { - selected_items.put(item); - mNumDescendantsSelected++; - num_selected++; + item = item->getParentFolder(); } + folder_set.insert(item); } - } - else if (found_selection) - { - // last selection was not in this folder....go ahead and select just the new item - if (selection->changeSelection(selection, TRUE)) + + std::set::iterator set_iter; + for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter) { - selected_items.put(selection); - mNumDescendantsSelected++; - num_selected++; + LLFolderViewEventListener* listener = (*set_iter)->getListener(); + if(listener && listener->isClipboardPasteable()) + { + listener->pasteFromClipboard(); + } } } - - return num_selected; + mSearchString.clear(); } -void LLFolderViewFolder::recursiveDeselect(BOOL deselect_self) +// public rename functionality - can only start the process +void LLFolderView::startRenamingSelectedItem( void ) { - // make sure we don't have negative values - llassert(mNumDescendantsSelected >= 0); + // make sure selection is visible + scrollToShowSelection(); - if (mIsSelected && deselect_self) + S32 count = mSelectedItems.size(); + LLFolderViewItem* item = NULL; + if(count > 0) { - mIsSelected = FALSE; - - // update ancestors' count of selected descendents - LLFolderViewFolder* parent_folder = getParentFolder(); - while(parent_folder) - { - parent_folder->mNumDescendantsSelected--; - parent_folder = parent_folder->getParentFolder(); - } + item = mSelectedItems.front(); } - - if (0 == mNumDescendantsSelected) + if(getVisible() && getEnabled() && (count == 1) && item && item->getListener() && + item->getListener()->isItemRenameable()) { - return; - } + mRenameItem = item; - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* item = (*iit); - item->recursiveDeselect(TRUE); - } + updateRenamerPosition(); - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - LLFolderViewFolder* folder = (*fit); - folder->recursiveDeselect(TRUE); - } + mRenamer->setText(item->getName()); + mRenamer->selectAll(); + mRenamer->setVisible( TRUE ); + // set focus will fail unless item is visible + mRenamer->setFocus( TRUE ); + mRenamer->setTopLostCallback(onRenamerLost); + gFocusMgr.setTopCtrl( mRenamer ); + } } -void LLFolderViewFolder::destroyView() +BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) { - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* item = (*iit); - getRoot()->removeItemID(item->getListener()->getUUID()); - } - - std::for_each(mItems.begin(), mItems.end(), DeletePointer()); - mItems.clear(); - - while (!mFolders.empty()) - { - LLFolderViewFolder *folderp = mFolders.back(); - folderp->destroyView(); // removes entry from mFolders - } + BOOL handled = FALSE; - deleteAllChildren(); - - if (mParentFolder) + // SL-51858: Key presses are not being passed to the Popup menu. + // A proper fix is non-trivial so instead just close the menu. + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); + if (menu && menu->isOpen()) { - mParentFolder->removeView(this); + LLMenuGL::sMenuContainer->hideMenus(); } -} -// remove the specified item (and any children) if possible. Return -// TRUE if the item was deleted. -BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item) -{ - if(item->remove()) + LLView *item = NULL; + if (getChildCount() > 0) { - //RN: this seem unneccessary as remove() moves to trash - //removeView(item); - return TRUE; + item = *(getChildList()->begin()); } - return FALSE; -} -// simply remove the view (and any children) Don't bother telling the -// listeners. -void LLFolderViewFolder::removeView(LLFolderViewItem* item) -{ - if (!item || item->getParentFolder() != this) + switch( key ) { - return; - } - // deselect without traversing hierarchy - item->recursiveDeselect(TRUE); - getRoot()->removeFromSelectionList(item); - extractItem(item); - delete item; -} + case KEY_F2: + mSearchString.clear(); + startRenamingSelectedItem(); + handled = TRUE; + break; -// extractItem() removes the specified item from the folder, but -// doesn't delete it. -void LLFolderViewFolder::extractItem( LLFolderViewItem* item ) -{ - items_t::iterator it = std::find(mItems.begin(), mItems.end(), item); - if(it == mItems.end()) - { - // This is an evil downcast. However, it's only doing - // pointer comparison to find if (which it should be ) the - // item is in the container, so it's pretty safe. - LLFolderViewFolder* f = reinterpret_cast(item); - folders_t::iterator ft; - ft = std::find(mFolders.begin(), mFolders.end(), f); - if(ft != mFolders.end()) + case KEY_RETURN: + if (mask == MASK_NONE) { - mFolders.erase(ft); - } - } - else - { - mItems.erase(it); - } - //item has been removed, need to update filter - dirtyFilter(); - //because an item is going away regardless of filter status, force rearrange - requestArrange(); - getRoot()->removeItemID(item->getListener()->getUUID()); - removeChild(item); -} - -bool LLFolderViewFolder::isTrash() const -{ - if (mAmTrash == LLFolderViewFolder::UNKNOWN) - { - mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH, false) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH; - } - return mAmTrash == LLFolderViewFolder::TRASH; -} - -void LLFolderViewFolder::sortBy(U32 order) -{ - if (!mSortFunction.updateSort(order)) - { - // No changes. - return; - } - - // Propegate this change to sub folders - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->sortBy(order); - } - - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - - if (order & LLInventoryFilter::SO_DATE) - { - time_t latest = 0; - - if (!mItems.empty()) - { - LLFolderViewItem* item = *(mItems.begin()); - latest = item->getCreationDate(); - } - - if (!mFolders.empty()) - { - LLFolderViewFolder* folder = *(mFolders.begin()); - if (folder->getCreationDate() > latest) + if( mRenameItem && mRenamer->getVisible() ) + { + finishRenamingItem(); + mSearchString.clear(); + handled = TRUE; + } + else { - latest = folder->getCreationDate(); + LLFolderView::openSelectedItems(); + handled = TRUE; } } - mSubtreeCreationDate = latest; - } -} + break; -void LLFolderViewFolder::setItemSortOrder(U32 ordering) -{ - if (mSortFunction.updateSort(ordering)) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + case KEY_ESCAPE: + if( mRenameItem && mRenamer->getVisible() ) { - folders_t::iterator fit = iter++; - (*fit)->setItemSortOrder(ordering); + closeRenamer(); + handled = TRUE; } + mSearchString.clear(); + break; - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - } -} + case KEY_PAGE_UP: + mSearchString.clear(); + mScrollContainer->pageUp(30); + handled = TRUE; + break; -EInventorySortGroup LLFolderViewFolder::getSortGroup() const -{ - if (isTrash()) - { - return SG_TRASH_FOLDER; - } + case KEY_PAGE_DOWN: + mSearchString.clear(); + mScrollContainer->pageDown(30); + handled = TRUE; + break; - // Folders that can't be moved are 'system' folders. - if( mListener ) - { - if( !(mListener->isItemMovable()) ) - { - return SG_SYSTEM_FOLDER; - } - } - - return SG_NORMAL_FOLDER; -} + case KEY_HOME: + mSearchString.clear(); + mScrollContainer->goToTop(); + handled = TRUE; + break; -BOOL LLFolderViewFolder::isMovable() -{ - if( mListener ) - { - if( !(mListener->isItemMovable()) ) - { - return FALSE; - } + case KEY_END: + mSearchString.clear(); + mScrollContainer->goToBottom(); + break; - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + case KEY_DOWN: + if((mSelectedItems.size() > 0) && mScrollContainer) { - items_t::iterator iit = iter++; - if(!(*iit)->isMovable()) + LLFolderViewItem* last_selected = getCurSelectedItem(); + + if (!mKeyboardSelection) + { + setSelection(last_selected, FALSE, TRUE); + mKeyboardSelection = TRUE; + } + + LLFolderViewItem* next = NULL; + if (mask & MASK_SHIFT) + { + // don't shift select down to children of folders (they are implicitly selected through parent) + next = last_selected->getNextOpenNode(FALSE); + if (next) + { + if (next->isSelected()) + { + // shrink selection + changeSelectionFromRoot(last_selected, FALSE); + } + else if (last_selected->getParentFolder() == next->getParentFolder()) + { + // grow selection + changeSelectionFromRoot(next, TRUE); + } + } + } + else { - return FALSE; + next = last_selected->getNextOpenNode(); + if( next ) + { + if (next == last_selected) + { + return FALSE; + } + setSelection( next, FALSE, TRUE ); + } } + scrollToShowSelection(); + mSearchString.clear(); + handled = TRUE; } + break; - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + case KEY_UP: + if((mSelectedItems.size() > 0) && mScrollContainer) { - folders_t::iterator fit = iter++; - if(!(*fit)->isMovable()) + LLFolderViewItem* last_selected = mSelectedItems.back(); + + if (!mKeyboardSelection) { - return FALSE; + setSelection(last_selected, FALSE, TRUE); + mKeyboardSelection = TRUE; } - } - } - return TRUE; -} + LLFolderViewItem* prev = NULL; + if (mask & MASK_SHIFT) + { + // don't shift select down to children of folders (they are implicitly selected through parent) + prev = last_selected->getPreviousOpenNode(FALSE); + if (prev) + { + if (prev->isSelected()) + { + // shrink selection + changeSelectionFromRoot(last_selected, FALSE); + } + else if (last_selected->getParentFolder() == prev->getParentFolder()) + { + // grow selection + changeSelectionFromRoot(prev, TRUE); + } + } + } + else + { + prev = last_selected->getPreviousOpenNode(); + if( prev ) + { + if (prev == this) + { + return FALSE; + } + setSelection( prev, FALSE, TRUE ); + } + } + scrollToShowSelection(); + mSearchString.clear(); + + handled = TRUE; + } + break; -BOOL LLFolderViewFolder::isRemovable() -{ - if( mListener ) - { - if( !(mListener->isItemRemovable()) ) + case KEY_RIGHT: + if(mSelectedItems.size()) { - return FALSE; + LLFolderViewItem* last_selected = getCurSelectedItem(); + last_selected->setOpen( TRUE ); + mSearchString.clear(); + handled = TRUE; } + break; - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + case KEY_LEFT: + if(mSelectedItems.size()) { - items_t::iterator iit = iter++; - if(!(*iit)->isRemovable()) + LLFolderViewItem* last_selected = getCurSelectedItem(); + LLFolderViewItem* parent_folder = last_selected->getParentFolder(); + if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder()) + { + setSelection(parent_folder, FALSE, TRUE); + } + else { - return FALSE; + last_selected->setOpen( FALSE ); } + mSearchString.clear(); + scrollToShowSelection(); + handled = TRUE; } + break; + } - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + if (!handled && mParentPanel->hasFocus()) + { + if (key == KEY_BACKSPACE) { - folders_t::iterator fit = iter++; - if(!(*fit)->isRemovable()) + mSearchTimer.reset(); + if (mSearchString.size()) { - return FALSE; + mSearchString.erase(mSearchString.size() - 1, 1); } + search(getCurSelectedItem(), mSearchString, FALSE); + handled = TRUE; } } - return TRUE; -} -// this is an internal method used for adding items to folders. -BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item) -{ - mItems.push_back(item); - item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); - item->setVisible(FALSE); - addChild( item ); - item->dirtyFilter(); - requestArrange(); - requestSort(); - return TRUE; + return handled; } -// this is an internal method used for adding items to folders. -BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) -{ - mFolders.push_back(folder); - folder->setOrigin(0, 0); - folder->reshape(getRect().getWidth(), 0); - folder->setVisible(FALSE); - addChild( folder ); - folder->dirtyFilter(); - // rearrange all descendants too, as our indentation level might have changed - folder->requestArrange(TRUE); - requestSort(); - return TRUE; -} -void LLFolderViewFolder::requestArrange(BOOL include_descendants) -{ - mLastArrangeGeneration = -1; - // flag all items up to root - if (mParentFolder) +BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) +{ + if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL { - mParentFolder->requestArrange(); + return FALSE; } - if (include_descendants) + if (uni_char > 0x7f) { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - (*iter)->requestArrange(TRUE); - } + llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl; + return FALSE; } -} - -void LLFolderViewFolder::toggleOpen() -{ - setOpen(!mIsOpen); -} - -// Force a folder open or closed -void LLFolderViewFolder::setOpen(BOOL openitem) -{ - setOpenArrangeRecursively(openitem); -} -void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) -{ - BOOL was_open = mIsOpen; - mIsOpen = openitem; - if(!was_open && openitem) + BOOL handled = FALSE; + if (gFocusMgr.childHasKeyboardFocus(getRoot())) { - if(mListener) + // SL-51858: Key presses are not being passed to the Popup menu. + // A proper fix is non-trivial so instead just close the menu. + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); + if (menu && menu->isOpen()) { - mListener->openItem(); + LLMenuGL::sMenuContainer->hideMenus(); } - } - if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + //do text search + if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout")) { - folders_t::iterator fit = iter++; - (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN); /* Flawfinder: ignore */ - } - } - if (mParentFolder && (recurse == RECURSE_UP || recurse == RECURSE_UP_DOWN)) - { - mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP); - } - - if (was_open != mIsOpen) - { - requestArrange(); - } -} - -BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, - BOOL drop, - EDragAndDropType c_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data); - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - // drag and drop to child item, so clear pending auto-opens - getRoot()->autoOpenTest(NULL); - - return TRUE; -} - -void LLFolderViewFolder::openItem( void ) -{ - toggleOpen(); -} - -void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor) -{ - functor.doFolder(this); - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->applyFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - functor.doItem((*iit)); - } -} - -void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->applyListenerFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - (*iit)->applyListenerFunctorRecursively(functor); - } -} - -// LLView functionality -BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - LLFolderView* root_view = getRoot(); - - BOOL handled = FALSE; - if(mIsOpen) - { - handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, - cargo_data, accept, tooltip_msg) != NULL; - } - - if (!handled) - { - BOOL accepted = mListener && mListener->dragOrDrop(mask, drop,cargo_type,cargo_data); - - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - if (!drop && accepted) - { - root_view->autoOpenTest(this); - } - - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl; - } - - return TRUE; -} - - -BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - // fetch contents of this folder, as context menu can depend on contents - // still, user would have to open context menu again to see the changes - gInventory.fetchDescendentsOf(mListener->getUUID()); - - if( mIsOpen ) - { - handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - } - if (!handled) - { - handled = LLFolderViewItem::handleRightMouseDown( x, y, mask ); - } - return handled; -} - - -BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) -{ - BOOL handled = LLView::handleHover(x, y, mask); - - if (!handled) - { - // this doesn't do child processing - handled = LLFolderViewItem::handleHover(x, y, mask); - } - - //if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD && y > getRect().getHeight() - ) - //{ - // gViewerWindow->setCursor(UI_CURSOR_ARROW); - // mExpanderHighlighted = TRUE; - // handled = TRUE; - //} - return handled; -} - -BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - if( mIsOpen ) - { - handled = childrenHandleMouseDown(x,y,mask) != NULL; - } - if( !handled ) - { - if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD) - { - toggleOpen(); - handled = TRUE; - } - else - { - // do normal selection logic - handled = LLFolderViewItem::handleMouseDown(x, y, mask); - } - } - - return handled; -} - -BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - if( mIsOpen ) - { - handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - } - if( !handled ) - { - if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD) - { - // don't select when user double-clicks plus sign - // so as not to contradict single-click behavior - toggleOpen(); - } - else - { - setSelectionFromRoot(this, FALSE); - toggleOpen(); - } - handled = TRUE; - } - return handled; -} - -void LLFolderViewFolder::draw() -{ - if (mAutoOpenCountdown != 0.f) - { - mControlLabelRotation = mAutoOpenCountdown * -90.f; - } - else if (mIsOpen) - { - mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); - } - else - { - mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); - } - - bool possibly_has_children = false; - bool up_to_date = mListener && mListener->isUpToDate(); - if(!up_to_date && mListener && mListener->hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) - { - possibly_has_children = true; - } - - - BOOL loading = ( mIsOpen && possibly_has_children && !up_to_date ); - - if ( loading && !mIsLoading ) - { - // Measure how long we've been in the loading state - mTimeSinceRequestStart.reset(); - } - - mIsLoading = loading; - - LLFolderViewItem::draw(); - - // draw children if root folder, or any other folder that is open or animating to closed state - if( getRoot() == this || (mIsOpen || mCurHeight != mTargetHeight )) - { - LLView::draw(); - } - - mExpanderHighlighted = FALSE; -} - -time_t LLFolderViewFolder::getCreationDate() const -{ - return llmax(mCreationDate, mSubtreeCreationDate); -} - - -BOOL LLFolderViewFolder::potentiallyVisible() -{ - // folder should be visible by it's own filter status - return LLFolderViewItem::potentiallyVisible() - // or one or more of its descendants have passed the minimum filter requirement - || hasFilteredDescendants(getRoot()->getFilter()->getMinRequiredGeneration()) - // or not all of its descendants have been checked against minimum filter requirement - || getCompletedFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration(); -} - -// this does prefix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children ) -{ - BOOL found_item = FALSE; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at beginning - if(item == NULL) - { - found_item = TRUE; - } - - // find current item among children - folders_t::iterator fit = mFolders.begin(); - folders_t::iterator fend = mFolders.end(); - - items_t::iterator iit = mItems.begin(); - items_t::iterator iend = mItems.end(); - - // if not trivially starting at the beginning, we have to find the current item - if (!found_item) - { - // first, look among folders, since they are always above items - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = TRUE; - // if we are on downwards traversal - if (include_children && (*fit)->isOpen()) - { - // look for first descendant - return (*fit)->getNextFromChild(NULL, TRUE); - } - // otherwise advance to next folder - ++fit; - include_children = TRUE; - break; - } - } - - // didn't find in folders? Check items... - if (!found_item) - { - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = TRUE; - // point to next item - ++iit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(FALSE); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found folder, continue through folders - // searching for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - // turn on downwards traversal for next folder - ++fit; - } - - if (fit != fend) - { - result = (*fit); - } - else - { - // otherwise, scan for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - // check to see if we have a valid item - if (iit != iend) - { - result = (*iit); - } - } - - if( !result && mParentFolder ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // and skip children for this folder, as we've already discounted them - result = mParentFolder->getNextFromChild(this, FALSE); - } - - return result; -} - -// this does postfix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children ) -{ - BOOL found_item = FALSE; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at end - if(item == NULL) - { - found_item = TRUE; - } - - // find current item among children - folders_t::reverse_iterator fit = mFolders.rbegin(); - folders_t::reverse_iterator fend = mFolders.rend(); - - items_t::reverse_iterator iit = mItems.rbegin(); - items_t::reverse_iterator iend = mItems.rend(); - - // if not trivially starting at the end, we have to find the current item - if (!found_item) - { - // first, look among items, since they are always below the folders - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = TRUE; - // point to next item - ++iit; - break; - } - } - - // didn't find in items? Check folders... - if (!found_item) - { - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = TRUE; - // point to next folder - ++fit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(FALSE); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found item, continue through items - // searching for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - if (iit != iend) - { - // we found an appropriate item - result = (*iit); - } - else - { - // otherwise, scan for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - ++fit; - } - - // check to see if we have a valid folder - if (fit != fend) - { - // try selecting child element of this folder - if ((*fit)->isOpen()) - { - result = (*fit)->getPreviousFromChild(NULL); - } - else - { - result = (*fit); - } - } - } - - if( !result ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // which gets back to this folder, which will only be visited if it is a valid, visible item - result = this; - } - - return result; -} - - -//--------------------------------------------------------------------------- - -// Tells all folders in a folderview to sort their items -// (and only their items, not folders) by a certain function. -class LLSetItemSortFunction : public LLFolderViewFunctor -{ -public: - LLSetItemSortFunction(U32 ordering) - : mSortOrder(ordering) {} - virtual ~LLSetItemSortFunction() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - - U32 mSortOrder; -}; - - -// Set the sort order. -void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder) -{ - folder->setItemSortOrder(mSortOrder); -} - -// Do nothing. -void LLSetItemSortFunction::doItem(LLFolderViewItem* item) -{ - return; -} - -//--------------------------------------------------------------------------- - -// 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 -{ -public: - LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; } - virtual ~LLCloseAllFoldersFunctor() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - - BOOL mOpen; -}; - - -// Set the sort order. -void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder) -{ - folder->setOpenArrangeRecursively(mOpen); -} - -// Do nothing. -void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item) -{ } - -///---------------------------------------------------------------------------- -/// Class LLFolderView -///---------------------------------------------------------------------------- - -// Default constructor -LLFolderView::LLFolderView(const Params& p) -: LLFolderViewFolder(p), - mScrollContainer( NULL ), - mPopupMenuHandle(), - mAllowMultiSelect(TRUE), - mShowFolderHierarchy(FALSE), - mSourceID(p.task_id), - mRenameItem( NULL ), - mNeedsScroll( FALSE ), - mLastScrollItem( NULL ), - mNeedsAutoSelect( FALSE ), - mAutoSelectOverride(FALSE), - mNeedsAutoRename(FALSE), - mDebugFilters(FALSE), - mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately - mFilter(p.name), - mShowSelectionContext(FALSE), - mShowSingleSelection(FALSE), - mArrangeGeneration(0), - mSignalSelectCallback(0), - mMinWidth(0), - mDragAndDropThisFrame(FALSE), - mCallbackRegistrar(NULL), - mParentPanel(p.parent_panel) - -{ - 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()); - mIsOpen = TRUE; // this view is always open. - mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH); - mAutoOpenCandidate = NULL; - mAutoOpenTimer.stop(); - mKeyboardSelection = FALSE; - mIndentation = -LEFT_INDENTATION; // children start at indentation 0 - gIdleCallbacks.addFunction(idle, this); - - //clear label - // go ahead and render root folder as usual - // just make sure the label ("Inventory Folder") never shows up - mLabel = LLStringUtil::null; - - //mRenamer->setWriteableBgColor(LLColor4::white); - // Escape is handled by reverting the rename, not commiting it (default behavior) - LLLineEditor::Params params; - params.name("ren"); - params.rect(getRect()); - params.font(sFont); - params.max_length_bytes(DB_INV_ITEM_NAME_STR_LEN); - params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2)); - params.prevalidate_callback(&LLLineEditor::prevalidatePrintableNotPipe); - params.commit_on_focus_lost(true); - params.visible(false); - mRenamer = LLUICtrlFactory::create (params); - addChild(mRenamer); - - // make the popup menu available - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_inventory.xml", gMenuHolder); - if (!menu) - { - menu = LLUICtrlFactory::getDefaultWidget("inventory_menu"); - } - menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); - mPopupMenuHandle = menu->getHandle(); - -} - -// Destroys the object -LLFolderView::~LLFolderView( void ) -{ - // 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; - - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - - mAutoOpenItems.removeAllNodes(); - gIdleCallbacks.deleteFunction(idle, this); - - LLView::deleteViewByHandle(mPopupMenuHandle); - - if(mRenamer == gFocusMgr.getTopCtrl()) - { - gFocusMgr.setTopCtrl(NULL); - } - - mAutoOpenItems.removeAllNodes(); - clearSelection(); - mItems.clear(); - mFolders.clear(); - - mItemMap.clear(); -} - -BOOL LLFolderView::canFocusChildren() const -{ - return FALSE; -} - -void LLFolderView::checkTreeResortForModelChanged() -{ - if (mSortOrder & LLInventoryFilter::SO_DATE && !(mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME)) - { - // This is the case where something got added or removed. If we are date sorting - // everything including folders, then we need to rebuild the whole tree. - // Just set to something not SO_DATE to force the folder most resent date resort. - mSortOrder = mSortOrder & ~LLInventoryFilter::SO_DATE; - setSortOrder(mSortOrder | LLInventoryFilter::SO_DATE); - } -} - -void LLFolderView::setSortOrder(U32 order) -{ - if (order != mSortOrder) - { - LLFastTimer t(LLFastTimer::FTM_SORT); - mSortOrder = order; - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->sortBy(order); - } - - arrangeAll(); - } -} - - -U32 LLFolderView::getSortOrder() const -{ - return mSortOrder; -} - -BOOL LLFolderView::addFolder( LLFolderViewFolder* folder) -{ - // enforce sort order of My Inventory followed by Library - if (folder->getListener()->getUUID() == gInventoryLibraryRoot) - { - mFolders.push_back(folder); - } - else - { - mFolders.insert(mFolders.begin(), folder); - } - folder->setOrigin(0, 0); - folder->reshape(getRect().getWidth(), 0); - folder->setVisible(FALSE); - addChild( folder ); - folder->dirtyFilter(); - folder->requestArrange(); - return TRUE; -} - -void LLFolderView::closeAllFolders() -{ - // Close all the folders - setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); - arrangeAll(); -} - -void LLFolderView::openFolder(const std::string& foldername) -{ - LLFolderViewFolder* inv = findChild(foldername); - if (inv) - { - setSelection(inv, FALSE, FALSE); - inv->setOpen(TRUE); - } -} - -void LLFolderView::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) -{ - // call base class to do proper recursion - LLFolderViewFolder::setOpenArrangeRecursively(openitem, recurse); - // make sure root folder is always open - mIsOpen = TRUE; -} - -// This view grows and shinks to enclose all of its children items and folders. -S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation ) -{ - LLFastTimer t2(LLFastTimer::FTM_ARRANGE); - - filter_generation = mFilter.getMinRequiredGeneration(); - mMinWidth = 0; - - mHasVisibleChildren = hasFilteredDescendants(filter_generation); - // arrange always finishes, so optimistically set the arrange generation to the most current - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - - LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState(); - - S32 total_width = LEFT_PAD; - S32 running_height = mDebugFilters ? llceil(sSmallFont->getLineHeight()) : 0; - S32 target_height = running_height; - S32 parent_item_height = getRect().getHeight(); - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - LLFolderViewFolder* folderp = (*fit); - if (getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? - (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter - } - if (folderp->getVisible()) - { - S32 child_height = 0; - S32 child_width = 0; - S32 child_top = parent_item_height - running_height; - - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); - - mMinWidth = llmax(mMinWidth, child_width); - total_width = llmax( total_width, child_width ); - running_height += child_height; - folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() ); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* itemp = (*iit); - itemp->setVisible(itemp->getFiltered(filter_generation)); - - if (itemp->getVisible()) - { - S32 child_width = 0; - S32 child_height = 0; - S32 child_top = parent_item_height - running_height; - - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); - itemp->reshape(itemp->getRect().getWidth(), child_height); - - mMinWidth = llmax(mMinWidth, child_width); - total_width = llmax( total_width, child_width ); - running_height += child_height; - itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() ); - } - } - - S32 dummy_s32; - BOOL dummy_bool; - S32 min_width; - mScrollContainer->calcVisibleSize( &min_width, &dummy_s32, &dummy_bool, &dummy_bool); - reshape( llmax(min_width, total_width), running_height ); - - S32 new_min_width; - mScrollContainer->calcVisibleSize( &new_min_width, &dummy_s32, &dummy_bool, &dummy_bool); - if (new_min_width != min_width) - { - reshape( llmax(min_width, total_width), running_height ); - } - - // move item renamer text field to item's new position - updateRenamerPosition(); - - mTargetHeight = (F32)target_height; - return llround(mTargetHeight); -} - -const std::string LLFolderView::getFilterSubString(BOOL trim) -{ - return mFilter.getFilterSubString(trim); -} - -void LLFolderView::filter( LLInventoryFilter& filter ) -{ - LLFastTimer t2(LLFastTimer::FTM_FILTER); - filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000)); - - if (getCompletedFilterGeneration() < filter.getCurrentGeneration()) - { - mFiltered = FALSE; - mMinWidth = 0; - LLFolderViewFolder::filter(filter); - } -} - -void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - S32 min_width = 0; - S32 dummy_height; - BOOL dummy_bool; - if (mScrollContainer) - { - mScrollContainer->calcVisibleSize( &min_width, &dummy_height, &dummy_bool, &dummy_bool); - } - width = llmax(mMinWidth, min_width); - LLView::reshape(width, height, called_from_parent); - - mReshapeSignal(mSelectedItems, FALSE); -} - -void LLFolderView::addToSelectionList(LLFolderViewItem* item) -{ - if (item->isSelected()) - { - removeFromSelectionList(item); - } - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(FALSE); - } - item->setIsCurSelection(TRUE); - mSelectedItems.push_back(item); -} - -void LLFolderView::removeFromSelectionList(LLFolderViewItem* item) -{ - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(FALSE); - } - - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();) - { - if (*item_iter == item) - { - item_iter = mSelectedItems.erase(item_iter); - } - else - { - ++item_iter; - } - } - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(TRUE); - } -} - -LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) -{ - if(mSelectedItems.size()) - { - LLFolderViewItem* itemp = mSelectedItems.back(); - llassert(itemp->getIsCurSelection()); - return itemp; - } - return NULL; -} - - -// Record the selected item and pass it down the hierachy. -BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus) -{ - if( selection == this ) - { - return FALSE; - } - - if( selection && take_keyboard_focus) - { - mParentPanel->setFocus(TRUE); - } - - // clear selection down here because change of keyboard focus can potentially - // affect selection - clearSelection(); - - if(selection) - { - addToSelectionList(selection); - } - - BOOL rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus); - if(openitem && selection) - { - selection->getParentFolder()->requestArrange(); - } - - llassert(mSelectedItems.size() <= 1); - - mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS; - - return rv; -} - -void LLFolderView::setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus) -{ - LLFolderViewItem* itemp = getItemByID(obj_id); - if(itemp && itemp->getListener()) - { - itemp->getListener()->arrangeAndSet(itemp, TRUE, take_keyboard_focus); - mSelectThisID.setNull(); - return; - } - else - { - // save the desired item to be selected later (if/when ready) - mSelectThisID = obj_id; - } -} - -void LLFolderView::updateSelection() -{ - if (mSelectThisID.notNull()) - { - setSelectionByID(mSelectThisID, false); - } -} - -BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) -{ - BOOL rv = FALSE; - - // can't select root folder - if(!selection || selection == this) - { - return FALSE; - } - - if (!mAllowMultiSelect) - { - clearSelection(); - } - - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) - { - if (*item_iter == selection) - { - break; - } - } - - BOOL on_list = (item_iter != mSelectedItems.end()); - - if(selected && !on_list) - { - addToSelectionList(selection); - } - if(!selected && on_list) - { - removeFromSelectionList(selection); - } - - rv = LLFolderViewFolder::changeSelection(selection, selected); - - mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS; - - return rv; -} - -S32 LLFolderView::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& items) -{ - S32 rv = 0; - - // now store resulting selection - if (mAllowMultiSelect) - { - LLFolderViewItem *cur_selection = getCurSelectedItem(); - rv = LLFolderViewFolder::extendSelection(selection, cur_selection, items); - for (S32 i = 0; i < items.count(); i++) - { - addToSelectionList(items[i]); - rv++; - } - } - else - { - setSelection(selection, FALSE, FALSE); - rv++; - } - - mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS; - return rv; -} - -void LLFolderView::sanitizeSelection() -{ - // store off current item in case it is automatically deselected - // and we want to preserve context - LLFolderViewItem* original_selected_item = getCurSelectedItem(); - - // Cache "Show all folders" filter setting - BOOL show_all_folders = (getRoot()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS); - - std::vector 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 - BOOL visible = item->potentiallyVisible(); // initialize from filter state for this item - // modify with parent open and filters states - LLFolderViewFolder* parent_folder = item->getParentFolder(); - if ( parent_folder ) - { - if ( show_all_folders ) - { // "Show all folders" is on, so this folder is visible - visible = TRUE; - } - else - { // Move up through parent folders and see what's visible - while(parent_folder) - { - visible = visible && parent_folder->isOpen() && parent_folder->potentiallyVisible(); - parent_folder = parent_folder->getParentFolder(); - } - } - } - - // deselect item if any ancestor is closed or didn't pass filter requirements. - if (!visible) - { - items_to_remove.push_back(item); - } - - // 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) - { - LLFolderViewItem* other_item = *other_item_iter; - for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder()) - { - if (parent_folder == item) - { - // this is a descendent of the current folder, remove from list - items_to_remove.push_back(other_item); - break; - } - } - } - } - - std::vector::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) - } - - // if nothing selected after prior constraints... - if (mSelectedItems.empty()) - { - // ...select first available parent of original selection, or "My Inventory" otherwise - LLFolderViewItem* new_selection = NULL; - if (original_selected_item) - { - for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder(); - parent_folder; - parent_folder = parent_folder->getParentFolder()) - { - if (parent_folder->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; - } - } - } - } - else - { - // nothing selected to start with, so pick "My Inventory" as best guess - new_selection = getItemByID(gAgent.getInventoryRootID()); - } - - if (new_selection) - { - setSelection(new_selection, FALSE, FALSE); - } - } -} - -void LLFolderView::clearSelection() -{ - if (mSelectedItems.size() > 0) - { - recursiveDeselect(FALSE); - mSelectedItems.clear(); - } - mSelectThisID.setNull(); -} - -BOOL LLFolderView::getSelectionList(std::set &selection) -{ - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - selection.insert((*item_it)->getListener()->getUUID()); - } - - return (selection.size() != 0); -} - -BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source) -{ - std::vector types; - std::vector cargo_ids; - selected_items_t::iterator item_it; - BOOL can_drag = TRUE; - if (!mSelectedItems.empty()) - { - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - EDragAndDropType type = DAD_NONE; - LLUUID id = LLUUID::null; - can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id); - - types.push_back(type); - cargo_ids.push_back(id); - } - - LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, source, mSourceID); - } - return can_drag; -} - -void LLFolderView::commitRename( const LLSD& data ) -{ - finishRenamingItem(); -} - -void LLFolderView::draw() -{ - static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); - if (mDebugFilters) - { - std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", - mFilter.getCurrentGeneration(), mFilter.getMinRequiredGeneration(), mFilter.getMustPassGeneration()); - sSmallFont->renderUTF8(current_filter_string, 0, 2, - getRect().getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); - } - - // if cursor has moved off of me during drag and drop - // close all auto opened folders - if (!mDragAndDropThisFrame) - { - closeAutoOpenedFolders(); - } - - // while dragging, update selection rendering to reflect single/multi drag status - if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) - { - EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept(); - if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) - { - setShowSingleSelection(TRUE); - } - else - { - setShowSingleSelection(FALSE); - } - } - else - { - setShowSingleSelection(FALSE); - } - - - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size()) - { - mSearchString.clear(); - } - - if (hasVisibleChildren() || getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) - { - mStatusText.clear(); - } - else - { - if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter.getMinRequiredGeneration()) - { - mStatusText = LLTrans::getString("Searching"); - sFont->renderUTF8(mStatusText, 0, 2, 1, sSearchStatusColor, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); - } - else - { - mStatusText = LLTrans::getString("InventoryNoMatchingItems"); - sFont->renderUTF8(mStatusText, 0, 2, 1, sSearchStatusColor, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); - } - } - - LLFolderViewFolder::draw(); - - mDragAndDropThisFrame = FALSE; -} - -void LLFolderView::finishRenamingItem( void ) -{ - if(!mRenamer) - { - return; - } - if( mRenameItem ) - { - mRenameItem->rename( mRenamer->getText() ); - } - - mRenamer->setCommitOnFocusLost( FALSE ); - mRenamer->setFocus( FALSE ); - mRenamer->setVisible( FALSE ); - mRenamer->setCommitOnFocusLost( TRUE ); - gFocusMgr.setTopCtrl( NULL ); - - if( mRenameItem ) - { - setSelectionFromRoot( mRenameItem, TRUE ); - mRenameItem = NULL; - } - - // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible. - scrollToShowSelection(); -} - -void LLFolderView::closeRenamer( void ) -{ - // will commit current name (which could be same as original name) - mRenamer->setFocus( FALSE ); - mRenamer->setVisible( FALSE ); - gFocusMgr.setTopCtrl( NULL ); - - if( mRenameItem ) - { - setSelectionFromRoot( mRenameItem, TRUE ); - mRenameItem = NULL; - } -} - -void LLFolderView::removeSelectedItems( void ) -{ - if(getVisible() && getEnabled()) - { - // 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 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) - { - item = *item_it; - if(item->isRemovable()) - { - items.push_back(item); - } - else - { - llinfos << "Cannot delete " << item->getName() << llendl; - return; - } - } - - // iterate through the new container. - count = items.size(); - LLUUID new_selection_id; - if(count == 1) - { - LLFolderViewItem* item_to_delete = items[0]; - LLFolderViewFolder* parent = item_to_delete->getParentFolder(); - LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE); - if (!new_selection) - { - new_selection = item_to_delete->getPreviousOpenNode(FALSE); - } - if(parent) - { - if (parent->removeItem(item_to_delete)) - { - // change selection on successful delete - if (new_selection) - { - setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); - } - else - { - setSelectionFromRoot(NULL, mParentPanel->hasFocus()); - } - } - } - arrangeAll(); - } - else if (count > 1) - { - LLDynamicArray listeners; - LLFolderViewEventListener* listener; - LLFolderViewItem* last_item = items[count - 1]; - LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE); - while(new_selection && new_selection->isSelected()) - { - new_selection = new_selection->getNextOpenNode(FALSE); - } - if (!new_selection) - { - new_selection = last_item->getPreviousOpenNode(FALSE); - while (new_selection && new_selection->isSelected()) - { - new_selection = new_selection->getPreviousOpenNode(FALSE); - } - } - if (new_selection) - { - setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); - } - else - { - setSelectionFromRoot(NULL, mParentPanel->hasFocus()); - } - - for(S32 i = 0; i < count; ++i) - { - listener = items[i]->getListener(); - if(listener && (listeners.find(listener) == LLDynamicArray::FAIL)) - { - listeners.put(listener); - } - } - listener = listeners.get(0); - if(listener) - { - listener->removeBatch(listeners); - } - } - arrangeAll(); - scrollToShowSelection(); - } -} - -// open the selected item. -void LLFolderView::openSelectedItems( void ) -{ - if(getVisible() && getEnabled()) - { - if (mSelectedItems.size() == 1) - { - mSelectedItems.front()->openItem(); - } - else - { - LLMultiPreview* multi_previewp = new LLMultiPreview(); - LLMultiProperties* multi_propertiesp = new LLMultiProperties(); - - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - // IT_{OBJECT,ATTACHMENT} creates LLProperties - // floaters; others create LLPreviews. Put - // each one in the right type of container. - LLFolderViewEventListener* listener = (*item_it)->getListener(); - bool is_prop = listener && (listener->getInventoryType() == LLInventoryType::IT_OBJECT || listener->getInventoryType() == LLInventoryType::IT_ATTACHMENT); - if (is_prop) - LLFloater::setFloaterHost(multi_propertiesp); - else - LLFloater::setFloaterHost(multi_previewp); - (*item_it)->openItem(); - } - - LLFloater::setFloaterHost(NULL); - // *NOTE: LLMulti* will safely auto-delete when open'd - // without any children. - multi_previewp->openFloater(LLSD()); - multi_propertiesp->openFloater(LLSD()); - } - } -} - -void LLFolderView::propertiesSelectedItems( void ) -{ - if(getVisible() && getEnabled()) - { - if (mSelectedItems.size() == 1) - { - LLFolderViewItem* folder_item = mSelectedItems.front(); - if(!folder_item) return; - folder_item->getListener()->showProperties(); - } - else - { - LLMultiProperties* multi_propertiesp = new LLMultiProperties(); - - LLFloater::setFloaterHost(multi_propertiesp); - - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - (*item_it)->getListener()->showProperties(); - } - - LLFloater::setFloaterHost(NULL); - multi_propertiesp->openFloater(LLSD()); - } - } -} - -void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) -{ - if (mAutoOpenItems.check() == item || mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) - { - return; - } - - // 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(); - } - - item->requestArrange(); - - mAutoOpenItems.push(item); - - item->setOpen(TRUE); - scrollToShowItem(item); -} - -void LLFolderView::closeAutoOpenedFolders() -{ - while (mAutoOpenItems.check()) - { - LLFolderViewFolder* close_item = mAutoOpenItems.pop(); - close_item->setOpen(FALSE); - } - - if (mAutoOpenCandidate) - { - mAutoOpenCandidate->setAutoOpenCountdown(0.f); - } - mAutoOpenCandidate = NULL; - mAutoOpenTimer.stop(); -} - -BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder) -{ - if (folder && mAutoOpenCandidate == folder) - { - if (mAutoOpenTimer.getStarted()) - { - if (!mAutoOpenCandidate->isOpen()) - { - mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f)); - } - if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime) - { - autoOpenItem(folder); - mAutoOpenTimer.stop(); - return TRUE; - } - } - return FALSE; - } - - // otherwise new candidate, restart timer - if (mAutoOpenCandidate) - { - mAutoOpenCandidate->setAutoOpenCountdown(0.f); - } - mAutoOpenCandidate = folder; - mAutoOpenTimer.start(); - return FALSE; -} - -BOOL LLFolderView::canCopy() const -{ - 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; - if (!item->getListener()->isItemCopyable()) - { - return FALSE; - } - } - return TRUE; -} - -// copy selected item -void LLFolderView::copy() -{ - // *NOTE: total hack to clear the inventory clipboard - LLInventoryClipboard::instance().reset(); - S32 count = mSelectedItems.size(); - if(getVisible() && getEnabled() && (count > 0)) - { - LLFolderViewEventListener* listener = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - listener = (*item_it)->getListener(); - if(listener) - { - listener->copyToClipboard(); - } - } - } - mSearchString.clear(); -} - -BOOL LLFolderView::canCut() const -{ - return FALSE; -} - -void LLFolderView::cut() -{ - // implement Windows-style cut-and-leave -} - -BOOL LLFolderView::canPaste() const -{ - if (mSelectedItems.empty()) - { - return FALSE; - } - - if(getVisible() && getEnabled()) - { - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); ++item_it) - { - // *TODO: only check folders and parent folders of items - const LLFolderViewItem* item = (*item_it); - const LLFolderViewEventListener* listener = item->getListener(); - if(!listener || !listener->isClipboardPasteable()) - { - const LLFolderViewFolder* folderp = item->getParentFolder(); - listener = folderp->getListener(); - if (!listener || !listener->isClipboardPasteable()) - { - return FALSE; - } - } - } - return TRUE; - } - return FALSE; -} - -// paste selected item -void LLFolderView::paste() -{ - if(getVisible() && getEnabled()) - { - // find set of unique folders to paste into - std::set folder_set; - - selected_items_t::iterator selected_it; - for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) - { - LLFolderViewItem* item = *selected_it; - LLFolderViewEventListener* listener = item->getListener(); - if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY) - { - item = item->getParentFolder(); - } - folder_set.insert(item); - } - - std::set::iterator set_iter; - for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter) - { - LLFolderViewEventListener* listener = (*set_iter)->getListener(); - if(listener && listener->isClipboardPasteable()) - { - listener->pasteFromClipboard(); - } - } - } - mSearchString.clear(); -} - -// public rename functionality - can only start the process -void LLFolderView::startRenamingSelectedItem( void ) -{ - // make sure selection is visible - scrollToShowSelection(); - - S32 count = mSelectedItems.size(); - LLFolderViewItem* item = NULL; - if(count > 0) - { - item = mSelectedItems.front(); - } - if(getVisible() && getEnabled() && (count == 1) && item && item->getListener() && - item->getListener()->isItemRenameable()) - { - mRenameItem = item; - - updateRenamerPosition(); - - - mRenamer->setText(item->getName()); - mRenamer->selectAll(); - mRenamer->setVisible( TRUE ); - // set focus will fail unless item is visible - mRenamer->setFocus( TRUE ); - mRenamer->setTopLostCallback(onRenamerLost); - gFocusMgr.setTopCtrl( mRenamer ); - } -} - -BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) -{ - BOOL handled = FALSE; - - // SL-51858: Key presses are not being passed to the Popup menu. - // A proper fix is non-trivial so instead just close the menu. - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - LLView *item = NULL; - if (getChildCount() > 0) - { - item = *(getChildList()->begin()); - } - - switch( key ) - { - case KEY_F2: - mSearchString.clear(); - startRenamingSelectedItem(); - handled = TRUE; - break; - - case KEY_RETURN: - if (mask == MASK_NONE) - { - if( mRenameItem && mRenamer->getVisible() ) - { - finishRenamingItem(); - mSearchString.clear(); - handled = TRUE; - } - else - { - LLFolderView::openSelectedItems(); - handled = TRUE; - } - } - break; - - case KEY_ESCAPE: - if( mRenameItem && mRenamer->getVisible() ) - { - closeRenamer(); - handled = TRUE; - } - mSearchString.clear(); - break; - - case KEY_PAGE_UP: - mSearchString.clear(); - mScrollContainer->pageUp(30); - handled = TRUE; - break; - - case KEY_PAGE_DOWN: - mSearchString.clear(); - mScrollContainer->pageDown(30); - handled = TRUE; - break; - - case KEY_HOME: - mSearchString.clear(); - mScrollContainer->goToTop(); - handled = TRUE; - break; - - case KEY_END: - mSearchString.clear(); - mScrollContainer->goToBottom(); - break; - - case KEY_DOWN: - if((mSelectedItems.size() > 0) && mScrollContainer) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - - if (!mKeyboardSelection) - { - setSelection(last_selected, FALSE, TRUE); - mKeyboardSelection = TRUE; - } - - LLFolderViewItem* next = NULL; - if (mask & MASK_SHIFT) - { - // don't shift select down to children of folders (they are implicitly selected through parent) - next = last_selected->getNextOpenNode(FALSE); - if (next) - { - if (next->isSelected()) - { - // shrink selection - changeSelectionFromRoot(last_selected, FALSE); - } - else if (last_selected->getParentFolder() == next->getParentFolder()) - { - // grow selection - changeSelectionFromRoot(next, TRUE); - } - } - } - else - { - next = last_selected->getNextOpenNode(); - if( next ) - { - if (next == last_selected) - { - return FALSE; - } - setSelection( next, FALSE, TRUE ); - } - } - scrollToShowSelection(); - mSearchString.clear(); - handled = TRUE; - } - break; - - case KEY_UP: - if((mSelectedItems.size() > 0) && mScrollContainer) - { - LLFolderViewItem* last_selected = mSelectedItems.back(); - - if (!mKeyboardSelection) - { - setSelection(last_selected, FALSE, TRUE); - mKeyboardSelection = TRUE; - } - - LLFolderViewItem* prev = NULL; - if (mask & MASK_SHIFT) - { - // don't shift select down to children of folders (they are implicitly selected through parent) - prev = last_selected->getPreviousOpenNode(FALSE); - if (prev) - { - if (prev->isSelected()) - { - // shrink selection - changeSelectionFromRoot(last_selected, FALSE); - } - else if (last_selected->getParentFolder() == prev->getParentFolder()) - { - // grow selection - changeSelectionFromRoot(prev, TRUE); - } - } - } - else - { - prev = last_selected->getPreviousOpenNode(); - if( prev ) - { - if (prev == this) - { - return FALSE; - } - setSelection( prev, FALSE, TRUE ); - } - } - scrollToShowSelection(); - mSearchString.clear(); - - handled = TRUE; - } - break; - - case KEY_RIGHT: - if(mSelectedItems.size()) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - last_selected->setOpen( TRUE ); - mSearchString.clear(); - handled = TRUE; - } - break; - - case KEY_LEFT: - if(mSelectedItems.size()) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - LLFolderViewItem* parent_folder = last_selected->getParentFolder(); - if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder()) - { - setSelection(parent_folder, FALSE, TRUE); - } - else - { - last_selected->setOpen( FALSE ); - } - mSearchString.clear(); - scrollToShowSelection(); - handled = TRUE; - } - break; - } - - if (!handled && mParentPanel->hasFocus()) - { - if (key == KEY_BACKSPACE) - { - mSearchTimer.reset(); - if (mSearchString.size()) - { - mSearchString.erase(mSearchString.size() - 1, 1); - } - search(getCurSelectedItem(), mSearchString, FALSE); - handled = TRUE; - } - } - - return handled; -} - - -BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) -{ - if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL - { - return FALSE; - } - - if (uni_char > 0x7f) - { - llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl; - return FALSE; - } - - BOOL handled = FALSE; - if (gFocusMgr.childHasKeyboardFocus(getRoot())) - { - // SL-51858: Key presses are not being passed to the Popup menu. - // A proper fix is non-trivial so instead just close the menu. - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - //do text search - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout")) - { - mSearchString.clear(); - } - mSearchTimer.reset(); - if (mSearchString.size() < 128) - { - mSearchString += uni_char; - } - search(getCurSelectedItem(), mSearchString, FALSE); - - handled = TRUE; - } - - return handled; -} - - -BOOL LLFolderView::canDoDelete() const -{ - if (mSelectedItems.size() == 0) return FALSE; - - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - if (!(*item_it)->getListener()->isItemRemovable()) - { - return FALSE; - } - } - return TRUE; -} - -void LLFolderView::doDelete() -{ - if(mSelectedItems.size() > 0) - { - removeSelectedItems(); - } -} - - -BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - mKeyboardSelection = FALSE; - mSearchString.clear(); - - mParentPanel->setFocus(TRUE); - - return LLView::handleMouseDown( x, y, mask ); -} - -BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward) -{ - // get first selected item - LLFolderViewItem* search_item = first_item; - - // make sure search string is upper case - std::string upper_case_string = search_string; - LLStringUtil::toUpper(upper_case_string); - - // if nothing selected, select first item in folder - if (!search_item) - { - // start from first item - search_item = getNextFromChild(NULL); - } - - // search over all open nodes for first substring match (with wrapping) - BOOL found = FALSE; - LLFolderViewItem* original_search_item = search_item; - do - { - // wrap at end - if (!search_item) - { - if (backward) - { - search_item = getPreviousFromChild(NULL); - } - else - { - search_item = getNextFromChild(NULL); - } - if (!search_item || search_item == original_search_item) - { - break; - } - } - - const std::string current_item_label(search_item->getSearchableLabel()); - S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); - if (!current_item_label.compare(0, search_string_length, upper_case_string)) - { - found = TRUE; - break; - } - if (backward) - { - search_item = search_item->getPreviousOpenNode(); - } - else - { - search_item = search_item->getNextOpenNode(); - } - - } while(search_item != original_search_item); - - - if (found) - { - setSelection(search_item, FALSE, TRUE); - scrollToShowSelection(); - } - - return found; -} - -BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - // skip LLFolderViewFolder::handleDoubleClick() - return LLView::handleDoubleClick( x, y, mask ); -} - -BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - // all user operations move keyboard focus to inventory - // this way, we know when to stop auto-updating a search - mParentPanel->setFocus(TRUE); - - BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL; - S32 count = mSelectedItems.size(); - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if(handled && (count > 0) && menu) - { - if (mCallbackRegistrar) - mCallbackRegistrar->pushScope(); - //menu->empty(); - const LLView::child_list_t *list = menu->getChildList(); - - LLView::child_list_t::const_iterator menu_itor; - for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor) - { - (*menu_itor)->setVisible(TRUE); - (*menu_itor)->setEnabled(TRUE); - } - - // Successively filter out invalid options - selected_items_t::iterator item_itor; - U32 flags = FIRST_SELECTED_ITEM; - for (item_itor = mSelectedItems.begin(); item_itor != mSelectedItems.end(); ++item_itor) - { - (*item_itor)->buildContextMenu(*menu, flags); - flags = 0x0; - } - - menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, menu, x, y); - if (mCallbackRegistrar) - mCallbackRegistrar->popScope(); - } - else - { - if(menu && menu->getVisible()) - { - menu->setVisible(FALSE); - } - setSelection(NULL, FALSE, TRUE); - } - return handled; -} - -BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask ) -{ - return LLView::handleHover( x, y, mask ); -} - -BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - mDragAndDropThisFrame = TRUE; - BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, - accept, tooltip_msg); - - if (handled) - { - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl; - } - - return handled; -} - -BOOL LLFolderView::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - if (mScrollContainer) - { - return mScrollContainer->handleScrollWheel(x, y, clicks); - } - return FALSE; -} - -void LLFolderView::deleteAllChildren() -{ - if(mRenamer == gFocusMgr.getTopCtrl()) - { - gFocusMgr.setTopCtrl(NULL); - } - LLView::deleteViewByHandle(mPopupMenuHandle); - mPopupMenuHandle = LLHandle(); - mRenamer = NULL; - mRenameItem = NULL; - clearSelection(); - LLView::deleteAllChildren(); -} - -void LLFolderView::scrollToShowSelection() -{ - if (mSelectedItems.size()) - { - mNeedsScroll = TRUE; - } -} - -// If the parent is scroll containter, scroll it to make the selection -// is maximally visible. -void LLFolderView::scrollToShowItem(LLFolderViewItem* item) -{ - // don't scroll to items when mouse is being used to scroll/drag and drop - if (gFocusMgr.childHasMouseCapture(mScrollContainer)) - { - mNeedsScroll = FALSE; - return; - } - if(item && mScrollContainer) - { - LLRect local_rect = item->getRect(); - LLRect item_scrolled_rect; // item position relative to display area of scroller - - S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); - S32 label_height = llround(sFont->getLineHeight()); - // when navigating with keyboard, only move top of folders on screen, otherwise show whole folder - S32 max_height_to_show = gFocusMgr.childHasKeyboardFocus(this) ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); - item->localPointToOtherView(item->getIndentation(), llmax(0, local_rect.getHeight() - max_height_to_show), &item_scrolled_rect.mLeft, &item_scrolled_rect.mBottom, mScrollContainer); - item->localPointToOtherView(local_rect.getWidth(), local_rect.getHeight(), &item_scrolled_rect.mRight, &item_scrolled_rect.mTop, mScrollContainer); - - item_scrolled_rect.mRight = llmin(item_scrolled_rect.mLeft + MIN_ITEM_WIDTH_VISIBLE, item_scrolled_rect.mRight); - LLCoordGL scroll_offset(-mScrollContainer->getBorderWidth() - item_scrolled_rect.mLeft, - mScrollContainer->getRect().getHeight() - item_scrolled_rect.mTop - 1); - - S32 max_scroll_offset = getVisibleRect().getHeight() - item_scrolled_rect.getHeight(); - if (item != mLastScrollItem || // if we're scrolling to focus on a new item - // or the item has just appeared on screen and it wasn't onscreen before - (scroll_offset.mY > 0 && scroll_offset.mY < max_scroll_offset && - (mLastScrollOffset.mY < 0 || mLastScrollOffset.mY > max_scroll_offset))) - { - // we now have a position on screen that we want to keep stable - // offset of selection relative to top of visible area - mLastScrollOffset = scroll_offset; - mLastScrollItem = item; - } - - mScrollContainer->scrollToShowRect( item_scrolled_rect, mLastScrollOffset ); - - // after scrolling, store new offset - // in case we don't have room to maintain the original position - LLCoordGL new_item_left_top; - item->localPointToOtherView(item->getIndentation(), item->getRect().getHeight(), &new_item_left_top.mX, &new_item_left_top.mY, mScrollContainer); - mLastScrollOffset.set(-mScrollContainer->getBorderWidth() - new_item_left_top.mX, mScrollContainer->getRect().getHeight() - new_item_left_top.mY - 1); - } -} - -LLRect LLFolderView::getVisibleRect() -{ - S32 visible_height = mScrollContainer->getRect().getHeight(); - S32 visible_width = mScrollContainer->getRect().getWidth(); - LLRect visible_rect; - visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height); - return visible_rect; -} - -BOOL LLFolderView::getShowSelectionContext() -{ - if (mShowSelectionContext) - { - return TRUE; - } - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->getVisible()) - { - return TRUE; - } - return FALSE; -} - -void LLFolderView::setShowSingleSelection(BOOL show) -{ - if (show != mShowSingleSelection) - { - mMultiSelectionFadeTimer.reset(); - mShowSingleSelection = show; - } -} - -void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp) -{ - mItemMap[id] = itemp; -} - -void LLFolderView::removeItemID(const LLUUID& id) -{ - mItemMap.erase(id); -} - -LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id) -{ - if (id.isNull()) - { - return this; - } - - std::map::iterator map_it; - map_it = mItemMap.find(id); - if (map_it != mItemMap.end()) - { - return map_it->second; - } - - return NULL; -} - -bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata) -{ - std::string action = userdata.asString(); - - if ("rename" == action) - { - startRenamingSelectedItem(); - return true; - } - if ("delete" == action) - { - removeSelectedItems(); - return true; - } - - if ("copy" == action) - { - LLInventoryClipboard::instance().reset(); - } - - std::set selected_items; - getSelectionList(selected_items); - - LLMultiPreview* multi_previewp = NULL; - LLMultiProperties* multi_propertiesp = NULL; - - if (("task_open" == action || "open" == action) && selected_items.size() > 1) - { - multi_previewp = new LLMultiPreview(); - gFloaterView->addChild(multi_previewp); - - LLFloater::setFloaterHost(multi_previewp); - - } - else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) - { - multi_propertiesp = new LLMultiProperties(); - gFloaterView->addChild(multi_propertiesp); - - LLFloater::setFloaterHost(multi_propertiesp); - } - - std::set::iterator set_iter; - - for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) - { - LLFolderViewItem* folder_item = getItemByID(*set_iter); - if(!folder_item) continue; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); - if(!bridge) continue; - - bridge->performAction(this, model, action); - } - - LLFloater::setFloaterHost(NULL); - if (multi_previewp) - { - multi_previewp->openFloater(LLSD()); - } - else if (multi_propertiesp) - { - multi_propertiesp->openFloater(LLSD()); - } - - return true; -} - -// Main idle routine -void LLFolderView::doIdle() -{ - LLFastTimer t2(LLFastTimer::FTM_INVENTORY); - - BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters"); - if (debug_filters != getDebugFilters()) - { - mDebugFilters = debug_filters; - arrangeAll(); - } - - mFilter.clearModified(); - BOOL filter_modified_and_active = mCompletedFilterGeneration < mFilter.getCurrentGeneration() && - mFilter.isNotDefault(); - mNeedsAutoSelect = filter_modified_and_active && - !(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture()); - - // filter to determine visiblity before arranging - filterFromRoot(); - - // automatically show matching items, and select first one - // do this every frame until user puts keyboard focus into the inventory window - // signaling the end of the automatic update - // only do this when mNeedsFilter is set, meaning filtered items have - // potentially changed - if (mNeedsAutoSelect) - { - LLFastTimer t3(LLFastTimer::FTM_AUTO_SELECT); - // select new item only if a filtered item not currently selected - LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back(); - if ((!selected_itemp || !selected_itemp->getFiltered()) && !mAutoSelectOverride) - { - // select first filtered item - LLSelectFirstFilteredItem filter; - applyFunctorRecursively(filter); - } - scrollToShowSelection(); - } - - BOOL is_visible = isInVisibleChain(); - - if ( is_visible ) - { - sanitizeSelection(); - if( needsArrange() ) - { - arrangeFromRoot(); - } - } - - if (mSelectedItems.size() && mNeedsScroll) - { - scrollToShowItem(mSelectedItems.back()); - // continue scrolling until animated layout change is done - if (getCompletedFilterGeneration() >= mFilter.getMinRequiredGeneration() && - (!needsArrange() || !is_visible)) - { - mNeedsScroll = FALSE; - } - } - - if (mSignalSelectCallback) - { - //RN: we use keyboard focus as a proxy for user-explicit actions - BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); - mSelectSignal(mSelectedItems, take_keyboard_focus); - } - mSignalSelectCallback = FALSE; -} - - -//static -void LLFolderView::idle(void* user_data) -{ - LLFolderView* self = (LLFolderView*)user_data; - if ( self ) - { // Do the real idle - self->doIdle(); - } -} - -void LLFolderView::dumpSelectionInformation() -{ - llinfos << "LLFolderView::dumpSelectionInformation()" << llendl; - llinfos << "****************************************" << llendl; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - llinfos << " " << (*item_it)->getName() << llendl; - } - llinfos << "****************************************" << llendl; -} - -void LLFolderView::updateRenamerPosition() -{ - if(mRenameItem) - { - S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 + mRenameItem->getIndentation(); - S32 y = llfloor(mRenameItem->getRect().getHeight()-sFont->getLineHeight()-2); - mRenameItem->localPointToScreen( x, y, &x, &y ); - screenPointToLocal( x, y, &x, &y ); - mRenamer->setOrigin( x, y ); - - S32 scroller_height = 0; - S32 scroller_width = gViewerWindow->getWindowWidth(); - BOOL dummy_bool; - if (mScrollContainer) - { - mScrollContainer->calcVisibleSize( &scroller_width, &scroller_height, &dummy_bool, &dummy_bool); - } - - S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_width - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH); - S32 height = llfloor(sFont->getLineHeight() + RENAME_HEIGHT_PAD); - mRenamer->reshape( width, height, TRUE ); - } -} - - -///---------------------------------------------------------------------------- -/// Local function definitions -///---------------------------------------------------------------------------- -bool LLInventorySort::updateSort(U32 order) -{ - if (order != mSortOrder) - { - mSortOrder = order; - mByDate = (order & LLInventoryFilter::SO_DATE); - mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); - mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME); - return true; - } - return false; -} - -bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b) -{ - // We sort by name if we aren't sorting by date - // OR if these are folders and we are sorting folders by name. - bool by_name = (!mByDate - || (mFoldersByName - && (a->getSortGroup() != SG_ITEM))); - - if (a->getSortGroup() != b->getSortGroup()) - { - if (mSystemToTop) - { - // Group order is System Folders, Trash, Normal Folders, Items - return (a->getSortGroup() < b->getSortGroup()); - } - else if (mByDate) - { - // Trash needs to go to the bottom if we are sorting by date - if ( (a->getSortGroup() == SG_TRASH_FOLDER) - || (b->getSortGroup() == SG_TRASH_FOLDER)) - { - return (b->getSortGroup() == SG_TRASH_FOLDER); - } - } - } - - if (by_name) - { - S32 compare = LLStringUtil::compareDict(a->getLabel(), b->getLabel()); - if (0 == compare) - { - return (a->getCreationDate() > b->getCreationDate()); - } - else - { - return (compare < 0); - } - } - else - { - // BUG: This is very very slow. The getCreationDate() is log n in number - // of inventory items. - time_t first_create = a->getCreationDate(); - time_t second_create = b->getCreationDate(); - if (first_create == second_create) - { - return (LLStringUtil::compareDict(a->getLabel(), b->getLabel()) < 0); - } - else - { - return (first_create > second_create); - } - } -} - -//static -void LLFolderView::onRenamerLost( LLFocusableElement* renamer, void* user_data) -{ - LLUICtrl* uictrl = dynamic_cast(renamer); - if (uictrl) - { - uictrl->setVisible(FALSE); - } -} - -void delete_selected_item(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast(user_data); - fv->removeSelectedItems(); - } -} - -void copy_selected_item(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast(user_data); - fv->copy(); - } -} - -void paste_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast(user_data); - fv->paste(); - } -} - -void open_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast(user_data); - fv->openSelectedItems(); - } -} - -void properties_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast(user_data); - fv->propertiesSelectedItems(); - } -} - -///---------------------------------------------------------------------------- -/// Class LLFolderViewEventListener -///---------------------------------------------------------------------------- - -void LLFolderViewEventListener::arrangeAndSet(LLFolderViewItem* focus, - BOOL set_selection, - BOOL take_keyboard_focus) -{ - if(!focus) return; - LLFolderView* root = focus->getRoot(); - focus->getParentFolder()->requestArrange(); - if(set_selection) - { - focus->setSelectionFromRoot(focus, TRUE, take_keyboard_focus); - if(root) - { - root->scrollToShowSelection(); + mSearchString.clear(); } - } -} - - -///---------------------------------------------------------------------------- -/// Class LLInventoryFilter -///---------------------------------------------------------------------------- -LLInventoryFilter::LLInventoryFilter(const std::string& name) : - mName(name), - mModified(FALSE), - mNeedTextRebuild(TRUE) -{ - mFilterOps.mFilterTypes = 0xffffffff; - mFilterOps.mMinDate = time_min(); - mFilterOps.mMaxDate = time_max(); - mFilterOps.mHoursAgo = 0; - mFilterOps.mShowFolderState = SHOW_NON_EMPTY_FOLDERS; - mFilterOps.mPermissions = PERM_NONE; - - mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately - - mSubStringMatchOffset = 0; - mFilterSubString.clear(); - mFilterGeneration = 0; - mMustPassGeneration = S32_MAX; - mMinRequiredGeneration = 0; - mFilterCount = 0; - mNextFilterGeneration = mFilterGeneration + 1; + mSearchTimer.reset(); + if (mSearchString.size() < 128) + { + mSearchString += uni_char; + } + search(getCurSelectedItem(), mSearchString, FALSE); - mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff"); - mFilterBehavior = FILTER_NONE; + handled = TRUE; + } - // copy mFilterOps into mDefaultFilterOps - markDefault(); + return handled; } -LLInventoryFilter::~LLInventoryFilter() -{ -} -BOOL LLInventoryFilter::check(LLFolderViewItem* item) +BOOL LLFolderView::canDoDelete() const { - time_t earliest; + if (mSelectedItems.size() == 0) return FALSE; - earliest = time_corrected() - mFilterOps.mHoursAgo * 3600; - if (mFilterOps.mMinDate > time_min() && mFilterOps.mMinDate < earliest) - { - earliest = mFilterOps.mMinDate; - } - else if (!mFilterOps.mHoursAgo) + for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - earliest = 0; + if (!(*item_it)->getListener()->isItemRemovable()) + { + return FALSE; + } } - LLFolderViewEventListener* listener = item->getListener(); - mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; - BOOL passed = (0x1 << listener->getInventoryType() & mFilterOps.mFilterTypes || listener->getInventoryType() == LLInventoryType::IT_NONE) - && (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos) - && ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions) - && (listener->getCreationDate() >= earliest && listener->getCreationDate() <= mFilterOps.mMaxDate); - return passed; + return TRUE; } -const std::string LLInventoryFilter::getFilterSubString(BOOL trim) +void LLFolderView::doDelete() { - return mFilterSubString; + if(mSelectedItems.size() > 0) + { + removeSelectedItems(); + } } -std::string::size_type LLInventoryFilter::getStringMatchOffset() const -{ - return mSubStringMatchOffset; -} -// has user modified default filter params? -BOOL LLInventoryFilter::isNotDefault() +BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) { - return mFilterOps.mFilterTypes != mDefaultFilterOps.mFilterTypes - || mFilterSubString.size() - || mFilterOps.mPermissions != mDefaultFilterOps.mPermissions - || mFilterOps.mMinDate != mDefaultFilterOps.mMinDate - || mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate - || mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo; -} + mKeyboardSelection = FALSE; + mSearchString.clear(); -BOOL LLInventoryFilter::isActive() -{ - return mFilterOps.mFilterTypes != 0xffffffff - || mFilterSubString.size() - || mFilterOps.mPermissions != PERM_NONE - || mFilterOps.mMinDate != time_min() - || mFilterOps.mMaxDate != time_max() - || mFilterOps.mHoursAgo != 0; -} + mParentPanel->setFocus(TRUE); -BOOL LLInventoryFilter::isModified() -{ - return mModified; + return LLView::handleMouseDown( x, y, mask ); } -BOOL LLInventoryFilter::isModifiedAndClear() +BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward) { - BOOL ret = mModified; - mModified = FALSE; - return ret; -} + // get first selected item + LLFolderViewItem* search_item = first_item; -void LLInventoryFilter::setFilterTypes(U32 types) -{ - if (mFilterOps.mFilterTypes != types) - { - // keep current items only if no type bits getting turned off - BOOL fewer_bits_set = (mFilterOps.mFilterTypes & ~types); - BOOL more_bits_set = (~mFilterOps.mFilterTypes & types); - - mFilterOps.mFilterTypes = types; - if (more_bits_set && fewer_bits_set) - { - // neither less or more restrive, both simultaneously - // so we need to filter from scratch - setModified(FILTER_RESTART); - } - else if (more_bits_set) - { - // target is only one of all requested types so more type bits == less restrictive - setModified(FILTER_LESS_RESTRICTIVE); - } - else if (fewer_bits_set) - { - setModified(FILTER_MORE_RESTRICTIVE); - } + // make sure search string is upper case + std::string upper_case_string = search_string; + LLStringUtil::toUpper(upper_case_string); + // if nothing selected, select first item in folder + if (!search_item) + { + // start from first item + search_item = getNextFromChild(NULL); } -} -void LLInventoryFilter::setFilterSubString(const std::string& string) -{ - if (mFilterSubString != string) + // search over all open nodes for first substring match (with wrapping) + BOOL found = FALSE; + LLFolderViewItem* original_search_item = search_item; + do { - // hitting BACKSPACE, for example - BOOL less_restrictive = mFilterSubString.size() >= string.size() && !mFilterSubString.substr(0, string.size()).compare(string); - // appending new characters - BOOL more_restrictive = mFilterSubString.size() < string.size() && !string.substr(0, mFilterSubString.size()).compare(mFilterSubString); - mFilterSubString = string; - LLStringUtil::toUpper(mFilterSubString); - LLStringUtil::trimHead(mFilterSubString); - - if (less_restrictive) + // wrap at end + if (!search_item) { - setModified(FILTER_LESS_RESTRICTIVE); + if (backward) + { + search_item = getPreviousFromChild(NULL); + } + else + { + search_item = getNextFromChild(NULL); + } + if (!search_item || search_item == original_search_item) + { + break; + } + } + + const std::string current_item_label(search_item->getSearchableLabel()); + S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); + if (!current_item_label.compare(0, search_string_length, upper_case_string)) + { + found = TRUE; + break; } - else if (more_restrictive) + if (backward) { - setModified(FILTER_MORE_RESTRICTIVE); + search_item = search_item->getPreviousOpenNode(); } else { - setModified(FILTER_RESTART); + search_item = search_item->getNextOpenNode(); } + + } while(search_item != original_search_item); + + + if (found) + { + setSelection(search_item, FALSE, TRUE); + scrollToShowSelection(); } + + return found; +} + +BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask ) +{ + // skip LLFolderViewFolder::handleDoubleClick() + return LLView::handleDoubleClick( x, y, mask ); } -void LLInventoryFilter::setFilterPermissions(PermissionMask perms) +BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) { - if (mFilterOps.mPermissions != perms) + // all user operations move keyboard focus to inventory + // this way, we know when to stop auto-updating a search + mParentPanel->setFocus(TRUE); + + BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL; + S32 count = mSelectedItems.size(); + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); + if(handled && (count > 0) && menu) { - // keep current items only if no perm bits getting turned off - BOOL fewer_bits_set = (mFilterOps.mPermissions & ~perms); - BOOL more_bits_set = (~mFilterOps.mPermissions & perms); - mFilterOps.mPermissions = perms; + if (mCallbackRegistrar) + mCallbackRegistrar->pushScope(); + //menu->empty(); + const LLView::child_list_t *list = menu->getChildList(); - if (more_bits_set && fewer_bits_set) + LLView::child_list_t::const_iterator menu_itor; + for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor) { - setModified(FILTER_RESTART); + (*menu_itor)->setVisible(TRUE); + (*menu_itor)->setEnabled(TRUE); } - else if (more_bits_set) + + // Successively filter out invalid options + selected_items_t::iterator item_itor; + U32 flags = FIRST_SELECTED_ITEM; + for (item_itor = mSelectedItems.begin(); item_itor != mSelectedItems.end(); ++item_itor) { - // target must have all requested permission bits, so more bits == more restrictive - setModified(FILTER_MORE_RESTRICTIVE); + (*item_itor)->buildContextMenu(*menu, flags); + flags = 0x0; } - else if (fewer_bits_set) + + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, menu, x, y); + if (mCallbackRegistrar) + mCallbackRegistrar->popScope(); + } + else + { + if(menu && menu->getVisible()) { - setModified(FILTER_LESS_RESTRICTIVE); + menu->setVisible(FALSE); } + setSelection(NULL, FALSE, TRUE); } + return handled; } -void LLInventoryFilter::setDateRange(time_t min_date, time_t max_date) +BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask ) { - mFilterOps.mHoursAgo = 0; - if (mFilterOps.mMinDate != min_date) - { - mFilterOps.mMinDate = min_date; - setModified(); - } - if (mFilterOps.mMaxDate != llmax(mFilterOps.mMinDate, max_date)) + return LLView::handleHover( x, y, mask ); +} + +BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + mDragAndDropThisFrame = TRUE; + BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, + accept, tooltip_msg); + + if (handled) { - mFilterOps.mMaxDate = llmax(mFilterOps.mMinDate, max_date); - setModified(); + lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl; } + + return handled; } -void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) +BOOL LLFolderView::handleScrollWheel(S32 x, S32 y, S32 clicks) { - if (sl && !isSinceLogoff()) + if (mScrollContainer) { - setDateRange(mLastLogoff, time_max()); - setModified(); + return mScrollContainer->handleScrollWheel(x, y, clicks); } - if (!sl && isSinceLogoff()) + return FALSE; +} + +void LLFolderView::deleteAllChildren() +{ + if(mRenamer == gFocusMgr.getTopCtrl()) { - setDateRange(0, time_max()); - setModified(); + gFocusMgr.setTopCtrl(NULL); } + LLView::deleteViewByHandle(mPopupMenuHandle); + mPopupMenuHandle = LLHandle(); + mRenamer = NULL; + mRenameItem = NULL; + clearSelection(); + LLView::deleteAllChildren(); } -BOOL LLInventoryFilter::isSinceLogoff() +void LLFolderView::scrollToShowSelection() { - return (mFilterOps.mMinDate == (time_t)mLastLogoff) && - (mFilterOps.mMaxDate == time_max()); + if (mSelectedItems.size()) + { + mNeedsScroll = TRUE; + } } -void LLInventoryFilter::setHoursAgo(U32 hours) +// If the parent is scroll containter, scroll it to make the selection +// is maximally visible. +void LLFolderView::scrollToShowItem(LLFolderViewItem* item) { - if (mFilterOps.mHoursAgo != hours) + // don't scroll to items when mouse is being used to scroll/drag and drop + if (gFocusMgr.childHasMouseCapture(mScrollContainer)) { - // *NOTE: need to cache last filter time, in case filter goes stale - BOOL less_restrictive = (mFilterOps.mMinDate == time_min() && mFilterOps.mMaxDate == time_max() && hours > mFilterOps.mHoursAgo); - BOOL more_restrictive = (mFilterOps.mMinDate == time_min() && mFilterOps.mMaxDate == time_max() && hours <= mFilterOps.mHoursAgo); - mFilterOps.mHoursAgo = hours; - mFilterOps.mMinDate = time_min(); - mFilterOps.mMaxDate = time_max(); - if (less_restrictive) - { - setModified(FILTER_LESS_RESTRICTIVE); - } - else if (more_restrictive) - { - setModified(FILTER_MORE_RESTRICTIVE); - } - else + mNeedsScroll = FALSE; + return; + } + if(item && mScrollContainer) + { + LLRect local_rect = item->getRect(); + LLRect item_scrolled_rect; // item position relative to display area of scroller + + S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); + S32 label_height = llround(sFont->getLineHeight()); + // when navigating with keyboard, only move top of folders on screen, otherwise show whole folder + S32 max_height_to_show = gFocusMgr.childHasKeyboardFocus(this) ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); + item->localPointToOtherView(item->getIndentation(), llmax(0, local_rect.getHeight() - max_height_to_show), &item_scrolled_rect.mLeft, &item_scrolled_rect.mBottom, mScrollContainer); + item->localPointToOtherView(local_rect.getWidth(), local_rect.getHeight(), &item_scrolled_rect.mRight, &item_scrolled_rect.mTop, mScrollContainer); + + item_scrolled_rect.mRight = llmin(item_scrolled_rect.mLeft + MIN_ITEM_WIDTH_VISIBLE, item_scrolled_rect.mRight); + LLCoordGL scroll_offset(-mScrollContainer->getBorderWidth() - item_scrolled_rect.mLeft, + mScrollContainer->getRect().getHeight() - item_scrolled_rect.mTop - 1); + + S32 max_scroll_offset = getVisibleRect().getHeight() - item_scrolled_rect.getHeight(); + if (item != mLastScrollItem || // if we're scrolling to focus on a new item + // or the item has just appeared on screen and it wasn't onscreen before + (scroll_offset.mY > 0 && scroll_offset.mY < max_scroll_offset && + (mLastScrollOffset.mY < 0 || mLastScrollOffset.mY > max_scroll_offset))) { - setModified(FILTER_RESTART); + // we now have a position on screen that we want to keep stable + // offset of selection relative to top of visible area + mLastScrollOffset = scroll_offset; + mLastScrollItem = item; } + + mScrollContainer->scrollToShowRect( item_scrolled_rect, mLastScrollOffset ); + + // after scrolling, store new offset + // in case we don't have room to maintain the original position + LLCoordGL new_item_left_top; + item->localPointToOtherView(item->getIndentation(), item->getRect().getHeight(), &new_item_left_top.mX, &new_item_left_top.mY, mScrollContainer); + mLastScrollOffset.set(-mScrollContainer->getBorderWidth() - new_item_left_top.mX, mScrollContainer->getRect().getHeight() - new_item_left_top.mY - 1); } } -void LLInventoryFilter::setShowFolderState(EFolderShow state) + +LLRect LLFolderView::getVisibleRect() +{ + S32 visible_height = mScrollContainer->getRect().getHeight(); + S32 visible_width = mScrollContainer->getRect().getWidth(); + LLRect visible_rect; + visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height); + return visible_rect; +} + +BOOL LLFolderView::getShowSelectionContext() { - if (mFilterOps.mShowFolderState != state) + if (mShowSelectionContext) { - mFilterOps.mShowFolderState = state; - if (state == SHOW_NON_EMPTY_FOLDERS) - { - // showing fewer folders than before - setModified(FILTER_MORE_RESTRICTIVE); - } - else if (state == SHOW_ALL_FOLDERS) - { - // showing same folders as before and then some - setModified(FILTER_LESS_RESTRICTIVE); - } - else - { - setModified(); - } + return TRUE; + } + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); + if (menu && menu->getVisible()) + { + return TRUE; } + return FALSE; } -void LLInventoryFilter::setSortOrder(U32 order) +void LLFolderView::setShowSingleSelection(BOOL show) { - if (mOrder != order) + if (show != mShowSingleSelection) { - mOrder = order; - setModified(); + mMultiSelectionFadeTimer.reset(); + mShowSingleSelection = show; } } -void LLInventoryFilter::markDefault() +void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp) { - mDefaultFilterOps = mFilterOps; + mItemMap[id] = itemp; } -void LLInventoryFilter::resetDefault() +void LLFolderView::removeItemID(const LLUUID& id) { - mFilterOps = mDefaultFilterOps; - setModified(); + mItemMap.erase(id); } -void LLInventoryFilter::setModified(EFilterBehavior behavior) +LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id) { - mModified = TRUE; - mNeedTextRebuild = TRUE; - mFilterGeneration = mNextFilterGeneration++; - - if (mFilterBehavior == FILTER_NONE) - { - mFilterBehavior = behavior; - } - else if (mFilterBehavior != behavior) + if (id.isNull()) { - // trying to do both less restrictive and more restrictive filter - // basically means restart from scratch - mFilterBehavior = FILTER_RESTART; + return this; } - if (isNotDefault()) - { - // if not keeping current filter results, update last valid as well - switch(mFilterBehavior) - { - case FILTER_RESTART: - mMustPassGeneration = mFilterGeneration; - mMinRequiredGeneration = mFilterGeneration; - break; - case FILTER_LESS_RESTRICTIVE: - mMustPassGeneration = mFilterGeneration; - break; - case FILTER_MORE_RESTRICTIVE: - mMinRequiredGeneration = mFilterGeneration; - // must have passed either current filter generation (meaningless, as it hasn't been run yet) - // or some older generation, so keep the value - mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration); - break; - default: - llerrs << "Bad filter behavior specified" << llendl; - } - } - else + std::map::iterator map_it; + map_it = mItemMap.find(id); + if (map_it != mItemMap.end()) { - // shortcut disabled filters to show everything immediately - mMinRequiredGeneration = 0; - mMustPassGeneration = S32_MAX; + return map_it->second; } -} -BOOL LLInventoryFilter::isFilterWith(LLInventoryType::EType t) -{ - return mFilterOps.mFilterTypes & (0x01 << t); + return NULL; } -std::string LLInventoryFilter::getFilterText() +bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata) { - if (!mNeedTextRebuild) + std::string action = userdata.asString(); + + if ("rename" == action) { - return mFilterText; + startRenamingSelectedItem(); + return true; } - - mNeedTextRebuild = FALSE; - std::string filtered_types; - std::string not_filtered_types; - BOOL filtered_by_type = FALSE; - BOOL filtered_by_all_types = TRUE; - S32 num_filter_types = 0; - mFilterText.clear(); - - if (isFilterWith(LLInventoryType::IT_ANIMATION)) + if ("delete" == action) { - //filtered_types += " Animations,"; - filtered_types += LLTrans::getString("Animations"); - filtered_by_type = TRUE; - num_filter_types++; + removeSelectedItems(); + return true; } - else - { - //not_filtered_types += " Animations,"; - not_filtered_types += LLTrans::getString("Animations"); - filtered_by_all_types = FALSE; + if ("copy" == action) + { + LLInventoryClipboard::instance().reset(); } - if (isFilterWith(LLInventoryType::IT_CALLINGCARD)) - { - //filtered_types += " Calling Cards,"; - filtered_types += LLTrans::getString("Calling Cards"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Calling Cards,"; - not_filtered_types += LLTrans::getString("Calling Cards"); - filtered_by_all_types = FALSE; - } + std::set selected_items; + getSelectionList(selected_items); - if (isFilterWith(LLInventoryType::IT_WEARABLE)) - { - //filtered_types += " Clothing,"; - filtered_types += LLTrans::getString("Clothing"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Clothing,"; - not_filtered_types += LLTrans::getString("Clothing"); - filtered_by_all_types = FALSE; - } + LLMultiPreview* multi_previewp = NULL; + LLMultiProperties* multi_propertiesp = NULL; - if (isFilterWith(LLInventoryType::IT_GESTURE)) - { - //filtered_types += " Gestures,"; - filtered_types += LLTrans::getString("Gestures"); - filtered_by_type = TRUE; - num_filter_types++; - } - else + if (("task_open" == action || "open" == action) && selected_items.size() > 1) { - //not_filtered_types += " Gestures,"; - not_filtered_types += LLTrans::getString("Gestures"); - filtered_by_all_types = FALSE; - } + multi_previewp = new LLMultiPreview(); + gFloaterView->addChild(multi_previewp); - if (isFilterWith(LLInventoryType::IT_LANDMARK)) - { - //filtered_types += " Landmarks,"; - filtered_types += LLTrans::getString("Landmarks"); - filtered_by_type = TRUE; - num_filter_types++; + LLFloater::setFloaterHost(multi_previewp); + } - else + else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) { - //not_filtered_types += " Landmarks,"; - not_filtered_types += LLTrans::getString("Landmarks"); - filtered_by_all_types = FALSE; - } + multi_propertiesp = new LLMultiProperties(); + gFloaterView->addChild(multi_propertiesp); - if (isFilterWith(LLInventoryType::IT_NOTECARD)) - { - //filtered_types += " Notecards,"; - filtered_types += LLTrans::getString("Notecards"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Notecards,"; - not_filtered_types += LLTrans::getString("Notecards"); - filtered_by_all_types = FALSE; + LLFloater::setFloaterHost(multi_propertiesp); } - - if (isFilterWith(LLInventoryType::IT_OBJECT) && isFilterWith(LLInventoryType::IT_ATTACHMENT)) + + std::set::iterator set_iter; + + for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) { - //filtered_types += " Objects,"; - filtered_types += LLTrans::getString("Objects"); - filtered_by_type = TRUE; - num_filter_types++; + LLFolderViewItem* folder_item = getItemByID(*set_iter); + if(!folder_item) continue; + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); + if(!bridge) continue; + + bridge->performAction(this, model, action); } - else + + LLFloater::setFloaterHost(NULL); + if (multi_previewp) { - //not_filtered_types += " Objects,"; - not_filtered_types += LLTrans::getString("Objects"); - filtered_by_all_types = FALSE; + multi_previewp->openFloater(LLSD()); } - - if (isFilterWith(LLInventoryType::IT_LSL)) + else if (multi_propertiesp) { - //filtered_types += " Scripts,"; - filtered_types += LLTrans::getString("Scripts"); - filtered_by_type = TRUE; - num_filter_types++; + multi_propertiesp->openFloater(LLSD()); } - else + + return true; +} + +// Main idle routine +void LLFolderView::doIdle() +{ + LLFastTimer t2(LLFastTimer::FTM_INVENTORY); + + BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters"); + if (debug_filters != getDebugFilters()) { - //not_filtered_types += " Scripts,"; - not_filtered_types += LLTrans::getString("Scripts"); - filtered_by_all_types = FALSE; + mDebugFilters = debug_filters; + arrangeAll(); } + + mFilter->clearModified(); + BOOL filter_modified_and_active = mCompletedFilterGeneration < mFilter->getCurrentGeneration() && + mFilter->isNotDefault(); + mNeedsAutoSelect = filter_modified_and_active && + !(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture()); - if (isFilterWith(LLInventoryType::IT_SOUND)) + // filter to determine visiblity before arranging + filterFromRoot(); + + // automatically show matching items, and select first one + // do this every frame until user puts keyboard focus into the inventory window + // signaling the end of the automatic update + // only do this when mNeedsFilter is set, meaning filtered items have + // potentially changed + if (mNeedsAutoSelect) { - //filtered_types += " Sounds,"; - filtered_types += LLTrans::getString("Sounds"); - filtered_by_type = TRUE; - num_filter_types++; + LLFastTimer t3(LLFastTimer::FTM_AUTO_SELECT); + // select new item only if a filtered item not currently selected + LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back(); + if ((!selected_itemp || !selected_itemp->getFiltered()) && !mAutoSelectOverride) + { + // select first filtered item + LLSelectFirstFilteredItem filter; + applyFunctorRecursively(filter); + } + scrollToShowSelection(); } - else + + BOOL is_visible = isInVisibleChain(); + + if ( is_visible ) { - //not_filtered_types += " Sounds,"; - not_filtered_types += LLTrans::getString("Sounds"); - filtered_by_all_types = FALSE; + sanitizeSelection(); + if( needsArrange() ) + { + arrangeFromRoot(); + } } - if (isFilterWith(LLInventoryType::IT_TEXTURE)) + if (mSelectedItems.size() && mNeedsScroll) { - //filtered_types += " Textures,"; - filtered_types += LLTrans::getString("Textures"); - filtered_by_type = TRUE; - num_filter_types++; + scrollToShowItem(mSelectedItems.back()); + // continue scrolling until animated layout change is done + if (getCompletedFilterGeneration() >= mFilter->getMinRequiredGeneration() && + (!needsArrange() || !is_visible)) + { + mNeedsScroll = FALSE; + } } - else + + if (mSignalSelectCallback) { - //not_filtered_types += " Textures,"; - not_filtered_types += LLTrans::getString("Textures"); - filtered_by_all_types = FALSE; + //RN: we use keyboard focus as a proxy for user-explicit actions + BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); + mSelectSignal(mSelectedItems, take_keyboard_focus); } + mSignalSelectCallback = FALSE; +} - if (isFilterWith(LLInventoryType::IT_SNAPSHOT)) - { - //filtered_types += " Snapshots,"; - filtered_types += LLTrans::getString("Snapshots"); - filtered_by_type = TRUE; - num_filter_types++; + +//static +void LLFolderView::idle(void* user_data) +{ + LLFolderView* self = (LLFolderView*)user_data; + if ( self ) + { // Do the real idle + self->doIdle(); } - else +} + +void LLFolderView::dumpSelectionInformation() +{ + llinfos << "LLFolderView::dumpSelectionInformation()" << llendl; + llinfos << "****************************************" << llendl; + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - //not_filtered_types += " Snapshots,"; - not_filtered_types += LLTrans::getString("Snapshots"); - filtered_by_all_types = FALSE; + llinfos << " " << (*item_it)->getName() << llendl; } + llinfos << "****************************************" << llendl; +} - if (!gInventory.backgroundFetchActive() && filtered_by_type && !filtered_by_all_types) +void LLFolderView::updateRenamerPosition() +{ + if(mRenameItem) { - mFilterText += " - "; - if (num_filter_types < 5) - { - mFilterText += filtered_types; - } - else + S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 + mRenameItem->getIndentation(); + S32 y = llfloor(mRenameItem->getRect().getHeight()-sFont->getLineHeight()-2); + mRenameItem->localPointToScreen( x, y, &x, &y ); + screenPointToLocal( x, y, &x, &y ); + mRenamer->setOrigin( x, y ); + + S32 scroller_height = 0; + S32 scroller_width = gViewerWindow->getWindowWidth(); + BOOL dummy_bool; + if (mScrollContainer) { - //mFilterText += "No "; - mFilterText += LLTrans::getString("No Filters"); - mFilterText += not_filtered_types; + mScrollContainer->calcVisibleSize( &scroller_width, &scroller_height, &dummy_bool, &dummy_bool); } - // remove the ',' at the end - mFilterText.erase(mFilterText.size() - 1, 1); + + S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_width - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH); + S32 height = llfloor(sFont->getLineHeight() + RENAME_HEIGHT_PAD); + mRenamer->reshape( width, height, TRUE ); } +} + + +///---------------------------------------------------------------------------- +/// Local function definitions +///---------------------------------------------------------------------------- - if (isSinceLogoff()) +//static +void LLFolderView::onRenamerLost( LLFocusableElement* renamer, void* user_data) +{ + LLUICtrl* uictrl = dynamic_cast(renamer); + if (uictrl) { - //mFilterText += " - Since Logoff"; - mFilterText += LLTrans::getString("Since Logoff"); + uictrl->setVisible(FALSE); } - return mFilterText; } -void LLInventoryFilter::toLLSD(LLSD& data) +LLInventoryFilter* LLFolderView::getFilter() { - data["filter_types"] = (LLSD::Integer)getFilterTypes(); - data["min_date"] = (LLSD::Integer)getMinDate(); - data["max_date"] = (LLSD::Integer)getMaxDate(); - data["hours_ago"] = (LLSD::Integer)getHoursAgo(); - data["show_folder_state"] = (LLSD::Integer)getShowFolderState(); - data["permissions"] = (LLSD::Integer)getFilterPermissions(); - data["substring"] = (LLSD::String)getFilterSubString(); - data["sort_order"] = (LLSD::Integer)getSortOrder(); - data["since_logoff"] = (LLSD::Boolean)isSinceLogoff(); + return mFilter; } -void LLInventoryFilter::fromLLSD(LLSD& data) +void LLFolderView::setFilterPermMask( PermissionMask filter_perm_mask ) { - if(data.has("filter_types")) - { - setFilterTypes((U32)data["filter_types"].asInteger()); - } + mFilter->setFilterPermissions(filter_perm_mask); +} - if(data.has("min_date") && data.has("max_date")) - { - setDateRange(data["min_date"].asInteger(), data["max_date"].asInteger()); - } +U32 LLFolderView::getFilterTypes() const +{ + return mFilter->getFilterTypes(); +} - if(data.has("hours_ago")) - { - setHoursAgo((U32)data["hours_ago"].asInteger()); - } +PermissionMask LLFolderView::getFilterPermissions() const +{ + return mFilter->getFilterPermissions(); +} + +// JAMESDEBUG +//LLInventoryFilter::EFolderShow LLFolderView::getShowFolderState() +//{ +// return mFilter->getShowFolderState(); +//} + +BOOL LLFolderView::isFilterModified() +{ + return mFilter->isNotDefault(); +} - if(data.has("show_folder_state")) +BOOL LLFolderView::getAllowMultiSelect() +{ + return mAllowMultiSelect; +} + +void delete_selected_item(void* user_data) +{ + if(user_data) { - setShowFolderState((EFolderShow)data["show_folder_state"].asInteger()); + LLFolderView* fv = reinterpret_cast(user_data); + fv->removeSelectedItems(); } +} - if(data.has("permissions")) +void copy_selected_item(void* user_data) +{ + if(user_data) { - setFilterPermissions((PermissionMask)data["permissions"].asInteger()); + LLFolderView* fv = reinterpret_cast(user_data); + fv->copy(); } +} - if(data.has("substring")) +void paste_items(void* user_data) +{ + if(user_data) { - setFilterSubString(std::string(data["substring"].asString())); + LLFolderView* fv = reinterpret_cast(user_data); + fv->paste(); } +} - if(data.has("sort_order")) +void open_selected_items(void* user_data) +{ + if(user_data) { - setSortOrder((U32)data["sort_order"].asInteger()); + LLFolderView* fv = reinterpret_cast(user_data); + fv->openSelectedItems(); } +} - if(data.has("since_logoff")) +void properties_selected_items(void* user_data) +{ + if(user_data) { - setDateRangeLastLogoff((bool)data["since_logoff"].asBoolean()); + LLFolderView* fv = reinterpret_cast(user_data); + fv->propertiesSelectedItems(); } } -- cgit v1.2.3 From 77f56a3f3db72b2938eadb0868fc7be975dabafa Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Fri, 10 Jul 2009 22:02:26 +0000 Subject: merge QAR-1579: texture-cleanup-1. --- indra/newview/llfolderview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview/llfolderview.cpp') diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index d0a82f283c..6ef011f1de 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -47,8 +47,8 @@ #include "lltooldraganddrop.h" #include "lltrans.h" #include "llui.h" -#include "llviewerimage.h" -#include "llviewerimagelist.h" +#include "llviewertexture.h" +#include "llviewertexturelist.h" #include "llviewerjointattachment.h" #include "llviewermenu.h" #include "lluictrlfactory.h" -- cgit v1.2.3 From 363de6c3bc231183224aed8ba287efe450adb00b Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Thu, 23 Jul 2009 23:41:12 +0000 Subject: svn merge -r 127683:128178 svn+ssh://svn.lindenlab.com/svn/linden/branches/avatar-pipeline/folder-links__merge__viewer-2.0.0.3-r127634 into svn+ssh://svn.lindenlab.com/svn/linden/branches/viewer/viewer-2.0.0-3 For DEV-36425 : Viewer merge for Folder Links and Types [VIEWER] Test plans - EXTERNAL * [ Test against a 1.30 server ] * Test various inventory operations -- move, copy, paste-as-link, etc. on both folders and inventory items. * Test ability to change user-created folder types. Test inventory operations on these folder types. Test plans - INTERNAL * Test against any inventory smoke tests. * See test plan in QAR-1643 for full FolderLinks&Types test plan. --- indra/newview/llfolderview.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'indra/newview/llfolderview.cpp') diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 6ef011f1de..c54eafb67a 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -34,10 +34,12 @@ #include "llfolderview.h" +#include "llagent.h" #include "llcallbacklist.h" #include "llinventorybridge.h" #include "llinventoryclipboard.h" // *TODO: remove this once hack below gone. #include "llinventoryfilter.h" +#include "llfoldertype.h" #include "llfloaterinventory.h"// hacked in for the bonus context menu items. #include "llkeyboard.h" #include "lllineeditor.h" @@ -1094,6 +1096,35 @@ void LLFolderView::propertiesSelectedItems( void ) } } +void LLFolderView::changeType(LLInventoryModel *model, LLAssetType::EType new_folder_type) +{ + LLFolderBridge *folder_bridge = LLFolderBridge::sSelf; + + if (!folder_bridge) return; + LLViewerInventoryCategory *cat = folder_bridge->getCategory(); + if (!cat) return; + + const LLUUID &folder_id = cat->getUUID(); + const LLUUID &parent_id = cat->getParentUUID(); + const std::string &name = cat->getName(); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_UpdateInventoryFolder); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_FolderData); + msg->addUUIDFast(_PREHASH_FolderID, folder_id); + msg->addUUIDFast(_PREHASH_ParentID, parent_id); + msg->addS8Fast(_PREHASH_Type, new_folder_type); + msg->addStringFast(_PREHASH_Name, name); + gAgent.sendReliableMessage(); + + cat->setPreferredType(new_folder_type); + gInventory.addChangedMask(LLInventoryObserver::LABEL, folder_id); + gInventory.updateLinkedObjects(folder_id); +} + void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) { if (mAutoOpenItems.check() == item || mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) @@ -1904,6 +1935,16 @@ bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata) LLInventoryClipboard::instance().reset(); } + static const std::string change_folder_string = "change_folder_type_"; + if (action.length() > change_folder_string.length() && + (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) + { + LLAssetType::EType new_folder_type = LLFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); + changeType(model, new_folder_type); + return true; + } + + std::set selected_items; getSelectionList(selected_items); -- cgit v1.2.3