PageRenderTime 56ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/newview/llfolderview.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2514 lines | 1981 code | 324 blank | 209 comment | 438 complexity | 95cc07bce5ed868c6b0471bfadbacf43 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llfolderview.cpp
  3. * @brief Implementation of the folder view collection of classes.
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llfolderview.h"
  28. #include "llcallbacklist.h"
  29. #include "llinventorybridge.h"
  30. #include "llinventoryclipboard.h" // *TODO: remove this once hack below gone.
  31. #include "llinventoryfilter.h"
  32. #include "llinventoryfunctions.h"
  33. #include "llinventorymodelbackgroundfetch.h"
  34. #include "llinventorypanel.h"
  35. #include "llfoldertype.h"
  36. #include "llfloaterinventory.h"// hacked in for the bonus context menu items.
  37. #include "llkeyboard.h"
  38. #include "lllineeditor.h"
  39. #include "llmenugl.h"
  40. #include "llpanel.h"
  41. #include "llpreview.h"
  42. #include "llscrollcontainer.h" // hack to allow scrolling
  43. #include "lltooldraganddrop.h"
  44. #include "lltrans.h"
  45. #include "llui.h"
  46. #include "llviewertexture.h"
  47. #include "llviewertexturelist.h"
  48. #include "llviewerjointattachment.h"
  49. #include "llviewermenu.h"
  50. #include "lluictrlfactory.h"
  51. #include "llviewercontrol.h"
  52. #include "llviewerfoldertype.h"
  53. #include "llviewerwindow.h"
  54. #include "llvoavatar.h"
  55. #include "llfloaterproperties.h"
  56. #include "llnotificationsutil.h"
  57. // Linden library includes
  58. #include "lldbstrings.h"
  59. #include "llfocusmgr.h"
  60. #include "llfontgl.h"
  61. #include "llgl.h"
  62. #include "llrender.h"
  63. #include "llinventory.h"
  64. // Third-party library includes
  65. #include <algorithm>
  66. ///----------------------------------------------------------------------------
  67. /// Local function declarations, constants, enums, and typedefs
  68. ///----------------------------------------------------------------------------
  69. const S32 RENAME_WIDTH_PAD = 4;
  70. const S32 RENAME_HEIGHT_PAD = 1;
  71. const S32 AUTO_OPEN_STACK_DEPTH = 16;
  72. const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH
  73. + LLFolderViewItem::ICON_PAD
  74. + LLFolderViewItem::ARROW_SIZE
  75. + LLFolderViewItem::TEXT_PAD
  76. + /*first few characters*/ 40;
  77. const S32 MINIMUM_RENAMER_WIDTH = 80;
  78. // *TODO: move in params in xml if necessary. Requires modification of LLFolderView & LLInventoryPanel Params.
  79. const S32 STATUS_TEXT_HPAD = 6;
  80. const S32 STATUS_TEXT_VPAD = 8;
  81. enum {
  82. SIGNAL_NO_KEYBOARD_FOCUS = 1,
  83. SIGNAL_KEYBOARD_FOCUS = 2
  84. };
  85. F32 LLFolderView::sAutoOpenTime = 1.f;
  86. void delete_selected_item(void* user_data);
  87. void copy_selected_item(void* user_data);
  88. void open_selected_items(void* user_data);
  89. void properties_selected_items(void* user_data);
  90. void paste_items(void* user_data);
  91. //---------------------------------------------------------------------------
  92. // Tells all folders in a folderview to sort their items
  93. // (and only their items, not folders) by a certain function.
  94. class LLSetItemSortFunction : public LLFolderViewFunctor
  95. {
  96. public:
  97. LLSetItemSortFunction(U32 ordering)
  98. : mSortOrder(ordering) {}
  99. virtual ~LLSetItemSortFunction() {}
  100. virtual void doFolder(LLFolderViewFolder* folder);
  101. virtual void doItem(LLFolderViewItem* item);
  102. U32 mSortOrder;
  103. };
  104. // Set the sort order.
  105. void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder)
  106. {
  107. folder->setItemSortOrder(mSortOrder);
  108. }
  109. // Do nothing.
  110. void LLSetItemSortFunction::doItem(LLFolderViewItem* item)
  111. {
  112. return;
  113. }
  114. //---------------------------------------------------------------------------
  115. // Tells all folders in a folderview to close themselves
  116. // For efficiency, calls setOpenArrangeRecursively().
  117. // The calling function must then call:
  118. // LLFolderView* root = getRoot();
  119. // if( root )
  120. // {
  121. // root->arrange( NULL, NULL );
  122. // root->scrollToShowSelection();
  123. // }
  124. // to patch things up.
  125. class LLCloseAllFoldersFunctor : public LLFolderViewFunctor
  126. {
  127. public:
  128. LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; }
  129. virtual ~LLCloseAllFoldersFunctor() {}
  130. virtual void doFolder(LLFolderViewFolder* folder);
  131. virtual void doItem(LLFolderViewItem* item);
  132. BOOL mOpen;
  133. };
  134. // Set the sort order.
  135. void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder)
  136. {
  137. folder->setOpenArrangeRecursively(mOpen);
  138. }
  139. // Do nothing.
  140. void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item)
  141. { }
  142. ///----------------------------------------------------------------------------
  143. /// Class LLFolderView
  144. ///----------------------------------------------------------------------------
  145. LLFolderView::Params::Params()
  146. : task_id("task_id"),
  147. title("title"),
  148. use_label_suffix("use_label_suffix"),
  149. allow_multiselect("allow_multiselect", true),
  150. show_empty_message("show_empty_message", true),
  151. show_load_status("show_load_status", true),
  152. use_ellipses("use_ellipses", false)
  153. {
  154. }
  155. // Default constructor
  156. LLFolderView::LLFolderView(const Params& p)
  157. : LLFolderViewFolder(p),
  158. mRunningHeight(0),
  159. mScrollContainer( NULL ),
  160. mPopupMenuHandle(),
  161. mAllowMultiSelect(p.allow_multiselect),
  162. mShowEmptyMessage(p.show_empty_message),
  163. mShowFolderHierarchy(FALSE),
  164. mSourceID(p.task_id),
  165. mRenameItem( NULL ),
  166. mNeedsScroll( FALSE ),
  167. mUseLabelSuffix(p.use_label_suffix),
  168. mPinningSelectedItem(FALSE),
  169. mNeedsAutoSelect( FALSE ),
  170. mAutoSelectOverride(FALSE),
  171. mNeedsAutoRename(FALSE),
  172. mDebugFilters(FALSE),
  173. mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately
  174. mFilter( new LLInventoryFilter(p.title) ),
  175. mShowSelectionContext(FALSE),
  176. mShowSingleSelection(FALSE),
  177. mArrangeGeneration(0),
  178. mSignalSelectCallback(0),
  179. mMinWidth(0),
  180. mDragAndDropThisFrame(FALSE),
  181. mCallbackRegistrar(NULL),
  182. mParentPanel(p.parent_panel),
  183. mUseEllipses(p.use_ellipses),
  184. mDraggingOverItem(NULL),
  185. mStatusTextBox(NULL)
  186. {
  187. mRoot = this;
  188. mShowLoadStatus = p.show_load_status();
  189. LLRect rect = p.rect;
  190. LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom);
  191. setRect( rect );
  192. reshape(rect.getWidth(), rect.getHeight());
  193. mIsOpen = TRUE; // this view is always open.
  194. mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH);
  195. mAutoOpenCandidate = NULL;
  196. mAutoOpenTimer.stop();
  197. mKeyboardSelection = FALSE;
  198. const LLFolderViewItem::Params& item_params =
  199. LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();
  200. S32 indentation = item_params.folder_indentation();
  201. mIndentation = -indentation; // children start at indentation 0
  202. gIdleCallbacks.addFunction(idle, this);
  203. //clear label
  204. // go ahead and render root folder as usual
  205. // just make sure the label ("Inventory Folder") never shows up
  206. mLabel = LLStringUtil::null;
  207. //mRenamer->setWriteableBgColor(LLColor4::white);
  208. // Escape is handled by reverting the rename, not commiting it (default behavior)
  209. LLLineEditor::Params params;
  210. params.name("ren");
  211. params.rect(rect);
  212. params.font(getLabelFontForStyle(LLFontGL::NORMAL));
  213. params.max_length.bytes(DB_INV_ITEM_NAME_STR_LEN);
  214. params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2));
  215. params.prevalidate_callback(&LLTextValidate::validateASCIIPrintableNoPipe);
  216. params.commit_on_focus_lost(true);
  217. params.visible(false);
  218. mRenamer = LLUICtrlFactory::create<LLLineEditor> (params);
  219. addChild(mRenamer);
  220. // Textbox
  221. LLTextBox::Params text_p;
  222. LLFontGL* font = getLabelFontForStyle(mLabelStyle);
  223. LLRect new_r = LLRect(rect.mLeft + ICON_PAD,
  224. rect.mTop - TEXT_PAD,
  225. rect.mRight,
  226. rect.mTop - TEXT_PAD - llfloor(font->getLineHeight()));
  227. text_p.rect(new_r);
  228. text_p.name(std::string(p.name));
  229. text_p.font(font);
  230. text_p.visible(false);
  231. text_p.parse_urls(true);
  232. text_p.wrap(true); // allow multiline text. See EXT-7564, EXT-7047
  233. // set text padding the same as in People panel. EXT-7047, EXT-4837
  234. text_p.h_pad(STATUS_TEXT_HPAD);
  235. text_p.v_pad(STATUS_TEXT_VPAD);
  236. mStatusTextBox = LLUICtrlFactory::create<LLTextBox> (text_p);
  237. mStatusTextBox->setFollowsLeft();
  238. mStatusTextBox->setFollowsTop();
  239. //addChild(mStatusTextBox);
  240. // make the popup menu available
  241. LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
  242. if (!menu)
  243. {
  244. menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu");
  245. }
  246. menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
  247. mPopupMenuHandle = menu->getHandle();
  248. mListener->openItem();
  249. }
  250. // Destroys the object
  251. LLFolderView::~LLFolderView( void )
  252. {
  253. closeRenamer();
  254. // The release focus call can potentially call the
  255. // scrollcontainer, which can potentially be called with a partly
  256. // destroyed scollcontainer. Just null it out here, and no worries
  257. // about calling into the invalid scroll container.
  258. // Same with the renamer.
  259. mScrollContainer = NULL;
  260. mRenameItem = NULL;
  261. mRenamer = NULL;
  262. mStatusTextBox = NULL;
  263. mAutoOpenItems.removeAllNodes();
  264. gIdleCallbacks.deleteFunction(idle, this);
  265. if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
  266. mAutoOpenItems.removeAllNodes();
  267. clearSelection();
  268. mItems.clear();
  269. mFolders.clear();
  270. mItemMap.clear();
  271. delete mFilter;
  272. mFilter = NULL;
  273. }
  274. BOOL LLFolderView::canFocusChildren() const
  275. {
  276. return FALSE;
  277. }
  278. static LLFastTimer::DeclareTimer FTM_SORT("Sort Inventory");
  279. void LLFolderView::setSortOrder(U32 order)
  280. {
  281. if (order != mSortOrder)
  282. {
  283. LLFastTimer t(FTM_SORT);
  284. mSortOrder = order;
  285. sortBy(order);
  286. arrangeAll();
  287. }
  288. }
  289. U32 LLFolderView::getSortOrder() const
  290. {
  291. return mSortOrder;
  292. }
  293. BOOL LLFolderView::addFolder( LLFolderViewFolder* folder)
  294. {
  295. // enforce sort order of My Inventory followed by Library
  296. if (folder->getListener()->getUUID() == gInventory.getLibraryRootFolderID())
  297. {
  298. mFolders.push_back(folder);
  299. }
  300. else
  301. {
  302. mFolders.insert(mFolders.begin(), folder);
  303. }
  304. folder->setShowLoadStatus(mShowLoadStatus);
  305. folder->setOrigin(0, 0);
  306. folder->reshape(getRect().getWidth(), 0);
  307. folder->setVisible(FALSE);
  308. addChild( folder );
  309. folder->dirtyFilter();
  310. folder->requestArrange();
  311. return TRUE;
  312. }
  313. void LLFolderView::closeAllFolders()
  314. {
  315. // Close all the folders
  316. setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN);
  317. arrangeAll();
  318. }
  319. void LLFolderView::openTopLevelFolders()
  320. {
  321. for (folders_t::iterator iter = mFolders.begin();
  322. iter != mFolders.end();)
  323. {
  324. folders_t::iterator fit = iter++;
  325. (*fit)->setOpen(TRUE);
  326. }
  327. }
  328. void LLFolderView::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse)
  329. {
  330. // call base class to do proper recursion
  331. LLFolderViewFolder::setOpenArrangeRecursively(openitem, recurse);
  332. // make sure root folder is always open
  333. mIsOpen = TRUE;
  334. }
  335. static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange");
  336. // This view grows and shinks to enclose all of its children items and folders.
  337. S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation )
  338. {
  339. if (getListener()->getUUID().notNull())
  340. {
  341. if (mNeedsSort)
  342. {
  343. mFolders.sort(mSortFunction);
  344. mItems.sort(mSortFunction);
  345. mNeedsSort = false;
  346. }
  347. }
  348. LLFastTimer t2(FTM_ARRANGE);
  349. filter_generation = mFilter->getMinRequiredGeneration();
  350. mMinWidth = 0;
  351. mHasVisibleChildren = hasFilteredDescendants(filter_generation);
  352. // arrange always finishes, so optimistically set the arrange generation to the most current
  353. mLastArrangeGeneration = getRoot()->getArrangeGeneration();
  354. LLInventoryFilter::EFolderShow show_folder_state =
  355. getRoot()->getFilter()->getShowFolderState();
  356. S32 total_width = LEFT_PAD;
  357. S32 running_height = mDebugFilters ? llceil(LLFontGL::getFontMonospace()->getLineHeight()) : 0;
  358. S32 target_height = running_height;
  359. S32 parent_item_height = getRect().getHeight();
  360. for (folders_t::iterator iter = mFolders.begin();
  361. iter != mFolders.end();)
  362. {
  363. folders_t::iterator fit = iter++;
  364. LLFolderViewFolder* folderp = (*fit);
  365. if (getDebugFilters())
  366. {
  367. folderp->setVisible(TRUE);
  368. }
  369. else
  370. {
  371. folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
  372. (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
  373. }
  374. if (folderp->getVisible())
  375. {
  376. S32 child_height = 0;
  377. S32 child_width = 0;
  378. S32 child_top = parent_item_height - running_height;
  379. target_height += folderp->arrange( &child_width, &child_height, filter_generation );
  380. mMinWidth = llmax(mMinWidth, child_width);
  381. total_width = llmax( total_width, child_width );
  382. running_height += child_height;
  383. folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() );
  384. }
  385. }
  386. for (items_t::iterator iter = mItems.begin();
  387. iter != mItems.end();)
  388. {
  389. items_t::iterator iit = iter++;
  390. LLFolderViewItem* itemp = (*iit);
  391. itemp->setVisible(itemp->getFiltered(filter_generation));
  392. if (itemp->getVisible())
  393. {
  394. S32 child_width = 0;
  395. S32 child_height = 0;
  396. S32 child_top = parent_item_height - running_height;
  397. target_height += itemp->arrange( &child_width, &child_height, filter_generation );
  398. itemp->reshape(itemp->getRect().getWidth(), child_height);
  399. mMinWidth = llmax(mMinWidth, child_width);
  400. total_width = llmax( total_width, child_width );
  401. running_height += child_height;
  402. itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() );
  403. }
  404. }
  405. if(!mHasVisibleChildren)// is there any filtered items ?
  406. {
  407. //Nope. We need to display status textbox, let's reserve some place for it
  408. running_height = mStatusTextBox->getTextPixelHeight();
  409. target_height = running_height;
  410. }
  411. mRunningHeight = running_height;
  412. LLRect scroll_rect = mScrollContainer->getContentWindowRect();
  413. reshape( llmax(scroll_rect.getWidth(), total_width), running_height );
  414. LLRect new_scroll_rect = mScrollContainer->getContentWindowRect();
  415. if (new_scroll_rect.getWidth() != scroll_rect.getWidth())
  416. {
  417. reshape( llmax(scroll_rect.getWidth(), total_width), running_height );
  418. }
  419. // move item renamer text field to item's new position
  420. updateRenamerPosition();
  421. mTargetHeight = (F32)target_height;
  422. return llround(mTargetHeight);
  423. }
  424. const std::string LLFolderView::getFilterSubString(BOOL trim)
  425. {
  426. return mFilter->getFilterSubString(trim);
  427. }
  428. static LLFastTimer::DeclareTimer FTM_FILTER("Filter Inventory");
  429. void LLFolderView::filter( LLInventoryFilter& filter )
  430. {
  431. LLFastTimer t2(FTM_FILTER);
  432. filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000));
  433. if (getCompletedFilterGeneration() < filter.getCurrentGeneration())
  434. {
  435. mPassedFilter = FALSE;
  436. mMinWidth = 0;
  437. LLFolderViewFolder::filter(filter);
  438. }
  439. else
  440. {
  441. mPassedFilter = TRUE;
  442. }
  443. }
  444. void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent)
  445. {
  446. LLRect scroll_rect;
  447. if (mScrollContainer)
  448. {
  449. LLView::reshape(width, height, called_from_parent);
  450. scroll_rect = mScrollContainer->getContentWindowRect();
  451. }
  452. width = llmax(mMinWidth, scroll_rect.getWidth());
  453. height = llmax(mRunningHeight, scroll_rect.getHeight());
  454. // restrict width with scroll container's width
  455. if (mUseEllipses)
  456. width = scroll_rect.getWidth();
  457. LLView::reshape(width, height, called_from_parent);
  458. mReshapeSignal(mSelectedItems, FALSE);
  459. }
  460. void LLFolderView::addToSelectionList(LLFolderViewItem* item)
  461. {
  462. if (item->isSelected())
  463. {
  464. removeFromSelectionList(item);
  465. }
  466. if (mSelectedItems.size())
  467. {
  468. mSelectedItems.back()->setIsCurSelection(FALSE);
  469. }
  470. item->setIsCurSelection(TRUE);
  471. mSelectedItems.push_back(item);
  472. }
  473. void LLFolderView::removeFromSelectionList(LLFolderViewItem* item)
  474. {
  475. if (mSelectedItems.size())
  476. {
  477. mSelectedItems.back()->setIsCurSelection(FALSE);
  478. }
  479. selected_items_t::iterator item_iter;
  480. for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();)
  481. {
  482. if (*item_iter == item)
  483. {
  484. item_iter = mSelectedItems.erase(item_iter);
  485. }
  486. else
  487. {
  488. ++item_iter;
  489. }
  490. }
  491. if (mSelectedItems.size())
  492. {
  493. mSelectedItems.back()->setIsCurSelection(TRUE);
  494. }
  495. }
  496. LLFolderViewItem* LLFolderView::getCurSelectedItem( void )
  497. {
  498. if(mSelectedItems.size())
  499. {
  500. LLFolderViewItem* itemp = mSelectedItems.back();
  501. llassert(itemp->getIsCurSelection());
  502. return itemp;
  503. }
  504. return NULL;
  505. }
  506. // Record the selected item and pass it down the hierachy.
  507. BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem,
  508. BOOL take_keyboard_focus)
  509. {
  510. mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS;
  511. if( selection == this )
  512. {
  513. return FALSE;
  514. }
  515. if( selection && take_keyboard_focus)
  516. {
  517. mParentPanel->setFocus(TRUE);
  518. }
  519. // clear selection down here because change of keyboard focus can potentially
  520. // affect selection
  521. clearSelection();
  522. if(selection)
  523. {
  524. addToSelectionList(selection);
  525. }
  526. BOOL rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus);
  527. if(openitem && selection)
  528. {
  529. selection->getParentFolder()->requestArrange();
  530. }
  531. llassert(mSelectedItems.size() <= 1);
  532. return rv;
  533. }
  534. void LLFolderView::setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus)
  535. {
  536. LLFolderViewItem* itemp = getItemByID(obj_id);
  537. if(itemp && itemp->getListener())
  538. {
  539. itemp->arrangeAndSet(TRUE, take_keyboard_focus);
  540. mSelectThisID.setNull();
  541. return;
  542. }
  543. else
  544. {
  545. // save the desired item to be selected later (if/when ready)
  546. mSelectThisID = obj_id;
  547. }
  548. }
  549. void LLFolderView::updateSelection()
  550. {
  551. if (mSelectThisID.notNull())
  552. {
  553. setSelectionByID(mSelectThisID, false);
  554. }
  555. }
  556. BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected)
  557. {
  558. BOOL rv = FALSE;
  559. // can't select root folder
  560. if(!selection || selection == this)
  561. {
  562. return FALSE;
  563. }
  564. if (!mAllowMultiSelect)
  565. {
  566. clearSelection();
  567. }
  568. selected_items_t::iterator item_iter;
  569. for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
  570. {
  571. if (*item_iter == selection)
  572. {
  573. break;
  574. }
  575. }
  576. BOOL on_list = (item_iter != mSelectedItems.end());
  577. if(selected && !on_list)
  578. {
  579. addToSelectionList(selection);
  580. }
  581. if(!selected && on_list)
  582. {
  583. removeFromSelectionList(selection);
  584. }
  585. rv = LLFolderViewFolder::changeSelection(selection, selected);
  586. mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS;
  587. return rv;
  588. }
  589. static LLFastTimer::DeclareTimer FTM_SANITIZE_SELECTION("Sanitize Selection");
  590. void LLFolderView::sanitizeSelection()
  591. {
  592. LLFastTimer _(FTM_SANITIZE_SELECTION);
  593. // store off current item in case it is automatically deselected
  594. // and we want to preserve context
  595. LLFolderViewItem* original_selected_item = getCurSelectedItem();
  596. // Cache "Show all folders" filter setting
  597. BOOL show_all_folders = (getRoot()->getFilter()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS);
  598. std::vector<LLFolderViewItem*> items_to_remove;
  599. selected_items_t::iterator item_iter;
  600. for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
  601. {
  602. LLFolderViewItem* item = *item_iter;
  603. // ensure that each ancestor is open and potentially passes filtering
  604. BOOL visible = item->potentiallyVisible(); // initialize from filter state for this item
  605. // modify with parent open and filters states
  606. LLFolderViewFolder* parent_folder = item->getParentFolder();
  607. if ( parent_folder )
  608. {
  609. if ( show_all_folders )
  610. { // "Show all folders" is on, so this folder is visible
  611. visible = TRUE;
  612. }
  613. else
  614. { // Move up through parent folders and see what's visible
  615. while(parent_folder)
  616. {
  617. visible = visible && parent_folder->isOpen() && parent_folder->potentiallyVisible();
  618. parent_folder = parent_folder->getParentFolder();
  619. }
  620. }
  621. }
  622. // deselect item if any ancestor is closed or didn't pass filter requirements.
  623. if (!visible)
  624. {
  625. items_to_remove.push_back(item);
  626. }
  627. // disallow nested selections (i.e. folder items plus one or more ancestors)
  628. // could check cached mum selections count and only iterate if there are any
  629. // but that may be a premature optimization.
  630. selected_items_t::iterator other_item_iter;
  631. for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter)
  632. {
  633. LLFolderViewItem* other_item = *other_item_iter;
  634. for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder())
  635. {
  636. if (parent_folder == item)
  637. {
  638. // this is a descendent of the current folder, remove from list
  639. items_to_remove.push_back(other_item);
  640. break;
  641. }
  642. }
  643. }
  644. // Don't allow invisible items (such as root folders) to be selected.
  645. if (item == getRoot())
  646. {
  647. items_to_remove.push_back(item);
  648. }
  649. }
  650. std::vector<LLFolderViewItem*>::iterator item_it;
  651. for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it )
  652. {
  653. changeSelection(*item_it, FALSE); // toggle selection (also removes from list)
  654. }
  655. // if nothing selected after prior constraints...
  656. if (mSelectedItems.empty())
  657. {
  658. // ...select first available parent of original selection, or "My Inventory" otherwise
  659. LLFolderViewItem* new_selection = NULL;
  660. if (original_selected_item)
  661. {
  662. for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder();
  663. parent_folder;
  664. parent_folder = parent_folder->getParentFolder())
  665. {
  666. if (parent_folder->potentiallyVisible())
  667. {
  668. // give initial selection to first ancestor folder that potentially passes the filter
  669. if (!new_selection)
  670. {
  671. new_selection = parent_folder;
  672. }
  673. // if any ancestor folder of original item is closed, move the selection up
  674. // to the highest closed
  675. if (!parent_folder->isOpen())
  676. {
  677. new_selection = parent_folder;
  678. }
  679. }
  680. }
  681. }
  682. else
  683. {
  684. new_selection = NULL;
  685. }
  686. if (new_selection)
  687. {
  688. setSelection(new_selection, FALSE, FALSE);
  689. }
  690. }
  691. }
  692. void LLFolderView::clearSelection()
  693. {
  694. for (selected_items_t::const_iterator item_it = mSelectedItems.begin();
  695. item_it != mSelectedItems.end();
  696. ++item_it)
  697. {
  698. (*item_it)->setUnselected();
  699. }
  700. mSelectedItems.clear();
  701. mSelectThisID.setNull();
  702. }
  703. std::set<LLUUID> LLFolderView::getSelectionList() const
  704. {
  705. std::set<LLUUID> selection;
  706. for (selected_items_t::const_iterator item_it = mSelectedItems.begin();
  707. item_it != mSelectedItems.end();
  708. ++item_it)
  709. {
  710. selection.insert((*item_it)->getListener()->getUUID());
  711. }
  712. return selection;
  713. }
  714. BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source)
  715. {
  716. std::vector<EDragAndDropType> types;
  717. uuid_vec_t cargo_ids;
  718. selected_items_t::iterator item_it;
  719. BOOL can_drag = TRUE;
  720. if (!mSelectedItems.empty())
  721. {
  722. for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
  723. {
  724. EDragAndDropType type = DAD_NONE;
  725. LLUUID id = LLUUID::null;
  726. can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id);
  727. types.push_back(type);
  728. cargo_ids.push_back(id);
  729. }
  730. LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, source, mSourceID);
  731. }
  732. return can_drag;
  733. }
  734. void LLFolderView::commitRename( const LLSD& data )
  735. {
  736. finishRenamingItem();
  737. }
  738. void LLFolderView::draw()
  739. {
  740. static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", LLColor4::white);
  741. if (mDebugFilters)
  742. {
  743. std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d",
  744. mFilter->getCurrentGeneration(), mFilter->getMinRequiredGeneration(), mFilter->getMustPassGeneration());
  745. LLFontGL::getFontMonospace()->renderUTF8(current_filter_string, 0, 2,
  746. getRect().getHeight() - LLFontGL::getFontMonospace()->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f),
  747. LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
  748. }
  749. //LLFontGL* font = getLabelFontForStyle(mLabelStyle);
  750. // if cursor has moved off of me during drag and drop
  751. // close all auto opened folders
  752. if (!mDragAndDropThisFrame)
  753. {
  754. closeAutoOpenedFolders();
  755. }
  756. // while dragging, update selection rendering to reflect single/multi drag status
  757. if (LLToolDragAndDrop::getInstance()->hasMouseCapture())
  758. {
  759. EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept();
  760. if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE)
  761. {
  762. setShowSingleSelection(TRUE);
  763. }
  764. else
  765. {
  766. setShowSingleSelection(FALSE);
  767. }
  768. }
  769. else
  770. {
  771. setShowSingleSelection(FALSE);
  772. }
  773. if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size())
  774. {
  775. mSearchString.clear();
  776. }
  777. if (hasVisibleChildren()
  778. || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS)
  779. {
  780. mStatusText.clear();
  781. mStatusTextBox->setVisible( FALSE );
  782. }
  783. else if (mShowEmptyMessage)
  784. {
  785. if (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() || mCompletedFilterGeneration < mFilter->getMinRequiredGeneration())
  786. {
  787. mStatusText = LLTrans::getString("Searching");
  788. }
  789. else
  790. {
  791. if (getFilter())
  792. {
  793. LLStringUtil::format_map_t args;
  794. args["[SEARCH_TERM]"] = LLURI::escape(getFilter()->getFilterSubStringOrig());
  795. mStatusText = LLTrans::getString(getFilter()->getEmptyLookupMessage(), args);
  796. }
  797. }
  798. mStatusTextBox->setValue(mStatusText);
  799. mStatusTextBox->setVisible( TRUE );
  800. // firstly reshape message textbox with current size. This is necessary to
  801. // LLTextBox::getTextPixelHeight works properly
  802. const LLRect local_rect = getLocalRect();
  803. mStatusTextBox->setShape(local_rect);
  804. // get preferable text height...
  805. S32 pixel_height = mStatusTextBox->getTextPixelHeight();
  806. bool height_changed = local_rect.getHeight() != pixel_height;
  807. if (height_changed)
  808. {
  809. // ... if it does not match current height, lets rearrange current view.
  810. // This will indirectly call ::arrange and reshape of the status textbox.
  811. // We should call this method to also notify parent about required rect.
  812. // See EXT-7564, EXT-7047.
  813. arrangeFromRoot();
  814. }
  815. }
  816. // skip over LLFolderViewFolder::draw since we don't want the folder icon, label,
  817. // and arrow for the root folder
  818. LLView::draw();
  819. mDragAndDropThisFrame = FALSE;
  820. }
  821. void LLFolderView::finishRenamingItem( void )
  822. {
  823. if(!mRenamer)
  824. {
  825. return;
  826. }
  827. if( mRenameItem )
  828. {
  829. mRenameItem->rename( mRenamer->getText() );
  830. }
  831. closeRenamer();
  832. // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible.
  833. scrollToShowSelection();
  834. }
  835. void LLFolderView::closeRenamer( void )
  836. {
  837. if (mRenamer && mRenamer->getVisible())
  838. {
  839. // Triggers onRenamerLost() that actually closes the renamer.
  840. gViewerWindow->removePopup(mRenamer);
  841. }
  842. }
  843. void LLFolderView::removeSelectedItems( void )
  844. {
  845. if (mSelectedItems.empty()) return;
  846. LLSD args;
  847. args["QUESTION"] = LLTrans::getString(mSelectedItems.size() > 1 ? "DeleteItems" : "DeleteItem");
  848. LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLFolderView::onItemsRemovalConfirmation, this, _1, _2));
  849. }
  850. bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector<LLFolderViewItem*>& selectedItems)
  851. {
  852. LLFolderViewItem* item_parent = dynamic_cast<LLFolderViewItem*>(item->getParent());
  853. if (item_parent)
  854. {
  855. for(std::vector<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it)
  856. {
  857. const LLFolderViewItem* const selected_item = (*it);
  858. LLFolderViewItem* parent = item_parent;
  859. while (parent)
  860. {
  861. if (selected_item == parent)
  862. {
  863. return true;
  864. }
  865. parent = dynamic_cast<LLFolderViewItem*>(parent->getParent());
  866. }
  867. }
  868. }
  869. return false;
  870. }
  871. void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response)
  872. {
  873. S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
  874. if (option != 0) return; // canceled
  875. if(getVisible() && getEnabled())
  876. {
  877. // just in case we're removing the renaming item.
  878. mRenameItem = NULL;
  879. // create a temporary structure which we will use to remove
  880. // items, since the removal will futz with internal data
  881. // structures.
  882. std::vector<LLFolderViewItem*> items;
  883. S32 count = mSelectedItems.size();
  884. if(count == 0) return;
  885. LLFolderViewItem* item = NULL;
  886. selected_items_t::iterator item_it;
  887. for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
  888. {
  889. item = *item_it;
  890. if (item && item->isRemovable())
  891. {
  892. items.push_back(item);
  893. }
  894. else
  895. {
  896. llinfos << "Cannot delete " << item->getName() << llendl;
  897. return;
  898. }
  899. }
  900. // iterate through the new container.
  901. count = items.size();
  902. LLUUID new_selection_id;
  903. if(count == 1)
  904. {
  905. LLFolderViewItem* item_to_delete = items[0];
  906. LLFolderViewFolder* parent = item_to_delete->getParentFolder();
  907. LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE);
  908. if (!new_selection)
  909. {
  910. new_selection = item_to_delete->getPreviousOpenNode(FALSE);
  911. }
  912. if(parent)
  913. {
  914. if (parent->removeItem(item_to_delete))
  915. {
  916. // change selection on successful delete
  917. if (new_selection)
  918. {
  919. setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus());
  920. }
  921. else
  922. {
  923. setSelectionFromRoot(NULL, mParentPanel->hasFocus());
  924. }
  925. }
  926. }
  927. arrangeAll();
  928. }
  929. else if (count > 1)
  930. {
  931. LLDynamicArray<LLFolderViewEventListener*> listeners;
  932. LLFolderViewEventListener* listener;
  933. LLFolderViewItem* last_item = items[count - 1];
  934. LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE);
  935. while(new_selection && new_selection->isSelected())
  936. {
  937. new_selection = new_selection->getNextOpenNode(FALSE);
  938. }
  939. if (!new_selection)
  940. {
  941. new_selection = last_item->getPreviousOpenNode(FALSE);
  942. while (new_selection && (new_selection->isSelected() || isDescendantOfASelectedItem(new_selection, items)))
  943. {
  944. new_selection = new_selection->getPreviousOpenNode(FALSE);
  945. }
  946. }
  947. if (new_selection)
  948. {
  949. setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus());
  950. }
  951. else
  952. {
  953. setSelectionFromRoot(NULL, mParentPanel->hasFocus());
  954. }
  955. for(S32 i = 0; i < count; ++i)
  956. {
  957. listener = items[i]->getListener();
  958. if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::FAIL))
  959. {
  960. listeners.put(listener);
  961. }
  962. }
  963. listener = listeners.get(0);
  964. if(listener)
  965. {
  966. listener->removeBatch(listeners);
  967. }
  968. }
  969. arrangeAll();
  970. scrollToShowSelection();
  971. }
  972. }
  973. // open the selected item.
  974. void LLFolderView::openSelectedItems( void )
  975. {
  976. if(getVisible() && getEnabled())
  977. {
  978. if (mSelectedItems.size() == 1)
  979. {
  980. mSelectedItems.front()->openItem();
  981. }
  982. else
  983. {
  984. LLMultiPreview* multi_previewp = new LLMultiPreview();
  985. LLMultiProperties* multi_propertiesp = new LLMultiProperties();
  986. selected_items_t::iterator item_it;
  987. for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
  988. {
  989. // IT_{OBJECT,ATTACHMENT} creates LLProperties
  990. // floaters; others create LLPreviews. Put
  991. // each one in the right type of container.
  992. LLFolderViewEventListener* listener = (*item_it)->getListener();
  993. bool is_prop = listener && (listener->getInventoryType() == LLInventoryType::IT_OBJECT || listener->getInventoryType() == LLInventoryType::IT_ATTACHMENT);
  994. if (is_prop)
  995. LLFloater::setFloaterHost(multi_propertiesp);
  996. else
  997. LLFloater::setFloaterHost(multi_previewp);
  998. (*item_it)->openItem();
  999. }
  1000. LLFloater::setFloaterHost(NULL);
  1001. // *NOTE: LLMulti* will safely auto-delete when open'd
  1002. // without any children.
  1003. multi_previewp->openFloater(LLSD());
  1004. multi_propertiesp->openFloater(LLSD());
  1005. }
  1006. }
  1007. }
  1008. void LLFolderView::propertiesSelectedItems( void )
  1009. {
  1010. if(getVisible() && getEnabled())
  1011. {
  1012. if (mSelectedItems.size() == 1)
  1013. {
  1014. LLFolderViewItem* folder_item = mSelectedItems.front();
  1015. if(!folder_item) return;
  1016. folder_item->getListener()->showProperties();
  1017. }
  1018. else
  1019. {
  1020. LLMultiProperties* multi_propertiesp = new LLMultiProperties();
  1021. LLFloater::setFloaterHost(multi_propertiesp);
  1022. selected_items_t::iterator item_it;
  1023. for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
  1024. {
  1025. (*item_it)->getListener()->showProperties();
  1026. }
  1027. LLFloater::setFloaterHost(NULL);
  1028. multi_propertiesp->openFloater(LLSD());
  1029. }
  1030. }
  1031. }
  1032. void LLFolderView::changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type)
  1033. {
  1034. LLFolderBridge *folder_bridge = LLFolderBridge::sSelf.get();
  1035. if (!folder_bridge) return;
  1036. LLViewerInventoryCategory *cat = folder_bridge->getCategory();
  1037. if (!cat) return;
  1038. cat->changeType(new_folder_type);
  1039. }
  1040. void LLFolderView::autoOpenItem( LLFolderViewFolder* item )
  1041. {
  1042. if ((mAutoOpenItems.check() == item) ||
  1043. (mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) ||
  1044. item->isOpen())
  1045. {
  1046. return;
  1047. }
  1048. // close auto-opened folders
  1049. LLFolderViewFolder* close_item = mAutoOpenItems.check();
  1050. while (close_item && close_item != item->getParentFolder())
  1051. {
  1052. mAutoOpenItems.pop();
  1053. close_item->setOpenArrangeRecursively(FALSE);
  1054. close_item = mAutoOpenItems.check();
  1055. }
  1056. item->requestArrange();
  1057. mAutoOpenItems.push(item);
  1058. item->setOpen(TRUE);
  1059. LLRect content_rect = mScrollContainer->getContentWindowRect();
  1060. LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0);
  1061. scrollToShowItem(item, constraint_rect);
  1062. }
  1063. void LLFolderView::closeAutoOpenedFolders()
  1064. {
  1065. while (mAutoOpenItems.check())
  1066. {
  1067. LLFolderViewFolder* close_item = mAutoOpenItems.pop();
  1068. close_item->setOpen(FALSE);
  1069. }
  1070. if (mAutoOpenCandidate)
  1071. {
  1072. mAutoOpenCandidate->setAutoOpenCountdown(0.f);
  1073. }
  1074. mAutoOpenCandidate = NULL;
  1075. mAutoOpenTimer.stop();
  1076. }
  1077. BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder)
  1078. {
  1079. if (folder && mAutoOpenCandidate == folder)
  1080. {
  1081. if (mAutoOpenTimer.getStarted())
  1082. {
  1083. if (!mAutoOpenCandidate->isOpen())
  1084. {
  1085. mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f));
  1086. }
  1087. if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime)
  1088. {
  1089. autoOpenItem(folder);
  1090. mAutoOpenTimer.stop();
  1091. return TRUE;
  1092. }
  1093. }
  1094. return FALSE;
  1095. }
  1096. // otherwise new candidate, restart timer
  1097. if (mAutoOpenCandidate)
  1098. {
  1099. mAutoOpenCandidate->setAutoOpenCountdown(0.f);
  1100. }
  1101. mAutoOpenCandidate = folder;
  1102. mAutoOpenTimer.start();
  1103. return FALSE;
  1104. }
  1105. BOOL LLFolderView::canCopy() const
  1106. {
  1107. if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0)))
  1108. {
  1109. return FALSE;
  1110. }
  1111. for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
  1112. {
  1113. const LLFolderViewItem* item = *selected_it;
  1114. if (!item->getListener()->isItemCopyable())
  1115. {
  1116. return FALSE;
  1117. }
  1118. }
  1119. return TRUE;
  1120. }
  1121. // copy selected item
  1122. void LLFolderView::copy()
  1123. {
  1124. // *NOTE: total hack to clear the inventory clipboard
  1125. LLInventoryClipboard::instance().reset();
  1126. S32 count = mSelectedItems.size();
  1127. if(getVisible() && getEnabled() && (count > 0))
  1128. {
  1129. LLFolderViewEventListener* listener = NULL;
  1130. selected_items_t::iterator item_it;
  1131. for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
  1132. {
  1133. listener = (*item_it)->getListener();
  1134. if(listener)
  1135. {
  1136. listener->copyToClipboard();
  1137. }
  1138. }
  1139. }
  1140. mSearchString.clear();
  1141. }
  1142. BOOL LLFolderView::canCut() const
  1143. {
  1144. if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0)))
  1145. {
  1146. return FALSE;
  1147. }
  1148. for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
  1149. {
  1150. const LLFolderViewItem* item = *selected_it;
  1151. const LLFolderViewEventListener* listener = item->getListener();
  1152. if (!listener || !listener->isItemRemovable())
  1153. {
  1154. return FALSE;
  1155. }
  1156. }
  1157. return TRUE;
  1158. }
  1159. void LLFolderView::cut()
  1160. {
  1161. // clear the inventory clipboard
  1162. LLInventoryClipboard::instance().reset();
  1163. S32 count = mSelectedItems.size();
  1164. if(getVisible() && getEnabled() && (count > 0))
  1165. {
  1166. LLFolderViewEventListener* listener = NULL;
  1167. selected_items_t::iterator item_it;
  1168. for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
  1169. {
  1170. listener = (*item_it)->getListener();
  1171. if(listener)
  1172. {
  1173. listener->cutToClipboard();
  1174. }
  1175. }
  1176. }
  1177. mSearchString.clear();
  1178. }
  1179. BOOL LLFolderView::canPaste() const
  1180. {
  1181. if (mSelectedItems.empty())
  1182. {
  1183. return FALSE;
  1184. }
  1185. if(getVisible() && getEnabled())
  1186. {
  1187. for (selected_items_t::const_iterator item_it = mSelectedItems.begin();
  1188. item_it != mSelectedItems.end(); ++item_it)
  1189. {
  1190. // *TODO: only check folders and parent folders of items
  1191. const LLFolderViewItem* item = (*item_it);
  1192. const LLFolderViewEventListener* listener = item->getListener();
  1193. if(!listener || !listener->isClipboardPasteable())
  1194. {
  1195. const LLFolderViewFolder* folderp = item->getParentFolder();
  1196. listener = folderp->getListener();
  1197. if (!listener || !listener->isClipboardPasteable())
  1198. {
  1199. return FALSE;
  1200. }
  1201. }
  1202. }
  1203. return TRUE;
  1204. }
  1205. return FALSE;
  1206. }
  1207. // paste selected item
  1208. void LLFolderView::paste()
  1209. {
  1210. if(getVisible() && getEnabled())
  1211. {
  1212. // find set of unique folders to paste into
  1213. std::set<LLFolderViewItem*> folder_set;
  1214. selected_items_t::iterator selected_it;
  1215. for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
  1216. {
  1217. LLFolderViewItem* item = *selected_it;
  1218. LLFolderViewEventListener* listener = item->getListener();
  1219. if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY)
  1220. {
  1221. item = item->getParentFolder();
  1222. }
  1223. folder_set.insert(item);
  1224. }
  1225. std::set<LLFolderViewItem*>::iterator set_iter;
  1226. for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter)
  1227. {
  1228. LLFolderViewEventListener* listener = (*set_iter)->getListener();
  1229. if(listener && listener->isClipboardPasteable())
  1230. {
  1231. listener->pasteFromClipboard();
  1232. }
  1233. }
  1234. }
  1235. mSearchString.clear();
  1236. }
  1237. // public rename functionality - can only start the process
  1238. void LLFolderView::startRenamingSelectedItem( void )
  1239. {
  1240. // make sure selection is visible
  1241. scrollToShowSelection();
  1242. S32 count = mSelectedItems.size();
  1243. LLFolderViewItem* item = NULL;
  1244. if(count > 0)
  1245. {
  1246. item = mSelectedItems.front();
  1247. }
  1248. if(getVisible() && getEnabled() && (count == 1) && item && item->getListener() &&
  1249. item->getListener()->isItemRenameable())
  1250. {
  1251. mRenameItem = item;
  1252. updateRenamerPosition();
  1253. mRenamer->setText(item->getName());
  1254. mRenamer->selectAll();
  1255. mRenamer->setVisible( TRUE );
  1256. // set focus will fail unless item is visible
  1257. mRenamer->setFocus( TRUE );
  1258. mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this));
  1259. gViewerWindow->addPopup(mRenamer);
  1260. }
  1261. }
  1262. BOOL LLFolderView::handleKeyHere( KEY key, MASK mask )
  1263. {
  1264. BOOL handled = FALSE;
  1265. // SL-51858: Key presses are not being passed to the Popup menu.
  1266. // A proper fix is non-trivial so instead just close the menu.
  1267. LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
  1268. if (menu && menu->isOpen())
  1269. {
  1270. LLMenuGL::sMenuContainer->hideMenus();
  1271. }
  1272. LLView *item = NULL;
  1273. if (getChildCount() > 0)
  1274. {
  1275. item = *(getChildList()->begin());
  1276. }
  1277. switch( key )
  1278. {
  1279. case KEY_F2:
  1280. mSearchString.clear();
  1281. startRenamingSelectedItem();
  1282. handled = TRUE;
  1283. break;
  1284. case KEY_RETURN:
  1285. if (mask == MASK_NONE)
  1286. {
  1287. if( mRenameItem && mRenamer->getVisible() )
  1288. {
  1289. finishRenamingItem();
  1290. mSearchString.clear();
  1291. handled = TRUE;
  1292. }
  1293. else
  1294. {
  1295. LLFolderView::openSelectedItems();
  1296. handled = TRUE;
  1297. }
  1298. }
  1299. break;
  1300. case KEY_ESCAPE:
  1301. if( mRenameItem && mRenamer->getVisible() )
  1302. {
  1303. closeRenamer();
  1304. handled = TRUE;
  1305. }
  1306. mSearchString.clear();
  1307. break;
  1308. case KEY_PAGE_UP:
  1309. mSearchString.clear();
  1310. mScrollContainer->pageUp(30);
  1311. handled = TRUE;
  1312. break;
  1313. case KEY_PAGE_DOWN:
  1314. mSearchString.clear();
  1315. mScrollContainer->pageDown(30);
  1316. handled = TRUE;
  1317. break;
  1318. case KEY_HOME:
  1319. mSearchString.clear();
  1320. mScrollContainer->goToTop();
  1321. handled = TRUE;
  1322. break;
  1323. case KEY_END:
  1324. mSearchString.clear();
  1325. mScrollContainer->goToBottom();
  1326. break;
  1327. case KEY_DOWN:
  1328. if((mSelectedItems.size() > 0) && mScrollContainer)
  1329. {
  1330. LLFolderViewItem* last_selected = getCurSelectedItem();
  1331. if (!mKeyboardSelection)
  1332. {
  1333. setSelection(last_selected, FALSE, TRUE);
  1334. mKeyboardSelection = TRUE;
  1335. }
  1336. LLFolderViewItem* next = NULL;
  1337. if (mask & MASK_SHIFT)
  1338. {
  1339. // don't shift select down to children of folders (they are implicitly selected through parent)
  1340. next = last_selected->getNextOpenNode(FALSE);
  1341. if (next)
  1342. {
  1343. if (next->isSelected())
  1344. {
  1345. // shrink selection
  1346. changeSelectionFromRoot(last_selected, FALSE);
  1347. }
  1348. else if (last_selected->getParentFolder() == next->getParentFolder())
  1349. {
  1350. // grow selection
  1351. changeSelectionFromRoot(next, TRUE);
  1352. }
  1353. }
  1354. }
  1355. else
  1356. {
  1357. next = last_selected->getNextOpenNode();
  1358. if( next )
  1359. {
  1360. if (next == last_selected)
  1361. {
  1362. //special case for LLAccordionCtrl
  1363. if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed
  1364. {
  1365. clearSelection();
  1366. return TRUE;
  1367. }
  1368. return FALSE;
  1369. }
  1370. setSelection( next, FALSE, TRUE );
  1371. }
  1372. else
  1373. {
  1374. //special case for LLAccordionCtrl
  1375. if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed
  1376. {
  1377. clearSelection();
  1378. return TRUE;
  1379. }
  1380. return FALSE;
  1381. }
  1382. }
  1383. scrollToShowSelection();
  1384. mSearchString.clear();
  1385. handled = TRUE;
  1386. }
  1387. break;
  1388. case KEY_UP:
  1389. if((mSelectedItems.size() > 0) && mScrollContainer)
  1390. {
  1391. LLFolderViewItem* last_selected = mSelectedItems.back();
  1392. if (!mKeyboardSelection)
  1393. {
  1394. setSelection(last_selected, FALSE, TRUE);
  1395. mKeyboardSelection = TRUE;
  1396. }
  1397. LLFolderViewItem* prev = NULL;
  1398. if (mask & MASK_SHIFT)
  1399. {
  1400. // don't shift select down to children of folders (they are implicitly selected through parent)
  1401. prev = last_selected->getPreviousOpenNode(FALSE);
  1402. if (prev)
  1403. {
  1404. if (prev->isSelected())
  1405. {
  1406. // shrink selection
  1407. changeSelectionFromRoot(last_selected, FALSE);
  1408. }
  1409. else if (last_selected->getParentFolder() == prev->getParentFolder())
  1410. {
  1411. // grow selection
  1412. changeSelectionFromRoot(prev, TRUE);
  1413. }
  1414. }
  1415. }
  1416. else
  1417. {
  1418. prev = last_selected->getPreviousOpenNode();
  1419. if( prev )
  1420. {
  1421. if (prev == this)
  1422. {
  1423. // If case we are in accordion tab notify parent to go to the previous accordion
  1424. if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed
  1425. {
  1426. clearSelection();
  1427. return TRUE;
  1428. }
  1429. return FALSE;
  1430. }
  1431. setSelection( prev, FALSE, TRUE );
  1432. }
  1433. }
  1434. scrollToShowSelection();
  1435. mSearchString.clear();
  1436. handled = TRUE;
  1437. }
  1438. break;
  1439. case KEY_RIGHT:
  1440. if(mSelectedItems.size())
  1441. {
  1442. LLFolderViewItem* last_selected = getCurSelectedItem();
  1443. last_selected->setOpen( TRUE );
  1444. mSearchString.clear();
  1445. handled = TRUE;
  1446. }
  1447. break;
  1448. case KEY_LEFT:
  1449. if(mSelectedItems.size())
  1450. {
  1451. LLFolderViewItem* last_selected = getCurSelectedItem();
  1452. LLFolderViewItem* parent_folder = last_selected->getParentFolder();
  1453. if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder())
  1454. {
  1455. setSelection(parent_folder, FALSE, TRUE);
  1456. }
  1457. else
  1458. {
  1459. last_selected->setOpen( FALSE );
  1460. }
  1461. mSearchString.clear();
  1462. scrollToShowSelection();
  1463. handled = TRUE;
  1464. }
  1465. break;
  1466. }
  1467. if (!handled && mParentPanel->hasFocus())
  1468. {
  1469. if (key == KEY_BACKSPACE)
  1470. {
  1471. mSearchTimer.reset();
  1472. if (mSearchString.size())
  1473. {
  1474. mSearchString.erase(mSearchString.size() - 1, 1);
  1475. }
  1476. search(getCurSelectedItem(), mSearchString, FALSE);
  1477. handled = TRUE;
  1478. }
  1479. }
  1480. return handled;
  1481. }
  1482. BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char)
  1483. {
  1484. if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
  1485. {
  1486. return FALSE;
  1487. }
  1488. if (uni_char > 0x7f)
  1489. {
  1490. llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl;
  1491. return FALSE;
  1492. }
  1493. BOOL handled = FALSE;
  1494. if (mParentPanel->hasFocus())
  1495. {
  1496. // SL-51858: Key presses are not being passed to the Popup menu.
  1497. // A proper fix is non-trivial so instead just close the menu.
  1498. LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
  1499. if (menu && menu->isOpen())
  1500. {
  1501. LLMenuGL::sMenuContainer->hideMenus();
  1502. }
  1503. //do text search
  1504. if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout"))
  1505. {
  1506. mSearchString.clear();
  1507. }
  1508. mSearchTimer.reset();
  1509. if (mSearchString.size() < 128)
  1510. {
  1511. mSearchString += uni_char;
  1512. }
  1513. search(getCurSelectedItem(), mSearchString, FALSE);
  1514. handled = TRUE;
  1515. }
  1516. return handled;
  1517. }
  1518. BOOL LLFolderView::canDoDelete() const
  1519. {
  1520. if (mSelectedItems.size() == 0) return FALSE;
  1521. for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
  1522. {
  1523. if (!(*item_it)->getListener()->isItemRemovable())
  1524. {
  1525. return FALSE;
  1526. }
  1527. }
  1528. return TRUE;
  1529. }
  1530. void LLFolderView::doDelete()
  1531. {
  1532. if(mSelectedItems.size() > 0)
  1533. {
  1534. removeSelectedItems();
  1535. }
  1536. }
  1537. BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask )
  1538. {
  1539. mKeyboardSelection = FALSE;
  1540. mSearchString.clear();
  1541. mParentPanel->setFocus(TRUE);
  1542. LLEditMenuHandler::gEditMenuHandler = this;
  1543. return LLView::handleMouseDown( x, y, mask );
  1544. }
  1545. BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward)
  1546. {
  1547. // get first selected item
  1548. LLFolderViewItem* search_item = first_item;
  1549. // make sure search string is upper case
  1550. std::string upper_case_string = search_string;
  1551. LLStringUtil::toUpper(upper_case_string);
  1552. // if nothing selected, select first item in folder
  1553. if (!search_item)
  1554. {
  1555. // start from first item
  1556. search_item = getNextFromChild(NULL);
  1557. }
  1558. // search over all open nodes for first substring match (with wrapping)
  1559. BOOL found = FALSE;
  1560. LLFolderViewItem* original_search_item = search_item;
  1561. do
  1562. {
  1563. // wrap at end
  1564. if (!search_item)
  1565. {
  1566. if (backward)
  1567. {
  1568. search_item = getPreviousFromChild(NULL);
  1569. }
  1570. else
  1571. {
  1572. search_item = getNextFromChild(NULL);
  1573. }
  1574. if (!search_item || search_item == original_search_item)
  1575. {
  1576. break;
  1577. }
  1578. }
  1579. const std::string current_item_label(search_item->getSearchableLabel());
  1580. S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size());
  1581. if (!current_item_label.compare(0, search_string_length, upper_case_string))
  1582. {
  1583. found = TRUE;
  1584. break;
  1585. }
  1586. if (backward)
  1587. {
  1588. search_item = search_item->getPreviousOpenNode();
  1589. }
  1590. else
  1591. {
  1592. search_item = search_item->getNextOpenNode();
  1593. }
  1594. } while(search_item != original_search_item);
  1595. if (found)
  1596. {
  1597. setSelection(search_item, FALSE, TRUE);
  1598. scrollToShowSelection();
  1599. }
  1600. return found;
  1601. }
  1602. BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask )
  1603. {
  1604. // skip LLFolderViewFolder::handleDoubleClick()
  1605. return LLView::handleDoubleClick( x, y, mask );
  1606. }
  1607. BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
  1608. {
  1609. // all user operations move keyboard focus to inventory
  1610. // this way, we know when to stop auto-updating a search
  1611. mParentPanel->setFocus(TRUE);
  1612. BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
  1613. S32 count = mSelectedItems.size();
  1614. LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
  1615. if ( handled
  1616. && ( count > 0 && (hasVisibleChildren() || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) ) // show menu only if selected items are visible
  1617. && menu )
  1618. {
  1619. if (mCallbackRegistrar)
  1620. mCallbackRegistrar->pushScope();
  1621. updateMenuOptions(menu);
  1622. menu->updateParent(LLMenuGL::sMenuContainer);
  1623. LLMenuGL::showPopup(this, menu, x, y);
  1624. if (mCallbackRegistrar)
  1625. mCallbackRegistrar->popScope();
  1626. }
  1627. else
  1628. {
  1629. if (menu && menu->getVisible())
  1630. {
  1631. menu->setVisible(FALSE);
  1632. }
  1633. setSelection(NULL, FALSE, TRUE);
  1634. }
  1635. return handled;
  1636. }
  1637. // Add "--no options--" if the menu is completely blank.
  1638. BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const
  1639. {
  1640. const std::string nooptions_str = "--no options--";
  1641. LLView *nooptions_item = NULL;
  1642. const LLView::child_list_t *list = menu->getChildList();
  1643. for (LLView::child_list_t::const_iterator itor = list->begin();
  1644. itor != list->end();
  1645. ++itor)
  1646. {
  1647. LLView *menu_item = (*itor);
  1648. if (menu_item->getVisible())
  1649. {
  1650. return FALSE;
  1651. }
  1652. std::string name = menu_item->getName();
  1653. if (menu_item->getName() == nooptions_str)
  1654. {
  1655. nooptions_item = menu_item;
  1656. }
  1657. }
  1658. if (nooptions_item)
  1659. {
  1660. nooptions_item->setVisible(TRUE);
  1661. nooptions_item->setEnabled(FALSE);
  1662. return TRUE;
  1663. }
  1664. return FALSE;
  1665. }
  1666. BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask )
  1667. {
  1668. return LLView::handleHover( x, y, mask );
  1669. }
  1670. BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
  1671. EDragAndDropType cargo_type,
  1672. void* cargo_data,
  1673. EAcceptance* accept,
  1674. std::string& tooltip_msg)
  1675. {
  1676. mDragAndDropThisFrame = TRUE;
  1677. // have children handle it first
  1678. BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data,
  1679. accept, tooltip_msg);
  1680. // when drop is not handled by child, it should be handled
  1681. // by the folder which is the hierarchy root.
  1682. if (!handled)
  1683. {
  1684. if (getListener()->getUUID().notNull())
  1685. {
  1686. handled = LLFolderViewFolder::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
  1687. }
  1688. else
  1689. {
  1690. if (!mFolders.empty())
  1691. {
  1692. // dispatch to last folder as a hack to support "Contents" folder in object inventory
  1693. handled = mFolders.back()->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg);
  1694. }
  1695. }
  1696. }
  1697. if (handled)
  1698. {
  1699. lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl;
  1700. }
  1701. return handled;
  1702. }
  1703. void LLFolderView::deleteAllChildren()
  1704. {
  1705. closeRenamer();
  1706. if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
  1707. mPopupMenuHandle = LLHandle<LLView>();
  1708. mScrollContainer = NULL;
  1709. mRenameItem = NULL;
  1710. mRenamer = NULL;
  1711. mStatusTextBox = NULL;
  1712. clearSelection();
  1713. LLView::deleteAllChildren();
  1714. }
  1715. void LLFolderView::scrollToShowSelection()
  1716. {
  1717. // If items are filtered while background fetch is in progress
  1718. // scrollbar resets to the first filtered item. See EXT-3981.
  1719. // However we allow scrolling for folder views with mAutoSelectOverride
  1720. // (used in Places SP) as an exception because the selection in them
  1721. // is not reset during items filtering. See STORM-133.
  1722. if ( (!LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() || mAutoSelectOverride)
  1723. && mSelectedItems.size() )
  1724. {
  1725. mNeedsScroll = TRUE;
  1726. }
  1727. }
  1728. // If the parent is scroll containter, scroll it to make the selection
  1729. // is maximally visible.
  1730. void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect)
  1731. {
  1732. if (!mScrollContainer) return;
  1733. // don't scroll to items when mouse is being used to scroll/drag and drop
  1734. if (gFocusMgr.childHasMouseCapture(mScrollContainer))
  1735. {
  1736. mNeedsScroll = FALSE;
  1737. return;
  1738. }
  1739. // if item exists and is in visible portion of parent folder...
  1740. if(item)
  1741. {
  1742. LLRect local_rect = item->getLocalRect();
  1743. LLRect item_scrolled_rect; // item position relative to display area of scroller
  1744. LLRect visible_doc_rect = mScrollContainer->getVisibleContentRect();
  1745. S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight();
  1746. S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight());
  1747. // when navigating with keyboard, only move top of opened folder on screen, otherwise show whole folder
  1748. S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight();
  1749. // get portion of item that we want to see...
  1750. LLRect item_local_rect = LLRect(item->getIndentation(),
  1751. local_rect.getHeight(),
  1752. llmin(MIN_ITEM_WIDTH_VISIBLE, local_rect.getWidth()),
  1753. llmax(0, local_rect.getHeight() - max_height_to_show));
  1754. LLRect item_doc_rect;
  1755. item->localRectToOtherView(item_local_rect, &item_doc_rect, this);
  1756. mScrollContainer->scrollToShowRect( item_doc_rect, constraint_rect );
  1757. }
  1758. }
  1759. LLRect LLFolderView::getVisibleRect()
  1760. {
  1761. S32 visible_height = mScrollContainer->getRect().getHeight();
  1762. S32 visible_width = mScrollContainer->getRect().getWidth();
  1763. LLRect visible_rect;
  1764. visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height);
  1765. return visible_rect;
  1766. }
  1767. BOOL LLFolderView::getShowSelectionContext()
  1768. {
  1769. if (mShowSelectionContext)
  1770. {
  1771. return TRUE;
  1772. }
  1773. LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
  1774. if (menu && menu->getVisible())
  1775. {
  1776. return TRUE;
  1777. }
  1778. return FALSE;
  1779. }
  1780. void LLFolderView::setShowSingleSelection(BOOL show)
  1781. {
  1782. if (show != mShowSingleSelection)
  1783. {
  1784. mMultiSelectionFadeTimer.reset();
  1785. mShowSingleSelection = show;
  1786. }
  1787. }
  1788. void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp)
  1789. {
  1790. mItemMap[id] = itemp;
  1791. }
  1792. void LLFolderView::removeItemID(const LLUUID& id)
  1793. {
  1794. mItemMap.erase(id);
  1795. }
  1796. LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID");
  1797. LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id)
  1798. {
  1799. LLFastTimer _(FTM_GET_ITEM_BY_ID);
  1800. if (id == getListener()->getUUID())
  1801. {
  1802. return this;
  1803. }
  1804. std::map<LLUUID, LLFolderViewItem*>::iterator map_it;
  1805. map_it = mItemMap.find(id);
  1806. if (map_it != mItemMap.end())
  1807. {
  1808. return map_it->second;
  1809. }
  1810. return NULL;
  1811. }
  1812. LLFolderViewFolder* LLFolderView::getFolderByID(const LLUUID& id)
  1813. {
  1814. if (id == getListener()->getUUID())
  1815. {
  1816. return this;
  1817. }
  1818. for (folders_t::iterator iter = mFolders.begin();
  1819. iter != mFolders.end();
  1820. ++iter)
  1821. {
  1822. LLFolderViewFolder *folder = (*iter);
  1823. if (folder->getListener()->getUUID() == id)
  1824. {
  1825. return folder;
  1826. }
  1827. }
  1828. return NULL;
  1829. }
  1830. bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata)
  1831. {
  1832. std::string action = userdata.asString();
  1833. if ("rename" == action)
  1834. {
  1835. startRenamingSelectedItem();
  1836. return true;
  1837. }
  1838. if ("delete" == action)
  1839. {
  1840. removeSelectedItems();
  1841. return true;
  1842. }
  1843. if ("copy" == action)
  1844. {
  1845. LLInventoryClipboard::instance().reset();
  1846. }
  1847. static const std::string change_folder_string = "change_folder_type_";
  1848. if (action.length() > change_folder_string.length() &&
  1849. (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0))
  1850. {
  1851. LLFolderType::EType new_folder_type = LLViewerFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length()));
  1852. changeType(model, new_folder_type);
  1853. return true;
  1854. }
  1855. std::set<LLUUID> selected_items = getSelectionList();
  1856. LLMultiPreview* multi_previewp = NULL;
  1857. LLMultiProperties* multi_propertiesp = NULL;
  1858. if (("task_open" == action || "open" == action) && selected_items.size() > 1)
  1859. {
  1860. multi_previewp = new LLMultiPreview();
  1861. gFloaterView->addChild(multi_previewp);
  1862. LLFloater::setFloaterHost(multi_previewp);
  1863. }
  1864. else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1)
  1865. {
  1866. multi_propertiesp = new LLMultiProperties();
  1867. gFloaterView->addChild(multi_propertiesp);
  1868. LLFloater::setFloaterHost(multi_propertiesp);
  1869. }
  1870. std::set<LLUUID>::iterator set_iter;
  1871. for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter)
  1872. {
  1873. LLFolderViewItem* folder_item = getItemByID(*set_iter);
  1874. if(!folder_item) continue;
  1875. LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener();
  1876. if(!bridge) continue;
  1877. bridge->performAction(model, action);
  1878. }
  1879. LLFloater::setFloaterHost(NULL);
  1880. if (multi_previewp)
  1881. {
  1882. multi_previewp->openFloater(LLSD());
  1883. }
  1884. else if (multi_propertiesp)
  1885. {
  1886. multi_propertiesp->openFloater(LLSD());
  1887. }
  1888. return true;
  1889. }
  1890. static LLFastTimer::DeclareTimer FTM_AUTO_SELECT("Open and Select");
  1891. static LLFastTimer::DeclareTimer FTM_INVENTORY("Inventory");
  1892. // Main idle routine
  1893. void LLFolderView::doIdle()
  1894. {
  1895. // If this is associated with the user's inventory, don't do anything
  1896. // until that inventory is loaded up.
  1897. const LLInventoryPanel *inventory_panel = dynamic_cast<LLInventoryPanel*>(mParentPanel);
  1898. if (inventory_panel && !inventory_panel->getIsViewsInitialized())
  1899. {
  1900. return;
  1901. }
  1902. LLFastTimer t2(FTM_INVENTORY);
  1903. BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters");
  1904. if (debug_filters != getDebugFilters())
  1905. {
  1906. mDebugFilters = debug_filters;
  1907. arrangeAll();
  1908. }
  1909. mFilter->clearModified();
  1910. BOOL filter_modified_and_active = mCompletedFilterGeneration < mFilter->getCurrentGeneration() &&
  1911. mFilter->isNotDefault();
  1912. mNeedsAutoSelect = filter_modified_and_active &&
  1913. !(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture());
  1914. // filter to determine visiblity before arranging
  1915. filterFromRoot();
  1916. // automatically show matching items, and select first one if we had a selection
  1917. // do this every frame until user puts keyboard focus into the inventory window
  1918. // signaling the end of the automatic update
  1919. // only do this when mNeedsFilter is set, meaning filtered items have
  1920. // potentially changed
  1921. if (mNeedsAutoSelect)
  1922. {
  1923. LLFastTimer t3(FTM_AUTO_SELECT);
  1924. // select new item only if a filtered item not currently selected
  1925. LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back();
  1926. if ((selected_itemp && !selected_itemp->getFiltered()) && !mAutoSelectOverride)
  1927. {
  1928. // select first filtered item
  1929. LLSelectFirstFilteredItem filter;
  1930. applyFunctorRecursively(filter);
  1931. }
  1932. // Open filtered folders for folder views with mAutoSelectOverride=TRUE.
  1933. // Used by LLPlacesFolderView.
  1934. if (mAutoSelectOverride && !mFilter->getFilterSubString().empty())
  1935. {
  1936. LLOpenFilteredFolders filter;
  1937. applyFunctorRecursively(filter);
  1938. }
  1939. scrollToShowSelection();
  1940. }
  1941. // during filtering process, try to pin selected item's location on screen
  1942. // this will happen when searching your inventory and when new items arrive
  1943. if (filter_modified_and_active)
  1944. {
  1945. // calculate rectangle to pin item to at start of animated rearrange
  1946. if (!mPinningSelectedItem && !mSelectedItems.empty())
  1947. {
  1948. // lets pin it!
  1949. mPinningSelectedItem = TRUE;
  1950. LLRect visible_content_rect = mScrollContainer->getVisibleContentRect();
  1951. LLFolderViewItem* selected_item = mSelectedItems.back();
  1952. LLRect item_rect;
  1953. selected_item->localRectToOtherView(selected_item->getLocalRect(), &item_rect, this);
  1954. // if item is visible in scrolled region
  1955. if (visible_content_rect.overlaps(item_rect))
  1956. {
  1957. // then attempt to keep it in same place on screen
  1958. mScrollConstraintRect = item_rect;
  1959. mScrollConstraintRect.translate(-visible_content_rect.mLeft, -visible_content_rect.mBottom);
  1960. }
  1961. else
  1962. {
  1963. // otherwise we just want it onscreen somewhere
  1964. LLRect content_rect = mScrollContainer->getContentWindowRect();
  1965. mScrollConstraintRect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight());
  1966. }
  1967. }
  1968. }
  1969. else
  1970. {
  1971. // stop pinning selected item after folders stop rearranging
  1972. if (!needsArrange())
  1973. {
  1974. mPinningSelectedItem = FALSE;
  1975. }
  1976. }
  1977. LLRect constraint_rect;
  1978. if (mPinningSelectedItem)
  1979. {
  1980. // use last known constraint rect for pinned item
  1981. constraint_rect = mScrollConstraintRect;
  1982. }
  1983. else
  1984. {
  1985. // during normal use (page up/page down, etc), just try to fit item on screen
  1986. LLRect content_rect = mScrollContainer->getContentWindowRect();
  1987. constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight());
  1988. }
  1989. BOOL is_visible = isInVisibleChain();
  1990. if ( is_visible )
  1991. {
  1992. sanitizeSelection();
  1993. if( needsArrange() )
  1994. {
  1995. arrangeFromRoot();
  1996. }
  1997. }
  1998. if (mSelectedItems.size() && mNeedsScroll)
  1999. {
  2000. scrollToShowItem(mSelectedItems.back(), constraint_rect);
  2001. // continue scrolling until animated layout change is done
  2002. if (!filter_modified_and_active
  2003. && (!needsArrange() || !is_visible))
  2004. {
  2005. mNeedsScroll = FALSE;
  2006. }
  2007. }
  2008. if (mSignalSelectCallback)
  2009. {
  2010. //RN: we use keyboard focus as a proxy for user-explicit actions
  2011. BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS);
  2012. mSelectSignal(mSelectedItems, take_keyboard_focus);
  2013. }
  2014. mSignalSelectCallback = FALSE;
  2015. }
  2016. //static
  2017. void LLFolderView::idle(void* user_data)
  2018. {
  2019. LLFolderView* self = (LLFolderView*)user_data;
  2020. if ( self )
  2021. { // Do the real idle
  2022. self->doIdle();
  2023. }
  2024. }
  2025. void LLFolderView::dumpSelectionInformation()
  2026. {
  2027. llinfos << "LLFolderView::dumpSelectionInformation()" << llendl;
  2028. llinfos << "****************************************" << llendl;
  2029. selected_items_t::iterator item_it;
  2030. for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
  2031. {
  2032. llinfos << " " << (*item_it)->getName() << llendl;
  2033. }
  2034. llinfos << "****************************************" << llendl;
  2035. }
  2036. void LLFolderView::updateRenamerPosition()
  2037. {
  2038. if(mRenameItem)
  2039. {
  2040. // See also LLFolderViewItem::draw()
  2041. S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mRenameItem->getIndentation();
  2042. S32 y = mRenameItem->getRect().getHeight() - mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD;
  2043. mRenameItem->localPointToScreen( x, y, &x, &y );
  2044. screenPointToLocal( x, y, &x, &y );
  2045. mRenamer->setOrigin( x, y );
  2046. LLRect scroller_rect(0, 0, gViewerWindow->getWindowWidthScaled(), 0);
  2047. if (mScrollContainer)
  2048. {
  2049. scroller_rect = mScrollContainer->getContentWindowRect();
  2050. }
  2051. S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_rect.getWidth() - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH);
  2052. S32 height = mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD;
  2053. mRenamer->reshape( width, height, TRUE );
  2054. }
  2055. }
  2056. // Update visibility and availability (i.e. enabled/disabled) of context menu items.
  2057. void LLFolderView::updateMenuOptions(LLMenuGL* menu)
  2058. {
  2059. const LLView::child_list_t *list = menu->getChildList();
  2060. LLView::child_list_t::const_iterator menu_itor;
  2061. for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor)
  2062. {
  2063. (*menu_itor)->setVisible(FALSE);
  2064. (*menu_itor)->pushVisible(TRUE);
  2065. (*menu_itor)->setEnabled(TRUE);
  2066. }
  2067. // Successively filter out invalid options
  2068. U32 flags = FIRST_SELECTED_ITEM;
  2069. for (selected_items_t::iterator item_itor = mSelectedItems.begin();
  2070. item_itor != mSelectedItems.end();
  2071. ++item_itor)
  2072. {
  2073. LLFolderViewItem* selected_item = (*item_itor);
  2074. selected_item->buildContextMenu(*menu, flags);
  2075. flags = 0x0;
  2076. }
  2077. addNoOptions(menu);
  2078. }
  2079. // Refresh the context menu (that is already shown).
  2080. void LLFolderView::updateMenu()
  2081. {
  2082. LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
  2083. if (menu && menu->getVisible())
  2084. {
  2085. updateMenuOptions(menu);
  2086. menu->needsArrange(); // update menu height if needed
  2087. }
  2088. }
  2089. bool LLFolderView::selectFirstItem()
  2090. {
  2091. for (folders_t::iterator iter = mFolders.begin();
  2092. iter != mFolders.end();++iter)
  2093. {
  2094. LLFolderViewFolder* folder = (*iter );
  2095. if (folder->getVisible())
  2096. {
  2097. LLFolderViewItem* itemp = folder->getNextFromChild(0,true);
  2098. if(itemp)
  2099. setSelection(itemp,FALSE,TRUE);
  2100. return true;
  2101. }
  2102. }
  2103. for(items_t::iterator iit = mItems.begin();
  2104. iit != mItems.end(); ++iit)
  2105. {
  2106. LLFolderViewItem* itemp = (*iit);
  2107. if (itemp->getVisible())
  2108. {
  2109. setSelection(itemp,FALSE,TRUE);
  2110. return true;
  2111. }
  2112. }
  2113. return false;
  2114. }
  2115. bool LLFolderView::selectLastItem()
  2116. {
  2117. for(items_t::reverse_iterator iit = mItems.rbegin();
  2118. iit != mItems.rend(); ++iit)
  2119. {
  2120. LLFolderViewItem* itemp = (*iit);
  2121. if (itemp->getVisible())
  2122. {
  2123. setSelection(itemp,FALSE,TRUE);
  2124. return true;
  2125. }
  2126. }
  2127. for (folders_t::reverse_iterator iter = mFolders.rbegin();
  2128. iter != mFolders.rend();++iter)
  2129. {
  2130. LLFolderViewFolder* folder = (*iter);
  2131. if (folder->getVisible())
  2132. {
  2133. LLFolderViewItem* itemp = folder->getPreviousFromChild(0,true);
  2134. if(itemp)
  2135. setSelection(itemp,FALSE,TRUE);
  2136. return true;
  2137. }
  2138. }
  2139. return false;
  2140. }
  2141. S32 LLFolderView::notify(const LLSD& info)
  2142. {
  2143. if(info.has("action"))
  2144. {
  2145. std::string str_action = info["action"];
  2146. if(str_action == "select_first")
  2147. {
  2148. setFocus(true);
  2149. selectFirstItem();
  2150. scrollToShowSelection();
  2151. return 1;
  2152. }
  2153. else if(str_action == "select_last")
  2154. {
  2155. setFocus(true);
  2156. selectLastItem();
  2157. scrollToShowSelection();
  2158. return 1;
  2159. }
  2160. }
  2161. return 0;
  2162. }
  2163. ///----------------------------------------------------------------------------
  2164. /// Local function definitions
  2165. ///----------------------------------------------------------------------------
  2166. void LLFolderView::onRenamerLost()
  2167. {
  2168. if (mRenamer && mRenamer->getVisible())
  2169. {
  2170. mRenamer->setVisible(FALSE);
  2171. // will commit current name (which could be same as original name)
  2172. mRenamer->setFocus(FALSE);
  2173. }
  2174. if( mRenameItem )
  2175. {
  2176. setSelectionFromRoot( mRenameItem, TRUE );
  2177. mRenameItem = NULL;
  2178. }
  2179. }
  2180. LLInventoryFilter* LLFolderView::getFilter()
  2181. {
  2182. return mFilter;
  2183. }
  2184. void LLFolderView::setFilterPermMask( PermissionMask filter_perm_mask )
  2185. {
  2186. mFilter->setFilterPermissions(filter_perm_mask);
  2187. }
  2188. U32 LLFolderView::getFilterObjectTypes() const
  2189. {
  2190. return mFilter