From f24f72d2f9db7490b169daea16f8ab8400ca12b4 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 6 Sep 2023 02:56:11 +0300 Subject: SL-19826 Gallery multiselect support Part#3 wip --- indra/newview/llinventoryfunctions.cpp | 5 +- indra/newview/llinventorygallery.cpp | 480 ++++++++++++++++----- indra/newview/llinventorygallery.h | 25 +- indra/newview/llinventorygallerymenu.cpp | 162 ++++--- indra/newview/llinventorygallerymenu.h | 6 +- .../default/xui/en/menu_gallery_inventory.xml | 26 +- 6 files changed, 509 insertions(+), 195 deletions(-) diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index f5f0d74f0f..4aeacae6ed 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -454,7 +454,10 @@ void copy_inventory_category(LLInventoryModel* model, inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items, callback](const LLUUID &new_id) { copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items); - callback(new_id); + if (callback) + { + callback(new_id); + } }; gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID()); } diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 8170c0c63b..eab15b79ad 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -107,7 +107,6 @@ LLInventoryGallery::LLInventoryGallery(const LLInventoryGallery::Params& p) mIsInitialized(false), mRootDirty(false), mNeedsArrange(false), - mNeedsSelection(false), mSearchType(LLInventoryFilter::SEARCHTYPE_NAME), mSortOrder(LLInventoryFilter::SO_DATE) { @@ -219,10 +218,10 @@ void LLInventoryGallery::setRootFolder(const LLUUID cat_id) gIdleCallbacks.deleteFunction(onIdle, (void*)this); mFolderID = cat_id; - mItemToSelect.setNull(); + mItemsToSelect.clear(); + mSelectedItemIDs.clear(); mItemBuildQuery.clear(); mNeedsArrange = false; - mNeedsSelection = false; dirtyRootFolder(); } @@ -831,15 +830,17 @@ void LLInventoryGallery::onIdle(void* userdata) self->updateMessageVisibility(); } - if (self->mNeedsSelection) + if (!self->mItemsToSelect.empty() && !self->mNeedsArrange) { - LLUUID item_to_select = self->mItemToSelect; - self->mItemToSelect = LLUUID::null; - self->mNeedsSelection = false; - self->changeItemSelection(item_to_select, true); + selection_deque selection_list(self->mItemsToSelect); + self->mItemsToSelect.clear(); + for (LLUUID & item_to_select : selection_list) + { + self->addItemSelection(item_to_select, true); + } } - if (!self->mNeedsSelection && self->mItemBuildQuery.empty()) + if (self->mItemsToSelect.empty() && self->mItemBuildQuery.empty()) { gIdleCallbacks.deleteFunction(onIdle, (void*)self); } @@ -911,7 +912,6 @@ bool LLInventoryGallery::updateAddedItem(LLUUID item_id) LLInventoryGalleryItem* item = buildGalleryItem(name, item_id, obj->getType(), thumbnail_id, inventory_type, misc_flags, obj->getCreationDate(), obj->getIsLinkType(), is_worn); mItemMap.insert(LLInventoryGallery::gallery_item_map_t::value_type(item_id, item)); - item->setFocusReceivedCallback(boost::bind(&LLInventoryGallery::changeItemSelection, this, item_id, false)); if (mGalleryCreated) { res = applyFilter(item, mFilterSubString); @@ -1074,8 +1074,7 @@ BOOL LLInventoryGallery::handleKeyHere(KEY key, MASK mask) mFilterSubString.clear(); if (mInventoryGalleryMenu && mSelectedItemIDs.size() == 1) { - selection_deque::iterator iter = mSelectedItemIDs.begin(); - mInventoryGalleryMenu->doToSelected("rename", *iter); + mInventoryGalleryMenu->rename(mSelectedItemIDs.front()); } handled = TRUE; break; @@ -1117,22 +1116,22 @@ BOOL LLInventoryGallery::handleKeyHere(KEY key, MASK mask) break; case KEY_LEFT: - moveLeft(); + moveLeft(mask); handled = TRUE; break; case KEY_RIGHT: - moveRight(); + moveRight(mask); handled = TRUE; break; case KEY_UP: - moveUp(); + moveUp(mask); handled = TRUE; break; case KEY_DOWN: - moveDown(); + moveDown(mask); handled = TRUE; break; @@ -1148,59 +1147,107 @@ BOOL LLInventoryGallery::handleKeyHere(KEY key, MASK mask) return handled; } -void LLInventoryGallery::moveUp() +void LLInventoryGallery::moveUp(MASK mask) { mFilterSubString.clear(); if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) { - LLInventoryGalleryItem* item = getFirstSelectedItem(); + LLInventoryGalleryItem* item = mItemMap[mLastSelectedUUID]; if (item) { - S32 n = mItemIndexMap[item]; - n -= mItemsInRow; - if (n >= 0) + if (mask == MASK_NONE || mask == MASK_CONTROL) { - item = mIndexToItemMap[n]; - LLUUID item_id = item->getUUID(); - changeItemSelection(item_id, true); - item->setFocus(TRUE); - claimEditHandler(); + S32 n = mItemIndexMap[item]; + n -= mItemsInRow; + if (n >= 0) + { + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + if (mask == MASK_CONTROL) + { + addItemSelection(item_id, true); + } + else + { + changeItemSelection(item_id, true); + } + item->setFocus(TRUE); + claimEditHandler(); + } + } + else if (mask == MASK_SHIFT) + { + S32 n = mItemIndexMap[item]; + S32 target = llmax(0, n - mItemsInRow); + toggleSelectionRange(target, n - 1); + if (target != n) + { + item = mIndexToItemMap[target]; + item->setFocus(TRUE); + claimEditHandler(); + } } } } } -void LLInventoryGallery::moveDown() +void LLInventoryGallery::moveDown(MASK mask) { mFilterSubString.clear(); if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) { - LLInventoryGalleryItem* item = getFirstSelectedItem(); + LLInventoryGalleryItem* item = mItemMap[mLastSelectedUUID]; if (item) { - S32 n = mItemIndexMap[item]; - n += mItemsInRow; - if (n < mItemsAddedCount) + if (mask == MASK_NONE || mask == MASK_CONTROL) { - item = mIndexToItemMap[n]; - LLUUID item_id = item->getUUID(); - changeItemSelection(item_id, true); - item->setFocus(TRUE); - claimEditHandler(); + S32 n = mItemIndexMap[item]; + n += mItemsInRow; + if (n < mItemsAddedCount) + { + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + if (mask == MASK_CONTROL) + { + addItemSelection(item_id, true); + } + else + { + changeItemSelection(item_id, true); + } + item->setFocus(TRUE); + claimEditHandler(); + } + } + else if (mask == MASK_SHIFT) + { + S32 n = mItemIndexMap[item]; + S32 target = llmin(mItemsAddedCount - 1, n + mItemsInRow); + toggleSelectionRange(n + 1, target); + if (target != n) + { + item = mIndexToItemMap[target]; + item->setFocus(TRUE); + claimEditHandler(); + } } } } } -void LLInventoryGallery::moveLeft() +void LLInventoryGallery::moveLeft(MASK mask) { mFilterSubString.clear(); if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) { - LLInventoryGalleryItem* item = getFirstSelectedItem(); + LLInventoryGalleryItem* item = mItemMap[mLastSelectedUUID]; + if (mask == MASK_SHIFT) + { + item = mItemMap[mLastSelectedUUID]; + } if (item) { // Might be better to get item from panel @@ -1212,20 +1259,31 @@ void LLInventoryGallery::moveLeft() } item = mIndexToItemMap[n]; LLUUID item_id = item->getUUID(); - changeItemSelection(item_id, true); + if (mask == MASK_CONTROL) + { + addItemSelection(item_id, true); + } + else if (mask == MASK_SHIFT) + { + toggleItemSelection(item_id, true); + } + else + { + changeItemSelection(item_id, true); + } item->setFocus(TRUE); claimEditHandler(); } } } -void LLInventoryGallery::moveRight() +void LLInventoryGallery::moveRight(MASK mask) { mFilterSubString.clear(); if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) { - LLInventoryGalleryItem* item = getFirstSelectedItem(); + LLInventoryGalleryItem* item = mItemMap[mLastSelectedUUID]; if (item) { S32 n = mItemIndexMap[item]; @@ -1236,13 +1294,58 @@ void LLInventoryGallery::moveRight() } item = mIndexToItemMap[n]; LLUUID item_id = item->getUUID(); - changeItemSelection(item_id, true); + if (mask == MASK_CONTROL) + { + addItemSelection(item_id, true); + } + else if (mask == MASK_SHIFT) + { + toggleItemSelection(item_id, true); + } + else + { + changeItemSelection(item_id, true); + } item->setFocus(TRUE); claimEditHandler(); } } } +void LLInventoryGallery::toggleSelectionRange(S32 start_idx, S32 end_idx) +{ + LLInventoryGalleryItem* item = NULL; + for (S32 i = start_idx; i <= end_idx; i++) + { + item = mIndexToItemMap[i]; + LLUUID item_id = item->getUUID(); + toggleItemSelection(item_id, true); + } +} + +void LLInventoryGallery::toggleSelectionRangeFromLast(const LLUUID target) +{ + if (mLastSelectedUUID == target) + { + return; + } + LLInventoryGalleryItem* last_item = mItemMap[mLastSelectedUUID]; + LLInventoryGalleryItem* next_item = mItemMap[target]; + if (last_item && next_item) + { + S32 last_idx = mItemIndexMap[last_item]; + S32 next_idx = mItemIndexMap[next_item]; + if (last_idx < next_idx) + { + toggleSelectionRange(last_idx + 1, next_idx); + } + else + { + toggleSelectionRange(next_idx, last_idx - 1); + } + } +} + void LLInventoryGallery::onFocusLost() { // inventory no longer handles cut/copy/paste/delete @@ -1270,17 +1373,21 @@ void LLInventoryGallery::onFocusReceived() // Tab support, when tabbing into this view, select first item if (mSelectedItemIDs.size() > 0) { + LLInventoryGalleryItem* focus_item = NULL; for (const LLUUID& id : mSelectedItemIDs) { if (mItemMap[id]) { - LLInventoryGalleryItem* focus_item = mItemMap[id]; + focus_item = mItemMap[id]; focus_item->setSelected(true); - focus_item->setFocus(TRUE); } } + if (focus_item) + { + focus_item->setFocus(TRUE); + } } - else if (mIndexToItemMap.size() > 0 && !mNeedsSelection) + else if (mIndexToItemMap.size() > 0 && mItemsToSelect.empty()) { // choose any items from visible rect S32 vert_offset = mScrollPanel->getDocPosVertical(); @@ -1304,18 +1411,26 @@ void LLInventoryGallery::showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLU mSelectedItemIDs.clear(); changeItemSelection(item_id, false); } - uuid_vec_t selected_uuids; - selected_uuids.push_back(item_id); + uuid_vec_t selected_uuids(mSelectedItemIDs.begin(), mSelectedItemIDs.end()); mInventoryGalleryMenu->show(ctrl, selected_uuids, x, y); } } void LLInventoryGallery::changeItemSelection(const LLUUID& item_id, bool scroll_to_selection) { + for (const LLUUID& id : mSelectedItemIDs) + { + if (mItemMap[id]) + { + mItemMap[id]->setSelected(FALSE); + } + } + mSelectedItemIDs.clear(); + if ((mItemMap.count(item_id) == 0) || mNeedsArrange) { - mItemToSelect = item_id; - mNeedsSelection = true; + mItemsToSelect.clear(); + mItemsToSelect.push_back(item_id); return; } if (mSelectedItemIDs.size() == 1 @@ -1325,14 +1440,32 @@ void LLInventoryGallery::changeItemSelection(const LLUUID& item_id, bool scroll_ return; } - for (const LLUUID& id : mSelectedItemIDs) + if (mItemMap[item_id]) { - if (mItemMap[id]) - { - mItemMap[id]->setSelected(FALSE); - } + mItemMap[item_id]->setSelected(TRUE); + } + mSelectedItemIDs.push_back(item_id); + signalSelectionItemID(item_id); + mLastSelectedUUID = item_id; + + if (scroll_to_selection) + { + scrollToShowItem(item_id); + } +} + +void LLInventoryGallery::addItemSelection(const LLUUID& item_id, bool scroll_to_selection) +{ + if ((mItemMap.count(item_id) == 0) || mNeedsArrange) + { + mItemsToSelect.push_back(item_id); + return; + } + if (std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) != mSelectedItemIDs.end()) + { + // Already selected + return; } - mSelectedItemIDs.clear(); if (mItemMap[item_id]) { @@ -1340,6 +1473,7 @@ void LLInventoryGallery::changeItemSelection(const LLUUID& item_id, bool scroll_ } mSelectedItemIDs.push_back(item_id); signalSelectionItemID(item_id); + mLastSelectedUUID = item_id; if (scroll_to_selection) { @@ -1347,6 +1481,43 @@ void LLInventoryGallery::changeItemSelection(const LLUUID& item_id, bool scroll_ } } +bool LLInventoryGallery::toggleItemSelection(const LLUUID& item_id, bool scroll_to_selection) +{ + bool result = false; + if ((mItemMap.count(item_id) == 0) || mNeedsArrange) + { + mItemsToSelect.push_back(item_id); + return result; + } + selection_deque::iterator found = std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id); + if (found != mSelectedItemIDs.end()) + { + if (mItemMap[item_id]) + { + mItemMap[item_id]->setSelected(FALSE); + } + mSelectedItemIDs.erase(found); + result = false; + } + else + { + if (mItemMap[item_id]) + { + mItemMap[item_id]->setSelected(TRUE); + } + mSelectedItemIDs.push_back(item_id); + signalSelectionItemID(item_id); + mLastSelectedUUID = item_id; + result = true; + } + + if (scroll_to_selection) + { + scrollToShowItem(item_id); + } + return result; +} + void LLInventoryGallery::scrollToShowItem(const LLUUID& item_id) { LLInventoryGalleryItem* item = mItemMap[item_id]; @@ -1476,32 +1647,63 @@ void LLInventoryGallery::paste() return; } - LLUUID first_selected_id; - if (mSelectedItemIDs.size() > 0) - { - first_selected_id = *mSelectedItemIDs.begin(); - } - - LLInventoryObject* obj = gInventory.getObject(first_selected_id); - bool is_folder = obj && (obj->getType() == LLAssetType::AT_CATEGORY); - LLUUID dest = is_folder ? first_selected_id : mFolderID; bool is_cut_mode = LLClipboard::instance().isCutMode(); - std::vector objects; LLClipboard::instance().pasteFromClipboard(objects); - LLHandle handle = getHandle(); - std::function on_copy_callback = [handle](const LLUUID& inv_item) + bool paste_into_root = mSelectedItemIDs.empty(); + for (LLUUID& dest : mSelectedItemIDs) { - LLInventoryGallery* panel = (LLInventoryGallery*)handle.get(); - if (panel) + LLInventoryObject* obj = gInventory.getObject(dest); + if (!obj || (obj->getType() != LLAssetType::AT_CATEGORY)) { - // Scroll to pasted item and highlight it - // Should it only highlight the last one? - panel->changeItemSelection(inv_item, true); + paste_into_root = true; + continue; } - }; - LLPointer cb = new LLBoostFuncInventoryCallback(on_copy_callback); + + paste(dest, objects, is_cut_mode, marketplacelistings_id); + is_cut_mode = false; + } + + if (paste_into_root) + { + for (const LLUUID& id : mSelectedItemIDs) + { + if (mItemMap[id]) + { + mItemMap[id]->setSelected(FALSE); + } + } + mSelectedItemIDs.clear(); + + paste(mFolderID, objects, is_cut_mode, marketplacelistings_id); + } + + LLClipboard::instance().setCutMode(false); +} + +void LLInventoryGallery::paste(const LLUUID& dest, + std::vector& objects, + bool is_cut_mode, + const LLUUID& marketplacelistings_id) +{ + LLHandle handle = getHandle(); + std::function on_copy_callback = NULL; + LLPointer cb = NULL; + if (dest == mFolderID) + { + on_copy_callback = [handle](const LLUUID& inv_item) + { + LLInventoryGallery* panel = (LLInventoryGallery*)handle.get(); + if (panel) + { + // Scroll to pasted item and highlight it + // Should it only highlight the last one? + panel->addItemSelection(inv_item, true); + } + }; + cb = new LLBoostFuncInventoryCallback(on_copy_callback); + } for (std::vector::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) { @@ -1517,9 +1719,11 @@ void LLInventoryGallery::paste() if (is_cut_mode) { gInventory.changeCategoryParent(cat, dest, false); - // Don't select immediately, wait for item to arrive - mItemToSelect = item_id; - mNeedsSelection = true; + if (dest == mFolderID) + { + // Don't select immediately, wait for item to arrive + mItemsToSelect.push_back(item_id); + } } else { @@ -1534,9 +1738,11 @@ void LLInventoryGallery::paste() if (is_cut_mode) { gInventory.changeItemParent(item, dest, false); - // Don't select immediately, wait for item to arrive - mItemToSelect = item_id; - mNeedsSelection = true; + if (dest == mFolderID) + { + // Don't select immediately, wait for item to arrive + mItemsToSelect.push_back(item_id); + } } else { @@ -1679,17 +1885,45 @@ void LLInventoryGallery::pasteAsLink() const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); const LLUUID& my_outifts_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - LLUUID dest; - if (mSelectedItemIDs.size() > 0) + std::vector objects; + LLClipboard::instance().pasteFromClipboard(objects); + + bool paste_into_root = mSelectedItemIDs.empty(); + for (LLUUID& dest : mSelectedItemIDs) { - dest = *mSelectedItemIDs.begin(); + LLInventoryObject* obj = gInventory.getObject(dest); + if (!obj || obj->getType() != LLAssetType::AT_CATEGORY) + { + paste_into_root = true; + continue; + } + + pasteAsLink(dest, objects, current_outfit_id, marketplacelistings_id, my_outifts_id); } - LLInventoryObject* obj = gInventory.getObject(dest); - if (!obj || obj->getType() != LLAssetType::AT_CATEGORY) + + if (paste_into_root) { - dest = mFolderID; + for (const LLUUID& id : mSelectedItemIDs) + { + if (mItemMap[id]) + { + mItemMap[id]->setSelected(FALSE); + } + } + mSelectedItemIDs.clear(); + + pasteAsLink(mFolderID, objects, current_outfit_id, marketplacelistings_id, my_outifts_id); } + LLClipboard::instance().setCutMode(false); +} + +void LLInventoryGallery::pasteAsLink(const LLUUID& dest, + std::vector& objects, + const LLUUID& current_outfit_id, + const LLUUID& marketplacelistings_id, + const LLUUID& my_outifts_id) +{ const BOOL move_is_into_current_outfit = (dest == current_outfit_id); const BOOL move_is_into_my_outfits = (dest == my_outifts_id) || gInventory.isObjectDescendentOf(dest, my_outifts_id); const BOOL move_is_into_marketplacelistings = gInventory.isObjectDescendentOf(dest, marketplacelistings_id); @@ -1698,21 +1932,23 @@ void LLInventoryGallery::pasteAsLink() { return; } - std::vector objects; - LLClipboard::instance().pasteFromClipboard(objects); - LLHandle handle = getHandle(); - std::function on_link_callback = [handle](const LLUUID& inv_item) + LLPointer cb = NULL; + if (dest == mFolderID) { - LLInventoryGallery *panel = (LLInventoryGallery*)handle.get(); - if (panel) - { - // Scroll to pasted item and highlight it - // Should it only highlight the last one? - panel->changeItemSelection(inv_item, true); - } - }; - LLPointer cb = new LLBoostFuncInventoryCallback(on_link_callback); + LLHandle handle = getHandle(); + std::function on_link_callback = [handle](const LLUUID& inv_item) + { + LLInventoryGallery* panel = (LLInventoryGallery*)handle.get(); + if (panel) + { + // Scroll to pasted item and highlight it + // Should it only highlight the last one? + panel->addItemSelection(inv_item, true); + } + }; + cb = new LLBoostFuncInventoryCallback(on_link_callback); + } for (std::vector::const_iterator iter = objects.begin(); iter != objects.end(); @@ -1724,8 +1960,6 @@ void LLInventoryGallery::pasteAsLink() link_inventory_object(dest, link_obj, cb); } } - - LLClipboard::instance().setCutMode(false); } void LLInventoryGallery::claimEditHandler() @@ -1852,7 +2086,7 @@ void LLInventoryGallery::refreshList(const LLUUID& category_id) mNeedsArrange = true; } - if(mNeedsArrange || mItemToSelect.notNull()) + if(mNeedsArrange || !mItemsToSelect.empty()) { // Don't scroll to target/arrange immediately // since more updates might be pending @@ -1969,7 +2203,7 @@ void LLInventoryGallery::deselectItem(const LLUUID& category_id) mItemMap[category_id]->setSelected(FALSE); setFocus(true); // Todo: support multiselect - signalSelectionItemID(LLUUID::null); + // signalSelectionItemID(LLUUID::null); } selection_deque::iterator found = std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), category_id); @@ -1991,6 +2225,7 @@ void LLInventoryGallery::clearSelection() if (!mSelectedItemIDs.empty()) { mSelectedItemIDs.clear(); + // BUG: wrong, item can be null signalSelectionItemID(LLUUID::null); } } @@ -2350,8 +2585,6 @@ void LLInventoryGalleryItem::draw() border.mTop = border.mTop + 1; gl_rect_2d(border, border_color.get(), FALSE); } - - } void LLInventoryGalleryItem::setItemName(std::string name) @@ -2379,7 +2612,18 @@ BOOL LLInventoryGalleryItem::handleMouseDown(S32 x, S32 y, MASK mask) { // call changeItemSelection directly, before setFocus // to avoid autoscroll from LLInventoryGallery::onFocusReceived() - mGallery->changeItemSelection(mUUID, false); + if (mask == MASK_CONTROL) + { + mGallery->addItemSelection(mUUID, false); + } + else if (mask == MASK_SHIFT) + { + mGallery->toggleSelectionRangeFromLast(mUUID); + } + else + { + mGallery->changeItemSelection(mUUID, false); + } setFocus(TRUE); mGallery->claimEditHandler(); @@ -2496,22 +2740,22 @@ BOOL LLInventoryGalleryItem::handleKeyHere(KEY key, MASK mask) switch (key) { case KEY_LEFT: - mGallery->moveLeft(); + mGallery->moveLeft(mask); handled = true; break; case KEY_RIGHT: - mGallery->moveRight(); + mGallery->moveRight(mask); handled = true; break; case KEY_UP: - mGallery->moveUp(); + mGallery->moveUp(mask); handled = true; break; case KEY_DOWN: - mGallery->moveDown(); + mGallery->moveDown(mask); handled = true; break; @@ -2525,7 +2769,6 @@ void LLInventoryGalleryItem::onFocusLost() { // inventory no longer handles cut/copy/paste/delete mGallery->resetEditHandler(); - setSelected(false); LLPanel::onFocusLost(); } @@ -2534,7 +2777,6 @@ void LLInventoryGalleryItem::onFocusReceived() { // inventory now handles cut/copy/paste/delete mGallery->claimEditHandler(); - setSelected(true); LLPanel::onFocusReceived(); } @@ -2649,6 +2891,11 @@ BOOL LLInventoryGallery::baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, { LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + if (drop && LLToolDragAndDrop::getInstance()->getCargoIndex() == 0) + { + clearSelection(); + } + BOOL accepted = FALSE; switch(cargo_type) { @@ -2669,8 +2916,7 @@ BOOL LLInventoryGallery::baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, if (accepted && drop) { // Don't select immediately, wait for item to arrive - mItemToSelect = inv_item->getUUID(); - mNeedsSelection = true; + mItemsToSelect.push_back(inv_item->getUUID()); } break; case DAD_LINK: @@ -2691,8 +2937,7 @@ BOOL LLInventoryGallery::baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, } if (accepted && drop && inv_item) { - mItemToSelect = inv_item->getUUID(); - mNeedsSelection = true; + mItemsToSelect.push_back(inv_item->getUUID()); } break; case DAD_CATEGORY: @@ -2706,8 +2951,7 @@ BOOL LLInventoryGallery::baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, accepted = dragCategoryIntoFolder(dest_id, cat_ptr, drop, tooltip_msg, FALSE); if (accepted && drop) { - mItemToSelect = cat_ptr->getUUID(); - mNeedsSelection = true; + mItemsToSelect.push_back(cat_ptr->getUUID()); } } break; diff --git a/indra/newview/llinventorygallery.h b/indra/newview/llinventorygallery.h index f2e5e38940..44e0dcf960 100644 --- a/indra/newview/llinventorygallery.h +++ b/indra/newview/llinventorygallery.h @@ -82,10 +82,12 @@ public: void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) override; BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override; BOOL handleKeyHere(KEY key, MASK mask) override; - void moveUp(); - void moveDown(); - void moveLeft(); - void moveRight(); + void moveUp(MASK mask); + void moveDown(MASK mask); + void moveLeft(MASK mask); + void moveRight(MASK mask); + void toggleSelectionRange(S32 start_idx, S32 end_idx); + void toggleSelectionRangeFromLast(const LLUUID target); void onFocusLost() override; void onFocusReceived() override; @@ -130,6 +132,8 @@ public: void deselectItem(const LLUUID& category_id); void clearSelection(); void changeItemSelection(const LLUUID& item_id, bool scroll_to_selection = false); + void addItemSelection(const LLUUID& item_id, bool scroll_to_selection = false); + bool toggleItemSelection(const LLUUID& item_id, bool scroll_to_selection = false); void scrollToShowItem(const LLUUID& item_id); void signalSelectionItemID(const LLUUID& category_id); boost::signals2::connection setSelectionChangeCallback(selection_change_callback_t cb); @@ -174,6 +178,15 @@ public: void showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id); protected: + void paste(const LLUUID& dest, + std::vector& objects, + bool is_cut_mode, + const LLUUID& marketplacelistings_id); + void pasteAsLink(const LLUUID& dest, + std::vector& objects, + const LLUUID& current_outfit_id, + const LLUUID& marketplacelistings_id, + const LLUUID& my_outifts_id); bool applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring); bool checkAgainstFilters(LLInventoryGalleryItem* item, const std::string& filter_substring); @@ -185,8 +198,8 @@ protected: LLGalleryGestureObserver* mGestureObserver; LLInventoryObserver* mInventoryObserver; selection_deque mSelectedItemIDs; - LLUUID mItemToSelect; - bool mNeedsSelection; + selection_deque mItemsToSelect; + LLUUID mLastSelectedUUID; bool mIsInitialized; bool mRootDirty; diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index bca10b9c0e..5f4b816b99 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -51,16 +51,17 @@ LLContextMenu* LLInventoryGalleryContextMenu::createMenu() { LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - //LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - LLUUID selected_id = mUUIDs.front(); + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - registrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryGalleryContextMenu::doToSelected, this, _2, selected_id)); - registrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::fileUploadLocation, this, _2, selected_id)); + registrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryGalleryContextMenu::doToSelected, this, _2)); + registrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::fileUploadLocation, this, _2)); registrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH)); registrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND)); - std::set uuids{selected_id}; + std::set uuids(mUUIDs.begin(), mUUIDs.end()); registrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, uuids, gFloaterView->getParentFloater(mGallery))); + + enable_registrar.add("Inventory.CanSetUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::canSetUploadLocation, this, _2)); LLContextMenu* menu = createFromFile("menu_gallery_inventory.xml"); @@ -69,49 +70,52 @@ LLContextMenu* LLInventoryGalleryContextMenu::createMenu() return menu; } -void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLUUID& selected_id) +void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata) { std::string action = userdata.asString(); - LLInventoryObject* obj = gInventory.getObject(selected_id); + LLInventoryObject* obj = gInventory.getObject(mUUIDs.front()); if(!obj) return; if ("open_selected_folder" == action) { - mGallery->setRootFolder(selected_id); + mGallery->setRootFolder(mUUIDs.front()); } else if ("open_in_new_window" == action) { - new_folder_window(selected_id); + new_folder_window(mUUIDs.front()); } else if ("properties" == action) { - show_item_profile(selected_id); + show_item_profile(mUUIDs.front()); } else if ("restore" == action) { - LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id); - if(cat) + for (LLUUID& selected_id : mUUIDs) { - const LLUUID new_parent = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType())); - // do not restamp children on restore - gInventory.changeCategoryParent(cat, new_parent, false); - } - else - { - LLViewerInventoryItem* item = gInventory.getItem(selected_id); - if(item) + LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id); + if (cat) { - bool is_snapshot = (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT); - - const LLUUID new_parent = gInventory.findCategoryUUIDForType(is_snapshot? LLFolderType::FT_SNAPSHOT_CATEGORY : LLFolderType::assetTypeToFolderType(item->getType())); + const LLUUID new_parent = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType())); // do not restamp children on restore - gInventory.changeItemParent(item, new_parent, false); + gInventory.changeCategoryParent(cat, new_parent, false); + } + else + { + LLViewerInventoryItem* item = gInventory.getItem(selected_id); + if (item) + { + bool is_snapshot = (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT); + + const LLUUID new_parent = gInventory.findCategoryUUIDForType(is_snapshot ? LLFolderType::FT_SNAPSHOT_CATEGORY : LLFolderType::assetTypeToFolderType(item->getType())); + // do not restamp children on restore + gInventory.changeItemParent(item, new_parent, false); + } } } } else if ("copy_uuid" == action) { - LLViewerInventoryItem* item = gInventory.getItem(selected_id); + LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front()); if(item) { LLUUID asset_id = item->getProtectedAssetUUID(); @@ -123,15 +127,18 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU } else if ("purge" == action) { - remove_inventory_object(selected_id, NULL); + for (LLUUID& selected_id : mUUIDs) + { + remove_inventory_object(selected_id, NULL); + } } else if ("goto" == action) { - show_item_original(selected_id); + show_item_original(mUUIDs.front()); } else if ("thumbnail" == action) { - LLSD data(selected_id); + LLSD data(mUUIDs.front()); LLFloaterReg::showInstance("change_item_thumbnail", data); } else if ("cut" == action) @@ -165,61 +172,70 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU } else if ("rename" == action) { - LLSD args; - args["NAME"] = obj->getName(); - - LLSD payload; - payload["id"] = selected_id; - - LLNotificationsUtil::add("RenameItem", args, payload, boost::bind(onRename, _1, _2)); + rename(mUUIDs.front()); } else if ("open" == action || "open_original" == action) { - LLViewerInventoryItem* item = gInventory.getItem(selected_id); + LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front()); if (item) { - LLInvFVBridgeAction::doAction(item->getType(), selected_id , &gInventory); + LLInvFVBridgeAction::doAction(item->getType(), mUUIDs.front(), &gInventory); } } else if ("ungroup_folder_items" == action) { - ungroup_folder_items(selected_id); + ungroup_folder_items(mUUIDs.front()); } else if ("take_off" == action || "detach" == action) { - LLAppearanceMgr::instance().removeItemFromAvatar(selected_id); + for (LLUUID& selected_id : mUUIDs) + { + LLAppearanceMgr::instance().removeItemFromAvatar(selected_id); + } } else if ("wear_add" == action) { - LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, false); // Don't replace if adding. + for (LLUUID& selected_id : mUUIDs) + { + LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, false); // Don't replace if adding. + } } else if ("wear" == action) { - LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, true); + for (LLUUID& selected_id : mUUIDs) + { + LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, true); + } } else if ("activate" == action) { - LLGestureMgr::instance().activateGesture(selected_id); + for (LLUUID& selected_id : mUUIDs) + { + LLGestureMgr::instance().activateGesture(selected_id); - LLViewerInventoryItem* item = gInventory.getItem(selected_id); - if (!item) return; + LLViewerInventoryItem* item = gInventory.getItem(selected_id); + if (!item) return; - gInventory.updateItem(item); + gInventory.updateItem(item); + } gInventory.notifyObservers(); } else if ("deactivate" == action) { - LLGestureMgr::instance().deactivateGesture(selected_id); + for (LLUUID& selected_id : mUUIDs) + { + LLGestureMgr::instance().deactivateGesture(selected_id); - LLViewerInventoryItem* item = gInventory.getItem(selected_id); - if (!item) return; + LLViewerInventoryItem* item = gInventory.getItem(selected_id); + if (!item) return; - gInventory.updateItem(item); + gInventory.updateItem(item); + } gInventory.notifyObservers(); } else if ("replace_links" == action) { - LLFloaterReg::showInstance("linkreplace", LLSD(selected_id)); + LLFloaterReg::showInstance("linkreplace", LLSD(mUUIDs.front())); } else if ("copy_slurl" == action) { @@ -236,7 +252,7 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU }; LLLandmarkActions::getSLURLfromPosGlobal(global_pos, copy_slurl_to_clipboard_cb, true); }; - LLLandmark* landmark = LLLandmarkActions::getLandmark(selected_id, copy_slurl_cb); + LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), copy_slurl_cb); if (landmark) { copy_slurl_cb(landmark); @@ -246,7 +262,7 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU { LLSD key; key["type"] = "landmark"; - key["id"] = selected_id; + key["id"] = mUUIDs.front(); LLFloaterSidePanelContainer::showPanel("places", key); } else if ("show_on_map" == action) @@ -264,7 +280,7 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU } } }; - LLLandmark* landmark = LLLandmarkActions::getLandmark(selected_id, show_on_map_cb); + LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), show_on_map_cb); if(landmark) { show_on_map_cb(landmark); @@ -272,7 +288,7 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU } else if ("save_as" == action) { - LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance("preview_texture", selected_id); + LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance("preview_texture", mUUIDs.front()); if (preview_texture) { preview_texture->openToSave(); @@ -281,6 +297,20 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU } } +void LLInventoryGalleryContextMenu::rename(const LLUUID& item_id) +{ + LLInventoryObject* obj = gInventory.getObject(item_id); + if (!obj) return; + + LLSD args; + args["NAME"] = obj->getName(); + + LLSD payload; + payload["id"] = mUUIDs.front(); + + LLNotificationsUtil::add("RenameItem", args, payload, boost::bind(onRename, _1, _2)); +} + void LLInventoryGalleryContextMenu::onRename(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -311,25 +341,39 @@ void LLInventoryGalleryContextMenu::onRename(const LLSD& notification, const LLS } } -void LLInventoryGalleryContextMenu::fileUploadLocation(const LLSD& userdata, const LLUUID& selected_id) +void LLInventoryGalleryContextMenu::fileUploadLocation(const LLSD& userdata) { const std::string param = userdata.asString(); if (param == "model") { - gSavedPerAccountSettings.setString("ModelUploadFolder", selected_id.asString()); + gSavedPerAccountSettings.setString("ModelUploadFolder", mUUIDs.front().asString()); } else if (param == "texture") { - gSavedPerAccountSettings.setString("TextureUploadFolder", selected_id.asString()); + gSavedPerAccountSettings.setString("TextureUploadFolder", mUUIDs.front().asString()); } else if (param == "sound") { - gSavedPerAccountSettings.setString("SoundUploadFolder", selected_id.asString()); + gSavedPerAccountSettings.setString("SoundUploadFolder", mUUIDs.front().asString()); } else if (param == "animation") { - gSavedPerAccountSettings.setString("AnimationUploadFolder", selected_id.asString()); + gSavedPerAccountSettings.setString("AnimationUploadFolder", mUUIDs.front().asString()); + } +} + +bool LLInventoryGalleryContextMenu::canSetUploadLocation(const LLSD& userdata) +{ + if (mUUIDs.size() != 1) + { + return false; + } + LLInventoryCategory* cat = gInventory.getCategory(mUUIDs.front()); + if (!cat) + { + return false; } + return true; } bool is_inbox_folder(LLUUID item_id) diff --git a/indra/newview/llinventorygallerymenu.h b/indra/newview/llinventorygallerymenu.h index 67cf9a569a..7c3545432b 100644 --- a/indra/newview/llinventorygallerymenu.h +++ b/indra/newview/llinventorygallerymenu.h @@ -39,13 +39,15 @@ public: bool isRootFolder() { return mRootFolder; } void setRootFolder(bool is_root) { mRootFolder = is_root; } - void doToSelected(const LLSD& userdata, const LLUUID& selected_id); + void doToSelected(const LLSD& userdata); + void rename(const LLUUID& item_id); protected: //virtual void buildContextMenu(class LLMenuGL& menu, U32 flags); void updateMenuItemsVisibility(LLContextMenu* menu); - void fileUploadLocation(const LLSD& userdata, const LLUUID& selected_id); + void fileUploadLocation(const LLSD& userdata); + bool canSetUploadLocation(const LLSD& userdata); static void onRename(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml index 46afa3821b..d82c453e5f 100644 --- a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml @@ -453,30 +453,38 @@ + - + + - + + - + +