diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp
index f1a9244c5b..b308c8baa6 100644
--- a/indra/llcommon/lluuid.cpp
+++ b/indra/llcommon/lluuid.cpp
@@ -176,14 +176,6 @@ void LLUUID::toString(std::string& out) const
(U8)(mData[15]));
}
-// *TODO: deprecate
-void LLUUID::toString(char* out) const
-{
- std::string buffer;
- toString(buffer);
- strcpy(out, buffer.c_str()); /* Flawfinder: ignore */
-}
-
void LLUUID::toCompressedString(std::string& out) const
{
char bytes[UUID_BYTES + 1];
@@ -192,13 +184,6 @@ void LLUUID::toCompressedString(std::string& out) const
out.assign(bytes, UUID_BYTES);
}
-// *TODO: deprecate
-void LLUUID::toCompressedString(char* out) const
-{
- memcpy(out, mData, UUID_BYTES); /* Flawfinder: ignore */
- out[UUID_BYTES] = '\0';
-}
-
std::string LLUUID::getString() const
{
return asString();
diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h
index 3690376e9f..52826e36e7 100644
--- a/indra/llcommon/lluuid.h
+++ b/indra/llcommon/lluuid.h
@@ -103,9 +103,7 @@ public:
friend LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLUUID &uuid);
friend LL_COMMON_API std::istream& operator>>(std::istream& s, LLUUID &uuid);
- void toString(char *out) const; // Does not allocate memory, needs 36 characters (including \0)
void toString(std::string& out) const;
- void toCompressedString(char *out) const; // Does not allocate memory, needs 17 characters (including \0)
void toCompressedString(std::string& out) const;
// last 4 chars for quick ref - Very lightweight, no nul-term added - provide your own, ensure min 4 bytes.
diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp
index a6d6e76e02..dc7bbc3236 100644
--- a/indra/newview/llappearancelistener.cpp
+++ b/indra/newview/llappearancelistener.cpp
@@ -122,7 +122,7 @@ void LLAppearanceListener::getOutfitsList(LLSD const &data)
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
- LLIsType is_category(LLAssetType::AT_CATEGORY);
+ LLIsFolderType is_category(LLFolderType::FT_OUTFIT);
gInventory.collectDescendentsIf(outfits_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, is_category);
response["outfits"] = llsd::toMap(cat_array,
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index ff49570ca2..5f78227a57 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -127,7 +127,6 @@ void teleport_via_landmark(const LLUUID& asset_id);
//
// Helper functions
-
bool isAddAction(const std::string& action)
{
return ("wear" == action || "attach" == action || "activate" == action);
@@ -2975,7 +2974,12 @@ bool LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit");
if (is_movable && move_is_into_outfit)
{
- if (mUUID == my_outifts_id)
+ if ((inv_cat->getPreferredType() != LLFolderType::FT_NONE) && (inv_cat->getPreferredType() != LLFolderType::FT_OUTFIT))
+ {
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
+ is_movable = false;
+ }
+ else if (mUUID == my_outifts_id)
{
if (source != LLToolDragAndDrop::SOURCE_AGENT || move_is_from_marketplacelistings)
{
@@ -2992,13 +2996,39 @@ bool LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
is_movable = false;
}
}
- else if(getCategory() && getCategory()->getPreferredType() == LLFolderType::FT_NONE)
+ else if (!getCategory())
{
- is_movable = ((inv_cat->getPreferredType() == LLFolderType::FT_NONE) || (inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT));
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
}
else
{
- is_movable = false;
+ EMyOutfitsSubfolderType dest_res = myoutfit_object_subfolder_type(model, mUUID, my_outifts_id);
+ EMyOutfitsSubfolderType inv_res = myoutfit_object_subfolder_type(model, cat_id, my_outifts_id);
+ if ((dest_res == MY_OUTFITS_OUTFIT || dest_res == MY_OUTFITS_SUBOUTFIT) && inv_res == MY_OUTFITS_OUTFIT)
+ {
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantMoveOutfitIntoOutfit");
+ }
+ else if ((dest_res == MY_OUTFITS_OUTFIT || dest_res == MY_OUTFITS_SUBOUTFIT) && inv_res == MY_OUTFITS_SUBFOLDER)
+ {
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
+ }
+ else if (dest_res == MY_OUTFITS_SUBFOLDER && inv_res == MY_OUTFITS_SUBOUTFIT)
+ {
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
+ }
+ else if (can_move_to_my_outfits(model, inv_cat, max_items_to_wear))
+ {
+ is_movable = true;
+ }
+ else
+ {
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
+ }
}
}
if (is_movable && move_is_into_current_outfit && is_link)
@@ -3201,9 +3231,77 @@ bool LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
if (mUUID == my_outifts_id)
{
- // Category can contains objects,
- // create a new folder and populate it with links to original objects
- dropToMyOutfits(inv_cat, cb);
+ EMyOutfitsSubfolderType inv_res = myoutfit_object_subfolder_type(model, cat_id, my_outifts_id);
+ if (inv_res == MY_OUTFITS_SUBFOLDER || inv_res == MY_OUTFITS_OUTFIT)
+ {
+ LLInvFVBridge::changeCategoryParent(
+ model,
+ (LLViewerInventoryCategory*)inv_cat,
+ mUUID,
+ false);
+ if (cb) cb->fire(inv_cat->getUUID());
+ }
+ else
+ {
+ // Moving from inventory
+ // create a new folder and populate it with links to original objects
+ dropToMyOutfits(inv_cat, cb);
+ }
+ }
+ else if (move_is_into_my_outfits)
+ {
+ EMyOutfitsSubfolderType dest_res = myoutfit_object_subfolder_type(model, mUUID, my_outifts_id);
+ EMyOutfitsSubfolderType inv_res = myoutfit_object_subfolder_type(model, cat_id, my_outifts_id);
+ switch (inv_res)
+ {
+ case MY_OUTFITS_NO:
+ // Moning from outside outfits into outfits
+ if (dest_res == MY_OUTFITS_SUBFOLDER)
+ {
+ // turn it into outfit
+ dropToMyOutfitsSubfolder(inv_cat, mUUID, LLFolderType::FT_OUTFIT, cb);
+ }
+ else
+ {
+ // or link it?
+ dropToMyOutfitsSubfolder(inv_cat, mUUID, LLFolderType::FT_NONE, cb);
+ }
+ break;
+ case MY_OUTFITS_SUBFOLDER:
+ case MY_OUTFITS_OUTFIT:
+ // only permit moving subfodlers and outfits into other subfolders
+ if (dest_res == MY_OUTFITS_SUBFOLDER)
+ {
+ LLInvFVBridge::changeCategoryParent(
+ model,
+ (LLViewerInventoryCategory*)inv_cat,
+ mUUID,
+ false);
+ if (cb) cb->fire(inv_cat->getUUID());
+ }
+ else
+ {
+ assert(false); // mot permitted, shouldn't have accepted
+ }
+ break;
+ case MY_OUTFITS_SUBOUTFIT:
+ if (dest_res == MY_OUTFITS_SUBOUTFIT || dest_res == MY_OUTFITS_OUTFIT)
+ {
+ LLInvFVBridge::changeCategoryParent(
+ model,
+ (LLViewerInventoryCategory*)inv_cat,
+ mUUID,
+ false);
+ if (cb) cb->fire(inv_cat->getUUID());
+ }
+ else
+ {
+ assert(false); // mot permitted, shouldn't have accepted
+ }
+ break;
+ default:
+ break;
+ }
}
// if target is current outfit folder we use link
else if (move_is_into_current_outfit &&
@@ -4398,6 +4496,7 @@ void LLFolderBridge::perform_pasteFromClipboard()
if (move_is_into_outfit && 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()) // Unable to copy&paste into outfits anymore
@@ -4406,7 +4505,23 @@ void LLFolderBridge::perform_pasteFromClipboard()
U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit");
if (cat && can_move_to_my_outfits(model, cat, max_items_to_wear))
{
- dropToMyOutfits(cat, cb);
+ if (mUUID == my_outifts_id)
+ {
+ dropToMyOutfits(cat, cb);
+ }
+ else if (move_is_into_my_outfits)
+ {
+ 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
{
@@ -4668,15 +4783,11 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
}
//
- // Fix "outfits" context menu
- //if (outfits_id == mUUID)
- if (model->isObjectDescendentOf(mUUID, outfits_id) && getCategory() &&
- (getCategory()->getPreferredType() == LLFolderType::FT_NONE ||
- getCategory()->getPreferredType() == LLFolderType::FT_MY_OUTFITS))
+ if (outfits_id == mUUID)
{
+ items.push_back(std::string("New Outfit Folder"));
items.push_back(std::string("New Outfit"));
}
- //
if (lost_and_found_id == mUUID)
{
@@ -4789,61 +4900,19 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
) // 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 (cat)
{
- 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;
- }
- // Fix "outfits" context menu
- //if (!isMarketplaceListingsFolder())
- if (!isMarketplaceListingsFolder() && !model->isObjectDescendentOf(mUUID, outfits_id))
- //
- {
- items.push_back(std::string("upload_def"));
- //items.push_back(std::string("create_new")); // Undo weird menu design
- 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
- {
- // 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")); // Normal subfolder underneath an outfit folder makes no sense
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"));
@@ -4857,6 +4926,67 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
}
//
}
+ 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")); // Undo weird menu design
+ 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)
@@ -5058,7 +5188,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
@@ -5910,13 +6044,24 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointergetUUID(), _1, cb, mInventoryPanel);
- gInventory.createNewCategory(dest_id,
+ getInventoryModel()->createNewCategory(dest_id,
LLFolderType::FT_OUTFIT,
inv_cat->getName(),
func,
inv_cat->getThumbnailUUID());
}
+void LLFolderBridge::dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest_id, LLFolderType::EType preferred_type, LLPointer cb)
+{
+ const LLUUID outfits_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+ inventory_func_type func = boost::bind(outfitFolderCreatedCallback, inv_cat->getUUID(), _1, cb, mInventoryPanel);
+ getInventoryModel()->createNewCategory(dest_id,
+ preferred_type,
+ inv_cat->getName(),
+ func,
+ inv_cat->getThumbnailUUID());
+}
+
void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id,
LLUUID cat_dest_id,
LLPointer cb,
@@ -6113,7 +6258,9 @@ bool LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
}
else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit))
{
- accept = 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);
}
// FIRE-1392: Allow dragging all asset types into Landmarks folder
//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 3fe1e4a603..95d630631a 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -397,6 +397,7 @@ protected:
void dropToFavorites(LLInventoryItem* inv_item, LLPointer cb = NULL);
void dropToOutfit(LLInventoryItem* inv_item, bool move_is_into_current_outfit, LLPointer cb = NULL);
void dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer cb = NULL);
+ void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest, LLFolderType::EType preferred_type, LLPointer cb = NULL);
//--------------------------------------------------------------------
// Messy hacks for handling folder options
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index e6c88399a9..26aef01e91 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -2654,6 +2654,40 @@ bool can_share_item(const LLUUID& item_id)
return can_share;
}
+
+EMyOutfitsSubfolderType myoutfit_object_subfolder_type(
+ LLInventoryModel* model,
+ const LLUUID& obj_id,
+ const LLUUID& my_outfits_id)
+{
+ if (obj_id == my_outfits_id) return MY_OUTFITS_NO;
+
+ const LLViewerInventoryCategory* test_cat = model->getCategory(obj_id);
+ if (test_cat->getPreferredType() == LLFolderType::FT_OUTFIT)
+ {
+ return MY_OUTFITS_OUTFIT;
+ }
+ while (test_cat)
+ {
+ if (test_cat->getPreferredType() == LLFolderType::FT_OUTFIT)
+ {
+ return MY_OUTFITS_SUBOUTFIT;
+ }
+
+ const LLUUID& parent_id = test_cat->getParentUUID();
+ if (parent_id.isNull())
+ {
+ return MY_OUTFITS_NO;
+ }
+ if (parent_id == my_outfits_id)
+ {
+ return MY_OUTFITS_SUBFOLDER;
+ }
+ test_cat = model->getCategory(parent_id);
+ }
+
+ return MY_OUTFITS_NO;
+}
///----------------------------------------------------------------------------
/// LLMarketplaceValidator implementations
///----------------------------------------------------------------------------
@@ -2782,6 +2816,11 @@ bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(const LLInventoryIte
return false;
}
+bool LLIsFolderType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ return cat && cat->getPreferredType() == mType;
+}
+
bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
{
if(mType == LLAssetType::AT_CATEGORY)
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index 97f7649aff..4dde2c4b73 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -128,6 +128,18 @@ std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& i
std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id);
bool can_share_item(const LLUUID& item_id);
+enum EMyOutfitsSubfolderType
+{
+ MY_OUTFITS_NO,
+ MY_OUTFITS_SUBFOLDER,
+ MY_OUTFITS_OUTFIT,
+ MY_OUTFITS_SUBOUTFIT,
+};
+EMyOutfitsSubfolderType myoutfit_object_subfolder_type(
+ LLInventoryModel* model,
+ const LLUUID& obj_id,
+ const LLUUID& my_outfits_id);
+
/** Miscellaneous global functions
** **
*******************************************************************************/
@@ -241,6 +253,24 @@ protected:
// the type is the type passed in during construction.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLIsFolderType : public LLInventoryCollectFunctor
+{
+public:
+ LLIsFolderType(LLFolderType::EType type) : mType(type) {}
+ virtual ~LLIsFolderType() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ LLFolderType::EType mType;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIsType
+//
+// Implementation of a LLInventoryCollectFunctor which returns true if
+// the type is the type passed in during construction.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
class LLIsType : public LLInventoryCollectFunctor
{
public:
diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp
index 733fde86d1..9b5859738d 100644
--- a/indra/newview/llinventorygallery.cpp
+++ b/indra/newview/llinventorygallery.cpp
@@ -60,10 +60,12 @@ static LLPanelInjector 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
+
// 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, LLFolderType::EType preferred_type);
class LLGalleryPanel: public LLPanel
{
@@ -3751,7 +3753,12 @@ bool dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat,
U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit");
if (is_movable && move_is_into_outfit)
{
- if (dest_id == my_outifts_id)
+ if ((inv_cat->getPreferredType() != LLFolderType::FT_NONE) && (inv_cat->getPreferredType() != LLFolderType::FT_OUTFIT))
+ {
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
+ is_movable = false;
+ }
+ else if (dest_id == my_outifts_id)
{
if (source != LLToolDragAndDrop::SOURCE_AGENT || move_is_from_marketplacelistings)
{
@@ -3768,13 +3775,39 @@ bool dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat,
is_movable = false;
}
}
- else if (dest_cat && dest_cat->getPreferredType() == LLFolderType::FT_NONE)
+ else if (!dest_cat)
{
- is_movable = ((inv_cat->getPreferredType() == LLFolderType::FT_NONE) || (inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT));
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
}
else
{
- is_movable = false;
+ EMyOutfitsSubfolderType dest_res = myoutfit_object_subfolder_type(model, dest_id, my_outifts_id);
+ EMyOutfitsSubfolderType inv_res = myoutfit_object_subfolder_type(model, cat_id, my_outifts_id);
+ if ((dest_res == MY_OUTFITS_OUTFIT || dest_res == MY_OUTFITS_SUBOUTFIT) && inv_res == MY_OUTFITS_OUTFIT)
+ {
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantMoveOutfitIntoOutfit");
+ }
+ else if ((dest_res == MY_OUTFITS_OUTFIT || dest_res == MY_OUTFITS_SUBOUTFIT) && inv_res == MY_OUTFITS_SUBFOLDER)
+ {
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
+ }
+ else if (dest_res == MY_OUTFITS_SUBFOLDER && inv_res == MY_OUTFITS_SUBOUTFIT)
+ {
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
+ }
+ else if (can_move_to_my_outfits(model, inv_cat, max_items_to_wear))
+ {
+ is_movable = true;
+ }
+ else
+ {
+ is_movable = false;
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
+ }
}
}
if (is_movable && move_is_into_current_outfit && is_link)
@@ -3900,9 +3933,70 @@ bool dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat,
if (dest_id == my_outifts_id)
{
- // Category can contains objects,
- // create a new folder and populate it with links to original objects
- dropToMyOutfits(inv_cat);
+ EMyOutfitsSubfolderType inv_res = myoutfit_object_subfolder_type(model, cat_id, my_outifts_id);
+ if (inv_res == MY_OUTFITS_SUBFOLDER || inv_res == MY_OUTFITS_OUTFIT)
+ {
+ gInventory.changeCategoryParent(
+ (LLViewerInventoryCategory*)inv_cat,
+ dest_id,
+ move_is_into_trash);
+ }
+ else
+ {
+ // Category can contains objects,
+ // create a new folder and populate it with links to original objects
+ dropToMyOutfits(inv_cat);
+ }
+ }
+ else if (move_is_into_my_outfits)
+ {
+ EMyOutfitsSubfolderType dest_res = myoutfit_object_subfolder_type(model, dest_id, my_outifts_id);
+ EMyOutfitsSubfolderType inv_res = myoutfit_object_subfolder_type(model, cat_id, my_outifts_id);
+ switch (inv_res)
+ {
+ case MY_OUTFITS_NO:
+ // Moning from outside outfits into outfits
+ if (dest_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);
+ }
+ break;
+ case MY_OUTFITS_SUBFOLDER:
+ case MY_OUTFITS_OUTFIT:
+ // only permit moving subfodlers and outfits into other subfolders
+ if (dest_res == MY_OUTFITS_SUBFOLDER)
+ {
+ gInventory.changeCategoryParent(
+ (LLViewerInventoryCategory*)inv_cat,
+ dest_id,
+ move_is_into_trash);
+ }
+ else
+ {
+ assert(false); // mot permitted, shouldn't have accepted
+ }
+ break;
+ case MY_OUTFITS_SUBOUTFIT:
+ if (dest_res == MY_OUTFITS_SUBOUTFIT || dest_res == MY_OUTFITS_OUTFIT)
+ {
+ gInventory.changeCategoryParent(
+ (LLViewerInventoryCategory*)inv_cat,
+ dest_id,
+ move_is_into_trash);
+ }
+ else
+ {
+ assert(false); // mot permitted, shouldn't have accepted
+ }
+ break;
+ default:
+ break;
+ }
}
// if target is current outfit folder we use link
else if (move_is_into_current_outfit &&
@@ -4047,3 +4141,11 @@ void dropToMyOutfits(LLInventoryCategory* inv_cat)
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());
}
+
+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, preferred_type, inv_cat->getName(), func, inv_cat->getThumbnailUUID());
+}
diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp
index a202eeeebf..c073f9cbb5 100644
--- a/indra/newview/llinventorygallerymenu.cpp
+++ b/indra/newview/llinventorygallerymenu.cpp
@@ -598,7 +598,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));
@@ -737,7 +739,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))
@@ -781,15 +783,26 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
items.push_back(std::string("upload_def"));
}
- if(is_outfits && !isRootFolder())
+ if(is_outfits)
{
- items.push_back(std::string("New Outfit"));
+ EMyOutfitsSubfolderType res = myoutfit_object_subfolder_type(&gInventory, selected_id, my_outfits);
+ if (res != MY_OUTFITS_OUTFIT && res != MY_OUTFITS_SUBOUTFIT)
+ {
+ items.push_back(std::string("New Outfit"));
+ }
+ items.push_back(std::string("New Outfit Folder"));
+ items.push_back(std::string("Delete"));
+ items.push_back(std::string("Rename"));
+ if (!get_is_category_and_children_removable(&gInventory, selected_id, false))
+ {
+ disabled_items.push_back(std::string("Delete"));
+ }
}
items.push_back(std::string("Subfolder Separator"));
- if (!is_system_folder && !isRootFolder())
+ if (!is_system_folder && !isRootFolder() && !is_outfits)
{
- 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 278705f3f1..2255818acf 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -1093,7 +1093,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
@@ -3805,7 +3806,7 @@ bool LLInventoryModel::saveToFile(const std::string& filename,
fileXML.close();
- LL_INFOS(LOG_INV) << "Inventory saved: " << cat_count << " categories, " << it_count << " items." << LL_ENDL;
+ LL_INFOS(LOG_INV) << "Inventory saved: " << (S32)cat_count << " categories, " << (S32)it_count << " items." << LL_ENDL;
}
catch (...)
{
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index b947038ba7..4336dac460 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1242,6 +1242,12 @@ void LLMeshRepoThread::run()
LL_WARNS(LOG_MESH) << "Convex decomposition unable to be quit." << LL_ENDL;
}
}
+void LLMeshRepoThread::cleanup()
+{
+ mShuttingDown = true;
+ mSignal->broadcast();
+ mMeshThreadPool->close();
+}
// Mutex: LLMeshRepoThread::mMutex must be held on entry
void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
@@ -1565,6 +1571,11 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
[mesh_id, buffer, size]
()
{
+ if (gMeshRepo.mThread->isShuttingDown())
+ {
+ delete[] buffer;
+ return;
+ }
if (!gMeshRepo.mThread->skinInfoReceived(mesh_id, buffer, size))
{
// either header is faulty or something else overwrote the cache
@@ -2093,6 +2104,11 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
[params, mesh_id, lod, buffer, size]
()
{
+ if (gMeshRepo.mThread->isShuttingDown())
+ {
+ delete[] buffer;
+ return;
+ }
if (gMeshRepo.mThread->lodReceived(params, lod, buffer, size) == MESH_OK)
{
LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL;
@@ -3899,6 +3915,11 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body
[shrd_handler, data, data_size]
()
{
+ if (gMeshRepo.mThread->isShuttingDown())
+ {
+ delete[] data;
+ return;
+ }
LLMeshLODHandler* handler = (LLMeshLODHandler * )shrd_handler.get();
handler->processLod(data, data_size);
delete[] data;
@@ -4012,6 +4033,11 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /*
[shrd_handler, data, data_size]
()
{
+ if (gMeshRepo.mThread->isShuttingDown())
+ {
+ delete[] data;
+ return;
+ }
LLMeshSkinInfoHandler* handler = (LLMeshSkinInfoHandler*)shrd_handler.get();
handler->processSkin(data, data_size);
delete[] data;
@@ -4235,8 +4261,7 @@ void LLMeshRepository::shutdown()
mUploads[i]->discard() ; //discard the uploading requests.
}
- mThread->mSignal->broadcast();
- mThread->mMeshThreadPool->close();
+ mThread->cleanup();
while (!mThread->isStopped())
{
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 11c381f77c..4e91f82ed6 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -531,6 +531,8 @@ public:
~LLMeshRepoThread();
virtual void run();
+ void cleanup();
+ bool isShuttingDown() { return mShuttingDown; }
void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
@@ -611,6 +613,7 @@ private:
U8* getDiskCacheBuffer(S32 size);
S32 mDiskCacheBufferSize = 0;
U8* mDiskCacheBuffer = nullptr;
+ bool mShuttingDown = false;
};
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 3635f8c9b8..3dbe456dde 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -890,6 +890,50 @@ void LLOutfitListBase::observerCallback(const LLUUID& category_id)
refreshList(category_id);
}
+class LLIsOutfitListFolder : public LLInventoryCollectFunctor
+{
+public:
+ LLIsOutfitListFolder()
+ {
+ mOutfitsId = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+ }
+ virtual ~LLIsOutfitListFolder() {}
+
+ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) override
+ {
+ if (cat)
+ {
+ if (cat->getPreferredType() == LLFolderType::FT_OUTFIT)
+ {
+ return true;
+ }
+ if (cat->getPreferredType() == LLFolderType::FT_NONE
+ && cat->getParentUUID() == mOutfitsId)
+ {
+ LLViewerInventoryCategory* inv_cat = dynamic_cast(cat);
+ if (inv_cat && inv_cat->getDescendentCount() > 3)
+ {
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items);
+ 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
+ // Todo: stop counting FT_NONE as outfits,
+ // convert obvious outfits into FT_OUTFIT
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+protected:
+ LLUUID mOutfitsId;
+};
+
void LLOutfitListBase::refreshList(const LLUUID& category_id)
{
bool wasNull = mRefreshListState.CategoryUUID.isNull();
@@ -899,36 +943,13 @@ void LLOutfitListBase::refreshList(const LLUUID& category_id)
LLInventoryModel::item_array_t item_array;
// Collect all sub-categories of a given category.
- // FIRE-6958/VWR-2862; Make sure to only collect folders of type FT_OUTFIT
- class ndOutfitsCollector: public LLIsType
- {
- public:
- ndOutfitsCollector()
- : LLIsType( LLAssetType::AT_CATEGORY )
- { }
-
- virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
- {
- if( !LLIsType::operator()( cat, item ) )
- return false;
-
- if( cat && LLFolderType::FT_OUTFIT == cat->getPreferredType() )
- return true;
-
- return false;
- }
- };
-
- // LLIsType is_category(LLAssetType::AT_CATEGORY);
- ndOutfitsCollector is_category;
- //
-
+ LLIsOutfitListFolder is_outfit;
gInventory.collectDescendentsIf(
category_id,
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH,
- is_category);
+ is_outfit);
// Memorize item names for each UUID
std::map names;
diff --git a/indra/newview/skins/default/xui/de/menu_inventory.xml b/indra/newview/skins/default/xui/de/menu_inventory.xml
index c8f4aa4aeb..dc72a6bbe4 100644
--- a/indra/newview/skins/default/xui/de/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/de/menu_inventory.xml
@@ -19,6 +19,7 @@
+
diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml
index bc45f1d3bd..673008f518 100644
--- a/indra/newview/skins/default/xui/de/strings.xml
+++ b/indra/newview/skins/default/xui/de/strings.xml
@@ -667,6 +667,9 @@ Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten ha
Ein oder mehrere Objekte können nicht innerhalb von „Outfits“ verwendet werden.
+
+ Outfit kann nicht in ein anderes Outfit verschoben werden.
+
Sie können einen Ordner nicht in einen seiner untergeordneten Ordner verschieben
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index eb1c7bf0e5..c764c23d9e 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -159,6 +159,14 @@
function="Inventory.DoCreate"
parameter="category" />
+
+
+
All items in a stock folder must have the same type and permission
You can only put items or outfits from your personal inventory into "Outfits"
One or more items can't be used inside "Outfits"
+ Can not move an outfit into another outfit
You can't move a folder into its child
You can't move a folder into itself