summaryrefslogtreecommitdiff
path: root/indra/newview/llinventoryfunctions.cpp
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-05-15 16:17:09 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-05-15 16:17:09 -0400
commit2e144ecbb2cc7676cfb9f5ab46a2f63c8af85d85 (patch)
tree0296876c167d0bec11434394157b4a5b1c0b63a5 /indra/newview/llinventoryfunctions.cpp
parent1b5f0590ce45ce6e540d266a8902af5839885cfb (diff)
parent33ad8db77584c66496f261a1cfd9aa535462a003 (diff)
Merge branch 'main' into nat/releaseos following Maint X promotion
Diffstat (limited to 'indra/newview/llinventoryfunctions.cpp')
-rw-r--r--indra/newview/llinventoryfunctions.cpp241
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();
}