summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kleshchev <andreykproductengine@lindenlab.com>2025-04-08 22:39:41 +0300
committerAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2025-04-11 12:36:48 +0300
commit1bb8fb2795920b57a5934ffa394d78a6c5733ce8 (patch)
tree576ce4330aa58d1335f265ace3d5b235b21db853
parentb95b20a1be56ff7baa3ad20e8d0571ec9f07ab12 (diff)
#3757 Menu for subfodlers in outfits
-rw-r--r--indra/newview/llinventorybridge.cpp199
-rw-r--r--indra/newview/llinventorybridge.h2
-rw-r--r--indra/newview/llinventorygallery.cpp57
-rw-r--r--indra/newview/llinventorygallerymenu.cpp8
-rw-r--r--indra/newview/llinventorymodel.cpp3
-rw-r--r--indra/newview/lloutfitslist.cpp3
6 files changed, 202 insertions, 70 deletions
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 8d31c4dde7..fd9d08a26d 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -105,6 +105,44 @@ static bool check_item(const LLUUID& item_id,
// Helper functions
+
+namespace {
+ enum EMyOutfitsSubfolderType
+ {
+ MY_OUTFITS_NO,
+ MY_OUTFITS_SUBFOLDER,
+ MY_OUTFITS_OUTFIT,
+ };
+
+ EMyOutfitsSubfolderType myoutfit_object_subfolder_type(LLInventoryModel* model, const LLUUID& obj_id,
+ const LLUUID& cat_id)
+ {
+ if (obj_id == cat_id) return MY_OUTFITS_NO;
+
+ const LLViewerInventoryCategory* test_cat = model->getCategory(obj_id);
+ while (test_cat)
+ {
+ if (test_cat->getPreferredType() == LLFolderType::FT_OUTFIT)
+ {
+ return MY_OUTFITS_OUTFIT;
+ }
+
+ const LLUUID& parent_id = test_cat->getParentUUID();
+ if (parent_id.isNull())
+ {
+ return MY_OUTFITS_NO;
+ }
+ if (parent_id == cat_id)
+ {
+ return MY_OUTFITS_SUBFOLDER;
+ }
+ test_cat = model->getCategory(parent_id);
+ }
+
+ return MY_OUTFITS_NO;
+ }
+}
+
bool isAddAction(const std::string& action)
{
return ("wear" == action || "attach" == action || "activate" == action);
@@ -2912,13 +2950,22 @@ bool LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
if (mUUID == my_outifts_id)
{
- // Category can contains objects,
+ // Category can't contains objects,
// create a new folder and populate it with links to original objects
dropToMyOutfits(inv_cat, cb);
}
- else if (getCategory() && getCategory()->getParentUUID() == my_outifts_id)
+ else if (move_is_into_my_outfits)
{
- dropToMyOutfitsSubfolder(inv_cat, mUUID, cb);
+ EMyOutfitsSubfolderType res = myoutfit_object_subfolder_type(model, mUUID, my_outifts_id);
+ if (res == MY_OUTFITS_SUBFOLDER)
+ {
+ // turn it into outfit
+ dropToMyOutfitsSubfolder(inv_cat, mUUID, LLFolderType::FT_OUTFIT, cb);
+ }
+ else
+ {
+ dropToMyOutfitsSubfolder(inv_cat, mUUID, LLFolderType::FT_NONE, cb);
+ }
}
// if target is current outfit folder we use link
else if (move_is_into_current_outfit &&
@@ -4012,6 +4059,7 @@ void LLFolderBridge::perform_pasteFromClipboard()
{
if (!move_is_into_my_outfits && item && can_move_to_outfit(item, move_is_into_current_outfit))
{
+ // todo: this is going to create dupplicate folders?
dropToOutfit(item, move_is_into_current_outfit, cb);
}
else if (move_is_into_my_outfits && LLAssetType::AT_CATEGORY == obj->getType())
@@ -4024,9 +4072,18 @@ void LLFolderBridge::perform_pasteFromClipboard()
{
dropToMyOutfits(cat, cb);
}
- else if (getCategory() && getCategory()->getParentUUID() == mUUID)
+ else if (move_is_into_my_outfits)
{
- dropToMyOutfitsSubfolder(cat, mUUID, cb);
+ EMyOutfitsSubfolderType res = myoutfit_object_subfolder_type(model, mUUID, my_outifts_id);
+ if (res == MY_OUTFITS_SUBFOLDER)
+ {
+ // turn it into outfit
+ dropToMyOutfitsSubfolder(cat, mUUID, LLFolderType::FT_OUTFIT, cb);
+ }
+ else
+ {
+ dropToMyOutfitsSubfolder(cat, mUUID, LLFolderType::FT_NONE, cb);
+ }
}
}
else
@@ -4361,63 +4418,85 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
else if(isAgentInventory()) // do not allow creating in library
{
LLViewerInventoryCategory *cat = getCategory();
- // BAP removed protected check to re-enable standard ops in untyped folders.
- // Not sure what the right thing is to do here.
- if (!isCOFFolder() && cat && (cat->getPreferredType() != LLFolderType::FT_OUTFIT))
- {
- if (!isInboxFolder() // don't allow creation in inbox
- && outfits_id != mUUID)
- {
- bool menu_items_added = false;
- // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
- if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
- {
- items.push_back(std::string("New Folder"));
- menu_items_added = true;
- }
- if (!isMarketplaceListingsFolder())
- {
- items.push_back(std::string("upload_def"));
- items.push_back(std::string("create_new"));
- items.push_back(std::string("New Script"));
- items.push_back(std::string("New Note"));
- items.push_back(std::string("New Gesture"));
- items.push_back(std::string("New Material"));
- items.push_back(std::string("New Clothes"));
- items.push_back(std::string("New Body Parts"));
- items.push_back(std::string("New Settings"));
- if (!LLEnvironment::instance().isInventoryEnabled())
- {
- disabled_items.push_back("New Settings");
- }
- }
- else
- {
- items.push_back(std::string("New Listing Folder"));
- }
- if (menu_items_added)
- {
- items.push_back(std::string("Create Separator"));
- }
- }
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
+
+ if (cat)
{
- // Want some but not all of the items from getClipboardEntries for outfits.
- if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT))
+ if (cat->getPreferredType() == LLFolderType::FT_OUTFIT)
{
+ // Want some but not all of the items from getClipboardEntries for outfits.
+ items.push_back(std::string("New Outfit Folder"));
items.push_back(std::string("Rename"));
items.push_back(std::string("thumbnail"));
addDeleteContextMenuOptions(items, disabled_items);
// EXT-4030: disallow deletion of currently worn outfit
- const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink();
+ const LLViewerInventoryItem* base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink();
if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory()))
{
disabled_items.push_back(std::string("Delete"));
}
}
+ else if (outfits_id == mUUID)
+ {
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
+ else if (!isCOFFolder())
+ {
+ EMyOutfitsSubfolderType in_my_outfits = myoutfit_object_subfolder_type(model, mUUID, outfits_id);
+ if (in_my_outfits != MY_OUTFITS_NO)
+ {
+ if (in_my_outfits == MY_OUTFITS_SUBFOLDER)
+ {
+ // Not inside an outfit, but inside 'my outfits'
+ items.push_back(std::string("New Outfit"));
+ }
+
+ items.push_back(std::string("New Outfit Folder"));
+ items.push_back(std::string("Rename"));
+ items.push_back(std::string("thumbnail"));
+
+ addDeleteContextMenuOptions(items, disabled_items);
+ }
+ else
+ {
+ if (!isInboxFolder() // don't allow creation in inbox
+ && outfits_id != mUUID)
+ {
+ bool menu_items_added = false;
+ // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
+ if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
+ {
+ items.push_back(std::string("New Folder"));
+ menu_items_added = true;
+ }
+ if (!isMarketplaceListingsFolder())
+ {
+ items.push_back(std::string("upload_def"));
+ items.push_back(std::string("create_new"));
+ items.push_back(std::string("New Script"));
+ items.push_back(std::string("New Note"));
+ items.push_back(std::string("New Gesture"));
+ items.push_back(std::string("New Material"));
+ items.push_back(std::string("New Clothes"));
+ items.push_back(std::string("New Body Parts"));
+ items.push_back(std::string("New Settings"));
+ if (!LLEnvironment::instance().isInventoryEnabled())
+ {
+ disabled_items.push_back("New Settings");
+ }
+ }
+ else
+ {
+ items.push_back(std::string("New Listing Folder"));
+ }
+ if (menu_items_added)
+ {
+ items.push_back(std::string("Create Separator"));
+ }
+ }
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
+ }
}
if (model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) == mUUID)
@@ -4570,7 +4649,11 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t&
if (((flags & ITEM_IN_MULTI_SELECTION) == 0) && hasChildren() && (type != LLFolderType::FT_OUTFIT))
{
- items.push_back(std::string("Ungroup folder items"));
+ const LLUUID my_outfits = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+ if (!gInventory.isObjectDescendentOf(mUUID, my_outfits))
+ {
+ items.push_back(std::string("Ungroup folder items"));
+ }
}
}
else
@@ -5350,7 +5433,7 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLI
inv_cat->getThumbnailUUID());
}
-void LLFolderBridge::dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest_id, LLPointer<LLInventoryCallback> cb)
+void LLFolderBridge::dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest_id, LLFolderType::EType preferred_type, LLPointer<LLInventoryCallback> cb)
{
LLViewerInventoryCategory* cat = getInventoryModel()->getCategory(dest_id);
const LLUUID outfits_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
@@ -5358,7 +5441,7 @@ void LLFolderBridge::dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, cons
if (cat && cat->getParentUUID() == outfits_id)
{
getInventoryModel()->createNewCategory(dest_id,
- LLFolderType::FT_OUTFIT,
+ preferred_type,
inv_cat->getName(),
func,
inv_cat->getThumbnailUUID());
@@ -5366,7 +5449,7 @@ void LLFolderBridge::dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, cons
else
{
getInventoryModel()->createNewCategory(outfits_id,
- LLFolderType::FT_OUTFIT,
+ preferred_type,
inv_cat->getName(),
func,
inv_cat->getThumbnailUUID());
@@ -5476,10 +5559,6 @@ bool LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
const bool move_is_into_favorites = (mUUID == favorites_id);
const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id);
const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
- const bool move_is_into_my_outfits_subfolder = move_is_into_my_outfits
- && getCategory()
- && getCategory()->getParentUUID() == my_outifts_id
- && getCategory()->getPreferredType() != LLFolderType::FT_OUTFIT;
const bool move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id);
const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
const bool move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id);
@@ -5550,7 +5629,9 @@ bool LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
}
else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit))
{
- accept = !move_is_into_my_outfits_subfolder && can_move_to_outfit(inv_item, move_is_into_current_outfit);
+ EMyOutfitsSubfolderType res = myoutfit_object_subfolder_type(model, mUUID, my_outifts_id);
+ // don't allow items in my outfits' subfodlers, only in outfits and outfit's subfolders
+ accept = res != MY_OUTFITS_SUBFOLDER && can_move_to_outfit(inv_item, move_is_into_current_outfit);
}
else if (user_confirm && (move_is_into_favorites || move_is_into_landmarks))
{
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index b7bdef9b21..a101c7368a 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -369,7 +369,7 @@ protected:
void dropToFavorites(LLInventoryItem* inv_item, LLPointer<LLInventoryCallback> cb = NULL);
void dropToOutfit(LLInventoryItem* inv_item, bool move_is_into_current_outfit, LLPointer<LLInventoryCallback> cb = NULL);
void dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLInventoryCallback> cb = NULL);
- void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest, LLPointer<LLInventoryCallback> cb = NULL);
+ void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest, LLFolderType::EType preferred_type, LLPointer<LLInventoryCallback> cb = NULL);
//--------------------------------------------------------------------
// Messy hacks for handling folder options
diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp
index 6188b812d5..15bc578c64 100644
--- a/indra/newview/llinventorygallery.cpp
+++ b/indra/newview/llinventorygallery.cpp
@@ -60,11 +60,49 @@ static LLPanelInjector<LLInventoryGallery> t_inventory_gallery("inventory_galler
const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;
const S32 FAST_LOAD_THUMBNAIL_TRSHOLD = 50; // load folders below this value immediately
+
+namespace {
+ enum EMyOutfitsSubfolderType
+ {
+ MY_OUTFITS_NO,
+ MY_OUTFITS_SUBFOLDER,
+ MY_OUTFITS_OUTFIT,
+ };
+
+ EMyOutfitsSubfolderType myoutfit_object_subfolder_type(LLInventoryModel* model, const LLUUID& obj_id,
+ const LLUUID& cat_id)
+ {
+ if (obj_id == cat_id) return MY_OUTFITS_NO;
+
+ const LLViewerInventoryCategory* test_cat = model->getCategory(obj_id);
+ while (test_cat)
+ {
+ if (test_cat->getPreferredType() == LLFolderType::FT_OUTFIT)
+ {
+ return MY_OUTFITS_OUTFIT;
+ }
+
+ const LLUUID& parent_id = test_cat->getParentUUID();
+ if (parent_id.isNull())
+ {
+ return MY_OUTFITS_NO;
+ }
+ if (parent_id == cat_id)
+ {
+ return MY_OUTFITS_SUBFOLDER;
+ }
+ test_cat = model->getCategory(parent_id);
+ }
+
+ return MY_OUTFITS_NO;
+ }
+}
+
// Helper dnd functions
bool dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, bool drop, std::string& tooltip_msg, bool is_link);
bool dragItemIntoFolder(LLUUID folder_id, LLInventoryItem* inv_item, bool drop, std::string& tooltip_msg, bool user_confirm);
void dropToMyOutfits(LLInventoryCategory* inv_cat);
-void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest_id);
+void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest_id, LLFolderType::EType preferred_type);
class LLGalleryPanel: public LLPanel
{
@@ -3899,9 +3937,18 @@ bool dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat,
// create a new folder and populate it with links to original objects
dropToMyOutfits(inv_cat);
}
- else if (dest_cat && dest_cat->getParentUUID() == my_outifts_id)
+ else if (move_is_into_my_outfits)
{
- dropToMyOutfitsSubfolder(inv_cat, dest_id);
+ EMyOutfitsSubfolderType res = myoutfit_object_subfolder_type(model, dest_id, my_outifts_id);
+ if (res == MY_OUTFITS_SUBFOLDER)
+ {
+ // turn it into outfit
+ dropToMyOutfitsSubfolder(inv_cat, dest_id, LLFolderType::FT_OUTFIT);
+ }
+ else
+ {
+ dropToMyOutfitsSubfolder(inv_cat, dest_id, LLFolderType::FT_NONE);
+ }
}
// if target is current outfit folder we use link
else if (move_is_into_current_outfit &&
@@ -4047,10 +4094,10 @@ void dropToMyOutfits(LLInventoryCategory* inv_cat)
gInventory.createNewCategory(dest_id, LLFolderType::FT_OUTFIT, inv_cat->getName(), func, inv_cat->getThumbnailUUID());
}
-void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID &dest_id)
+void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID &dest_id, LLFolderType::EType preferred_type)
{
// Note: creation will take time, so passing folder id to callback is slightly unreliable,
// but so is collecting and passing descendants' ids
inventory_func_type func = boost::bind(&outfitFolderCreatedCallback, inv_cat->getUUID(), _1);
- gInventory.createNewCategory(dest_id, LLFolderType::FT_OUTFIT, inv_cat->getName(), func, inv_cat->getThumbnailUUID());
+ gInventory.createNewCategory(dest_id, preferred_type, inv_cat->getName(), func, inv_cat->getThumbnailUUID());
}
diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp
index 25b339d51e..3ef5071c97 100644
--- a/indra/newview/llinventorygallerymenu.cpp
+++ b/indra/newview/llinventorygallerymenu.cpp
@@ -586,7 +586,9 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
bool is_trash = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH));
bool is_in_trash = gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH));
bool is_lost_and_found = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
- bool is_outfits= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));
+ const LLUUID my_outfits = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+ bool is_outfits= (selected_id == my_outfits);
+ bool is_in_outfits = is_outfits || gInventory.isObjectDescendentOf(selected_id, my_outfits);
bool is_in_favorites = gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE));
//bool is_favorites= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE));
@@ -725,7 +727,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
}
else
{
- if (is_agent_inventory && !is_inbox && !is_cof && !is_in_favorites && !is_outfits)
+ if (is_agent_inventory && !is_inbox && !is_cof && !is_in_favorites && !is_outfits && !is_in_outfits)
{
LLViewerInventoryCategory* category = gInventory.getCategory(selected_id);
if (!category || !LLFriendCardsManager::instance().isCategoryInFriendFolder(category))
@@ -778,7 +780,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
items.push_back(std::string("Subfolder Separator"));
if (!is_system_folder && !isRootFolder())
{
- if(has_children && (folder_type != LLFolderType::FT_OUTFIT))
+ if(has_children && (folder_type != LLFolderType::FT_OUTFIT) && !is_in_outfits)
{
items.push_back(std::string("Ungroup folder items"));
}
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 9fffe6378e..6f4d2db12e 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -1007,7 +1007,8 @@ void LLInventoryModel::createNewCategory(const LLUUID& parent_id,
return;
}
- if (preferred_type != LLFolderType::FT_NONE)
+ if (preferred_type != LLFolderType::FT_NONE
+ && preferred_type != LLFolderType::FT_OUTFIT)
{
// Ultimately this should only be done for non-singleton
// types. Requires back-end changes to guarantee that others
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index cfbfe8f8e0..9d8493549d 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -845,7 +845,8 @@ public:
LLInventoryModel::cat_array_t* cats;
LLInventoryModel::item_array_t* items;
gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items);
- if (items->size() > 3) // eyes, skin, hair and shape are required
+ if (cats->empty() // protection against outfits inside
+ && items->size() > 3) // eyes, skin, hair and shape are required
{
// For now assume this to be an old style outfit, not a subfolder
// but ideally no such 'outfits' should be left in My Outfits