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

Generated on Fri May 16 08:33:36 2008 for SecondLife by  doxygen 1.5.5