diff options
Diffstat (limited to 'indra/newview/llfolderview.cpp')
-rw-r--r-- | indra/newview/llfolderview.cpp | 3164 |
1 files changed, 119 insertions, 3045 deletions
diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index fd8c22b8e5..c54eafb67a 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -34,20 +34,13 @@ #include "llfolderview.h" -#include <algorithm> - -#include "llviewercontrol.h" -#include "lldbstrings.h" -#include "llfocusmgr.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llrender.h" -#include "llinventory.h" - +#include "llagent.h" #include "llcallbacklist.h" #include "llinventorybridge.h" #include "llinventoryclipboard.h" // *TODO: remove this once hack below gone. -#include "llinventoryview.h"// hacked in for the bonus context menu items. +#include "llinventoryfilter.h" +#include "llfoldertype.h" +#include "llfloaterinventory.h"// hacked in for the bonus context menu items. #include "llkeyboard.h" #include "lllineeditor.h" #include "llmenugl.h" @@ -56,39 +49,40 @@ #include "lltooldraganddrop.h" #include "lltrans.h" #include "llui.h" -#include "llviewerimage.h" -#include "llviewerimagelist.h" +#include "llviewertexture.h" +#include "llviewertexturelist.h" #include "llviewerjointattachment.h" #include "llviewermenu.h" #include "lluictrlfactory.h" +#include "llviewercontrol.h" #include "llviewerwindow.h" #include "llvoavatar.h" #include "llfloaterproperties.h" -// RN: HACK -// We need these because some of the code below relies on things like -// gAgent root folder. Remove them once the abstraction leak is fixed. -#include "llagent.h" -#include "llappviewer.h" +// Linden library includes +#include "lldbstrings.h" +#include "llfocusmgr.h" +#include "llfontgl.h" +#include "llgl.h" +#include "llrender.h" +#include "llinventory.h" + +// Third-party library includes +#include <algorithm> ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- -const S32 LEFT_PAD = 5; -const S32 LEFT_INDENTATION = 13; -const S32 ICON_PAD = 2; -const S32 ICON_WIDTH = 16; -const S32 TEXT_PAD = 1; -const S32 ARROW_SIZE = 12; const S32 RENAME_WIDTH_PAD = 4; const S32 RENAME_HEIGHT_PAD = 2; const S32 AUTO_OPEN_STACK_DEPTH = 16; -const S32 MIN_ITEM_WIDTH_VISIBLE = ICON_WIDTH + ICON_PAD + ARROW_SIZE + TEXT_PAD + /*first few characters*/ 40; +const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH + + LLFolderViewItem::ICON_PAD + + LLFolderViewItem::ARROW_SIZE + + LLFolderViewItem::TEXT_PAD + + /*first few characters*/ 40; const S32 MINIMUM_RENAMER_WIDTH = 80; -const F32 FOLDER_CLOSE_TIME_CONSTANT = 0.02f; -const F32 FOLDER_OPEN_TIME_CONSTANT = 0.03f; -const S32 MAX_FOLDER_ITEM_OVERLAP = 2; enum { SIGNAL_NO_KEYBOARD_FOCUS = 1, @@ -104,2332 +98,6 @@ void properties_selected_items(void* user_data); void paste_items(void* user_data); void renamer_focus_lost( LLFocusableElement* handler, void* user_data ); -///---------------------------------------------------------------------------- -/// Class LLFolderViewItem -///---------------------------------------------------------------------------- - -// statics -const LLFontGL* LLFolderViewItem::sFont = NULL; -const LLFontGL* LLFolderViewItem::sSmallFont = NULL; -LLUIImagePtr LLFolderViewItem::sArrowImage; -LLUIImagePtr LLFolderViewItem::sBoxImage; - -const LLColor4U DEFAULT_WHITE(255, 255, 255); - -//static -void LLFolderViewItem::initClass() -{ - sFont = LLFontGL::getFontSansSerifSmall(); - sSmallFont = LLFontGL::getFontMonospace(); - sArrowImage = LLUI::getUIImage("folder_arrow.tga"); - sBoxImage = LLUI::getUIImage("rounded_square.tga"); -} - -//static -void LLFolderViewItem::cleanupClass() -{ - sArrowImage = NULL; - sBoxImage = NULL; -} - -// NOTE: Optimize this, we call it a *lot* when opening a large inventory - -// Default constructor -LLFolderViewItem::LLFolderViewItem(LLFolderViewItem::Params p) -: LLView(p), - mLabelWidth(0), - mLabelWidthDirty(false), - mParentFolder( NULL ), - mIsSelected( FALSE ), - mIsCurSelection( FALSE ), - mSelectPending(FALSE), - mLabelStyle( LLFontGL::NORMAL ), - mHasVisibleChildren(FALSE), - mIndentation(0), - mNumDescendantsSelected(0), - mFiltered(FALSE), - mLastFilterGeneration(-1), - mStringMatchOffset(std::string::npos), - mControlLabelRotation(0.f), - mDragAndDropTarget(FALSE), - mIsLoading(FALSE), - mLabel(p.name), - mRoot(p.root), - mCreationDate(p.creation_date), - mListener(p.listener), - mArrowImage(p.folder_arrow_image), - mBoxImage(p.selection_image) -{ - refresh(); -} - -// Destroys the object -LLFolderViewItem::~LLFolderViewItem( void ) -{ - delete mListener; - mListener = 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; -} - -// is this item something we think we should be showing? -// for example, if we haven't gotten around to filtering it yet, then the answer is yes -// until we find out otherwise -BOOL LLFolderViewItem::potentiallyVisible() -{ - // we haven't been checked against min required filter - // or we have and we passed - return getLastFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration() || getFiltered(); -} - -BOOL LLFolderViewItem::getFiltered() -{ - return mFiltered && mLastFilterGeneration >= getRoot()->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(LLUIImagePtr icon) -{ - mIcon = icon; -} - -// refresh information from the listener -void LLFolderViewItem::refreshFromListener() -{ - if(mListener) - { - mLabel = mListener->getDisplayName(); - LLAssetType::EType preferred_type = mListener->getPreferredType(); - - // *TODO: to be removed when database supports multi language. This is a - // temporary attempt to display the inventory folder in the user locale. - if (preferred_type != LLAssetType::AT_NONE) - { - mLabel = LLTrans::getString("InvFolder " + mLabel); - }; - - setIcon(mListener->getIcon()); - time_t creation_date = mListener->getCreationDate(); - if (mCreationDate != creation_date) - { - mCreationDate = mListener->getCreationDate(); - dirtyFilter(); - } - mLabelStyle = mListener->getLabelStyle(); - mLabelSuffix = mListener->getLabelSuffix(); - } -} - -void LLFolderViewItem::refresh() -{ - refreshFromListener(); - - std::string searchable_label(mLabel); - searchable_label.append(mLabelSuffix); - LLStringUtil::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, and sort order too - if (mParentFolder) - { - mParentFolder->requestSort(); - mParentFolder->requestArrange(); - } - } - - mLabelWidthDirty = true; -} - -void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); -} - -// This function is called when items are added or view filters change. It's -// implemented here but called by derived classes when folding the -// views. -void LLFolderViewItem::filterFromRoot( void ) -{ - LLFolderViewItem* root = getRoot(); - - 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 openitem, - BOOL take_keyboard_focus) -{ - getRoot()->setSelection(selection, openitem, 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); -} - -EInventorySortGroup LLFolderViewItem::getSortGroup() const -{ - return SG_ITEM; -} - -// 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; - if (mLabelWidthDirty) - { - mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + sFont->getWidth(mSearchableLabel); - mLabelWidthDirty = false; - } - - *width = llmax(*width, mLabelWidth + mIndentation); - *height = getItemHeight(); - return *height; -} - -S32 LLFolderViewItem::getItemHeight() -{ - S32 icon_height = mIcon->getHeight(); - S32 label_height = llround(sFont->getLineHeight()); - return llmax( icon_height, label_height ) + ICON_PAD; -} - -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); - } -} - -// *TODO: This can be optimized a lot by simply recording that it is -// selected in the appropriate places, and assuming that set selection -// means 'deselect' for a leaf item. Do this optimization after -// multiple selection is implemented to make sure it all plays nice -// together. -BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus) -{ - 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::openItem( void ) -{ - if( mListener ) - { - mListener->openItem(); - } -} - -void LLFolderViewItem::preview( void ) -{ - if (mListener) - { - mListener->previewItem(); - } -} - -void LLFolderViewItem::rename(const std::string& new_name) -{ - if( !new_name.empty() ) - { - if( mListener ) - { - mListener->renameItem(new_name); - - if(mParentFolder) - { - mParentFolder->requestSort(); - } - } - } -} - -const std::string& LLFolderViewItem::getSearchableLabel() const -{ - return mSearchableLabel; -} - -const std::string& LLFolderViewItem::getName( void ) const -{ - if(mListener) - { - return mListener->getName(); - } - return mLabel; -} - -// 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. - gFocusMgr.setMouseCapture( this ); - - 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 ); - LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y ); - } - return TRUE; -} - -BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) -{ - if( hasMouseCapture() && isMovable() ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - BOOL can_drag = TRUE; - if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) - { - LLFolderView* root = getRoot(); - - if(root->getCurSelectedItem()) - { - LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD; - - // *TODO: push this into listener and remove - // dependency on llagent - if(mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gAgent.getInventoryRootID())) - { - src = LLToolDragAndDrop::SOURCE_AGENT; - } - else if (mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventoryLibraryRoot)) - { - src = LLToolDragAndDrop::SOURCE_LIBRARY; - } - - can_drag = root->startDrag(src); - if (can_drag) - { - // if (mListener) mListener->startDrag(); - // RN: when starting drag and drop, clear out last auto-open - root->autoOpenTest(NULL); - root->setShowSelectionContext(TRUE); - - // Release keyboard focus, so that if stuff is dropped into the - // world, pressing the delete key won't blow away the inventory - // item. - gFocusMgr.setKeyboardFocus(NULL); - - return LLToolDragAndDrop::getInstance()->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( hasMouseCapture() ) - { - getRoot()->setShowSelectionContext(FALSE); - gFocusMgr.setMouseCapture( NULL ); - } - return TRUE; -} - -BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = FALSE; - BOOL handled = FALSE; - if(mListener) - { - 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() -{ - static LLCachedControl<LLColor4> sFgColor(gSavedSkinSettings, "MenuItemEnabledColor", DEFAULT_WHITE); - static LLCachedControl<LLColor4> sHighlightBgColor(gSavedSkinSettings, "MenuItemHighlightBgColor", DEFAULT_WHITE); - static LLCachedControl<LLColor4> sHighlightFgColor(gSavedSkinSettings, "MenuItemHighlightFgColor", DEFAULT_WHITE); - static LLCachedControl<LLColor4> sFilterBGColor(gSavedSkinSettings, "FilterBackgroundColor", DEFAULT_WHITE); - static LLCachedControl<LLColor4> sFilterTextColor(gSavedSkinSettings, "FilterTextColor", DEFAULT_WHITE); - static LLCachedControl<LLColor4> sSuffixColor(gSavedSkinSettings, "InventoryItemSuffixColor", DEFAULT_WHITE); - static LLCachedControl<LLColor4> sSearchStatusColor(gSavedSkinSettings, "InventorySearchStatusColor", DEFAULT_WHITE); - - bool possibly_has_children = false; - bool up_to_date = mListener && mListener->isUpToDate(); - if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter... - (!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter) - { - possibly_has_children = true; - } - if(/*mControlLabel[0] != '\0' && */possibly_has_children) - { - if (sArrowImage) - { - gl_draw_scaled_rotated_image(mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD, - ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, sArrowImage->getImage(), 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 || (getRoot()->getParentPanel()->hasFocus()); - - // always render "current" item, only render other selected items if - // mShowSingleSelection is FALSE - if( mIsSelected ) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_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, - getRect().getHeight(), - getRect().getWidth() - 2, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD), - bg_color, filled); - if (mIsCurSelection) - { - gl_rect_2d( - 0, - getRect().getHeight(), - getRect().getWidth() - 2, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD), - sHighlightFgColor, FALSE); - } - if (getRect().getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2) - { - gl_rect_2d( - 0, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, - getRect().getWidth() - 2, - 2, - sHighlightFgColor, FALSE); - if (show_context) - { - gl_rect_2d( - 0, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, - getRect().getWidth() - 2, - 2, - sHighlightBgColor, TRUE); - } - } - } - if (mDragAndDropTarget) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d( - 0, - getRect().getHeight(), - getRect().getWidth() - 2, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD), - sHighlightBgColor, FALSE); - - if (getRect().getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2) - { - gl_rect_2d( - 0, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, - getRect().getWidth() - 2, - 2, - sHighlightBgColor, FALSE); - } - mDragAndDropTarget = FALSE; - } - - - if(mIcon) - { - mIcon->draw(mIndentation + ARROW_SIZE + TEXT_PAD, getRect().getHeight() - 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)getRect().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, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - text_left = right_x; - } - - - if ( mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime") ) - { - sFont->renderUTF8(LLTrans::getString("LoadingData"), 0, text_left, y, sSearchStatusColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &right_x, FALSE); - text_left = right_x; - } - - sFont->renderUTF8( mLabel, 0, text_left, y, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - if (!mLabelSuffix.empty()) - { - sFont->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - } - - if (sBoxImage.notNull() && mStringMatchOffset != std::string::npos) - { - // don't draw backgrounds for zero-length strings - S32 filter_string_length = getRoot()->getFilterSubString().size(); - if (filter_string_length > 0) - { - std::string combined_string = mLabel + mLabelSuffix; - S32 left = llround(text_left) + sFont->getWidth(combined_string, 0, mStringMatchOffset) - 1; - S32 right = left + sFont->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; - S32 bottom = llfloor(getRect().getHeight() - sFont->getLineHeight() - 3); - S32 top = getRect().getHeight(); - - LLRect box_rect(left, top, right, bottom); - sBoxImage->draw(box_rect, sFilterBGColor); - F32 match_string_left = text_left + sFont->getWidthF32(combined_string, 0, mStringMatchOffset); - F32 y = (F32)getRect().getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD; - sFont->renderUTF8( combined_string, mStringMatchOffset, match_string_left, y, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, - filter_string_length, S32_MAX, &right_x, FALSE ); - } - } - } - - if( sDebugRects ) - { - drawDebugRect(); - } - - //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview) - //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); - //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights)) - //{ - // drawDebugRect(); - //} -} - - -///---------------------------------------------------------------------------- -/// Class LLFolderViewFolder -///---------------------------------------------------------------------------- - -LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): - LLFolderViewItem( p ), // 0 = no create time - mIsOpen(FALSE), - mExpanderHighlighted(FALSE), - mCurHeight(0.f), - mTargetHeight(0.f), - mAutoOpenCountdown(0.f), - mSubtreeCreationDate(0), - mAmTrash(LLFolderViewFolder::UNKNOWN), - mLastArrangeGeneration( -1 ), - mLastCalculatedWidth(0), - mCompletedFilterGeneration(-1), - mMostFilteredDescendantGeneration(-1), - mNeedsSort(false) -{} - -// 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() -} - -// 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) -{ - // sort before laying out contents - if (mNeedsSort) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; - } - - 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 = getRoot()->getArrangeGeneration(); - if (mIsOpen) - { - // Add sizes of children - S32 parent_item_height = getRect().getHeight(); - - for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - if (getRoot()->getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? - (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter - } - - if (folderp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() ); - } - } - for(items_t::iterator iit = mItems.begin(); - iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - if (getRoot()->getDebugFilters()) - { - itemp->setVisible(TRUE); - } - else - { - itemp->setVisible(itemp->getFiltered(filter_generation)); - } - - if (itemp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); - // don't change width, as this item is as wide as its parent folder by construction - itemp->reshape( itemp->getRect().getWidth(), child_height); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() ); - } - } - } - - 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 (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - // hide if beyond current folder height - (*fit)->setVisible(FALSE); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - // number of pixels that bottom of item label is from top of parent folder - if (getRect().getHeight() - (*iit)->getRect().mBottom - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - (*iit)->setVisible(FALSE); - } - } - } - else - { - mCurHeight = mTargetHeight; - } - - // don't change width as this item is already as wide as its parent folder - reshape(getRect().getWidth(),llround(mCurHeight)); - - // pass current height value back to parent - *height = llround(mCurHeight); - - return llround(mTargetHeight); -} - -BOOL LLFolderViewFolder::needsArrange() -{ - return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); -} - -void LLFolderViewFolder::requestSort() -{ - mNeedsSort = true; - // whenever item order changes, we need to lay things out again - requestArrange(); -} - -void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up) -{ - mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation); - mCompletedFilterGeneration = generation; - // only aggregate up if we are a lower (older) value - if (recurse_up && mParentFolder && generation < mParentFolder->getCompletedFilterGeneration()) - { - 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 (filter.isNotDefault() && getFiltered(filter.getMinRequiredGeneration()) && (mListener && !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 (getRoot()->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 (getRoot()->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 >= getRoot()->getFilter()->getCurrentGeneration(); -} - -// Passes selection information on to children and record selection -// information if necessary. -BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem, - 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, openitem, take_keyboard_focus)) - { - rv = TRUE; - child_selected = TRUE; - mNumDescendantsSelected++; - } - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if((*iit)->setSelection(selection, openitem, take_keyboard_focus)) - { - rv = TRUE; - child_selected = TRUE; - mNumDescendantsSelected++; - } - } - if(openitem && 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(); // removes entry from mFolders - } - - 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()) - { - //RN: this seem unneccessary as remove() moves to trash - //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 || item->getParentFolder() != this) - { - 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); -} - -bool LLFolderViewFolder::isTrash() const -{ - if (mAmTrash == LLFolderViewFolder::UNKNOWN) - { - mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH, false) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH; - } - return mAmTrash == LLFolderViewFolder::TRASH; -} - -void LLFolderViewFolder::sortBy(U32 order) -{ - if (!mSortFunction.updateSort(order)) - { - // No changes. - return; - } - - // Propegate this change to sub folders - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->sortBy(order); - } - - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - - if (order & LLInventoryFilter::SO_DATE) - { - time_t latest = 0; - - if (!mItems.empty()) - { - LLFolderViewItem* item = *(mItems.begin()); - latest = item->getCreationDate(); - } - - if (!mFolders.empty()) - { - LLFolderViewFolder* folder = *(mFolders.begin()); - if (folder->getCreationDate() > latest) - { - latest = folder->getCreationDate(); - } - } - mSubtreeCreationDate = latest; - } -} - -void LLFolderViewFolder::setItemSortOrder(U32 ordering) -{ - if (mSortFunction.updateSort(ordering)) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setItemSortOrder(ordering); - } - - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - } -} - -EInventorySortGroup LLFolderViewFolder::getSortGroup() const -{ - if (isTrash()) - { - return SG_TRASH_FOLDER; - } - - // Folders that can't be moved are 'system' folders. - if( mListener ) - { - if( !(mListener->isItemMovable()) ) - { - return SG_SYSTEM_FOLDER; - } - } - - return SG_NORMAL_FOLDER; -} - -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) -{ - mItems.push_back(item); - item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); - item->setVisible(FALSE); - addChild( item ); - item->dirtyFilter(); - requestArrange(); - requestSort(); - return TRUE; -} - -// this is an internal method used for adding items to folders. -BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) -{ - mFolders.push_back(folder); - folder->setOrigin(0, 0); - folder->reshape(getRect().getWidth(), 0); - folder->setVisible(FALSE); - addChild( folder ); - folder->dirtyFilter(); - // rearrange all descendants too, as our indentation level might have changed - folder->requestArrange(TRUE); - requestSort(); - return TRUE; -} - -void LLFolderViewFolder::requestArrange(BOOL include_descendants) -{ - mLastArrangeGeneration = -1; - // flag all items up to root - if (mParentFolder) - { - mParentFolder->requestArrange(); - } - - if (include_descendants) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - (*iter)->requestArrange(TRUE); - } - } -} - -void LLFolderViewFolder::toggleOpen() -{ - setOpen(!mIsOpen); -} - -// Force a folder open or closed -void LLFolderViewFolder::setOpen(BOOL openitem) -{ - setOpenArrangeRecursively(openitem); -} - -void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) -{ - BOOL was_open = mIsOpen; - mIsOpen = openitem; - if(!was_open && openitem) - { - 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(openitem, RECURSE_DOWN); /* Flawfinder: ignore */ - } - } - if (mParentFolder && (recurse == RECURSE_UP || recurse == RECURSE_UP_DOWN)) - { - mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP); - } - - if (was_open != mIsOpen) - { - requestArrange(); - } -} - -BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, - BOOL drop, - EDragAndDropType c_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data); - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - // drag and drop to child item, so clear pending auto-opens - getRoot()->autoOpenTest(NULL); - - return TRUE; -} - -void LLFolderViewFolder::openItem( void ) -{ - toggleOpen(); -} - -void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor) -{ - functor.doFolder(this); - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->applyFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - functor.doItem((*iit)); - } -} - -void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->applyListenerFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - (*iit)->applyListenerFunctorRecursively(functor); - } -} - -// LLView functionality -BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - LLFolderView* root_view = getRoot(); - - BOOL handled = FALSE; - if(mIsOpen) - { - handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, - cargo_data, accept, tooltip_msg) != NULL; - } - - if (!handled) - { - BOOL accepted = mListener && mListener->dragOrDrop(mask, drop,cargo_type,cargo_data); - - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - if (!drop && accepted) - { - root_view->autoOpenTest(this); - } - - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl; - } - - return TRUE; -} - - -BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - // fetch contents of this folder, as context menu can depend on contents - // still, user would have to open context menu again to see the changes - gInventory.fetchDescendentsOf(mListener->getUUID()); - - if( mIsOpen ) - { - handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - } - if (!handled) - { - handled = LLFolderViewItem::handleRightMouseDown( x, y, mask ); - } - return handled; -} - - -BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) -{ - BOOL handled = LLView::handleHover(x, y, mask); - - if (!handled) - { - // this doesn't do child processing - handled = LLFolderViewItem::handleHover(x, y, mask); - } - - //if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD && y > getRect().getHeight() - ) - //{ - // gViewerWindow->setCursor(UI_CURSOR_ARROW); - // mExpanderHighlighted = TRUE; - // handled = TRUE; - //} - return handled; -} - -BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - if( mIsOpen ) - { - handled = childrenHandleMouseDown(x,y,mask) != NULL; - } - if( !handled ) - { - if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD) - { - toggleOpen(); - handled = TRUE; - } - else - { - // do normal selection logic - handled = LLFolderViewItem::handleMouseDown(x, y, mask); - } - } - - return handled; -} - -BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - if( mIsOpen ) - { - handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - } - if( !handled ) - { - if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD) - { - // don't select when user double-clicks plus sign - // so as not to contradict single-click behavior - toggleOpen(); - } - else - { - setSelectionFromRoot(this, FALSE); - toggleOpen(); - } - handled = TRUE; - } - return handled; -} - -void LLFolderViewFolder::draw() -{ - if (mAutoOpenCountdown != 0.f) - { - mControlLabelRotation = mAutoOpenCountdown * -90.f; - } - else if (mIsOpen) - { - mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); - } - else - { - mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); - } - - bool possibly_has_children = false; - bool up_to_date = mListener && mListener->isUpToDate(); - if(!up_to_date && mListener && mListener->hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) - { - possibly_has_children = true; - } - - - BOOL loading = ( mIsOpen && possibly_has_children && !up_to_date ); - - if ( loading && !mIsLoading ) - { - // Measure how long we've been in the loading state - mTimeSinceRequestStart.reset(); - } - - mIsLoading = loading; - - LLFolderViewItem::draw(); - - // draw children if root folder, or any other folder that is open or animating to closed state - if( getRoot() == this || (mIsOpen || mCurHeight != mTargetHeight )) - { - LLView::draw(); - } - - mExpanderHighlighted = FALSE; -} - -time_t LLFolderViewFolder::getCreationDate() const -{ - return llmax<time_t>(mCreationDate, mSubtreeCreationDate); -} - - -BOOL LLFolderViewFolder::potentiallyVisible() -{ - // folder should be visible by it's own filter status - return LLFolderViewItem::potentiallyVisible() - // or one or more of its descendants have passed the minimum filter requirement - || hasFilteredDescendants(getRoot()->getFilter()->getMinRequiredGeneration()) - // or not all of its descendants have been checked against minimum filter requirement - || getCompletedFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration(); -} - -// this does prefix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children ) -{ - BOOL found_item = FALSE; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at beginning - if(item == NULL) - { - found_item = TRUE; - } - - // find current item among children - folders_t::iterator fit = mFolders.begin(); - folders_t::iterator fend = mFolders.end(); - - items_t::iterator iit = mItems.begin(); - items_t::iterator iend = mItems.end(); - - // if not trivially starting at the beginning, we have to find the current item - if (!found_item) - { - // first, look among folders, since they are always above items - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = TRUE; - // if we are on downwards traversal - if (include_children && (*fit)->isOpen()) - { - // look for first descendant - return (*fit)->getNextFromChild(NULL, TRUE); - } - // otherwise advance to next folder - ++fit; - include_children = TRUE; - break; - } - } - - // didn't find in folders? Check items... - if (!found_item) - { - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = TRUE; - // point to next item - ++iit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(FALSE); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found folder, continue through folders - // searching for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - // turn on downwards traversal for next folder - ++fit; - } - - if (fit != fend) - { - result = (*fit); - } - else - { - // otherwise, scan for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - // check to see if we have a valid item - if (iit != iend) - { - result = (*iit); - } - } - - if( !result && mParentFolder ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // and skip children for this folder, as we've already discounted them - result = mParentFolder->getNextFromChild(this, FALSE); - } - - return result; -} - -// this does postfix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children ) -{ - BOOL found_item = FALSE; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at end - if(item == NULL) - { - found_item = TRUE; - } - - // find current item among children - folders_t::reverse_iterator fit = mFolders.rbegin(); - folders_t::reverse_iterator fend = mFolders.rend(); - - items_t::reverse_iterator iit = mItems.rbegin(); - items_t::reverse_iterator iend = mItems.rend(); - - // if not trivially starting at the end, we have to find the current item - if (!found_item) - { - // first, look among items, since they are always below the folders - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = TRUE; - // point to next item - ++iit; - break; - } - } - - // didn't find in items? Check folders... - if (!found_item) - { - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = TRUE; - // point to next folder - ++fit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(FALSE); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found item, continue through items - // searching for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - if (iit != iend) - { - // we found an appropriate item - result = (*iit); - } - else - { - // otherwise, scan for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - ++fit; - } - - // check to see if we have a valid folder - if (fit != fend) - { - // try selecting child element of this folder - if ((*fit)->isOpen()) - { - result = (*fit)->getPreviousFromChild(NULL); - } - else - { - result = (*fit); - } - } - } - - if( !result ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // which gets back to this folder, which will only be visited if it is a valid, visible item - result = this; - } - - return result; -} - //--------------------------------------------------------------------------- @@ -2514,7 +182,7 @@ LLFolderView::LLFolderView(const Params& p) mNeedsAutoRename(FALSE), mDebugFilters(FALSE), mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately - mFilter(p.name), + mFilter( new LLInventoryFilter(p.name) ), mShowSelectionContext(FALSE), mShowSingleSelection(FALSE), mArrangeGeneration(0), @@ -2557,12 +225,12 @@ LLFolderView::LLFolderView(const Params& p) addChild(mRenamer); // make the popup menu available - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", gMenuHolder); + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if (!menu) { menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); } - menu->setBackgroundColor(gSavedSkinSettings.getColor("MenuPopupBgColor")); + menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); mPopupMenuHandle = menu->getHandle(); } @@ -2600,6 +268,9 @@ LLFolderView::~LLFolderView( void ) mFolders.clear(); mItemMap.clear(); + + delete mFilter; + mFilter = NULL; } BOOL LLFolderView::canFocusChildren() const @@ -2646,7 +317,7 @@ U32 LLFolderView::getSortOrder() const BOOL LLFolderView::addFolder( LLFolderViewFolder* folder) { // enforce sort order of My Inventory followed by Library - if (folder->getListener()->getUUID() == gInventoryLibraryRoot) + if (folder->getListener()->getUUID() == gInventory.getLibraryRootFolderID()) { mFolders.push_back(folder); } @@ -2693,14 +364,15 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen { LLFastTimer t2(LLFastTimer::FTM_ARRANGE); - filter_generation = mFilter.getMinRequiredGeneration(); + filter_generation = mFilter->getMinRequiredGeneration(); mMinWidth = 0; mHasVisibleChildren = hasFilteredDescendants(filter_generation); // arrange always finishes, so optimistically set the arrange generation to the most current mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState(); + LLInventoryFilter::EFolderShow show_folder_state = + getRoot()->getFilter()->getShowFolderState(); S32 total_width = LEFT_PAD; S32 running_height = mDebugFilters ? llceil(sSmallFont->getLineHeight()) : 0; @@ -2781,7 +453,7 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen const std::string LLFolderView::getFilterSubString(BOOL trim) { - return mFilter.getFilterSubString(trim); + return mFilter->getFilterSubString(trim); } void LLFolderView::filter( LLInventoryFilter& filter ) @@ -2904,7 +576,7 @@ void LLFolderView::setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_foc LLFolderViewItem* itemp = getItemByID(obj_id); if(itemp && itemp->getListener()) { - itemp->getListener()->arrangeAndSet(itemp, TRUE, take_keyboard_focus); + itemp->arrangeAndSet(TRUE, take_keyboard_focus); mSelectThisID.setNull(); return; } @@ -2997,7 +669,7 @@ void LLFolderView::sanitizeSelection() LLFolderViewItem* original_selected_item = getCurSelectedItem(); // Cache "Show all folders" filter setting - BOOL show_all_folders = (getRoot()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS); + BOOL show_all_folders = (getRoot()->getFilter()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS); std::vector<LLFolderViewItem*> items_to_remove; selected_items_t::iterator item_iter; @@ -3087,7 +759,7 @@ void LLFolderView::sanitizeSelection() else { // nothing selected to start with, so pick "My Inventory" as best guess - new_selection = getItemByID(gAgent.getInventoryRootID()); + new_selection = getItemByID(gInventory.getRootFolderID()); } if (new_selection) @@ -3148,11 +820,11 @@ void LLFolderView::commitRename( const LLSD& data ) void LLFolderView::draw() { - static LLCachedControl<LLColor4> sSearchStatusColor(gSavedSkinSettings, "InventorySearchStatusColor", DEFAULT_WHITE); + static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", LLColor4::white); if (mDebugFilters) { std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", - mFilter.getCurrentGeneration(), mFilter.getMinRequiredGeneration(), mFilter.getMustPassGeneration()); + mFilter->getCurrentGeneration(), mFilter->getMinRequiredGeneration(), mFilter->getMustPassGeneration()); sSmallFont->renderUTF8(current_filter_string, 0, 2, getRect().getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); @@ -3189,13 +861,14 @@ void LLFolderView::draw() mSearchString.clear(); } - if (hasVisibleChildren() || getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) + if (hasVisibleChildren() + || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) { mStatusText.clear(); } else { - if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter.getMinRequiredGeneration()) + if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter->getMinRequiredGeneration()) { mStatusText = LLTrans::getString("Searching"); sFont->renderUTF8(mStatusText, 0, 2, 1, sSearchStatusColor, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); @@ -3423,6 +1096,35 @@ void LLFolderView::propertiesSelectedItems( void ) } } +void LLFolderView::changeType(LLInventoryModel *model, LLAssetType::EType new_folder_type) +{ + LLFolderBridge *folder_bridge = LLFolderBridge::sSelf; + + if (!folder_bridge) return; + LLViewerInventoryCategory *cat = folder_bridge->getCategory(); + if (!cat) return; + + const LLUUID &folder_id = cat->getUUID(); + const LLUUID &parent_id = cat->getParentUUID(); + const std::string &name = cat->getName(); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_UpdateInventoryFolder); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_FolderData); + msg->addUUIDFast(_PREHASH_FolderID, folder_id); + msg->addUUIDFast(_PREHASH_ParentID, parent_id); + msg->addS8Fast(_PREHASH_Type, new_folder_type); + msg->addStringFast(_PREHASH_Name, name); + gAgent.sendReliableMessage(); + + cat->setPreferredType(new_folder_type); + gInventory.addChangedMask(LLInventoryObserver::LABEL, folder_id); + gInventory.updateLinkedObjects(folder_id); +} + void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) { if (mAutoOpenItems.check() == item || mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) @@ -4233,6 +1935,16 @@ bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata) LLInventoryClipboard::instance().reset(); } + static const std::string change_folder_string = "change_folder_type_"; + if (action.length() > change_folder_string.length() && + (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) + { + LLAssetType::EType new_folder_type = LLFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); + changeType(model, new_folder_type); + return true; + } + + std::set<LLUUID> selected_items; getSelectionList(selected_items); @@ -4292,9 +2004,9 @@ void LLFolderView::doIdle() arrangeAll(); } - mFilter.clearModified(); - BOOL filter_modified_and_active = mCompletedFilterGeneration < mFilter.getCurrentGeneration() && - mFilter.isNotDefault(); + mFilter->clearModified(); + BOOL filter_modified_and_active = mCompletedFilterGeneration < mFilter->getCurrentGeneration() && + mFilter->isNotDefault(); mNeedsAutoSelect = filter_modified_and_active && !(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture()); @@ -4335,7 +2047,7 @@ void LLFolderView::doIdle() { scrollToShowItem(mSelectedItems.back()); // continue scrolling until animated layout change is done - if (getCompletedFilterGeneration() >= mFilter.getMinRequiredGeneration() && + if (getCompletedFilterGeneration() >= mFilter->getMinRequiredGeneration() && (!needsArrange() || !is_visible)) { mNeedsScroll = FALSE; @@ -4402,82 +2114,51 @@ void LLFolderView::updateRenamerPosition() ///---------------------------------------------------------------------------- /// Local function definitions ///---------------------------------------------------------------------------- -bool LLInventorySort::updateSort(U32 order) + +//static +void LLFolderView::onRenamerLost( LLFocusableElement* renamer, void* user_data) { - if (order != mSortOrder) + LLUICtrl* uictrl = dynamic_cast<LLUICtrl*>(renamer); + if (uictrl) { - mSortOrder = order; - mByDate = (order & LLInventoryFilter::SO_DATE); - mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); - mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME); - return true; + uictrl->setVisible(FALSE); } - return false; } -bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b) +LLInventoryFilter* LLFolderView::getFilter() { - // We sort by name if we aren't sorting by date - // OR if these are folders and we are sorting folders by name. - bool by_name = (!mByDate - || (mFoldersByName - && (a->getSortGroup() != SG_ITEM))); - - if (a->getSortGroup() != b->getSortGroup()) - { - if (mSystemToTop) - { - // Group order is System Folders, Trash, Normal Folders, Items - return (a->getSortGroup() < b->getSortGroup()); - } - else if (mByDate) - { - // Trash needs to go to the bottom if we are sorting by date - if ( (a->getSortGroup() == SG_TRASH_FOLDER) - || (b->getSortGroup() == SG_TRASH_FOLDER)) - { - return (b->getSortGroup() == SG_TRASH_FOLDER); - } - } - } + return mFilter; +} - if (by_name) - { - S32 compare = LLStringUtil::compareDict(a->getLabel(), b->getLabel()); - if (0 == compare) - { - return (a->getCreationDate() > b->getCreationDate()); - } - else - { - return (compare < 0); - } - } - else - { - // BUG: This is very very slow. The getCreationDate() is log n in number - // of inventory items. - time_t first_create = a->getCreationDate(); - time_t second_create = b->getCreationDate(); - if (first_create == second_create) - { - return (LLStringUtil::compareDict(a->getLabel(), b->getLabel()) < 0); - } - else - { - return (first_create > second_create); - } - } +void LLFolderView::setFilterPermMask( PermissionMask filter_perm_mask ) +{ + mFilter->setFilterPermissions(filter_perm_mask); } -//static -void LLFolderView::onRenamerLost( LLFocusableElement* renamer, void* user_data) +U32 LLFolderView::getFilterTypes() const { - LLUICtrl* uictrl = dynamic_cast<LLUICtrl*>(renamer); - if (uictrl) - { - uictrl->setVisible(FALSE); - } + return mFilter->getFilterTypes(); +} + +PermissionMask LLFolderView::getFilterPermissions() const +{ + return mFilter->getFilterPermissions(); +} + +// JAMESDEBUG +//LLInventoryFilter::EFolderShow LLFolderView::getShowFolderState() +//{ +// return mFilter->getShowFolderState(); +//} + +BOOL LLFolderView::isFilterModified() +{ + return mFilter->isNotDefault(); +} + +BOOL LLFolderView::getAllowMultiSelect() +{ + return mAllowMultiSelect; } void delete_selected_item(void* user_data) @@ -4524,610 +2205,3 @@ void properties_selected_items(void* 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 std::string& name) : - mName(name), - mModified(FALSE), - mNeedTextRebuild(TRUE) -{ - mFilterOps.mFilterTypes = 0xffffffff; - mFilterOps.mMinDate = time_min(); - mFilterOps.mMaxDate = time_max(); - mFilterOps.mHoursAgo = 0; - mFilterOps.mShowFolderState = SHOW_NON_EMPTY_FOLDERS; - mFilterOps.mPermissions = PERM_NONE; - - mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately - - mSubStringMatchOffset = 0; - mFilterSubString.clear(); - mFilterGeneration = 0; - mMustPassGeneration = S32_MAX; - mMinRequiredGeneration = 0; - mFilterCount = 0; - mNextFilterGeneration = mFilterGeneration + 1; - - mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff"); - mFilterBehavior = FILTER_NONE; - - // copy mFilterOps into mDefaultFilterOps - markDefault(); -} - -LLInventoryFilter::~LLInventoryFilter() -{ -} - -BOOL LLInventoryFilter::check(LLFolderViewItem* item) -{ - time_t earliest; - - earliest = time_corrected() - mFilterOps.mHoursAgo * 3600; - if (mFilterOps.mMinDate > time_min() && mFilterOps.mMinDate < earliest) - { - earliest = mFilterOps.mMinDate; - } - else if (!mFilterOps.mHoursAgo) - { - earliest = 0; - } - LLFolderViewEventListener* listener = item->getListener(); - mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; - BOOL passed = (0x1 << listener->getInventoryType() & mFilterOps.mFilterTypes || listener->getInventoryType() == LLInventoryType::IT_NONE) - && (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos) - && ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions) - && (listener->getCreationDate() >= earliest && listener->getCreationDate() <= mFilterOps.mMaxDate); - return passed; -} - -const std::string LLInventoryFilter::getFilterSubString(BOOL trim) -{ - return mFilterSubString; -} - -std::string::size_type LLInventoryFilter::getStringMatchOffset() const -{ - return mSubStringMatchOffset; -} - -// has user modified default filter params? -BOOL LLInventoryFilter::isNotDefault() -{ - return mFilterOps.mFilterTypes != mDefaultFilterOps.mFilterTypes - || mFilterSubString.size() - || mFilterOps.mPermissions != mDefaultFilterOps.mPermissions - || mFilterOps.mMinDate != mDefaultFilterOps.mMinDate - || mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate - || mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo; -} - -BOOL LLInventoryFilter::isActive() -{ - return mFilterOps.mFilterTypes != 0xffffffff - || mFilterSubString.size() - || mFilterOps.mPermissions != PERM_NONE - || mFilterOps.mMinDate != time_min() - || mFilterOps.mMaxDate != time_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 std::string& 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; - LLStringUtil::toUpper(mFilterSubString); - LLStringUtil::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(time_t min_date, time_t 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, time_max()); - setModified(); - } - if (!sl && isSinceLogoff()) - { - setDateRange(0, time_max()); - setModified(); - } -} - -BOOL LLInventoryFilter::isSinceLogoff() -{ - return (mFilterOps.mMinDate == (time_t)mLastLogoff) && - (mFilterOps.mMaxDate == time_max()); -} - -void LLInventoryFilter::setHoursAgo(U32 hours) -{ - if (mFilterOps.mHoursAgo != hours) - { - // *NOTE: need to cache last filter time, in case filter goes stale - BOOL less_restrictive = (mFilterOps.mMinDate == time_min() && mFilterOps.mMaxDate == time_max() && hours > mFilterOps.mHoursAgo); - BOOL more_restrictive = (mFilterOps.mMinDate == time_min() && mFilterOps.mMaxDate == time_max() && hours <= mFilterOps.mHoursAgo); - mFilterOps.mHoursAgo = hours; - mFilterOps.mMinDate = time_min(); - mFilterOps.mMaxDate = time_max(); - if (less_restrictive) - { - setModified(FILTER_LESS_RESTRICTIVE); - } - else if (more_restrictive) - { - setModified(FILTER_MORE_RESTRICTIVE); - } - else - { - 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 (isNotDefault()) - { - // if not keeping current filter results, update last valid as well - switch(mFilterBehavior) - { - case FILTER_RESTART: - mMustPassGeneration = mFilterGeneration; - mMinRequiredGeneration = mFilterGeneration; - break; - case FILTER_LESS_RESTRICTIVE: - mMustPassGeneration = mFilterGeneration; - break; - case FILTER_MORE_RESTRICTIVE: - mMinRequiredGeneration = mFilterGeneration; - // must have passed either current filter generation (meaningless, as it hasn't been run yet) - // or some older generation, so keep the value - mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration); - break; - default: - llerrs << "Bad filter behavior specified" << llendl; - } - } - else - { - // shortcut disabled filters to show everything immediately - mMinRequiredGeneration = 0; - mMustPassGeneration = S32_MAX; - } -} - -BOOL LLInventoryFilter::isFilterWith(LLInventoryType::EType t) -{ - return mFilterOps.mFilterTypes & (0x01 << t); -} - -std::string LLInventoryFilter::getFilterText() -{ - if (!mNeedTextRebuild) - { - return mFilterText; - } - - mNeedTextRebuild = FALSE; - std::string filtered_types; - std::string not_filtered_types; - BOOL filtered_by_type = FALSE; - BOOL filtered_by_all_types = TRUE; - S32 num_filter_types = 0; - mFilterText.clear(); - - if (isFilterWith(LLInventoryType::IT_ANIMATION)) - { - //filtered_types += " Animations,"; - filtered_types += LLTrans::getString("Animations"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Animations,"; - not_filtered_types += LLTrans::getString("Animations"); - - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_CALLINGCARD)) - { - //filtered_types += " Calling Cards,"; - filtered_types += LLTrans::getString("Calling Cards"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Calling Cards,"; - not_filtered_types += LLTrans::getString("Calling Cards"); - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_WEARABLE)) - { - //filtered_types += " Clothing,"; - filtered_types += LLTrans::getString("Clothing"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Clothing,"; - not_filtered_types += LLTrans::getString("Clothing"); - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_GESTURE)) - { - //filtered_types += " Gestures,"; - filtered_types += LLTrans::getString("Gestures"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Gestures,"; - not_filtered_types += LLTrans::getString("Gestures"); - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_LANDMARK)) - { - //filtered_types += " Landmarks,"; - filtered_types += LLTrans::getString("Landmarks"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Landmarks,"; - not_filtered_types += LLTrans::getString("Landmarks"); - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_NOTECARD)) - { - //filtered_types += " Notecards,"; - filtered_types += LLTrans::getString("Notecards"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Notecards,"; - not_filtered_types += LLTrans::getString("Notecards"); - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_OBJECT) && isFilterWith(LLInventoryType::IT_ATTACHMENT)) - { - //filtered_types += " Objects,"; - filtered_types += LLTrans::getString("Objects"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Objects,"; - not_filtered_types += LLTrans::getString("Objects"); - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_LSL)) - { - //filtered_types += " Scripts,"; - filtered_types += LLTrans::getString("Scripts"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Scripts,"; - not_filtered_types += LLTrans::getString("Scripts"); - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_SOUND)) - { - //filtered_types += " Sounds,"; - filtered_types += LLTrans::getString("Sounds"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Sounds,"; - not_filtered_types += LLTrans::getString("Sounds"); - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_TEXTURE)) - { - //filtered_types += " Textures,"; - filtered_types += LLTrans::getString("Textures"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Textures,"; - not_filtered_types += LLTrans::getString("Textures"); - filtered_by_all_types = FALSE; - } - - if (isFilterWith(LLInventoryType::IT_SNAPSHOT)) - { - //filtered_types += " Snapshots,"; - filtered_types += LLTrans::getString("Snapshots"); - filtered_by_type = TRUE; - num_filter_types++; - } - else - { - //not_filtered_types += " Snapshots,"; - not_filtered_types += LLTrans::getString("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 += LLTrans::getString("No Filters"); - mFilterText += not_filtered_types; - } - // remove the ',' at the end - mFilterText.erase(mFilterText.size() - 1, 1); - } - - if (isSinceLogoff()) - { - //mFilterText += " - Since Logoff"; - mFilterText += LLTrans::getString("Since Logoff"); - } - return mFilterText; -} - -void LLInventoryFilter::toLLSD(LLSD& data) -{ - data["filter_types"] = (LLSD::Integer)getFilterTypes(); - data["min_date"] = (LLSD::Integer)getMinDate(); - data["max_date"] = (LLSD::Integer)getMaxDate(); - data["hours_ago"] = (LLSD::Integer)getHoursAgo(); - data["show_folder_state"] = (LLSD::Integer)getShowFolderState(); - data["permissions"] = (LLSD::Integer)getFilterPermissions(); - data["substring"] = (LLSD::String)getFilterSubString(); - data["sort_order"] = (LLSD::Integer)getSortOrder(); - data["since_logoff"] = (LLSD::Boolean)isSinceLogoff(); -} - -void LLInventoryFilter::fromLLSD(LLSD& data) -{ - if(data.has("filter_types")) - { - setFilterTypes((U32)data["filter_types"].asInteger()); - } - - if(data.has("min_date") && data.has("max_date")) - { - setDateRange(data["min_date"].asInteger(), data["max_date"].asInteger()); - } - - if(data.has("hours_ago")) - { - setHoursAgo((U32)data["hours_ago"].asInteger()); - } - - if(data.has("show_folder_state")) - { - setShowFolderState((EFolderShow)data["show_folder_state"].asInteger()); - } - - if(data.has("permissions")) - { - setFilterPermissions((PermissionMask)data["permissions"].asInteger()); - } - - if(data.has("substring")) - { - setFilterSubString(std::string(data["substring"].asString())); - } - - if(data.has("sort_order")) - { - setSortOrder((U32)data["sort_order"].asInteger()); - } - - if(data.has("since_logoff")) - { - setDateRangeLastLogoff((bool)data["since_logoff"].asBoolean()); - } -} |