diff options
Diffstat (limited to '')
-rw-r--r-- | linden/indra/newview/llfolderview.cpp | 4924 |
1 files changed, 4924 insertions, 0 deletions
diff --git a/linden/indra/newview/llfolderview.cpp b/linden/indra/newview/llfolderview.cpp new file mode 100644 index 0000000..431f212 --- /dev/null +++ b/linden/indra/newview/llfolderview.cpp | |||
@@ -0,0 +1,4924 @@ | |||
1 | /** | ||
2 | * @file llfolderview.cpp | ||
3 | * @brief Implementation of the folder view collection of classes. | ||
4 | * | ||
5 | * Copyright (c) 2001-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "llviewerprecompiledheaders.h" | ||
29 | |||
30 | #include "llfolderview.h" | ||
31 | |||
32 | #include <algorithm> | ||
33 | |||
34 | #include "llviewercontrol.h" | ||
35 | #include "lldbstrings.h" | ||
36 | #include "llfocusmgr.h" | ||
37 | #include "llfontgl.h" | ||
38 | #include "llgl.h" | ||
39 | #include "llinventory.h" | ||
40 | |||
41 | #include "llcallbacklist.h" | ||
42 | #include "llinventoryclipboard.h" // *TODO: remove this once hack below gone. | ||
43 | #include "llinventoryview.h"// hacked in for the bonus context menu items. | ||
44 | #include "llkeyboard.h" | ||
45 | #include "lllineeditor.h" | ||
46 | #include "llmenugl.h" | ||
47 | #include "llresmgr.h" | ||
48 | #include "llpreview.h" | ||
49 | #include "llscrollcontainer.h" // hack to allow scrolling | ||
50 | #include "lltooldraganddrop.h" | ||
51 | #include "llui.h" | ||
52 | #include "llviewerimage.h" | ||
53 | #include "llviewerimagelist.h" | ||
54 | #include "llviewerjointattachment.h" | ||
55 | #include "llviewermenu.h" | ||
56 | #include "llvieweruictrlfactory.h" | ||
57 | #include "llviewerwindow.h" | ||
58 | #include "llvoavatar.h" | ||
59 | #include "llfloaterproperties.h" | ||
60 | |||
61 | // RN: HACK | ||
62 | // We need these because some of the code below relies on things like | ||
63 | // gAgent root folder. Remove them once the abstraction leak is fixed. | ||
64 | #include "llagent.h" | ||
65 | #include "viewer.h" | ||
66 | |||
67 | ///---------------------------------------------------------------------------- | ||
68 | /// Local function declarations, constants, enums, and typedefs | ||
69 | ///---------------------------------------------------------------------------- | ||
70 | |||
71 | const S32 LEFT_PAD = 5; | ||
72 | const S32 LEFT_INDENTATION = 13; | ||
73 | const S32 ICON_PAD = 2; | ||
74 | const S32 ICON_WIDTH = 16; | ||
75 | const S32 TEXT_PAD = 1; | ||
76 | const S32 ARROW_SIZE = 12; | ||
77 | const S32 RENAME_WIDTH_PAD = 4; | ||
78 | const S32 RENAME_HEIGHT_PAD = 6; | ||
79 | const S32 AUTO_OPEN_STACK_DEPTH = 16; | ||
80 | const S32 MIN_ITEM_WIDTH_VISIBLE = ICON_WIDTH + ICON_PAD + ARROW_SIZE + TEXT_PAD + /*first few characters*/ 40; | ||
81 | const S32 MINIMUM_RENAMER_WIDTH = 80; | ||
82 | const F32 FOLDER_CLOSE_TIME_CONSTANT = 0.02f; | ||
83 | const F32 FOLDER_OPEN_TIME_CONSTANT = 0.03f; | ||
84 | const S32 MAX_FOLDER_ITEM_OVERLAP = 2; | ||
85 | |||
86 | F32 LLFolderView::sAutoOpenTime = 1.f; | ||
87 | |||
88 | void delete_selected_item(void* user_data); | ||
89 | void copy_selected_item(void* user_data); | ||
90 | void open_selected_items(void* user_data); | ||
91 | void properties_selected_items(void* user_data); | ||
92 | void paste_items(void* user_data); | ||
93 | void top_view_lost( LLView* handler ); | ||
94 | |||
95 | ///---------------------------------------------------------------------------- | ||
96 | /// Class LLFolderViewItem | ||
97 | ///---------------------------------------------------------------------------- | ||
98 | |||
99 | // statics | ||
100 | const LLFontGL* LLFolderViewItem::sFont = NULL; | ||
101 | const LLFontGL* LLFolderViewItem::sSmallFont = NULL; | ||
102 | LLColor4 LLFolderViewItem::sFgColor; | ||
103 | LLColor4 LLFolderViewItem::sHighlightBgColor; | ||
104 | LLColor4 LLFolderViewItem::sHighlightFgColor; | ||
105 | LLColor4 LLFolderViewItem::sFilterBGColor; | ||
106 | LLColor4 LLFolderViewItem::sFilterTextColor; | ||
107 | |||
108 | // Default constructor | ||
109 | LLFolderViewItem::LLFolderViewItem( const LLString& name, LLViewerImage* icon, | ||
110 | S32 creation_date, | ||
111 | LLFolderView* root, | ||
112 | LLFolderViewEventListener* listener ) : | ||
113 | LLUICtrl( name, LLRect(0, 0, 0, 0), TRUE, NULL, NULL, FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_RIGHT), | ||
114 | mLabel( name ), | ||
115 | mLabelWidth(0), | ||
116 | mCreationDate(creation_date), | ||
117 | mParentFolder( NULL ), | ||
118 | mListener( listener ), | ||
119 | mIsSelected( FALSE ), | ||
120 | mIsCurSelection( FALSE ), | ||
121 | mSelectPending(FALSE), | ||
122 | mLabelStyle( LLFontGL::NORMAL ), | ||
123 | mHasVisibleChildren(FALSE), | ||
124 | mIndentation(0), | ||
125 | mNumDescendantsSelected(0), | ||
126 | mFiltered(FALSE), | ||
127 | mLastFilterGeneration(-1), | ||
128 | mStringMatchOffset(LLString::npos), | ||
129 | mControlLabelRotation(0.f), | ||
130 | mRoot( root ), | ||
131 | mDragAndDropTarget(FALSE) | ||
132 | { | ||
133 | setIcon(icon); | ||
134 | if( !LLFolderViewItem::sFont ) | ||
135 | { | ||
136 | LLFolderViewItem::sFont = gResMgr->getRes( LLFONT_SANSSERIF_SMALL ); | ||
137 | } | ||
138 | |||
139 | if (!LLFolderViewItem::sSmallFont) | ||
140 | { | ||
141 | LLFolderViewItem::sSmallFont = gResMgr->getRes( LLFONT_SMALL ); | ||
142 | } | ||
143 | |||
144 | // HACK: Can't be set above because gSavedSettings might not be constructed. | ||
145 | LLFolderViewItem::sFgColor = gColors.getColor( "MenuItemEnabledColor" ); | ||
146 | LLFolderViewItem::sHighlightBgColor = gColors.getColor( "MenuItemHighlightBgColor" ); | ||
147 | LLFolderViewItem::sHighlightFgColor = gColors.getColor( "MenuItemHighlightFgColor" ); | ||
148 | LLFolderViewItem::sFilterBGColor = gColors.getColor( "FilterBackgroundColor" ); | ||
149 | LLFolderViewItem::sFilterTextColor = gColors.getColor( "FilterTextColor" ); | ||
150 | |||
151 | mArrowImage = gImageList.getImage(LLUUID(gViewerArt.getString("folder_arrow.tga")), MIPMAP_FALSE, TRUE); | ||
152 | mBoxImage = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE); | ||
153 | |||
154 | refresh(); | ||
155 | setTabStop(FALSE); | ||
156 | } | ||
157 | |||
158 | // Destroys the object | ||
159 | LLFolderViewItem::~LLFolderViewItem( void ) | ||
160 | { | ||
161 | delete mListener; | ||
162 | mListener = NULL; | ||
163 | mArrowImage = NULL; | ||
164 | mBoxImage = NULL; | ||
165 | } | ||
166 | |||
167 | LLFolderView* LLFolderViewItem::getRoot() | ||
168 | { | ||
169 | return mRoot; | ||
170 | } | ||
171 | |||
172 | // Returns true if this object is a child (or grandchild, etc.) of potential_ancestor. | ||
173 | BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) | ||
174 | { | ||
175 | LLFolderViewItem* root = this; | ||
176 | while( root->mParentFolder ) | ||
177 | { | ||
178 | if( root->mParentFolder == potential_ancestor ) | ||
179 | { | ||
180 | return TRUE; | ||
181 | } | ||
182 | root = root->mParentFolder; | ||
183 | } | ||
184 | return FALSE; | ||
185 | } | ||
186 | |||
187 | LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children) | ||
188 | { | ||
189 | if (!mParentFolder) | ||
190 | { | ||
191 | return NULL; | ||
192 | } | ||
193 | |||
194 | LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children ); | ||
195 | while(itemp && !itemp->getVisible()) | ||
196 | { | ||
197 | LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children ); | ||
198 | if (itemp == next_itemp) | ||
199 | { | ||
200 | // hit last item | ||
201 | return itemp->getVisible() ? itemp : this; | ||
202 | } | ||
203 | itemp = next_itemp; | ||
204 | } | ||
205 | |||
206 | return itemp; | ||
207 | } | ||
208 | |||
209 | LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children) | ||
210 | { | ||
211 | if (!mParentFolder) | ||
212 | { | ||
213 | return NULL; | ||
214 | } | ||
215 | |||
216 | LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children ); | ||
217 | while(itemp && !itemp->getVisible()) | ||
218 | { | ||
219 | LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children ); | ||
220 | if (itemp == next_itemp) | ||
221 | { | ||
222 | // hit first item | ||
223 | return itemp->getVisible() ? itemp : this; | ||
224 | } | ||
225 | itemp = next_itemp; | ||
226 | } | ||
227 | |||
228 | return itemp; | ||
229 | } | ||
230 | |||
231 | BOOL LLFolderViewItem::getFiltered() | ||
232 | { | ||
233 | return mFiltered && mLastFilterGeneration >= mRoot->getFilter()->getMinRequiredGeneration(); | ||
234 | } | ||
235 | |||
236 | BOOL LLFolderViewItem::getFiltered(S32 filter_generation) | ||
237 | { | ||
238 | return mFiltered && mLastFilterGeneration >= filter_generation; | ||
239 | } | ||
240 | |||
241 | void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation) | ||
242 | { | ||
243 | mFiltered = filtered; | ||
244 | mLastFilterGeneration = filter_generation; | ||
245 | } | ||
246 | |||
247 | void LLFolderViewItem::setIcon(LLViewerImage* icon) | ||
248 | { | ||
249 | mIcon = icon; | ||
250 | if (mIcon) | ||
251 | { | ||
252 | mIcon->setBoostLevel(LLViewerImage::BOOST_UI); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | // refresh information from the listener | ||
257 | void LLFolderViewItem::refresh() | ||
258 | { | ||
259 | if(mListener) | ||
260 | { | ||
261 | const char* label = mListener->getDisplayName().c_str(); | ||
262 | mLabel = label ? label : ""; | ||
263 | setIcon(mListener->getIcon()); | ||
264 | U32 creation_date = mListener->getCreationDate(); | ||
265 | if (mCreationDate != creation_date) | ||
266 | { | ||
267 | mCreationDate = mListener->getCreationDate(); | ||
268 | dirtyFilter(); | ||
269 | } | ||
270 | mLabelStyle = mListener->getLabelStyle(); | ||
271 | mLabelSuffix = mListener->getLabelSuffix(); | ||
272 | |||
273 | LLString searchable_label(mLabel); | ||
274 | searchable_label.append(mLabelSuffix); | ||
275 | LLString::toUpper(searchable_label); | ||
276 | |||
277 | if (mSearchableLabel.compare(searchable_label)) | ||
278 | { | ||
279 | mSearchableLabel.assign(searchable_label); | ||
280 | dirtyFilter(); | ||
281 | // some part of label has changed, so overall width has potentially changed | ||
282 | if (mParentFolder) | ||
283 | { | ||
284 | mParentFolder->requestArrange(); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | S32 label_width = sFont->getWidth(mLabel); | ||
289 | if( mLabelSuffix.size() ) | ||
290 | { | ||
291 | label_width += sFont->getWidth( mLabelSuffix ); | ||
292 | } | ||
293 | |||
294 | mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + label_width; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) | ||
299 | { | ||
300 | functor(mListener); | ||
301 | } | ||
302 | |||
303 | // This function is called when items are added or view filters change. It's | ||
304 | // implemented here but called by derived classes when folding the | ||
305 | // views. | ||
306 | void LLFolderViewItem::filterFromRoot( void ) | ||
307 | { | ||
308 | LLFolderViewItem* root = getRoot(); | ||
309 | |||
310 | root->filter(*((LLFolderView*)root)->getFilter()); | ||
311 | } | ||
312 | |||
313 | // This function is called when the folder view is dirty. It's | ||
314 | // implemented here but called by derived classes when folding the | ||
315 | // views. | ||
316 | void LLFolderViewItem::arrangeFromRoot() | ||
317 | { | ||
318 | LLFolderViewItem* root = getRoot(); | ||
319 | |||
320 | S32 height = 0; | ||
321 | S32 width = 0; | ||
322 | root->arrange( &width, &height, 0 ); | ||
323 | } | ||
324 | |||
325 | // This function clears the currently selected item, and records the | ||
326 | // specified selected item appropriately for display and use in the | ||
327 | // UI. If open is TRUE, then folders are opened up along the way to | ||
328 | // the selection. | ||
329 | void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection, | ||
330 | BOOL open, | ||
331 | BOOL take_keyboard_focus) | ||
332 | { | ||
333 | getRoot()->setSelection(selection, open, take_keyboard_focus); | ||
334 | } | ||
335 | |||
336 | // helper function to change the selection from the root. | ||
337 | void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, | ||
338 | BOOL selected) | ||
339 | { | ||
340 | getRoot()->changeSelection(selection, selected); | ||
341 | } | ||
342 | |||
343 | void LLFolderViewItem::extendSelectionFromRoot(LLFolderViewItem* selection) | ||
344 | { | ||
345 | LLDynamicArray<LLFolderViewItem*> selected_items; | ||
346 | |||
347 | getRoot()->extendSelection(selection, NULL, selected_items); | ||
348 | } | ||
349 | |||
350 | EWidgetType LLFolderViewItem::getWidgetType() const | ||
351 | { | ||
352 | return WIDGET_TYPE_FOLDER_ITEM; | ||
353 | } | ||
354 | |||
355 | LLString LLFolderViewItem::getWidgetTag() const | ||
356 | { | ||
357 | return LL_FOLDER_VIEW_ITEM_TAG; | ||
358 | } | ||
359 | |||
360 | // addToFolder() returns TRUE if it succeeds. FALSE otherwise | ||
361 | BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) | ||
362 | { | ||
363 | if (!folder) | ||
364 | { | ||
365 | return FALSE; | ||
366 | } | ||
367 | mParentFolder = folder; | ||
368 | root->addItemID(getListener()->getUUID(), this); | ||
369 | return folder->addItem(this); | ||
370 | } | ||
371 | |||
372 | |||
373 | // Finds width and height of this object and it's children. Also | ||
374 | // makes sure that this view and it's children are the right size. | ||
375 | S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation) | ||
376 | { | ||
377 | mIndentation = mParentFolder ? mParentFolder->getIndentation() + LEFT_INDENTATION : 0; | ||
378 | *width = llmax(*width, mLabelWidth + mIndentation); | ||
379 | *height = getItemHeight(); | ||
380 | return *height; | ||
381 | } | ||
382 | |||
383 | S32 LLFolderViewItem::getItemHeight() | ||
384 | { | ||
385 | S32 icon_height = mIcon->getHeight(); | ||
386 | S32 label_height = llround(sFont->getLineHeight()); | ||
387 | return llmax( icon_height, label_height ) + ICON_PAD; | ||
388 | } | ||
389 | |||
390 | void LLFolderViewItem::filter( LLInventoryFilter& filter) | ||
391 | { | ||
392 | BOOL filtered = mListener && filter.check(this); | ||
393 | |||
394 | // if our visibility will change as a result of this filter, then | ||
395 | // we need to be rearranged in our parent folder | ||
396 | if (getVisible() != filtered) | ||
397 | { | ||
398 | if (mParentFolder) | ||
399 | { | ||
400 | mParentFolder->requestArrange(); | ||
401 | } | ||
402 | } | ||
403 | |||
404 | setFiltered(filtered, filter.getCurrentGeneration()); | ||
405 | mStringMatchOffset = filter.getStringMatchOffset(); | ||
406 | filter.decrementFilterCount(); | ||
407 | |||
408 | if (getRoot()->getDebugFilters()) | ||
409 | { | ||
410 | mStatusText = llformat("%d", mLastFilterGeneration); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | void LLFolderViewItem::dirtyFilter() | ||
415 | { | ||
416 | mLastFilterGeneration = -1; | ||
417 | // bubble up dirty flag all the way to root | ||
418 | if (getParentFolder()) | ||
419 | { | ||
420 | getParentFolder()->setCompletedFilterGeneration(-1, TRUE); | ||
421 | } | ||
422 | } | ||
423 | |||
424 | // *TODO: This can be optimized a lot by simply recording that it is | ||
425 | // selected in the appropriate places, and assuming that set selection | ||
426 | // means 'deselect' for a leaf item. Do this optimization after | ||
427 | // multiple selection is implemented to make sure it all plays nice | ||
428 | // together. | ||
429 | BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL open, | ||
430 | BOOL take_keyboard_focus) | ||
431 | { | ||
432 | if( selection == this ) | ||
433 | { | ||
434 | mIsSelected = TRUE; | ||
435 | if(mListener) | ||
436 | { | ||
437 | mListener->selectItem(); | ||
438 | } | ||
439 | } | ||
440 | else | ||
441 | { | ||
442 | mIsSelected = FALSE; | ||
443 | } | ||
444 | return mIsSelected; | ||
445 | } | ||
446 | |||
447 | BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, | ||
448 | BOOL selected) | ||
449 | { | ||
450 | if(selection == this && mIsSelected != selected) | ||
451 | { | ||
452 | mIsSelected = selected; | ||
453 | if(mListener) | ||
454 | { | ||
455 | mListener->selectItem(); | ||
456 | } | ||
457 | return TRUE; | ||
458 | } | ||
459 | return FALSE; | ||
460 | } | ||
461 | |||
462 | void LLFolderViewItem::recursiveDeselect(BOOL deselect_self) | ||
463 | { | ||
464 | if (mIsSelected && deselect_self) | ||
465 | { | ||
466 | mIsSelected = FALSE; | ||
467 | |||
468 | // update ancestors' count of selected descendents | ||
469 | LLFolderViewFolder* parent_folder = getParentFolder(); | ||
470 | while(parent_folder) | ||
471 | { | ||
472 | parent_folder->mNumDescendantsSelected--; | ||
473 | parent_folder = parent_folder->getParentFolder(); | ||
474 | } | ||
475 | } | ||
476 | } | ||
477 | |||
478 | |||
479 | BOOL LLFolderViewItem::isMovable() | ||
480 | { | ||
481 | if( mListener ) | ||
482 | { | ||
483 | return mListener->isItemMovable(); | ||
484 | } | ||
485 | else | ||
486 | { | ||
487 | return TRUE; | ||
488 | } | ||
489 | } | ||
490 | |||
491 | BOOL LLFolderViewItem::isRemovable() | ||
492 | { | ||
493 | if( mListener ) | ||
494 | { | ||
495 | return mListener->isItemRemovable(); | ||
496 | } | ||
497 | else | ||
498 | { | ||
499 | return TRUE; | ||
500 | } | ||
501 | } | ||
502 | |||
503 | void LLFolderViewItem::destroyView() | ||
504 | { | ||
505 | if (mParentFolder) | ||
506 | { | ||
507 | // removeView deletes me | ||
508 | mParentFolder->removeView(this); | ||
509 | } | ||
510 | } | ||
511 | |||
512 | // Call through to the viewed object and return true if it can be | ||
513 | // removed. | ||
514 | //BOOL LLFolderViewItem::removeRecursively(BOOL single_item) | ||
515 | BOOL LLFolderViewItem::remove() | ||
516 | { | ||
517 | if(!isRemovable()) | ||
518 | { | ||
519 | return FALSE; | ||
520 | } | ||
521 | if(mListener) | ||
522 | { | ||
523 | return mListener->removeItem(); | ||
524 | } | ||
525 | return TRUE; | ||
526 | } | ||
527 | |||
528 | // Build an appropriate context menu for the item. | ||
529 | void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) | ||
530 | { | ||
531 | if(mListener) | ||
532 | { | ||
533 | mListener->buildContextMenu(menu, flags); | ||
534 | } | ||
535 | } | ||
536 | |||
537 | void LLFolderViewItem::open( void ) | ||
538 | { | ||
539 | if( mListener ) | ||
540 | { | ||
541 | mListener->openItem(); | ||
542 | } | ||
543 | } | ||
544 | |||
545 | void LLFolderViewItem::preview( void ) | ||
546 | { | ||
547 | if (mListener) | ||
548 | { | ||
549 | mListener->previewItem(); | ||
550 | } | ||
551 | } | ||
552 | |||
553 | void LLFolderViewItem::rename(const LLString& new_name) | ||
554 | { | ||
555 | if( !new_name.empty() ) | ||
556 | { | ||
557 | mLabel = new_name.c_str(); | ||
558 | BOOL is_renamed = TRUE; | ||
559 | if( mListener ) | ||
560 | { | ||
561 | is_renamed = mListener->renameItem(new_name); | ||
562 | } | ||
563 | if(mParentFolder && is_renamed) | ||
564 | { | ||
565 | mParentFolder->resort(this); | ||
566 | } | ||
567 | //refresh(); | ||
568 | } | ||
569 | } | ||
570 | |||
571 | const LLString& LLFolderViewItem::getSearchableLabel() const | ||
572 | { | ||
573 | return mSearchableLabel; | ||
574 | } | ||
575 | |||
576 | const LLString& LLFolderViewItem::getName( void ) const | ||
577 | { | ||
578 | if(mListener) | ||
579 | { | ||
580 | return mListener->getName(); | ||
581 | } | ||
582 | return mLabel; | ||
583 | } | ||
584 | |||
585 | LLFolderViewFolder* LLFolderViewItem::getParentFolder( void ) | ||
586 | { | ||
587 | return mParentFolder; | ||
588 | } | ||
589 | |||
590 | LLFolderViewEventListener* LLFolderViewItem::getListener( void ) | ||
591 | { | ||
592 | return mListener; | ||
593 | } | ||
594 | |||
595 | // LLView functionality | ||
596 | BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) | ||
597 | { | ||
598 | if(!mIsSelected) | ||
599 | { | ||
600 | setSelectionFromRoot(this, FALSE); | ||
601 | } | ||
602 | make_ui_sound("UISndClick"); | ||
603 | return TRUE; | ||
604 | } | ||
605 | |||
606 | BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) | ||
607 | { | ||
608 | // No handler needed for focus lost since this class has no | ||
609 | // state that depends on it. | ||
610 | gViewerWindow->setMouseCapture( this, NULL ); | ||
611 | |||
612 | if (!mIsSelected) | ||
613 | { | ||
614 | if(mask & MASK_CONTROL) | ||
615 | { | ||
616 | changeSelectionFromRoot(this, !mIsSelected); | ||
617 | } | ||
618 | else if (mask & MASK_SHIFT) | ||
619 | { | ||
620 | extendSelectionFromRoot(this); | ||
621 | } | ||
622 | else | ||
623 | { | ||
624 | setSelectionFromRoot(this, FALSE); | ||
625 | } | ||
626 | make_ui_sound("UISndClick"); | ||
627 | } | ||
628 | else | ||
629 | { | ||
630 | mSelectPending = TRUE; | ||
631 | } | ||
632 | |||
633 | if( isMovable() ) | ||
634 | { | ||
635 | S32 screen_x; | ||
636 | S32 screen_y; | ||
637 | localPointToScreen(x, y, &screen_x, &screen_y ); | ||
638 | gToolDragAndDrop->setDragStart( screen_x, screen_y ); | ||
639 | } | ||
640 | return TRUE; | ||
641 | } | ||
642 | |||
643 | BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) | ||
644 | { | ||
645 | if( gViewerWindow->hasMouseCapture( this ) && isMovable() ) | ||
646 | { | ||
647 | S32 screen_x; | ||
648 | S32 screen_y; | ||
649 | localPointToScreen(x, y, &screen_x, &screen_y ); | ||
650 | BOOL can_drag = TRUE; | ||
651 | if( gToolDragAndDrop->isOverThreshold( screen_x, screen_y ) ) | ||
652 | { | ||
653 | LLFolderView* root = getRoot(); | ||
654 | |||
655 | if(root->getCurSelectedItem()) | ||
656 | { | ||
657 | LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD; | ||
658 | |||
659 | // *TODO: push this into listener and remove | ||
660 | // dependency on llagent | ||
661 | if(mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gAgent.getInventoryRootID())) | ||
662 | { | ||
663 | src = LLToolDragAndDrop::SOURCE_AGENT; | ||
664 | } | ||
665 | else if (mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventoryLibraryRoot)) | ||
666 | { | ||
667 | src = LLToolDragAndDrop::SOURCE_LIBRARY; | ||
668 | } | ||
669 | |||
670 | can_drag = root->startDrag(src); | ||
671 | if (can_drag) | ||
672 | { | ||
673 | // if (mListener) mListener->startDrag(); | ||
674 | // RN: when starting drag and drop, clear out last auto-open | ||
675 | root->autoOpenTest(NULL); | ||
676 | root->setShowSelectionContext(TRUE); | ||
677 | |||
678 | // Release keyboard focus, so that if stuff is dropped into the | ||
679 | // world, pressing the delete key won't blow away the inventory | ||
680 | // item. | ||
681 | gViewerWindow->setKeyboardFocus(NULL, NULL); | ||
682 | |||
683 | return gToolDragAndDrop->handleHover( x, y, mask ); | ||
684 | } | ||
685 | } | ||
686 | } | ||
687 | |||
688 | if (can_drag) | ||
689 | { | ||
690 | gViewerWindow->setCursor(UI_CURSOR_ARROW); | ||
691 | } | ||
692 | else | ||
693 | { | ||
694 | gViewerWindow->setCursor(UI_CURSOR_NOLOCKED); | ||
695 | } | ||
696 | return TRUE; | ||
697 | } | ||
698 | else | ||
699 | { | ||
700 | getRoot()->setShowSelectionContext(FALSE); | ||
701 | gViewerWindow->setCursor(UI_CURSOR_ARROW); | ||
702 | // let parent handle this then... | ||
703 | return FALSE; | ||
704 | } | ||
705 | } | ||
706 | |||
707 | |||
708 | BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) | ||
709 | { | ||
710 | preview(); | ||
711 | return TRUE; | ||
712 | } | ||
713 | |||
714 | BOOL LLFolderViewItem::handleScrollWheel(S32 x, S32 y, S32 clicks) | ||
715 | { | ||
716 | if (getParent()) | ||
717 | { | ||
718 | return getParent()->handleScrollWheel(x, y, clicks); | ||
719 | } | ||
720 | return FALSE; | ||
721 | } | ||
722 | |||
723 | BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) | ||
724 | { | ||
725 | // if mouse hasn't moved since mouse down... | ||
726 | if ( pointInView(x, y) && mSelectPending ) | ||
727 | { | ||
728 | //...then select | ||
729 | if(mask & MASK_CONTROL) | ||
730 | { | ||
731 | changeSelectionFromRoot(this, !mIsSelected); | ||
732 | } | ||
733 | else if (mask & MASK_SHIFT) | ||
734 | { | ||
735 | extendSelectionFromRoot(this); | ||
736 | } | ||
737 | else | ||
738 | { | ||
739 | setSelectionFromRoot(this, FALSE); | ||
740 | } | ||
741 | } | ||
742 | |||
743 | mSelectPending = FALSE; | ||
744 | |||
745 | if( gViewerWindow->hasMouseCapture( this ) ) | ||
746 | { | ||
747 | getRoot()->setShowSelectionContext(FALSE); | ||
748 | gViewerWindow->setMouseCapture( NULL, NULL ); | ||
749 | } | ||
750 | return TRUE; | ||
751 | } | ||
752 | |||
753 | BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, | ||
754 | EDragAndDropType cargo_type, | ||
755 | void* cargo_data, | ||
756 | EAcceptance* accept, | ||
757 | LLString& tooltip_msg) | ||
758 | { | ||
759 | BOOL accepted = FALSE; | ||
760 | BOOL handled = FALSE; | ||
761 | if(mListener) | ||
762 | { | ||
763 | accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data); | ||
764 | handled = accepted; | ||
765 | if (accepted) | ||
766 | { | ||
767 | mDragAndDropTarget = TRUE; | ||
768 | *accept = ACCEPT_YES_MULTI; | ||
769 | } | ||
770 | else | ||
771 | { | ||
772 | *accept = ACCEPT_NO; | ||
773 | } | ||
774 | } | ||
775 | if(mParentFolder && !handled) | ||
776 | { | ||
777 | handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); | ||
778 | } | ||
779 | if (handled) | ||
780 | { | ||
781 | lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl; | ||
782 | } | ||
783 | |||
784 | return handled; | ||
785 | } | ||
786 | |||
787 | |||
788 | void LLFolderViewItem::draw() | ||
789 | { | ||
790 | if( getVisible() ) | ||
791 | { | ||
792 | bool possibly_has_children = false; | ||
793 | bool up_to_date = mListener && mListener->isUpToDate(); | ||
794 | if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter... | ||
795 | (!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter) | ||
796 | { | ||
797 | possibly_has_children = true; | ||
798 | } | ||
799 | if(/*mControlLabel[0] != '\0' && */possibly_has_children) | ||
800 | { | ||
801 | LLGLSTexture gls_texture; | ||
802 | if (mArrowImage) | ||
803 | { | ||
804 | gl_draw_scaled_rotated_image(mIndentation, mRect.getHeight() - ARROW_SIZE - TEXT_PAD, | ||
805 | ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, mArrowImage, sFgColor); | ||
806 | } | ||
807 | } | ||
808 | |||
809 | F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); | ||
810 | |||
811 | // If we have keyboard focus, draw selection filled | ||
812 | BOOL show_context = getRoot()->getShowSelectionContext(); | ||
813 | BOOL filled = show_context || (gFocusMgr.getKeyboardFocus() == getRoot()); | ||
814 | |||
815 | // always render "current" item, only render other selected items if | ||
816 | // mShowSingleSelection is FALSE | ||
817 | if( mIsSelected ) | ||
818 | { | ||
819 | LLGLSNoTexture gls_no_texture; | ||
820 | LLColor4 bg_color = sHighlightBgColor; | ||
821 | //const S32 TRAILING_PAD = 5; // It just looks better with this. | ||
822 | if (!mIsCurSelection) | ||
823 | { | ||
824 | // do time-based fade of extra objects | ||
825 | F32 fade_time = getRoot()->getSelectionFadeElapsedTime(); | ||
826 | if (getRoot()->getShowSingleSelection()) | ||
827 | { | ||
828 | // fading out | ||
829 | bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f); | ||
830 | } | ||
831 | else | ||
832 | { | ||
833 | // fading in | ||
834 | bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]); | ||
835 | } | ||
836 | } | ||
837 | |||
838 | gl_rect_2d( | ||
839 | 0, | ||
840 | mRect.getHeight(), | ||
841 | mRect.getWidth() - 2, | ||
842 | llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD), | ||
843 | bg_color, filled); | ||
844 | if (mIsCurSelection) | ||
845 | { | ||
846 | gl_rect_2d( | ||
847 | 0, | ||
848 | mRect.getHeight(), | ||
849 | mRect.getWidth() - 2, | ||
850 | llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD), | ||
851 | sHighlightFgColor, FALSE); | ||
852 | } | ||
853 | if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2) | ||
854 | { | ||
855 | gl_rect_2d( | ||
856 | 0, | ||
857 | llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, | ||
858 | mRect.getWidth() - 2, | ||
859 | 2, | ||
860 | sHighlightFgColor, FALSE); | ||
861 | if (show_context) | ||
862 | { | ||
863 | gl_rect_2d( | ||
864 | 0, | ||
865 | llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, | ||
866 | mRect.getWidth() - 2, | ||
867 | 2, | ||
868 | sHighlightBgColor, TRUE); | ||
869 | } | ||
870 | } | ||
871 | } | ||
872 | if (mDragAndDropTarget) | ||
873 | { | ||
874 | LLGLSNoTexture gls_no_texture; | ||
875 | gl_rect_2d( | ||
876 | 0, | ||
877 | mRect.getHeight(), | ||
878 | mRect.getWidth() - 2, | ||
879 | llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD), | ||
880 | sHighlightBgColor, FALSE); | ||
881 | |||
882 | if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2) | ||
883 | { | ||
884 | gl_rect_2d( | ||
885 | 0, | ||
886 | llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, | ||
887 | mRect.getWidth() - 2, | ||
888 | 2, | ||
889 | sHighlightBgColor, FALSE); | ||
890 | } | ||
891 | mDragAndDropTarget = FALSE; | ||
892 | } | ||
893 | |||
894 | |||
895 | if(mIcon) | ||
896 | { | ||
897 | gl_draw_image(mIndentation + ARROW_SIZE + TEXT_PAD, mRect.getHeight() - mIcon->getHeight(), mIcon); | ||
898 | mIcon->addTextureStats( (F32)(mIcon->getWidth() * mIcon->getHeight())); | ||
899 | } | ||
900 | |||
901 | if (!mLabel.empty()) | ||
902 | { | ||
903 | // highlight filtered text | ||
904 | BOOL debug_filters = getRoot()->getDebugFilters(); | ||
905 | LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor ); | ||
906 | F32 right_x; | ||
907 | F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD; | ||
908 | |||
909 | if (debug_filters) | ||
910 | { | ||
911 | if (!getFiltered() && !possibly_has_children) | ||
912 | { | ||
913 | color.mV[VALPHA] *= 0.5f; | ||
914 | } | ||
915 | |||
916 | 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); | ||
917 | sSmallFont->renderUTF8(mStatusText, 0, text_left, y, filter_color, | ||
918 | LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, | ||
919 | S32_MAX, S32_MAX, &right_x, FALSE ); | ||
920 | text_left = right_x; | ||
921 | } | ||
922 | |||
923 | sFont->renderUTF8( mLabel, 0, text_left, y, color, | ||
924 | LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, | ||
925 | S32_MAX, S32_MAX, &right_x, FALSE ); | ||
926 | if (!mLabelSuffix.empty()) | ||
927 | { | ||
928 | sFont->renderUTF8( mLabelSuffix, 0, right_x, y, LLColor4(0.75f, 0.85f, 0.85f, 1.f), | ||
929 | LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, | ||
930 | S32_MAX, S32_MAX, &right_x, FALSE ); | ||
931 | } | ||
932 | |||
933 | if (mBoxImage.notNull() && mStringMatchOffset != LLString::npos) | ||
934 | { | ||
935 | // don't draw backgrounds for zero-length strings | ||
936 | S32 filter_string_length = mRoot->getFilterSubString().size(); | ||
937 | if (filter_string_length > 0) | ||
938 | { | ||
939 | LLString combined_string = mLabel + mLabelSuffix; | ||
940 | S32 left = llround(text_left) + sFont->getWidth(combined_string, 0, mStringMatchOffset) - 1; | ||
941 | S32 right = left + sFont->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; | ||
942 | S32 bottom = llfloor(mRect.getHeight() - sFont->getLineHeight() - 3); | ||
943 | S32 top = mRect.getHeight(); | ||
944 | |||
945 | LLViewerImage::bindTexture(mBoxImage); | ||
946 | glColor4fv(sFilterBGColor.mV); | ||
947 | gl_segmented_rect_2d_tex(left, top, right, bottom, mBoxImage->getWidth(), mBoxImage->getHeight(), 16); | ||
948 | F32 match_string_left = text_left + sFont->getWidthF32(combined_string, 0, mStringMatchOffset); | ||
949 | F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD; | ||
950 | sFont->renderUTF8( combined_string, mStringMatchOffset, match_string_left, y, | ||
951 | sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, | ||
952 | filter_string_length, S32_MAX, &right_x, FALSE ); | ||
953 | } | ||
954 | } | ||
955 | } | ||
956 | |||
957 | if( sDebugRects ) | ||
958 | { | ||
959 | drawDebugRect(); | ||
960 | } | ||
961 | } | ||
962 | else if (mStatusText.size()) | ||
963 | { | ||
964 | // just draw status text | ||
965 | sFont->renderUTF8( mStatusText, 0, 0, 1, sFgColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, S32_MAX, S32_MAX, NULL, FALSE ); | ||
966 | } | ||
967 | } | ||
968 | |||
969 | |||
970 | ///---------------------------------------------------------------------------- | ||
971 | /// Class LLFolderViewFolder | ||
972 | ///---------------------------------------------------------------------------- | ||
973 | |||
974 | // Default constructor | ||
975 | LLFolderViewFolder::LLFolderViewFolder( const LLString& name, LLViewerImage* icon, | ||
976 | LLFolderView* root, | ||
977 | LLFolderViewEventListener* listener ): | ||
978 | LLFolderViewItem( name, icon, 0, root, listener ), // 0 = no create time | ||
979 | mSortFunction(sort_item_name), | ||
980 | mIsOpen(FALSE), | ||
981 | mExpanderHighlighted(FALSE), | ||
982 | mCurHeight(0.f), | ||
983 | mTargetHeight(0.f), | ||
984 | mAutoOpenCountdown(0.f), | ||
985 | mSubtreeCreationDate(0), | ||
986 | mAmTrash(LLFolderViewFolder::UNKNOWN), | ||
987 | mLastArrangeGeneration( -1 ), | ||
988 | mLastCalculatedWidth(0), | ||
989 | mCompletedFilterGeneration(-1), | ||
990 | mMostFilteredDescendantGeneration(-1) | ||
991 | { | ||
992 | mType = "(folder)"; | ||
993 | |||
994 | //mItems.setInsertBefore( &sort_item_name ); | ||
995 | //mFolders.setInsertBefore( &folder_insert_before ); | ||
996 | } | ||
997 | |||
998 | // Destroys the object | ||
999 | LLFolderViewFolder::~LLFolderViewFolder( void ) | ||
1000 | { | ||
1001 | // The LLView base class takes care of object destruction. make sure that we | ||
1002 | // don't have mouse or keyboard focus | ||
1003 | gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() | ||
1004 | |||
1005 | //mItems.reset(); | ||
1006 | //mItems.removeAllNodes(); | ||
1007 | //mFolders.removeAllNodes(); | ||
1008 | } | ||
1009 | |||
1010 | EWidgetType LLFolderViewFolder::getWidgetType() const | ||
1011 | { | ||
1012 | return WIDGET_TYPE_FOLDER; | ||
1013 | } | ||
1014 | |||
1015 | LLString LLFolderViewFolder::getWidgetTag() const | ||
1016 | { | ||
1017 | return LL_FOLDER_VIEW_FOLDER_TAG; | ||
1018 | } | ||
1019 | |||
1020 | // addToFolder() returns TRUE if it succeeds. FALSE otherwise | ||
1021 | BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) | ||
1022 | { | ||
1023 | if (!folder) | ||
1024 | { | ||
1025 | return FALSE; | ||
1026 | } | ||
1027 | mParentFolder = folder; | ||
1028 | root->addItemID(getListener()->getUUID(), this); | ||
1029 | return folder->addFolder(this); | ||
1030 | } | ||
1031 | |||
1032 | // Finds width and height of this object and it's children. Also | ||
1033 | // makes sure that this view and it's children are the right size. | ||
1034 | S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) | ||
1035 | { | ||
1036 | mHasVisibleChildren = hasFilteredDescendants(filter_generation); | ||
1037 | |||
1038 | LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState(); | ||
1039 | |||
1040 | // calculate height as a single item (without any children), and reshapes rectangle to match | ||
1041 | LLFolderViewItem::arrange( width, height, filter_generation ); | ||
1042 | |||
1043 | // clamp existing animated height so as to never get smaller than a single item | ||
1044 | mCurHeight = llmax((F32)*height, mCurHeight); | ||
1045 | |||
1046 | // initialize running height value as height of single item in case we have no children | ||
1047 | *height = getItemHeight(); | ||
1048 | F32 running_height = (F32)*height; | ||
1049 | F32 target_height = (F32)*height; | ||
1050 | |||
1051 | // are my children visible? | ||
1052 | if (needsArrange()) | ||
1053 | { | ||
1054 | // set last arrange generation first, in case children are animating | ||
1055 | // and need to be arranged again | ||
1056 | mLastArrangeGeneration = mRoot->getArrangeGeneration(); | ||
1057 | if (mIsOpen) | ||
1058 | { | ||
1059 | // Add sizes of children | ||
1060 | S32 parent_item_height = mRect.getHeight(); | ||
1061 | |||
1062 | folders_t::iterator fit = mFolders.begin(); | ||
1063 | folders_t::iterator fend = mFolders.end(); | ||
1064 | for(; fit < fend; ++fit) | ||
1065 | { | ||
1066 | LLFolderViewFolder* folderp = (*fit); | ||
1067 | if (getRoot()->getDebugFilters()) | ||
1068 | { | ||
1069 | folderp->setVisible(TRUE); | ||
1070 | } | ||
1071 | else | ||
1072 | { | ||
1073 | folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? | ||
1074 | (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter | ||
1075 | } | ||
1076 | |||
1077 | if (folderp->getVisible()) | ||
1078 | { | ||
1079 | S32 child_width = *width; | ||
1080 | S32 child_height = 0; | ||
1081 | S32 child_top = parent_item_height - llround(running_height); | ||
1082 | |||
1083 | target_height += folderp->arrange( &child_width, &child_height, filter_generation ); | ||
1084 | |||
1085 | running_height += (F32)child_height; | ||
1086 | *width = llmax(*width, child_width); | ||
1087 | folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() ); | ||
1088 | } | ||
1089 | } | ||
1090 | items_t::iterator iit = mItems.begin(); | ||
1091 | items_t::iterator iend = mItems.end(); | ||
1092 | for(;iit < iend; ++iit) | ||
1093 | { | ||
1094 | LLFolderViewItem* itemp = (*iit); | ||
1095 | if (getRoot()->getDebugFilters()) | ||
1096 | { | ||
1097 | itemp->setVisible(TRUE); | ||
1098 | } | ||
1099 | else | ||
1100 | { | ||
1101 | itemp->setVisible(itemp->getFiltered(filter_generation)); | ||
1102 | } | ||
1103 | |||
1104 | if (itemp->getVisible()) | ||
1105 | { | ||
1106 | S32 child_width = *width; | ||
1107 | S32 child_height = 0; | ||
1108 | S32 child_top = parent_item_height - llround(running_height); | ||
1109 | |||
1110 | target_height += itemp->arrange( &child_width, &child_height, filter_generation ); | ||
1111 | // don't change width, as this item is as wide as its parent folder by construction | ||
1112 | itemp->reshape( itemp->getRect().getWidth(), child_height); | ||
1113 | |||
1114 | running_height += (F32)child_height; | ||
1115 | *width = llmax(*width, child_width); | ||
1116 | itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() ); | ||
1117 | } | ||
1118 | } | ||
1119 | } | ||
1120 | |||
1121 | mTargetHeight = target_height; | ||
1122 | // cache this width so next time we can just return it | ||
1123 | mLastCalculatedWidth = *width; | ||
1124 | } | ||
1125 | else | ||
1126 | { | ||
1127 | // just use existing width | ||
1128 | *width = mLastCalculatedWidth; | ||
1129 | } | ||
1130 | |||
1131 | // animate current height towards target height | ||
1132 | if (llabs(mCurHeight - mTargetHeight) > 1.f) | ||
1133 | { | ||
1134 | mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT)); | ||
1135 | |||
1136 | requestArrange(); | ||
1137 | |||
1138 | // hide child elements that fall out of current animated height | ||
1139 | for (folders_t::iterator iter = mFolders.begin(); | ||
1140 | iter != mFolders.end();) | ||
1141 | { | ||
1142 | folders_t::iterator fit = iter++; | ||
1143 | // number of pixels that bottom of folder label is from top of parent folder | ||
1144 | if (mRect.getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() | ||
1145 | > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) | ||
1146 | { | ||
1147 | // hide if beyond current folder height | ||
1148 | (*fit)->setVisible(FALSE); | ||
1149 | } | ||
1150 | } | ||
1151 | |||
1152 | for (items_t::iterator iter = mItems.begin(); | ||
1153 | iter != mItems.end();) | ||
1154 | { | ||
1155 | items_t::iterator iit = iter++; | ||
1156 | // number of pixels that bottom of item label is from top of parent folder | ||
1157 | if (mRect.getHeight() - (*iit)->getRect().mBottom | ||
1158 | > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) | ||
1159 | { | ||
1160 | (*iit)->setVisible(FALSE); | ||
1161 | } | ||
1162 | } | ||
1163 | } | ||
1164 | else | ||
1165 | { | ||
1166 | mCurHeight = mTargetHeight; | ||
1167 | } | ||
1168 | |||
1169 | // don't change width as this item is already as wide as its parent folder | ||
1170 | reshape(mRect.getWidth(),llround(mCurHeight)); | ||
1171 | |||
1172 | // pass current height value back to parent | ||
1173 | *height = llround(mCurHeight); | ||
1174 | |||
1175 | return llround(mTargetHeight); | ||
1176 | } | ||
1177 | |||
1178 | BOOL LLFolderViewFolder::needsArrange() | ||
1179 | { | ||
1180 | return mLastArrangeGeneration < mRoot->getArrangeGeneration(); | ||
1181 | } | ||
1182 | |||
1183 | void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up) | ||
1184 | { | ||
1185 | mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation); | ||
1186 | mCompletedFilterGeneration = generation; | ||
1187 | // only aggregate up if we are a lower (older) value | ||
1188 | if (recurse_up && mParentFolder && generation < mParentFolder->getCompletedFilterGeneration()) | ||
1189 | { | ||
1190 | mParentFolder->setCompletedFilterGeneration(generation, TRUE); | ||
1191 | } | ||
1192 | } | ||
1193 | |||
1194 | void LLFolderViewFolder::filter( LLInventoryFilter& filter) | ||
1195 | { | ||
1196 | S32 filter_generation = filter.getCurrentGeneration(); | ||
1197 | // if failed to pass filter newer than must_pass_generation | ||
1198 | // you will automatically fail this time, so we only | ||
1199 | // check against items that have passed the filter | ||
1200 | S32 must_pass_generation = filter.getMustPassGeneration(); | ||
1201 | |||
1202 | // if we have already been filtered against this generation, skip out | ||
1203 | if (getCompletedFilterGeneration() >= filter_generation) | ||
1204 | { | ||
1205 | return; | ||
1206 | } | ||
1207 | |||
1208 | // filter folder itself | ||
1209 | if (getLastFilterGeneration() < filter_generation) | ||
1210 | { | ||
1211 | if (getLastFilterGeneration() >= must_pass_generation && // folder has been compared to a valid precursor filter | ||
1212 | !mFiltered) // and did not pass the filter | ||
1213 | { | ||
1214 | // go ahead and flag this folder as done | ||
1215 | mLastFilterGeneration = filter_generation; | ||
1216 | } | ||
1217 | else | ||
1218 | { | ||
1219 | // filter self only on first pass through | ||
1220 | LLFolderViewItem::filter( filter ); | ||
1221 | } | ||
1222 | } | ||
1223 | |||
1224 | if (getRoot()->getDebugFilters()) | ||
1225 | { | ||
1226 | mStatusText = llformat("%d", mLastFilterGeneration); | ||
1227 | mStatusText += llformat("(%d)", mCompletedFilterGeneration); | ||
1228 | mStatusText += llformat("+%d", mMostFilteredDescendantGeneration); | ||
1229 | } | ||
1230 | |||
1231 | // all descendants have been filtered later than must pass generation | ||
1232 | // but none passed | ||
1233 | if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation)) | ||
1234 | { | ||
1235 | // don't traverse children if we've already filtered them since must_pass_generation | ||
1236 | // and came back with nothing | ||
1237 | return; | ||
1238 | } | ||
1239 | |||
1240 | // we entered here with at least one filter iteration left | ||
1241 | // check to see if we have any more before continuing on to children | ||
1242 | if (filter.getFilterCount() < 0) | ||
1243 | { | ||
1244 | return; | ||
1245 | } | ||
1246 | |||
1247 | // when applying a filter, matching folders get their contents downloaded first | ||
1248 | if (getRoot()->isFilterActive() && getFiltered(filter.getMinRequiredGeneration()) && !gInventory.isCategoryComplete(mListener->getUUID())) | ||
1249 | { | ||
1250 | gInventory.startBackgroundFetch(mListener->getUUID()); | ||
1251 | } | ||
1252 | |||
1253 | // now query children | ||
1254 | for (folders_t::iterator iter = mFolders.begin(); | ||
1255 | iter != mFolders.end();) | ||
1256 | { | ||
1257 | folders_t::iterator fit = iter++; | ||
1258 | // have we run out of iterations this frame? | ||
1259 | if (filter.getFilterCount() < 0) | ||
1260 | { | ||
1261 | break; | ||
1262 | } | ||
1263 | |||
1264 | // mMostFilteredDescendantGeneration might have been reset | ||
1265 | // in which case we need to update it even for folders that | ||
1266 | // don't need to be filtered anymore | ||
1267 | if ((*fit)->getCompletedFilterGeneration() >= filter_generation) | ||
1268 | { | ||
1269 | // track latest generation to pass any child items | ||
1270 | if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter.getMinRequiredGeneration())) | ||
1271 | { | ||
1272 | mMostFilteredDescendantGeneration = filter_generation; | ||
1273 | if (mRoot->needsAutoSelect()) | ||
1274 | { | ||
1275 | (*fit)->setOpenArrangeRecursively(TRUE); | ||
1276 | } | ||
1277 | } | ||
1278 | // just skip it, it has already been filtered | ||
1279 | continue; | ||
1280 | } | ||
1281 | |||
1282 | // update this folders filter status (and children) | ||
1283 | (*fit)->filter( filter ); | ||
1284 | |||
1285 | // track latest generation to pass any child items | ||
1286 | if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter_generation)) | ||
1287 | { | ||
1288 | mMostFilteredDescendantGeneration = filter_generation; | ||
1289 | if (mRoot->needsAutoSelect()) | ||
1290 | { | ||
1291 | (*fit)->setOpenArrangeRecursively(TRUE); | ||
1292 | } | ||
1293 | } | ||
1294 | } | ||
1295 | |||
1296 | for (items_t::iterator iter = mItems.begin(); | ||
1297 | iter != mItems.end();) | ||
1298 | { | ||
1299 | items_t::iterator iit = iter++; | ||
1300 | if (filter.getFilterCount() < 0) | ||
1301 | { | ||
1302 | break; | ||
1303 | } | ||
1304 | if ((*iit)->getLastFilterGeneration() >= filter_generation) | ||
1305 | { | ||
1306 | if ((*iit)->getFiltered()) | ||
1307 | { | ||
1308 | mMostFilteredDescendantGeneration = filter_generation; | ||
1309 | } | ||
1310 | continue; | ||
1311 | } | ||
1312 | |||
1313 | if ((*iit)->getLastFilterGeneration() >= must_pass_generation && | ||
1314 | !(*iit)->getFiltered(must_pass_generation)) | ||
1315 | { | ||
1316 | // failed to pass an earlier filter that was a subset of the current one | ||
1317 | // go ahead and flag this item as done | ||
1318 | (*iit)->setFiltered(FALSE, filter_generation); | ||
1319 | continue; | ||
1320 | } | ||
1321 | |||
1322 | (*iit)->filter( filter ); | ||
1323 | |||
1324 | if ((*iit)->getFiltered(filter.getMinRequiredGeneration())) | ||
1325 | { | ||
1326 | mMostFilteredDescendantGeneration = filter_generation; | ||
1327 | } | ||
1328 | } | ||
1329 | |||
1330 | // if we didn't use all filter iterations | ||
1331 | // that means we filtered all of our descendants | ||
1332 | // instead of exhausting the filter count for this frame | ||
1333 | if (filter.getFilterCount() > 0) | ||
1334 | { | ||
1335 | // flag this folder as having completed filter pass for all descendants | ||
1336 | setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/); | ||
1337 | } | ||
1338 | } | ||
1339 | |||
1340 | void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation) | ||
1341 | { | ||
1342 | // if this folder is now filtered, but wasn't before | ||
1343 | // (it just passed) | ||
1344 | if (filtered && !mFiltered) | ||
1345 | { | ||
1346 | // reset current height, because last time we drew it | ||
1347 | // it might have had more visible items than now | ||
1348 | mCurHeight = 0.f; | ||
1349 | } | ||
1350 | |||
1351 | LLFolderViewItem::setFiltered(filtered, filter_generation); | ||
1352 | } | ||
1353 | |||
1354 | void LLFolderViewFolder::dirtyFilter() | ||
1355 | { | ||
1356 | // we're a folder, so invalidate our completed generation | ||
1357 | setCompletedFilterGeneration(-1, FALSE); | ||
1358 | LLFolderViewItem::dirtyFilter(); | ||
1359 | } | ||
1360 | |||
1361 | BOOL LLFolderViewFolder::hasFilteredDescendants() | ||
1362 | { | ||
1363 | return mMostFilteredDescendantGeneration >= mRoot->getFilter()->getCurrentGeneration(); | ||
1364 | } | ||
1365 | |||
1366 | // Passes selection information on to children and record selection | ||
1367 | // information if necessary. | ||
1368 | BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL open, | ||
1369 | BOOL take_keyboard_focus) | ||
1370 | { | ||
1371 | BOOL rv = FALSE; | ||
1372 | if( selection == this ) | ||
1373 | { | ||
1374 | mIsSelected = TRUE; | ||
1375 | if(mListener) | ||
1376 | { | ||
1377 | mListener->selectItem(); | ||
1378 | } | ||
1379 | rv = TRUE; | ||
1380 | } | ||
1381 | else | ||
1382 | { | ||
1383 | mIsSelected = FALSE; | ||
1384 | rv = FALSE; | ||
1385 | } | ||
1386 | BOOL child_selected = FALSE; | ||
1387 | |||
1388 | for (folders_t::iterator iter = mFolders.begin(); | ||
1389 | iter != mFolders.end();) | ||
1390 | { | ||
1391 | folders_t::iterator fit = iter++; | ||
1392 | if((*fit)->setSelection(selection, open, take_keyboard_focus)) | ||
1393 | { | ||
1394 | rv = TRUE; | ||
1395 | child_selected = TRUE; | ||
1396 | mNumDescendantsSelected++; | ||
1397 | } | ||
1398 | } | ||
1399 | for (items_t::iterator iter = mItems.begin(); | ||
1400 | iter != mItems.end();) | ||
1401 | { | ||
1402 | items_t::iterator iit = iter++; | ||
1403 | if((*iit)->setSelection(selection, open, take_keyboard_focus)) | ||
1404 | { | ||
1405 | rv = TRUE; | ||
1406 | child_selected = TRUE; | ||
1407 | mNumDescendantsSelected++; | ||
1408 | } | ||
1409 | } | ||
1410 | if(open && child_selected) | ||
1411 | { | ||
1412 | setOpenArrangeRecursively(TRUE); | ||
1413 | } | ||
1414 | return rv; | ||
1415 | } | ||
1416 | |||
1417 | // This method is used to change the selection of an item. If | ||
1418 | // selection is 'this', then note selection as true. Returns TRUE | ||
1419 | // if this or a child is now selected. | ||
1420 | BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, | ||
1421 | BOOL selected) | ||
1422 | { | ||
1423 | BOOL rv = FALSE; | ||
1424 | if(selection == this) | ||
1425 | { | ||
1426 | mIsSelected = selected; | ||
1427 | if(mListener && selected) | ||
1428 | { | ||
1429 | mListener->selectItem(); | ||
1430 | } | ||
1431 | rv = TRUE; | ||
1432 | } | ||
1433 | |||
1434 | for (folders_t::iterator iter = mFolders.begin(); | ||
1435 | iter != mFolders.end();) | ||
1436 | { | ||
1437 | folders_t::iterator fit = iter++; | ||
1438 | if((*fit)->changeSelection(selection, selected)) | ||
1439 | { | ||
1440 | if (selected) | ||
1441 | { | ||
1442 | mNumDescendantsSelected++; | ||
1443 | } | ||
1444 | else | ||
1445 | { | ||
1446 | mNumDescendantsSelected--; | ||
1447 | } | ||
1448 | rv = TRUE; | ||
1449 | } | ||
1450 | } | ||
1451 | for (items_t::iterator iter = mItems.begin(); | ||
1452 | iter != mItems.end();) | ||
1453 | { | ||
1454 | items_t::iterator iit = iter++; | ||
1455 | if((*iit)->changeSelection(selection, selected)) | ||
1456 | { | ||
1457 | if (selected) | ||
1458 | { | ||
1459 | mNumDescendantsSelected++; | ||
1460 | } | ||
1461 | else | ||
1462 | { | ||
1463 | mNumDescendantsSelected--; | ||
1464 | } | ||
1465 | rv = TRUE; | ||
1466 | } | ||
1467 | } | ||
1468 | return rv; | ||
1469 | } | ||
1470 | |||
1471 | S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& selected_items) | ||
1472 | { | ||
1473 | S32 num_selected = 0; | ||
1474 | |||
1475 | // pass on to child folders first | ||
1476 | for (folders_t::iterator iter = mFolders.begin(); | ||
1477 | iter != mFolders.end();) | ||
1478 | { | ||
1479 | folders_t::iterator fit = iter++; | ||
1480 | num_selected += (*fit)->extendSelection(selection, last_selected, selected_items); | ||
1481 | mNumDescendantsSelected += num_selected; | ||
1482 | } | ||
1483 | |||
1484 | // handle selection of our immediate children... | ||
1485 | BOOL reverse_select = FALSE; | ||
1486 | BOOL found_last_selected = FALSE; | ||
1487 | BOOL found_selection = FALSE; | ||
1488 | LLDynamicArray<LLFolderViewItem*> items_to_select; | ||
1489 | LLFolderViewItem* item; | ||
1490 | |||
1491 | //...folders first... | ||
1492 | for (folders_t::iterator iter = mFolders.begin(); | ||
1493 | iter != mFolders.end();) | ||
1494 | { | ||
1495 | folders_t::iterator fit = iter++; | ||
1496 | item = (*fit); | ||
1497 | if(item == selection) | ||
1498 | { | ||
1499 | found_selection = TRUE; | ||
1500 | } | ||
1501 | else if (item == last_selected) | ||
1502 | { | ||
1503 | found_last_selected = TRUE; | ||
1504 | if (found_selection) | ||
1505 | { | ||
1506 | reverse_select = TRUE; | ||
1507 | } | ||
1508 | } | ||
1509 | |||
1510 | if (found_selection || found_last_selected) | ||
1511 | { | ||
1512 | // deselect currently selected items so they can be pushed back on queue | ||
1513 | if (item->isSelected()) | ||
1514 | { | ||
1515 | item->changeSelection(item, FALSE); | ||
1516 | } | ||
1517 | items_to_select.put(item); | ||
1518 | } | ||
1519 | |||
1520 | if (found_selection && found_last_selected) | ||
1521 | { | ||
1522 | break; | ||
1523 | } | ||
1524 | } | ||
1525 | |||
1526 | if (!(found_selection && found_last_selected)) | ||
1527 | { | ||
1528 | //,,,then items | ||
1529 | for (items_t::iterator iter = mItems.begin(); | ||
1530 | iter != mItems.end();) | ||
1531 | { | ||
1532 | items_t::iterator iit = iter++; | ||
1533 | item = (*iit); | ||
1534 | if(item == selection) | ||
1535 | { | ||
1536 | found_selection = TRUE; | ||
1537 | } | ||
1538 | else if (item == last_selected) | ||
1539 | { | ||
1540 | found_last_selected = TRUE; | ||
1541 | if (found_selection) | ||
1542 | { | ||
1543 | reverse_select = TRUE; | ||
1544 | } | ||
1545 | } | ||
1546 | |||
1547 | if (found_selection || found_last_selected) | ||
1548 | { | ||
1549 | // deselect currently selected items so they can be pushed back on queue | ||
1550 | if (item->isSelected()) | ||
1551 | { | ||
1552 | item->changeSelection(item, FALSE); | ||
1553 | } | ||
1554 | items_to_select.put(item); | ||
1555 | } | ||
1556 | |||
1557 | if (found_selection && found_last_selected) | ||
1558 | { | ||
1559 | break; | ||
1560 | } | ||
1561 | } | ||
1562 | } | ||
1563 | |||
1564 | if (found_last_selected && found_selection) | ||
1565 | { | ||
1566 | // we have a complete selection inside this folder | ||
1567 | for (S32 index = reverse_select ? items_to_select.getLength() - 1 : 0; | ||
1568 | reverse_select ? index >= 0 : index < items_to_select.getLength(); reverse_select ? index-- : index++) | ||
1569 | { | ||
1570 | LLFolderViewItem* item = items_to_select[index]; | ||
1571 | if (item->changeSelection(item, TRUE)) | ||
1572 | { | ||
1573 | selected_items.put(item); | ||
1574 | mNumDescendantsSelected++; | ||
1575 | num_selected++; | ||
1576 | } | ||
1577 | } | ||
1578 | } | ||
1579 | else if (found_selection) | ||
1580 | { | ||
1581 | // last selection was not in this folder....go ahead and select just the new item | ||
1582 | if (selection->changeSelection(selection, TRUE)) | ||
1583 | { | ||
1584 | selected_items.put(selection); | ||
1585 | mNumDescendantsSelected++; | ||
1586 | num_selected++; | ||
1587 | } | ||
1588 | } | ||
1589 | |||
1590 | return num_selected; | ||
1591 | } | ||
1592 | |||
1593 | void LLFolderViewFolder::recursiveDeselect(BOOL deselect_self) | ||
1594 | { | ||
1595 | // make sure we don't have negative values | ||
1596 | llassert(mNumDescendantsSelected >= 0); | ||
1597 | |||
1598 | if (mIsSelected && deselect_self) | ||
1599 | { | ||
1600 | mIsSelected = FALSE; | ||
1601 | |||
1602 | // update ancestors' count of selected descendents | ||
1603 | LLFolderViewFolder* parent_folder = getParentFolder(); | ||
1604 | while(parent_folder) | ||
1605 | { | ||
1606 | parent_folder->mNumDescendantsSelected--; | ||
1607 | parent_folder = parent_folder->getParentFolder(); | ||
1608 | } | ||
1609 | } | ||
1610 | |||
1611 | if (0 == mNumDescendantsSelected) | ||
1612 | { | ||
1613 | return; | ||
1614 | } | ||
1615 | |||
1616 | for (items_t::iterator iter = mItems.begin(); | ||
1617 | iter != mItems.end();) | ||
1618 | { | ||
1619 | items_t::iterator iit = iter++; | ||
1620 | LLFolderViewItem* item = (*iit); | ||
1621 | item->recursiveDeselect(TRUE); | ||
1622 | } | ||
1623 | |||
1624 | for (folders_t::iterator iter = mFolders.begin(); | ||
1625 | iter != mFolders.end();) | ||
1626 | { | ||
1627 | folders_t::iterator fit = iter++; | ||
1628 | LLFolderViewFolder* folder = (*fit); | ||
1629 | folder->recursiveDeselect(TRUE); | ||
1630 | } | ||
1631 | |||
1632 | } | ||
1633 | |||
1634 | void LLFolderViewFolder::destroyView() | ||
1635 | { | ||
1636 | for (items_t::iterator iter = mItems.begin(); | ||
1637 | iter != mItems.end();) | ||
1638 | { | ||
1639 | items_t::iterator iit = iter++; | ||
1640 | LLFolderViewItem* item = (*iit); | ||
1641 | getRoot()->removeItemID(item->getListener()->getUUID()); | ||
1642 | } | ||
1643 | |||
1644 | std::for_each(mItems.begin(), mItems.end(), DeletePointer()); | ||
1645 | mItems.clear(); | ||
1646 | |||
1647 | while (!mFolders.empty()) | ||
1648 | { | ||
1649 | LLFolderViewFolder *folderp = mFolders.back(); | ||
1650 | folderp->destroyView(); | ||
1651 | } | ||
1652 | |||
1653 | mFolders.clear(); | ||
1654 | |||
1655 | deleteAllChildren(); | ||
1656 | |||
1657 | if (mParentFolder) | ||
1658 | { | ||
1659 | mParentFolder->removeView(this); | ||
1660 | } | ||
1661 | } | ||
1662 | |||
1663 | // remove the specified item (and any children) if possible. Return | ||
1664 | // TRUE if the item was deleted. | ||
1665 | BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item) | ||
1666 | { | ||
1667 | if(item->remove()) | ||
1668 | { | ||
1669 | removeView(item); | ||
1670 | return TRUE; | ||
1671 | } | ||
1672 | return FALSE; | ||
1673 | } | ||
1674 | |||
1675 | // simply remove the view (and any children) Don't bother telling the | ||
1676 | // listeners. | ||
1677 | void LLFolderViewFolder::removeView(LLFolderViewItem* item) | ||
1678 | { | ||
1679 | if (!item) | ||
1680 | { | ||
1681 | return; | ||
1682 | } | ||
1683 | // deselect without traversing hierarchy | ||
1684 | item->recursiveDeselect(TRUE); | ||
1685 | getRoot()->removeFromSelectionList(item); | ||
1686 | extractItem(item); | ||
1687 | delete item; | ||
1688 | } | ||
1689 | |||
1690 | // extractItem() removes the specified item from the folder, but | ||
1691 | // doesn't delete it. | ||
1692 | void LLFolderViewFolder::extractItem( LLFolderViewItem* item ) | ||
1693 | { | ||
1694 | items_t::iterator it = std::find(mItems.begin(), mItems.end(), item); | ||
1695 | if(it == mItems.end()) | ||
1696 | { | ||
1697 | // This is an evil downcast. However, it's only doing | ||
1698 | // pointer comparison to find if (which it should be ) the | ||
1699 | // item is in the container, so it's pretty safe. | ||
1700 | LLFolderViewFolder* f = reinterpret_cast<LLFolderViewFolder*>(item); | ||
1701 | folders_t::iterator ft; | ||
1702 | ft = std::find(mFolders.begin(), mFolders.end(), f); | ||
1703 | if(ft != mFolders.end()) | ||
1704 | { | ||
1705 | mFolders.erase(ft); | ||
1706 | } | ||
1707 | } | ||
1708 | else | ||
1709 | { | ||
1710 | mItems.erase(it); | ||
1711 | } | ||
1712 | //item has been removed, need to update filter | ||
1713 | dirtyFilter(); | ||
1714 | //because an item is going away regardless of filter status, force rearrange | ||
1715 | requestArrange(); | ||
1716 | getRoot()->removeItemID(item->getListener()->getUUID()); | ||
1717 | removeChild(item); | ||
1718 | } | ||
1719 | |||
1720 | // This function is called by a child that needs to be resorted. | ||
1721 | // This is only called for renaming an object because it won't work for date | ||
1722 | void LLFolderViewFolder::resort(LLFolderViewItem* item) | ||
1723 | { | ||
1724 | std::sort(mItems.begin(), mItems.end(), *mSortFunction); | ||
1725 | std::sort(mFolders.begin(), mFolders.end(), *mSortFunction); | ||
1726 | //if(mItems.removeData(item)) | ||
1727 | //{ | ||
1728 | // mItems.addDataSorted(item); | ||
1729 | //} | ||
1730 | //else | ||
1731 | //{ | ||
1732 | // // This is an evil downcast. However, it's only doing | ||
1733 | // // pointer comparison to find if (which it should be ) the | ||
1734 | // // item is in the container, so it's pretty safe. | ||
1735 | // LLFolderViewFolder* f = reinterpret_cast<LLFolderViewFolder*>(item); | ||
1736 | // if(mFolders.removeData(f)) | ||
1737 | // { | ||
1738 | // mFolders.addDataSorted(f); | ||
1739 | // } | ||
1740 | //} | ||
1741 | } | ||
1742 | |||
1743 | bool LLFolderViewFolder::isTrash() | ||
1744 | { | ||
1745 | if (mAmTrash == LLFolderViewFolder::UNKNOWN) | ||
1746 | { | ||
1747 | mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH; | ||
1748 | } | ||
1749 | return mAmTrash == LLFolderViewFolder::TRASH; | ||
1750 | } | ||
1751 | |||
1752 | void LLFolderViewFolder::sortBy(U32 order) | ||
1753 | { | ||
1754 | BOOL sort_order_changed = FALSE; | ||
1755 | if (!(order & LLInventoryFilter::SO_DATE)) | ||
1756 | { | ||
1757 | if (mSortFunction != sort_item_name) | ||
1758 | { | ||
1759 | mSortFunction = sort_item_name; | ||
1760 | sort_order_changed = TRUE; | ||
1761 | } | ||
1762 | } | ||
1763 | else | ||
1764 | { | ||
1765 | if (mSortFunction != sort_item_date) | ||
1766 | { | ||
1767 | mSortFunction = sort_item_date; | ||
1768 | sort_order_changed = TRUE; | ||
1769 | } | ||
1770 | } | ||
1771 | |||
1772 | for (folders_t::iterator iter = mFolders.begin(); | ||
1773 | iter != mFolders.end();) | ||
1774 | { | ||
1775 | folders_t::iterator fit = iter++; | ||
1776 | (*fit)->sortBy(order); | ||
1777 | } | ||
1778 | if (order & LLInventoryFilter::SO_FOLDERS_BY_NAME) | ||
1779 | { | ||
1780 | // sort folders by name if always by name | ||
1781 | std::sort(mFolders.begin(), mFolders.end(), sort_item_name); | ||
1782 | } | ||
1783 | else | ||
1784 | { | ||
1785 | // sort folders by the default sort ordering | ||
1786 | std::sort(mFolders.begin(), mFolders.end(), *mSortFunction); | ||
1787 | |||
1788 | // however, if we are at the root of the inventory and we are sorting by date | ||
1789 | if (mListener->getUUID() == gAgent.getInventoryRootID() && order & LLInventoryFilter::SO_DATE) | ||
1790 | { | ||
1791 | // pull the trash folder and stick it on the end of the list | ||
1792 | LLFolderViewFolder *t = NULL; | ||
1793 | for (folders_t::iterator fit = mFolders.begin(); | ||
1794 | fit != mFolders.end(); ++fit) | ||
1795 | { | ||
1796 | if ((*fit)->isTrash()) | ||
1797 | { | ||
1798 | t = *fit; | ||
1799 | mFolders.erase(fit); | ||
1800 | break; | ||
1801 | } | ||
1802 | } | ||
1803 | if (t) | ||
1804 | { | ||
1805 | mFolders.push_back(t); | ||
1806 | } | ||
1807 | } | ||
1808 | } | ||
1809 | if (sort_order_changed) | ||
1810 | { | ||
1811 | std::sort(mItems.begin(), mItems.end(), *mSortFunction); | ||
1812 | } | ||
1813 | |||
1814 | if (order & LLInventoryFilter::SO_DATE) | ||
1815 | { | ||
1816 | U32 latest = 0; | ||
1817 | |||
1818 | if (!mItems.empty()) | ||
1819 | { | ||
1820 | LLFolderViewItem* item = *(mItems.begin()); | ||
1821 | latest = item->getCreationDate(); | ||
1822 | } | ||
1823 | |||
1824 | if (!mFolders.empty()) | ||
1825 | { | ||
1826 | LLFolderViewFolder* folder = *(mFolders.begin()); | ||
1827 | if (folder->getCreationDate() > latest) | ||
1828 | { | ||
1829 | latest = folder->getCreationDate(); | ||
1830 | } | ||
1831 | } | ||
1832 | mSubtreeCreationDate = latest; | ||
1833 | } | ||
1834 | } | ||
1835 | |||
1836 | void LLFolderViewFolder::setItemSortFunction(sort_order_f ordering) | ||
1837 | { | ||
1838 | mSortFunction = ordering; | ||
1839 | |||
1840 | for (folders_t::iterator iter = mFolders.begin(); | ||
1841 | iter != mFolders.end();) | ||
1842 | { | ||
1843 | folders_t::iterator fit = iter++; | ||
1844 | (*fit)->setItemSortFunction(ordering); | ||
1845 | } | ||
1846 | |||
1847 | std::sort(mFolders.begin(), mFolders.end(), *mSortFunction); | ||
1848 | std::sort(mItems.begin(), mItems.end(), *mSortFunction); | ||
1849 | } | ||
1850 | |||
1851 | BOOL LLFolderViewFolder::isMovable() | ||
1852 | { | ||
1853 | if( mListener ) | ||
1854 | { | ||
1855 | if( !(mListener->isItemMovable()) ) | ||
1856 | { | ||
1857 | return FALSE; | ||
1858 | } | ||
1859 | |||
1860 | for (items_t::iterator iter = mItems.begin(); | ||
1861 | iter != mItems.end();) | ||
1862 | { | ||
1863 | items_t::iterator iit = iter++; | ||
1864 | if(!(*iit)->isMovable()) | ||
1865 | { | ||
1866 | return FALSE; | ||
1867 | } | ||
1868 | } | ||
1869 | |||
1870 | for (folders_t::iterator iter = mFolders.begin(); | ||
1871 | iter != mFolders.end();) | ||
1872 | { | ||
1873 | folders_t::iterator fit = iter++; | ||
1874 | if(!(*fit)->isMovable()) | ||
1875 | { | ||
1876 | return FALSE; | ||
1877 | } | ||
1878 | } | ||
1879 | } | ||
1880 | return TRUE; | ||
1881 | } | ||
1882 | |||
1883 | |||
1884 | BOOL LLFolderViewFolder::isRemovable() | ||
1885 | { | ||
1886 | if( mListener ) | ||
1887 | { | ||
1888 | if( !(mListener->isItemRemovable()) ) | ||
1889 | { | ||
1890 | return FALSE; | ||
1891 | } | ||
1892 | |||
1893 | for (items_t::iterator iter = mItems.begin(); | ||
1894 | iter != mItems.end();) | ||
1895 | { | ||
1896 | items_t::iterator iit = iter++; | ||
1897 | if(!(*iit)->isRemovable()) | ||
1898 | { | ||
1899 | return FALSE; | ||
1900 | } | ||
1901 | } | ||
1902 | |||
1903 | for (folders_t::iterator iter = mFolders.begin(); | ||
1904 | iter != mFolders.end();) | ||
1905 | { | ||
1906 | folders_t::iterator fit = iter++; | ||
1907 | if(!(*fit)->isRemovable()) | ||
1908 | { | ||
1909 | return FALSE; | ||
1910 | } | ||
1911 | } | ||
1912 | } | ||
1913 | return TRUE; | ||
1914 | } | ||
1915 | |||
1916 | // this is an internal method used for adding items to folders. | ||
1917 | BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item) | ||
1918 | { | ||
1919 | items_t::iterator it = std::lower_bound( | ||
1920 | mItems.begin(), | ||
1921 | mItems.end(), | ||
1922 | item, | ||
1923 | mSortFunction); | ||
1924 | mItems.insert(it,item); | ||
1925 | item->setRect(LLRect(0, 0, mRect.getWidth(), 0)); | ||
1926 | item->setVisible(FALSE); | ||
1927 | addChild( item ); | ||
1928 | item->dirtyFilter(); | ||
1929 | requestArrange(); | ||
1930 | return TRUE; | ||
1931 | } | ||
1932 | |||
1933 | // this is an internal method used for adding items to folders. | ||
1934 | BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) | ||
1935 | { | ||
1936 | folders_t::iterator it = std::lower_bound( | ||
1937 | mFolders.begin(), | ||
1938 | mFolders.end(), | ||
1939 | folder, | ||
1940 | mSortFunction); | ||
1941 | mFolders.insert(it,folder); | ||
1942 | folder->setOrigin(0, 0); | ||
1943 | folder->reshape(mRect.getWidth(), 0); | ||
1944 | folder->setVisible(FALSE); | ||
1945 | addChild( folder ); | ||
1946 | folder->dirtyFilter(); | ||
1947 | requestArrange(); | ||
1948 | return TRUE; | ||
1949 | } | ||
1950 | |||
1951 | void LLFolderViewFolder::requestArrange() | ||
1952 | { | ||
1953 | mLastArrangeGeneration = -1; | ||
1954 | // flag all items up to root | ||
1955 | if (mParentFolder) | ||
1956 | { | ||
1957 | mParentFolder->requestArrange(); | ||
1958 | } | ||
1959 | } | ||
1960 | |||
1961 | void LLFolderViewFolder::toggleOpen() | ||
1962 | { | ||
1963 | setOpen(!mIsOpen); | ||
1964 | } | ||
1965 | |||
1966 | // Force a folder open or closed | ||
1967 | void LLFolderViewFolder::setOpen(BOOL open) | ||
1968 | { | ||
1969 | setOpenArrangeRecursively(open); | ||
1970 | } | ||
1971 | |||
1972 | void LLFolderViewFolder::setOpenArrangeRecursively(BOOL open, ERecurseType recurse) | ||
1973 | { | ||
1974 | BOOL was_open = mIsOpen; | ||
1975 | mIsOpen = open; | ||
1976 | if(!was_open && open) | ||
1977 | { | ||
1978 | if(mListener) | ||
1979 | { | ||
1980 | mListener->openItem(); | ||
1981 | } | ||
1982 | } | ||
1983 | if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN) | ||
1984 | { | ||
1985 | for (folders_t::iterator iter = mFolders.begin(); | ||
1986 | iter != mFolders.end();) | ||
1987 | { | ||
1988 | folders_t::iterator fit = iter++; | ||
1989 | (*fit)->setOpenArrangeRecursively(open, RECURSE_DOWN); | ||
1990 | } | ||
1991 | } | ||
1992 | if (mParentFolder && (recurse == RECURSE_UP || recurse == RECURSE_UP_DOWN)) | ||
1993 | { | ||
1994 | mParentFolder->setOpenArrangeRecursively(open, RECURSE_UP); | ||
1995 | } | ||
1996 | |||
1997 | if (was_open != mIsOpen) | ||
1998 | { | ||
1999 | requestArrange(); | ||
2000 | } | ||
2001 | } | ||
2002 | |||
2003 | BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, | ||
2004 | BOOL drop, | ||
2005 | EDragAndDropType c_type, | ||
2006 | void* cargo_data, | ||
2007 | EAcceptance* accept, | ||
2008 | LLString& tooltip_msg) | ||
2009 | { | ||
2010 | BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data); | ||
2011 | if (accepted) | ||
2012 | { | ||
2013 | mDragAndDropTarget = TRUE; | ||
2014 | *accept = ACCEPT_YES_MULTI; | ||
2015 | } | ||
2016 | else | ||
2017 | { | ||
2018 | *accept = ACCEPT_NO; | ||
2019 | } | ||
2020 | |||
2021 | // drag and drop to child item, so clear pending auto-opens | ||
2022 | getRoot()->autoOpenTest(NULL); | ||
2023 | |||
2024 | return TRUE; | ||
2025 | } | ||
2026 | |||
2027 | void LLFolderViewFolder::open( void ) | ||
2028 | { | ||
2029 | toggleOpen(); | ||
2030 | } | ||
2031 | |||
2032 | void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor) | ||
2033 | { | ||
2034 | functor.doFolder(this); | ||
2035 | |||
2036 | for (folders_t::iterator iter = mFolders.begin(); | ||
2037 | iter != mFolders.end();) | ||
2038 | { | ||
2039 | folders_t::iterator fit = iter++; | ||
2040 | (*fit)->applyFunctorRecursively(functor); | ||
2041 | } | ||
2042 | for (items_t::iterator iter = mItems.begin(); | ||
2043 | iter != mItems.end();) | ||
2044 | { | ||
2045 | items_t::iterator iit = iter++; | ||
2046 | functor.doItem((*iit)); | ||
2047 | } | ||
2048 | } | ||
2049 | |||
2050 | void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) | ||
2051 | { | ||
2052 | functor(mListener); | ||
2053 | for (folders_t::iterator iter = mFolders.begin(); | ||
2054 | iter != mFolders.end();) | ||
2055 | { | ||
2056 | folders_t::iterator fit = iter++; | ||
2057 | (*fit)->applyListenerFunctorRecursively(functor); | ||
2058 | } | ||
2059 | for (items_t::iterator iter = mItems.begin(); | ||
2060 | iter != mItems.end();) | ||
2061 | { | ||
2062 | items_t::iterator iit = iter++; | ||
2063 | (*iit)->applyListenerFunctorRecursively(functor); | ||
2064 | } | ||
2065 | } | ||
2066 | |||
2067 | // LLView functionality | ||
2068 | BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, | ||
2069 | BOOL drop, | ||
2070 | EDragAndDropType cargo_type, | ||
2071 | void* cargo_data, | ||
2072 | EAcceptance* accept, | ||
2073 | LLString& tooltip_msg) | ||
2074 | { | ||
2075 | LLFolderView* root_view = getRoot(); | ||
2076 | |||
2077 | BOOL handled = FALSE; | ||
2078 | if(mIsOpen) | ||
2079 | { | ||
2080 | handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, | ||
2081 | cargo_data, accept, tooltip_msg) != NULL; | ||
2082 | } | ||
2083 | |||
2084 | if (!handled) | ||
2085 | { | ||
2086 | BOOL accepted = mListener && mListener->dragOrDrop(mask, drop,cargo_type,cargo_data); | ||
2087 | |||
2088 | if (accepted) | ||
2089 | { | ||
2090 | mDragAndDropTarget = TRUE; | ||
2091 | *accept = ACCEPT_YES_MULTI; | ||
2092 | } | ||
2093 | else | ||
2094 | { | ||
2095 | *accept = ACCEPT_NO; | ||
2096 | } | ||
2097 | |||
2098 | if (!drop && accepted) | ||
2099 | { | ||
2100 | root_view->autoOpenTest(this); | ||
2101 | } | ||
2102 | |||
2103 | lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl; | ||
2104 | } | ||
2105 | |||
2106 | return TRUE; | ||
2107 | } | ||
2108 | |||
2109 | |||
2110 | BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) | ||
2111 | { | ||
2112 | BOOL handled = FALSE; | ||
2113 | if( getVisible() ) | ||
2114 | { | ||
2115 | // fetch contents of this folder, as context menu can depend on contents | ||
2116 | // still, user would have to open context menu again to see the changes | ||
2117 | gInventory.fetchDescendentsOf(mListener->getUUID()); | ||
2118 | |||
2119 | if( mIsOpen ) | ||
2120 | { | ||
2121 | handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; | ||
2122 | } | ||
2123 | if (!handled) | ||
2124 | { | ||
2125 | handled = LLFolderViewItem::handleRightMouseDown( x, y, mask ); | ||
2126 | } | ||
2127 | } | ||
2128 | return handled; | ||
2129 | } | ||
2130 | |||
2131 | |||
2132 | BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) | ||
2133 | { | ||
2134 | BOOL handled = LLView::handleHover(x, y, mask); | ||
2135 | |||
2136 | if (!handled) | ||
2137 | { | ||
2138 | // this doesn't do child processing | ||
2139 | handled = LLFolderViewItem::handleHover(x, y, mask); | ||
2140 | } | ||
2141 | |||
2142 | //if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD && y > mRect.getHeight() - ) | ||
2143 | //{ | ||
2144 | // gViewerWindow->setCursor(UI_CURSOR_ARROW); | ||
2145 | // mExpanderHighlighted = TRUE; | ||
2146 | // handled = TRUE; | ||
2147 | //} | ||
2148 | return handled; | ||
2149 | } | ||
2150 | |||
2151 | BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) | ||
2152 | { | ||
2153 | BOOL handled = FALSE; | ||
2154 | if( mIsOpen ) | ||
2155 | { | ||
2156 | handled = childrenHandleMouseDown(x,y,mask) != NULL; | ||
2157 | } | ||
2158 | if( !handled ) | ||
2159 | { | ||
2160 | if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD) | ||
2161 | { | ||
2162 | toggleOpen(); | ||
2163 | handled = TRUE; | ||
2164 | } | ||
2165 | else | ||
2166 | { | ||
2167 | // do normal selection logic | ||
2168 | handled = LLFolderViewItem::handleMouseDown(x, y, mask); | ||
2169 | } | ||
2170 | } | ||
2171 | |||
2172 | return handled; | ||
2173 | } | ||
2174 | |||
2175 | BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) | ||
2176 | { | ||
2177 | if (!getVisible()) | ||
2178 | { | ||
2179 | return FALSE; | ||
2180 | } | ||
2181 | BOOL rv = false; | ||
2182 | if( mIsOpen ) | ||
2183 | { | ||
2184 | rv = childrenHandleDoubleClick( x, y, mask ) != NULL; | ||
2185 | } | ||
2186 | if( !rv ) | ||
2187 | { | ||
2188 | if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD) | ||
2189 | { | ||
2190 | // don't select when user double-clicks plus sign | ||
2191 | // so as not to contradict single-click behavior | ||
2192 | toggleOpen(); | ||
2193 | } | ||
2194 | else | ||
2195 | { | ||
2196 | setSelectionFromRoot(this, FALSE); | ||
2197 | toggleOpen(); | ||
2198 | } | ||
2199 | return TRUE; | ||
2200 | } | ||
2201 | return rv; | ||
2202 | } | ||
2203 | |||
2204 | void LLFolderViewFolder::draw() | ||
2205 | { | ||
2206 | if (mAutoOpenCountdown != 0.f) | ||
2207 | { | ||
2208 | mControlLabelRotation = mAutoOpenCountdown * -90.f; | ||
2209 | } | ||
2210 | else if (mIsOpen) | ||
2211 | { | ||
2212 | mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); | ||
2213 | } | ||
2214 | else | ||
2215 | { | ||
2216 | mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); | ||
2217 | } | ||
2218 | |||
2219 | LLFolderViewItem::draw(); | ||
2220 | if( mIsOpen ) | ||
2221 | { | ||
2222 | LLView::draw(); | ||
2223 | } | ||
2224 | |||
2225 | // if (mExpanderHighlighted) | ||
2226 | // { | ||
2227 | // gl_rect_2d(mIndentation - TEXT_PAD, llfloor(mRect.getHeight() - TEXT_PAD), mIndentation + sFont->getWidth(mControlLabel) + TEXT_PAD, llfloor(mRect.getHeight() - sFont->getLineHeight() - TEXT_PAD), sFgColor, FALSE); | ||
2228 | // //sFont->renderUTF8( mControlLabel, 0, mIndentation, llfloor(mRect.getHeight() - sFont->getLineHeight() - TEXT_PAD), sFgColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, S32_MAX, S32_MAX, NULL, FALSE ); | ||
2229 | // } | ||
2230 | mExpanderHighlighted = FALSE; | ||
2231 | } | ||
2232 | |||
2233 | U32 LLFolderViewFolder::getCreationDate() const | ||
2234 | { | ||
2235 | return llmax<U32>(mCreationDate, mSubtreeCreationDate); | ||
2236 | } | ||
2237 | |||
2238 | |||
2239 | // this does prefix traversal, as folders are listed above their contents | ||
2240 | LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children ) | ||
2241 | { | ||
2242 | BOOL found_item = FALSE; | ||
2243 | |||
2244 | LLFolderViewItem* result = NULL; | ||
2245 | // when not starting from a given item, start at beginning | ||
2246 | if(item == NULL) | ||
2247 | { | ||
2248 | found_item = TRUE; | ||
2249 | } | ||
2250 | |||
2251 | // find current item among children | ||
2252 | folders_t::iterator fit = mFolders.begin(); | ||
2253 | folders_t::iterator fend = mFolders.end(); | ||
2254 | |||
2255 | items_t::iterator iit = mItems.begin(); | ||
2256 | items_t::iterator iend = mItems.end(); | ||
2257 | |||
2258 | // if not trivially starting at the beginning, we have to find the current item | ||
2259 | if (!found_item) | ||
2260 | { | ||
2261 | // first, look among folders, since they are always above items | ||
2262 | for(; fit != fend; ++fit) | ||
2263 | { | ||
2264 | if(item == (*fit)) | ||
2265 | { | ||
2266 | found_item = TRUE; | ||
2267 | // if we are on downwards traversal | ||
2268 | if (include_children && (*fit)->isOpen()) | ||
2269 | { | ||
2270 | // look for first descendant | ||
2271 | return (*fit)->getNextFromChild(NULL, TRUE); | ||
2272 | } | ||
2273 | // otherwise advance to next folder | ||
2274 | ++fit; | ||
2275 | include_children = TRUE; | ||
2276 | break; | ||
2277 | } | ||
2278 | } | ||
2279 | |||
2280 | // didn't find in folders? Check items... | ||
2281 | if (!found_item) | ||
2282 | { | ||
2283 | for(; iit != iend; ++iit) | ||
2284 | { | ||
2285 | if(item == (*iit)) | ||
2286 | { | ||
2287 | found_item = TRUE; | ||
2288 | // point to next item | ||
2289 | ++iit; | ||
2290 | break; | ||
2291 | } | ||
2292 | } | ||
2293 | } | ||
2294 | } | ||
2295 | |||
2296 | if (!found_item) | ||
2297 | { | ||
2298 | // you should never call this method with an item that isn't a child | ||
2299 | // so we should always find something | ||
2300 | llassert(FALSE); | ||
2301 | return NULL; | ||
2302 | } | ||
2303 | |||
2304 | // at this point, either iit or fit point to a candidate "next" item | ||
2305 | // if both are out of range, we need to punt up to our parent | ||
2306 | |||
2307 | // now, starting from found folder, continue through folders | ||
2308 | // searching for next visible folder | ||
2309 | while(fit != fend && !(*fit)->getVisible()) | ||
2310 | { | ||
2311 | // turn on downwards traversal for next folder | ||
2312 | ++fit; | ||
2313 | } | ||
2314 | |||
2315 | if (fit != fend) | ||
2316 | { | ||
2317 | result = (*fit); | ||
2318 | } | ||
2319 | else | ||
2320 | { | ||
2321 | // otherwise, scan for next visible item | ||
2322 | while(iit != iend && !(*iit)->getVisible()) | ||
2323 | { | ||
2324 | ++iit; | ||
2325 | } | ||
2326 | |||
2327 | // check to see if we have a valid item | ||
2328 | if (iit != iend) | ||
2329 | { | ||
2330 | result = (*iit); | ||
2331 | } | ||
2332 | } | ||
2333 | |||
2334 | if( !result && mParentFolder ) | ||
2335 | { | ||
2336 | // If there are no siblings or children to go to, recurse up one level in the tree | ||
2337 | // and skip children for this folder, as we've already discounted them | ||
2338 | result = mParentFolder->getNextFromChild(this, FALSE); | ||
2339 | } | ||
2340 | |||
2341 | return result; | ||
2342 | } | ||
2343 | |||
2344 | // this does postfix traversal, as folders are listed above their contents | ||
2345 | LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children ) | ||
2346 | { | ||
2347 | BOOL found_item = FALSE; | ||
2348 | |||
2349 | LLFolderViewItem* result = NULL; | ||
2350 | // when not starting from a given item, start at end | ||
2351 | if(item == NULL) | ||
2352 | { | ||
2353 | found_item = TRUE; | ||
2354 | } | ||
2355 | |||
2356 | // find current item among children | ||
2357 | folders_t::reverse_iterator fit = mFolders.rbegin(); | ||
2358 | folders_t::reverse_iterator fend = mFolders.rend(); | ||
2359 | |||
2360 | items_t::reverse_iterator iit = mItems.rbegin(); | ||
2361 | items_t::reverse_iterator iend = mItems.rend(); | ||
2362 | |||
2363 | // if not trivially starting at the end, we have to find the current item | ||
2364 | if (!found_item) | ||
2365 | { | ||
2366 | // first, look among items, since they are always below the folders | ||
2367 | for(; iit != iend; ++iit) | ||
2368 | { | ||
2369 | if(item == (*iit)) | ||
2370 | { | ||
2371 | found_item = TRUE; | ||
2372 | // point to next item | ||
2373 | ++iit; | ||
2374 | break; | ||
2375 | } | ||
2376 | } | ||
2377 | |||
2378 | // didn't find in items? Check folders... | ||
2379 | if (!found_item) | ||
2380 | { | ||
2381 | for(; fit != fend; ++fit) | ||
2382 | { | ||
2383 | if(item == (*fit)) | ||
2384 | { | ||
2385 | found_item = TRUE; | ||
2386 | // point to next folder | ||
2387 | ++fit; | ||
2388 | break; | ||
2389 | } | ||
2390 | } | ||
2391 | } | ||
2392 | } | ||
2393 | |||
2394 | if (!found_item) | ||
2395 | { | ||
2396 | // you should never call this method with an item that isn't a child | ||
2397 | // so we should always find something | ||
2398 | llassert(FALSE); | ||
2399 | return NULL; | ||
2400 | } | ||
2401 | |||
2402 | // at this point, either iit or fit point to a candidate "next" item | ||
2403 | // if both are out of range, we need to punt up to our parent | ||
2404 | |||
2405 | // now, starting from found item, continue through items | ||
2406 | // searching for next visible item | ||
2407 | while(iit != iend && !(*iit)->getVisible()) | ||
2408 | { | ||
2409 | ++iit; | ||
2410 | } | ||
2411 | |||
2412 | if (iit != iend) | ||
2413 | { | ||
2414 | // we found an appropriate item | ||
2415 | result = (*iit); | ||
2416 | } | ||
2417 | else | ||
2418 | { | ||
2419 | // otherwise, scan for next visible folder | ||
2420 | while(fit != fend && !(*fit)->getVisible()) | ||
2421 | { | ||
2422 | ++fit; | ||
2423 | } | ||
2424 | |||
2425 | // check to see if we have a valid folder | ||
2426 | if (fit != fend) | ||
2427 | { | ||
2428 | // try selecting child element of this folder | ||
2429 | if ((*fit)->isOpen()) | ||
2430 | { | ||
2431 | result = (*fit)->getPreviousFromChild(NULL); | ||
2432 | } | ||
2433 | else | ||
2434 | { | ||
2435 | result = (*fit); | ||
2436 | } | ||
2437 | } | ||
2438 | } | ||
2439 | |||
2440 | if( !result ) | ||
2441 | { | ||
2442 | // If there are no siblings or children to go to, recurse up one level in the tree | ||
2443 | // which gets back to this folder, which will only be visited if it is a valid, visible item | ||
2444 | result = this; | ||
2445 | } | ||
2446 | |||
2447 | return result; | ||
2448 | } | ||
2449 | |||
2450 | |||
2451 | //--------------------------------------------------------------------------- | ||
2452 | |||
2453 | // Tells all folders in a folderview to sort their items | ||
2454 | // (and only their items, not folders) by a certain function. | ||
2455 | class LLSetItemSortFunction : public LLFolderViewFunctor | ||
2456 | { | ||
2457 | public: | ||
2458 | LLSetItemSortFunction(sort_order_f ordering) | ||
2459 | : mSortFunction(ordering) {} | ||
2460 | virtual ~LLSetItemSortFunction() {} | ||
2461 | virtual void doFolder(LLFolderViewFolder* folder); | ||
2462 | virtual void doItem(LLFolderViewItem* item); | ||
2463 | |||
2464 | sort_order_f mSortFunction; | ||
2465 | }; | ||
2466 | |||
2467 | |||
2468 | // Set the sort order. | ||
2469 | void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder) | ||
2470 | { | ||
2471 | folder->setItemSortFunction(mSortFunction); | ||
2472 | } | ||
2473 | |||
2474 | // Do nothing. | ||
2475 | void LLSetItemSortFunction::doItem(LLFolderViewItem* item) | ||
2476 | { | ||
2477 | return; | ||
2478 | } | ||
2479 | |||
2480 | //--------------------------------------------------------------------------- | ||
2481 | |||
2482 | // Tells all folders in a folderview to close themselves | ||
2483 | // For efficiency, calls setOpenArrangeRecursively(). | ||
2484 | // The calling function must then call: | ||
2485 | // LLFolderView* root = getRoot(); | ||
2486 | // if( root ) | ||
2487 | // { | ||
2488 | // root->arrange( NULL, NULL ); | ||
2489 | // root->scrollToShowSelection(); | ||
2490 | // } | ||
2491 | // to patch things up. | ||
2492 | class LLCloseAllFoldersFunctor : public LLFolderViewFunctor | ||
2493 | { | ||
2494 | public: | ||
2495 | LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; } | ||
2496 | virtual ~LLCloseAllFoldersFunctor() {} | ||
2497 | virtual void doFolder(LLFolderViewFolder* folder); | ||
2498 | virtual void doItem(LLFolderViewItem* item); | ||
2499 | |||
2500 | BOOL mOpen; | ||
2501 | }; | ||
2502 | |||
2503 | |||
2504 | // Set the sort order. | ||
2505 | void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder) | ||
2506 | { | ||
2507 | folder->setOpenArrangeRecursively(mOpen); | ||
2508 | } | ||
2509 | |||
2510 | // Do nothing. | ||
2511 | void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item) | ||
2512 | { } | ||
2513 | |||
2514 | ///---------------------------------------------------------------------------- | ||
2515 | /// Class LLFolderView | ||
2516 | ///---------------------------------------------------------------------------- | ||
2517 | |||
2518 | // Default constructor | ||
2519 | LLFolderView::LLFolderView( const LLString& name, LLViewerImage* root_folder_icon, | ||
2520 | const LLRect& rect, const LLUUID& source_id, LLView *parent_view ) : | ||
2521 | #if LL_WINDOWS | ||
2522 | #pragma warning( push ) | ||
2523 | #pragma warning( disable : 4355 ) // warning C4355: 'this' : used in base member initializer list | ||
2524 | #endif | ||
2525 | LLFolderViewFolder( name, root_folder_icon, this, NULL ), | ||
2526 | #if LL_WINDOWS | ||
2527 | #pragma warning( pop ) | ||
2528 | #endif | ||
2529 | mScrollContainer( NULL ), | ||
2530 | mPopupMenuHandle( LLViewHandle::sDeadHandle ), | ||
2531 | mAllowMultiSelect(TRUE), | ||
2532 | mShowFolderHierarchy(FALSE), | ||
2533 | mSourceID(source_id), | ||
2534 | mRenameItem( NULL ), | ||
2535 | mNeedsScroll( FALSE ), | ||
2536 | mLastScrollItem( NULL ), | ||
2537 | mNeedsAutoSelect( FALSE ), | ||
2538 | mAutoSelectOverride(FALSE), | ||
2539 | mDebugFilters(FALSE), | ||
2540 | mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately | ||
2541 | mFilter(name), | ||
2542 | mShowSelectionContext(FALSE), | ||
2543 | mShowSingleSelection(FALSE), | ||
2544 | mArrangeGeneration(0), | ||
2545 | mSelectCallback(NULL), | ||
2546 | mMinWidth(0), | ||
2547 | mDragAndDropThisFrame(FALSE) | ||
2548 | { | ||
2549 | LLRect new_rect(rect.mLeft, rect.mBottom + mRect.getHeight(), rect.mLeft + mRect.getWidth(), rect.mBottom); | ||
2550 | setRect( rect ); | ||
2551 | reshape(rect.getWidth(), rect.getHeight()); | ||
2552 | mIsOpen = TRUE; // this view is always open. | ||
2553 | mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH); | ||
2554 | mAutoOpenCandidate = NULL; | ||
2555 | mAutoOpenTimer.stop(); | ||
2556 | mKeyboardSelection = FALSE; | ||
2557 | mIndentation = -LEFT_INDENTATION; // children start at indentation 0 | ||
2558 | gIdleCallbacks.addFunction(idle, this); | ||
2559 | |||
2560 | //clear label | ||
2561 | // go ahead and render root folder as usual | ||
2562 | // just make sure the label ("Inventory Folder") never shows up | ||
2563 | mLabel = LLString::null; | ||
2564 | |||
2565 | mRenamer = new LLLineEditor("ren", mRect, "", sFont, | ||
2566 | DB_INV_ITEM_NAME_STR_LEN, | ||
2567 | &LLFolderView::commitRename, | ||
2568 | NULL, | ||
2569 | NULL, | ||
2570 | this, | ||
2571 | &LLLineEditor::prevalidatePrintableNotPipe, | ||
2572 | LLViewBorder::BEVEL_NONE, | ||
2573 | LLViewBorder::STYLE_LINE, | ||
2574 | 2); | ||
2575 | mRenamer->setWriteableBgColor(LLColor4::white); | ||
2576 | // Escape is handled by reverting the rename, not commiting it (default behavior) | ||
2577 | mRenamer->setCommitOnFocusLost(TRUE); | ||
2578 | mRenamer->setVisible(FALSE); | ||
2579 | addChild(mRenamer); | ||
2580 | |||
2581 | // make the popup menu available | ||
2582 | LLMenuGL* menu = gUICtrlFactory->buildMenu("menu_inventory.xml", parent_view); | ||
2583 | if (!menu) | ||
2584 | { | ||
2585 | menu = new LLMenuGL(""); | ||
2586 | } | ||
2587 | menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor")); | ||
2588 | menu->setVisible(FALSE); | ||
2589 | mPopupMenuHandle = menu->mViewHandle; | ||
2590 | |||
2591 | setTabStop(TRUE); | ||
2592 | } | ||
2593 | |||
2594 | // Destroys the object | ||
2595 | LLFolderView::~LLFolderView( void ) | ||
2596 | { | ||
2597 | // The release focus call can potentially call the | ||
2598 | // scrollcontainer, which can potentially be called with a partly | ||
2599 | // destroyed scollcontainer. Just null it out here, and no worries | ||
2600 | // about calling into the invalid scroll container. | ||
2601 | // Same with the renamer. | ||
2602 | mScrollContainer = NULL; | ||
2603 | mRenameItem = NULL; | ||
2604 | mRenamer = NULL; | ||
2605 | gFocusMgr.releaseFocusIfNeeded( this ); | ||
2606 | |||
2607 | if( gEditMenuHandler == this ) | ||
2608 | { | ||
2609 | gEditMenuHandler = NULL; | ||
2610 | } | ||
2611 | |||
2612 | mAutoOpenItems.removeAllNodes(); | ||
2613 | gIdleCallbacks.deleteFunction(idle, this); | ||
2614 | |||
2615 | LLView::deleteViewByHandle(mPopupMenuHandle); | ||
2616 | |||
2617 | if(gViewerWindow->hasTopView(mRenamer)) | ||
2618 | { | ||
2619 | gViewerWindow->setTopView(NULL, NULL); | ||
2620 | } | ||
2621 | |||
2622 | mAutoOpenItems.removeAllNodes(); | ||
2623 | clearSelection(); | ||
2624 | mItems.clear(); | ||
2625 | mFolders.clear(); | ||
2626 | |||
2627 | mItemMap.clear(); | ||
2628 | } | ||
2629 | |||
2630 | EWidgetType LLFolderView::getWidgetType() const | ||
2631 | { | ||
2632 | return WIDGET_TYPE_FOLDER_VIEW; | ||
2633 | } | ||
2634 | |||
2635 | LLString LLFolderView::getWidgetTag() const | ||
2636 | { | ||
2637 | return LL_FOLDER_VIEW_TAG; | ||
2638 | } | ||
2639 | |||
2640 | BOOL LLFolderView::canFocusChildren() const | ||
2641 | { | ||
2642 | return FALSE; | ||
2643 | } | ||
2644 | |||
2645 | void LLFolderView::checkTreeResortForModelChanged() | ||
2646 | { | ||
2647 | if (mSortOrder & LLInventoryFilter::SO_DATE && !(mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME)) | ||
2648 | { | ||
2649 | // This is the case where something got added or removed. If we are date sorting | ||
2650 | // everything including folders, then we need to rebuild the whole tree. | ||
2651 | // Just set to something not SO_DATE to force the folder most resent date resort. | ||
2652 | mSortOrder = mSortOrder & ~LLInventoryFilter::SO_DATE; | ||
2653 | setSortOrder(mSortOrder | LLInventoryFilter::SO_DATE); | ||
2654 | } | ||
2655 | } | ||
2656 | |||
2657 | void LLFolderView::setSortOrder(U32 order) | ||
2658 | { | ||
2659 | if (order != mSortOrder) | ||
2660 | { | ||
2661 | LLFastTimer t(LLFastTimer::FTM_SORT); | ||
2662 | mSortOrder = order; | ||
2663 | |||
2664 | for (folders_t::iterator iter = mFolders.begin(); | ||
2665 | iter != mFolders.end();) | ||
2666 | { | ||
2667 | folders_t::iterator fit = iter++; | ||
2668 | (*fit)->sortBy(order); | ||
2669 | } | ||
2670 | |||
2671 | arrangeAll(); | ||
2672 | } | ||
2673 | } | ||
2674 | |||
2675 | |||
2676 | U32 LLFolderView::getSortOrder() const | ||
2677 | { | ||
2678 | return mSortOrder; | ||
2679 | } | ||
2680 | |||
2681 | BOOL LLFolderView::addFolder( LLFolderViewFolder* folder) | ||
2682 | { | ||
2683 | // enforce sort order of My Inventory followed by Library | ||
2684 | if (folder->getListener()->getUUID() == gInventoryLibraryRoot) | ||
2685 | { | ||
2686 | mFolders.push_back(folder); | ||
2687 | } | ||
2688 | else | ||
2689 | { | ||
2690 | mFolders.insert(mFolders.begin(), folder); | ||
2691 | } | ||
2692 | folder->setOrigin(0, 0); | ||
2693 | folder->reshape(mRect.getWidth(), 0); | ||
2694 | folder->setVisible(FALSE); | ||
2695 | addChild( folder ); | ||
2696 | folder->dirtyFilter(); | ||
2697 | return TRUE; | ||
2698 | } | ||
2699 | |||
2700 | void LLFolderView::closeAllFolders() | ||
2701 | { | ||
2702 | // Close all the folders | ||
2703 | setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); | ||
2704 | } | ||
2705 | |||
2706 | void LLFolderView::openFolder(const LLString& foldername) | ||
2707 | { | ||
2708 | LLFolderViewFolder* inv = (LLFolderViewFolder*)getChildByName(foldername); | ||
2709 | if (inv) | ||
2710 | { | ||
2711 | setSelection(inv, FALSE, FALSE); | ||
2712 | inv->setOpen(TRUE); | ||
2713 | } | ||
2714 | } | ||
2715 | |||
2716 | void LLFolderView::setOpenArrangeRecursively(BOOL open, ERecurseType recurse) | ||
2717 | { | ||
2718 | // call base class to do proper recursion | ||
2719 | LLFolderViewFolder::setOpenArrangeRecursively(open, recurse); | ||
2720 | // make sure root folder is always open | ||
2721 | mIsOpen = TRUE; | ||
2722 | } | ||
2723 | |||
2724 | // This view grows and shinks to enclose all of its children items and folders. | ||
2725 | S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation ) | ||
2726 | { | ||
2727 | LLFastTimer t2(LLFastTimer::FTM_ARRANGE); | ||
2728 | |||
2729 | filter_generation = mFilter.getMinRequiredGeneration(); | ||
2730 | mMinWidth = 0; | ||
2731 | |||
2732 | mHasVisibleChildren = hasFilteredDescendants(filter_generation); | ||
2733 | // arrange always finishes, so optimistically set the arrange generation to the most current | ||
2734 | mLastArrangeGeneration = mRoot->getArrangeGeneration(); | ||
2735 | |||
2736 | LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState(); | ||
2737 | |||
2738 | S32 total_width = LEFT_PAD; | ||
2739 | S32 running_height = mDebugFilters ? llceil(sSmallFont->getLineHeight()) : 0; | ||
2740 | S32 target_height = running_height; | ||
2741 | S32 parent_item_height = mRect.getHeight(); | ||
2742 | |||
2743 | for (folders_t::iterator iter = mFolders.begin(); | ||
2744 | iter != mFolders.end();) | ||
2745 | { | ||
2746 | folders_t::iterator fit = iter++; | ||
2747 | LLFolderViewFolder* folderp = (*fit); | ||
2748 | if (getDebugFilters()) | ||
2749 | { | ||
2750 | folderp->setVisible(TRUE); | ||
2751 | } | ||
2752 | else | ||
2753 | { | ||
2754 | folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? | ||
2755 | (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter | ||
2756 | } | ||
2757 | if (folderp->getVisible()) | ||
2758 | { | ||
2759 | S32 child_height = 0; | ||
2760 | S32 child_width = 0; | ||
2761 | S32 child_top = parent_item_height - running_height; | ||
2762 | |||
2763 | target_height += folderp->arrange( &child_width, &child_height, filter_generation ); | ||
2764 | |||
2765 | mMinWidth = llmax(mMinWidth, child_width); | ||
2766 | total_width = llmax( total_width, child_width ); | ||
2767 | running_height += child_height; | ||
2768 | folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() ); | ||
2769 | } | ||
2770 | } | ||
2771 | |||
2772 | for (items_t::iterator iter = mItems.begin(); | ||
2773 | iter != mItems.end();) | ||
2774 | { | ||
2775 | items_t::iterator iit = iter++; | ||
2776 | LLFolderViewItem* itemp = (*iit); | ||
2777 | itemp->setVisible(itemp->getFiltered(filter_generation)); | ||
2778 | |||
2779 | if (itemp->getVisible()) | ||
2780 | { | ||
2781 | S32 child_width = 0; | ||
2782 | S32 child_height = 0; | ||
2783 | S32 child_top = parent_item_height - running_height; | ||
2784 | |||
2785 | target_height += itemp->arrange( &child_width, &child_height, filter_generation ); | ||
2786 | itemp->reshape(itemp->getRect().getWidth(), child_height); | ||
2787 | |||
2788 | mMinWidth = llmax(mMinWidth, child_width); | ||
2789 | total_width = llmax( total_width, child_width ); | ||
2790 | running_height += child_height; | ||
2791 | itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() ); | ||
2792 | } | ||
2793 | } | ||
2794 | |||
2795 | S32 dummy_s32; | ||
2796 | BOOL dummy_bool; | ||
2797 | S32 min_width; | ||
2798 | mScrollContainer->calcVisibleSize( &min_width, &dummy_s32, &dummy_bool, &dummy_bool); | ||
2799 | reshape( llmax(min_width, total_width), running_height ); | ||
2800 | |||
2801 | S32 new_min_width; | ||
2802 | mScrollContainer->calcVisibleSize( &new_min_width, &dummy_s32, &dummy_bool, &dummy_bool); | ||
2803 | if (new_min_width != min_width) | ||
2804 | { | ||
2805 | reshape( llmax(min_width, total_width), running_height ); | ||
2806 | } | ||
2807 | |||
2808 | mTargetHeight = (F32)target_height; | ||
2809 | return llround(mTargetHeight); | ||
2810 | } | ||
2811 | |||
2812 | const LLString LLFolderView::getFilterSubString(BOOL trim) | ||
2813 | { | ||
2814 | return mFilter.getFilterSubString(trim); | ||
2815 | } | ||
2816 | |||
2817 | void LLFolderView::filter( LLInventoryFilter& filter ) | ||
2818 | { | ||
2819 | LLFastTimer t2(LLFastTimer::FTM_FILTER); | ||
2820 | filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000)); | ||
2821 | |||
2822 | if (getCompletedFilterGeneration() < filter.getCurrentGeneration()) | ||
2823 | { | ||
2824 | mFiltered = FALSE; | ||
2825 | mMinWidth = 0; | ||
2826 | LLFolderViewFolder::filter(filter); | ||
2827 | } | ||
2828 | } | ||
2829 | |||
2830 | void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) | ||
2831 | { | ||
2832 | S32 min_width = 0; | ||
2833 | S32 dummy_height; | ||
2834 | BOOL dummy_bool; | ||
2835 | if (mScrollContainer) | ||
2836 | { | ||
2837 | mScrollContainer->calcVisibleSize( &min_width, &dummy_height, &dummy_bool, &dummy_bool); | ||
2838 | } | ||
2839 | width = llmax(mMinWidth, min_width); | ||
2840 | LLView::reshape(width, height, called_from_parent); | ||
2841 | } | ||
2842 | |||
2843 | void LLFolderView::addToSelectionList(LLFolderViewItem* item) | ||
2844 | { | ||
2845 | if (item->isSelected()) | ||
2846 | { | ||
2847 | removeFromSelectionList(item); | ||
2848 | } | ||
2849 | if (mSelectedItems.size()) | ||
2850 | { | ||
2851 | mSelectedItems.back()->setIsCurSelection(FALSE); | ||
2852 | } | ||
2853 | item->setIsCurSelection(TRUE); | ||
2854 | mSelectedItems.push_back(item); | ||
2855 | } | ||
2856 | |||
2857 | void LLFolderView::removeFromSelectionList(LLFolderViewItem* item) | ||
2858 | { | ||
2859 | if (mSelectedItems.size()) | ||
2860 | { | ||
2861 | mSelectedItems.back()->setIsCurSelection(FALSE); | ||
2862 | } | ||
2863 | |||
2864 | selected_items_t::iterator item_iter; | ||
2865 | for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();) | ||
2866 | { | ||
2867 | if (*item_iter == item) | ||
2868 | { | ||
2869 | item_iter = mSelectedItems.erase(item_iter); | ||
2870 | } | ||
2871 | else | ||
2872 | { | ||
2873 | ++item_iter; | ||
2874 | } | ||
2875 | } | ||
2876 | if (mSelectedItems.size()) | ||
2877 | { | ||
2878 | mSelectedItems.back()->setIsCurSelection(TRUE); | ||
2879 | } | ||
2880 | } | ||
2881 | |||
2882 | LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) | ||
2883 | { | ||
2884 | if(mSelectedItems.size()) | ||
2885 | { | ||
2886 | LLFolderViewItem* itemp = mSelectedItems.back(); | ||
2887 | llassert(itemp->getIsCurSelection()); | ||
2888 | return itemp; | ||
2889 | } | ||
2890 | return NULL; | ||
2891 | } | ||
2892 | |||
2893 | |||
2894 | // Record the selected item and pass it down the hierachy. | ||
2895 | BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL open, | ||
2896 | BOOL take_keyboard_focus) | ||
2897 | { | ||
2898 | if( selection == this ) | ||
2899 | { | ||
2900 | return FALSE; | ||
2901 | } | ||
2902 | |||
2903 | if( selection && take_keyboard_focus) | ||
2904 | { | ||
2905 | setFocus(TRUE); | ||
2906 | } | ||
2907 | |||
2908 | // clear selection down here because change of keyboard focus can potentially | ||
2909 | // affect selection | ||
2910 | clearSelection(); | ||
2911 | |||
2912 | if(selection) | ||
2913 | { | ||
2914 | addToSelectionList(selection); | ||
2915 | } | ||
2916 | |||
2917 | BOOL rv = LLFolderViewFolder::setSelection(selection, open, take_keyboard_focus); | ||
2918 | if(open) | ||
2919 | { | ||
2920 | selection->getParentFolder()->requestArrange(); | ||
2921 | } | ||
2922 | |||
2923 | llassert(mSelectedItems.size() <= 1); | ||
2924 | |||
2925 | mSelectionChanged = TRUE; | ||
2926 | |||
2927 | return rv; | ||
2928 | } | ||
2929 | |||
2930 | BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) | ||
2931 | { | ||
2932 | BOOL rv = FALSE; | ||
2933 | |||
2934 | // can't select root folder | ||
2935 | if(!selection || selection == this) | ||
2936 | { | ||
2937 | return FALSE; | ||
2938 | } | ||
2939 | |||
2940 | if (!mAllowMultiSelect) | ||
2941 | { | ||
2942 | clearSelection(); | ||
2943 | } | ||
2944 | |||
2945 | selected_items_t::iterator item_iter; | ||
2946 | for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) | ||
2947 | { | ||
2948 | if (*item_iter == selection) | ||
2949 | { | ||
2950 | break; | ||
2951 | } | ||
2952 | } | ||
2953 | |||
2954 | BOOL on_list = (item_iter != mSelectedItems.end()); | ||
2955 | if (on_list && mSelectedItems.size() == 1) | ||
2956 | { | ||
2957 | // we are trying to select/deselect the only selected item | ||
2958 | return FALSE; | ||
2959 | } | ||
2960 | |||
2961 | if(selected && !on_list) | ||
2962 | { | ||
2963 | mNumDescendantsSelected++; | ||
2964 | addToSelectionList(selection); | ||
2965 | } | ||
2966 | if(!selected && on_list) | ||
2967 | { | ||
2968 | mNumDescendantsSelected--; | ||
2969 | removeFromSelectionList(selection); | ||
2970 | } | ||
2971 | |||
2972 | rv = LLFolderViewFolder::changeSelection(selection, selected); | ||
2973 | |||
2974 | mSelectionChanged = TRUE; | ||
2975 | |||
2976 | return rv; | ||
2977 | } | ||
2978 | |||
2979 | S32 LLFolderView::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items) | ||
2980 | { | ||
2981 | S32 rv = 0; | ||
2982 | |||
2983 | // now store resulting selection | ||
2984 | if (mAllowMultiSelect) | ||
2985 | { | ||
2986 | LLFolderViewItem *cur_selection = getCurSelectedItem(); | ||
2987 | rv = LLFolderViewFolder::extendSelection(selection, cur_selection, items); | ||
2988 | for (S32 i = 0; i < items.count(); i++) | ||
2989 | { | ||
2990 | addToSelectionList(items[i]); | ||
2991 | rv++; | ||
2992 | } | ||
2993 | } | ||
2994 | else | ||
2995 | { | ||
2996 | setSelection(selection, FALSE, FALSE); | ||
2997 | rv++; | ||
2998 | } | ||
2999 | |||
3000 | mSelectionChanged = TRUE; | ||
3001 | return rv; | ||
3002 | } | ||
3003 | |||
3004 | void LLFolderView::sanitizeSelection() | ||
3005 | { | ||
3006 | std::vector<LLFolderViewItem*> items_to_remove; | ||
3007 | selected_items_t::iterator item_iter; | ||
3008 | for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) | ||
3009 | { | ||
3010 | LLFolderViewItem* item = *item_iter; | ||
3011 | |||
3012 | BOOL visible = item->getVisible(); | ||
3013 | LLFolderViewFolder* parent_folder = item->getParentFolder(); | ||
3014 | while(visible && parent_folder) | ||
3015 | { | ||
3016 | visible = visible && parent_folder->isOpen() && parent_folder->getVisible(); | ||
3017 | parent_folder = parent_folder->getParentFolder(); | ||
3018 | } | ||
3019 | if (!visible || item->getNumSelectedDescendants() > 0) | ||
3020 | { | ||
3021 | // only deselect self if not visible | ||
3022 | // check to see if item failed the filter but was checked against most recent generation | ||
3023 | if ((!item->getFiltered() && item->getLastFilterGeneration() >= getFilter()->getMinRequiredGeneration()) | ||
3024 | || (item->getParentFolder() && !item->getParentFolder()->isOpen())) | ||
3025 | { | ||
3026 | item->recursiveDeselect(TRUE); | ||
3027 | items_to_remove.push_back(item); | ||
3028 | } | ||
3029 | else | ||
3030 | { | ||
3031 | item->recursiveDeselect(FALSE); | ||
3032 | } | ||
3033 | |||
3034 | selected_items_t::iterator other_item_iter; | ||
3035 | for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter) | ||
3036 | { | ||
3037 | LLFolderViewItem* other_item = *other_item_iter; | ||
3038 | LLFolderViewFolder* parent_folder = other_item->getParentFolder(); | ||
3039 | while (parent_folder) | ||
3040 | { | ||
3041 | if (parent_folder == item) | ||
3042 | { | ||
3043 | // this is a descendent of the current folder, remove from list | ||
3044 | items_to_remove.push_back(other_item); | ||
3045 | break; | ||
3046 | } | ||
3047 | parent_folder = parent_folder->getParentFolder(); | ||
3048 | } | ||
3049 | } | ||
3050 | } | ||
3051 | } | ||
3052 | |||
3053 | std::vector<LLFolderViewItem*>::iterator item_it; | ||
3054 | for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it ) | ||
3055 | { | ||
3056 | removeFromSelectionList(*item_it); | ||
3057 | } | ||
3058 | } | ||
3059 | |||
3060 | void LLFolderView::clearSelection() | ||
3061 | { | ||
3062 | if (mSelectedItems.size() > 0) | ||
3063 | { | ||
3064 | recursiveDeselect(FALSE); | ||
3065 | mSelectedItems.clear(); | ||
3066 | } | ||
3067 | } | ||
3068 | |||
3069 | BOOL LLFolderView::getSelectionList(std::set<LLUUID> &selection) | ||
3070 | { | ||
3071 | selected_items_t::iterator item_it; | ||
3072 | for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) | ||
3073 | { | ||
3074 | selection.insert((*item_it)->getListener()->getUUID()); | ||
3075 | } | ||
3076 | |||
3077 | return (selection.size() != 0); | ||
3078 | } | ||
3079 | |||
3080 | BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source) | ||
3081 | { | ||
3082 | std::vector<EDragAndDropType> types; | ||
3083 | std::vector<LLUUID> cargo_ids; | ||
3084 | selected_items_t::iterator item_it; | ||
3085 | BOOL can_drag = TRUE; | ||
3086 | if (!mSelectedItems.empty()) | ||
3087 | { | ||
3088 | for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) | ||
3089 | { | ||
3090 | EDragAndDropType type = DAD_NONE; | ||
3091 | LLUUID id = LLUUID::null; | ||
3092 | can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id); | ||
3093 | |||
3094 | types.push_back(type); | ||
3095 | cargo_ids.push_back(id); | ||
3096 | } | ||
3097 | |||
3098 | gToolDragAndDrop->beginMultiDrag(types, cargo_ids, source, mSourceID); | ||
3099 | } | ||
3100 | return can_drag; | ||
3101 | } | ||
3102 | |||
3103 | void LLFolderView::commitRename( LLUICtrl* renamer, void* user_data ) | ||
3104 | { | ||
3105 | LLFolderView* root = reinterpret_cast<LLFolderView*>(user_data); | ||
3106 | if( root ) | ||
3107 | { | ||
3108 | root->finishRenamingItem(); | ||
3109 | } | ||
3110 | } | ||
3111 | |||
3112 | void LLFolderView::draw() | ||
3113 | { | ||
3114 | if (mDebugFilters) | ||
3115 | { | ||
3116 | LLString current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", | ||
3117 | mFilter.getCurrentGeneration(), mFilter.getMinRequiredGeneration(), mFilter.getMustPassGeneration()); | ||
3118 | sSmallFont->renderUTF8(current_filter_string, 0, 2, | ||
3119 | mRect.getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), | ||
3120 | LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE ); | ||
3121 | } | ||
3122 | |||
3123 | // if cursor has moved off of me during drag and drop | ||
3124 | // close all auto opened folders | ||
3125 | if (!mDragAndDropThisFrame) | ||
3126 | { | ||
3127 | closeAutoOpenedFolders(); | ||
3128 | } | ||
3129 | if(gViewerWindow->hasKeyboardFocus(this) && !getVisible()) | ||
3130 | { | ||
3131 | gViewerWindow->setKeyboardFocus( NULL, NULL ); | ||
3132 | } | ||
3133 | |||
3134 | // while dragging, update selection rendering to reflect single/multi drag status | ||
3135 | if (gToolDragAndDrop->hasMouseCapture()) | ||
3136 | { | ||
3137 | EAcceptance last_accept = gToolDragAndDrop->getLastAccept(); | ||
3138 | if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) | ||
3139 | { | ||
3140 | setShowSingleSelection(TRUE); | ||
3141 | } | ||
3142 | else | ||
3143 | { | ||
3144 | setShowSingleSelection(FALSE); | ||
3145 | } | ||
3146 | } | ||
3147 | else | ||
3148 | { | ||
3149 | setShowSingleSelection(FALSE); | ||
3150 | } | ||
3151 | |||
3152 | |||
3153 | if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size()) | ||
3154 | { | ||
3155 | mSearchString.clear(); | ||
3156 | } | ||
3157 | |||
3158 | if (hasVisibleChildren() || getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) | ||
3159 | { | ||
3160 | setStatusText(""); | ||
3161 | } | ||
3162 | else | ||
3163 | { | ||
3164 | if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter.getMinRequiredGeneration()) | ||
3165 | { | ||
3166 | setStatusText("Searching..."); | ||
3167 | sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE ); | ||
3168 | } | ||
3169 | else | ||
3170 | { | ||
3171 | setStatusText("No matching items found in inventory."); | ||
3172 | sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE ); | ||
3173 | } | ||
3174 | } | ||
3175 | |||
3176 | LLFolderViewFolder::draw(); | ||
3177 | |||
3178 | mDragAndDropThisFrame = FALSE; | ||
3179 | } | ||
3180 | |||
3181 | void LLFolderView::finishRenamingItem( void ) | ||
3182 | { | ||
3183 | if(!mRenamer) | ||
3184 | { | ||
3185 | return; | ||
3186 | } | ||
3187 | if( mRenameItem ) | ||
3188 | { | ||
3189 | mRenameItem->rename( mRenamer->getText().c_str() ); | ||
3190 | } | ||
3191 | |||
3192 | mRenamer->setCommitOnFocusLost( FALSE ); | ||
3193 | mRenamer->setFocus( FALSE ); | ||
3194 | mRenamer->setVisible( FALSE ); | ||
3195 | mRenamer->setCommitOnFocusLost( TRUE ); | ||
3196 | gViewerWindow->setTopView( NULL, NULL ); | ||
3197 | |||
3198 | if( mRenameItem ) | ||
3199 | { | ||
3200 | setSelectionFromRoot( mRenameItem, TRUE ); | ||
3201 | mRenameItem = NULL; | ||
3202 | } | ||
3203 | |||
3204 | // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible. | ||
3205 | scrollToShowSelection(); | ||
3206 | } | ||
3207 | |||
3208 | void LLFolderView::revertRenamingItem( void ) | ||
3209 | { | ||
3210 | mRenamer->setCommitOnFocusLost( FALSE ); | ||
3211 | mRenamer->setFocus( FALSE ); | ||
3212 | mRenamer->setVisible( FALSE ); | ||
3213 | mRenamer->setCommitOnFocusLost( TRUE ); | ||
3214 | gViewerWindow->setTopView( NULL, NULL ); | ||
3215 | |||
3216 | if( mRenameItem ) | ||
3217 | { | ||
3218 | setSelectionFromRoot( mRenameItem, TRUE ); | ||
3219 | mRenameItem = NULL; | ||
3220 | } | ||
3221 | } | ||
3222 | |||
3223 | void LLFolderView::removeSelectedItems( void ) | ||
3224 | { | ||
3225 | if(getVisible() && mEnabled) | ||
3226 | { | ||
3227 | // just in case we're removing the renaming item. | ||
3228 | mRenameItem = NULL; | ||
3229 | |||
3230 | // create a temporary structure which we will use to remove | ||
3231 | // items, since the removal will futz with internal data | ||
3232 | // structures. | ||
3233 | LLDynamicArray<LLFolderViewItem*> items; | ||
3234 | S32 count = mSelectedItems.size(); | ||
3235 | if(count == 0) return; | ||
3236 | LLFolderViewItem* item = NULL; | ||
3237 | selected_items_t::iterator item_it; | ||
3238 | for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) | ||
3239 | { | ||
3240 | item = *item_it; | ||
3241 | if(item->isRemovable()) | ||
3242 | { | ||
3243 | items.put(item); | ||
3244 | } | ||
3245 | else | ||
3246 | { | ||
3247 | llinfos << "Cannot delete " << item->getName() << llendl; | ||
3248 | return; | ||
3249 | } | ||
3250 | } | ||
3251 | |||
3252 | // iterate through the new container. | ||
3253 | count = items.count(); | ||
3254 | LLUUID new_selection_id; | ||
3255 | if(count == 1) | ||
3256 | { | ||
3257 | LLFolderViewItem* item_to_delete = items.get(0); | ||
3258 | LLFolderViewFolder* parent = item_to_delete->getParentFolder(); | ||
3259 | LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE); | ||
3260 | if (!new_selection) | ||
3261 | { | ||
3262 | new_selection = item_to_delete->getPreviousOpenNode(FALSE); | ||
3263 | } | ||
3264 | if(parent) | ||
3265 | { | ||
3266 | if (parent->removeItem(item_to_delete)) | ||
3267 | { | ||
3268 | // change selection on successful delete | ||
3269 | if (new_selection) | ||
3270 | { | ||
3271 | setSelectionFromRoot(new_selection, new_selection->isOpen(), gViewerWindow->childHasKeyboardFocus(this)); | ||
3272 | } | ||
3273 | else | ||
3274 | { | ||
3275 | setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this)); | ||
3276 | } | ||
3277 | } | ||
3278 | } | ||
3279 | arrangeAll(); | ||
3280 | } | ||
3281 | else if (count > 1) | ||
3282 | { | ||
3283 | LLDynamicArray<LLFolderViewEventListener*> listeners; | ||
3284 | LLFolderViewEventListener* listener; | ||
3285 | LLFolderViewItem* last_item = items.get(count - 1); | ||
3286 | LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE); | ||
3287 | while(new_selection && new_selection->isSelected()) | ||
3288 | { | ||
3289 | new_selection = new_selection->getNextOpenNode(FALSE); | ||
3290 | } | ||
3291 | if (!new_selection) | ||
3292 | { | ||
3293 | new_selection = last_item->getPreviousOpenNode(FALSE); | ||
3294 | while (new_selection && new_selection->isSelected()) | ||
3295 | { | ||
3296 | new_selection = new_selection->getPreviousOpenNode(FALSE); | ||
3297 | } | ||
3298 | } | ||
3299 | if (new_selection) | ||
3300 | { | ||
3301 | setSelectionFromRoot(new_selection, new_selection->isOpen(), gViewerWindow->childHasKeyboardFocus(this)); | ||
3302 | } | ||
3303 | else | ||
3304 | { | ||
3305 | setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this)); | ||
3306 | } | ||
3307 | |||
3308 | for(S32 i = 0; i < count; ++i) | ||
3309 | { | ||
3310 | listener = items.get(i)->getListener(); | ||
3311 | if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::FAIL)) | ||
3312 | { | ||
3313 | listeners.put(listener); | ||
3314 | } | ||
3315 | } | ||
3316 | listener = listeners.get(0); | ||
3317 | if(listener) | ||
3318 | { | ||
3319 | listener->removeBatch(listeners); | ||
3320 | } | ||
3321 | } | ||
3322 | arrangeAll(); | ||
3323 | scrollToShowSelection(); | ||
3324 | } | ||
3325 | } | ||
3326 | |||
3327 | // open the selected item. | ||
3328 | void LLFolderView::openSelectedItems( void ) | ||
3329 | { | ||
3330 | if(getVisible() && mEnabled) | ||
3331 | { | ||
3332 | if (mSelectedItems.size() == 1) | ||
3333 | { | ||
3334 | mSelectedItems.front()->open(); | ||
3335 | } | ||
3336 | else | ||
3337 | { | ||
3338 | S32 left, top; | ||
3339 | gFloaterView->getNewFloaterPosition(&left, &top); | ||
3340 | |||
3341 | LLMultiPreview* multi_previewp = new LLMultiPreview(LLRect(left, top, left + 300, top - 100)); | ||
3342 | |||
3343 | LLFloater::setFloaterHost(multi_previewp); | ||
3344 | |||
3345 | selected_items_t::iterator item_it; | ||
3346 | for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) | ||
3347 | { | ||
3348 | (*item_it)->open(); | ||
3349 | } | ||
3350 | |||
3351 | LLFloater::setFloaterHost(NULL); | ||
3352 | multi_previewp->open(); | ||
3353 | } | ||
3354 | } | ||
3355 | } | ||
3356 | |||
3357 | void LLFolderView::propertiesSelectedItems( void ) | ||
3358 | { | ||
3359 | if(getVisible() && mEnabled) | ||
3360 | { | ||
3361 | if (mSelectedItems.size() == 1) | ||
3362 | { | ||
3363 | LLFolderViewItem* folder_item = mSelectedItems.front(); | ||
3364 | if(!folder_item) return; | ||
3365 | folder_item->getListener()->showProperties(); | ||
3366 | } | ||
3367 | else | ||
3368 | { | ||
3369 | S32 left, top; | ||
3370 | gFloaterView->getNewFloaterPosition(&left, &top); | ||
3371 | |||
3372 | LLMultiProperties* multi_propertiesp = new LLMultiProperties(LLRect(left, top, left + 100, top - 100)); | ||
3373 | |||
3374 | LLFloater::setFloaterHost(multi_propertiesp); | ||
3375 | |||
3376 | selected_items_t::iterator item_it; | ||
3377 | for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) | ||
3378 | { | ||
3379 | (*item_it)->getListener()->showProperties(); | ||
3380 | } | ||
3381 | |||
3382 | LLFloater::setFloaterHost(NULL); | ||
3383 | multi_propertiesp->open(); | ||
3384 | } | ||
3385 | } | ||
3386 | } | ||
3387 | |||
3388 | void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) | ||
3389 | { | ||
3390 | if (mAutoOpenItems.check() == item || mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) | ||
3391 | { | ||
3392 | return; | ||
3393 | } | ||
3394 | |||
3395 | // close auto-opened folders | ||
3396 | LLFolderViewFolder* close_item = mAutoOpenItems.check(); | ||
3397 | while (close_item && close_item != item->getParentFolder()) | ||
3398 | { | ||
3399 | mAutoOpenItems.pop(); | ||
3400 | close_item->setOpenArrangeRecursively(FALSE); | ||
3401 | close_item = mAutoOpenItems.check(); | ||
3402 | } | ||
3403 | |||
3404 | item->requestArrange(); | ||
3405 | |||
3406 | mAutoOpenItems.push(item); | ||
3407 | |||
3408 | item->setOpen(TRUE); | ||
3409 | scrollToShowItem(item); | ||
3410 | } | ||
3411 | |||
3412 | void LLFolderView::closeAutoOpenedFolders() | ||
3413 | { | ||
3414 | while (mAutoOpenItems.check()) | ||
3415 | { | ||
3416 | LLFolderViewFolder* close_item = mAutoOpenItems.pop(); | ||
3417 | close_item->setOpen(FALSE); | ||
3418 | } | ||
3419 | |||
3420 | if (mAutoOpenCandidate) | ||
3421 | { | ||
3422 | mAutoOpenCandidate->setAutoOpenCountdown(0.f); | ||
3423 | } | ||
3424 | mAutoOpenCandidate = NULL; | ||
3425 | mAutoOpenTimer.stop(); | ||
3426 | } | ||
3427 | |||
3428 | BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder) | ||
3429 | { | ||
3430 | if (folder && mAutoOpenCandidate == folder) | ||
3431 | { | ||
3432 | if (mAutoOpenTimer.getStarted()) | ||
3433 | { | ||
3434 | if (!mAutoOpenCandidate->isOpen()) | ||
3435 | { | ||
3436 | mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f)); | ||
3437 | } | ||
3438 | if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime) | ||
3439 | { | ||
3440 | autoOpenItem(folder); | ||
3441 | mAutoOpenTimer.stop(); | ||
3442 | return TRUE; | ||
3443 | } | ||
3444 | } | ||
3445 | return FALSE; | ||
3446 | } | ||
3447 | |||
3448 | // otherwise new candidate, restart timer | ||
3449 | if (mAutoOpenCandidate) | ||
3450 | { | ||
3451 | mAutoOpenCandidate->setAutoOpenCountdown(0.f); | ||
3452 | } | ||
3453 | mAutoOpenCandidate = folder; | ||
3454 | mAutoOpenTimer.start(); | ||
3455 | return FALSE; | ||
3456 | } | ||
3457 | |||
3458 | BOOL LLFolderView::canCopy() | ||
3459 | { | ||
3460 | if (!(getVisible() && mEnabled && (mSelectedItems.size() > 0))) | ||
3461 | { | ||
3462 | return FALSE; | ||
3463 | } | ||
3464 | |||
3465 | selected_items_t::iterator selected_it; | ||
3466 | for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) | ||
3467 | { | ||
3468 | LLFolderViewItem* item = *selected_it; | ||
3469 | if (!item->getListener()->isItemCopyable()) | ||
3470 | { | ||
3471 | return FALSE; | ||
3472 | } | ||
3473 | } | ||
3474 | return TRUE; | ||
3475 | } | ||
3476 | |||
3477 | // copy selected item | ||
3478 | void LLFolderView::copy() | ||
3479 | { | ||
3480 | // *NOTE: total hack to clear the inventory clipboard | ||
3481 | LLInventoryClipboard::instance().reset(); | ||
3482 | S32 count = mSelectedItems.size(); | ||
3483 | if(getVisible() && mEnabled && (count > 0)) | ||
3484 | { | ||
3485 | LLFolderViewEventListener* listener = NULL; | ||
3486 | selected_items_t::iterator item_it; | ||
3487 | for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) | ||
3488 | { | ||
3489 | listener = (*item_it)->getListener(); | ||
3490 | if(listener) | ||
3491 | { | ||
3492 | listener->copyToClipboard(); | ||
3493 | } | ||
3494 | } | ||
3495 | } | ||
3496 | mSearchString.clear(); | ||
3497 | } | ||
3498 | |||
3499 | BOOL LLFolderView::canCut() | ||
3500 | { | ||
3501 | return FALSE; | ||
3502 | } | ||
3503 | |||
3504 | void LLFolderView::cut() | ||
3505 | { | ||
3506 | // implement Windows-style cut-and-leave | ||
3507 | } | ||
3508 | |||
3509 | BOOL LLFolderView::canPaste() | ||
3510 | { | ||
3511 | if (mSelectedItems.empty()) | ||
3512 | { | ||
3513 | return FALSE; | ||
3514 | } | ||
3515 | |||
3516 | if(getVisible() && mEnabled) | ||
3517 | { | ||
3518 | selected_items_t::iterator item_it; | ||
3519 | for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) | ||
3520 | { | ||
3521 | // *TODO: only check folders and parent folders of items | ||
3522 | LLFolderViewItem* item = (*item_it); | ||
3523 | LLFolderViewEventListener* listener = item->getListener(); | ||
3524 | if(!listener || !listener->isClipboardPasteable()) | ||
3525 | { | ||
3526 | LLFolderViewFolder* folderp = item->getParentFolder(); | ||
3527 | listener = folderp->getListener(); | ||
3528 | if (!listener || !listener->isClipboardPasteable()) | ||
3529 | { | ||
3530 | return FALSE; | ||
3531 | } | ||
3532 | } | ||
3533 | } | ||
3534 | return TRUE; | ||
3535 | } | ||
3536 | return FALSE; | ||
3537 | } | ||
3538 | |||
3539 | // paste selected item | ||
3540 | void LLFolderView::paste() | ||
3541 | { | ||
3542 | if(getVisible() && mEnabled) | ||
3543 | { | ||
3544 | // find set of unique folders to paste into | ||
3545 | std::set<LLFolderViewItem*> folder_set; | ||
3546 | |||
3547 | selected_items_t::iterator selected_it; | ||
3548 | for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) | ||
3549 | { | ||
3550 | LLFolderViewItem* item = *selected_it; | ||
3551 | LLFolderViewEventListener* listener = item->getListener(); | ||
3552 | if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY) | ||
3553 | { | ||
3554 | item = item->getParentFolder(); | ||
3555 | } | ||
3556 | folder_set.insert(item); | ||
3557 | } | ||
3558 | |||
3559 | std::set<LLFolderViewItem*>::iterator set_iter; | ||
3560 | for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter) | ||
3561 | { | ||
3562 | LLFolderViewEventListener* listener = (*set_iter)->getListener(); | ||
3563 | if(listener && listener->isClipboardPasteable()) | ||
3564 | { | ||
3565 | listener->pasteFromClipboard(); | ||
3566 | } | ||
3567 | } | ||
3568 | } | ||
3569 | mSearchString.clear(); | ||
3570 | } | ||
3571 | |||
3572 | // public rename functionality - can only start the process | ||
3573 | void LLFolderView::startRenamingSelectedItem( void ) | ||
3574 | { | ||
3575 | // make sure selection is visible | ||
3576 | scrollToShowSelection(); | ||
3577 | |||
3578 | S32 count = mSelectedItems.size(); | ||
3579 | LLFolderViewItem* item = NULL; | ||
3580 | if(count > 0) | ||
3581 | { | ||
3582 | item = mSelectedItems.front(); | ||
3583 | } | ||
3584 | if(getVisible() && mEnabled && (count == 1) && item && item->getListener() && | ||
3585 | item->getListener()->isItemRenameable()) | ||
3586 | { | ||
3587 | mRenameItem = item; | ||
3588 | |||
3589 | S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 + item->getIndentation(); | ||
3590 | S32 y = llfloor(item->getRect().getHeight()-sFont->getLineHeight()-2); | ||
3591 | item->localPointToScreen( x, y, &x, &y ); | ||
3592 | screenPointToLocal( x, y, &x, &y ); | ||
3593 | mRenamer->setOrigin( x, y ); | ||
3594 | |||
3595 | S32 scroller_height = 0; | ||
3596 | S32 scroller_width = gViewerWindow->getWindowWidth(); | ||
3597 | BOOL dummy_bool; | ||
3598 | if (mScrollContainer) | ||
3599 | { | ||
3600 | mScrollContainer->calcVisibleSize( &scroller_width, &scroller_height, &dummy_bool, &dummy_bool); | ||
3601 | } | ||
3602 | |||
3603 | S32 width = llmax(llmin(item->getRect().getWidth() - x, scroller_width - x - mRect.mLeft), MINIMUM_RENAMER_WIDTH); | ||
3604 | S32 height = llfloor(sFont->getLineHeight() + RENAME_HEIGHT_PAD); | ||
3605 | mRenamer->reshape( width, height, TRUE ); | ||
3606 | |||
3607 | mRenamer->setText(item->getName()); | ||
3608 | mRenamer->selectAll(); | ||
3609 | mRenamer->setVisible( TRUE ); | ||
3610 | // set focus will fail unless item is visible | ||
3611 | mRenamer->setFocus( TRUE ); | ||
3612 | gViewerWindow->setTopView( mRenamer, top_view_lost ); | ||
3613 | } | ||
3614 | } | ||
3615 | |||
3616 | void LLFolderView::setFocus(BOOL focus) | ||
3617 | { | ||
3618 | if (focus) | ||
3619 | { | ||
3620 | // select "My Inventory" if nothing selected | ||
3621 | if (!getCurSelectedItem()) | ||
3622 | { | ||
3623 | LLFolderViewItem* itemp = getItemByID(gAgent.getInventoryRootID()); | ||
3624 | if (itemp) | ||
3625 | { | ||
3626 | setSelection(itemp, FALSE, FALSE); | ||
3627 | } | ||
3628 | } | ||
3629 | |||
3630 | if (mRenamer->getVisible()) | ||
3631 | { | ||
3632 | //RN: commit rename changes when focus is moved, only revert on ESC | ||
3633 | finishRenamingItem(); | ||
3634 | } | ||
3635 | if(!hasFocus()) | ||
3636 | { | ||
3637 | gEditMenuHandler = this; | ||
3638 | } | ||
3639 | } | ||
3640 | |||
3641 | LLFolderViewFolder::setFocus(focus); | ||
3642 | } | ||
3643 | |||
3644 | BOOL LLFolderView::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) | ||
3645 | { | ||
3646 | BOOL handled = FALSE; | ||
3647 | |||
3648 | LLView *item = NULL; | ||
3649 | if (getChildCount() > 0) | ||
3650 | { | ||
3651 | item = *(getChildList()->begin()); | ||
3652 | } | ||
3653 | |||
3654 | if( getVisible() && mEnabled && !called_from_parent ) | ||
3655 | { | ||
3656 | switch( key ) | ||
3657 | { | ||
3658 | case KEY_F2: | ||
3659 | mSearchString.clear(); | ||
3660 | startRenamingSelectedItem(); | ||
3661 | handled = TRUE; | ||
3662 | break; | ||
3663 | |||
3664 | case KEY_RETURN: | ||
3665 | if (mask == MASK_NONE) | ||
3666 | { | ||
3667 | if( mRenameItem && mRenamer->getVisible() ) | ||
3668 | { | ||
3669 | finishRenamingItem(); | ||
3670 | mSearchString.clear(); | ||
3671 | handled = TRUE; | ||
3672 | } | ||
3673 | else | ||
3674 | { | ||
3675 | LLFolderView::openSelectedItems(); | ||
3676 | handled = TRUE; | ||
3677 | } | ||
3678 | } | ||
3679 | break; | ||
3680 | |||
3681 | case KEY_ESCAPE: | ||
3682 | // mark flag don't commit | ||
3683 | if( mRenameItem && mRenamer->getVisible() ) | ||
3684 | { | ||
3685 | revertRenamingItem(); | ||
3686 | handled = TRUE; | ||
3687 | } | ||
3688 | else | ||
3689 | { | ||
3690 | if( gViewerWindow->childHasKeyboardFocus( this ) ) | ||
3691 | { | ||
3692 | gViewerWindow->setKeyboardFocus( NULL, NULL ); | ||
3693 | } | ||
3694 | } | ||
3695 | mSearchString.clear(); | ||
3696 | break; | ||
3697 | |||
3698 | case KEY_PAGE_UP: | ||
3699 | mSearchString.clear(); | ||
3700 | mScrollContainer->pageUp(30); | ||
3701 | handled = TRUE; | ||
3702 | break; | ||
3703 | |||
3704 | case KEY_PAGE_DOWN: | ||
3705 | mSearchString.clear(); | ||
3706 | mScrollContainer->pageDown(30); | ||
3707 | handled = TRUE; | ||
3708 | break; | ||
3709 | |||
3710 | case KEY_HOME: | ||
3711 | mSearchString.clear(); | ||
3712 | mScrollContainer->goToTop(); | ||
3713 | handled = TRUE; | ||
3714 | break; | ||
3715 | |||
3716 | case KEY_END: | ||
3717 | mSearchString.clear(); | ||
3718 | mScrollContainer->goToBottom(); | ||
3719 | break; | ||
3720 | |||
3721 | case KEY_DOWN: | ||
3722 | if((mSelectedItems.size() > 0) && mScrollContainer) | ||
3723 | { | ||
3724 | LLFolderViewItem* last_selected = getCurSelectedItem(); | ||
3725 | |||
3726 | if (!mKeyboardSelection) | ||
3727 | { | ||
3728 | setSelection(last_selected, FALSE, TRUE); | ||
3729 | mKeyboardSelection = TRUE; | ||
3730 | } | ||
3731 | |||
3732 | LLFolderViewItem* next = NULL; | ||
3733 | if (mask & MASK_SHIFT) | ||
3734 | { | ||
3735 | // don't shift select down to children of folders (they are implicitly selected through parent) | ||
3736 | next = last_selected->getNextOpenNode(FALSE); | ||
3737 | if (next) | ||
3738 | { | ||
3739 | if (next->isSelected()) | ||
3740 | { | ||
3741 | // shrink selection | ||
3742 | changeSelectionFromRoot(last_selected, FALSE); | ||
3743 | } | ||
3744 | else if (last_selected->getParentFolder() == next->getParentFolder()) | ||
3745 | { | ||
3746 | // grow selection | ||
3747 | changeSelectionFromRoot(next, TRUE); | ||
3748 | } | ||
3749 | } | ||
3750 | } | ||
3751 | else | ||
3752 | { | ||
3753 | next = last_selected->getNextOpenNode(); | ||
3754 | if( next ) | ||
3755 | { | ||
3756 | if (next == last_selected) | ||
3757 | { | ||
3758 | return FALSE; | ||
3759 | } | ||
3760 | setSelection( next, FALSE, TRUE ); | ||
3761 | } | ||
3762 | } | ||
3763 | scrollToShowSelection(); | ||
3764 | mSearchString.clear(); | ||
3765 | handled = TRUE; | ||
3766 | } | ||
3767 | break; | ||
3768 | |||
3769 | case KEY_UP: | ||
3770 | if((mSelectedItems.size() > 0) && mScrollContainer) | ||
3771 | { | ||
3772 | LLFolderViewItem* last_selected = mSelectedItems.back(); | ||
3773 | |||
3774 | if (!mKeyboardSelection) | ||
3775 | { | ||
3776 | setSelection(last_selected, FALSE, TRUE); | ||
3777 | mKeyboardSelection = TRUE; | ||
3778 | } | ||
3779 | |||
3780 | LLFolderViewItem* prev = NULL; | ||
3781 | if (mask & MASK_SHIFT) | ||
3782 | { | ||
3783 | // don't shift select down to children of folders (they are implicitly selected through parent) | ||
3784 | prev = last_selected->getPreviousOpenNode(FALSE); | ||
3785 | if (prev) | ||
3786 | { | ||
3787 | if (prev->isSelected()) | ||
3788 | { | ||
3789 | // shrink selection | ||
3790 | changeSelectionFromRoot(last_selected, FALSE); | ||
3791 | } | ||
3792 | else if (last_selected->getParentFolder() == prev->getParentFolder()) | ||
3793 | { | ||
3794 | // grow selection | ||
3795 | changeSelectionFromRoot(prev, TRUE); | ||
3796 | } | ||
3797 | } | ||
3798 | } | ||
3799 | else | ||
3800 | { | ||
3801 | prev = last_selected->getPreviousOpenNode(); | ||
3802 | if( prev ) | ||
3803 | { | ||
3804 | if (prev == this) | ||
3805 | { | ||
3806 | return FALSE; | ||
3807 | } | ||
3808 | setSelection( prev, FALSE, TRUE ); | ||
3809 | } | ||
3810 | } | ||
3811 | scrollToShowSelection(); | ||
3812 | mSearchString.clear(); | ||
3813 | |||
3814 | handled = TRUE; | ||
3815 | } | ||
3816 | break; | ||
3817 | |||
3818 | case KEY_RIGHT: | ||
3819 | if(mSelectedItems.size()) | ||
3820 | { | ||
3821 | LLFolderViewItem* last_selected = getCurSelectedItem(); | ||
3822 | last_selected->setOpen( TRUE ); | ||
3823 | mSearchString.clear(); | ||
3824 | handled = TRUE; | ||
3825 | } | ||
3826 | break; | ||
3827 | |||
3828 | case KEY_LEFT: | ||
3829 | if(mSelectedItems.size()) | ||
3830 | { | ||
3831 | LLFolderViewItem* last_selected = getCurSelectedItem(); | ||
3832 | LLFolderViewItem* parent_folder = last_selected->getParentFolder(); | ||
3833 | if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder()) | ||
3834 | { | ||
3835 | setSelection(parent_folder, FALSE, TRUE); | ||
3836 | } | ||
3837 | else | ||
3838 | { | ||
3839 | last_selected->setOpen( FALSE ); | ||
3840 | } | ||
3841 | mSearchString.clear(); | ||
3842 | scrollToShowSelection(); | ||
3843 | handled = TRUE; | ||
3844 | } | ||
3845 | break; | ||
3846 | } | ||
3847 | } | ||
3848 | |||
3849 | if (!handled && gFocusMgr.childHasKeyboardFocus(getRoot())) | ||
3850 | { | ||
3851 | if (key == KEY_BACKSPACE) | ||
3852 | { | ||
3853 | mSearchTimer.reset(); | ||
3854 | if (mSearchString.size()) | ||
3855 | { | ||
3856 | mSearchString.erase(mSearchString.size() - 1, 1); | ||
3857 | } | ||
3858 | search(getCurSelectedItem(), mSearchString.c_str(), FALSE); | ||
3859 | handled = TRUE; | ||
3860 | } | ||
3861 | else if (mask & MASK_CONTROL && key == 'N') | ||
3862 | { | ||
3863 | LLFolderViewItem* selection = getCurSelectedItem(); | ||
3864 | if (selection) | ||
3865 | { | ||
3866 | selection = selection->getNextOpenNode(); | ||
3867 | } | ||
3868 | search(selection, mSearchString.c_str(), FALSE); | ||
3869 | mSearchTimer.reset(); | ||
3870 | handled = TRUE; | ||
3871 | } | ||
3872 | else if (mask & MASK_CONTROL && key == 'P') | ||
3873 | { | ||
3874 | LLFolderViewItem* selection = getCurSelectedItem(); | ||
3875 | if (selection) | ||
3876 | { | ||
3877 | selection = selection->getPreviousOpenNode(); | ||
3878 | } | ||
3879 | search(selection, mSearchString.c_str(), TRUE); | ||
3880 | mSearchTimer.reset(); | ||
3881 | handled = TRUE; | ||
3882 | } | ||
3883 | } | ||
3884 | |||
3885 | if (handled) | ||
3886 | { | ||
3887 | gViewerWindow->requestFastFrame(this); | ||
3888 | } | ||
3889 | return handled; | ||
3890 | } | ||
3891 | |||
3892 | |||
3893 | BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent) | ||
3894 | { | ||
3895 | if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL | ||
3896 | { | ||
3897 | return FALSE; | ||
3898 | } | ||
3899 | |||
3900 | if (uni_char > 0x7f) | ||
3901 | { | ||
3902 | llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl; | ||
3903 | return FALSE; | ||
3904 | } | ||
3905 | |||
3906 | BOOL handled = FALSE; | ||
3907 | if (gFocusMgr.childHasKeyboardFocus(getRoot())) | ||
3908 | { | ||
3909 | //do text search | ||
3910 | if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout")) | ||
3911 | { | ||
3912 | mSearchString.clear(); | ||
3913 | } | ||
3914 | mSearchTimer.reset(); | ||
3915 | if (mSearchString.size() < 128) | ||
3916 | { | ||
3917 | mSearchString += uni_char; | ||
3918 | } | ||
3919 | search(getCurSelectedItem(), mSearchString.c_str(), FALSE); | ||
3920 | |||
3921 | handled = TRUE; | ||
3922 | } | ||
3923 | |||
3924 | if (handled) | ||
3925 | { | ||
3926 | gViewerWindow->requestFastFrame(this); | ||
3927 | } | ||
3928 | |||
3929 | return handled; | ||
3930 | } | ||
3931 | |||
3932 | |||
3933 | BOOL LLFolderView::canDoDelete() | ||
3934 | { | ||
3935 | if (mSelectedItems.size() == 0) return FALSE; | ||
3936 | selected_items_t::iterator item_it; | ||
3937 | for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) | ||
3938 | { | ||
3939 | if (!(*item_it)->getListener()->isItemRemovable()) | ||
3940 | { | ||
3941 | return FALSE; | ||
3942 | } | ||
3943 | } | ||
3944 | return TRUE; | ||
3945 | } | ||
3946 | |||
3947 | void LLFolderView::doDelete() | ||
3948 | { | ||
3949 | if(mSelectedItems.size() > 0) | ||
3950 | { | ||
3951 | removeSelectedItems(); | ||
3952 | } | ||
3953 | } | ||
3954 | |||
3955 | |||
3956 | BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) | ||
3957 | { | ||
3958 | mKeyboardSelection = FALSE; | ||
3959 | mSearchString.clear(); | ||
3960 | |||
3961 | setFocus(TRUE); | ||
3962 | |||
3963 | return LLView::handleMouseDown( x, y, mask ); | ||
3964 | } | ||
3965 | |||
3966 | void LLFolderView::onFocusLost( ) | ||
3967 | { | ||
3968 | if( gEditMenuHandler == this ) | ||
3969 | { | ||
3970 | gEditMenuHandler = NULL; | ||
3971 | } | ||
3972 | } | ||
3973 | |||
3974 | BOOL LLFolderView::search(LLFolderViewItem* first_item, const LLString &search_string, BOOL backward) | ||
3975 | { | ||
3976 | // get first selected item | ||
3977 | LLFolderViewItem* search_item = first_item; | ||
3978 | |||
3979 | // make sure search string is upper case | ||
3980 | LLString upper_case_string = search_string; | ||
3981 | LLString::toUpper(upper_case_string); | ||
3982 | |||
3983 | // if nothing selected, select first item in folder | ||
3984 | if (!first_item) | ||
3985 | { | ||
3986 | // start from first item | ||
3987 | first_item = getNextFromChild(NULL); | ||
3988 | } | ||
3989 | |||
3990 | // search over all open nodes for first substring match (with wrapping) | ||
3991 | BOOL found = FALSE; | ||
3992 | LLFolderViewItem* original_search_item = search_item; | ||
3993 | do | ||
3994 | { | ||
3995 | // wrap at end | ||
3996 | if (!search_item) | ||
3997 | { | ||
3998 | if (backward) | ||
3999 | { | ||
4000 | search_item = getPreviousFromChild(NULL); | ||
4001 | } | ||
4002 | else | ||
4003 | { | ||
4004 | search_item = getNextFromChild(NULL); | ||
4005 | } | ||
4006 | if (!search_item || search_item == original_search_item) | ||
4007 | { | ||
4008 | break; | ||
4009 | } | ||
4010 | } | ||
4011 | |||
4012 | const LLString current_item_label(search_item->getSearchableLabel()); | ||
4013 | S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); | ||
4014 | if (!current_item_label.compare(0, search_string_length, upper_case_string)) | ||
4015 | { | ||
4016 | found = TRUE; | ||
4017 | break; | ||
4018 | } | ||
4019 | if (backward) | ||
4020 | { | ||
4021 | search_item = search_item->getPreviousOpenNode(); | ||
4022 | } | ||
4023 | else | ||
4024 | { | ||
4025 | search_item = search_item->getNextOpenNode(); | ||
4026 | } | ||
4027 | |||
4028 | } while(search_item != original_search_item); | ||
4029 | |||
4030 | |||
4031 | if (found) | ||
4032 | { | ||
4033 | setSelection(search_item, FALSE, TRUE); | ||
4034 | scrollToShowSelection(); | ||
4035 | } | ||
4036 | |||
4037 | return found; | ||
4038 | } | ||
4039 | |||
4040 | BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask ) | ||
4041 | { | ||
4042 | if (!getVisible()) | ||
4043 | { | ||
4044 | return FALSE; | ||
4045 | } | ||
4046 | |||
4047 | return LLView::handleDoubleClick( x, y, mask ); | ||
4048 | } | ||
4049 | |||
4050 | BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) | ||
4051 | { | ||
4052 | // all user operations move keyboard focus to inventory | ||
4053 | // this way, we know when to stop auto-updating a search | ||
4054 | setFocus(TRUE); | ||
4055 | |||
4056 | BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL; | ||
4057 | S32 count = mSelectedItems.size(); | ||
4058 | LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle); | ||
4059 | if(handled && (count > 0) && menu) | ||
4060 | { | ||
4061 | //menu->empty(); | ||
4062 | const LLView::child_list_t *list = menu->getChildList(); | ||
4063 | |||
4064 | LLView::child_list_t::const_iterator menu_itor; | ||
4065 | for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor) | ||
4066 | { | ||
4067 | (*menu_itor)->setVisible(TRUE); | ||
4068 | (*menu_itor)->setEnabled(TRUE); | ||
4069 | } | ||
4070 | |||
4071 | // Successively filter out invalid options | ||
4072 | selected_items_t::iterator item_itor; | ||
4073 | U32 flags = FIRST_SELECTED_ITEM; | ||
4074 | for (item_itor = mSelectedItems.begin(); item_itor != mSelectedItems.end(); ++item_itor) | ||
4075 | { | ||
4076 | (*item_itor)->buildContextMenu(*menu, flags); | ||
4077 | flags = 0x0; | ||
4078 | } | ||
4079 | |||
4080 | menu->arrange(); | ||
4081 | menu->updateParent(gMenuHolder); | ||
4082 | LLMenuGL::showPopup(this, menu, x, y); | ||
4083 | } | ||
4084 | else | ||
4085 | { | ||
4086 | if(menu && menu->getVisible()) | ||
4087 | { | ||
4088 | menu->setVisible(FALSE); | ||
4089 | } | ||
4090 | setSelection(NULL, FALSE, TRUE); | ||
4091 | } | ||
4092 | return handled; | ||
4093 | } | ||
4094 | |||
4095 | BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask ) | ||
4096 | { | ||
4097 | return LLView::handleHover( x, y, mask ); | ||
4098 | } | ||
4099 | |||
4100 | BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, | ||
4101 | EDragAndDropType cargo_type, | ||
4102 | void* cargo_data, | ||
4103 | EAcceptance* accept, | ||
4104 | LLString& tooltip_msg) | ||
4105 | { | ||
4106 | mDragAndDropThisFrame = TRUE; | ||
4107 | BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, | ||
4108 | accept, tooltip_msg); | ||
4109 | |||
4110 | if (handled) | ||
4111 | { | ||
4112 | lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl; | ||
4113 | } | ||
4114 | |||
4115 | return handled; | ||
4116 | } | ||
4117 | |||
4118 | BOOL LLFolderView::handleScrollWheel(S32 x, S32 y, S32 clicks) | ||
4119 | { | ||
4120 | if (mScrollContainer) | ||
4121 | { | ||
4122 | return mScrollContainer->handleScrollWheel(x, y, clicks); | ||
4123 | } | ||
4124 | return FALSE; | ||
4125 | } | ||
4126 | |||
4127 | void LLFolderView::deleteAllChildren() | ||
4128 | { | ||
4129 | if(gViewerWindow->hasTopView(mRenamer)) | ||
4130 | { | ||
4131 | gViewerWindow->setTopView(NULL, NULL); | ||
4132 | } | ||
4133 | LLView::deleteViewByHandle(mPopupMenuHandle); | ||
4134 | mPopupMenuHandle = LLViewHandle::sDeadHandle; | ||
4135 | mRenamer = NULL; | ||
4136 | mRenameItem = NULL; | ||
4137 | clearSelection(); | ||
4138 | LLView::deleteAllChildren(); | ||
4139 | } | ||
4140 | |||
4141 | void LLFolderView::scrollToShowSelection() | ||
4142 | { | ||
4143 | if (mSelectedItems.size()) | ||
4144 | { | ||
4145 | mNeedsScroll = TRUE; | ||
4146 | } | ||
4147 | } | ||
4148 | |||
4149 | // If the parent is scroll containter, scroll it to make the selection | ||
4150 | // is maximally visible. | ||
4151 | void LLFolderView::scrollToShowItem(LLFolderViewItem* item) | ||
4152 | { | ||
4153 | // don't scroll to items when mouse is being used to scroll/drag and drop | ||
4154 | if (gFocusMgr.childHasMouseCapture(mScrollContainer)) | ||
4155 | { | ||
4156 | mNeedsScroll = FALSE; | ||
4157 | return; | ||
4158 | } | ||
4159 | if(item && mScrollContainer) | ||
4160 | { | ||
4161 | LLRect local_rect = item->getRect(); | ||
4162 | LLRect item_scrolled_rect; // item position relative to display area of scroller | ||
4163 | |||
4164 | S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); | ||
4165 | S32 label_height = llround(sFont->getLineHeight()); | ||
4166 | // when navigating with keyboard, only move top of folders on screen, otherwise show whole folder | ||
4167 | S32 max_height_to_show = gFocusMgr.childHasKeyboardFocus(this) ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); | ||
4168 | item->localPointToOtherView(item->getIndentation(), llmax(0, local_rect.getHeight() - max_height_to_show), &item_scrolled_rect.mLeft, &item_scrolled_rect.mBottom, mScrollContainer); | ||
4169 | item->localPointToOtherView(local_rect.getWidth(), local_rect.getHeight(), &item_scrolled_rect.mRight, &item_scrolled_rect.mTop, mScrollContainer); | ||
4170 | |||
4171 | item_scrolled_rect.mRight = llmin(item_scrolled_rect.mLeft + MIN_ITEM_WIDTH_VISIBLE, item_scrolled_rect.mRight); | ||
4172 | LLCoordGL scroll_offset(-mScrollContainer->getBorderWidth() - item_scrolled_rect.mLeft, | ||
4173 | mScrollContainer->getRect().getHeight() - item_scrolled_rect.mTop - 1); | ||
4174 | |||
4175 | S32 max_scroll_offset = getVisibleRect().getHeight() - item_scrolled_rect.getHeight(); | ||
4176 | if (item != mLastScrollItem || // if we're scrolling to focus on a new item | ||
4177 | // or the item has just appeared on screen and it wasn't onscreen before | ||
4178 | (scroll_offset.mY > 0 && scroll_offset.mY < max_scroll_offset && | ||
4179 | (mLastScrollOffset.mY < 0 || mLastScrollOffset.mY > max_scroll_offset))) | ||
4180 | { | ||
4181 | // we now have a position on screen that we want to keep stable | ||
4182 | // offset of selection relative to top of visible area | ||
4183 | mLastScrollOffset = scroll_offset; | ||
4184 | mLastScrollItem = item; | ||
4185 | } | ||
4186 | |||
4187 | mScrollContainer->scrollToShowRect( item_scrolled_rect, mLastScrollOffset ); | ||
4188 | |||
4189 | // after scrolling, store new offset | ||
4190 | // in case we don't have room to maintain the original position | ||
4191 | LLCoordGL new_item_left_top; | ||
4192 | item->localPointToOtherView(item->getIndentation(), item->getRect().getHeight(), &new_item_left_top.mX, &new_item_left_top.mY, mScrollContainer); | ||
4193 | mLastScrollOffset.set(-mScrollContainer->getBorderWidth() - new_item_left_top.mX, mScrollContainer->getRect().getHeight() - new_item_left_top.mY - 1); | ||
4194 | } | ||
4195 | } | ||
4196 | |||
4197 | LLRect LLFolderView::getVisibleRect() | ||
4198 | { | ||
4199 | S32 visible_height = mScrollContainer->getRect().getHeight(); | ||
4200 | S32 visible_width = mScrollContainer->getRect().getWidth(); | ||
4201 | LLRect visible_rect; | ||
4202 | visible_rect.setLeftTopAndSize(-mRect.mLeft, visible_height - mRect.mBottom, visible_width, visible_height); | ||
4203 | return visible_rect; | ||
4204 | } | ||
4205 | |||
4206 | BOOL LLFolderView::getShowSelectionContext() | ||
4207 | { | ||
4208 | if (mShowSelectionContext) | ||
4209 | { | ||
4210 | return TRUE; | ||
4211 | } | ||
4212 | LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle); | ||
4213 | if (menu && menu->getVisible()) | ||
4214 | { | ||
4215 | return TRUE; | ||
4216 | } | ||
4217 | return FALSE; | ||
4218 | } | ||
4219 | |||
4220 | void LLFolderView::setShowSingleSelection(BOOL show) | ||
4221 | { | ||
4222 | if (show != mShowSingleSelection) | ||
4223 | { | ||
4224 | mMultiSelectionFadeTimer.reset(); | ||
4225 | mShowSingleSelection = show; | ||
4226 | } | ||
4227 | } | ||
4228 | |||
4229 | void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp) | ||
4230 | { | ||
4231 | mItemMap[id] = itemp; | ||
4232 | } | ||
4233 | |||
4234 | void LLFolderView::removeItemID(const LLUUID& id) | ||
4235 | { | ||
4236 | mItemMap.erase(id); | ||
4237 | } | ||
4238 | |||
4239 | LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id) | ||
4240 | { | ||
4241 | if (id.isNull()) | ||
4242 | { | ||
4243 | return this; | ||
4244 | } | ||
4245 | |||
4246 | std::map<LLUUID, LLFolderViewItem*>::iterator map_it; | ||
4247 | map_it = mItemMap.find(id); | ||
4248 | if (map_it != mItemMap.end()) | ||
4249 | { | ||
4250 | return map_it->second; | ||
4251 | } | ||
4252 | |||
4253 | return NULL; | ||
4254 | } | ||
4255 | |||
4256 | //static | ||
4257 | void LLFolderView::idle(void* user_data) | ||
4258 | { | ||
4259 | LLFastTimer t2(LLFastTimer::FTM_INVENTORY); | ||
4260 | LLFolderView* self = (LLFolderView*)user_data; | ||
4261 | |||
4262 | BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters"); | ||
4263 | if (debug_filters != self->getDebugFilters()) | ||
4264 | { | ||
4265 | self->mDebugFilters = debug_filters; | ||
4266 | self->arrangeAll(); | ||
4267 | } | ||
4268 | |||
4269 | self->mFilter.clearModified(); | ||
4270 | BOOL filter_modified_and_active = self->mCompletedFilterGeneration < self->mFilter.getCurrentGeneration() && | ||
4271 | self->mFilter.isActive(); | ||
4272 | self->mNeedsAutoSelect = filter_modified_and_active && | ||
4273 | !(gFocusMgr.childHasKeyboardFocus(self) || gFocusMgr.getMouseCapture()); | ||
4274 | |||
4275 | // filter to determine visiblity before arranging | ||
4276 | self->filterFromRoot(); | ||
4277 | |||
4278 | self->sanitizeSelection(); | ||
4279 | |||
4280 | // automatically show matching items, and select first one | ||
4281 | // do this every frame until user puts keyboard focus into the inventory window | ||
4282 | // signaling the end of the automatic update | ||
4283 | // only do this when mNeedsFilter is set, meaning filtered items have | ||
4284 | // potentially changed | ||
4285 | if (self->mNeedsAutoSelect) | ||
4286 | { | ||
4287 | LLFastTimer t3(LLFastTimer::FTM_AUTO_SELECT); | ||
4288 | // select new item only if a filtered item not currently selected | ||
4289 | LLFolderViewItem* selected_itemp = self->mSelectedItems.empty() ? NULL : self->mSelectedItems.back(); | ||
4290 | if ((!selected_itemp || !selected_itemp->getFiltered()) && !self->mAutoSelectOverride) | ||
4291 | { | ||
4292 | // select first filtered item | ||
4293 | LLSelectFirstFilteredItem filter; | ||
4294 | self->applyFunctorRecursively(filter); | ||
4295 | } | ||
4296 | self->scrollToShowSelection(); | ||
4297 | } | ||
4298 | |||
4299 | if( self->needsArrange() && self->isInVisibleChain()) | ||
4300 | { | ||
4301 | self->arrangeFromRoot(); | ||
4302 | } | ||
4303 | |||
4304 | if (self->mSelectedItems.size() && self->mNeedsScroll) | ||
4305 | { | ||
4306 | self->scrollToShowItem(self->mSelectedItems.back()); | ||
4307 | // continue scrolling until animated layout change is done | ||
4308 | if (!self->needsArrange() || !self->isInVisibleChain()) | ||
4309 | { | ||
4310 | self->mNeedsScroll = FALSE; | ||
4311 | } | ||
4312 | } | ||
4313 | |||
4314 | if (self->mSelectionChanged && self->mSelectCallback) | ||
4315 | { | ||
4316 | //RN: we use keyboard focus as a proxy for user-explicit actions | ||
4317 | self->mSelectCallback(self->mSelectedItems, gFocusMgr.childHasKeyboardFocus(self), self->mUserData); | ||
4318 | } | ||
4319 | self->mSelectionChanged = FALSE; | ||
4320 | } | ||
4321 | |||
4322 | void LLFolderView::dumpSelectionInformation() | ||
4323 | { | ||
4324 | llinfos << "LLFolderView::dumpSelectionInformation()" << llendl; | ||
4325 | llinfos << "****************************************" << llendl; | ||
4326 | selected_items_t::iterator item_it; | ||
4327 | for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) | ||
4328 | { | ||
4329 | llinfos << " " << (*item_it)->getName() << llendl; | ||
4330 | } | ||
4331 | llinfos << "****************************************" << llendl; | ||
4332 | } | ||
4333 | |||
4334 | ///---------------------------------------------------------------------------- | ||
4335 | /// Local function definitions | ||
4336 | ///---------------------------------------------------------------------------- | ||
4337 | |||
4338 | bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b) | ||
4339 | { | ||
4340 | S32 compare = LLString::compareDict(a->getLabel(), b->getLabel()); | ||
4341 | if (0 == compare) | ||
4342 | { | ||
4343 | return (a->getCreationDate() > b->getCreationDate()); | ||
4344 | } | ||
4345 | else | ||
4346 | { | ||
4347 | return (compare < 0); | ||
4348 | } | ||
4349 | } | ||
4350 | |||
4351 | // BUG: This is very very slow. The getCreationDate() is log n in number | ||
4352 | // of inventory items. | ||
4353 | bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b) | ||
4354 | { | ||
4355 | U32 first_create = a->getCreationDate(); | ||
4356 | U32 second_create = b->getCreationDate(); | ||
4357 | if (first_create == second_create) | ||
4358 | { | ||
4359 | return (LLString::compareDict(a->getLabel(), b->getLabel()) < 0); | ||
4360 | } | ||
4361 | else | ||
4362 | { | ||
4363 | return (first_create > second_create); | ||
4364 | } | ||
4365 | } | ||
4366 | |||
4367 | void top_view_lost( LLView* view ) | ||
4368 | { | ||
4369 | if( view ) view->setVisible( FALSE ); | ||
4370 | } | ||
4371 | |||
4372 | void delete_selected_item(void* user_data) | ||
4373 | { | ||
4374 | if(user_data) | ||
4375 | { | ||
4376 | LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); | ||
4377 | fv->removeSelectedItems(); | ||
4378 | } | ||
4379 | } | ||
4380 | |||
4381 | void copy_selected_item(void* user_data) | ||
4382 | { | ||
4383 | if(user_data) | ||
4384 | { | ||
4385 | LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); | ||
4386 | fv->copy(); | ||
4387 | } | ||
4388 | } | ||
4389 | |||
4390 | void paste_items(void* user_data) | ||
4391 | { | ||
4392 | if(user_data) | ||
4393 | { | ||
4394 | LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); | ||
4395 | fv->paste(); | ||
4396 | } | ||
4397 | } | ||
4398 | |||
4399 | void open_selected_items(void* user_data) | ||
4400 | { | ||
4401 | if(user_data) | ||
4402 | { | ||
4403 | LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); | ||
4404 | fv->openSelectedItems(); | ||
4405 | } | ||
4406 | } | ||
4407 | |||
4408 | void properties_selected_items(void* user_data) | ||
4409 | { | ||
4410 | if(user_data) | ||
4411 | { | ||
4412 | LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); | ||
4413 | fv->propertiesSelectedItems(); | ||
4414 | } | ||
4415 | } | ||
4416 | |||
4417 | ///---------------------------------------------------------------------------- | ||
4418 | /// Class LLFolderViewEventListener | ||
4419 | ///---------------------------------------------------------------------------- | ||
4420 | |||
4421 | void LLFolderViewEventListener::arrangeAndSet(LLFolderViewItem* focus, | ||
4422 | BOOL set_selection, | ||
4423 | BOOL take_keyboard_focus) | ||
4424 | { | ||
4425 | if(!focus) return; | ||
4426 | LLFolderView* root = focus->getRoot(); | ||
4427 | focus->getParentFolder()->requestArrange(); | ||
4428 | if(set_selection) | ||
4429 | { | ||
4430 | focus->setSelectionFromRoot(focus, TRUE, take_keyboard_focus); | ||
4431 | if(root) | ||
4432 | { | ||
4433 | root->scrollToShowSelection(); | ||
4434 | } | ||
4435 | } | ||
4436 | } | ||
4437 | |||
4438 | |||
4439 | ///---------------------------------------------------------------------------- | ||
4440 | /// Class LLInventoryFilter | ||
4441 | ///---------------------------------------------------------------------------- | ||
4442 | LLInventoryFilter::LLInventoryFilter(const LLString& name) : | ||
4443 | mName(name), | ||
4444 | mModified(FALSE), | ||
4445 | mNeedTextRebuild(TRUE) | ||
4446 | { | ||
4447 | mFilterOps.mFilterTypes = 0xffffffff; | ||
4448 | mFilterOps.mMinDate = 0; | ||
4449 | mFilterOps.mMaxDate = U32_MAX; | ||
4450 | mFilterOps.mHoursAgo = 0; | ||
4451 | mFilterOps.mShowFolderState = SHOW_NON_EMPTY_FOLDERS; | ||
4452 | mFilterOps.mPermissions = PERM_NONE; | ||
4453 | |||
4454 | mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately | ||
4455 | |||
4456 | mSubStringMatchOffset = 0; | ||
4457 | mFilterSubString = ""; | ||
4458 | mFilterGeneration = 0; | ||
4459 | mMustPassGeneration = S32_MAX; | ||
4460 | mMinRequiredGeneration = 0; | ||
4461 | mNextFilterGeneration = mFilterGeneration + 1; | ||
4462 | |||
4463 | mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff"); | ||
4464 | } | ||
4465 | |||
4466 | LLInventoryFilter::~LLInventoryFilter() | ||
4467 | { | ||
4468 | } | ||
4469 | |||
4470 | BOOL LLInventoryFilter::check(LLFolderViewItem* item) | ||
4471 | { | ||
4472 | U32 earliest; | ||
4473 | |||
4474 | earliest = time_corrected() - mFilterOps.mHoursAgo * 3600; | ||
4475 | if (mFilterOps.mMinDate && mFilterOps.mMinDate < earliest) | ||
4476 | { | ||
4477 | earliest = mFilterOps.mMinDate; | ||
4478 | } | ||
4479 | else if (!mFilterOps.mHoursAgo) | ||
4480 | { | ||
4481 | earliest = 0; | ||
4482 | } | ||
4483 | LLFolderViewEventListener* listener = item->getListener(); | ||
4484 | mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : LLString::npos; | ||
4485 | BOOL passed = (0x1 << listener->getInventoryType() & mFilterOps.mFilterTypes || listener->getInventoryType() == LLInventoryType::IT_NONE) | ||
4486 | && (mFilterSubString.size() == 0 || mSubStringMatchOffset != LLString::npos) | ||
4487 | && ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions) | ||
4488 | && (listener->getCreationDate() >= earliest && listener->getCreationDate() <= mFilterOps.mMaxDate); | ||
4489 | return passed; | ||
4490 | } | ||
4491 | |||
4492 | const LLString LLInventoryFilter::getFilterSubString(BOOL trim) | ||
4493 | { | ||
4494 | return mFilterSubString; | ||
4495 | } | ||
4496 | |||
4497 | std::string::size_type LLInventoryFilter::getStringMatchOffset() const | ||
4498 | { | ||
4499 | return mSubStringMatchOffset; | ||
4500 | } | ||
4501 | |||
4502 | BOOL LLInventoryFilter::isActive() | ||
4503 | { | ||
4504 | return mFilterOps.mFilterTypes != 0xffffffff | ||
4505 | || mFilterSubString.size() | ||
4506 | || mFilterOps.mPermissions != PERM_NONE | ||
4507 | || mFilterOps.mMinDate != 0 | ||
4508 | || mFilterOps.mMaxDate != U32_MAX | ||
4509 | || mFilterOps.mHoursAgo != 0; | ||
4510 | } | ||
4511 | |||
4512 | BOOL LLInventoryFilter::isModified() | ||
4513 | { | ||
4514 | return mModified; | ||
4515 | } | ||
4516 | |||
4517 | BOOL LLInventoryFilter::isModifiedAndClear() | ||
4518 | { | ||
4519 | BOOL ret = mModified; | ||
4520 | mModified = FALSE; | ||
4521 | return ret; | ||
4522 | } | ||
4523 | |||
4524 | void LLInventoryFilter::setFilterTypes(U32 types) | ||
4525 | { | ||
4526 | if (mFilterOps.mFilterTypes != types) | ||
4527 | { | ||
4528 | // keep current items only if no type bits getting turned off | ||
4529 | BOOL fewer_bits_set = (mFilterOps.mFilterTypes & ~types); | ||
4530 | BOOL more_bits_set = (~mFilterOps.mFilterTypes & types); | ||
4531 | |||
4532 | mFilterOps.mFilterTypes = types; | ||
4533 | if (more_bits_set && fewer_bits_set) | ||
4534 | { | ||
4535 | // neither less or more restrive, both simultaneously | ||
4536 | // so we need to filter from scratch | ||
4537 | setModified(FILTER_RESTART); | ||
4538 | } | ||
4539 | else if (more_bits_set) | ||
4540 | { | ||
4541 | // target is only one of all requested types so more type bits == less restrictive | ||
4542 | setModified(FILTER_LESS_RESTRICTIVE); | ||
4543 | } | ||
4544 | else if (fewer_bits_set) | ||
4545 | { | ||
4546 | setModified(FILTER_MORE_RESTRICTIVE); | ||
4547 | } | ||
4548 | |||
4549 | } | ||
4550 | } | ||
4551 | |||
4552 | void LLInventoryFilter::setFilterSubString(const LLString& string) | ||
4553 | { | ||
4554 | if (mFilterSubString != string) | ||
4555 | { | ||
4556 | // hitting BACKSPACE, for example | ||
4557 | BOOL less_restrictive = mFilterSubString.size() >= string.size() && !mFilterSubString.substr(0, string.size()).compare(string); | ||
4558 | // appending new characters | ||
4559 | BOOL more_restrictive = mFilterSubString.size() < string.size() && !string.substr(0, mFilterSubString.size()).compare(mFilterSubString); | ||
4560 | mFilterSubString = string; | ||
4561 | LLString::toUpper(mFilterSubString); | ||
4562 | LLString::trimHead(mFilterSubString); | ||
4563 | |||
4564 | if (less_restrictive) | ||
4565 | { | ||
4566 | setModified(FILTER_LESS_RESTRICTIVE); | ||
4567 | } | ||
4568 | else if (more_restrictive) | ||
4569 | { | ||
4570 | setModified(FILTER_MORE_RESTRICTIVE); | ||
4571 | } | ||
4572 | else | ||
4573 | { | ||
4574 | setModified(FILTER_RESTART); | ||
4575 | } | ||
4576 | } | ||
4577 | } | ||
4578 | |||
4579 | void LLInventoryFilter::setFilterPermissions(PermissionMask perms) | ||
4580 | { | ||
4581 | if (mFilterOps.mPermissions != perms) | ||
4582 | { | ||
4583 | // keep current items only if no perm bits getting turned off | ||
4584 | BOOL fewer_bits_set = (mFilterOps.mPermissions & ~perms); | ||
4585 | BOOL more_bits_set = (~mFilterOps.mPermissions & perms); | ||
4586 | mFilterOps.mPermissions = perms; | ||
4587 | |||
4588 | if (more_bits_set && fewer_bits_set) | ||
4589 | { | ||
4590 | setModified(FILTER_RESTART); | ||
4591 | } | ||
4592 | else if (more_bits_set) | ||
4593 | { | ||
4594 | // target must have all requested permission bits, so more bits == more restrictive | ||
4595 | setModified(FILTER_MORE_RESTRICTIVE); | ||
4596 | } | ||
4597 | else if (fewer_bits_set) | ||
4598 | { | ||
4599 | setModified(FILTER_LESS_RESTRICTIVE); | ||
4600 | } | ||
4601 | } | ||
4602 | } | ||
4603 | |||
4604 | void LLInventoryFilter::setDateRange(U32 min_date, U32 max_date) | ||
4605 | { | ||
4606 | mFilterOps.mHoursAgo = 0; | ||
4607 | if (mFilterOps.mMinDate != min_date) | ||
4608 | { | ||
4609 | mFilterOps.mMinDate = min_date; | ||
4610 | setModified(); | ||
4611 | } | ||
4612 | if (mFilterOps.mMaxDate != llmax(mFilterOps.mMinDate, max_date)) | ||
4613 | { | ||
4614 | mFilterOps.mMaxDate = llmax(mFilterOps.mMinDate, max_date); | ||
4615 | setModified(); | ||
4616 | } | ||
4617 | } | ||
4618 | |||
4619 | void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) | ||
4620 | { | ||
4621 | if (sl && !isSinceLogoff()) | ||
4622 | { | ||
4623 | setDateRange(mLastLogoff, U32_MAX); | ||
4624 | setModified(); | ||
4625 | } | ||
4626 | if (!sl && isSinceLogoff()) | ||
4627 | { | ||
4628 | setDateRange(0, U32_MAX); | ||
4629 | setModified(); | ||
4630 | } | ||
4631 | } | ||
4632 | |||
4633 | BOOL LLInventoryFilter::isSinceLogoff() | ||
4634 | { | ||
4635 | return mFilterOps.mMinDate == mLastLogoff && mFilterOps.mMaxDate == U32_MAX; | ||
4636 | } | ||
4637 | |||
4638 | void LLInventoryFilter::setHoursAgo(U32 hours) | ||
4639 | { | ||
4640 | if (mFilterOps.mHoursAgo != hours) | ||
4641 | { | ||
4642 | // *NOTE: need to cache last filter time, in case filter goes stale | ||
4643 | BOOL less_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours > mFilterOps.mHoursAgo); | ||
4644 | BOOL more_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours <= mFilterOps.mHoursAgo); | ||
4645 | mFilterOps.mHoursAgo = hours; | ||
4646 | mFilterOps.mMinDate = 0; | ||
4647 | mFilterOps.mMaxDate = U32_MAX; | ||
4648 | if (less_restrictive) | ||
4649 | { | ||
4650 | setModified(FILTER_LESS_RESTRICTIVE); | ||
4651 | } | ||
4652 | else if (more_restrictive) | ||
4653 | { | ||
4654 | setModified(FILTER_MORE_RESTRICTIVE); | ||
4655 | } | ||
4656 | else | ||
4657 | { | ||
4658 | setModified(FILTER_RESTART); | ||
4659 | } | ||
4660 | } | ||
4661 | } | ||
4662 | void LLInventoryFilter::setShowFolderState(EFolderShow state) | ||
4663 | { | ||
4664 | if (mFilterOps.mShowFolderState != state) | ||
4665 | { | ||
4666 | mFilterOps.mShowFolderState = state; | ||
4667 | if (state == SHOW_NON_EMPTY_FOLDERS) | ||
4668 | { | ||
4669 | // showing fewer folders than before | ||
4670 | setModified(FILTER_MORE_RESTRICTIVE); | ||
4671 | } | ||
4672 | else if (state == SHOW_ALL_FOLDERS) | ||
4673 | { | ||
4674 | // showing same folders as before and then some | ||
4675 | setModified(FILTER_LESS_RESTRICTIVE); | ||
4676 | } | ||
4677 | else | ||
4678 | { | ||
4679 | setModified(); | ||
4680 | } | ||
4681 | } | ||
4682 | } | ||
4683 | |||
4684 | void LLInventoryFilter::setSortOrder(U32 order) | ||
4685 | { | ||
4686 | if (mOrder != order) | ||
4687 | { | ||
4688 | mOrder = order; | ||
4689 | setModified(); | ||
4690 | } | ||
4691 | } | ||
4692 | |||
4693 | void LLInventoryFilter::markDefault() | ||
4694 | { | ||
4695 | mDefaultFilterOps = mFilterOps; | ||
4696 | } | ||
4697 | |||
4698 | void LLInventoryFilter::resetDefault() | ||
4699 | { | ||
4700 | mFilterOps = mDefaultFilterOps; | ||
4701 | setModified(); | ||
4702 | } | ||
4703 | |||
4704 | void LLInventoryFilter::setModified(EFilterBehavior behavior) | ||
4705 | { | ||
4706 | mModified = TRUE; | ||
4707 | mNeedTextRebuild = TRUE; | ||
4708 | mFilterGeneration = mNextFilterGeneration++; | ||
4709 | |||
4710 | if (mFilterBehavior == FILTER_NONE) | ||
4711 | { | ||
4712 | mFilterBehavior = behavior; | ||
4713 | } | ||
4714 | else if (mFilterBehavior != behavior) | ||
4715 | { | ||
4716 | // trying to do both less restrictive and more restrictive filter | ||
4717 | // basically means restart from scratch | ||
4718 | mFilterBehavior = FILTER_RESTART; | ||
4719 | } | ||
4720 | |||
4721 | if (isActive()) | ||
4722 | { | ||
4723 | // if not keeping current filter results, update last valid as well | ||
4724 | switch(mFilterBehavior) | ||
4725 | { | ||
4726 | case FILTER_RESTART: | ||
4727 | mMustPassGeneration = mFilterGeneration; | ||
4728 | mMinRequiredGeneration = mFilterGeneration; | ||
4729 | break; | ||
4730 | case FILTER_LESS_RESTRICTIVE: | ||
4731 | mMustPassGeneration = mFilterGeneration; | ||
4732 | break; | ||
4733 | case FILTER_MORE_RESTRICTIVE: | ||
4734 | mMinRequiredGeneration = mFilterGeneration; | ||
4735 | // must have passed either current filter generation (meaningless, as it hasn't been run yet) | ||
4736 | // or some older generation, so keep the value | ||
4737 | mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration); | ||
4738 | break; | ||
4739 | default: | ||
4740 | llerrs << "Bad filter behavior specified" << llendl; | ||
4741 | } | ||
4742 | } | ||
4743 | else | ||
4744 | { | ||
4745 | // shortcut disabled filters to show everything immediately | ||
4746 | mMinRequiredGeneration = 0; | ||
4747 | mMustPassGeneration = S32_MAX; | ||
4748 | } | ||
4749 | } | ||
4750 | |||
4751 | BOOL LLInventoryFilter::isFilterWith(LLInventoryType::EType t) | ||
4752 | { | ||
4753 | return mFilterOps.mFilterTypes & (0x01 << t); | ||
4754 | } | ||
4755 | |||
4756 | LLString LLInventoryFilter::getFilterText() | ||
4757 | { | ||
4758 | if (!mNeedTextRebuild) | ||
4759 | { | ||
4760 | return mFilterText; | ||
4761 | } | ||
4762 | |||
4763 | mNeedTextRebuild = FALSE; | ||
4764 | LLString filtered_types; | ||
4765 | LLString not_filtered_types; | ||
4766 | BOOL filtered_by_type = FALSE; | ||
4767 | BOOL filtered_by_all_types = TRUE; | ||
4768 | S32 num_filter_types = 0; | ||
4769 | mFilterText = ""; | ||
4770 | |||
4771 | if (isFilterWith(LLInventoryType::IT_ANIMATION)) | ||
4772 | { | ||
4773 | filtered_types += " Animations,"; | ||
4774 | filtered_by_type = TRUE; | ||
4775 | num_filter_types++; | ||
4776 | } | ||
4777 | else | ||
4778 | { | ||
4779 | not_filtered_types += " Animations,"; | ||
4780 | filtered_by_all_types = FALSE; | ||
4781 | } | ||
4782 | |||
4783 | if (isFilterWith(LLInventoryType::IT_CALLINGCARD)) | ||
4784 | { | ||
4785 | filtered_types += " Calling Cards,"; | ||
4786 | filtered_by_type = TRUE; | ||
4787 | num_filter_types++; | ||
4788 | } | ||
4789 | else | ||
4790 | { | ||
4791 | not_filtered_types += " Calling Cards,"; | ||
4792 | filtered_by_all_types = FALSE; | ||
4793 | } | ||
4794 | |||
4795 | if (isFilterWith(LLInventoryType::IT_WEARABLE)) | ||
4796 | { | ||
4797 | filtered_types += " Clothing,"; | ||
4798 | filtered_by_type = TRUE; | ||
4799 | num_filter_types++; | ||
4800 | } | ||
4801 | else | ||
4802 | { | ||
4803 | not_filtered_types += " Clothing,"; | ||
4804 | filtered_by_all_types = FALSE; | ||
4805 | } | ||
4806 | |||
4807 | if (isFilterWith(LLInventoryType::IT_GESTURE)) | ||
4808 | { | ||
4809 | filtered_types += " Gestures,"; | ||
4810 | filtered_by_type = TRUE; | ||
4811 | num_filter_types++; | ||
4812 | } | ||
4813 | else | ||
4814 | { | ||
4815 | not_filtered_types += " Gestures,"; | ||
4816 | filtered_by_all_types = FALSE; | ||
4817 | } | ||
4818 | |||
4819 | if (isFilterWith(LLInventoryType::IT_LANDMARK)) | ||
4820 | { | ||
4821 | filtered_types += " Landmarks,"; | ||
4822 | filtered_by_type = TRUE; | ||
4823 | num_filter_types++; | ||
4824 | } | ||
4825 | else | ||
4826 | { | ||
4827 | not_filtered_types += " Landmarks,"; | ||
4828 | filtered_by_all_types = FALSE; | ||
4829 | } | ||
4830 | |||
4831 | if (isFilterWith(LLInventoryType::IT_NOTECARD)) | ||
4832 | { | ||
4833 | filtered_types += " Notecards,"; | ||
4834 | filtered_by_type = TRUE; | ||
4835 | num_filter_types++; | ||
4836 | } | ||
4837 | else | ||
4838 | { | ||
4839 | not_filtered_types += " Notecards,"; | ||
4840 | filtered_by_all_types = FALSE; | ||
4841 | } | ||
4842 | |||
4843 | if (isFilterWith(LLInventoryType::IT_OBJECT) && isFilterWith(LLInventoryType::IT_ATTACHMENT)) | ||
4844 | { | ||
4845 | filtered_types += " Objects,"; | ||
4846 | filtered_by_type = TRUE; | ||
4847 | num_filter_types++; | ||
4848 | } | ||
4849 | else | ||
4850 | { | ||
4851 | not_filtered_types += " Objects,"; | ||
4852 | filtered_by_all_types = FALSE; | ||
4853 | } | ||
4854 | |||
4855 | if (isFilterWith(LLInventoryType::IT_LSL)) | ||
4856 | { | ||
4857 | filtered_types += " Scripts,"; | ||
4858 | filtered_by_type = TRUE; | ||
4859 | num_filter_types++; | ||
4860 | } | ||
4861 | else | ||
4862 | { | ||
4863 | not_filtered_types += " Scripts,"; | ||
4864 | filtered_by_all_types = FALSE; | ||
4865 | } | ||
4866 | |||
4867 | if (isFilterWith(LLInventoryType::IT_SOUND)) | ||
4868 | { | ||
4869 | filtered_types += " Sounds,"; | ||
4870 | filtered_by_type = TRUE; | ||
4871 | num_filter_types++; | ||
4872 | } | ||
4873 | else | ||
4874 | { | ||
4875 | not_filtered_types += " Sounds,"; | ||
4876 | filtered_by_all_types = FALSE; | ||
4877 | } | ||
4878 | |||
4879 | if (isFilterWith(LLInventoryType::IT_TEXTURE)) | ||
4880 | { | ||
4881 | filtered_types += " Textures,"; | ||
4882 | filtered_by_type = TRUE; | ||
4883 | num_filter_types++; | ||
4884 | } | ||
4885 | else | ||
4886 | { | ||
4887 | not_filtered_types += " Textures,"; | ||
4888 | filtered_by_all_types = FALSE; | ||
4889 | } | ||
4890 | |||
4891 | if (isFilterWith(LLInventoryType::IT_SNAPSHOT)) | ||
4892 | { | ||
4893 | filtered_types += " Snapshots,"; | ||
4894 | filtered_by_type = TRUE; | ||
4895 | num_filter_types++; | ||
4896 | } | ||
4897 | else | ||
4898 | { | ||
4899 | not_filtered_types += " Snapshots,"; | ||
4900 | filtered_by_all_types = FALSE; | ||
4901 | } | ||
4902 | |||
4903 | if (!gInventory.backgroundFetchActive() && filtered_by_type && !filtered_by_all_types) | ||
4904 | { | ||
4905 | mFilterText += " - "; | ||
4906 | if (num_filter_types < 5) | ||
4907 | { | ||
4908 | mFilterText += filtered_types; | ||
4909 | } | ||
4910 | else | ||
4911 | { | ||
4912 | mFilterText += "No "; | ||
4913 | mFilterText += not_filtered_types; | ||
4914 | } | ||
4915 | // remove the ',' at the end | ||
4916 | mFilterText.erase(mFilterText.size() - 1, 1); | ||
4917 | } | ||
4918 | |||
4919 | if (isSinceLogoff()) | ||
4920 | { | ||
4921 | mFilterText += " - Since Logoff"; | ||
4922 | } | ||
4923 | return mFilterText; | ||
4924 | } | ||