From a62f5bfac1e55455c67e1c36eb388af99795dab6 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 14 Oct 2023 02:40:59 +0300 Subject: SL-20232 Allow deletion of worn items #1 --- indra/newview/llinventorygallerymenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llinventorygallerymenu.cpp') diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index 5f4b816b99..50eefc7d25 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -577,7 +577,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men { items.push_back(std::string("Delete")); } - if(!get_is_item_removable(&gInventory, selected_id)) + if(!get_is_item_removable(&gInventory, selected_id, true)) { disabled_items.push_back(std::string("Delete")); disabled_items.push_back(std::string("Cut")); -- cgit v1.3 From 813b0e7675f0f9fb682831bdf60319f8b584b882 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 14 Oct 2023 12:14:51 +0300 Subject: SL-20232 Allow deletion of worn items #2 --- indra/newview/llinventoryfunctions.cpp | 7 ++-- indra/newview/llinventorygallery.cpp | 64 +++++++++++++++++++++++++++++--- indra/newview/llinventorygallerymenu.cpp | 7 +++- 3 files changed, 68 insertions(+), 10 deletions(-) (limited to 'indra/newview/llinventorygallerymenu.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 109dffae6b..5e702ef755 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -3024,7 +3024,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); bool marketplacelistings_item = false; - bool worn_item = false; + bool has_worn = false; bool needs_replacement = false; LLAllDescendentsPassedFilter f; for (std::set::iterator it = selected_items.begin(); (it != selected_items.end()) && (f.allDescendentsPassedFilter()); ++it) @@ -3041,7 +3041,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root } if (get_is_item_worn(viewModel->getUUID())) { - worn_item = true; + has_worn = true; LLWearableType::EType type = viewModel->getWearableType(); if (type == LLWearableType::WT_SHAPE || type == LLWearableType::WT_SKIN @@ -3058,7 +3058,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { LLNotificationsUtil::add("CantDeleteRequiredClothing"); } - else if (worn_item) + else if (has_worn) { LLSD payload; payload["has_worn"] = true; @@ -3411,6 +3411,7 @@ void LLInventoryAction::onItemsRemovalConfirmation(const LLSD& notification, con } } } + // TODO: collect worn items from content and folders that neede to be deleted after that // removeSelectedItems will check if items are worn before deletion, // don't 'unwear' yet to prevent a race condition from unwearing diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 9074c8466a..23a00e2e51 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -1864,6 +1864,8 @@ void LLInventoryGallery::onDelete(const LLSD& notification, const LLSD& response S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (option == 0) { + bool has_worn = notification["payload"]["has_worn"].asBoolean(); + uuid_vec_t worn; for (const LLUUID& id : selected_ids) { LLInventoryObject* obj = gInventory.getObject(id); @@ -1884,22 +1886,72 @@ void LLInventoryGallery::onDelete(const LLSD& notification, const LLSD& response { gInventory.removeItem(id); } + else if (has_worn && get_is_item_worn(id)) + { + worn.push_back(id); + } } } + + if (!worn.empty()) + { + // should fire once after every item gets detached + LLAppearanceMgr::instance().removeItemsFromAvatar(worn, + [worn]() + { + for (const LLUUID& id : worn) + { + remove_inventory_item(id, NULL); + } + }); + } } } void LLInventoryGallery::deleteSelection() { - if (!LLInventoryAction::sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session + bool has_worn = false; + bool needs_replacement = false; + for (const LLUUID& id : mSelectedItemIDs) { - LLNotifications::instance().setIgnored("DeleteItems", false); - LLInventoryAction::sDeleteConfirmationDisplayed = true; + if (get_is_item_worn(id)) + { + has_worn = true; + const LLViewerInventoryItem* item = gInventory.getItem(id); + LLWearableType::EType type = item->getWearableType(); + if (type == LLWearableType::WT_SHAPE + || type == LLWearableType::WT_SKIN + || type == LLWearableType::WT_HAIR + || type == LLWearableType::WT_EYES) + { + needs_replacement = true; + break; + } + } } - LLSD args; - args["QUESTION"] = LLTrans::getString("DeleteItem"); - LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryGallery::onDelete, _1, _2, mSelectedItemIDs)); + if (needs_replacement) + { + LLNotificationsUtil::add("CantDeleteRequiredClothing"); + } + else if (has_worn) + { + LLSD payload; + payload["has_worn"] = true; + LLNotificationsUtil::add("DeleteWornItems", LLSD(), payload, boost::bind(&LLInventoryGallery::onDelete, _1, _2, mSelectedItemIDs)); + } + else + { + if (!LLInventoryAction::sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session + { + LLNotifications::instance().setIgnored("DeleteItems", false); + LLInventoryAction::sDeleteConfirmationDisplayed = true; + } + + LLSD args; + args["QUESTION"] = LLTrans::getString("DeleteItem"); + LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryGallery::onDelete, _1, _2, mSelectedItemIDs)); + } } bool LLInventoryGallery::canDeleteSelection() diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index 50eefc7d25..d4cf9c2f9f 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -542,6 +542,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men } items.push_back(std::string("Cut")); items.push_back(std::string("Delete")); + // TODO: check descendants!!! if(!get_is_category_removable(&gInventory, selected_id)) { disabled_items.push_back(std::string("Delete")); @@ -577,11 +578,15 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men { items.push_back(std::string("Delete")); } - if(!get_is_item_removable(&gInventory, selected_id, true)) + if (!get_is_item_removable(&gInventory, selected_id, false)) { disabled_items.push_back(std::string("Delete")); disabled_items.push_back(std::string("Cut")); } + else if(!get_is_item_removable(&gInventory, selected_id, true)) + { + disabled_items.push_back(std::string("Cut")); + } if (selected_item && (selected_item->getInventoryType() != LLInventoryType::IT_CALLINGCARD) && !is_inbox && selected_item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID())) { -- cgit v1.3 From 13ab6a408b1f42de278edf8a2517b9ed3dc6c66d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 16 Oct 2023 22:32:22 +0300 Subject: SL-20232 Fix gallery not checking descendants --- indra/newview/llinventorygallerymenu.cpp | 44 +++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) (limited to 'indra/newview/llinventorygallerymenu.cpp') diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index d4cf9c2f9f..49969e1a52 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -388,6 +388,44 @@ bool is_inbox_folder(LLUUID item_id) return gInventory.isObjectDescendentOf(item_id, inbox_id); } +bool is_category_removable(const LLUUID &folder_id, bool check_worn) +{ + if (!get_is_category_removable(&gInventory, folder_id)) + { + return false; + } + + // check children + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(folder_id, cat_array, item_array); + + for (LLInventoryModel::item_array_t::value_type& item : *item_array) + { + if (!get_is_item_removable(&gInventory, item->getUUID(), check_worn)) + { + return false; + } + } + + for (LLInventoryModel::cat_array_t::value_type& cat : *cat_array) + { + if (!is_category_removable(cat->getUUID(), check_worn)) + { + return false; + } + } + + const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + if (mp_id.notNull() && gInventory.isObjectDescendentOf(folder_id, mp_id)) + { + return false; + } + + return true; +} + + void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* menu) { LLUUID selected_id = mUUIDs.front(); @@ -495,7 +533,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men } } items.push_back(std::string("Purge Item")); - if (is_folder && !get_is_category_removable(&gInventory, selected_id)) + if (is_folder && !is_category_removable(selected_id, true)) { disabled_items.push_back(std::string("Purge Item")); } @@ -542,8 +580,8 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men } items.push_back(std::string("Cut")); items.push_back(std::string("Delete")); - // TODO: check descendants!!! - if(!get_is_category_removable(&gInventory, selected_id)) + + if(!is_category_removable(selected_id, true)) { disabled_items.push_back(std::string("Delete")); disabled_items.push_back(std::string("Cut")); -- cgit v1.3 From 7d82ee8075e81a6d3640103b68e98a6a2cbbe623 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 17 Oct 2023 01:17:51 +0300 Subject: SL-20232 Allow deletion of folders with worn content in gallery view #4 --- indra/newview/llinventorygallery.cpp | 86 +++++++++++++++++++++++++------- indra/newview/llinventorygallerymenu.cpp | 7 ++- 2 files changed, 73 insertions(+), 20 deletions(-) (limited to 'indra/newview/llinventorygallerymenu.cpp') diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 23a00e2e51..058bbde8b9 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -1866,29 +1866,49 @@ void LLInventoryGallery::onDelete(const LLSD& notification, const LLSD& response { bool has_worn = notification["payload"]["has_worn"].asBoolean(); uuid_vec_t worn; - for (const LLUUID& id : selected_ids) + uuid_vec_t deletion_list; + for (const LLUUID& obj_id : selected_ids) { - LLInventoryObject* obj = gInventory.getObject(id); - if (!obj) + LLViewerInventoryCategory* cat = gInventory.getCategory(obj_id); + if (cat) { - return; - } - if (obj->getType() == LLAssetType::AT_CATEGORY) - { - if (get_is_category_removable(&gInventory, id)) + bool cat_has_worn = false; + if (has_worn) { - gInventory.removeCategory(id); + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + + gInventory.collectDescendents(obj_id, categories, items, FALSE); + + for (LLInventoryModel::item_array_t::value_type& item : items) + { + if (get_is_item_worn(item)) + { + worn.push_back(item->getUUID()); + cat_has_worn = true; + } + } + } + if (cat_has_worn) + { + deletion_list.push_back(obj_id); + } + else + { + gInventory.removeCategory(obj_id); } } - else + LLViewerInventoryItem* item = gInventory.getItem(obj_id); + if (item) { - if (get_is_item_removable(&gInventory, id, true)) + if (has_worn && get_is_item_worn(item)) { - gInventory.removeItem(id); + worn.push_back(item->getUUID()); + deletion_list.push_back(item->getUUID()); } - else if (has_worn && get_is_item_worn(id)) + else { - worn.push_back(id); + gInventory.removeItem(obj_id); } } } @@ -1897,9 +1917,9 @@ void LLInventoryGallery::onDelete(const LLSD& notification, const LLSD& response { // should fire once after every item gets detached LLAppearanceMgr::instance().removeItemsFromAvatar(worn, - [worn]() + [deletion_list]() { - for (const LLUUID& id : worn) + for (const LLUUID& id : deletion_list) { remove_inventory_item(id, NULL); } @@ -1914,10 +1934,40 @@ void LLInventoryGallery::deleteSelection() bool needs_replacement = false; for (const LLUUID& id : mSelectedItemIDs) { - if (get_is_item_worn(id)) + LLViewerInventoryCategory* cat = gInventory.getCategory(id); + if (cat) + { + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + + gInventory.collectDescendents(id, categories, items, FALSE); + + for (LLInventoryModel::item_array_t::value_type& item : items) + { + if (get_is_item_worn(item)) + { + has_worn = true; + LLWearableType::EType type = item->getWearableType(); + if (type == LLWearableType::WT_SHAPE + || type == LLWearableType::WT_SKIN + || type == LLWearableType::WT_HAIR + || type == LLWearableType::WT_EYES) + { + needs_replacement = true; + break; + } + } + } + if (needs_replacement) + { + break; + } + } + + LLViewerInventoryItem* item = gInventory.getItem(id); + if (item && get_is_item_worn(item)) { has_worn = true; - const LLViewerInventoryItem* item = gInventory.getItem(id); LLWearableType::EType type = item->getWearableType(); if (type == LLWearableType::WT_SHAPE || type == LLWearableType::WT_SKIN diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index 49969e1a52..2bca33dec4 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -425,7 +425,6 @@ bool is_category_removable(const LLUUID &folder_id, bool check_worn) return true; } - void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* menu) { LLUUID selected_id = mUUIDs.front(); @@ -581,11 +580,15 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men items.push_back(std::string("Cut")); items.push_back(std::string("Delete")); - if(!is_category_removable(selected_id, true)) + if(!is_category_removable(selected_id, false)) { disabled_items.push_back(std::string("Delete")); disabled_items.push_back(std::string("Cut")); } + else if (!is_category_removable(selected_id, true)) + { + disabled_items.push_back(std::string("Cut")); + } if(!is_inbox) { -- cgit v1.3 From aff01e2fd4fb5475eb42ce58d1ad997341cb7a94 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 10 Nov 2023 23:36:33 +0200 Subject: SL-20575 Fix some deletion issues - Fixes 'cut' checks for gallery - Checking if large folder is deletable should be faster now - For large flat root folders eliminated delay of looking for cof Might be a good idea to start cashing isItemRemovable if category version hasn't changed. --- indra/newview/llappearancemgr.cpp | 48 +++++++++++++++++--- indra/newview/llappearancemgr.h | 9 +++- indra/newview/llinventorybridge.cpp | 42 +++--------------- indra/newview/llinventoryfunctions.cpp | 76 +++++++++++++++++++++++++++++--- indra/newview/llinventoryfunctions.h | 1 + indra/newview/llinventorygallery.cpp | 41 ++++++++++++++++- indra/newview/llinventorygallerymenu.cpp | 43 ++---------------- indra/newview/llstartup.cpp | 6 ++- 8 files changed, 176 insertions(+), 90 deletions(-) (limited to 'indra/newview/llinventorygallerymenu.cpp') diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 8673dc6a33..10b3f4b573 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1399,7 +1399,7 @@ const std::string LLAppearanceMgr::sExpectedTextureName = "OutfitPreview"; const LLUUID LLAppearanceMgr::getCOF() const { - return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + return mCOFID; } S32 LLAppearanceMgr::getCOFVersion() const @@ -1415,6 +1415,11 @@ S32 LLAppearanceMgr::getCOFVersion() const } } +void LLAppearanceMgr::initCOFID() +{ + mCOFID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); +} + const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink() { const LLUUID& current_outfit_cat = getCOF(); @@ -3749,6 +3754,14 @@ LLSD LLAppearanceMgr::dumpCOF() const return result; } +void LLAppearanceMgr::cleanup() +{ + mIsInUpdateAppearanceFromCOF = false; + mOutstandingAppearanceBakeRequest = false; + mRerequestAppearanceBake = false; + mCOFID.setNull(); +} + // static void LLAppearanceMgr::onIdle(void *) { @@ -4387,20 +4400,45 @@ BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const return FALSE; } -BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const +bool LLAppearanceMgr::getIsInCOF(const LLInventoryObject* obj) const { - if (!getIsInCOF(obj_id)) return FALSE; + const LLUUID& cof = getCOF(); + if (obj->getUUID() == cof) + return true; + if (obj && obj->getParentUUID() == cof) + return true; + return false; +} + +bool LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const +{ + if (!getIsInCOF(obj_id)) return false; // If a non-link somehow ended up in COF, allow deletion. const LLInventoryObject *obj = gInventory.getObject(obj_id); if (obj && !obj->getIsLinkType()) { - return FALSE; + return false; } // For now, don't allow direct deletion from the COF. Instead, force users // to choose "Detach" or "Take Off". - return TRUE; + return true; +} + +bool LLAppearanceMgr::getIsProtectedCOFItem(const LLInventoryObject* obj) const +{ + if (!getIsInCOF(obj)) return false; + + // If a non-link somehow ended up in COF, allow deletion. + if (obj && !obj->getIsLinkType()) + { + return false; + } + + // For now, don't allow direct deletion from the COF. Instead, force users + // to choose "Detach" or "Take Off". + return true; } class CallAfterCategoryFetchStage2: public LLInventoryFetchItemsObserver diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 80d6587ad3..02859dc12c 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -111,9 +111,11 @@ public: // Find the Current Outfit folder. const LLUUID getCOF() const; S32 getCOFVersion() const; + void initCOFID(); // Debugging - get truncated LLSD summary of COF contents. LLSD dumpCOF() const; + void cleanup(); // Finds the folder link to the currently worn outfit const LLViewerInventoryItem *getBaseOutfitLink(); @@ -276,6 +278,7 @@ private: attachments_changed_signal_t mAttachmentsChangeSignal; LLUUID mCOFImageID; + LLUUID mCOFID; std::unique_ptr mUnlockOutfitTimer; @@ -290,8 +293,10 @@ private: public: // Is this in the COF? BOOL getIsInCOF(const LLUUID& obj_id) const; - // Is this in the COF and can the user delete it from the COF? - BOOL getIsProtectedCOFItem(const LLUUID& obj_id) const; + bool getIsInCOF(const LLInventoryObject* obj) const; + // Is this in the COF and can the user delete it from the COF? + bool getIsProtectedCOFItem(const LLUUID& obj_id) const; + bool getIsProtectedCOFItem(const LLInventoryObject* obj) const; // Outfits will prioritize textures with such name to use for preview in gallery static const std::string sExpectedTextureName; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index a12c4f7f76..4fb60b4c55 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -2399,49 +2399,19 @@ void LLFolderBridge::update() } } - -// Iterate through a folder's children to determine if -// all the children are removable. -class LLIsItemRemovable : public LLFolderViewFunctor -{ -public: - LLIsItemRemovable(bool check_worn = true) : mPassed(TRUE), mCheckWorn(check_worn) {} - virtual void doFolder(LLFolderViewFolder* folder) - { - mPassed &= folder->getViewModelItem()->isItemRemovable(mCheckWorn); - } - virtual void doItem(LLFolderViewItem* item) - { - mPassed &= item->getViewModelItem()->isItemRemovable(mCheckWorn); - } - BOOL mPassed; - bool mCheckWorn; -}; - // Can be destroyed (or moved to trash) BOOL LLFolderBridge::isItemRemovable(bool check_worn) const { - if (!get_is_category_removable(getInventoryModel(), mUUID)) + if (!get_is_category_and_children_removable(getInventoryModel(), mUUID, check_worn)) { return FALSE; } - LLInventoryPanel* panel = mInventoryPanel.get(); - LLFolderViewFolder* folderp = dynamic_cast(panel ? panel->getItemByID(mUUID) : NULL); - if (folderp) - { - LLIsItemRemovable folder_test(check_worn); - folderp->applyFunctorToChildren(folder_test); - if (!folder_test.mPassed) - { - return FALSE; - } - } - - if (isMarketplaceListingsFolder() && (!LLMarketplaceData::instance().isSLMDataFetched() || LLMarketplaceData::instance().getActivationState(mUUID))) - { - return FALSE; - } + if (isMarketplaceListingsFolder() + && (!LLMarketplaceData::instance().isSLMDataFetched() || LLMarketplaceData::instance().getActivationState(mUUID))) + { + return FALSE; + } return TRUE; } diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index c2cbf4b8ac..5d6bf8ab4b 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -621,7 +621,7 @@ BOOL get_is_item_worn(const LLUUID& id, const LLViewerInventoryItem* item) BOOL get_is_item_worn(const LLUUID& id) { const LLViewerInventoryItem* item = gInventory.getItem(id); - return get_is_item_worn(item); + return get_is_item_worn(id, item); } BOOL get_is_item_worn(const LLViewerInventoryItem* item) @@ -711,20 +711,20 @@ bool get_is_item_removable(const LLInventoryModel* model, const LLUUID& id, bool // Disable delete from COF folder; have users explicitly choose "detach/take off", // unless the item is not worn but in the COF (i.e. is bugged). - if (LLAppearanceMgr::instance().getIsProtectedCOFItem(id)) + const LLViewerInventoryItem* obj = model->getItem(id); + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(obj)) { - if (get_is_item_worn(id)) + if (get_is_item_worn(id, obj)) { return false; } } - const LLInventoryObject *obj = model->getItem(id); if (obj && obj->getIsLinkType()) { return true; } - if (check_worn && get_is_item_worn(id)) + if (check_worn && get_is_item_worn(id, obj)) { return false; } @@ -819,6 +819,72 @@ BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) return TRUE; } +bool get_is_category_and_children_removable(LLInventoryModel* model, const LLUUID& folder_id, bool check_worn) +{ + if (!get_is_category_removable(model, folder_id)) + { + return false; + } + + const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + if (mp_id.notNull() && gInventory.isObjectDescendentOf(folder_id, mp_id)) + { + return false; + } + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + model->collectDescendents( + folder_id, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH); + + for (LLInventoryModel::item_array_t::value_type& item : item_array) + { + // Disable delete from COF folder; have users explicitly choose "detach/take off", + // unless the item is not worn but in the COF (i.e. is bugged). + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(item)) + { + if (get_is_item_worn(item)) + { + return false; + } + } + + if (item && item->getIsLinkType()) + { + return true; + } + if (check_worn && get_is_item_worn(item)) + { + return false; + } + } + + const LLViewerInventoryItem* base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); + LLViewerInventoryCategory* outfit_linked_category = base_outfit_link ? base_outfit_link->getLinkedCategory() : nullptr; + for (LLInventoryModel::cat_array_t::value_type& cat : cat_array) + { + const LLFolderType::EType folder_type = cat->getPreferredType(); + if (LLFolderType::lookupIsProtectedType(folder_type)) + { + return false; + } + + // Can't delete the outfit that is currently being worn. + if (folder_type == LLFolderType::FT_OUTFIT) + { + if (cat == outfit_linked_category) + { + return false; + } + } + } + + return true; +} + BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) { if (!model) diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index a8a9bb9735..deacaced42 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -59,6 +59,7 @@ bool get_is_item_editable(const LLUUID& inv_item_id); void handle_item_edit(const LLUUID& inv_item_id); BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id); +bool get_is_category_and_children_removable(LLInventoryModel* model, const LLUUID& folder_id, bool check_worn); BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id); diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index ad6c3909ef..3c3da253d3 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -1668,6 +1668,45 @@ void LLInventoryGallery::cut() mFilterSubString.clear(); } + + +bool is_category_removable(const LLUUID& folder_id, bool check_worn) +{ + if (!get_is_category_removable(&gInventory, folder_id)) + { + return false; + } + + // check children + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(folder_id, cat_array, item_array); + + for (LLInventoryModel::item_array_t::value_type& item : *item_array) + { + if (!get_is_item_removable(&gInventory, item->getUUID(), check_worn)) + { + return false; + } + } + + for (LLInventoryModel::cat_array_t::value_type& cat : *cat_array) + { + if (!is_category_removable(cat->getUUID(), check_worn)) + { + return false; + } + } + + const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + if (mp_id.notNull() && gInventory.isObjectDescendentOf(folder_id, mp_id)) + { + return false; + } + + return true; +} + BOOL LLInventoryGallery::canCut() const { if (!getVisible() || !getEnabled() || mSelectedItemIDs.empty()) @@ -1680,7 +1719,7 @@ BOOL LLInventoryGallery::canCut() const LLViewerInventoryCategory* cat = gInventory.getCategory(id); if (cat) { - if (!get_is_category_removable(&gInventory, id)) + if (!get_is_category_and_children_removable(&gInventory, id, true)) { return FALSE; } diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index 2bca33dec4..4b47346473 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -388,43 +388,6 @@ bool is_inbox_folder(LLUUID item_id) return gInventory.isObjectDescendentOf(item_id, inbox_id); } -bool is_category_removable(const LLUUID &folder_id, bool check_worn) -{ - if (!get_is_category_removable(&gInventory, folder_id)) - { - return false; - } - - // check children - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(folder_id, cat_array, item_array); - - for (LLInventoryModel::item_array_t::value_type& item : *item_array) - { - if (!get_is_item_removable(&gInventory, item->getUUID(), check_worn)) - { - return false; - } - } - - for (LLInventoryModel::cat_array_t::value_type& cat : *cat_array) - { - if (!is_category_removable(cat->getUUID(), check_worn)) - { - return false; - } - } - - const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); - if (mp_id.notNull() && gInventory.isObjectDescendentOf(folder_id, mp_id)) - { - return false; - } - - return true; -} - void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* menu) { LLUUID selected_id = mUUIDs.front(); @@ -532,7 +495,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men } } items.push_back(std::string("Purge Item")); - if (is_folder && !is_category_removable(selected_id, true)) + if (is_folder && !get_is_category_and_children_removable(&gInventory, selected_id, true)) { disabled_items.push_back(std::string("Purge Item")); } @@ -580,12 +543,12 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men items.push_back(std::string("Cut")); items.push_back(std::string("Delete")); - if(!is_category_removable(selected_id, false)) + if(!get_is_category_and_children_removable(&gInventory, selected_id, false)) { disabled_items.push_back(std::string("Delete")); disabled_items.push_back(std::string("Cut")); } - else if (!is_category_removable(selected_id, true)) + else if (!get_is_category_and_children_removable(&gInventory, selected_id, true)) { disabled_items.push_back(std::string("Cut")); } diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 916848ff5f..bb867f7b23 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1938,8 +1938,10 @@ bool idle_startup() display_startup(); return FALSE; } + LLInventoryModelBackgroundFetch::instance().start(); - LLUUID cof_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + LLAppearanceMgr::instance().initCOFID(); + LLUUID cof_id = LLAppearanceMgr::instance().getCOF(); LLViewerInventoryCategory* cof = gInventory.getCategory(cof_id); if (cof && cof->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) @@ -2812,6 +2814,7 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name, LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true); // Initiate creation of COF, since we're also bypassing that. gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CURRENT_OUTFIT); + LLAppearanceMgr::getInstance()->initCOFID(); ESex gender; if (gender_name == "male") @@ -2968,6 +2971,7 @@ void reset_login() gAgent.cleanup(); gSky.cleanup(); // mVOSkyp is an inworld object. LLWorld::getInstance()->resetClass(); + LLAppearanceMgr::getInstance()->cleanup(); if ( gViewerWindow ) { // Hide menus and normal buttons -- cgit v1.3