PageRenderTime 115ms CodeModel.GetById 21ms RepoModel.GetById 1ms 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

Large files files are truncated, but you can click here to view the full file

  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

Large files files are truncated, but you can click here to view the full file