llfolderview.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "llfolderview.h"
00035 
00036 #include <algorithm>
00037 
00038 #include "llviewercontrol.h"
00039 #include "lldbstrings.h"
00040 #include "llfocusmgr.h"
00041 #include "llfontgl.h"
00042 #include "llgl.h" 
00043 #include "llinventory.h"
00044 
00045 #include "llcallbacklist.h"
00046 #include "llinventoryclipboard.h" // *TODO: remove this once hack below gone.
00047 #include "llinventoryview.h"// hacked in for the bonus context menu items.
00048 #include "llkeyboard.h"
00049 #include "lllineeditor.h"
00050 #include "llmenugl.h"
00051 #include "llresmgr.h"
00052 #include "llpreview.h"
00053 #include "llscrollcontainer.h" // hack to allow scrolling
00054 #include "lltooldraganddrop.h"
00055 #include "llui.h"
00056 #include "llviewerimage.h"
00057 #include "llviewerimagelist.h"
00058 #include "llviewerjointattachment.h"
00059 #include "llviewermenu.h"
00060 #include "llvieweruictrlfactory.h"
00061 #include "llviewerwindow.h"
00062 #include "llvoavatar.h"
00063 #include "llfloaterproperties.h"
00064 
00065 // RN: HACK
00066 // We need these because some of the code below relies on things like
00067 // gAgent root folder. Remove them once the abstraction leak is fixed.
00068 #include "llagent.h"
00069 #include "viewer.h"
00070 
00074 
00075 const S32 LEFT_PAD = 5;
00076 const S32 LEFT_INDENTATION = 13;
00077 const S32 ICON_PAD = 2;
00078 const S32 ICON_WIDTH = 16;
00079 const S32 TEXT_PAD = 1;
00080 const S32 ARROW_SIZE = 12;
00081 const S32 RENAME_WIDTH_PAD = 4;
00082 const S32 RENAME_HEIGHT_PAD = 6;
00083 const S32 AUTO_OPEN_STACK_DEPTH = 16;
00084 const S32 MIN_ITEM_WIDTH_VISIBLE = ICON_WIDTH + ICON_PAD + ARROW_SIZE + TEXT_PAD + /*first few characters*/ 40;
00085 const S32 MINIMUM_RENAMER_WIDTH = 80;
00086 const F32 FOLDER_CLOSE_TIME_CONSTANT = 0.02f;
00087 const F32 FOLDER_OPEN_TIME_CONSTANT = 0.03f;
00088 const S32 MAX_FOLDER_ITEM_OVERLAP = 2;
00089 
00090 F32 LLFolderView::sAutoOpenTime = 1.f;
00091 
00092 void delete_selected_item(void* user_data);
00093 void copy_selected_item(void* user_data);
00094 void open_selected_items(void* user_data);
00095 void properties_selected_items(void* user_data);
00096 void paste_items(void* user_data);
00097 void renamer_focus_lost( LLUICtrl* handler, void* user_data );
00098 
00102 
00103 // statics 
00104 const LLFontGL* LLFolderViewItem::sFont = NULL;
00105 const LLFontGL* LLFolderViewItem::sSmallFont = NULL;
00106 LLColor4 LLFolderViewItem::sFgColor;
00107 LLColor4 LLFolderViewItem::sHighlightBgColor;
00108 LLColor4 LLFolderViewItem::sHighlightFgColor;
00109 LLColor4 LLFolderViewItem::sFilterBGColor;
00110 LLColor4 LLFolderViewItem::sFilterTextColor;
00111 
00112 // Default constructor
00113 LLFolderViewItem::LLFolderViewItem( const LLString& name, LLViewerImage* icon,
00114                                                                    S32 creation_date,
00115                                                                    LLFolderView* root,
00116                                                                         LLFolderViewEventListener* listener ) :
00117         LLUICtrl( name, LLRect(0, 0, 0, 0), TRUE, NULL, NULL, FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_RIGHT),
00118         mLabel( name ),
00119         mLabelWidth(0),
00120         mCreationDate(creation_date),
00121         mParentFolder( NULL ),
00122         mListener( listener ),
00123         mIsSelected( FALSE ),
00124         mIsCurSelection( FALSE ),
00125         mSelectPending(FALSE),
00126         mLabelStyle( LLFontGL::NORMAL ),
00127         mHasVisibleChildren(FALSE),
00128         mIndentation(0),
00129         mNumDescendantsSelected(0),
00130         mFiltered(FALSE),
00131         mLastFilterGeneration(-1),
00132         mStringMatchOffset(LLString::npos),
00133         mControlLabelRotation(0.f),
00134         mRoot( root ),
00135         mDragAndDropTarget(FALSE)
00136 {
00137         setIcon(icon);
00138         if( !LLFolderViewItem::sFont )
00139         {
00140                 LLFolderViewItem::sFont = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
00141         }
00142 
00143         if (!LLFolderViewItem::sSmallFont)
00144         {
00145                 LLFolderViewItem::sSmallFont = gResMgr->getRes( LLFONT_SMALL );
00146         }
00147 
00148         // HACK: Can't be set above because gSavedSettings might not be constructed.
00149         LLFolderViewItem::sFgColor = gColors.getColor( "MenuItemEnabledColor" );
00150         LLFolderViewItem::sHighlightBgColor = gColors.getColor( "MenuItemHighlightBgColor" );
00151         LLFolderViewItem::sHighlightFgColor = gColors.getColor( "MenuItemHighlightFgColor" );
00152         LLFolderViewItem::sFilterBGColor = gColors.getColor( "FilterBackgroundColor" );
00153         LLFolderViewItem::sFilterTextColor = gColors.getColor( "FilterTextColor" );
00154 
00155         mArrowImage = gImageList.getImage(LLUUID(gViewerArt.getString("folder_arrow.tga")), MIPMAP_FALSE, TRUE); 
00156         mBoxImage = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE);
00157 
00158         refresh();
00159         setTabStop(FALSE);
00160 }
00161 
00162 // Destroys the object
00163 LLFolderViewItem::~LLFolderViewItem( void )
00164 {
00165         delete mListener;
00166         mListener = NULL;
00167         mArrowImage = NULL;
00168         mBoxImage = NULL;
00169 }
00170 
00171 LLFolderView* LLFolderViewItem::getRoot()
00172 {
00173         return mRoot;
00174 }
00175 
00176 // Returns true if this object is a child (or grandchild, etc.) of potential_ancestor.
00177 BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor )
00178 {
00179         LLFolderViewItem* root = this;
00180         while( root->mParentFolder )
00181         {
00182                 if( root->mParentFolder == potential_ancestor )
00183                 {
00184                         return TRUE;
00185                 }
00186                 root = root->mParentFolder;
00187         }
00188         return FALSE;
00189 }
00190 
00191 LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children)
00192 {
00193         if (!mParentFolder)
00194         {
00195                 return NULL;
00196         }
00197         
00198         LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children );
00199         while(itemp && !itemp->getVisible())
00200         {
00201                 LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children );
00202                 if (itemp == next_itemp) 
00203                 {
00204                         // hit last item
00205                         return itemp->getVisible() ? itemp : this;
00206                 }
00207                 itemp = next_itemp;
00208         }
00209 
00210         return itemp;
00211 }
00212 
00213 LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children)
00214 {
00215         if (!mParentFolder)
00216         {
00217                 return NULL;
00218         }
00219         
00220         LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children );
00221         while(itemp && !itemp->getVisible())
00222         {
00223                 LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children );
00224                 if (itemp == next_itemp) 
00225                 {
00226                         // hit first item
00227                         return itemp->getVisible() ? itemp : this;
00228                 }
00229                 itemp = next_itemp;
00230         }
00231 
00232         return itemp;
00233 }
00234 
00235 // is this item something we think we should be showing?
00236 // for example, if we haven't gotten around to filtering it yet, then the answer is yes
00237 // until we find out otherwise
00238 BOOL LLFolderViewItem::potentiallyVisible()
00239 {
00240         // we haven't been checked against min required filter
00241         // or we have and we passed
00242         return getLastFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration() || getFiltered();
00243 }
00244 
00245 BOOL LLFolderViewItem::getFiltered() 
00246 { 
00247         return mFiltered && mLastFilterGeneration >= mRoot->getFilter()->getMinRequiredGeneration(); 
00248 }
00249 
00250 BOOL LLFolderViewItem::getFiltered(S32 filter_generation) 
00251 {
00252         return mFiltered && mLastFilterGeneration >= filter_generation;
00253 }
00254 
00255 void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation)
00256 {
00257         mFiltered = filtered;
00258         mLastFilterGeneration = filter_generation;
00259 }
00260 
00261 void LLFolderViewItem::setIcon(LLViewerImage* icon)
00262 {
00263         mIcon = icon;
00264         if (mIcon)
00265         {
00266                 mIcon->setBoostLevel(LLViewerImage::BOOST_UI);
00267         }
00268 }
00269 
00270 // refresh information from the listener
00271 void LLFolderViewItem::refresh()
00272 {
00273         if(mListener)
00274         {
00275                 const char* label = mListener->getDisplayName().c_str();
00276                 mLabel = label ? label : "";
00277                 setIcon(mListener->getIcon());
00278                 U32 creation_date = mListener->getCreationDate();
00279                 if (mCreationDate != creation_date)
00280                 {
00281                         mCreationDate = mListener->getCreationDate();
00282                         dirtyFilter();
00283                 }
00284                 mLabelStyle = mListener->getLabelStyle();
00285                 mLabelSuffix = mListener->getLabelSuffix();
00286 
00287                 LLString searchable_label(mLabel);
00288                 searchable_label.append(mLabelSuffix);
00289                 LLString::toUpper(searchable_label);
00290 
00291                 if (mSearchableLabel.compare(searchable_label))
00292                 {
00293                         mSearchableLabel.assign(searchable_label);
00294                         dirtyFilter();
00295                         // some part of label has changed, so overall width has potentially changed
00296                         if (mParentFolder)
00297                         {
00298                                 mParentFolder->requestArrange();
00299                         }
00300                 }
00301 
00302                 S32 label_width = sFont->getWidth(mLabel);
00303                 if( mLabelSuffix.size() )   
00304                 {   
00305                         label_width += sFont->getWidth( mLabelSuffix );   
00306                 }   
00307 
00308                 mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + label_width; 
00309         }
00310 }
00311 
00312 void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
00313 {
00314         functor(mListener);
00315 }
00316 
00317 // This function is called when items are added or view filters change. It's
00318 // implemented here but called by derived classes when folding the
00319 // views.
00320 void LLFolderViewItem::filterFromRoot( void )
00321 {
00322         LLFolderViewItem* root = getRoot();
00323 
00324         root->filter(*((LLFolderView*)root)->getFilter());
00325 }
00326 
00327 // This function is called when the folder view is dirty. It's
00328 // implemented here but called by derived classes when folding the
00329 // views.
00330 void LLFolderViewItem::arrangeFromRoot()
00331 {
00332         LLFolderViewItem* root = getRoot();
00333 
00334         S32 height = 0;
00335         S32 width = 0;
00336         root->arrange( &width, &height, 0 );
00337 }
00338 
00339 // This function clears the currently selected item, and records the
00340 // specified selected item appropriately for display and use in the
00341 // UI. If open is TRUE, then folders are opened up along the way to
00342 // the selection.
00343 void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection,
00344                                                                                         BOOL open,                                      /* Flawfinder: ignore */
00345                                                                                         BOOL take_keyboard_focus)
00346 {
00347         getRoot()->setSelection(selection, open, take_keyboard_focus);          /* Flawfinder: ignore */
00348 }
00349 
00350 // helper function to change the selection from the root.
00351 void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected)
00352 {
00353         getRoot()->changeSelection(selection, selected);
00354 }
00355 
00356 void LLFolderViewItem::extendSelectionFromRoot(LLFolderViewItem* selection)
00357 {
00358         LLDynamicArray<LLFolderViewItem*> selected_items;
00359 
00360         getRoot()->extendSelection(selection, NULL, selected_items);
00361 }
00362 
00363 EWidgetType LLFolderViewItem::getWidgetType() const
00364 {
00365         return WIDGET_TYPE_FOLDER_ITEM;
00366 }
00367 
00368 LLString LLFolderViewItem::getWidgetTag() const
00369 {
00370         return LL_FOLDER_VIEW_ITEM_TAG;
00371 }
00372 
00373 EInventorySortGroup LLFolderViewItem::getSortGroup() 
00374 { 
00375         return SG_ITEM; 
00376 }
00377 
00378 // addToFolder() returns TRUE if it succeeds. FALSE otherwise
00379 BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
00380 {
00381         if (!folder)
00382         {
00383                 return FALSE;
00384         }
00385         mParentFolder = folder;
00386         root->addItemID(getListener()->getUUID(), this);
00387         return folder->addItem(this);
00388 }
00389 
00390 
00391 // Finds width and height of this object and it's children.  Also
00392 // makes sure that this view and it's children are the right size.
00393 S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation)
00394 {
00395         mIndentation = mParentFolder ? mParentFolder->getIndentation() + LEFT_INDENTATION : 0;
00396         *width = llmax(*width, mLabelWidth + mIndentation); 
00397         *height = getItemHeight();
00398         return *height;
00399 }
00400 
00401 S32 LLFolderViewItem::getItemHeight()
00402 {
00403         S32 icon_height = mIcon->getHeight();
00404         S32 label_height = llround(sFont->getLineHeight());
00405         return llmax( icon_height, label_height ) + ICON_PAD;
00406 }
00407 
00408 void LLFolderViewItem::filter( LLInventoryFilter& filter)
00409 {
00410         BOOL filtered = mListener && filter.check(this);
00411         
00412         // if our visibility will change as a result of this filter, then
00413         // we need to be rearranged in our parent folder
00414         if (getVisible() != filtered)
00415         {
00416                 if (mParentFolder)
00417                 {
00418                         mParentFolder->requestArrange();
00419                 }
00420         }
00421 
00422         setFiltered(filtered, filter.getCurrentGeneration());
00423         mStringMatchOffset = filter.getStringMatchOffset();
00424         filter.decrementFilterCount();
00425 
00426         if (getRoot()->getDebugFilters())
00427         {
00428                 mStatusText = llformat("%d", mLastFilterGeneration);
00429         }
00430 }
00431 
00432 void LLFolderViewItem::dirtyFilter()
00433 {
00434         mLastFilterGeneration = -1;
00435         // bubble up dirty flag all the way to root
00436         if (getParentFolder())
00437         {
00438                 getParentFolder()->setCompletedFilterGeneration(-1, TRUE);
00439         }
00440 }
00441 
00442 // *TODO: This can be optimized a lot by simply recording that it is
00443 // selected in the appropriate places, and assuming that set selection
00444 // means 'deselect' for a leaf item. Do this optimization after
00445 // multiple selection is implemented to make sure it all plays nice
00446 // together.
00447 BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL open, BOOL take_keyboard_focus)
00448 {
00449         if( selection == this )
00450         {
00451                 mIsSelected = TRUE;
00452                 if(mListener)
00453                 {
00454                         mListener->selectItem();
00455                 }
00456         }
00457         else
00458         {
00459                 mIsSelected = FALSE;
00460         }
00461         return mIsSelected;
00462 }
00463 
00464 BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selected)
00465 {
00466         if(selection == this && mIsSelected != selected)
00467         {
00468                 mIsSelected = selected;
00469                 if(mListener)
00470                 {
00471                         mListener->selectItem();
00472                 }
00473                 return TRUE;
00474         }
00475         return FALSE;
00476 }
00477 
00478 void LLFolderViewItem::recursiveDeselect(BOOL deselect_self)
00479 {
00480         if (mIsSelected && deselect_self)
00481         {
00482                 mIsSelected = FALSE;
00483 
00484                 // update ancestors' count of selected descendents
00485                 LLFolderViewFolder* parent_folder = getParentFolder();
00486                 while(parent_folder)
00487                 {
00488                         parent_folder->mNumDescendantsSelected--;
00489                         parent_folder = parent_folder->getParentFolder();
00490                 }
00491         }
00492 }
00493 
00494 
00495 BOOL LLFolderViewItem::isMovable()
00496 {
00497         if( mListener )
00498         {
00499                 return mListener->isItemMovable();
00500         }
00501         else
00502         {
00503                 return TRUE;
00504         }
00505 }
00506 
00507 BOOL LLFolderViewItem::isRemovable()
00508 {
00509         if( mListener )
00510         {
00511                 return mListener->isItemRemovable();
00512         }
00513         else
00514         {
00515                 return TRUE;
00516         }
00517 }
00518 
00519 void LLFolderViewItem::destroyView()
00520 {
00521         if (mParentFolder)
00522         {
00523                 // removeView deletes me
00524                 mParentFolder->removeView(this);
00525         }
00526 }
00527 
00528 // Call through to the viewed object and return true if it can be
00529 // removed.
00530 //BOOL LLFolderViewItem::removeRecursively(BOOL single_item)
00531 BOOL LLFolderViewItem::remove()
00532 {
00533         if(!isRemovable())
00534         {
00535                 return FALSE;
00536         }
00537         if(mListener)
00538         {
00539                 return mListener->removeItem();
00540         }
00541         return TRUE;
00542 }
00543 
00544 // Build an appropriate context menu for the item.
00545 void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags)
00546 {
00547         if(mListener)
00548         {
00549                 mListener->buildContextMenu(menu, flags);
00550         }
00551 }
00552 
00553 void LLFolderViewItem::open( void )             /* Flawfinder: ignore */
00554 {
00555         if( mListener )
00556         {
00557                 mListener->openItem();
00558         }
00559 }
00560 
00561 void LLFolderViewItem::preview( void )
00562 {
00563         if (mListener)
00564         {
00565                 mListener->previewItem();
00566         }
00567 }
00568 
00569 void LLFolderViewItem::rename(const LLString& new_name)
00570 {
00571         if( !new_name.empty() )
00572         {
00573                 mLabel = new_name.c_str();
00574                 if( mListener )
00575                 {
00576                         mListener->renameItem(new_name);
00577 
00578                         if(mParentFolder)
00579                         {
00580                                 mParentFolder->resort(this);
00581                         }
00582                 }
00583         }
00584 }
00585 
00586 const LLString& LLFolderViewItem::getSearchableLabel() const
00587 {
00588         return mSearchableLabel;
00589 }
00590 
00591 const LLString& LLFolderViewItem::getName( void ) const
00592 {
00593         if(mListener)
00594         {
00595                 return mListener->getName();
00596         }
00597         return mLabel;
00598 }
00599 
00600 LLFolderViewFolder* LLFolderViewItem::getParentFolder( void )
00601 {
00602         return mParentFolder;
00603 }
00604 
00605 LLFolderViewEventListener* LLFolderViewItem::getListener( void )
00606 {
00607         return mListener;
00608 }
00609 
00610 // LLView functionality
00611 BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask )
00612 {
00613         if(!mIsSelected)
00614         {
00615                 setSelectionFromRoot(this, FALSE);
00616         }
00617         make_ui_sound("UISndClick");
00618         return TRUE;
00619 }
00620 
00621 BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask )
00622 {
00623         // No handler needed for focus lost since this class has no
00624         // state that depends on it.
00625         gViewerWindow->setMouseCapture( this );
00626 
00627         if (!mIsSelected)
00628         {
00629                 if(mask & MASK_CONTROL)
00630                 {
00631                         changeSelectionFromRoot(this, !mIsSelected);
00632                 }
00633                 else if (mask & MASK_SHIFT)
00634                 {
00635                         extendSelectionFromRoot(this);
00636                 }
00637                 else
00638                 {
00639                         setSelectionFromRoot(this, FALSE);
00640                 }
00641                 make_ui_sound("UISndClick");
00642         }
00643         else
00644         {
00645                 mSelectPending = TRUE;
00646         }
00647 
00648         if( isMovable() )
00649         {
00650                 S32 screen_x;
00651                 S32 screen_y;
00652                 localPointToScreen(x, y, &screen_x, &screen_y );
00653                 gToolDragAndDrop->setDragStart( screen_x, screen_y );
00654         }
00655         return TRUE;
00656 }
00657 
00658 BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
00659 {
00660         if( hasMouseCapture() && isMovable() )
00661         {
00662                 S32 screen_x;
00663                 S32 screen_y;
00664                 localPointToScreen(x, y, &screen_x, &screen_y );
00665                 BOOL can_drag = TRUE;
00666                 if( gToolDragAndDrop->isOverThreshold( screen_x, screen_y ) )
00667                 {
00668                         LLFolderView* root = getRoot();
00669                         
00670                         if(root->getCurSelectedItem())
00671                         {
00672                                 LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD;
00673 
00674                                 // *TODO: push this into listener and remove
00675                                 // dependency on llagent
00676                                 if(mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gAgent.getInventoryRootID()))
00677                                 {
00678                                         src = LLToolDragAndDrop::SOURCE_AGENT;
00679                                 }
00680                                 else if (mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventoryLibraryRoot))
00681                                 {
00682                                         src = LLToolDragAndDrop::SOURCE_LIBRARY;
00683                                 }
00684 
00685                                 can_drag = root->startDrag(src);
00686                                 if (can_drag)
00687                                 {
00688                                         // if (mListener) mListener->startDrag();
00689                                         // RN: when starting drag and drop, clear out last auto-open
00690                                         root->autoOpenTest(NULL);
00691                                         root->setShowSelectionContext(TRUE);
00692 
00693                                         // Release keyboard focus, so that if stuff is dropped into the
00694                                         // world, pressing the delete key won't blow away the inventory
00695                                         // item.
00696                                         gViewerWindow->setKeyboardFocus(NULL, NULL);
00697 
00698                                         return gToolDragAndDrop->handleHover( x, y, mask );
00699                                 }
00700                         }
00701                 }
00702 
00703                 if (can_drag)
00704                 {
00705                         gViewerWindow->setCursor(UI_CURSOR_ARROW);
00706                 }
00707                 else
00708                 {
00709                         gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
00710                 }
00711                 return TRUE;
00712         }
00713         else
00714         {
00715                 getRoot()->setShowSelectionContext(FALSE);
00716                 gViewerWindow->setCursor(UI_CURSOR_ARROW);
00717                 // let parent handle this then...
00718                 return FALSE;
00719         }
00720 }
00721 
00722 
00723 BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask )
00724 {
00725         preview();
00726         return TRUE;
00727 }
00728 
00729 BOOL LLFolderViewItem::handleScrollWheel(S32 x, S32 y, S32 clicks)
00730 {
00731         if (getParent())
00732         {
00733                 return getParent()->handleScrollWheel(x, y, clicks);
00734         }
00735         return FALSE;
00736 }
00737 
00738 BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )
00739 {
00740         // if mouse hasn't moved since mouse down...
00741         if ( pointInView(x, y) && mSelectPending )
00742         {
00743                 //...then select
00744                 if(mask & MASK_CONTROL)
00745                 {
00746                         changeSelectionFromRoot(this, !mIsSelected);
00747                 }
00748                 else if (mask & MASK_SHIFT)
00749                 {
00750                         extendSelectionFromRoot(this);
00751                 }
00752                 else
00753                 {
00754                         setSelectionFromRoot(this, FALSE);
00755                 }
00756         }
00757         
00758         mSelectPending = FALSE;
00759 
00760         if( hasMouseCapture() )
00761         {
00762                 getRoot()->setShowSelectionContext(FALSE);
00763                 gViewerWindow->setMouseCapture( NULL );
00764         }
00765         return TRUE;
00766 }
00767 
00768 BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
00769                                                                                  EDragAndDropType cargo_type,
00770                                                                                  void* cargo_data,
00771                                                                                  EAcceptance* accept,
00772                                                                                  LLString& tooltip_msg)
00773 {
00774         BOOL accepted = FALSE;
00775         BOOL handled = FALSE;
00776         if(mListener)
00777         {
00778                 accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data);
00779                 handled = accepted;
00780                 if (accepted)
00781                 {
00782                         mDragAndDropTarget = TRUE;
00783                         *accept = ACCEPT_YES_MULTI;
00784                 }
00785                 else
00786                 {
00787                         *accept = ACCEPT_NO;
00788                 }
00789         }
00790         if(mParentFolder && !handled)
00791         {
00792                 handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg);
00793         }
00794         if (handled)
00795         {
00796                 lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl;
00797         }
00798 
00799         return handled;
00800 }
00801 
00802 
00803 void LLFolderViewItem::draw()
00804 {
00805         bool possibly_has_children = false;
00806         bool up_to_date = mListener && mListener->isUpToDate();
00807         if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter...
00808                 (!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter)
00809         {
00810                 possibly_has_children = true;
00811         }
00812         if(/*mControlLabel[0] != '\0' && */possibly_has_children)
00813         {
00814                 LLGLSTexture gls_texture;
00815                 if (mArrowImage)
00816                 {
00817                         gl_draw_scaled_rotated_image(mIndentation, mRect.getHeight() - ARROW_SIZE - TEXT_PAD,
00818                                 ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, mArrowImage, sFgColor);
00819                 }
00820         }
00821 
00822         F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation);
00823 
00824         // If we have keyboard focus, draw selection filled
00825         BOOL show_context = getRoot()->getShowSelectionContext();
00826         BOOL filled = show_context || (gFocusMgr.getKeyboardFocus() == getRoot());
00827 
00828         // always render "current" item, only render other selected items if
00829         // mShowSingleSelection is FALSE
00830         if( mIsSelected )
00831         {
00832                 LLGLSNoTexture gls_no_texture;
00833                 LLColor4 bg_color = sHighlightBgColor;
00834                 //const S32 TRAILING_PAD = 5;  // It just looks better with this.
00835                 if (!mIsCurSelection)
00836                 {
00837                         // do time-based fade of extra objects
00838                         F32 fade_time = getRoot()->getSelectionFadeElapsedTime();
00839                         if (getRoot()->getShowSingleSelection())
00840                         {
00841                                 // fading out
00842                                 bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f);
00843                         }
00844                         else
00845                         {
00846                                 // fading in
00847                                 bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);
00848                         }
00849                 }
00850 
00851                 gl_rect_2d(
00852                         0, 
00853                         mRect.getHeight(), 
00854                         mRect.getWidth() - 2,
00855                         llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
00856                         bg_color, filled);
00857                 if (mIsCurSelection)
00858                 {
00859                         gl_rect_2d(
00860                                 0, 
00861                                 mRect.getHeight(), 
00862                                 mRect.getWidth() - 2,
00863                                 llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
00864                                 sHighlightFgColor, FALSE);
00865                 }
00866                 if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2)
00867                 {
00868                         gl_rect_2d(
00869                                 0, 
00870                                 llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, 
00871                                 mRect.getWidth() - 2,
00872                                 2,
00873                                 sHighlightFgColor, FALSE);
00874                         if (show_context)
00875                         {
00876                                 gl_rect_2d(
00877                                         0, 
00878                                         llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, 
00879                                         mRect.getWidth() - 2,
00880                                         2,
00881                                         sHighlightBgColor, TRUE);
00882                         }
00883                 }
00884         }
00885         if (mDragAndDropTarget)
00886         {
00887                 LLGLSNoTexture gls_no_texture;
00888                 gl_rect_2d(
00889                         0, 
00890                         mRect.getHeight(), 
00891                         mRect.getWidth() - 2,
00892                         llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
00893                         sHighlightBgColor, FALSE);
00894 
00895                 if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2)
00896                 {
00897                         gl_rect_2d(
00898                                 0, 
00899                                 llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, 
00900                                 mRect.getWidth() - 2,
00901                                 2,
00902                                 sHighlightBgColor, FALSE);
00903                 }
00904                 mDragAndDropTarget = FALSE;
00905         }
00906 
00907 
00908         if(mIcon)
00909         {
00910                 gl_draw_image(mIndentation + ARROW_SIZE + TEXT_PAD, mRect.getHeight() - mIcon->getHeight(), mIcon);
00911                 mIcon->addTextureStats( (F32)(mIcon->getWidth() * mIcon->getHeight()));
00912         }
00913 
00914         if (!mLabel.empty())
00915         {
00916                 // highlight filtered text
00917                 BOOL debug_filters = getRoot()->getDebugFilters();
00918                 LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor );
00919                 F32 right_x;
00920                 F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD;
00921 
00922                 if (debug_filters)
00923                 {
00924                         if (!getFiltered() && !possibly_has_children)
00925                         {
00926                                 color.mV[VALPHA] *= 0.5f;
00927                         }
00928                         
00929                         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);
00930                         sSmallFont->renderUTF8(mStatusText, 0, text_left, y, filter_color,
00931                                                         LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL,
00932                                                         S32_MAX, S32_MAX, &right_x, FALSE );
00933                         text_left = right_x;
00934                 }
00935 
00936                 sFont->renderUTF8( mLabel, 0, text_left, y, color,
00937                                                         LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
00938                                                         S32_MAX, S32_MAX, &right_x, FALSE );
00939                 if (!mLabelSuffix.empty())
00940                 {
00941                         sFont->renderUTF8( mLabelSuffix, 0, right_x, y, LLColor4(0.75f, 0.85f, 0.85f, 1.f),
00942                                                                 LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
00943                                                                 S32_MAX, S32_MAX, &right_x, FALSE );
00944                 }
00945 
00946                 if (mBoxImage.notNull() && mStringMatchOffset != LLString::npos)
00947                 {
00948                         // don't draw backgrounds for zero-length strings
00949                         S32 filter_string_length = mRoot->getFilterSubString().size();
00950                         if (filter_string_length > 0)
00951                         {
00952                                 LLString combined_string = mLabel + mLabelSuffix;
00953                                 S32 left = llround(text_left) + sFont->getWidth(combined_string, 0, mStringMatchOffset) - 1;
00954                                 S32 right = left + sFont->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2;
00955                                 S32 bottom = llfloor(mRect.getHeight() - sFont->getLineHeight() - 3);
00956                                 S32 top = mRect.getHeight();
00957 
00958                                 LLViewerImage::bindTexture(mBoxImage);
00959                                 glColor4fv(sFilterBGColor.mV);
00960                                 gl_segmented_rect_2d_tex(left, top, right, bottom, mBoxImage->getWidth(), mBoxImage->getHeight(), 16);
00961                                 F32 match_string_left = text_left + sFont->getWidthF32(combined_string, 0, mStringMatchOffset);
00962                                 F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD;
00963                                 sFont->renderUTF8( combined_string, mStringMatchOffset, match_string_left, y,
00964                                                                 sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
00965                                                                 filter_string_length, S32_MAX, &right_x, FALSE );
00966                         }
00967                 }
00968         }
00969 
00970         if( sDebugRects )
00971         {
00972                 drawDebugRect();
00973         }
00974 }
00975 
00976 
00980 
00981 // Default constructor
00982 LLFolderViewFolder::LLFolderViewFolder( const LLString& name, LLViewerImage* icon,
00983                                                                                 LLFolderView* root,
00984                                                                                 LLFolderViewEventListener* listener ): 
00985         LLFolderViewItem( name, icon, 0, root, listener ),      // 0 = no create time
00986         mIsOpen(FALSE),
00987         mExpanderHighlighted(FALSE),
00988         mCurHeight(0.f),
00989         mTargetHeight(0.f),
00990         mAutoOpenCountdown(0.f),
00991         mSubtreeCreationDate(0),
00992         mAmTrash(LLFolderViewFolder::UNKNOWN),
00993         mLastArrangeGeneration( -1 ),
00994         mLastCalculatedWidth(0),
00995         mCompletedFilterGeneration(-1),
00996         mMostFilteredDescendantGeneration(-1)
00997 {
00998         mType = "(folder)";
00999 
01000         //mItems.setInsertBefore( &sort_item_name );
01001         //mFolders.setInsertBefore( &folder_insert_before );
01002 }
01003 
01004 // Destroys the object
01005 LLFolderViewFolder::~LLFolderViewFolder( void )
01006 {
01007         // The LLView base class takes care of object destruction. make sure that we
01008         // don't have mouse or keyboard focus
01009         gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
01010 
01011         //mItems.reset();
01012         //mItems.removeAllNodes();
01013         //mFolders.removeAllNodes();
01014 }
01015 
01016 EWidgetType LLFolderViewFolder::getWidgetType() const
01017 {
01018         return WIDGET_TYPE_FOLDER;
01019 }
01020 
01021 LLString LLFolderViewFolder::getWidgetTag() const
01022 {
01023         return LL_FOLDER_VIEW_FOLDER_TAG;
01024 }
01025 
01026 // addToFolder() returns TRUE if it succeeds. FALSE otherwise
01027 BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
01028 {
01029         if (!folder)
01030         {
01031                 return FALSE;
01032         }
01033         mParentFolder = folder;
01034         root->addItemID(getListener()->getUUID(), this);
01035         return folder->addFolder(this);
01036 }
01037 
01038 // Finds width and height of this object and it's children.  Also
01039 // makes sure that this view and it's children are the right size.
01040 S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation)
01041 {
01042         mHasVisibleChildren = hasFilteredDescendants(filter_generation);
01043         
01044         LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState();
01045 
01046         // calculate height as a single item (without any children), and reshapes rectangle to match
01047         LLFolderViewItem::arrange( width, height, filter_generation );
01048 
01049         // clamp existing animated height so as to never get smaller than a single item
01050         mCurHeight = llmax((F32)*height, mCurHeight);
01051 
01052         // initialize running height value as height of single item in case we have no children
01053         *height = getItemHeight();
01054         F32 running_height = (F32)*height;
01055         F32 target_height = (F32)*height;
01056 
01057         // are my children visible?
01058         if (needsArrange())
01059         {
01060                 // set last arrange generation first, in case children are animating
01061                 // and need to be arranged again
01062                 mLastArrangeGeneration = mRoot->getArrangeGeneration();
01063                 if (mIsOpen)
01064                 {
01065                         // Add sizes of children
01066                         S32 parent_item_height = mRect.getHeight();
01067 
01068                         folders_t::iterator fit = mFolders.begin();
01069                         folders_t::iterator fend = mFolders.end();
01070                         for(; fit < fend; ++fit)
01071                         {
01072                                 LLFolderViewFolder* folderp = (*fit);
01073                                 if (getRoot()->getDebugFilters())
01074                                 {
01075                                         folderp->setVisible(TRUE);
01076                                 }
01077                                 else
01078                                 {
01079                                         folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
01080                                                                                 (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
01081                                 }
01082 
01083                                 if (folderp->getVisible())
01084                                 {
01085                                         S32 child_width = *width;
01086                                         S32 child_height = 0;
01087                                         S32 child_top = parent_item_height - llround(running_height);
01088 
01089                                         target_height += folderp->arrange( &child_width, &child_height, filter_generation );
01090 
01091                                         running_height += (F32)child_height;
01092                                         *width = llmax(*width, child_width);
01093                                         folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() );
01094                                 }
01095                         }
01096                         items_t::iterator iit = mItems.begin();
01097                         items_t::iterator iend = mItems.end();
01098                         for(;iit < iend; ++iit)
01099                         {
01100                                 LLFolderViewItem* itemp = (*iit);
01101                                 if (getRoot()->getDebugFilters())
01102                                 {
01103                                         itemp->setVisible(TRUE);
01104                                 }
01105                                 else
01106                                 {
01107                                         itemp->setVisible(itemp->getFiltered(filter_generation));
01108                                 }
01109 
01110                                 if (itemp->getVisible())
01111                                 {
01112                                         S32 child_width = *width;
01113                                         S32 child_height = 0;
01114                                         S32 child_top = parent_item_height - llround(running_height);
01115 
01116                                         target_height += itemp->arrange( &child_width, &child_height, filter_generation );
01117                                         // don't change width, as this item is as wide as its parent folder by construction
01118                                         itemp->reshape( itemp->getRect().getWidth(), child_height);
01119 
01120                                         running_height += (F32)child_height;
01121                                         *width = llmax(*width, child_width);
01122                                         itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() );
01123                                 }
01124                         }
01125                 }
01126 
01127                 mTargetHeight = target_height;
01128                 // cache this width so next time we can just return it
01129                 mLastCalculatedWidth = *width;
01130         }
01131         else
01132         {
01133                 // just use existing width
01134                 *width = mLastCalculatedWidth;
01135         }
01136 
01137         // animate current height towards target height
01138         if (llabs(mCurHeight - mTargetHeight) > 1.f)
01139         {
01140                 mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT));
01141                 
01142                 requestArrange();
01143 
01144                 // hide child elements that fall out of current animated height
01145                 for (folders_t::iterator iter = mFolders.begin();
01146                          iter != mFolders.end();)
01147                 {
01148                         folders_t::iterator fit = iter++;
01149                         // number of pixels that bottom of folder label is from top of parent folder
01150                         if (mRect.getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() 
01151                                 > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
01152                         {
01153                                 // hide if beyond current folder height
01154                                 (*fit)->setVisible(FALSE);
01155                         }
01156                 }
01157 
01158                 for (items_t::iterator iter = mItems.begin();
01159                          iter != mItems.end();)
01160                 {
01161                         items_t::iterator iit = iter++;
01162                         // number of pixels that bottom of item label is from top of parent folder
01163                         if (mRect.getHeight() - (*iit)->getRect().mBottom
01164                                 > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
01165                         {
01166                                 (*iit)->setVisible(FALSE);
01167                         }
01168                 }
01169         }
01170         else
01171         {
01172                 mCurHeight = mTargetHeight;
01173         }
01174 
01175         // don't change width as this item is already as wide as its parent folder
01176         reshape(mRect.getWidth(),llround(mCurHeight));
01177 
01178         // pass current height value back to parent
01179         *height = llround(mCurHeight);
01180 
01181         return llround(mTargetHeight);
01182 }
01183 
01184 BOOL LLFolderViewFolder::needsArrange()
01185 {
01186         return mLastArrangeGeneration < mRoot->getArrangeGeneration(); 
01187 }
01188 
01189 void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up)
01190 {
01191         mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation);
01192         mCompletedFilterGeneration = generation;
01193         // only aggregate up if we are a lower (older) value
01194         if (recurse_up && mParentFolder && generation < mParentFolder->getCompletedFilterGeneration())
01195         {
01196                 mParentFolder->setCompletedFilterGeneration(generation, TRUE);
01197         }
01198 }
01199 
01200 void LLFolderViewFolder::filter( LLInventoryFilter& filter)
01201 {
01202         S32 filter_generation = filter.getCurrentGeneration();
01203         // if failed to pass filter newer than must_pass_generation
01204         // you will automatically fail this time, so we only
01205         // check against items that have passed the filter
01206         S32 must_pass_generation = filter.getMustPassGeneration();
01207 
01208         // if we have already been filtered against this generation, skip out
01209         if (getCompletedFilterGeneration() >= filter_generation)
01210         {
01211                 return;
01212         }
01213 
01214         // filter folder itself
01215         if (getLastFilterGeneration() < filter_generation)
01216         {
01217                 if (getLastFilterGeneration() >= must_pass_generation &&                // folder has been compared to a valid precursor filter
01218                         !mFiltered)                                                                                                     // and did not pass the filter
01219                 {
01220                         // go ahead and flag this folder as done
01221                         mLastFilterGeneration = filter_generation;                      
01222                 }
01223                 else
01224                 {
01225                         // filter self only on first pass through
01226                         LLFolderViewItem::filter( filter );
01227                 }
01228         }
01229 
01230         if (getRoot()->getDebugFilters())
01231         {
01232                 mStatusText = llformat("%d", mLastFilterGeneration);
01233                 mStatusText += llformat("(%d)", mCompletedFilterGeneration);
01234                 mStatusText += llformat("+%d", mMostFilteredDescendantGeneration);
01235         }
01236 
01237         // all descendants have been filtered later than must pass generation
01238         // but none passed
01239         if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation))
01240         {
01241                 // don't traverse children if we've already filtered them since must_pass_generation
01242                 // and came back with nothing
01243                 return;
01244         }
01245 
01246         // we entered here with at least one filter iteration left
01247         // check to see if we have any more before continuing on to children
01248         if (filter.getFilterCount() < 0)
01249         {
01250                 return;
01251         }
01252 
01253         // when applying a filter, matching folders get their contents downloaded first
01254         if (filter.isNotDefault() && getFiltered(filter.getMinRequiredGeneration()) && (mListener && !gInventory.isCategoryComplete(mListener->getUUID())))
01255         {
01256                 gInventory.startBackgroundFetch(mListener->getUUID());
01257         }
01258 
01259         // now query children
01260         for (folders_t::iterator iter = mFolders.begin();
01261                  iter != mFolders.end();)
01262         {
01263                 folders_t::iterator fit = iter++;
01264                 // have we run out of iterations this frame?
01265                 if (filter.getFilterCount() < 0)
01266                 {
01267                         break;
01268                 }
01269 
01270                 // mMostFilteredDescendantGeneration might have been reset
01271                 // in which case we need to update it even for folders that
01272                 // don't need to be filtered anymore
01273                 if ((*fit)->getCompletedFilterGeneration() >= filter_generation)
01274                 {
01275                         // track latest generation to pass any child items
01276                         if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter.getMinRequiredGeneration()))
01277                         {
01278                                 mMostFilteredDescendantGeneration = filter_generation;
01279                                 if (mRoot->needsAutoSelect())
01280                                 {
01281                                         (*fit)->setOpenArrangeRecursively(TRUE);
01282                                 }
01283                         }
01284                         // just skip it, it has already been filtered
01285                         continue;
01286                 }
01287 
01288                 // update this folders filter status (and children)
01289                 (*fit)->filter( filter );
01290                 
01291                 // track latest generation to pass any child items
01292                 if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter_generation))
01293                 {
01294                         mMostFilteredDescendantGeneration = filter_generation;
01295                         if (mRoot->needsAutoSelect())
01296                         {
01297                                 (*fit)->setOpenArrangeRecursively(TRUE);
01298                         }
01299                 }
01300         }
01301 
01302         for (items_t::iterator iter = mItems.begin();
01303                  iter != mItems.end();)
01304         {
01305                 items_t::iterator iit = iter++;
01306                 if (filter.getFilterCount() < 0)
01307                 {
01308                         break;
01309                 }
01310                 if ((*iit)->getLastFilterGeneration() >= filter_generation)
01311                 {
01312                         if ((*iit)->getFiltered())
01313                         {
01314                                 mMostFilteredDescendantGeneration = filter_generation;
01315                         }
01316                         continue;
01317                 }
01318 
01319                 if ((*iit)->getLastFilterGeneration() >= must_pass_generation && 
01320                         !(*iit)->getFiltered(must_pass_generation))
01321                 {
01322                         // failed to pass an earlier filter that was a subset of the current one
01323                         // go ahead and flag this item as done
01324                         (*iit)->setFiltered(FALSE, filter_generation);
01325                         continue;
01326                 }
01327 
01328                 (*iit)->filter( filter );
01329                 
01330                 if ((*iit)->getFiltered(filter.getMinRequiredGeneration()))
01331                 {
01332                         mMostFilteredDescendantGeneration = filter_generation;
01333                 }
01334         }
01335         
01336         // if we didn't use all filter iterations
01337         // that means we filtered all of our descendants
01338         // instead of exhausting the filter count for this frame
01339         if (filter.getFilterCount() > 0)
01340         {
01341                 // flag this folder as having completed filter pass for all descendants
01342                 setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/);
01343         }
01344 }
01345 
01346 void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation)
01347 {
01348         // if this folder is now filtered, but wasn't before
01349         // (it just passed)
01350         if (filtered && !mFiltered)
01351         {
01352                 // reset current height, because last time we drew it
01353                 // it might have had more visible items than now
01354                 mCurHeight = 0.f;
01355         }
01356 
01357         LLFolderViewItem::setFiltered(filtered, filter_generation);
01358 }
01359 
01360 void LLFolderViewFolder::dirtyFilter()
01361 {
01362         // we're a folder, so invalidate our completed generation
01363         setCompletedFilterGeneration(-1, FALSE);
01364         LLFolderViewItem::dirtyFilter();
01365 }
01366 
01367 BOOL LLFolderViewFolder::hasFilteredDescendants()
01368 {
01369         return mMostFilteredDescendantGeneration >= mRoot->getFilter()->getCurrentGeneration();
01370 }
01371 
01372 // Passes selection information on to children and record selection
01373 // information if necessary.
01374 BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL open,           /* Flawfinder: ignore */
01375                                                                           BOOL take_keyboard_focus)
01376 {
01377         BOOL rv = FALSE;
01378         if( selection == this )
01379         {
01380                 mIsSelected = TRUE;
01381                 if(mListener)
01382                 {
01383                         mListener->selectItem();
01384                 }
01385                 rv = TRUE;
01386         }
01387         else
01388         {
01389                 mIsSelected = FALSE;
01390                 rv = FALSE;
01391         }
01392         BOOL child_selected = FALSE;
01393         
01394         for (folders_t::iterator iter = mFolders.begin();
01395                  iter != mFolders.end();)
01396         {
01397                 folders_t::iterator fit = iter++;
01398                 if((*fit)->setSelection(selection, open, take_keyboard_focus))          /* Flawfinder: ignore */
01399                 {
01400                         rv = TRUE;
01401                         child_selected = TRUE;
01402                         mNumDescendantsSelected++;
01403                 }
01404         }
01405         for (items_t::iterator iter = mItems.begin();
01406                  iter != mItems.end();)
01407         {
01408                 items_t::iterator iit = iter++;
01409                 if((*iit)->setSelection(selection, open, take_keyboard_focus))          /* Flawfinder: ignore */
01410                 {
01411                         rv = TRUE;
01412                         child_selected = TRUE;
01413                         mNumDescendantsSelected++;
01414                 }
01415         }
01416         if(open && child_selected)              /* Flawfinder: ignore */
01417         {
01418                 setOpenArrangeRecursively(TRUE);
01419         }
01420         return rv;
01421 }
01422 
01423 // This method is used to change the selection of an item. If
01424 // selection is 'this', then note selection as true. Returns TRUE
01425 // if this or a child is now selected.
01426 BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection,
01427                                                                                  BOOL selected)
01428 {
01429         BOOL rv = FALSE;
01430         if(selection == this)
01431         {
01432                 mIsSelected = selected;
01433                 if(mListener && selected)
01434                 {
01435                         mListener->selectItem();
01436                 }
01437                 rv = TRUE;
01438         }
01439 
01440         for (folders_t::iterator iter = mFolders.begin();
01441                  iter != mFolders.end();)
01442         {
01443                 folders_t::iterator fit = iter++;
01444                 if((*fit)->changeSelection(selection, selected))
01445                 {
01446                         if (selected)
01447                         {
01448                                 mNumDescendantsSelected++;
01449                         }
01450                         else
01451                         {
01452                                 mNumDescendantsSelected--;
01453                         }
01454                         rv = TRUE;
01455                 }
01456         }
01457         for (items_t::iterator iter = mItems.begin();
01458                  iter != mItems.end();)
01459         {
01460                 items_t::iterator iit = iter++;
01461                 if((*iit)->changeSelection(selection, selected))
01462                 {
01463                         if (selected)
01464                         {
01465                                 mNumDescendantsSelected++;
01466                         }
01467                         else
01468                         {
01469                                 mNumDescendantsSelected--;
01470                         }
01471                         rv = TRUE;
01472                 }
01473         }
01474         return rv;
01475 }
01476 
01477 S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& selected_items)
01478 {
01479         S32 num_selected = 0;
01480 
01481         // pass on to child folders first
01482         for (folders_t::iterator iter = mFolders.begin();
01483                  iter != mFolders.end();)
01484         {
01485                 folders_t::iterator fit = iter++;
01486                 num_selected += (*fit)->extendSelection(selection, last_selected, selected_items);
01487                 mNumDescendantsSelected += num_selected;
01488         }
01489 
01490         // handle selection of our immediate children...
01491         BOOL reverse_select = FALSE;
01492         BOOL found_last_selected = FALSE;
01493         BOOL found_selection = FALSE;
01494         LLDynamicArray<LLFolderViewItem*> items_to_select;
01495         LLFolderViewItem* item;
01496 
01497         //...folders first...
01498         for (folders_t::iterator iter = mFolders.begin();
01499                  iter != mFolders.end();)
01500         {
01501                 folders_t::iterator fit = iter++;
01502                 item = (*fit);
01503                 if(item == selection)
01504                 {
01505                         found_selection = TRUE;
01506                 }
01507                 else if (item == last_selected)
01508                 {
01509                         found_last_selected = TRUE;
01510                         if (found_selection)
01511                         {
01512                                 reverse_select = TRUE;
01513                         }
01514                 }
01515 
01516                 if (found_selection || found_last_selected)
01517                 {
01518                         // deselect currently selected items so they can be pushed back on queue
01519                         if (item->isSelected())
01520                         {
01521                                 item->changeSelection(item, FALSE);
01522                         }
01523                         items_to_select.put(item);
01524                 }
01525 
01526                 if (found_selection && found_last_selected)
01527                 {
01528                         break;
01529                 }               
01530         }
01531 
01532         if (!(found_selection && found_last_selected))
01533         {
01534                 //,,,then items
01535                 for (items_t::iterator iter = mItems.begin();
01536                          iter != mItems.end();)
01537                 {
01538                         items_t::iterator iit = iter++;
01539                         item = (*iit);
01540                         if(item == selection)
01541                         {
01542                                 found_selection = TRUE;
01543                         }
01544                         else if (item == last_selected)
01545                         {
01546                                 found_last_selected = TRUE;
01547                                 if (found_selection)
01548                                 {
01549                                         reverse_select = TRUE;
01550                                 }
01551                         }
01552 
01553                         if (found_selection || found_last_selected)
01554                         {
01555                                 // deselect currently selected items so they can be pushed back on queue
01556                                 if (item->isSelected())
01557                                 {
01558                                         item->changeSelection(item, FALSE);
01559                                 }
01560                                 items_to_select.put(item);
01561                         }
01562 
01563                         if (found_selection && found_last_selected)
01564                         {
01565                                 break;
01566                         }
01567                 }
01568         }
01569 
01570         if (found_last_selected && found_selection)
01571         {
01572                 // we have a complete selection inside this folder
01573                 for (S32 index = reverse_select ? items_to_select.getLength() - 1 : 0; 
01574                         reverse_select ? index >= 0 : index < items_to_select.getLength(); reverse_select ? index-- : index++)
01575                 {
01576                         LLFolderViewItem* item = items_to_select[index];
01577                         if (item->changeSelection(item, TRUE))
01578                         {
01579                                 selected_items.put(item);
01580                                 mNumDescendantsSelected++;
01581                                 num_selected++;
01582                         }
01583                 }
01584         }
01585         else if (found_selection)
01586         {
01587                 // last selection was not in this folder....go ahead and select just the new item
01588                 if (selection->changeSelection(selection, TRUE))
01589                 {
01590                         selected_items.put(selection);
01591                         mNumDescendantsSelected++;
01592                         num_selected++;
01593                 }
01594         }
01595 
01596         return num_selected;
01597 }
01598 
01599 void LLFolderViewFolder::recursiveDeselect(BOOL deselect_self)
01600 {
01601         // make sure we don't have negative values
01602         llassert(mNumDescendantsSelected >= 0);
01603 
01604         if (mIsSelected && deselect_self)
01605         {
01606                 mIsSelected = FALSE;
01607 
01608                 // update ancestors' count of selected descendents
01609                 LLFolderViewFolder* parent_folder = getParentFolder();
01610                 while(parent_folder)
01611                 {
01612                         parent_folder->mNumDescendantsSelected--;
01613                         parent_folder = parent_folder->getParentFolder();
01614                 }
01615         }
01616 
01617         if (0 == mNumDescendantsSelected)
01618         {
01619                 return;
01620         }
01621 
01622         for (items_t::iterator iter = mItems.begin();
01623                  iter != mItems.end();)
01624         {
01625                 items_t::iterator iit = iter++;
01626                 LLFolderViewItem* item = (*iit);
01627                 item->recursiveDeselect(TRUE);
01628         }
01629 
01630         for (folders_t::iterator iter = mFolders.begin();
01631                  iter != mFolders.end();)
01632         {
01633                 folders_t::iterator fit = iter++;
01634                 LLFolderViewFolder* folder = (*fit);
01635                 folder->recursiveDeselect(TRUE);
01636         }
01637 
01638 }
01639 
01640 void LLFolderViewFolder::destroyView()
01641 {
01642         for (items_t::iterator iter = mItems.begin();
01643                  iter != mItems.end();)
01644         {
01645                 items_t::iterator iit = iter++;
01646                 LLFolderViewItem* item = (*iit);
01647                 getRoot()->removeItemID(item->getListener()->getUUID());
01648         }
01649 
01650         std::for_each(mItems.begin(), mItems.end(), DeletePointer());
01651         mItems.clear();
01652 
01653         while (!mFolders.empty())
01654         {
01655                 LLFolderViewFolder *folderp = mFolders.back();
01656                 folderp->destroyView();
01657         }
01658 
01659         mFolders.clear();
01660 
01661         deleteAllChildren();
01662         
01663         if (mParentFolder)
01664         {
01665                 mParentFolder->removeView(this);
01666         }
01667 }
01668 
01669 // remove the specified item (and any children) if possible. Return
01670 // TRUE if the item was deleted.
01671 BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item)
01672 {
01673         if(item->remove())
01674         {
01675                 //RN: this seem unneccessary as remove() moves to trash
01676                 //removeView(item);
01677                 return TRUE;
01678         }
01679         return FALSE;
01680 }
01681 
01682 // simply remove the view (and any children) Don't bother telling the
01683 // listeners.
01684 void LLFolderViewFolder::removeView(LLFolderViewItem* item)
01685 {
01686         if (!item || item->getParentFolder() != this)
01687         {
01688                 return;
01689         }
01690         // deselect without traversing hierarchy
01691         item->recursiveDeselect(TRUE);
01692         getRoot()->removeFromSelectionList(item);
01693         extractItem(item);
01694         delete item;
01695 }
01696 
01697 // extractItem() removes the specified item from the folder, but
01698 // doesn't delete it.
01699 void LLFolderViewFolder::extractItem( LLFolderViewItem* item )
01700 {
01701         items_t::iterator it = std::find(mItems.begin(), mItems.end(), item);
01702         if(it == mItems.end())
01703         {
01704                 // This is an evil downcast. However, it's only doing
01705                 // pointer comparison to find if (which it should be ) the
01706                 // item is in the container, so it's pretty safe.
01707                 LLFolderViewFolder* f = reinterpret_cast<LLFolderViewFolder*>(item);
01708                 folders_t::iterator ft;
01709                 ft = std::find(mFolders.begin(), mFolders.end(), f);
01710                 if(ft != mFolders.end())
01711                 {
01712                         mFolders.erase(ft);
01713                 }
01714         }
01715         else
01716         {
01717                 mItems.erase(it);
01718         }
01719         //item has been removed, need to update filter
01720         dirtyFilter();
01721         //because an item is going away regardless of filter status, force rearrange
01722         requestArrange();
01723         getRoot()->removeItemID(item->getListener()->getUUID());
01724         removeChild(item);
01725 }
01726 
01727 // This function is called by a child that needs to be resorted.
01728 // This is only called for renaming an object because it won't work for date
01729 void LLFolderViewFolder::resort(LLFolderViewItem* item)
01730 {
01731         std::sort(mItems.begin(), mItems.end(), mSortFunction);
01732         std::sort(mFolders.begin(), mFolders.end(), mSortFunction);
01733 }
01734 
01735 bool LLFolderViewFolder::isTrash()
01736 {
01737         if (mAmTrash == LLFolderViewFolder::UNKNOWN)
01738         {
01739                 mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH, false) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH;
01740         }
01741         return mAmTrash == LLFolderViewFolder::TRASH;
01742 }
01743 
01744 void LLFolderViewFolder::sortBy(U32 order)
01745 {
01746         if (!mSortFunction.updateSort(order))
01747         {
01748                 // No changes.
01749                 return;
01750         }
01751 
01752         // Propegate this change to sub folders
01753         for (folders_t::iterator iter = mFolders.begin();
01754                  iter != mFolders.end();)
01755         {
01756                 folders_t::iterator fit = iter++;
01757                 (*fit)->sortBy(order);
01758         }
01759 
01760         std::sort(mFolders.begin(), mFolders.end(), mSortFunction);
01761         std::sort(mItems.begin(), mItems.end(), mSortFunction);
01762 
01763         if (order & LLInventoryFilter::SO_DATE)
01764         {
01765                 U32 latest = 0;
01766                 
01767                 if (!mItems.empty())
01768                 {
01769                         LLFolderViewItem* item = *(mItems.begin());
01770                         latest = item->getCreationDate();
01771                 }
01772 
01773                 if (!mFolders.empty())
01774                 {
01775                         LLFolderViewFolder* folder = *(mFolders.begin());
01776                         if (folder->getCreationDate() > latest)
01777                         {
01778                                 latest = folder->getCreationDate();
01779                         }
01780                 }
01781                 mSubtreeCreationDate = latest;
01782         }
01783 }
01784 
01785 void LLFolderViewFolder::setItemSortOrder(U32 ordering)
01786 {
01787         if (mSortFunction.updateSort(ordering))
01788         {
01789                 for (folders_t::iterator iter = mFolders.begin();
01790                         iter != mFolders.end();)
01791                 {
01792                         folders_t::iterator fit = iter++;
01793                         (*fit)->setItemSortOrder(ordering);
01794                 }
01795 
01796                 std::sort(mFolders.begin(), mFolders.end(), mSortFunction);
01797                 std::sort(mItems.begin(), mItems.end(), mSortFunction);
01798         }
01799 }
01800 
01801 EInventorySortGroup LLFolderViewFolder::getSortGroup()
01802 {
01803         if (isTrash())
01804         {
01805                 return SG_TRASH_FOLDER;
01806         }
01807 
01808         // Folders that can't be moved are 'system' folders. 
01809         if( mListener )
01810         {
01811                 if( !(mListener->isItemMovable()) )
01812                 {
01813                         return SG_SYSTEM_FOLDER;
01814                 }
01815         }
01816         
01817         return SG_NORMAL_FOLDER;
01818 }
01819 
01820 BOOL LLFolderViewFolder::isMovable()
01821 {
01822         if( mListener )
01823         {
01824                 if( !(mListener->isItemMovable()) )
01825                 {
01826                         return FALSE;
01827                 }
01828 
01829                 for (items_t::iterator iter = mItems.begin();
01830                          iter != mItems.end();)
01831                 {
01832                         items_t::iterator iit = iter++;
01833                         if(!(*iit)->isMovable())
01834                         {
01835                                 return FALSE;
01836                         }
01837                 }
01838 
01839                 for (folders_t::iterator iter = mFolders.begin();
01840                          iter != mFolders.end();)
01841                 {
01842                         folders_t::iterator fit = iter++;
01843                         if(!(*fit)->isMovable())
01844                         {
01845                                 return FALSE;
01846                         }
01847                 }
01848         }
01849         return TRUE;
01850 }
01851 
01852 
01853 BOOL LLFolderViewFolder::isRemovable()
01854 {
01855         if( mListener )
01856         {
01857                 if( !(mListener->isItemRemovable()) )
01858                 {
01859                         return FALSE;
01860                 }
01861 
01862                 for (items_t::iterator iter = mItems.begin();
01863                          iter != mItems.end();)
01864                 {
01865                         items_t::iterator iit = iter++;
01866                         if(!(*iit)->isRemovable())
01867                         {
01868                                 return FALSE;
01869                         }
01870                 }
01871 
01872                 for (folders_t::iterator iter = mFolders.begin();
01873                          iter != mFolders.end();)
01874                 {
01875                         folders_t::iterator fit = iter++;
01876                         if(!(*fit)->isRemovable())
01877                         {
01878                                 return FALSE;
01879                         }
01880                 }
01881         }
01882         return TRUE;
01883 }
01884 
01885 // this is an internal method used for adding items to folders. 
01886 BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item)
01887 {
01888 
01889         items_t::iterator it = std::lower_bound(
01890                 mItems.begin(),
01891                 mItems.end(),
01892                 item,
01893                 mSortFunction);
01894         mItems.insert(it,item);
01895         item->setRect(LLRect(0, 0, mRect.getWidth(), 0));
01896         item->setVisible(FALSE);
01897         addChild( item );
01898         item->dirtyFilter();
01899         requestArrange();
01900         return TRUE;
01901 }
01902 
01903 // this is an internal method used for adding items to folders. 
01904 BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder)
01905 {
01906         folders_t::iterator it = std::lower_bound(
01907                 mFolders.begin(),
01908                 mFolders.end(),
01909                 folder,
01910                 mSortFunction);
01911         mFolders.insert(it,folder);
01912         folder->setOrigin(0, 0);
01913         folder->reshape(mRect.getWidth(), 0);
01914         folder->setVisible(FALSE);
01915         addChild( folder );
01916         folder->dirtyFilter();
01917         // rearrange all descendants too, as our indentation level might have changed
01918         folder->requestArrange(TRUE);
01919         return TRUE;
01920 }
01921 
01922 void LLFolderViewFolder::requestArrange(BOOL include_descendants)       
01923 { 
01924         mLastArrangeGeneration = -1; 
01925         // flag all items up to root
01926         if (mParentFolder)
01927         {
01928                 mParentFolder->requestArrange();
01929         }
01930 
01931         if (include_descendants)
01932         {
01933                 for (folders_t::iterator iter = mFolders.begin();
01934                         iter != mFolders.end();
01935                         ++iter)
01936                 {
01937                         (*iter)->requestArrange(TRUE);
01938                 }
01939         }
01940 }
01941 
01942 void LLFolderViewFolder::toggleOpen()
01943 {
01944         setOpen(!mIsOpen);
01945 }
01946 
01947 // Force a folder open or closed
01948 void LLFolderViewFolder::setOpen(BOOL open)             /* Flawfinder: ignore */
01949 {
01950         setOpenArrangeRecursively(open);                /* Flawfinder: ignore */
01951 }
01952 
01953 void LLFolderViewFolder::setOpenArrangeRecursively(BOOL open, ERecurseType recurse)             /* Flawfinder: ignore */
01954 {
01955         BOOL was_open = mIsOpen;
01956         mIsOpen = open;         /* Flawfinder: ignore */
01957         if(!was_open && open)           /* Flawfinder: ignore */
01958         {
01959                 if(mListener)
01960                 {
01961                         mListener->openItem();
01962                 }
01963         }
01964         if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN)
01965         {
01966                 for (folders_t::iterator iter = mFolders.begin();
01967                          iter != mFolders.end();)
01968                 {
01969                         folders_t::iterator fit = iter++;
01970                         (*fit)->setOpenArrangeRecursively(open, RECURSE_DOWN);          /* Flawfinder: ignore */
01971                 }
01972         }
01973         if (mParentFolder && (recurse == RECURSE_UP || recurse == RECURSE_UP_DOWN))
01974         {
01975                 mParentFolder->setOpenArrangeRecursively(open, RECURSE_UP);             /* Flawfinder: ignore */
01976         }
01977         
01978         if (was_open != mIsOpen)
01979         {
01980                 requestArrange();
01981         }
01982 }
01983 
01984 BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask,
01985                                                 BOOL drop,
01986                                                 EDragAndDropType c_type,
01987                                                 void* cargo_data,
01988                                                 EAcceptance* accept,
01989                                                 LLString& tooltip_msg)
01990 {
01991         BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data);
01992         if (accepted) 
01993         {
01994                 mDragAndDropTarget = TRUE;
01995                 *accept = ACCEPT_YES_MULTI;
01996         }
01997         else 
01998         {
01999                 *accept = ACCEPT_NO;
02000         }
02001 
02002         // drag and drop to child item, so clear pending auto-opens
02003         getRoot()->autoOpenTest(NULL);
02004 
02005         return TRUE;
02006 }
02007 
02008 void LLFolderViewFolder::open( void )           /* Flawfinder: ignore */
02009 {
02010         toggleOpen();
02011 }
02012 
02013 void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor)
02014 {
02015         functor.doFolder(this);
02016 
02017         for (folders_t::iterator iter = mFolders.begin();
02018                  iter != mFolders.end();)
02019         {
02020                 folders_t::iterator fit = iter++;
02021                 (*fit)->applyFunctorRecursively(functor);
02022         }
02023         for (items_t::iterator iter = mItems.begin();
02024                  iter != mItems.end();)
02025         {
02026                 items_t::iterator iit = iter++;
02027                 functor.doItem((*iit));
02028         }
02029 }
02030 
02031 void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
02032 {
02033         functor(mListener);
02034         for (folders_t::iterator iter = mFolders.begin();
02035                  iter != mFolders.end();)
02036         {
02037                 folders_t::iterator fit = iter++;
02038                 (*fit)->applyListenerFunctorRecursively(functor);
02039         }
02040         for (items_t::iterator iter = mItems.begin();
02041                  iter != mItems.end();)
02042         {
02043                 items_t::iterator iit = iter++;
02044                 (*iit)->applyListenerFunctorRecursively(functor);
02045         }
02046 }
02047 
02048 // LLView functionality
02049 BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask,
02050                                                                                    BOOL drop,
02051                                                                                    EDragAndDropType cargo_type,
02052                                                                                    void* cargo_data,
02053                                                                                    EAcceptance* accept,
02054                                                                                    LLString& tooltip_msg)
02055 {
02056         LLFolderView* root_view = getRoot();
02057 
02058         BOOL handled = FALSE;
02059         if(mIsOpen)
02060         {
02061                 handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
02062                                                                                         cargo_data, accept, tooltip_msg) != NULL;
02063         }
02064 
02065         if (!handled)
02066         {
02067                 BOOL accepted = mListener && mListener->dragOrDrop(mask, drop,cargo_type,cargo_data);
02068 
02069                 if (accepted) 
02070                 {
02071                         mDragAndDropTarget = TRUE;
02072                         *accept = ACCEPT_YES_MULTI;
02073                 }
02074                 else 
02075                 {
02076                         *accept = ACCEPT_NO;
02077                 }
02078 
02079                 if (!drop && accepted)
02080                 {
02081                         root_view->autoOpenTest(this);
02082                 }
02083 
02084                 lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl;
02085         }
02086 
02087         return TRUE;
02088 }
02089 
02090 
02091 BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask )
02092 {
02093         BOOL handled = FALSE;
02094         if( getVisible() )
02095         {
02096                 // fetch contents of this folder, as context menu can depend on contents
02097                 // still, user would have to open context menu again to see the changes
02098                 gInventory.fetchDescendentsOf(mListener->getUUID());
02099                 
02100                 if( mIsOpen )
02101                 {
02102                         handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
02103                 }
02104                 if (!handled)
02105                 {
02106                         handled = LLFolderViewItem::handleRightMouseDown( x, y, mask );
02107                 }
02108         }
02109         return handled;
02110 }
02111 
02112 
02113 BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask)
02114 {
02115         BOOL handled = LLView::handleHover(x, y, mask);
02116 
02117         if (!handled)
02118         {
02119                 // this doesn't do child processing
02120                 handled = LLFolderViewItem::handleHover(x, y, mask);
02121         }
02122 
02123         //if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD && y > mRect.getHeight() - )
02124         //{
02125         //      gViewerWindow->setCursor(UI_CURSOR_ARROW);
02126         //      mExpanderHighlighted = TRUE;
02127         //      handled = TRUE;
02128         //}
02129         return handled;
02130 }
02131 
02132 BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )
02133 {
02134         BOOL handled = FALSE;
02135         if( mIsOpen )
02136         {
02137                 handled = childrenHandleMouseDown(x,y,mask) != NULL;
02138         }
02139         if( !handled )
02140         {
02141                 if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD)
02142                 {
02143                         toggleOpen();
02144                         handled = TRUE;
02145                 }
02146                 else
02147                 {
02148                         // do normal selection logic
02149                         handled = LLFolderViewItem::handleMouseDown(x, y, mask);
02150                 }
02151         }
02152 
02153         return handled;
02154 }
02155 
02156 BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask )
02157 {
02158         if (!getVisible())
02159         {
02160                 return FALSE;
02161         }
02162         BOOL rv = false;
02163         if( mIsOpen )
02164         {
02165                 rv = childrenHandleDoubleClick( x, y, mask ) != NULL;
02166         }
02167         if( !rv )
02168         {
02169                 if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD)
02170                 {
02171                         // don't select when user double-clicks plus sign
02172                         // so as not to contradict single-click behavior
02173                         toggleOpen();
02174                 }
02175                 else
02176                 {
02177                         setSelectionFromRoot(this, FALSE);
02178                         toggleOpen();
02179                 }
02180                 return TRUE;
02181         }
02182         return rv;
02183 }
02184 
02185 void LLFolderViewFolder::draw()
02186 {
02187         if (mAutoOpenCountdown != 0.f)
02188         {
02189                 mControlLabelRotation = mAutoOpenCountdown * -90.f;
02190         }
02191         else if (mIsOpen)
02192         {
02193                 mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f));
02194         }
02195         else
02196         {
02197                 mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f));
02198         }
02199 
02200         LLFolderViewItem::draw();
02201 
02202         // draw children if root folder, or any other folder that is open or animating to closed state
02203         if( getRoot() == this || (mIsOpen || mCurHeight != mTargetHeight ))
02204         {
02205                 LLView::draw();
02206         }
02207 
02208         mExpanderHighlighted = FALSE;
02209 }
02210 
02211 U32 LLFolderViewFolder::getCreationDate() const
02212 {
02213         return llmax<U32>(mCreationDate, mSubtreeCreationDate);
02214 }
02215 
02216 
02217 BOOL    LLFolderViewFolder::potentiallyVisible()
02218 {
02219         // folder should be visible by it's own filter status
02220         return LLFolderViewItem::potentiallyVisible()   
02221                  // or one or more of its descendants have passed the minimum filter requirement
02222                 || hasFilteredDescendants(mRoot->getFilter()->getMinRequiredGeneration())
02223                 // or not all of its descendants have been checked against minimum filter requirement
02224                 || getCompletedFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration(); 
02225 }
02226 
02227 // this does prefix traversal, as folders are listed above their contents
02228 LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children )
02229 {
02230         BOOL found_item = FALSE;
02231 
02232         LLFolderViewItem* result = NULL;
02233         // when not starting from a given item, start at beginning
02234         if(item == NULL)
02235         {
02236                 found_item = TRUE;
02237         }
02238 
02239         // find current item among children
02240         folders_t::iterator fit = mFolders.begin();
02241         folders_t::iterator fend = mFolders.end();
02242 
02243         items_t::iterator iit = mItems.begin();
02244         items_t::iterator iend = mItems.end();
02245 
02246         // if not trivially starting at the beginning, we have to find the current item
02247         if (!found_item)
02248         {
02249                 // first, look among folders, since they are always above items
02250                 for(; fit != fend; ++fit)
02251                 {
02252                         if(item == (*fit))
02253                         {
02254                                 found_item = TRUE;
02255                                 // if we are on downwards traversal
02256                                 if (include_children && (*fit)->isOpen())
02257                                 {
02258                                         // look for first descendant
02259                                         return (*fit)->getNextFromChild(NULL, TRUE);
02260                                 }
02261                                 // otherwise advance to next folder
02262                                 ++fit;
02263                                 include_children = TRUE;
02264                                 break;
02265                         }
02266                 }
02267 
02268                 // didn't find in folders?  Check items...
02269                 if (!found_item)
02270                 {
02271                         for(; iit != iend; ++iit)
02272                         {
02273                                 if(item == (*iit))
02274                                 {
02275                                         found_item = TRUE;
02276                                         // point to next item
02277                                         ++iit;
02278                                         break;
02279                                 }
02280                         }
02281                 }
02282         }
02283 
02284         if (!found_item)
02285         {
02286                 // you should never call this method with an item that isn't a child
02287                 // so we should always find something
02288                 llassert(FALSE);
02289                 return NULL;
02290         }
02291 
02292         // at this point, either iit or fit point to a candidate "next" item
02293         // if both are out of range, we need to punt up to our parent
02294 
02295         // now, starting from found folder, continue through folders
02296         // searching for next visible folder
02297         while(fit != fend && !(*fit)->getVisible())
02298         {
02299                 // turn on downwards traversal for next folder
02300                 ++fit;
02301         } 
02302         
02303         if (fit != fend)
02304         {
02305                 result = (*fit);
02306         }
02307         else
02308         {
02309                 // otherwise, scan for next visible item
02310                 while(iit != iend && !(*iit)->getVisible())
02311                 {
02312                         ++iit;
02313                 } 
02314 
02315                 // check to see if we have a valid item
02316                 if (iit != iend)
02317                 {
02318                         result = (*iit);
02319                 }
02320         }
02321 
02322         if( !result && mParentFolder )
02323         {
02324                 // If there are no siblings or children to go to, recurse up one level in the tree
02325                 // and skip children for this folder, as we've already discounted them
02326                 result = mParentFolder->getNextFromChild(this, FALSE);
02327         }
02328 
02329         return result;
02330 }
02331 
02332 // this does postfix traversal, as folders are listed above their contents
02333 LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children )
02334 {
02335         BOOL found_item = FALSE;
02336 
02337         LLFolderViewItem* result = NULL;
02338         // when not starting from a given item, start at end
02339         if(item == NULL)
02340         {
02341                 found_item = TRUE;
02342         }
02343 
02344         // find current item among children
02345         folders_t::reverse_iterator fit = mFolders.rbegin();
02346         folders_t::reverse_iterator fend = mFolders.rend();
02347 
02348         items_t::reverse_iterator iit = mItems.rbegin();
02349         items_t::reverse_iterator iend = mItems.rend();
02350 
02351         // if not trivially starting at the end, we have to find the current item
02352         if (!found_item)
02353         {
02354                 // first, look among items, since they are always below the folders
02355                 for(; iit != iend; ++iit)
02356                 {
02357                         if(item == (*iit))
02358                         {
02359                                 found_item = TRUE;
02360                                 // point to next item
02361                                 ++iit;
02362                                 break;
02363                         }
02364                 }
02365 
02366                 // didn't find in items?  Check folders...
02367                 if (!found_item)
02368                 {
02369                         for(; fit != fend; ++fit)
02370                         {
02371                                 if(item == (*fit))
02372                                 {
02373                                         found_item = TRUE;
02374                                         // point to next folder
02375                                         ++fit;
02376                                         break;
02377                                 }
02378                         }
02379                 }
02380         }
02381 
02382         if (!found_item)
02383         {
02384                 // you should never call this method with an item that isn't a child
02385                 // so we should always find something
02386                 llassert(FALSE);
02387                 return NULL;
02388         }
02389 
02390         // at this point, either iit or fit point to a candidate "next" item
02391         // if both are out of range, we need to punt up to our parent
02392 
02393         // now, starting from found item, continue through items
02394         // searching for next visible item
02395         while(iit != iend && !(*iit)->getVisible())
02396         {
02397                 ++iit;
02398         } 
02399         
02400         if (iit != iend)
02401         {
02402                 // we found an appropriate item
02403                 result = (*iit);
02404         }
02405         else
02406         {
02407                 // otherwise, scan for next visible folder
02408                 while(fit != fend && !(*fit)->getVisible())
02409                 {
02410                         ++fit;
02411                 } 
02412 
02413                 // check to see if we have a valid folder
02414                 if (fit != fend)
02415                 {
02416                         // try selecting child element of this folder
02417                         if ((*fit)->isOpen())
02418                         {
02419                                 result = (*fit)->getPreviousFromChild(NULL);
02420                         }
02421                         else
02422                         {
02423                                 result = (*fit);
02424                         }
02425                 }
02426         }
02427 
02428         if( !result )
02429         {
02430                 // If there are no siblings or children to go to, recurse up one level in the tree
02431                 // which gets back to this folder, which will only be visited if it is a valid, visible item
02432                 result = this;
02433         }
02434 
02435         return result;
02436 }
02437 
02438 
02439 //---------------------------------------------------------------------------
02440 
02441 // Tells all folders in a folderview to sort their items
02442 // (and only their items, not folders) by a certain function.
02443 class LLSetItemSortFunction : public LLFolderViewFunctor
02444 {
02445 public:
02446         LLSetItemSortFunction(U32 ordering)
02447                 : mSortOrder(ordering) {}
02448         virtual ~LLSetItemSortFunction() {}
02449         virtual void doFolder(LLFolderViewFolder* folder);
02450         virtual void doItem(LLFolderViewItem* item);
02451 
02452         U32 mSortOrder;
02453 };
02454 
02455 
02456 // Set the sort order.
02457 void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder)
02458 {
02459         folder->setItemSortOrder(mSortOrder);
02460 }
02461 
02462 // Do nothing.
02463 void LLSetItemSortFunction::doItem(LLFolderViewItem* item)
02464 {
02465         return;
02466 }
02467 
02468 //---------------------------------------------------------------------------
02469 
02470 // Tells all folders in a folderview to close themselves
02471 // For efficiency, calls setOpenArrangeRecursively().
02472 // The calling function must then call:
02473 //      LLFolderView* root = getRoot();
02474 //      if( root )
02475 //      {
02476 //              root->arrange( NULL, NULL );
02477 //              root->scrollToShowSelection();
02478 //      }
02479 // to patch things up.
02480 class LLCloseAllFoldersFunctor : public LLFolderViewFunctor
02481 {
02482 public:
02483         LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; }
02484         virtual ~LLCloseAllFoldersFunctor() {}
02485         virtual void doFolder(LLFolderViewFolder* folder);
02486         virtual void doItem(LLFolderViewItem* item);
02487 
02488         BOOL mOpen;
02489 };
02490 
02491 
02492 // Set the sort order.
02493 void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder)
02494 {
02495         folder->setOpenArrangeRecursively(mOpen);
02496 }
02497 
02498 // Do nothing.
02499 void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item)
02500 { }
02501 
02505 
02506 // Default constructor
02507 LLFolderView::LLFolderView( const LLString& name, LLViewerImage* root_folder_icon, 
02508                                                    const LLRect& rect, const LLUUID& source_id, LLView *parent_view ) :
02509 #if LL_WINDOWS
02510 #pragma warning( push )
02511 #pragma warning( disable : 4355 ) // warning C4355: 'this' : used in base member initializer list
02512 #endif
02513         LLFolderViewFolder( name, root_folder_icon, this, NULL ),
02514 #if LL_WINDOWS
02515 #pragma warning( pop )
02516 #endif
02517         mScrollContainer( NULL ),
02518         mPopupMenuHandle( LLViewHandle::sDeadHandle ),
02519         mAllowMultiSelect(TRUE),
02520         mShowFolderHierarchy(FALSE),
02521         mSourceID(source_id),
02522         mRenameItem( NULL ),
02523         mNeedsScroll( FALSE ),
02524         mLastScrollItem( NULL ),
02525         mNeedsAutoSelect( FALSE ),
02526         mAutoSelectOverride(FALSE),
02527         mNeedsAutoRename(FALSE),
02528         mDebugFilters(FALSE),
02529         mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME),      // This gets overridden by a pref immediately
02530         mFilter(name),
02531         mShowSelectionContext(FALSE),
02532         mShowSingleSelection(FALSE),
02533         mArrangeGeneration(0),
02534         mUserData(NULL),
02535         mSelectCallback(NULL),
02536         mSelectionChanged(FALSE),
02537         mMinWidth(0),
02538         mDragAndDropThisFrame(FALSE)
02539 {
02540         LLRect new_rect(rect.mLeft, rect.mBottom + mRect.getHeight(), rect.mLeft + mRect.getWidth(), rect.mBottom);
02541         setRect( rect );
02542         reshape(rect.getWidth(), rect.getHeight());
02543         mIsOpen = TRUE; // this view is always open.
02544         mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH);
02545         mAutoOpenCandidate = NULL;
02546         mAutoOpenTimer.stop();
02547         mKeyboardSelection = FALSE;
02548         mIndentation = -LEFT_INDENTATION; // children start at indentation 0
02549         gIdleCallbacks.addFunction(idle, this);
02550 
02551         //clear label
02552         // go ahead and render root folder as usual
02553         // just make sure the label ("Inventory Folder") never shows up
02554         mLabel = LLString::null;
02555 
02556         mRenamer = new LLLineEditor("ren", mRect, "", sFont,
02557                                                                 DB_INV_ITEM_NAME_STR_LEN,
02558                                                                 &LLFolderView::commitRename,
02559                                                                 NULL,
02560                                                                 NULL,
02561                                                                 this,
02562                                                                 &LLLineEditor::prevalidatePrintableNotPipe,
02563                                                                 LLViewBorder::BEVEL_NONE,
02564                                                                 LLViewBorder::STYLE_LINE,
02565                                                                 2);
02566         mRenamer->setWriteableBgColor(LLColor4::white);
02567         // Escape is handled by reverting the rename, not commiting it (default behavior)
02568         mRenamer->setCommitOnFocusLost(TRUE);
02569         mRenamer->setVisible(FALSE);
02570         addChild(mRenamer);
02571 
02572         // make the popup menu available
02573         LLMenuGL* menu = gUICtrlFactory->buildMenu("menu_inventory.xml", parent_view);
02574         if (!menu)
02575         {
02576                 menu = new LLMenuGL("");
02577         }
02578         menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor"));
02579         menu->setVisible(FALSE);
02580         mPopupMenuHandle = menu->mViewHandle;
02581 
02582         setTabStop(TRUE);
02583 }
02584 
02585 // Destroys the object
02586 LLFolderView::~LLFolderView( void )
02587 {
02588         // The release focus call can potentially call the
02589         // scrollcontainer, which can potentially be called with a partly
02590         // destroyed scollcontainer. Just null it out here, and no worries
02591         // about calling into the invalid scroll container.
02592         // Same with the renamer.
02593         mScrollContainer = NULL;
02594         mRenameItem = NULL;
02595         mRenamer = NULL;
02596         gFocusMgr.releaseFocusIfNeeded( this );
02597 
02598         if( gEditMenuHandler == this )
02599         {
02600                 gEditMenuHandler = NULL;
02601         }
02602 
02603         mAutoOpenItems.removeAllNodes();
02604         gIdleCallbacks.deleteFunction(idle, this);
02605 
02606         LLView::deleteViewByHandle(mPopupMenuHandle);
02607 
02608         if(gViewerWindow->hasTopCtrl(mRenamer))
02609         {
02610                 gViewerWindow->setTopCtrl(NULL);
02611         }
02612 
02613         mAutoOpenItems.removeAllNodes();
02614         clearSelection();
02615         mItems.clear();
02616         mFolders.clear();
02617 
02618         mItemMap.clear();
02619 }
02620 
02621 EWidgetType LLFolderView::getWidgetType() const
02622 {
02623         return WIDGET_TYPE_FOLDER_VIEW;
02624 }
02625 
02626 LLString LLFolderView::getWidgetTag() const
02627 {
02628         return LL_FOLDER_VIEW_TAG;
02629 }
02630 
02631 BOOL LLFolderView::canFocusChildren() const
02632 {
02633         return FALSE;
02634 }
02635 
02636 void LLFolderView::checkTreeResortForModelChanged()
02637 {
02638         if (mSortOrder & LLInventoryFilter::SO_DATE && !(mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME))
02639         {
02640                 // This is the case where something got added or removed.  If we are date sorting
02641                 // everything including folders, then we need to rebuild the whole tree.
02642                 // Just set to something not SO_DATE to force the folder most resent date resort.
02643                 mSortOrder = mSortOrder & ~LLInventoryFilter::SO_DATE;
02644                 setSortOrder(mSortOrder | LLInventoryFilter::SO_DATE);
02645         }
02646 }
02647 
02648 void LLFolderView::setSortOrder(U32 order)
02649 {
02650         if (order != mSortOrder)
02651         {
02652                 LLFastTimer t(LLFastTimer::FTM_SORT);
02653                 mSortOrder = order;
02654 
02655                 for (folders_t::iterator iter = mFolders.begin();
02656                          iter != mFolders.end();)
02657                 {
02658                         folders_t::iterator fit = iter++;
02659                         (*fit)->sortBy(order);
02660                 }
02661 
02662                 arrangeAll();
02663         }
02664 }
02665 
02666 
02667 U32 LLFolderView::getSortOrder() const
02668 {
02669         return mSortOrder;
02670 }
02671 
02672 BOOL LLFolderView::addFolder( LLFolderViewFolder* folder)
02673 {
02674         // enforce sort order of My Inventory followed by Library
02675         if (folder->getListener()->getUUID() == gInventoryLibraryRoot)
02676         {
02677                 mFolders.push_back(folder);
02678         }
02679         else
02680         {
02681                 mFolders.insert(mFolders.begin(), folder);
02682         }
02683         folder->setOrigin(0, 0);
02684         folder->reshape(mRect.getWidth(), 0);
02685         folder->setVisible(FALSE);
02686         addChild( folder );
02687         folder->dirtyFilter();
02688         folder->requestArrange();
02689         return TRUE;
02690 }
02691 
02692 void LLFolderView::closeAllFolders()
02693 {
02694         // Close all the folders
02695         setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN);
02696 }
02697 
02698 void LLFolderView::openFolder(const LLString& foldername)
02699 {
02700         LLFolderViewFolder* inv = (LLFolderViewFolder*)getChildByName(foldername);
02701         if (inv)
02702         {
02703                 setSelection(inv, FALSE, FALSE);
02704                 inv->setOpen(TRUE);
02705         }
02706 }
02707 
02708 void LLFolderView::setOpenArrangeRecursively(BOOL open, ERecurseType recurse)           /* Flawfinder: ignore */
02709 {
02710         // call base class to do proper recursion
02711         LLFolderViewFolder::setOpenArrangeRecursively(open, recurse);           /* Flawfinder: ignore */
02712         // make sure root folder is always open
02713         mIsOpen = TRUE;
02714 }
02715 
02716 // This view grows and shinks to enclose all of its children items and folders.
02717 S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation )
02718 {
02719         LLFastTimer t2(LLFastTimer::FTM_ARRANGE);
02720 
02721         filter_generation = mFilter.getMinRequiredGeneration();
02722         mMinWidth = 0;
02723 
02724         mHasVisibleChildren = hasFilteredDescendants(filter_generation);
02725         // arrange always finishes, so optimistically set the arrange generation to the most current
02726         mLastArrangeGeneration = mRoot->getArrangeGeneration();
02727 
02728         LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState();
02729 
02730         S32 total_width = LEFT_PAD;
02731         S32 running_height = mDebugFilters ? llceil(sSmallFont->getLineHeight()) : 0;
02732         S32 target_height = running_height;
02733         S32 parent_item_height = mRect.getHeight();
02734 
02735         for (folders_t::iterator iter = mFolders.begin();
02736                  iter != mFolders.end();)
02737         {
02738                 folders_t::iterator fit = iter++;
02739                 LLFolderViewFolder* folderp = (*fit);
02740                 if (getDebugFilters())
02741                 {
02742                         folderp->setVisible(TRUE);
02743                 }
02744                 else
02745                 {
02746                         folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
02747                                                                         (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
02748                 }
02749                 if (folderp->getVisible())
02750                 {
02751                         S32 child_height = 0;
02752                         S32 child_width = 0;
02753                         S32 child_top = parent_item_height - running_height;
02754                         
02755                         target_height += folderp->arrange( &child_width, &child_height, filter_generation );
02756 
02757                         mMinWidth = llmax(mMinWidth, child_width);
02758                         total_width = llmax( total_width, child_width );
02759                         running_height += child_height;
02760                         folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() );
02761                 }
02762         }
02763 
02764         for (items_t::iterator iter = mItems.begin();
02765                  iter != mItems.end();)
02766         {
02767                 items_t::iterator iit = iter++;
02768                 LLFolderViewItem* itemp = (*iit);
02769                 itemp->setVisible(itemp->getFiltered(filter_generation));
02770 
02771                 if (itemp->getVisible())
02772                 {
02773                         S32 child_width = 0;
02774                         S32 child_height = 0;
02775                         S32 child_top = parent_item_height - running_height;
02776                         
02777                         target_height += itemp->arrange( &child_width, &child_height, filter_generation );
02778                         itemp->reshape(itemp->getRect().getWidth(), child_height);
02779 
02780                         mMinWidth = llmax(mMinWidth, child_width);
02781                         total_width = llmax( total_width, child_width );
02782                         running_height += child_height;
02783                         itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() );
02784                 }
02785         }
02786 
02787         S32 dummy_s32;
02788         BOOL dummy_bool;
02789         S32 min_width;
02790         mScrollContainer->calcVisibleSize( &min_width, &dummy_s32, &dummy_bool, &dummy_bool);
02791         reshape( llmax(min_width, total_width), running_height );
02792 
02793         S32 new_min_width;
02794         mScrollContainer->calcVisibleSize( &new_min_width, &dummy_s32, &dummy_bool, &dummy_bool);
02795         if (new_min_width != min_width)
02796         {
02797                 reshape( llmax(min_width, total_width), running_height );
02798         }
02799 
02800         mTargetHeight = (F32)target_height;
02801         return llround(mTargetHeight);
02802 }
02803 
02804 const LLString LLFolderView::getFilterSubString(BOOL trim)
02805 {
02806         return mFilter.getFilterSubString(trim);
02807 }
02808 
02809 void LLFolderView::filter( LLInventoryFilter& filter )
02810 {
02811         LLFastTimer t2(LLFastTimer::FTM_FILTER);
02812         filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000));
02813 
02814         if (getCompletedFilterGeneration() < filter.getCurrentGeneration())
02815         {
02816                 mFiltered = FALSE;
02817                 mMinWidth = 0;
02818                 LLFolderViewFolder::filter(filter);
02819         }
02820 }
02821 
02822 void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent)
02823 {
02824         S32 min_width = 0;
02825         S32 dummy_height;
02826         BOOL dummy_bool;
02827         if (mScrollContainer)
02828         {
02829                 mScrollContainer->calcVisibleSize( &min_width, &dummy_height, &dummy_bool, &dummy_bool);
02830         }
02831         width = llmax(mMinWidth, min_width);
02832         LLView::reshape(width, height, called_from_parent);
02833 }
02834 
02835 void LLFolderView::addToSelectionList(LLFolderViewItem* item)
02836 {
02837         if (item->isSelected())
02838         {
02839                 removeFromSelectionList(item);
02840         }
02841         if (mSelectedItems.size())
02842         {
02843                 mSelectedItems.back()->setIsCurSelection(FALSE);
02844         }
02845         item->setIsCurSelection(TRUE);
02846         mSelectedItems.push_back(item);
02847 }
02848 
02849 void LLFolderView::removeFromSelectionList(LLFolderViewItem* item)
02850 {
02851         if (mSelectedItems.size())
02852         {
02853                 mSelectedItems.back()->setIsCurSelection(FALSE);
02854         }
02855 
02856         selected_items_t::iterator item_iter;
02857         for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();)
02858         {
02859                 if (*item_iter == item)
02860                 {
02861                         item_iter = mSelectedItems.erase(item_iter);
02862                 }
02863                 else
02864                 {
02865                         ++item_iter;
02866                 }
02867         }
02868         if (mSelectedItems.size())
02869         {
02870                 mSelectedItems.back()->setIsCurSelection(TRUE);
02871         }
02872 }
02873 
02874 LLFolderViewItem* LLFolderView::getCurSelectedItem( void )
02875 {
02876         if(mSelectedItems.size())
02877         {
02878                 LLFolderViewItem* itemp = mSelectedItems.back();
02879                 llassert(itemp->getIsCurSelection());
02880                 return itemp;
02881         }
02882         return NULL;
02883 }
02884 
02885 
02886 // Record the selected item and pass it down the hierachy.
02887 BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL open,         /* Flawfinder: ignore */
02888                                                                 BOOL take_keyboard_focus)
02889 {
02890         if( selection == this )
02891         {
02892                 return FALSE;
02893         }
02894 
02895         if( selection && take_keyboard_focus)
02896         {
02897                 setFocus(TRUE);
02898         }
02899 
02900         // clear selection down here because change of keyboard focus can potentially
02901         // affect selection
02902         clearSelection();
02903 
02904         if(selection)
02905         {
02906                 addToSelectionList(selection);
02907         }
02908 
02909         BOOL rv = LLFolderViewFolder::setSelection(selection, open, take_keyboard_focus);
02910         if(open && selection)
02911         {
02912                 selection->getParentFolder()->requestArrange();
02913         }
02914 
02915         llassert(mSelectedItems.size() <= 1);
02916 
02917         mSelectionChanged = TRUE;
02918 
02919         return rv;
02920 }
02921 
02922 BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected)
02923 {
02924         BOOL rv = FALSE;
02925 
02926         // can't select root folder
02927         if(!selection || selection == this)
02928         {
02929                 return FALSE;
02930         }
02931 
02932         if (!mAllowMultiSelect)
02933         {
02934                 clearSelection();
02935         }
02936 
02937         selected_items_t::iterator item_iter;
02938         for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
02939         {
02940                 if (*item_iter == selection)
02941                 {
02942                         break;
02943                 }
02944         }
02945 
02946         BOOL on_list = (item_iter != mSelectedItems.end());
02947 
02948         if(selected && !on_list)
02949         {
02950                 addToSelectionList(selection);
02951         }
02952         if(!selected && on_list)
02953         {
02954                 removeFromSelectionList(selection);
02955         }
02956 
02957         rv = LLFolderViewFolder::changeSelection(selection, selected);
02958 
02959         mSelectionChanged = TRUE;
02960         
02961         return rv;
02962 }
02963 
02964 S32 LLFolderView::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items)
02965 {
02966         S32 rv = 0;
02967 
02968         // now store resulting selection
02969         if (mAllowMultiSelect)
02970         {
02971                 LLFolderViewItem *cur_selection = getCurSelectedItem();
02972                 rv = LLFolderViewFolder::extendSelection(selection, cur_selection, items);
02973                 for (S32 i = 0; i < items.count(); i++)
02974                 {
02975                         addToSelectionList(items[i]);
02976                         rv++;
02977                 }
02978         }
02979         else
02980         {
02981                 setSelection(selection, FALSE, FALSE);
02982                 rv++;
02983         }
02984 
02985         mSelectionChanged = TRUE;
02986         return rv;
02987 }
02988 
02989 void LLFolderView::sanitizeSelection()
02990 {
02991         // store off current item in case it is automatically deselected
02992         // and we want to preserve context
02993         LLFolderViewItem* original_selected_item = getCurSelectedItem();
02994 
02995         // Cache "Show all folders" filter setting
02996         BOOL show_all_folders = (getRoot()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS);
02997 
02998         std::vector<LLFolderViewItem*> items_to_remove;
02999         selected_items_t::iterator item_iter;
03000         for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
03001         {
03002                 LLFolderViewItem* item = *item_iter;
03003 
03004                 // ensure that each ancestor is open and potentially passes filtering
03005                 BOOL visible = item->potentiallyVisible(); // initialize from filter state for this item
03006                 // modify with parent open and filters states
03007                 LLFolderViewFolder* parent_folder = item->getParentFolder();
03008                 if ( parent_folder )
03009                 {
03010                         if ( show_all_folders )
03011                         {       // "Show all folders" is on, so this folder is visible
03012                                 visible = TRUE;
03013                         }
03014                         else
03015                         {       // Move up through parent folders and see what's visible
03016                                 while(parent_folder)
03017                                 {
03018                                         visible = visible && parent_folder->isOpen() && parent_folder->potentiallyVisible();
03019                                         parent_folder = parent_folder->getParentFolder();
03020                                 }
03021                         }
03022                 }
03023 
03024                 //  deselect item if any ancestor is closed or didn't pass filter requirements.
03025                 if (!visible)
03026                 {
03027                         items_to_remove.push_back(item);
03028                 }
03029 
03030                 // disallow nested selections (i.e. folder items plus one or more ancestors)
03031                 // could check cached mum selections count and only iterate if there are any
03032                 // but that may be a premature optimization.
03033                 selected_items_t::iterator other_item_iter;
03034                 for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter)
03035                 {
03036                         LLFolderViewItem* other_item = *other_item_iter;
03037                         for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder())
03038                         {
03039                                 if (parent_folder == item)
03040                                 {
03041                                         // this is a descendent of the current folder, remove from list
03042                                         items_to_remove.push_back(other_item);
03043                                         break;
03044                                 }
03045                         }
03046                 }
03047         }
03048 
03049         std::vector<LLFolderViewItem*>::iterator item_it;
03050         for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it )
03051         {
03052                 changeSelection(*item_it, FALSE); // toggle selection (also removes from list)
03053         }
03054 
03055         // if nothing selected after prior constraints...
03056         if (mSelectedItems.empty())
03057         {
03058                 // ...select first available parent of original selection, or "My Inventory" otherwise
03059                 LLFolderViewItem* new_selection = NULL;
03060                 if (original_selected_item)
03061                 {
03062                         for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder();
03063                                 parent_folder;
03064                                 parent_folder = parent_folder->getParentFolder())
03065                         {
03066                                 if (parent_folder->potentiallyVisible())
03067                                 {
03068                                         // give initial selection to first ancestor folder that potentially passes the filter
03069                                         if (!new_selection)
03070                                         {
03071                                                 new_selection = parent_folder;
03072                                         }
03073 
03074                                         // if any ancestor folder of original item is closed, move the selection up 
03075                                         // to the highest closed
03076                                         if (!parent_folder->isOpen())
03077                                         {       
03078                                                 new_selection = parent_folder;
03079                                         }
03080                                 }
03081                         }
03082                 }
03083                 else
03084                 {
03085                         // nothing selected to start with, so pick "My Inventory" as best guess
03086                         new_selection = getItemByID(gAgent.getInventoryRootID());
03087                 }
03088 
03089                 if (new_selection)
03090                 {
03091                         setSelection(new_selection, FALSE, FALSE);
03092                 }
03093         }
03094 }
03095 
03096 void LLFolderView::clearSelection()
03097 {
03098         if (mSelectedItems.size() > 0)
03099         {
03100                 recursiveDeselect(FALSE);
03101                 mSelectedItems.clear();
03102         }
03103 }
03104 
03105 BOOL LLFolderView::getSelectionList(std::set<LLUUID> &selection)
03106 {
03107         selected_items_t::iterator item_it;
03108         for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
03109         {
03110                 selection.insert((*item_it)->getListener()->getUUID());
03111         }
03112 
03113         return (selection.size() != 0);
03114 }
03115 
03116 BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source)
03117 {
03118         std::vector<EDragAndDropType> types;
03119         std::vector<LLUUID> cargo_ids;
03120         selected_items_t::iterator item_it;
03121         BOOL can_drag = TRUE;
03122         if (!mSelectedItems.empty())
03123         {
03124                 for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
03125                 {
03126                         EDragAndDropType type = DAD_NONE;
03127                         LLUUID id = LLUUID::null;
03128                         can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id);
03129 
03130                         types.push_back(type);
03131                         cargo_ids.push_back(id);
03132                 }
03133 
03134                 gToolDragAndDrop->beginMultiDrag(types, cargo_ids, source, mSourceID); 
03135         }
03136         return can_drag;
03137 }
03138 
03139 void LLFolderView::commitRename( LLUICtrl* renamer, void* user_data )
03140 {
03141         LLFolderView* root = reinterpret_cast<LLFolderView*>(user_data);
03142         if( root )
03143         {
03144                 root->finishRenamingItem();
03145         }
03146 }
03147 
03148 void LLFolderView::draw()
03149 {
03150         if (mDebugFilters)
03151         {
03152                 LLString current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d",
03153                                                                                 mFilter.getCurrentGeneration(), mFilter.getMinRequiredGeneration(), mFilter.getMustPassGeneration());
03154                 sSmallFont->renderUTF8(current_filter_string, 0, 2, 
03155                         mRect.getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), 
03156                         LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
03157         }
03158 
03159         // if cursor has moved off of me during drag and drop
03160         // close all auto opened folders
03161         if (!mDragAndDropThisFrame)
03162         {
03163                 closeAutoOpenedFolders();
03164         }
03165         if(gViewerWindow->hasKeyboardFocus(this) && !getVisible())
03166         {
03167                 gViewerWindow->setKeyboardFocus( NULL, NULL );
03168         }
03169 
03170         // while dragging, update selection rendering to reflect single/multi drag status
03171         if (gToolDragAndDrop && gToolDragAndDrop->hasMouseCapture())
03172         {
03173                 EAcceptance last_accept = gToolDragAndDrop->getLastAccept();
03174                 if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE)
03175                 {
03176                         setShowSingleSelection(TRUE);
03177                 }
03178                 else
03179                 {
03180                         setShowSingleSelection(FALSE);
03181                 }
03182         }
03183         else
03184         {
03185                 setShowSingleSelection(FALSE);
03186         }
03187 
03188 
03189         if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size())
03190         {
03191                 mSearchString.clear();
03192         }
03193 
03194         if (hasVisibleChildren() || getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS)
03195         {
03196                 mStatusText.clear();
03197         }
03198         else
03199         {
03200                 if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter.getMinRequiredGeneration())
03201                 {
03202                         mStatusText = "Searching..."; // *TODO:translate
03203                         sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
03204                 }
03205                 else
03206                 {
03207                         mStatusText = "No matching items found in inventory."; // *TODO:translate
03208                         sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
03209                 }
03210         }
03211 
03212         LLFolderViewFolder::draw();
03213 
03214         mDragAndDropThisFrame = FALSE;
03215 }
03216 
03217 void LLFolderView::finishRenamingItem( void )
03218 {
03219         if(!mRenamer)
03220         {
03221                 return;
03222         }
03223         if( mRenameItem )
03224         {
03225                 mRenameItem->rename( mRenamer->getText().c_str() );
03226         }
03227 
03228         mRenamer->setCommitOnFocusLost( FALSE );
03229         mRenamer->setFocus( FALSE );
03230         mRenamer->setVisible( FALSE );
03231         mRenamer->setCommitOnFocusLost( TRUE );
03232         gViewerWindow->setTopCtrl( NULL );
03233 
03234         if( mRenameItem )
03235         {
03236                 setSelectionFromRoot( mRenameItem, TRUE );
03237                 mRenameItem = NULL;
03238         }
03239 
03240         // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible.
03241         scrollToShowSelection();
03242 }
03243 
03244 void LLFolderView::revertRenamingItem( void )
03245 {
03246         mRenamer->setCommitOnFocusLost( FALSE );
03247         mRenamer->setFocus( FALSE );
03248         mRenamer->setVisible( FALSE );
03249         mRenamer->setCommitOnFocusLost( TRUE );
03250         gViewerWindow->setTopCtrl( NULL );
03251 
03252         if( mRenameItem )
03253         {
03254                 setSelectionFromRoot( mRenameItem, TRUE );
03255                 mRenameItem = NULL;
03256         }
03257 }
03258 
03259 void LLFolderView::removeSelectedItems( void )
03260 {
03261         if(getVisible() && mEnabled)
03262         {
03263                 // just in case we're removing the renaming item.
03264                 mRenameItem = NULL;
03265 
03266                 // create a temporary structure which we will use to remove
03267                 // items, since the removal will futz with internal data
03268                 // structures.
03269                 std::vector<LLFolderViewItem*> items;
03270                 S32 count = mSelectedItems.size();
03271                 if(count == 0) return;
03272                 LLFolderViewItem* item = NULL;
03273                 selected_items_t::iterator item_it;
03274                 for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
03275                 {
03276                         item = *item_it;
03277                         if(item->isRemovable())
03278                         {
03279                                 items.push_back(item);
03280                         }
03281                         else
03282                         {
03283                                 llinfos << "Cannot delete " << item->getName() << llendl;
03284                                 return;
03285                         }
03286                 }
03287 
03288                 // iterate through the new container.
03289                 count = items.size();
03290                 LLUUID new_selection_id;
03291                 if(count == 1)
03292                 {
03293                         LLFolderViewItem* item_to_delete = items[0];
03294                         LLFolderViewFolder* parent = item_to_delete->getParentFolder();
03295                         LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE);
03296                         if (!new_selection)
03297                         {
03298                                 new_selection = item_to_delete->getPreviousOpenNode(FALSE);
03299                         }
03300                         if(parent)
03301                         {
03302                                 if (parent->removeItem(item_to_delete))
03303                                 {
03304                                         // change selection on successful delete
03305                                         if (new_selection)
03306                                         {
03307                                                 setSelectionFromRoot(new_selection, new_selection->isOpen(), gViewerWindow->childHasKeyboardFocus(this));
03308                                         }
03309                                         else
03310                                         {
03311                                                 setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this));
03312                                         }
03313                                 }
03314                         }
03315                         arrangeAll();
03316                 }
03317                 else if (count > 1)
03318                 {
03319                         LLDynamicArray<LLFolderViewEventListener*> listeners;
03320                         LLFolderViewEventListener* listener;
03321                         LLFolderViewItem* last_item = items[count - 1];
03322                         LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE);
03323                         while(new_selection && new_selection->isSelected())
03324                         {
03325                                 new_selection = new_selection->getNextOpenNode(FALSE);
03326                         }
03327                         if (!new_selection)
03328                         {
03329                                 new_selection = last_item->getPreviousOpenNode(FALSE);
03330                                 while (new_selection && new_selection->isSelected())
03331                                 {
03332                                         new_selection = new_selection->getPreviousOpenNode(FALSE);
03333                                 }
03334                         }
03335                         if (new_selection)
03336                         {
03337                                 setSelectionFromRoot(new_selection, new_selection->isOpen(), gViewerWindow->childHasKeyboardFocus(this));
03338                         }
03339                         else
03340                         {
03341                                 setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this));
03342                         }
03343 
03344                         for(S32 i = 0; i < count; ++i)
03345                         {
03346                                 listener = items[i]->getListener();
03347                                 if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::FAIL))
03348                                 {
03349                                         listeners.put(listener);
03350                                 }
03351                         }
03352                         listener = listeners.get(0);
03353                         if(listener)
03354                         {
03355                                 listener->removeBatch(listeners);
03356                         }
03357                 }
03358                 arrangeAll();
03359                 scrollToShowSelection();
03360         }
03361 }
03362 
03363 // open the selected item.
03364 void LLFolderView::openSelectedItems( void )
03365 {
03366         if(getVisible() && mEnabled)
03367         {
03368                 if (mSelectedItems.size() == 1)
03369                 {
03370                         mSelectedItems.front()->open();         /* Flawfinder: ignore */
03371                 }
03372                 else
03373                 {
03374                         S32 left, top;
03375                         gFloaterView->getNewFloaterPosition(&left, &top);
03376                         LLMultiPreview* multi_previewp = new LLMultiPreview(LLRect(left, top, left + 300, top - 100));
03377                         gFloaterView->getNewFloaterPosition(&left, &top);
03378                         LLMultiProperties* multi_propertiesp = new LLMultiProperties(LLRect(left, top, left + 300, top - 100));
03379 
03380                         selected_items_t::iterator item_it;
03381                         for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
03382                         {
03383                                 // IT_{OBJECT,ATTACHMENT} creates LLProperties
03384                                 // floaters; others create LLPreviews.  Put
03385                                 // each one in the right type of container.
03386                                 LLFolderViewEventListener* listener = (*item_it)->getListener();
03387                                 bool is_prop = listener && (listener->getInventoryType() == LLInventoryType::IT_OBJECT || listener->getInventoryType() == LLInventoryType::IT_ATTACHMENT);
03388                                 if (is_prop)
03389                                         LLFloater::setFloaterHost(multi_propertiesp);
03390                                 else
03391                                         LLFloater::setFloaterHost(multi_previewp);
03392                                 (*item_it)->open();
03393                         }
03394 
03395                         LLFloater::setFloaterHost(NULL);
03396                         // *NOTE: LLMulti* will safely auto-delete when open'd
03397                         // without any children.
03398                         multi_previewp->open();
03399                         multi_propertiesp->open();
03400                 }
03401         }
03402 }
03403 
03404 void LLFolderView::propertiesSelectedItems( void )
03405 {
03406         if(getVisible() && mEnabled)
03407         {
03408                 if (mSelectedItems.size() == 1)
03409                 {
03410                         LLFolderViewItem* folder_item = mSelectedItems.front();
03411                         if(!folder_item) return;
03412                         folder_item->getListener()->showProperties();
03413                 }
03414                 else
03415                 {
03416                         S32 left, top;
03417                         gFloaterView->getNewFloaterPosition(&left, &top);
03418 
03419                         LLMultiProperties* multi_propertiesp = new LLMultiProperties(LLRect(left, top, left + 100, top - 100));
03420 
03421                         LLFloater::setFloaterHost(multi_propertiesp);
03422 
03423                         selected_items_t::iterator item_it;
03424                         for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
03425                         {
03426                                 (*item_it)->getListener()->showProperties();
03427                         }
03428 
03429                         LLFloater::setFloaterHost(NULL);
03430                         multi_propertiesp->open();              /* Flawfinder: ignore */
03431                 }
03432         }
03433 }
03434 
03435 void LLFolderView::autoOpenItem( LLFolderViewFolder* item )
03436 {
03437         if (mAutoOpenItems.check() == item || mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH)
03438         {
03439                 return;
03440         }
03441 
03442         // close auto-opened folders
03443         LLFolderViewFolder* close_item = mAutoOpenItems.check();
03444         while (close_item && close_item != item->getParentFolder())
03445         {
03446                 mAutoOpenItems.pop();
03447                 close_item->setOpenArrangeRecursively(FALSE);
03448                 close_item = mAutoOpenItems.check();
03449         }
03450 
03451         item->requestArrange();
03452 
03453         mAutoOpenItems.push(item);
03454         
03455         item->setOpen(TRUE);
03456         scrollToShowItem(item);
03457 }
03458 
03459 void LLFolderView::closeAutoOpenedFolders()
03460 {
03461         while (mAutoOpenItems.check())
03462         {
03463                 LLFolderViewFolder* close_item = mAutoOpenItems.pop();
03464                 close_item->setOpen(FALSE);
03465         }
03466 
03467         if (mAutoOpenCandidate)
03468         {
03469                 mAutoOpenCandidate->setAutoOpenCountdown(0.f);
03470         }
03471         mAutoOpenCandidate = NULL;
03472         mAutoOpenTimer.stop();
03473 }
03474 
03475 BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder)
03476 {
03477         if (folder && mAutoOpenCandidate == folder)
03478         {
03479                 if (mAutoOpenTimer.getStarted())
03480                 {
03481                         if (!mAutoOpenCandidate->isOpen())
03482                         {
03483                                 mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f));
03484                         }
03485                         if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime)
03486                         {
03487                                 autoOpenItem(folder);
03488                                 mAutoOpenTimer.stop();
03489                                 return TRUE;
03490                         }
03491                 }
03492                 return FALSE;
03493         }
03494 
03495         // otherwise new candidate, restart timer
03496         if (mAutoOpenCandidate)
03497         {
03498                 mAutoOpenCandidate->setAutoOpenCountdown(0.f);
03499         }
03500         mAutoOpenCandidate = folder;
03501         mAutoOpenTimer.start();
03502         return FALSE;
03503 }
03504 
03505 BOOL LLFolderView::canCopy()
03506 {
03507         if (!(getVisible() && mEnabled && (mSelectedItems.size() > 0)))
03508         {
03509                 return FALSE;
03510         }
03511 
03512         selected_items_t::iterator selected_it;
03513         for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
03514         {
03515                 LLFolderViewItem* item = *selected_it;
03516                 if (!item->getListener()->isItemCopyable())
03517                 {
03518                         return FALSE;
03519                 }
03520         }
03521         return TRUE;
03522 }
03523 
03524 // copy selected item
03525 void LLFolderView::copy()
03526 {
03527         // *NOTE: total hack to clear the inventory clipboard
03528         LLInventoryClipboard::instance().reset();
03529         S32 count = mSelectedItems.size();
03530         if(getVisible() && mEnabled && (count > 0))
03531         {
03532                 LLFolderViewEventListener* listener = NULL;
03533                 selected_items_t::iterator item_it;
03534                 for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
03535                 {
03536                         listener = (*item_it)->getListener();
03537                         if(listener)
03538                         {
03539                                 listener->copyToClipboard();
03540                         }
03541                 }
03542         }
03543         mSearchString.clear();
03544 }
03545 
03546 BOOL LLFolderView::canCut()
03547 {
03548         return FALSE;
03549 }
03550 
03551 void LLFolderView::cut()
03552 {
03553         // implement Windows-style cut-and-leave
03554 }
03555 
03556 BOOL LLFolderView::canPaste()
03557 {
03558         if (mSelectedItems.empty())
03559         {
03560                 return FALSE;
03561         }
03562 
03563         if(getVisible() && mEnabled)
03564         {
03565                 selected_items_t::iterator item_it;
03566                 for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
03567                 {
03568                         // *TODO: only check folders and parent folders of items
03569                         LLFolderViewItem* item = (*item_it);
03570                         LLFolderViewEventListener* listener = item->getListener();
03571                         if(!listener || !listener->isClipboardPasteable())
03572                         {
03573                                 LLFolderViewFolder* folderp = item->getParentFolder();
03574                                 listener = folderp->getListener();
03575                                 if (!listener || !listener->isClipboardPasteable())
03576                                 {
03577                                         return FALSE;
03578                                 }
03579                         }
03580                 }
03581                 return TRUE;
03582         }
03583         return FALSE;
03584 }
03585 
03586 // paste selected item
03587 void LLFolderView::paste()
03588 {
03589         if(getVisible() && mEnabled)
03590         {
03591                 // find set of unique folders to paste into
03592                 std::set<LLFolderViewItem*> folder_set;
03593 
03594                 selected_items_t::iterator selected_it;
03595                 for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
03596                 {
03597                         LLFolderViewItem* item = *selected_it;
03598                         LLFolderViewEventListener* listener = item->getListener();
03599                         if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY)
03600                         {
03601                                 item = item->getParentFolder();
03602                         }
03603                         folder_set.insert(item);
03604                 }
03605 
03606                 std::set<LLFolderViewItem*>::iterator set_iter;
03607                 for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter)
03608                 {
03609                         LLFolderViewEventListener* listener = (*set_iter)->getListener();
03610                         if(listener && listener->isClipboardPasteable())
03611                         {
03612                                 listener->pasteFromClipboard();
03613                         }
03614                 }
03615         }
03616         mSearchString.clear();
03617 }
03618 
03619 // public rename functionality - can only start the process
03620 void LLFolderView::startRenamingSelectedItem( void )
03621 {
03622         // make sure selection is visible
03623         scrollToShowSelection();
03624 
03625         S32 count = mSelectedItems.size();
03626         LLFolderViewItem* item = NULL;
03627         if(count > 0)
03628         {
03629                 item = mSelectedItems.front();
03630         }
03631         if(getVisible() && mEnabled && (count == 1) && item && item->getListener() &&
03632            item->getListener()->isItemRenameable())
03633         {
03634                 mRenameItem = item;
03635 
03636                 S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 + item->getIndentation();
03637                 S32 y = llfloor(item->getRect().getHeight()-sFont->getLineHeight()-2);
03638                 item->localPointToScreen( x, y, &x, &y );
03639                 screenPointToLocal( x, y, &x, &y );
03640                 mRenamer->setOrigin( x, y );
03641 
03642                 S32 scroller_height = 0;
03643                 S32 scroller_width = gViewerWindow->getWindowWidth();
03644                 BOOL dummy_bool;
03645                 if (mScrollContainer)
03646                 {
03647                         mScrollContainer->calcVisibleSize( &scroller_width, &scroller_height, &dummy_bool, &dummy_bool);
03648                 }
03649 
03650                 S32 width = llmax(llmin(item->getRect().getWidth() - x, scroller_width - x - mRect.mLeft), MINIMUM_RENAMER_WIDTH);
03651                 S32 height = llfloor(sFont->getLineHeight() + RENAME_HEIGHT_PAD);
03652                 mRenamer->reshape( width, height, TRUE );
03653 
03654                 mRenamer->setText(item->getName());
03655                 mRenamer->selectAll();
03656                 mRenamer->setVisible( TRUE );
03657                 // set focus will fail unless item is visible
03658                 mRenamer->setFocus( TRUE );
03659                 mRenamer->setFocusLostCallback(renamer_focus_lost);
03660                 gViewerWindow->setTopCtrl( mRenamer );
03661         }
03662 }
03663 
03664 void LLFolderView::setFocus(BOOL focus)
03665 {
03666         if (focus)
03667         {
03668                 if(!hasFocus())
03669                 {
03670                         gEditMenuHandler = this;
03671                 }
03672         }
03673 
03674         LLFolderViewFolder::setFocus(focus);
03675 }
03676 
03677 BOOL LLFolderView::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
03678 {
03679         BOOL handled = FALSE;
03680 
03681         // SL-51858: Key presses are not being passed to the Popup menu.
03682         // A proper fix is non-trivial so instead just close the menu.
03683         LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
03684         if (menu->isOpen())
03685         {
03686                 LLMenuGL::sMenuContainer->hideMenus();
03687         }
03688 
03689         LLView *item = NULL;
03690         if (getChildCount() > 0)
03691         {
03692                 item = *(getChildList()->begin());
03693         }
03694 
03695         if( getVisible() && mEnabled && !called_from_parent )
03696         {
03697                 switch( key )
03698                 {
03699                 case KEY_F2:
03700                         mSearchString.clear();
03701                         startRenamingSelectedItem();
03702                         handled = TRUE;
03703                         break;
03704 
03705                 case KEY_RETURN:
03706                         if (mask == MASK_NONE)
03707                         {
03708                                 if( mRenameItem && mRenamer->getVisible() )
03709                                 {
03710                                         finishRenamingItem();
03711                                         mSearchString.clear();
03712                                         handled = TRUE;
03713                                 }
03714                                 else
03715                                 {
03716                                         LLFolderView::openSelectedItems();
03717                                         handled = TRUE;
03718                                 }
03719                         }
03720                         break;
03721 
03722                 case KEY_ESCAPE:
03723                         // mark flag don't commit
03724                         if( mRenameItem && mRenamer->getVisible() )
03725                         {
03726                                 revertRenamingItem();
03727                                 handled = TRUE;
03728                         }
03729                         else
03730                         {
03731                                 if( gViewerWindow->childHasKeyboardFocus( this ) )
03732                                 {
03733                                         gViewerWindow->setKeyboardFocus( NULL, NULL );
03734                                 }
03735                         }
03736                         mSearchString.clear();
03737                         break;
03738 
03739                 case KEY_PAGE_UP:
03740                         mSearchString.clear();
03741                         mScrollContainer->pageUp(30);
03742                         handled = TRUE;
03743                         break;
03744 
03745                 case KEY_PAGE_DOWN:
03746                         mSearchString.clear();
03747                         mScrollContainer->pageDown(30);
03748                         handled = TRUE;
03749                         break;
03750 
03751                 case KEY_HOME:
03752                         mSearchString.clear();
03753                         mScrollContainer->goToTop();
03754                         handled = TRUE;
03755                         break;
03756 
03757                 case KEY_END:
03758                         mSearchString.clear();
03759                         mScrollContainer->goToBottom();
03760                         break;
03761 
03762                 case KEY_DOWN:
03763                         if((mSelectedItems.size() > 0) && mScrollContainer)
03764                         {
03765                                 LLFolderViewItem* last_selected = getCurSelectedItem();
03766 
03767                                 if (!mKeyboardSelection)
03768                                 {
03769                                         setSelection(last_selected, FALSE, TRUE);
03770                                         mKeyboardSelection = TRUE;
03771                                 }
03772 
03773                                 LLFolderViewItem* next = NULL;
03774                                 if (mask & MASK_SHIFT)
03775                                 {
03776                                         // don't shift select down to children of folders (they are implicitly selected through parent)
03777                                         next = last_selected->getNextOpenNode(FALSE);
03778                                         if (next)
03779                                         {
03780                                                 if (next->isSelected())
03781                                                 {
03782                                                         // shrink selection
03783                                                         changeSelectionFromRoot(last_selected, FALSE);
03784                                                 }
03785                                                 else if (last_selected->getParentFolder() == next->getParentFolder())
03786                                                 {
03787                                                         // grow selection
03788                                                         changeSelectionFromRoot(next, TRUE);
03789                                                 }
03790                                         }
03791                                 }
03792                                 else
03793                                 {
03794                                         next = last_selected->getNextOpenNode();
03795                                         if( next )
03796                                         {
03797                                                 if (next == last_selected)
03798                                                 {
03799                                                         return FALSE;
03800                                                 }
03801                                                 setSelection( next, FALSE, TRUE );
03802                                         }
03803                                 }
03804                                 scrollToShowSelection();
03805                                 mSearchString.clear();
03806                                 handled = TRUE;
03807                         }
03808                         break;
03809 
03810                 case KEY_UP:
03811                         if((mSelectedItems.size() > 0) && mScrollContainer)
03812                         {
03813                                 LLFolderViewItem* last_selected = mSelectedItems.back();
03814 
03815                                 if (!mKeyboardSelection)
03816                                 {
03817                                         setSelection(last_selected, FALSE, TRUE);
03818                                         mKeyboardSelection = TRUE;
03819                                 }
03820 
03821                                 LLFolderViewItem* prev = NULL;
03822                                 if (mask & MASK_SHIFT)
03823                                 {
03824                                         // don't shift select down to children of folders (they are implicitly selected through parent)
03825                                         prev = last_selected->getPreviousOpenNode(FALSE);
03826                                         if (prev)
03827                                         {
03828                                                 if (prev->isSelected())
03829                                                 {
03830                                                         // shrink selection
03831                                                         changeSelectionFromRoot(last_selected, FALSE);
03832                                                 }
03833                                                 else if (last_selected->getParentFolder() == prev->getParentFolder())
03834                                                 {
03835                                                         // grow selection
03836                                                         changeSelectionFromRoot(prev, TRUE);
03837                                                 }
03838                                         }
03839                                 }
03840                                 else
03841                                 {
03842                                         prev = last_selected->getPreviousOpenNode();
03843                                         if( prev )
03844                                         {
03845                                                 if (prev == this)
03846                                                 {
03847                                                         return FALSE;
03848                                                 }
03849                                                 setSelection( prev, FALSE, TRUE );
03850                                         }
03851                                 }
03852                                 scrollToShowSelection();
03853                                 mSearchString.clear();
03854 
03855                                 handled = TRUE;
03856                         }
03857                         break;
03858 
03859                 case KEY_RIGHT:
03860                         if(mSelectedItems.size())
03861                         {
03862                                 LLFolderViewItem* last_selected = getCurSelectedItem();
03863                                 last_selected->setOpen( TRUE );
03864                                 mSearchString.clear();
03865                                 handled = TRUE;
03866                         }
03867                         break;
03868 
03869                 case KEY_LEFT:
03870                         if(mSelectedItems.size())
03871                         {
03872                                 LLFolderViewItem* last_selected = getCurSelectedItem();
03873                                 LLFolderViewItem* parent_folder = last_selected->getParentFolder();
03874                                 if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder())
03875                                 {
03876                                         setSelection(parent_folder, FALSE, TRUE);
03877                                 }
03878                                 else
03879                                 {
03880                                         last_selected->setOpen( FALSE );
03881                                 }
03882                                 mSearchString.clear();
03883                                 scrollToShowSelection();
03884                                 handled = TRUE;
03885                         }
03886                         break;
03887                 }
03888         }
03889 
03890         if (!handled && gFocusMgr.childHasKeyboardFocus(getRoot()))
03891         {
03892                 if (key == KEY_BACKSPACE)
03893                 {
03894                         mSearchTimer.reset();
03895                         if (mSearchString.size())
03896                         {
03897                                 mSearchString.erase(mSearchString.size() - 1, 1);
03898                         }
03899                         search(getCurSelectedItem(), mSearchString.c_str(), FALSE);
03900                         handled = TRUE;
03901                 }
03902         }
03903 
03904         return handled;
03905 }
03906 
03907 
03908 BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
03909 {
03910         if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
03911         {
03912                 return FALSE;
03913         }
03914 
03915         if (uni_char > 0x7f)
03916         {
03917                 llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl;
03918                 return FALSE;
03919         }
03920 
03921         BOOL handled = FALSE;
03922         if (gFocusMgr.childHasKeyboardFocus(getRoot()))
03923         {
03924                 //do text search
03925                 if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout"))
03926                 {
03927                         mSearchString.clear();
03928                 }
03929                 mSearchTimer.reset();
03930                 if (mSearchString.size() < 128)
03931                 {
03932                         mSearchString += uni_char;
03933                 }
03934                 search(getCurSelectedItem(), mSearchString.c_str(), FALSE);
03935 
03936                 handled = TRUE;
03937         }
03938 
03939         return handled;
03940 }
03941 
03942 
03943 BOOL LLFolderView::canDoDelete()
03944 {
03945         if (mSelectedItems.size() == 0) return FALSE;
03946         selected_items_t::iterator item_it;
03947         for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
03948         {
03949                 if (!(*item_it)->getListener()->isItemRemovable())
03950                 {
03951                         return FALSE;
03952                 }
03953         }
03954         return TRUE;
03955 }
03956 
03957 void LLFolderView::doDelete()
03958 {
03959         if(mSelectedItems.size() > 0)
03960         {                               
03961                 removeSelectedItems();
03962         }
03963 }
03964 
03965 
03966 BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask )
03967 {
03968         mKeyboardSelection = FALSE;
03969         mSearchString.clear();
03970 
03971         setFocus(TRUE);
03972 
03973         return LLView::handleMouseDown( x, y, mask );
03974 }
03975 
03976 void LLFolderView::onFocusLost( )
03977 {
03978         if( gEditMenuHandler == this )
03979         {
03980                 gEditMenuHandler = NULL;
03981         }
03982         LLUICtrl::onFocusLost();
03983 }
03984 
03985 BOOL LLFolderView::search(LLFolderViewItem* first_item, const LLString &search_string, BOOL backward)
03986 {
03987         // get first selected item
03988         LLFolderViewItem* search_item = first_item;
03989 
03990         // make sure search string is upper case
03991         LLString upper_case_string = search_string;
03992         LLString::toUpper(upper_case_string);
03993 
03994         // if nothing selected, select first item in folder
03995         if (!search_item)
03996         {
03997                 // start from first item
03998                 search_item = getNextFromChild(NULL);
03999         }
04000 
04001         // search over all open nodes for first substring match (with wrapping)
04002         BOOL found = FALSE;
04003         LLFolderViewItem* original_search_item = search_item;
04004         do
04005         {
04006                 // wrap at end
04007                 if (!search_item)
04008                 {
04009                         if (backward)
04010                         {
04011                                 search_item = getPreviousFromChild(NULL);
04012                         }
04013                         else
04014                         {
04015                                 search_item = getNextFromChild(NULL);
04016                         }
04017                         if (!search_item || search_item == original_search_item)
04018                         {
04019                                 break;
04020                         }
04021                 }
04022 
04023                 const LLString current_item_label(search_item->getSearchableLabel());
04024                 S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size());
04025                 if (!current_item_label.compare(0, search_string_length, upper_case_string))
04026                 {
04027                         found = TRUE;
04028                         break;
04029                 }
04030                 if (backward)
04031                 {
04032                         search_item = search_item->getPreviousOpenNode();
04033                 }
04034                 else
04035                 {
04036                         search_item = search_item->getNextOpenNode();
04037                 }
04038 
04039         } while(search_item != original_search_item);
04040         
04041 
04042         if (found)
04043         {
04044                 setSelection(search_item, FALSE, TRUE);
04045                 scrollToShowSelection();
04046         }
04047 
04048         return found;
04049 }
04050 
04051 BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask )
04052 {
04053         if (!getVisible())
04054         {
04055                 return FALSE;
04056         }
04057 
04058         return LLView::handleDoubleClick( x, y, mask );
04059 }
04060 
04061 BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
04062 {
04063         // all user operations move keyboard focus to inventory
04064         // this way, we know when to stop auto-updating a search
04065         setFocus(TRUE);
04066 
04067         BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
04068         S32 count = mSelectedItems.size();
04069         LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
04070         if(handled && (count > 0) && menu)
04071         {
04072                 //menu->empty();
04073                 const LLView::child_list_t *list = menu->getChildList();
04074 
04075                 LLView::child_list_t::const_iterator menu_itor;
04076                 for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor)
04077                 {
04078                         (*menu_itor)->setVisible(TRUE);
04079                         (*menu_itor)->setEnabled(TRUE);
04080                 }
04081                 
04082                 // Successively filter out invalid options
04083                 selected_items_t::iterator item_itor;
04084                 U32 flags = FIRST_SELECTED_ITEM;
04085                 for (item_itor = mSelectedItems.begin(); item_itor != mSelectedItems.end(); ++item_itor)
04086                 {
04087                         (*item_itor)->buildContextMenu(*menu, flags);
04088                         flags = 0x0;
04089                 }
04090 
04091                 menu->arrange();
04092                 menu->updateParent(LLMenuGL::sMenuContainer);
04093                 LLMenuGL::showPopup(this, menu, x, y);
04094         }
04095         else
04096         {
04097                 if(menu && menu->getVisible())
04098                 {
04099                         menu->setVisible(FALSE);
04100                 }
04101                 setSelection(NULL, FALSE, TRUE);
04102         }
04103         return handled;
04104 }
04105 
04106 BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask )
04107 {
04108         return LLView::handleHover( x, y, mask );
04109 }
04110 
04111 BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
04112                                                                          EDragAndDropType cargo_type,
04113                                                                          void* cargo_data, 
04114                                                                          EAcceptance* accept,
04115                                                                          LLString& tooltip_msg)
04116 {
04117         mDragAndDropThisFrame = TRUE;
04118         BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data,
04119                                                                                          accept, tooltip_msg);
04120 
04121         if (handled)
04122         {
04123                 lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl;
04124         }
04125 
04126         return handled;
04127 }
04128 
04129 BOOL LLFolderView::handleScrollWheel(S32 x, S32 y, S32 clicks)
04130 {
04131         if (mScrollContainer)
04132         {
04133                 return mScrollContainer->handleScrollWheel(x, y, clicks);
04134         }
04135         return FALSE;
04136 }
04137 
04138 void LLFolderView::deleteAllChildren()
04139 {
04140         if(gViewerWindow->hasTopCtrl(mRenamer))
04141         {
04142                 gViewerWindow->setTopCtrl(NULL);
04143         }
04144         LLView::deleteViewByHandle(mPopupMenuHandle);
04145         mPopupMenuHandle = LLViewHandle::sDeadHandle;
04146         mRenamer = NULL;
04147         mRenameItem = NULL;
04148         clearSelection();
04149         LLView::deleteAllChildren();
04150 }
04151 
04152 void LLFolderView::scrollToShowSelection()
04153 {
04154         if (mSelectedItems.size())
04155         {
04156                 mNeedsScroll = TRUE;
04157         }
04158 }
04159 
04160 // If the parent is scroll containter, scroll it to make the selection
04161 // is maximally visible.
04162 void LLFolderView::scrollToShowItem(LLFolderViewItem* item)
04163 {
04164         // don't scroll to items when mouse is being used to scroll/drag and drop
04165         if (gFocusMgr.childHasMouseCapture(mScrollContainer))
04166         {
04167                 mNeedsScroll = FALSE;
04168                 return;
04169         }
04170         if(item && mScrollContainer)
04171         {
04172                 LLRect local_rect = item->getRect();
04173                 LLRect item_scrolled_rect; // item position relative to display area of scroller
04174                 
04175                 S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); 
04176                 S32 label_height = llround(sFont->getLineHeight()); 
04177                 // when navigating with keyboard, only move top of folders on screen, otherwise show whole folder
04178                 S32 max_height_to_show = gFocusMgr.childHasKeyboardFocus(this) ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); 
04179                 item->localPointToOtherView(item->getIndentation(), llmax(0, local_rect.getHeight() - max_height_to_show), &item_scrolled_rect.mLeft, &item_scrolled_rect.mBottom, mScrollContainer);
04180                 item->localPointToOtherView(local_rect.getWidth(), local_rect.getHeight(), &item_scrolled_rect.mRight, &item_scrolled_rect.mTop, mScrollContainer);
04181 
04182                 item_scrolled_rect.mRight = llmin(item_scrolled_rect.mLeft + MIN_ITEM_WIDTH_VISIBLE, item_scrolled_rect.mRight);
04183                 LLCoordGL scroll_offset(-mScrollContainer->getBorderWidth() - item_scrolled_rect.mLeft, 
04184                                 mScrollContainer->getRect().getHeight() - item_scrolled_rect.mTop - 1);
04185 
04186                 S32 max_scroll_offset = getVisibleRect().getHeight() - item_scrolled_rect.getHeight();
04187                 if (item != mLastScrollItem || // if we're scrolling to focus on a new item
04188                 // or the item has just appeared on screen and it wasn't onscreen before
04189                         (scroll_offset.mY > 0 && scroll_offset.mY < max_scroll_offset && 
04190                         (mLastScrollOffset.mY < 0 || mLastScrollOffset.mY > max_scroll_offset)))
04191                 {
04192                         // we now have a position on screen that we want to keep stable
04193                         // offset of selection relative to top of visible area
04194                         mLastScrollOffset = scroll_offset;
04195                         mLastScrollItem = item;
04196                 }
04197 
04198                 mScrollContainer->scrollToShowRect( item_scrolled_rect, mLastScrollOffset );
04199 
04200                 // after scrolling, store new offset
04201                 // in case we don't have room to maintain the original position
04202                 LLCoordGL new_item_left_top;
04203                 item->localPointToOtherView(item->getIndentation(), item->getRect().getHeight(), &new_item_left_top.mX, &new_item_left_top.mY, mScrollContainer);
04204                 mLastScrollOffset.set(-mScrollContainer->getBorderWidth() - new_item_left_top.mX, mScrollContainer->getRect().getHeight() - new_item_left_top.mY - 1);
04205         }
04206 }
04207 
04208 LLRect LLFolderView::getVisibleRect()
04209 {
04210         S32 visible_height = mScrollContainer->getRect().getHeight();
04211         S32 visible_width = mScrollContainer->getRect().getWidth();
04212         LLRect visible_rect;
04213         visible_rect.setLeftTopAndSize(-mRect.mLeft, visible_height - mRect.mBottom, visible_width, visible_height);
04214         return visible_rect;
04215 }
04216 
04217 BOOL LLFolderView::getShowSelectionContext()
04218 {
04219         if (mShowSelectionContext)
04220         {
04221                 return TRUE;
04222         }
04223         LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
04224         if (menu && menu->getVisible())
04225         {
04226                 return TRUE;
04227         }
04228         return FALSE;
04229 }
04230 
04231 void LLFolderView::setShowSingleSelection(BOOL show)
04232 {
04233         if (show != mShowSingleSelection)
04234         {
04235                 mMultiSelectionFadeTimer.reset();
04236                 mShowSingleSelection = show;
04237         }
04238 }
04239 
04240 void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp)
04241 {
04242         mItemMap[id] = itemp;
04243 }
04244 
04245 void LLFolderView::removeItemID(const LLUUID& id)
04246 {
04247         mItemMap.erase(id);
04248 }
04249 
04250 LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id)
04251 {
04252         if (id.isNull())
04253         {
04254                 return this;
04255         }
04256 
04257         std::map<LLUUID, LLFolderViewItem*>::iterator map_it;
04258         map_it = mItemMap.find(id);
04259         if (map_it != mItemMap.end())
04260         {
04261                 return map_it->second;
04262         }
04263 
04264         return NULL;
04265 }
04266 
04267 
04268 // Main idle routine
04269 void LLFolderView::doIdle()
04270 {
04271         LLFastTimer t2(LLFastTimer::FTM_INVENTORY);
04272 
04273         BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters");
04274         if (debug_filters != getDebugFilters())
04275         {
04276                 mDebugFilters = debug_filters;
04277                 arrangeAll();
04278         }
04279 
04280         mFilter.clearModified();
04281         BOOL filter_modified_and_active = mCompletedFilterGeneration < mFilter.getCurrentGeneration() && 
04282                                                                                 mFilter.isNotDefault();
04283         mNeedsAutoSelect = filter_modified_and_active &&
04284                                                         !(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture());
04285         
04286         // filter to determine visiblity before arranging
04287         filterFromRoot();
04288 
04289         // automatically show matching items, and select first one
04290         // do this every frame until user puts keyboard focus into the inventory window
04291         // signaling the end of the automatic update
04292         // only do this when mNeedsFilter is set, meaning filtered items have
04293         // potentially changed
04294         if (mNeedsAutoSelect)
04295         {
04296                 LLFastTimer t3(LLFastTimer::FTM_AUTO_SELECT);
04297                 // select new item only if a filtered item not currently selected
04298                 LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back();
04299                 if ((!selected_itemp || !selected_itemp->getFiltered()) && !mAutoSelectOverride)
04300                 {
04301                         // select first filtered item
04302                         LLSelectFirstFilteredItem filter;
04303                         applyFunctorRecursively(filter);
04304                 }
04305                 scrollToShowSelection();
04306         }
04307 
04308         BOOL is_visible = isInVisibleChain();
04309 
04310         if ( is_visible )
04311         {
04312                 sanitizeSelection();
04313                 if( needsArrange() )
04314                 {
04315                         arrangeFromRoot();
04316                 }
04317         }
04318 
04319         if (mSelectedItems.size() && mNeedsScroll)
04320         {
04321                 scrollToShowItem(mSelectedItems.back());
04322                 // continue scrolling until animated layout change is done
04323                 if (getCompletedFilterGeneration() >= mFilter.getMinRequiredGeneration() &&
04324                         (!needsArrange() || !is_visible))
04325                 {
04326                         mNeedsScroll = FALSE;
04327                 }
04328         }
04329 
04330         if (mSelectionChanged && mSelectCallback)
04331         {
04332                 //RN: we use keyboard focus as a proxy for user-explicit actions
04333                 mSelectCallback(mSelectedItems, gFocusMgr.childHasKeyboardFocus(this), mUserData);
04334         }
04335         mSelectionChanged = FALSE;
04336 }
04337 
04338 
04339 //static
04340 void LLFolderView::idle(void* user_data)
04341 {
04342         LLFolderView* self = (LLFolderView*)user_data;
04343         if ( self )
04344         {       // Do the real idle 
04345                 self->doIdle();
04346         }
04347 }
04348 
04349 
04350 void LLFolderView::dumpSelectionInformation()
04351 {
04352         llinfos << "LLFolderView::dumpSelectionInformation()" << llendl;
04353         llinfos << "****************************************" << llendl;
04354         selected_items_t::iterator item_it;
04355         for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
04356         {
04357                 llinfos << "  " << (*item_it)->getName() << llendl;
04358         }
04359         llinfos << "****************************************" << llendl;
04360 }
04361 
04365 bool LLInventorySort::updateSort(U32 order)
04366 {
04367         if (order != mSortOrder)
04368         {
04369                 mSortOrder = order;
04370                 mByDate = (order & LLInventoryFilter::SO_DATE);
04371                 mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP);
04372                 mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME);
04373                 return true;
04374         }
04375         return false;
04376 }
04377 
04378 bool LLInventorySort::operator()(LLFolderViewItem* a, LLFolderViewItem* b)
04379 {
04380         return mComparisonFunction(this, a, b);
04381 }
04382 
04383 bool LLInventorySort::compare(LLInventorySort *self, LLFolderViewItem* a, LLFolderViewItem* b)
04384 {
04385         // We sort by name if we aren't sorting by date
04386         // OR if these are folders and we are sorting folders by name.
04387         bool by_name = (!self->isByDate()
04388                                         || (self->isFoldersByName()
04389                                                 && (a->getSortGroup() != SG_ITEM)));
04390         
04391         if (a->getSortGroup() != b->getSortGroup())
04392         {
04393                 if (self->isSystemToTop())
04394                 {
04395                         // Group order is System Folders, Trash, Normal Folders, Items
04396                         return (a->getSortGroup() < b->getSortGroup());
04397                 }
04398                 else if (self->isByDate())
04399                 {
04400                         // Trash needs to go to the bottom if we are sorting by date
04401                         if ( (a->getSortGroup() == SG_TRASH_FOLDER)
04402                                 || (b->getSortGroup() == SG_TRASH_FOLDER))
04403                         {
04404                                 return (b->getSortGroup() == SG_TRASH_FOLDER);
04405                         }
04406                 }
04407         }
04408 
04409         if (by_name)
04410         {
04411                 S32 compare = LLString::compareDict(a->getLabel(), b->getLabel());
04412                 if (0 == compare)
04413                 {
04414                         return (a->getCreationDate() > b->getCreationDate());
04415                 }
04416                 else
04417                 {
04418                         return (compare < 0);
04419                 }
04420         }
04421         else
04422         {
04423                 // BUG: This is very very slow.  The getCreationDate() is log n in number
04424                 // of inventory items.
04425                 U32 first_create = a->getCreationDate();
04426                 U32 second_create = b->getCreationDate();
04427                 if (first_create == second_create)
04428                 {
04429                         return (LLString::compareDict(a->getLabel(), b->getLabel()) < 0);
04430                 }
04431                 else
04432                 {
04433                         return (first_create > second_create);
04434                 }
04435         }
04436 }
04437 
04438 void renamer_focus_lost( LLUICtrl* ctrl, void* userdata)
04439 {
04440         if( ctrl ) 
04441         {
04442                 ctrl->setVisible( FALSE );
04443         }
04444 }
04445 
04446 void delete_selected_item(void* user_data)
04447 {
04448         if(user_data)
04449         {
04450                 LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
04451                 fv->removeSelectedItems();
04452         }
04453 }
04454 
04455 void copy_selected_item(void* user_data)
04456 {
04457         if(user_data)
04458         {
04459                 LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
04460                 fv->copy();
04461         }
04462 }
04463 
04464 void paste_items(void* user_data)
04465 {
04466         if(user_data)
04467         {
04468                 LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
04469                 fv->paste();
04470         }
04471 }
04472 
04473 void open_selected_items(void* user_data)
04474 {
04475         if(user_data)
04476         {
04477                 LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
04478                 fv->openSelectedItems();
04479         }
04480 }
04481 
04482 void properties_selected_items(void* user_data)
04483 {
04484         if(user_data)
04485         {
04486                 LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
04487                 fv->propertiesSelectedItems();
04488         }
04489 }
04490 
04494 
04495 void LLFolderViewEventListener::arrangeAndSet(LLFolderViewItem* focus,
04496                                                                                           BOOL set_selection,
04497                                                                                           BOOL take_keyboard_focus)
04498 {
04499         if(!focus) return;
04500         LLFolderView* root = focus->getRoot();
04501         focus->getParentFolder()->requestArrange();
04502         if(set_selection)
04503         {
04504                 focus->setSelectionFromRoot(focus, TRUE, take_keyboard_focus);
04505                 if(root)
04506                 {
04507                         root->scrollToShowSelection();
04508                 }
04509         }
04510 }
04511 
04512 
04516 LLInventoryFilter::LLInventoryFilter(const LLString& name) :
04517         mName(name),
04518         mModified(FALSE),
04519         mNeedTextRebuild(TRUE)
04520 {
04521         mFilterOps.mFilterTypes = 0xffffffff;
04522         mFilterOps.mMinDate = 0;
04523         mFilterOps.mMaxDate = U32_MAX;
04524         mFilterOps.mHoursAgo = 0;
04525         mFilterOps.mShowFolderState = SHOW_NON_EMPTY_FOLDERS;
04526         mFilterOps.mPermissions = PERM_NONE;
04527         
04528         mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately
04529 
04530         mSubStringMatchOffset = 0;
04531         mFilterSubString = "";
04532         mFilterGeneration = 0;
04533         mMustPassGeneration = S32_MAX;
04534         mMinRequiredGeneration = 0;
04535         mFilterCount = 0;
04536         mNextFilterGeneration = mFilterGeneration + 1;
04537 
04538         mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff");
04539         mFilterBehavior = FILTER_NONE;
04540 }
04541 
04542 LLInventoryFilter::~LLInventoryFilter()
04543 {
04544 }
04545 
04546 BOOL LLInventoryFilter::check(LLFolderViewItem* item) 
04547 {
04548         U32 earliest;
04549 
04550         earliest = time_corrected() - mFilterOps.mHoursAgo * 3600;
04551         if (mFilterOps.mMinDate && mFilterOps.mMinDate < earliest)
04552         {
04553                 earliest = mFilterOps.mMinDate;
04554         }
04555         else if (!mFilterOps.mHoursAgo)
04556         {
04557                 earliest = 0;
04558         }
04559         LLFolderViewEventListener* listener = item->getListener();
04560         mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : LLString::npos;
04561         BOOL passed = (0x1 << listener->getInventoryType() & mFilterOps.mFilterTypes || listener->getInventoryType() == LLInventoryType::IT_NONE)
04562                                         && (mFilterSubString.size() == 0 || mSubStringMatchOffset != LLString::npos)
04563                                         && ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions)
04564                                         && (listener->getCreationDate() >= earliest && listener->getCreationDate() <= mFilterOps.mMaxDate);
04565         return passed;
04566 }
04567 
04568 const LLString LLInventoryFilter::getFilterSubString(BOOL trim)
04569 {
04570         return mFilterSubString;
04571 }
04572 
04573 std::string::size_type LLInventoryFilter::getStringMatchOffset() const
04574 {
04575         return mSubStringMatchOffset;
04576 }
04577 
04578 // has user modified default filter params?
04579 BOOL LLInventoryFilter::isNotDefault()
04580 {
04581         return mFilterOps.mFilterTypes != mDefaultFilterOps.mFilterTypes 
04582                 || mFilterSubString.size() 
04583                 || mFilterOps.mPermissions != mDefaultFilterOps.mPermissions
04584                 || mFilterOps.mMinDate != mDefaultFilterOps.mMinDate 
04585                 || mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate
04586                 || mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo;
04587 }
04588 
04589 BOOL LLInventoryFilter::isActive()
04590 {
04591         return mFilterOps.mFilterTypes != 0xffffffff 
04592                 || mFilterSubString.size() 
04593                 || mFilterOps.mPermissions != PERM_NONE 
04594                 || mFilterOps.mMinDate != 0 
04595                 || mFilterOps.mMaxDate != U32_MAX
04596                 || mFilterOps.mHoursAgo != 0;
04597 }
04598 
04599 BOOL LLInventoryFilter::isModified()
04600 {
04601         return mModified;
04602 }
04603 
04604 BOOL LLInventoryFilter::isModifiedAndClear()
04605 {
04606         BOOL ret = mModified;
04607         mModified = FALSE;
04608         return ret;
04609 }
04610 
04611 void LLInventoryFilter::setFilterTypes(U32 types)
04612 {
04613         if (mFilterOps.mFilterTypes != types)
04614         {
04615                 // keep current items only if no type bits getting turned off
04616                 BOOL fewer_bits_set = (mFilterOps.mFilterTypes & ~types);
04617                 BOOL more_bits_set = (~mFilterOps.mFilterTypes & types);
04618         
04619                 mFilterOps.mFilterTypes = types;
04620                 if (more_bits_set && fewer_bits_set)
04621                 {
04622                         // neither less or more restrive, both simultaneously
04623                         // so we need to filter from scratch
04624                         setModified(FILTER_RESTART);
04625                 }
04626                 else if (more_bits_set)
04627                 {
04628                         // target is only one of all requested types so more type bits == less restrictive
04629                         setModified(FILTER_LESS_RESTRICTIVE);
04630                 }
04631                 else if (fewer_bits_set)
04632                 {
04633                         setModified(FILTER_MORE_RESTRICTIVE);
04634                 }
04635 
04636         }
04637 }
04638 
04639 void LLInventoryFilter::setFilterSubString(const LLString& string)
04640 {
04641         if (mFilterSubString != string)
04642         {
04643                 // hitting BACKSPACE, for example
04644                 BOOL less_restrictive = mFilterSubString.size() >= string.size() && !mFilterSubString.substr(0, string.size()).compare(string);
04645                 // appending new characters
04646                 BOOL more_restrictive = mFilterSubString.size() < string.size() && !string.substr(0, mFilterSubString.size()).compare(mFilterSubString);
04647                 mFilterSubString = string;
04648                 LLString::toUpper(mFilterSubString);
04649                 LLString::trimHead(mFilterSubString);
04650 
04651                 if (less_restrictive)
04652                 {
04653                         setModified(FILTER_LESS_RESTRICTIVE);
04654                 }
04655                 else if (more_restrictive)
04656                 {
04657                         setModified(FILTER_MORE_RESTRICTIVE);
04658                 }
04659                 else
04660                 {
04661                         setModified(FILTER_RESTART);
04662                 }
04663         }
04664 }
04665 
04666 void LLInventoryFilter::setFilterPermissions(PermissionMask perms)
04667 {
04668         if (mFilterOps.mPermissions != perms)
04669         {
04670                 // keep current items only if no perm bits getting turned off
04671                 BOOL fewer_bits_set = (mFilterOps.mPermissions & ~perms);
04672                 BOOL more_bits_set = (~mFilterOps.mPermissions & perms);
04673                 mFilterOps.mPermissions = perms;
04674 
04675                 if (more_bits_set && fewer_bits_set)
04676                 {
04677                         setModified(FILTER_RESTART);
04678                 }
04679                 else if (more_bits_set)
04680                 {
04681                         // target must have all requested permission bits, so more bits == more restrictive
04682                         setModified(FILTER_MORE_RESTRICTIVE);
04683                 }
04684                 else if (fewer_bits_set)
04685                 {
04686                         setModified(FILTER_LESS_RESTRICTIVE);
04687                 }
04688         }
04689 }
04690 
04691 void LLInventoryFilter::setDateRange(U32 min_date, U32 max_date)
04692 {
04693         mFilterOps.mHoursAgo = 0;
04694         if (mFilterOps.mMinDate != min_date)
04695         {
04696                 mFilterOps.mMinDate = min_date;
04697                 setModified();
04698         }
04699         if (mFilterOps.mMaxDate != llmax(mFilterOps.mMinDate, max_date))
04700         {
04701                 mFilterOps.mMaxDate = llmax(mFilterOps.mMinDate, max_date);
04702                 setModified();
04703         }
04704 }
04705 
04706 void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl)
04707 {
04708         if (sl && !isSinceLogoff())
04709         {
04710                 setDateRange(mLastLogoff, U32_MAX);
04711                 setModified();
04712         }
04713         if (!sl && isSinceLogoff())
04714         {
04715                 setDateRange(0, U32_MAX);
04716                 setModified();
04717         }
04718 }
04719 
04720 BOOL LLInventoryFilter::isSinceLogoff()
04721 {
04722         return (mFilterOps.mMinDate == mLastLogoff) && (mFilterOps.mMaxDate == U32_MAX);
04723 }
04724 
04725 void LLInventoryFilter::setHoursAgo(U32 hours)
04726 {
04727         if (mFilterOps.mHoursAgo != hours)
04728         {
04729                 // *NOTE: need to cache last filter time, in case filter goes stale
04730                 BOOL less_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours > mFilterOps.mHoursAgo);
04731                 BOOL more_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours <= mFilterOps.mHoursAgo);
04732                 mFilterOps.mHoursAgo = hours;
04733                 mFilterOps.mMinDate = 0;
04734                 mFilterOps.mMaxDate = U32_MAX;
04735                 if (less_restrictive)
04736                 {
04737                         setModified(FILTER_LESS_RESTRICTIVE);
04738                 }
04739                 else if (more_restrictive)
04740                 {
04741                         setModified(FILTER_MORE_RESTRICTIVE);
04742                 }
04743                 else
04744                 {
04745                         setModified(FILTER_RESTART);
04746                 }
04747         }
04748 }
04749 void LLInventoryFilter::setShowFolderState(EFolderShow state)
04750 {
04751         if (mFilterOps.mShowFolderState != state)
04752         {
04753                 mFilterOps.mShowFolderState = state;
04754                 if (state == SHOW_NON_EMPTY_FOLDERS)
04755                 {
04756                         // showing fewer folders than before
04757                         setModified(FILTER_MORE_RESTRICTIVE);
04758                 }
04759                 else if (state == SHOW_ALL_FOLDERS)
04760                 {
04761                         // showing same folders as before and then some
04762                         setModified(FILTER_LESS_RESTRICTIVE);
04763                 }
04764                 else
04765                 {
04766                         setModified();
04767                 }
04768         }
04769 }
04770 
04771 void LLInventoryFilter::setSortOrder(U32 order)
04772 {
04773         if (mOrder != order)
04774         {
04775                 mOrder = order;
04776                 setModified();
04777         }
04778 }
04779 
04780 void LLInventoryFilter::markDefault()
04781 {
04782         mDefaultFilterOps = mFilterOps;
04783 }
04784 
04785 void LLInventoryFilter::resetDefault()
04786 {
04787         mFilterOps = mDefaultFilterOps;
04788         setModified();
04789 }
04790 
04791 void LLInventoryFilter::setModified(EFilterBehavior behavior)
04792 {
04793         mModified = TRUE;
04794         mNeedTextRebuild = TRUE;
04795         mFilterGeneration = mNextFilterGeneration++;
04796         
04797         if (mFilterBehavior == FILTER_NONE)
04798         {
04799                 mFilterBehavior = behavior;
04800         }
04801         else if (mFilterBehavior != behavior)
04802         {
04803                 // trying to do both less restrictive and more restrictive filter
04804                 // basically means restart from scratch
04805                 mFilterBehavior = FILTER_RESTART;
04806         }
04807 
04808         if (isNotDefault())
04809         {
04810                 // if not keeping current filter results, update last valid as well
04811                 switch(mFilterBehavior)
04812                 {
04813                 case FILTER_RESTART:
04814                         mMustPassGeneration = mFilterGeneration;
04815                         mMinRequiredGeneration = mFilterGeneration;
04816                         break;
04817                 case FILTER_LESS_RESTRICTIVE:
04818                         mMustPassGeneration = mFilterGeneration;
04819                         break;
04820                 case FILTER_MORE_RESTRICTIVE:
04821                         mMinRequiredGeneration = mFilterGeneration;
04822                         // must have passed either current filter generation (meaningless, as it hasn't been run yet)
04823                         // or some older generation, so keep the value
04824                         mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration);
04825                         break;
04826                 default:
04827                         llerrs << "Bad filter behavior specified" << llendl;
04828                 }
04829         }
04830         else
04831         {
04832                 // shortcut disabled filters to show everything immediately
04833                 mMinRequiredGeneration = 0;
04834                 mMustPassGeneration = S32_MAX;
04835         }
04836 }
04837 
04838 BOOL LLInventoryFilter::isFilterWith(LLInventoryType::EType t)
04839 {
04840         return mFilterOps.mFilterTypes & (0x01 << t);
04841 }
04842 
04843 LLString LLInventoryFilter::getFilterText()
04844 {
04845         if (!mNeedTextRebuild)
04846         {
04847                 return mFilterText;
04848         }
04849 
04850         mNeedTextRebuild = FALSE;
04851         LLString filtered_types;
04852         LLString not_filtered_types;
04853         BOOL filtered_by_type = FALSE;
04854         BOOL filtered_by_all_types = TRUE;
04855         S32 num_filter_types = 0;
04856         mFilterText = "";
04857 
04858         if (isFilterWith(LLInventoryType::IT_ANIMATION))
04859         {
04860                 filtered_types += " Animations,";
04861                 filtered_by_type = TRUE;
04862                 num_filter_types++;
04863         }
04864         else
04865         {
04866                 not_filtered_types += " Animations,";
04867                 filtered_by_all_types = FALSE;
04868         }
04869 
04870         if (isFilterWith(LLInventoryType::IT_CALLINGCARD))
04871         {
04872                 filtered_types += " Calling Cards,";
04873                 filtered_by_type = TRUE;
04874                 num_filter_types++;
04875         }
04876         else
04877         {
04878                 not_filtered_types += " Calling Cards,";
04879                 filtered_by_all_types = FALSE;
04880         }
04881 
04882         if (isFilterWith(LLInventoryType::IT_WEARABLE))
04883         {
04884                 filtered_types += " Clothing,";
04885                 filtered_by_type = TRUE;
04886                 num_filter_types++;
04887         }
04888         else
04889         {
04890                 not_filtered_types += " Clothing,";
04891                 filtered_by_all_types = FALSE;
04892         }
04893 
04894         if (isFilterWith(LLInventoryType::IT_GESTURE))
04895         {
04896                 filtered_types += " Gestures,";
04897                 filtered_by_type = TRUE;
04898                 num_filter_types++;
04899         }
04900         else
04901         {
04902                 not_filtered_types += " Gestures,";
04903                 filtered_by_all_types = FALSE;
04904         }
04905 
04906         if (isFilterWith(LLInventoryType::IT_LANDMARK))
04907         {
04908                 filtered_types += " Landmarks,";
04909                 filtered_by_type = TRUE;
04910                 num_filter_types++;
04911         }
04912         else
04913         {
04914                 not_filtered_types += " Landmarks,";
04915                 filtered_by_all_types = FALSE;
04916         }
04917 
04918         if (isFilterWith(LLInventoryType::IT_NOTECARD))
04919         {
04920                 filtered_types += " Notecards,";
04921                 filtered_by_type = TRUE;
04922                 num_filter_types++;
04923         }
04924         else
04925         {
04926                 not_filtered_types += " Notecards,";
04927                 filtered_by_all_types = FALSE;
04928         }
04929         
04930         if (isFilterWith(LLInventoryType::IT_OBJECT) && isFilterWith(LLInventoryType::IT_ATTACHMENT))
04931         {
04932                 filtered_types += " Objects,";
04933                 filtered_by_type = TRUE;
04934                 num_filter_types++;
04935         }
04936         else
04937         {
04938                 not_filtered_types += " Objects,";
04939                 filtered_by_all_types = FALSE;
04940         }
04941         
04942         if (isFilterWith(LLInventoryType::IT_LSL))
04943         {
04944                 filtered_types += " Scripts,";
04945                 filtered_by_type = TRUE;
04946                 num_filter_types++;
04947         }
04948         else
04949         {
04950                 not_filtered_types += " Scripts,";
04951                 filtered_by_all_types = FALSE;
04952         }
04953         
04954         if (isFilterWith(LLInventoryType::IT_SOUND))
04955         {
04956                 filtered_types += " Sounds,";
04957                 filtered_by_type = TRUE;
04958                 num_filter_types++;
04959         }
04960         else
04961         {
04962                 not_filtered_types += " Sounds,";
04963                 filtered_by_all_types = FALSE;
04964         }
04965 
04966         if (isFilterWith(LLInventoryType::IT_TEXTURE))
04967         {
04968                 filtered_types += " Textures,";
04969                 filtered_by_type = TRUE;
04970                 num_filter_types++;
04971         }
04972         else
04973         {
04974                 not_filtered_types += " Textures,";
04975                 filtered_by_all_types = FALSE;
04976         }
04977 
04978         if (isFilterWith(LLInventoryType::IT_SNAPSHOT))
04979         {
04980                 filtered_types += " Snapshots,";
04981                 filtered_by_type = TRUE;
04982                 num_filter_types++;
04983         }
04984         else
04985         {
04986                 not_filtered_types += " Snapshots,";
04987                 filtered_by_all_types = FALSE;
04988         }
04989 
04990         if (!gInventory.backgroundFetchActive() && filtered_by_type && !filtered_by_all_types)
04991         {
04992                 mFilterText += " - ";
04993                 if (num_filter_types < 5)
04994                 {
04995                         mFilterText += filtered_types;
04996                 }
04997                 else
04998                 {
04999                         mFilterText += "No ";
05000                         mFilterText += not_filtered_types;
05001                 }
05002                 // remove the ',' at the end
05003                 mFilterText.erase(mFilterText.size() - 1, 1);
05004         }
05005 
05006         if (isSinceLogoff())
05007         {
05008                 mFilterText += " - Since Logoff";
05009         }
05010         return mFilterText;
05011 }
05012 
05013 void LLInventoryFilter::toLLSD(LLSD& data)
05014 {
05015         data["filter_types"] = (LLSD::Integer)getFilterTypes();
05016         data["min_date"] = (LLSD::Integer)getMinDate();
05017         data["max_date"] = (LLSD::Integer)getMaxDate();
05018         data["hours_ago"] = (LLSD::Integer)getHoursAgo();
05019         data["show_folder_state"] = (LLSD::Integer)getShowFolderState();
05020         data["permissions"] = (LLSD::Integer)getFilterPermissions();
05021         data["substring"] = (LLSD::String)getFilterSubString();
05022         data["sort_order"] = (LLSD::Integer)getSortOrder();
05023         data["since_logoff"] = (LLSD::Boolean)isSinceLogoff();
05024 }
05025 
05026 void LLInventoryFilter::fromLLSD(LLSD& data)
05027 {
05028         if(data.has("filter_types"))
05029         {
05030                 setFilterTypes((U32)data["filter_types"].asInteger());
05031         }
05032 
05033         if(data.has("min_date") && data.has("max_date"))
05034         {
05035                 setDateRange((U32)data["min_date"].asInteger(), (U32)data["max_date"].asInteger());
05036         }
05037 
05038         if(data.has("hours_ago"))
05039         {
05040                 setHoursAgo((U32)data["hours_ago"].asInteger());
05041         }
05042 
05043         if(data.has("show_folder_state"))
05044         {
05045                 setShowFolderState((EFolderShow)data["show_folder_state"].asInteger());
05046         }
05047 
05048         if(data.has("permissions"))
05049         {
05050                 setFilterPermissions((PermissionMask)data["permissions"].asInteger());
05051         }
05052 
05053         if(data.has("substring"))
05054         {
05055                 setFilterSubString(LLString(data["substring"].asString()));
05056         }
05057 
05058         if(data.has("sort_order"))
05059         {
05060                 setSortOrder((U32)data["sort_order"].asInteger());
05061         }
05062 
05063         if(data.has("since_logoff"))
05064         {
05065                 setDateRangeLastLogoff((bool)data["since_logoff"].asBoolean());
05066         }
05067 }

Generated on Thu Jul 1 06:08:38 2010 for Second Life Viewer by  doxygen 1.4.7