summaryrefslogtreecommitdiff
path: root/indra/newview/llfolderview.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llfolderview.cpp
Print done when done.
Diffstat (limited to 'indra/newview/llfolderview.cpp')
-rw-r--r--indra/newview/llfolderview.cpp4899
1 files changed, 4899 insertions, 0 deletions
diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp
new file mode 100644
index 0000000000..e9d7808fd2
--- /dev/null
+++ b/indra/newview/llfolderview.cpp
@@ -0,0 +1,4899 @@
+/**
+ * @file llfolderview.cpp
+ * @brief Implementation of the folder view collection of classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfolderview.h"
+
+#include <algorithm>
+
+#include "llviewercontrol.h"
+#include "lldbstrings.h"
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llinventory.h"
+
+#include "llcallbacklist.h"
+#include "llinventoryclipboard.h" // *FIX: remove this!!!!
+#include "llinventoryview.h"// hacked in for the bonus context menu items.
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llresmgr.h"
+#include "llpreview.h"
+#include "llscrollcontainer.h" // hack to allow scrolling
+#include "lltooldraganddrop.h"
+#include "llui.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerjointattachment.h"
+#include "llviewermenu.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llfloaterproperties.h"
+
+//RN: HACK
+#include "llagent.h"
+#include "viewer.h"
+
+///----------------------------------------------------------------------------
+/// 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 = 6;
+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 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;
+
+F32 LLFolderView::sAutoOpenTime = 1.f;
+
+void delete_selected_item(void* user_data);
+void copy_selected_item(void* user_data);
+void open_selected_items(void* user_data);
+void properties_selected_items(void* user_data);
+void paste_items(void* user_data);
+void top_view_lost( LLView* handler );
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewItem
+///----------------------------------------------------------------------------
+
+// statics
+const LLFontGL* LLFolderViewItem::sFont = NULL;
+const LLFontGL* LLFolderViewItem::sSmallFont = NULL;
+LLColor4 LLFolderViewItem::sFgColor;
+LLColor4 LLFolderViewItem::sHighlightBgColor;
+LLColor4 LLFolderViewItem::sHighlightFgColor;
+LLColor4 LLFolderViewItem::sFilterBGColor;
+LLColor4 LLFolderViewItem::sFilterTextColor;
+
+// Default constructor
+LLFolderViewItem::LLFolderViewItem( const LLString& name, LLViewerImage* icon,
+ S32 creation_date,
+ LLFolderView* root,
+ LLFolderViewEventListener* listener ) :
+ LLUICtrl( name, LLRect(0, 0, 0, 0), TRUE, NULL, NULL, FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_RIGHT),
+ mLabel( name ),
+ mLabelWidth(0),
+ mCreationDate(creation_date),
+ mParentFolder( NULL ),
+ mListener( listener ),
+ mIsSelected( FALSE ),
+ mIsCurSelection( FALSE ),
+ mSelectPending(FALSE),
+ mLabelStyle( LLFontGL::NORMAL ),
+ mHasVisibleChildren(FALSE),
+ mIndentation(0),
+ mNumDescendantsSelected(0),
+ mFiltered(FALSE),
+ mLastFilterGeneration(-1),
+ mStringMatchOffset(LLString::npos),
+ mControlLabelRotation(0.f),
+ mRoot( root ),
+ mDragAndDropTarget(FALSE)
+{
+ setIcon(icon);
+ if( !LLFolderViewItem::sFont )
+ {
+ LLFolderViewItem::sFont = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+ }
+
+ if (!LLFolderViewItem::sSmallFont)
+ {
+ LLFolderViewItem::sSmallFont = gResMgr->getRes( LLFONT_SMALL );
+ }
+
+ // HACK: Can't be set above because gSavedSettings might not be constructed.
+ LLFolderViewItem::sFgColor = gColors.getColor( "MenuItemEnabledColor" );
+ LLFolderViewItem::sHighlightBgColor = gColors.getColor( "MenuItemHighlightBgColor" );
+ LLFolderViewItem::sHighlightFgColor = gColors.getColor( "MenuItemHighlightFgColor" );
+ LLFolderViewItem::sFilterBGColor = gColors.getColor( "FilterBackgroundColor" );
+ LLFolderViewItem::sFilterTextColor = gColors.getColor( "FilterTextColor" );
+
+ mArrowImage = gImageList.getImage(LLUUID(gViewerArt.getString("folder_arrow.tga")), MIPMAP_FALSE, TRUE);
+ mBoxImage = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE);
+
+ refresh();
+ setTabStop(FALSE);
+}
+
+// Destroys the object
+LLFolderViewItem::~LLFolderViewItem( void )
+{
+ delete mListener;
+ mListener = NULL;
+ mArrowImage = NULL;
+ mBoxImage = NULL;
+}
+
+LLFolderView* LLFolderViewItem::getRoot()
+{
+ return mRoot;
+}
+
+// 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)
+{
+ 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;
+}
+
+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;
+ }
+
+ return itemp;
+}
+
+BOOL LLFolderViewItem::getFiltered()
+{
+ return mFiltered && mLastFilterGeneration >= mRoot->getFilter()->getMinRequiredGeneration();
+}
+
+BOOL LLFolderViewItem::getFiltered(S32 filter_generation)
+{
+ return mFiltered && mLastFilterGeneration >= filter_generation;
+}
+
+void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation)
+{
+ mFiltered = filtered;
+ mLastFilterGeneration = filter_generation;
+}
+
+void LLFolderViewItem::setIcon(LLViewerImage* icon)
+{
+ mIcon = icon;
+ if (mIcon)
+ {
+ mIcon->setBoostLevel(LLViewerImage::BOOST_UI);
+ }
+}
+
+// refresh information from the listener
+void LLFolderViewItem::refresh()
+{
+ if(mListener)
+ {
+ const char* label = mListener->getDisplayName().c_str();
+ mLabel = label ? label : "";
+ setIcon(mListener->getIcon());
+ U32 creation_date = mListener->getCreationDate();
+ if (mCreationDate != creation_date)
+ {
+ mCreationDate = mListener->getCreationDate();
+ dirtyFilter();
+ }
+ mLabelStyle = mListener->getLabelStyle();
+ mLabelSuffix = mListener->getLabelSuffix();
+
+ LLString searchable_label(mLabel);
+ searchable_label.append(mLabelSuffix);
+ LLString::toUpper(searchable_label);
+
+ if (mSearchableLabel.compare(searchable_label))
+ {
+ mSearchableLabel.assign(searchable_label);
+ dirtyFilter();
+ // some part of label has changed, so overall width has potentially changed
+ if (mParentFolder)
+ {
+ mParentFolder->requestArrange();
+ }
+ }
+
+ S32 label_width = sFont->getWidth(mLabel);
+ if( mLabelSuffix.size() )
+ {
+ label_width += sFont->getWidth( mLabelSuffix );
+ }
+
+ mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + label_width;
+ }
+}
+
+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();
+
+ root->filter(*((LLFolderView*)root)->getFilter());
+}
+
+// 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();
+
+ S32 height = 0;
+ S32 width = 0;
+ root->arrange( &width, &height, 0 );
+}
+
+// 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 open,
+ BOOL take_keyboard_focus)
+{
+ getRoot()->setSelection(selection, open, take_keyboard_focus);
+}
+
+// helper function to change the selection from the root.
+void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection,
+ BOOL selected)
+{
+ getRoot()->changeSelection(selection, selected);
+}
+
+void LLFolderViewItem::extendSelectionFromRoot(LLFolderViewItem* selection)
+{
+ LLDynamicArray<LLFolderViewItem*> selected_items;
+
+ getRoot()->extendSelection(selection, NULL, selected_items);
+}
+
+EWidgetType LLFolderViewItem::getWidgetType() const
+{
+ return WIDGET_TYPE_FOLDER_ITEM;
+}
+
+LLString LLFolderViewItem::getWidgetTag() const
+{
+ return LL_FOLDER_VIEW_ITEM_TAG;
+}
+
+// addToFolder() returns TRUE if it succeeds. FALSE otherwise
+BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
+{
+ if (!folder)
+ {
+ return FALSE;
+ }
+ 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)
+{
+ mIndentation = mParentFolder ? mParentFolder->getIndentation() + LEFT_INDENTATION : 0;
+ *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;
+}
+
+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)
+ {
+ mParentFolder->requestArrange();
+ }
+ }
+
+ setFiltered(filtered, filter.getCurrentGeneration());
+ mStringMatchOffset = filter.getStringMatchOffset();
+ filter.decrementFilterCount();
+
+ if (getRoot()->getDebugFilters())
+ {
+ mStatusText = llformat("%d", mLastFilterGeneration);
+ }
+}
+
+void LLFolderViewItem::dirtyFilter()
+{
+ mLastFilterGeneration = -1;
+ // bubble up dirty flag all the way to root
+ if (getParentFolder())
+ {
+ getParentFolder()->setCompletedFilterGeneration(-1, TRUE);
+ }
+}
+
+// *FIX: 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 open,
+ BOOL take_keyboard_focus)
+{
+ if( selection == this )
+ {
+ mIsSelected = TRUE;
+ if(mListener)
+ {
+ mListener->selectItem();
+ }
+ }
+ else
+ {
+ mIsSelected = FALSE;
+ }
+ return mIsSelected;
+}
+
+BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection,
+ BOOL selected)
+{
+ if(selection == this && mIsSelected != selected)
+ {
+ mIsSelected = selected;
+ if(mListener)
+ {
+ mListener->selectItem();
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderViewItem::recursiveDeselect(BOOL deselect_self)
+{
+ if (mIsSelected && deselect_self)
+ {
+ mIsSelected = FALSE;
+
+ // update ancestors' count of selected descendents
+ LLFolderViewFolder* parent_folder = getParentFolder();
+ while(parent_folder)
+ {
+ parent_folder->mNumDescendantsSelected--;
+ parent_folder = parent_folder->getParentFolder();
+ }
+ }
+}
+
+
+BOOL LLFolderViewItem::isMovable()
+{
+ if( mListener )
+ {
+ return mListener->isItemMovable();
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+BOOL LLFolderViewItem::isRemovable()
+{
+ if( mListener )
+ {
+ return mListener->isItemRemovable();
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+void LLFolderViewItem::destroyView()
+{
+ if (mParentFolder)
+ {
+ // removeView deletes me
+ mParentFolder->removeView(this);
+ }
+}
+
+// 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())
+ {
+ return FALSE;
+ }
+ if(mListener)
+ {
+ return mListener->removeItem();
+ }
+ return TRUE;
+}
+
+// Build an appropriate context menu for the item.
+void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ if(mListener)
+ {
+ mListener->buildContextMenu(menu, flags);
+ }
+}
+
+void LLFolderViewItem::open( void )
+{
+ if( mListener )
+ {
+ mListener->openItem();
+ }
+}
+
+void LLFolderViewItem::preview( void )
+{
+ if (mListener)
+ {
+ mListener->previewItem();
+ }
+}
+
+void LLFolderViewItem::rename(const LLString& new_name)
+{
+ if( !new_name.empty() )
+ {
+ mLabel = new_name.c_str();
+ BOOL is_renamed = TRUE;
+ if( mListener )
+ {
+ is_renamed = mListener->renameItem(new_name);
+ }
+ if(mParentFolder && is_renamed)
+ {
+ mParentFolder->resort(this);
+ }
+ //refresh();
+ }
+}
+
+const LLString& LLFolderViewItem::getSearchableLabel() const
+{
+ return mSearchableLabel;
+}
+
+const LLString& LLFolderViewItem::getName( void ) const
+{
+ if(mListener)
+ {
+ return mListener->getName();
+ }
+ return mLabel;
+}
+
+LLFolderViewFolder* LLFolderViewItem::getParentFolder( void )
+{
+ return mParentFolder;
+}
+
+LLFolderViewEventListener* LLFolderViewItem::getListener( void )
+{
+ return mListener;
+}
+
+// LLView functionality
+BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ if(!mIsSelected)
+ {
+ setSelectionFromRoot(this, FALSE);
+ }
+ make_ui_sound("UISndClick");
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ // No handler needed for focus lost since this class has no
+ // state that depends on it.
+ gViewerWindow->setMouseCapture( this, NULL );
+
+ if (!mIsSelected)
+ {
+ if(mask & MASK_CONTROL)
+ {
+ changeSelectionFromRoot(this, !mIsSelected);
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ extendSelectionFromRoot(this);
+ }
+ else
+ {
+ setSelectionFromRoot(this, FALSE);
+ }
+ make_ui_sound("UISndClick");
+ }
+ else
+ {
+ mSelectPending = TRUE;
+ }
+
+ if( isMovable() )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ gToolDragAndDrop->setDragStart( screen_x, screen_y );
+ }
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
+{
+ if( gViewerWindow->hasMouseCapture( this ) && isMovable() )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ BOOL can_drag = TRUE;
+ if( gToolDragAndDrop->isOverThreshold( screen_x, screen_y ) )
+ {
+ LLFolderView* root = getRoot();
+
+ if(root->getCurSelectedItem())
+ {
+ LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD;
+
+ //FIXME: 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);
+
+ // Release keyboard focus, so that if stuff is dropped into the
+ // world, pressing the delete key won't blow away the inventory
+ // item.
+ gViewerWindow->setKeyboardFocus(NULL, NULL);
+
+ return gToolDragAndDrop->handleHover( x, y, mask );
+ }
+ }
+ }
+
+ if (can_drag)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
+ }
+ return TRUE;
+ }
+ else
+ {
+ getRoot()->setShowSelectionContext(FALSE);
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ // let parent handle this then...
+ return FALSE;
+ }
+}
+
+
+BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ preview();
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (getParent())
+ {
+ return getParent()->handleScrollWheel(x, y, clicks);
+ }
+ return FALSE;
+}
+
+BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ // if mouse hasn't moved since mouse down...
+ if ( pointInView(x, y) && mSelectPending )
+ {
+ //...then select
+ if(mask & MASK_CONTROL)
+ {
+ changeSelectionFromRoot(this, !mIsSelected);
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ extendSelectionFromRoot(this);
+ }
+ else
+ {
+ setSelectionFromRoot(this, FALSE);
+ }
+ }
+
+ mSelectPending = FALSE;
+
+ if( gViewerWindow->hasMouseCapture( this ) )
+ {
+ getRoot()->setShowSelectionContext(FALSE);
+ gViewerWindow->setMouseCapture( NULL, NULL );
+ }
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL accepted = FALSE;
+ BOOL handled = FALSE;
+ if(mListener)
+ {
+ accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data);
+ handled = accepted;
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ }
+ if(mParentFolder && !handled)
+ {
+ handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg);
+ }
+ if (handled)
+ {
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl;
+ }
+
+ return handled;
+}
+
+
+void LLFolderViewItem::draw()
+{
+ if( getVisible() )
+ {
+ 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)
+ {
+ LLGLSTexture gls_texture;
+ if (mArrowImage)
+ {
+ gl_draw_scaled_rotated_image(mIndentation, mRect.getHeight() - ARROW_SIZE - TEXT_PAD,
+ ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, mArrowImage, sFgColor);
+ }
+ }
+
+ F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation);
+
+ // If we have keyboard focus, draw selection filled
+ BOOL show_context = getRoot()->getShowSelectionContext();
+ BOOL filled = show_context || (gFocusMgr.getKeyboardFocus() == getRoot());
+
+ // always render "current" item, only render other selected items if
+ // mShowSingleSelection is FALSE
+ if( mIsSelected )
+ {
+ LLGLSNoTexture gls_no_texture;
+ LLColor4 bg_color = sHighlightBgColor;
+ //const S32 TRAILING_PAD = 5; // It just looks better with this.
+ if (!mIsCurSelection)
+ {
+ // 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);
+ }
+ else
+ {
+ // fading in
+ bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);
+ }
+ }
+
+ gl_rect_2d(
+ 0,
+ mRect.getHeight(),
+ mRect.getWidth() - 2,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
+ bg_color, filled);
+ if (mIsCurSelection)
+ {
+ gl_rect_2d(
+ 0,
+ mRect.getHeight(),
+ mRect.getWidth() - 2,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
+ sHighlightFgColor, FALSE);
+ }
+ if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2)
+ {
+ gl_rect_2d(
+ 0,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2,
+ mRect.getWidth() - 2,
+ 2,
+ sHighlightFgColor, FALSE);
+ if (show_context)
+ {
+ gl_rect_2d(
+ 0,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2,
+ mRect.getWidth() - 2,
+ 2,
+ sHighlightBgColor, TRUE);
+ }
+ }
+ }
+ if (mDragAndDropTarget)
+ {
+ LLGLSNoTexture gls_no_texture;
+ gl_rect_2d(
+ 0,
+ mRect.getHeight(),
+ mRect.getWidth() - 2,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
+ sHighlightBgColor, FALSE);
+
+ if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2)
+ {
+ gl_rect_2d(
+ 0,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2,
+ mRect.getWidth() - 2,
+ 2,
+ sHighlightBgColor, FALSE);
+ }
+ mDragAndDropTarget = FALSE;
+ }
+
+
+ if(mIcon)
+ {
+ gl_draw_image(mIndentation + ARROW_SIZE + TEXT_PAD, mRect.getHeight() - mIcon->getHeight(), mIcon);
+ mIcon->addTextureStats( (F32)(mIcon->getWidth() * mIcon->getHeight()));
+ }
+
+ if (!mLabel.empty())
+ {
+ // highlight filtered text
+ BOOL debug_filters = getRoot()->getDebugFilters();
+ LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor );
+ F32 right_x;
+ F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD;
+
+ if (debug_filters)
+ {
+ if (!getFiltered() && !possibly_has_children)
+ {
+ color.mV[VALPHA] *= 0.5f;
+ }
+
+ 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,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ text_left = right_x;
+ }
+
+ sFont->renderUTF8( mLabel, 0, text_left, y, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ if (!mLabelSuffix.empty())
+ {
+ sFont->renderUTF8( mLabelSuffix, 0, right_x, y, LLColor4(0.75f, 0.85f, 0.85f, 1.f),
+ LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ }
+
+ if (mBoxImage.notNull() && mStringMatchOffset != LLString::npos)
+ {
+ // don't draw backgrounds for zero-length strings
+ S32 filter_string_length = mRoot->getFilterSubString().size();
+ if (filter_string_length > 0)
+ {
+ LLString 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(mRect.getHeight() - sFont->getLineHeight() - 3);
+ S32 top = mRect.getHeight();
+
+ LLViewerImage::bindTexture(mBoxImage);
+ glColor4fv(sFilterBGColor.mV);
+ gl_segmented_rect_2d_tex(left, top, right, bottom, mBoxImage->getWidth(), mBoxImage->getHeight(), 16);
+ F32 match_string_left = text_left + sFont->getWidthF32(combined_string, 0, mStringMatchOffset);
+ F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD;
+ sFont->renderUTF8( combined_string, mStringMatchOffset, match_string_left, y,
+ sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
+ filter_string_length, S32_MAX, &right_x, FALSE );
+ }
+ }
+ }
+
+ if( sDebugRects )
+ {
+ drawDebugRect();
+ }
+ }
+ else if (mStatusText.size())
+ {
+ // just draw status text
+ sFont->renderUTF8( mStatusText, 0, 0, 1, sFgColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewFolder
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFolderViewFolder::LLFolderViewFolder( const LLString& name, LLViewerImage* icon,
+ LLFolderView* root,
+ LLFolderViewEventListener* listener ):
+ LLFolderViewItem( name, icon, 0, root, listener ), // 0 = no create time
+ mSortFunction(sort_item_name),
+ 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)
+{
+ mType = "(folder)";
+
+ //mItems.setInsertBefore( &sort_item_name );
+ //mFolders.setInsertBefore( &folder_insert_before );
+}
+
+// Destroys the object
+LLFolderViewFolder::~LLFolderViewFolder( void )
+{
+ // 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()
+
+ //mItems.reset();
+ //mItems.removeAllNodes();
+ //mFolders.removeAllNodes();
+}
+
+EWidgetType LLFolderViewFolder::getWidgetType() const
+{
+ return WIDGET_TYPE_FOLDER;
+}
+
+LLString LLFolderViewFolder::getWidgetTag() const
+{
+ return LL_FOLDER_VIEW_FOLDER_TAG;
+}
+
+// addToFolder() returns TRUE if it succeeds. FALSE otherwise
+BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
+{
+ if (!folder)
+ {
+ return 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)
+{
+ 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())
+ {
+ // set last arrange generation first, in case children are animating
+ // and need to be arranged again
+ mLastArrangeGeneration = mRoot->getArrangeGeneration();
+ if (mIsOpen)
+ {
+ // Add sizes of children
+ S32 parent_item_height = mRect.getHeight();
+
+ folders_t::iterator fit = mFolders.begin();
+ folders_t::iterator fend = mFolders.end();
+ for(; fit < fend; ++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() );
+ }
+ }
+ items_t::iterator iit = mItems.begin();
+ items_t::iterator iend = mItems.end();
+ for(;iit < iend; ++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() );
+ }
+ }
+ }
+
+ mTargetHeight = target_height;
+ // cache this width so next time we can just return it
+ mLastCalculatedWidth = *width;
+ }
+ else
+ {
+ // just use existing width
+ *width = mLastCalculatedWidth;
+ }
+
+ // 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 (mRect.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 (mRect.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(mRect.getWidth(),llround(mCurHeight));
+
+ // pass current height value back to parent
+ *height = llround(mCurHeight);
+
+ return llround(mTargetHeight);
+}
+
+BOOL LLFolderViewFolder::needsArrange()
+{
+ return mLastArrangeGeneration < mRoot->getArrangeGeneration();
+}
+
+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())
+ {
+ mParentFolder->setCompletedFilterGeneration(generation, TRUE);
+ }
+}
+
+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)
+ {
+ return;
+ }
+
+ // filter folder itself
+ if (getLastFilterGeneration() < filter_generation)
+ {
+ if (getLastFilterGeneration() >= must_pass_generation && // folder has been compared to a valid precursor filter
+ !mFiltered) // and did not pass the filter
+ {
+ // go ahead and flag this folder as done
+ mLastFilterGeneration = filter_generation;
+ }
+ else
+ {
+ // filter self only on first pass through
+ LLFolderViewItem::filter( filter );
+ }
+ }
+
+ if (getRoot()->getDebugFilters())
+ {
+ mStatusText = llformat("%d", mLastFilterGeneration);
+ mStatusText += llformat("(%d)", mCompletedFilterGeneration);
+ mStatusText += llformat("+%d", mMostFilteredDescendantGeneration);
+ }
+
+ // all descendants have been filtered later than must pass generation
+ // but none passed
+ if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation))
+ {
+ // don't traverse children if we've already filtered them since must_pass_generation
+ // and came back with nothing
+ return;
+ }
+
+ // 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)
+ {
+ return;
+ }
+
+ // when applying a filter, matching folders get their contents downloaded first
+ if (getRoot()->isFilterActive() && getFiltered(filter.getMinRequiredGeneration()) && !gInventory.isCategoryComplete(mListener->getUUID()))
+ {
+ gInventory.startBackgroundFetch(mListener->getUUID());
+ }
+
+ // now query children
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ // have we run out of iterations this frame?
+ if (filter.getFilterCount() < 0)
+ {
+ break;
+ }
+
+ // 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)
+ {
+ // track latest generation to pass any child items
+ if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter.getMinRequiredGeneration()))
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ if (mRoot->needsAutoSelect())
+ {
+ (*fit)->setOpenArrangeRecursively(TRUE);
+ }
+ }
+ // just skip it, it has already been filtered
+ continue;
+ }
+
+ // 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))
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ if (mRoot->needsAutoSelect())
+ {
+ (*fit)->setOpenArrangeRecursively(TRUE);
+ }
+ }
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if (filter.getFilterCount() < 0)
+ {
+ break;
+ }
+ if ((*iit)->getLastFilterGeneration() >= filter_generation)
+ {
+ if ((*iit)->getFiltered())
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ }
+ continue;
+ }
+
+ if ((*iit)->getLastFilterGeneration() >= must_pass_generation &&
+ !(*iit)->getFiltered(must_pass_generation))
+ {
+ // 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;
+ }
+
+ (*iit)->filter( filter );
+
+ if ((*iit)->getFiltered(filter.getMinRequiredGeneration()))
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ }
+ }
+
+ // 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)
+{
+ // if this folder is now filtered, but wasn't before
+ // (it just passed)
+ if (filtered && !mFiltered)
+ {
+ // reset current height, because last time we drew it
+ // it might have had more visible items than now
+ mCurHeight = 0.f;
+ }
+
+ LLFolderViewItem::setFiltered(filtered, filter_generation);
+}
+
+void LLFolderViewFolder::dirtyFilter()
+{
+ // we're a folder, so invalidate our completed generation
+ setCompletedFilterGeneration(-1, FALSE);
+ LLFolderViewItem::dirtyFilter();
+}
+
+BOOL LLFolderViewFolder::hasFilteredDescendants()
+{
+ return mMostFilteredDescendantGeneration >= mRoot->getFilter()->getCurrentGeneration();
+}
+
+// Passes selection information on to children and record selection
+// information if necessary.
+BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus)
+{
+ BOOL rv = FALSE;
+ if( selection == this )
+ {
+ 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, open, 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, open, take_keyboard_focus))
+ {
+ rv = TRUE;
+ child_selected = TRUE;
+ mNumDescendantsSelected++;
+ }
+ }
+ if(open && child_selected)
+ {
+ setOpenArrangeRecursively(TRUE);
+ }
+ return rv;
+}
+
+// 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 rv = FALSE;
+ if(selection == this)
+ {
+ mIsSelected = selected;
+ if(mListener && selected)
+ {
+ mListener->selectItem();
+ }
+ rv = TRUE;
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if((*fit)->changeSelection(selection, selected))
+ {
+ if (selected)
+ {
+ mNumDescendantsSelected++;
+ }
+ else
+ {
+ mNumDescendantsSelected--;
+ }
+ rv = TRUE;
+ }
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if((*iit)->changeSelection(selection, selected))
+ {
+ if (selected)
+ {
+ mNumDescendantsSelected++;
+ }
+ else
+ {
+ mNumDescendantsSelected--;
+ }
+ rv = TRUE;
+ }
+ }
+ return rv;
+}
+
+S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& selected_items)
+{
+ S32 num_selected = 0;
+
+ // pass on to child folders first
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ num_selected += (*fit)->extendSelection(selection, last_selected, selected_items);
+ mNumDescendantsSelected += num_selected;
+ }
+
+ // handle selection of our immediate children...
+ BOOL reverse_select = FALSE;
+ BOOL found_last_selected = FALSE;
+ BOOL found_selection = FALSE;
+ LLDynamicArray<LLFolderViewItem*> items_to_select;
+ LLFolderViewItem* item;
+
+ //...folders first...
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ item = (*fit);
+ 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)
+ {
+ // deselect currently selected items so they can be pushed back on queue
+ if (item->isSelected())
+ {
+ item->changeSelection(item, FALSE);
+ }
+ items_to_select.put(item);
+ }
+
+ if (found_selection && found_last_selected)
+ {
+ break;
+ }
+ }
+
+ if (!(found_selection && found_last_selected))
+ {
+ //,,,then items
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ 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)
+ {
+ // deselect currently selected items so they can be pushed back on queue
+ if (item->isSelected())
+ {
+ item->changeSelection(item, FALSE);
+ }
+ items_to_select.put(item);
+ }
+
+ if (found_selection && found_last_selected)
+ {
+ break;
+ }
+ }
+ }
+
+ if (found_last_selected && found_selection)
+ {
+ // 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++)
+ {
+ LLFolderViewItem* item = items_to_select[index];
+ if (item->changeSelection(item, TRUE))
+ {
+ selected_items.put(item);
+ mNumDescendantsSelected++;
+ num_selected++;
+ }
+ }
+ }
+ else if (found_selection)
+ {
+ // last selection was not in this folder....go ahead and select just the new item
+ if (selection->changeSelection(selection, TRUE))
+ {
+ selected_items.put(selection);
+ mNumDescendantsSelected++;
+ num_selected++;
+ }
+ }
+
+ return num_selected;
+}
+
+void LLFolderViewFolder::recursiveDeselect(BOOL deselect_self)
+{
+ // make sure we don't have negative values
+ llassert(mNumDescendantsSelected >= 0);
+
+ if (mIsSelected && deselect_self)
+ {
+ mIsSelected = FALSE;
+
+ // update ancestors' count of selected descendents
+ LLFolderViewFolder* parent_folder = getParentFolder();
+ while(parent_folder)
+ {
+ parent_folder->mNumDescendantsSelected--;
+ parent_folder = parent_folder->getParentFolder();
+ }
+ }
+
+ if (0 == mNumDescendantsSelected)
+ {
+ return;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ LLFolderViewItem* item = (*iit);
+ item->recursiveDeselect(TRUE);
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ LLFolderViewFolder* folder = (*fit);
+ folder->recursiveDeselect(TRUE);
+ }
+
+}
+
+void LLFolderViewFolder::destroyView()
+{
+ 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();
+ }
+
+ mFolders.clear();
+
+ deleteAllChildren();
+
+ if (mParentFolder)
+ {
+ mParentFolder->removeView(this);
+ }
+}
+
+// remove the specified item (and any children) if possible. Return
+// TRUE if the item was deleted.
+BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item)
+{
+ if(item->remove())
+ {
+ removeView(item);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// simply remove the view (and any children) Don't bother telling the
+// listeners.
+void LLFolderViewFolder::removeView(LLFolderViewItem* item)
+{
+ if (!item)
+ {
+ return;
+ }
+ // deselect without traversing hierarchy
+ item->recursiveDeselect(TRUE);
+ getRoot()->removeFromSelectionList(item);
+ extractItem(item);
+ delete item;
+}
+
+// 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<LLFolderViewFolder*>(item);
+ folders_t::iterator ft;
+ ft = std::find(mFolders.begin(), mFolders.end(), f);
+ if(ft != mFolders.end())
+ {
+ 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);
+}
+
+// This function is called by a child that needs to be resorted.
+// This is only called for renaming an object because it won't work for date
+void LLFolderViewFolder::resort(LLFolderViewItem* item)
+{
+ std::sort(mItems.begin(), mItems.end(), *mSortFunction);
+ std::sort(mFolders.begin(), mFolders.end(), *mSortFunction);
+ //if(mItems.removeData(item))
+ //{
+ // mItems.addDataSorted(item);
+ //}
+ //else
+ //{
+ // // 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<LLFolderViewFolder*>(item);
+ // if(mFolders.removeData(f))
+ // {
+ // mFolders.addDataSorted(f);
+ // }
+ //}
+}
+
+bool LLFolderViewFolder::isTrash()
+{
+ if (mAmTrash == LLFolderViewFolder::UNKNOWN)
+ {
+ mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH;
+ }
+ return mAmTrash == LLFolderViewFolder::TRASH;
+}
+
+void LLFolderViewFolder::sortBy(U32 order)
+{
+ BOOL sort_order_changed = FALSE;
+ if (!(order & LLInventoryFilter::SO_DATE))
+ {
+ if (mSortFunction != sort_item_name)
+ {
+ mSortFunction = sort_item_name;
+ sort_order_changed = TRUE;
+ }
+ }
+ else
+ {
+ if (mSortFunction != sort_item_date)
+ {
+ mSortFunction = sort_item_date;
+ sort_order_changed = TRUE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->sortBy(order);
+ }
+ if (order & LLInventoryFilter::SO_FOLDERS_BY_NAME)
+ {
+ // sort folders by name if always by name
+ std::sort(mFolders.begin(), mFolders.end(), sort_item_name);
+ }
+ else
+ {
+ // sort folders by the default sort ordering
+ std::sort(mFolders.begin(), mFolders.end(), *mSortFunction);
+
+ // however, if we are at the root of the inventory and we are sorting by date
+ if (mListener->getUUID() == gAgent.getInventoryRootID() && order & LLInventoryFilter::SO_DATE)
+ {
+ // pull the trash folder and stick it on the end of the list
+ LLFolderViewFolder *t = NULL;
+ for (folders_t::iterator fit = mFolders.begin();
+ fit != mFolders.end(); ++fit)
+ {
+ if ((*fit)->isTrash())
+ {
+ t = *fit;
+ mFolders.erase(fit);
+ break;
+ }
+ }
+ if (t)
+ {
+ mFolders.push_back(t);
+ }
+ }
+ }
+ if (sort_order_changed)
+ {
+ std::sort(mItems.begin(), mItems.end(), *mSortFunction);
+ }
+
+ if (order & LLInventoryFilter::SO_DATE)
+ {
+ U32 latest = 0;
+
+ if (!mItems.empty())
+ {
+ LLFolderViewItem* item = *(mItems.begin());
+ latest = item->getCreationDate();
+ }
+
+ if (!mFolders.empty())
+ {
+ LLFolderViewFolder* folder = *(mFolders.begin());
+ if (folder->getCreationDate() > latest)
+ {
+ latest = folder->getCreationDate();
+ }
+ }
+ mSubtreeCreationDate = latest;
+ }
+}
+
+void LLFolderViewFolder::setItemSortFunction(sort_order_f ordering)
+{
+ mSortFunction = ordering;
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->setItemSortFunction(ordering);
+ }
+
+ std::sort(mFolders.begin(), mFolders.end(), *mSortFunction);
+ std::sort(mItems.begin(), mItems.end(), *mSortFunction);
+}
+
+BOOL LLFolderViewFolder::isMovable()
+{
+ if( mListener )
+ {
+ if( !(mListener->isItemMovable()) )
+ {
+ return FALSE;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if(!(*iit)->isMovable())
+ {
+ return FALSE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if(!(*fit)->isMovable())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLFolderViewFolder::isRemovable()
+{
+ if( mListener )
+ {
+ if( !(mListener->isItemRemovable()) )
+ {
+ return FALSE;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if(!(*iit)->isRemovable())
+ {
+ return FALSE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if(!(*fit)->isRemovable())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// this is an internal method used for adding items to folders.
+BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item)
+{
+ items_t::iterator it = std::lower_bound(
+ mItems.begin(),
+ mItems.end(),
+ item,
+ mSortFunction);
+ mItems.insert(it,item);
+ item->setRect(LLRect(0, 0, mRect.getWidth(), 0));
+ item->setVisible(FALSE);
+ addChild( item );
+ item->dirtyFilter();
+ requestArrange();
+ return TRUE;
+}
+
+// this is an internal method used for adding items to folders.
+BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder)
+{
+ folders_t::iterator it = std::lower_bound(
+ mFolders.begin(),
+ mFolders.end(),
+ folder,
+ mSortFunction);
+ mFolders.insert(it,folder);
+ folder->setOrigin(0, 0);
+ folder->reshape(mRect.getWidth(), 0);
+ folder->setVisible(FALSE);
+ addChild( folder );
+ folder->dirtyFilter();
+ requestArrange();
+ return TRUE;
+}
+
+void LLFolderViewFolder::requestArrange()
+{
+ mLastArrangeGeneration = -1;
+ // flag all items up to root
+ if (mParentFolder)
+ {
+ mParentFolder->requestArrange();
+ }
+}
+
+void LLFolderViewFolder::toggleOpen()
+{
+ setOpen(!mIsOpen);
+}
+
+// Force a folder open or closed
+void LLFolderViewFolder::setOpen(BOOL open)
+{
+ setOpenArrangeRecursively(open);
+}
+
+void LLFolderViewFolder::setOpenArrangeRecursively(BOOL open, ERecurseType recurse)
+{
+ BOOL was_open = mIsOpen;
+ mIsOpen = open;
+ if(!was_open && open)
+ {
+ if(mListener)
+ {
+ mListener->openItem();
+ }
+ }
+ if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN)
+ {
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->setOpenArrangeRecursively(open, RECURSE_DOWN);
+ }
+ }
+ if (mParentFolder && (recurse == RECURSE_UP || recurse == RECURSE_UP_DOWN))
+ {
+ mParentFolder->setOpenArrangeRecursively(open, RECURSE_UP);
+ }
+
+ if (was_open != mIsOpen)
+ {
+ requestArrange();
+ }
+}
+
+BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask,
+ BOOL drop,
+ EDragAndDropType c_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& 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::open( 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,
+ LLString& 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;
+ if( getVisible() )
+ {
+ // 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 > mRect.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 )
+{
+ if (!getVisible())
+ {
+ return FALSE;
+ }
+ BOOL rv = false;
+ if( mIsOpen )
+ {
+ rv = childrenHandleDoubleClick( x, y, mask ) != NULL;
+ }
+ if( !rv )
+ {
+ 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();
+ }
+ return TRUE;
+ }
+ return rv;
+}
+
+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));
+ }
+
+ LLFolderViewItem::draw();
+ if( mIsOpen )
+ {
+ LLView::draw();
+ }
+
+// if (mExpanderHighlighted)
+// {
+// gl_rect_2d(mIndentation - TEXT_PAD, llfloor(mRect.getHeight() - TEXT_PAD), mIndentation + sFont->getWidth(mControlLabel) + TEXT_PAD, llfloor(mRect.getHeight() - sFont->getLineHeight() - TEXT_PAD), sFgColor, FALSE);
+// //sFont->renderUTF8( mControlLabel, 0, mIndentation, llfloor(mRect.getHeight() - sFont->getLineHeight() - TEXT_PAD), sFgColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, S32_MAX, S32_MAX, NULL, FALSE );
+// }
+ mExpanderHighlighted = FALSE;
+}
+
+U32 LLFolderViewFolder::getCreationDate() const
+{
+ return llmax<U32>(mCreationDate, mSubtreeCreationDate);
+}
+
+
+// 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(sort_order_f ordering)
+ : mSortFunction(ordering) {}
+ virtual ~LLSetItemSortFunction() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+
+ sort_order_f mSortFunction;
+};
+
+
+// Set the sort order.
+void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder)
+{
+ folder->setItemSortFunction(mSortFunction);
+}
+
+// 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 LLString& name, LLViewerImage* root_folder_icon,
+ const LLRect& rect, const LLUUID& source_id, LLView *parent_view ) :
+#if LL_WINDOWS
+#pragma warning( push )
+#pragma warning( disable : 4355 ) // warning C4355: 'this' : used in base member initializer list
+#endif
+ LLFolderViewFolder( name, root_folder_icon, this, NULL ),
+#if LL_WINDOWS
+#pragma warning( pop )
+#endif
+ mScrollContainer( NULL ),
+ mPopupMenuHandle( LLViewHandle::sDeadHandle ),
+ mAllowMultiSelect(TRUE),
+ mShowFolderHierarchy(FALSE),
+ mSourceID(source_id),
+ mRenameItem( NULL ),
+ mNeedsScroll( FALSE ),
+ mLastScrollItem( NULL ),
+ mNeedsAutoSelect( FALSE ),
+ mAutoSelectOverride(FALSE),
+ mDebugFilters(FALSE),
+ mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately
+ mFilter(name),
+ mShowSelectionContext(FALSE),
+ mShowSingleSelection(FALSE),
+ mArrangeGeneration(0),
+ mSelectCallback(NULL),
+ mMinWidth(0),
+ mDragAndDropThisFrame(FALSE)
+{
+ LLRect new_rect(rect.mLeft, rect.mBottom + mRect.getHeight(), rect.mLeft + mRect.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 = LLString::null;
+
+ mRenamer = new LLLineEditor("ren", mRect, "", sFont,
+ DB_INV_ITEM_NAME_STR_LEN,
+ &LLFolderView::commitRename,
+ NULL,
+ NULL,
+ this,
+ &LLLineEditor::prevalidatePrintableNotPipe,
+ LLViewBorder::BEVEL_NONE,
+ LLViewBorder::STYLE_LINE,
+ 2);
+ mRenamer->setWriteableBgColor(LLColor4::white);
+ // Escape is handled by reverting the rename, not commiting it (default behavior)
+ mRenamer->setCommitOnFocusLost(TRUE);
+ mRenamer->setVisible(FALSE);
+ addChild(mRenamer);
+
+ // make the popup menu available
+ LLMenuGL* menu = gUICtrlFactory->buildMenu("menu_inventory.xml", parent_view);
+ if (!menu)
+ {
+ menu = new LLMenuGL("");
+ }
+ menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor"));
+ menu->setVisible(FALSE);
+ mPopupMenuHandle = menu->mViewHandle;
+
+ setTabStop(TRUE);
+}
+
+// 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;
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ mAutoOpenItems.removeAllNodes();
+ gIdleCallbacks.deleteFunction(idle, this);
+
+ LLView::deleteViewByHandle(mPopupMenuHandle);
+
+ if(gViewerWindow->hasTopView(mRenamer))
+ {
+ gViewerWindow->setTopView(NULL, NULL);
+ }
+
+ mAutoOpenItems.removeAllNodes();
+ clearSelection();
+ mItems.clear();
+ mFolders.clear();
+
+ mItemMap.clear();
+}
+
+EWidgetType LLFolderView::getWidgetType() const
+{
+ return WIDGET_TYPE_FOLDER_VIEW;
+}
+
+LLString LLFolderView::getWidgetTag() const
+{
+ return LL_FOLDER_VIEW_TAG;
+}
+
+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(mRect.getWidth(), 0);
+ folder->setVisible(FALSE);
+ addChild( folder );
+ folder->dirtyFilter();
+ return TRUE;
+}
+
+void LLFolderView::closeAllFolders()
+{
+ // Close all the folders
+ setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN);
+}
+
+void LLFolderView::openFolder(const LLString& foldername)
+{
+ LLFolderViewFolder* inv = (LLFolderViewFolder*)getChildByName(foldername);
+ if (inv)
+ {
+ setSelection(inv, FALSE, FALSE);
+ inv->setOpen(TRUE);
+ }
+}
+
+void LLFolderView::setOpenArrangeRecursively(BOOL open, ERecurseType recurse)
+{
+ // call base class to do proper recursion
+ LLFolderViewFolder::setOpenArrangeRecursively(open, 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 = mRoot->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 = mRect.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 );
+ }
+
+ mTargetHeight = (F32)target_height;
+ return llround(mTargetHeight);
+}
+
+const LLString 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);
+}
+
+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 open,
+ BOOL take_keyboard_focus)
+{
+ if( selection == this )
+ {
+ return FALSE;
+ }
+
+ if( selection && take_keyboard_focus)
+ {
+ 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, open, take_keyboard_focus);
+ if(open)
+ {
+ selection->getParentFolder()->requestArrange();
+ }
+
+ llassert(mSelectedItems.size() <= 1);
+
+ mSelectionChanged = TRUE;
+
+ return rv;
+}
+
+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 (on_list && mSelectedItems.size() == 1)
+ {
+ // we are trying to select/deselect the only selected item
+ return FALSE;
+ }
+
+ if(selected && !on_list)
+ {
+ mNumDescendantsSelected++;
+ addToSelectionList(selection);
+ }
+ if(!selected && on_list)
+ {
+ mNumDescendantsSelected--;
+ removeFromSelectionList(selection);
+ }
+
+ rv = LLFolderViewFolder::changeSelection(selection, selected);
+
+ mSelectionChanged = TRUE;
+
+ return rv;
+}
+
+S32 LLFolderView::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& 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++;
+ }
+
+ mSelectionChanged = TRUE;
+ return rv;
+}
+
+void LLFolderView::sanitizeSelection()
+{
+ std::vector<LLFolderViewItem*> items_to_remove;
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
+ {
+ LLFolderViewItem* item = *item_iter;
+
+ BOOL visible = item->getVisible();
+ LLFolderViewFolder* parent_folder = item->getParentFolder();
+ while(visible && parent_folder)
+ {
+ visible = visible && parent_folder->isOpen() && parent_folder->getVisible();
+ parent_folder = parent_folder->getParentFolder();
+ }
+ if (!visible || item->getNumSelectedDescendants() > 0)
+ {
+ // only deselect self if not visible
+ // check to see if item failed the filter but was checked against most recent generation
+ if ((!item->getFiltered() && item->getLastFilterGeneration() >= getFilter()->getMinRequiredGeneration())
+ || (item->getParentFolder() && !item->getParentFolder()->isOpen()))
+ {
+ item->recursiveDeselect(TRUE);
+ items_to_remove.push_back(item);
+ }
+ else
+ {
+ item->recursiveDeselect(FALSE);
+ }
+
+ 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;
+ LLFolderViewFolder* parent_folder = other_item->getParentFolder();
+ while (parent_folder)
+ {
+ if (parent_folder == item)
+ {
+ // this is a descendent of the current folder, remove from list
+ items_to_remove.push_back(other_item);
+ break;
+ }
+ parent_folder = parent_folder->getParentFolder();
+ }
+ }
+ }
+ }
+
+ std::vector<LLFolderViewItem*>::iterator item_it;
+ for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it )
+ {
+ removeFromSelectionList(*item_it);
+ }
+}
+
+void LLFolderView::clearSelection()
+{
+ if (mSelectedItems.size() > 0)
+ {
+ recursiveDeselect(FALSE);
+ mSelectedItems.clear();
+ }
+}
+
+BOOL LLFolderView::getSelectionList(std::set<LLUUID> &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<EDragAndDropType> types;
+ std::vector<LLUUID> 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);
+ }
+
+ gToolDragAndDrop->beginMultiDrag(types, cargo_ids, source, mSourceID);
+ }
+ return can_drag;
+}
+
+void LLFolderView::commitRename( LLUICtrl* renamer, void* user_data )
+{
+ LLFolderView* root = reinterpret_cast<LLFolderView*>(user_data);
+ if( root )
+ {
+ root->finishRenamingItem();
+ }
+}
+
+void LLFolderView::draw()
+{
+ if (mDebugFilters)
+ {
+ LLString 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,
+ mRect.getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f),
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, 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();
+ }
+ if(gViewerWindow->hasKeyboardFocus(this) && !getVisible())
+ {
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+ }
+
+ // while dragging, update selection rendering to reflect single/multi drag status
+ if (gToolDragAndDrop->hasMouseCapture())
+ {
+ EAcceptance last_accept = gToolDragAndDrop->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)
+ {
+ setStatusText("");
+ }
+ else
+ {
+ if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter.getMinRequiredGeneration())
+ {
+ setStatusText("Searching...");
+ sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+ else
+ {
+ setStatusText("No matching items found in inventory.");
+ sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+ }
+
+ LLFolderViewFolder::draw();
+
+ mDragAndDropThisFrame = FALSE;
+}
+
+void LLFolderView::finishRenamingItem( void )
+{
+ if(!mRenamer)
+ {
+ return;
+ }
+ if( mRenameItem )
+ {
+ mRenameItem->rename( mRenamer->getText().c_str() );
+ }
+
+ mRenamer->setCommitOnFocusLost( FALSE );
+ mRenamer->setFocus( FALSE );
+ mRenamer->setVisible( FALSE );
+ mRenamer->setCommitOnFocusLost( TRUE );
+ gViewerWindow->setTopView( NULL, 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::revertRenamingItem( void )
+{
+ mRenamer->setCommitOnFocusLost( FALSE );
+ mRenamer->setFocus( FALSE );
+ mRenamer->setVisible( FALSE );
+ mRenamer->setCommitOnFocusLost( TRUE );
+ gViewerWindow->setTopView( NULL, NULL );
+
+ if( mRenameItem )
+ {
+ setSelectionFromRoot( mRenameItem, TRUE );
+ mRenameItem = NULL;
+ }
+}
+
+void LLFolderView::removeSelectedItems( void )
+{
+ if(getVisible() && mEnabled)
+ {
+ // 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.
+ LLDynamicArray<LLFolderViewItem*> items;
+ S32 count = mSelectedItems.size();
+ if(count == 0) return;
+ LLFolderViewItem* item = NULL;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ item = *item_it;
+ if(item->isRemovable())
+ {
+ items.put(item);
+ }
+ else
+ {
+ llinfos << "Cannot delete " << item->getName() << llendl;
+ return;
+ }
+ }
+
+ // iterate through the new container.
+ count = items.count();
+ LLUUID new_selection_id;
+ if(count == 1)
+ {
+ LLFolderViewItem* item_to_delete = items.get(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 (new_selection)
+ {
+ setSelectionFromRoot(new_selection, new_selection->isOpen(), gViewerWindow->childHasKeyboardFocus(this));
+ }
+ else
+ {
+ setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this));
+ }
+
+ if(parent)
+ {
+ parent->removeItem(item_to_delete);
+ }
+ arrangeAll();
+ }
+ else if (count > 1)
+ {
+ LLDynamicArray<LLFolderViewEventListener*> listeners;
+ LLFolderViewEventListener* listener;
+ LLFolderViewItem* last_item = items.get(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(), gViewerWindow->childHasKeyboardFocus(this));
+ }
+ else
+ {
+ setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this));
+ }
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ listener = items.get(i)->getListener();
+ if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::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() && mEnabled)
+ {
+ if (mSelectedItems.size() == 1)
+ {
+ mSelectedItems.front()->open();
+ }
+ else
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+
+ LLMultiPreview* multi_previewp = new LLMultiPreview(LLRect(left, top, left + 300, top - 100));
+
+ LLFloater::setFloaterHost(multi_previewp);
+
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ (*item_it)->open();
+ }
+
+ LLFloater::setFloaterHost(NULL);
+ multi_previewp->open();
+ }
+ }
+}
+
+void LLFolderView::propertiesSelectedItems( void )
+{
+ if(getVisible() && mEnabled)
+ {
+ if (mSelectedItems.size() == 1)
+ {
+ LLFolderViewItem* folder_item = mSelectedItems.front();
+ if(!folder_item) return;
+ folder_item->getListener()->showProperties();
+ }
+ else
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+
+ LLMultiProperties* multi_propertiesp = new LLMultiProperties(LLRect(left, top, left + 100, top - 100));
+
+ 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->open();
+ }
+ }
+}
+
+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()
+{
+ if (!(getVisible() && mEnabled && (mSelectedItems.size() > 0)))
+ {
+ return FALSE;
+ }
+
+ selected_items_t::iterator selected_it;
+ for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
+ {
+ LLFolderViewItem* item = *selected_it;
+ if (!item->getListener()->isItemCopyable())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// copy selected item
+void LLFolderView::copy()
+{
+ // *FIX: total hack to clear the inventory clipboard
+ LLInventoryClipboard::instance().reset();
+ S32 count = mSelectedItems.size();
+ if(getVisible() && mEnabled && (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()
+{
+ return FALSE;
+}
+
+void LLFolderView::cut()
+{
+ // implement Windows-style cut-and-leave
+}
+
+BOOL LLFolderView::canPaste()
+{
+ if (mSelectedItems.empty())
+ {
+ return FALSE;
+ }
+
+ if(getVisible() && mEnabled)
+ {
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ //FIXME: only check folders and parent folders of items
+ LLFolderViewItem* item = (*item_it);
+ LLFolderViewEventListener* listener = item->getListener();
+ if(!listener || !listener->isClipboardPasteable())
+ {
+ 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() && mEnabled)
+ {
+ // find set of unique folders to paste into
+ std::set<LLFolderViewItem*> 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<LLFolderViewItem*>::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() && mEnabled && (count == 1) && item && item->getListener() &&
+ item->getListener()->isItemRenameable())
+ {
+ mRenameItem = item;
+
+ S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 + item->getIndentation();
+ S32 y = llfloor(item->getRect().getHeight()-sFont->getLineHeight()-2);
+ item->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(item->getRect().getWidth() - x, scroller_width - x - mRect.mLeft), MINIMUM_RENAMER_WIDTH);
+ S32 height = llfloor(sFont->getLineHeight() + RENAME_HEIGHT_PAD);
+ mRenamer->reshape( width, height, TRUE );
+
+ mRenamer->setText(item->getName());
+ mRenamer->selectAll();
+ mRenamer->setVisible( TRUE );
+ // set focus will fail unless item is visible
+ mRenamer->setFocus( TRUE );
+ gViewerWindow->setTopView( mRenamer, top_view_lost );
+ }
+}
+
+void LLFolderView::setFocus(BOOL focus)
+{
+ if (focus)
+ {
+ // select "My Inventory" if nothing selected
+ if (!getCurSelectedItem())
+ {
+ LLFolderViewItem* itemp = getItemByID(gAgent.getInventoryRootID());
+ if (itemp)
+ {
+ setSelection(itemp, FALSE, FALSE);
+ }
+ }
+
+ if (mRenamer->getVisible())
+ {
+ //RN: commit rename changes when focus is moved, only revert on ESC
+ finishRenamingItem();
+ }
+ if(!hasFocus())
+ {
+ gEditMenuHandler = this;
+ }
+ }
+
+ LLFolderViewFolder::setFocus(focus);
+}
+
+BOOL LLFolderView::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+
+ LLView *item = NULL;
+ if (getChildCount() > 0)
+ {
+ item = *(getChildList()->begin());
+ }
+
+ if( getVisible() && mEnabled && !called_from_parent )
+ {
+ 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:
+ // mark flag don't commit
+ if( mRenameItem && mRenamer->getVisible() )
+ {
+ revertRenamingItem();
+ handled = TRUE;
+ }
+ else
+ {
+ if( gViewerWindow->childHasKeyboardFocus( this ) )
+ {
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+ }
+ }
+ 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 && gFocusMgr.childHasKeyboardFocus(getRoot()))
+ {
+ if (key == KEY_BACKSPACE)
+ {
+ mSearchTimer.reset();
+ if (mSearchString.size())
+ {
+ mSearchString.erase(mSearchString.size() - 1, 1);
+ }
+ search(getCurSelectedItem(), mSearchString.c_str(), FALSE);
+ handled = TRUE;
+ }
+ else if (mask & MASK_CONTROL && key == 'N')
+ {
+ LLFolderViewItem* selection = getCurSelectedItem();
+ if (selection)
+ {
+ selection = selection->getNextOpenNode();
+ }
+ search(selection, mSearchString.c_str(), FALSE);
+ mSearchTimer.reset();
+ handled = TRUE;
+ }
+ else if (mask & MASK_CONTROL && key == 'P')
+ {
+ LLFolderViewItem* selection = getCurSelectedItem();
+ if (selection)
+ {
+ selection = selection->getPreviousOpenNode();
+ }
+ search(selection, mSearchString.c_str(), TRUE);
+ mSearchTimer.reset();
+ handled = TRUE;
+ }
+ }
+
+ if (handled)
+ {
+ gViewerWindow->requestFastFrame(this);
+ }
+ return handled;
+}
+
+
+BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ 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()))
+ {
+ //do text search
+ if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout"))
+ {
+ mSearchString.clear();
+ }
+ mSearchTimer.reset();
+ if (mSearchString.size() < 128)
+ {
+ mSearchString += uni_char;
+ }
+ search(getCurSelectedItem(), mSearchString.c_str(), FALSE);
+
+ handled = TRUE;
+ }
+
+ if (handled)
+ {
+ gViewerWindow->requestFastFrame(this);
+ }
+
+ return handled;
+}
+
+
+BOOL LLFolderView::canDoDelete()
+{
+ if (mSelectedItems.size() == 0) return FALSE;
+ selected_items_t::iterator item_it;
+ for (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();
+
+ setFocus(TRUE);
+
+ return LLView::handleMouseDown( x, y, mask );
+}
+
+void LLFolderView::onFocusLost( )
+{
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+BOOL LLFolderView::search(LLFolderViewItem* first_item, const LLString &search_string, BOOL backward)
+{
+ // get first selected item
+ LLFolderViewItem* search_item = first_item;
+
+ // make sure search string is upper case
+ LLString upper_case_string = search_string;
+ LLString::toUpper(upper_case_string);
+
+ // if nothing selected, select first item in folder
+ if (!first_item)
+ {
+ // start from first item
+ first_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 LLString 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 )
+{
+ if (!getVisible())
+ {
+ return FALSE;
+ }
+
+ 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
+ setFocus(TRUE);
+
+ BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
+ S32 count = mSelectedItems.size();
+ LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
+ if(handled && (count > 0) && menu)
+ {
+ //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->arrange();
+ menu->updateParent(gMenuHolder);
+ LLMenuGL::showPopup(this, menu, x, y);
+ }
+ 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,
+ LLString& 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(gViewerWindow->hasTopView(mRenamer))
+ {
+ gViewerWindow->setTopView(NULL, NULL);
+ }
+ LLView::deleteViewByHandle(mPopupMenuHandle);
+ mPopupMenuHandle = LLViewHandle::sDeadHandle;
+ 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(-mRect.mLeft, visible_height - mRect.mBottom, visible_width, visible_height);
+ return visible_rect;
+}
+
+BOOL LLFolderView::getShowSelectionContext()
+{
+ if (mShowSelectionContext)
+ {
+ return TRUE;
+ }
+ LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
+ 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<LLUUID, LLFolderViewItem*>::iterator map_it;
+ map_it = mItemMap.find(id);
+ if (map_it != mItemMap.end())
+ {
+ return map_it->second;
+ }
+
+ return NULL;
+}
+
+//static
+void LLFolderView::idle(void* user_data)
+{
+ LLFastTimer t2(LLFastTimer::FTM_INVENTORY);
+ LLFolderView* self = (LLFolderView*)user_data;
+
+ BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters");
+ if (debug_filters != self->getDebugFilters())
+ {
+ self->mDebugFilters = debug_filters;
+ self->arrangeAll();
+ }
+
+ self->mFilter.clearModified();
+ BOOL filter_modified_and_active = self->mCompletedFilterGeneration < self->mFilter.getCurrentGeneration() &&
+ self->mFilter.isActive();
+ self->mNeedsAutoSelect = filter_modified_and_active &&
+ !(gFocusMgr.childHasKeyboardFocus(self) || gFocusMgr.getMouseCapture());
+
+ // filter to determine visiblity before arranging
+ self->filterFromRoot();
+
+ self->sanitizeSelection();
+
+ // 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 (self->mNeedsAutoSelect)
+ {
+ LLFastTimer t3(LLFastTimer::FTM_AUTO_SELECT);
+ // select new item only if a filtered item not currently selected
+ LLFolderViewItem* selected_itemp = self->mSelectedItems.empty() ? NULL : self->mSelectedItems.back();
+ if ((!selected_itemp || !selected_itemp->getFiltered()) && !self->mAutoSelectOverride)
+ {
+ // select first filtered item
+ LLSelectFirstFilteredItem filter;
+ self->applyFunctorRecursively(filter);
+ }
+ self->scrollToShowSelection();
+ }
+
+ if( self->needsArrange() && self->isInVisibleChain())
+ {
+ self->arrangeFromRoot();
+ }
+
+ if (self->mSelectedItems.size() && self->mNeedsScroll)
+ {
+ self->scrollToShowItem(self->mSelectedItems.back());
+ // continue scrolling until animated layout change is done
+ if (!self->needsArrange() || !self->isInVisibleChain())
+ {
+ self->mNeedsScroll = FALSE;
+ }
+ }
+
+ if (self->mSelectionChanged && self->mSelectCallback)
+ {
+ //RN: we use keyboard focus as a proxy for user-explicit actions
+ self->mSelectCallback(self->mSelectedItems, gFocusMgr.childHasKeyboardFocus(self), self->mUserData);
+ }
+ self->mSelectionChanged = FALSE;
+}
+
+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;
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b)
+{
+ S32 compare = LLString::compareDict(a->getLabel(), b->getLabel());
+ if (0 == compare)
+ {
+ return (a->getCreationDate() > b->getCreationDate());
+ }
+ else
+ {
+ return (compare < 0);
+ }
+}
+
+// BUG: This is very very slow. The getCreationDate() is log n in number
+// of inventory items.
+bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b)
+{
+ U32 first_create = a->getCreationDate();
+ U32 second_create = b->getCreationDate();
+ if (first_create == second_create)
+ {
+ return (LLString::compareDict(a->getLabel(), b->getLabel()) < 0);
+ }
+ else
+ {
+ return (first_create > second_create);
+ }
+}
+
+void top_view_lost( LLView* view )
+{
+ if( view ) view->setVisible( FALSE );
+}
+
+void delete_selected_item(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->removeSelectedItems();
+ }
+}
+
+void copy_selected_item(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->copy();
+ }
+}
+
+void paste_items(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->paste();
+ }
+}
+
+void open_selected_items(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->openSelectedItems();
+ }
+}
+
+void properties_selected_items(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(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();
+ }
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryFilter
+///----------------------------------------------------------------------------
+LLInventoryFilter::LLInventoryFilter(const LLString& name) :
+ mName(name),
+ mModified(FALSE),
+ mNeedTextRebuild(TRUE)
+{
+ mFilterOps.mFilterTypes = 0xffffffff;
+ mFilterOps.mMinDate = 0;
+ mFilterOps.mMaxDate = U32_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 = "";
+ mFilterGeneration = 0;
+ mMustPassGeneration = S32_MAX;
+ mMinRequiredGeneration = 0;
+ mNextFilterGeneration = mFilterGeneration + 1;
+
+ mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff");
+}
+
+LLInventoryFilter::~LLInventoryFilter()
+{
+}
+
+BOOL LLInventoryFilter::check(LLFolderViewItem* item)
+{
+ U32 earliest;
+
+ earliest = time_corrected() - mFilterOps.mHoursAgo * 3600;
+ if (mFilterOps.mMinDate && mFilterOps.mMinDate < earliest)
+ {
+ earliest = mFilterOps.mMinDate;
+ }
+ else if (!mFilterOps.mHoursAgo)
+ {
+ earliest = 0;
+ }
+ LLFolderViewEventListener* listener = item->getListener();
+ mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : LLString::npos;
+ BOOL passed = (0x1 << listener->getInventoryType() & mFilterOps.mFilterTypes || listener->getInventoryType() == LLInventoryType::IT_NONE)
+ && (mFilterSubString.size() == 0 || mSubStringMatchOffset != LLString::npos)
+ && ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions)
+ && (listener->getCreationDate() >= earliest && listener->getCreationDate() <= mFilterOps.mMaxDate);
+ return passed;
+}
+
+const LLString LLInventoryFilter::getFilterSubString(BOOL trim)
+{
+ return mFilterSubString;
+}
+
+std::string::size_type LLInventoryFilter::getStringMatchOffset() const
+{
+ return mSubStringMatchOffset;
+}
+
+BOOL LLInventoryFilter::isActive()
+{
+ return mFilterOps.mFilterTypes != 0xffffffff
+ || mFilterSubString.size()
+ || mFilterOps.mPermissions != PERM_NONE
+ || mFilterOps.mMinDate != 0
+ || mFilterOps.mMaxDate != U32_MAX
+ || mFilterOps.mHoursAgo != 0;
+}
+
+BOOL LLInventoryFilter::isModified()
+{
+ return mModified;
+}
+
+BOOL LLInventoryFilter::isModifiedAndClear()
+{
+ BOOL ret = mModified;
+ mModified = FALSE;
+ return ret;
+}
+
+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);
+ }
+
+ }
+}
+
+void LLInventoryFilter::setFilterSubString(const LLString& string)
+{
+ if (mFilterSubString != string)
+ {
+ // 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;
+ LLString::toUpper(mFilterSubString);
+ LLString::trimHead(mFilterSubString);
+
+ if (less_restrictive)
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else if (more_restrictive)
+ {
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else
+ {
+ setModified(FILTER_RESTART);
+ }
+ }
+}
+
+void LLInventoryFilter::setFilterPermissions(PermissionMask perms)
+{
+ if (mFilterOps.mPermissions != perms)
+ {
+ // 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 (more_bits_set && fewer_bits_set)
+ {
+ setModified(FILTER_RESTART);
+ }
+ else if (more_bits_set)
+ {
+ // target must have all requested permission bits, so more bits == more restrictive
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else if (fewer_bits_set)
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ }
+}
+
+void LLInventoryFilter::setDateRange(U32 min_date, U32 max_date)
+{
+ mFilterOps.mHoursAgo = 0;
+ if (mFilterOps.mMinDate != min_date)
+ {
+ mFilterOps.mMinDate = min_date;
+ setModified();
+ }
+ if (mFilterOps.mMaxDate != llmax(mFilterOps.mMinDate, max_date))
+ {
+ mFilterOps.mMaxDate = llmax(mFilterOps.mMinDate, max_date);
+ setModified();
+ }
+}
+
+void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl)
+{
+ if (sl && !isSinceLogoff())
+ {
+ setDateRange(mLastLogoff, U32_MAX);
+ setModified();
+ }
+ if (!sl && isSinceLogoff())
+ {
+ setDateRange(0, U32_MAX);
+ setModified();
+ }
+}
+
+BOOL LLInventoryFilter::isSinceLogoff()
+{
+ return mFilterOps.mMinDate == mLastLogoff && mFilterOps.mMaxDate == U32_MAX;
+}
+
+void LLInventoryFilter::setHoursAgo(U32 hours)
+{
+ if (mFilterOps.mHoursAgo != hours)
+ {
+ //FIXME: need to cache last filter time, in case filter goes stale
+ BOOL less_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours > mFilterOps.mHoursAgo);
+ BOOL more_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours <= mFilterOps.mHoursAgo);
+ mFilterOps.mHoursAgo = hours;
+ mFilterOps.mMinDate = 0;
+ mFilterOps.mMaxDate = U32_MAX;
+ if (less_restrictive)
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else if (more_restrictive)
+ {
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else
+ {
+ setModified(FILTER_RESTART);
+ }
+ }
+}
+void LLInventoryFilter::setShowFolderState(EFolderShow state)
+{
+ if (mFilterOps.mShowFolderState != state)
+ {
+ 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();
+ }
+ }
+}
+
+void LLInventoryFilter::setSortOrder(U32 order)
+{
+ if (mOrder != order)
+ {
+ mOrder = order;
+ setModified();
+ }
+}
+
+void LLInventoryFilter::markDefault()
+{
+ mDefaultFilterOps = mFilterOps;
+}
+
+void LLInventoryFilter::resetDefault()
+{
+ mFilterOps = mDefaultFilterOps;
+ setModified();
+}
+
+void LLInventoryFilter::setModified(EFilterBehavior behavior)
+{
+ mModified = TRUE;
+ mNeedTextRebuild = TRUE;
+ mFilterGeneration = mNextFilterGeneration++;
+
+ if (mFilterBehavior == FILTER_NONE)
+ {
+ mFilterBehavior = behavior;
+ }
+ else if (mFilterBehavior != behavior)
+ {
+ // trying to do both less restrictive and more restrictive filter
+ // basically means restart from scratch
+ mFilterBehavior = FILTER_RESTART;
+ }
+
+ if (isActive())
+ {
+ // 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
+ {
+ // shortcut disabled filters to show everything immediately
+ mMinRequiredGeneration = 0;
+ mMustPassGeneration = S32_MAX;
+ }
+}
+
+BOOL LLInventoryFilter::isFilterWith(LLInventoryType::EType t)
+{
+ return mFilterOps.mFilterTypes & (0x01 << t);
+}
+
+LLString LLInventoryFilter::getFilterText()
+{
+ if (!mNeedTextRebuild)
+ {
+ return mFilterText;
+ }
+
+ mNeedTextRebuild = FALSE;
+ LLString filtered_types;
+ LLString not_filtered_types;
+ BOOL filtered_by_type = FALSE;
+ BOOL filtered_by_all_types = TRUE;
+ S32 num_filter_types = 0;
+ mFilterText = "";
+
+ if (isFilterWith(LLInventoryType::IT_ANIMATION))
+ {
+ filtered_types += " Animations,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Animations,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_CALLINGCARD))
+ {
+ filtered_types += " Calling Cards,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Calling Cards,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_WEARABLE))
+ {
+ filtered_types += " Clothing,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Clothing,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_GESTURE))
+ {
+ filtered_types += " Gestures,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Gestures,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_LANDMARK))
+ {
+ filtered_types += " Landmarks,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Landmarks,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_NOTECARD))
+ {
+ filtered_types += " Notecards,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Notecards,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_OBJECT) && isFilterWith(LLInventoryType::IT_ATTACHMENT))
+ {
+ filtered_types += " Objects,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Objects,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_LSL))
+ {
+ filtered_types += " Scripts,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Scripts,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_SOUND))
+ {
+ filtered_types += " Sounds,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Sounds,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_TEXTURE))
+ {
+ filtered_types += " Textures,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Textures,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_SNAPSHOT))
+ {
+ filtered_types += " Snapshots,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Snapshots,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!gInventory.backgroundFetchActive() && filtered_by_type && !filtered_by_all_types)
+ {
+ mFilterText += " - ";
+ if (num_filter_types < 5)
+ {
+ mFilterText += filtered_types;
+ }
+ else
+ {
+ mFilterText += "No ";
+ mFilterText += not_filtered_types;
+ }
+ // remove the ',' at the end
+ mFilterText.erase(mFilterText.size() - 1, 1);
+ }
+
+ if (isSinceLogoff())
+ {
+ mFilterText += " - Since Logoff";
+ }
+ return mFilterText;
+}