diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2024-05-15 16:17:09 -0400 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2024-05-15 16:17:09 -0400 |
commit | 2e144ecbb2cc7676cfb9f5ab46a2f63c8af85d85 (patch) | |
tree | 0296876c167d0bec11434394157b4a5b1c0b63a5 /indra/newview/llinventoryfunctions.cpp | |
parent | 1b5f0590ce45ce6e540d266a8902af5839885cfb (diff) | |
parent | 33ad8db77584c66496f261a1cfd9aa535462a003 (diff) |
Merge branch 'main' into nat/releaseos following Maint X promotion
Diffstat (limited to 'indra/newview/llinventoryfunctions.cpp')
-rw-r--r-- | indra/newview/llinventoryfunctions.cpp | 241 |
1 files changed, 225 insertions, 16 deletions
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 168099f1e7..c8aa235506 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -596,9 +596,8 @@ BOOL get_is_parent_to_worn_item(const LLUUID& id) return FALSE; } -BOOL get_is_item_worn(const LLUUID& id) +BOOL get_is_item_worn(const LLUUID& id, const LLViewerInventoryItem* item) { - const LLViewerInventoryItem* item = gInventory.getItem(id); if (!item) return FALSE; @@ -636,6 +635,21 @@ BOOL get_is_item_worn(const LLUUID& id) return FALSE; } +BOOL get_is_item_worn(const LLUUID& id) +{ + const LLViewerInventoryItem* item = gInventory.getItem(id); + return get_is_item_worn(id, item); +} + +BOOL get_is_item_worn(const LLViewerInventoryItem* item) +{ + if (!item) + { + return FALSE; + } + return get_is_item_worn(item->getUUID(), item); +} + BOOL get_can_item_be_worn(const LLUUID& id) { const LLViewerInventoryItem* item = gInventory.getItem(id); @@ -699,39 +713,39 @@ BOOL get_can_item_be_worn(const LLUUID& id) return FALSE; } -BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) +bool get_is_item_removable(const LLInventoryModel* model, const LLUUID& id, bool check_worn) { if (!model) { - return FALSE; + return false; } // Can't delete an item that's in the library. if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) { - return FALSE; + return false; } // 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; + return false; } } - const LLInventoryObject *obj = model->getItem(id); if (obj && obj->getIsLinkType()) { - return TRUE; + return true; } - if (get_is_item_worn(id)) + if (check_worn && get_is_item_worn(id, obj)) { - return FALSE; + return false; } - return TRUE; + return true; } bool get_is_item_editable(const LLUUID& inv_item_id) @@ -822,6 +836,74 @@ 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); + + if (check_worn) + { + for (LLInventoryModel::item_array_t::value_type& item : item_array) + { + // Disable delete/cut 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 (item) + { + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(item)) + { + if (get_is_item_worn(item)) + { + return false; + } + } + + if (!item->getIsLinkType() && 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) @@ -2800,7 +2882,7 @@ bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventory { if (item) { - return !get_is_item_removable(&gInventory, item->getUUID()); + return !get_is_item_removable(&gInventory, item->getUUID(), true); } if (cat) { @@ -3082,6 +3164,8 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); bool marketplacelistings_item = false; + bool has_worn = false; + bool needs_replacement = false; LLAllDescendentsPassedFilter f; for (std::set<LLFolderViewItem*>::iterator it = selected_items.begin(); (it != selected_items.end()) && (f.allDescendentsPassedFilter()); ++it) { @@ -3090,14 +3174,69 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root folder->applyFunctorRecursively(f); } LLFolderViewModelItemInventory * viewModel = dynamic_cast<LLFolderViewModelItemInventory *>((*it)->getViewModelItem()); - if (viewModel && gInventory.isObjectDescendentOf(viewModel->getUUID(), marketplacelistings_id)) + LLUUID obj_id = viewModel->getUUID(); + if (viewModel && gInventory.isObjectDescendentOf(obj_id, marketplacelistings_id)) { marketplacelistings_item = true; break; } + + LLViewerInventoryCategory* cat = gInventory.getCategory(obj_id); + if (cat) + { + 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)) + { + 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(obj_id); + if (item && 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; + } + } } // Fall through to the generic confirmation if the user choose to ignore the specialized one - if ( (!f.allDescendentsPassedFilter()) && !marketplacelistings_item && (!LLNotifications::instance().getIgnored("DeleteFilteredItems")) ) + if (needs_replacement) + { + LLNotificationsUtil::add("CantDeleteRequiredClothing"); + } + else if (has_worn) + { + LLSD payload; + payload["has_worn"] = true; + LLNotificationsUtil::add("DeleteWornItems", LLSD(), payload, boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root->getHandle())); + } + else if ( (!f.allDescendentsPassedFilter()) && !marketplacelistings_item && (!LLNotifications::instance().getIgnored("DeleteFilteredItems")) ) { LLNotificationsUtil::add("DeleteFilteredItems", LLSD(), LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root->getHandle())); } @@ -3420,12 +3559,82 @@ void LLInventoryAction::onItemsRemovalConfirmation(const LLSD& notification, con S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (option == 0 && !root.isDead() && !root.get()->isDead()) { + bool has_worn = notification["payload"]["has_worn"].asBoolean(); LLFolderView* folder_root = root.get(); //Need to remove item from DND before item is removed from root folder view //because once removed from root folder view the item is no longer a selected item removeItemFromDND(folder_root); + + // removeSelectedItems will change selection, collect worn items beforehand + uuid_vec_t worn; + uuid_vec_t item_deletion_list; + uuid_vec_t cat_deletion_list; + if (has_worn) + { + //Get selected items + LLFolderView::selected_items_t selectedItems = folder_root->getSelectedItems(); + + //If user is in DND and deletes item, make sure the notification is not displayed by removing the notification + //from DND history and .xml file. Once this is done, upon exit of DND mode the item deleted will not show a notification. + for (LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) + { + LLFolderViewModelItemInventory* viewModel = dynamic_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem()); + + LLUUID obj_id = viewModel->getUUID(); + LLViewerInventoryCategory* cat = gInventory.getCategory(obj_id); + bool cat_has_worn = false; + if (cat) + { + 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) + { + cat_deletion_list.push_back(obj_id); + } + } + LLViewerInventoryItem* item = gInventory.getItem(obj_id); + if (item && get_is_item_worn(item)) + { + worn.push_back(obj_id); + item_deletion_list.push_back(obj_id); + } + } + } + + // removeSelectedItems will check if items are worn before deletion, + // don't 'unwear' yet to prevent race conditions from unwearing + // and removing simultaneously folder_root->removeSelectedItems(); + // unwear then delete the rest + if (!worn.empty()) + { + // should fire once after every item gets detached + LLAppearanceMgr::instance().removeItemsFromAvatar(worn, + [item_deletion_list, cat_deletion_list]() + { + for (const LLUUID& id : item_deletion_list) + { + remove_inventory_item(id, NULL); + } + for (const LLUUID& id : cat_deletion_list) + { + remove_inventory_category(id, NULL); + } + }); + } + // Update the marketplace listings that have been affected by the operation updateMarketplaceFolders(); } |