Merge branch 'release/2025.04' of https://github.com/secondlife/viewer

# Conflicts:
#	indra/newview/llinventorybridge.cpp
#	indra/newview/lloutfitslist.cpp
#	indra/newview/skins/default/xui/en/strings.xml
master
Ansariel 2025-04-13 17:47:57 +02:00
commit 03f622d0d4
17 changed files with 504 additions and 126 deletions

View File

@ -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();

View File

@ -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.

View File

@ -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,

View File

@ -127,7 +127,6 @@ void teleport_via_landmark(const LLUUID& asset_id);
// </FS:ND>
// 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))
// </FS:Ansariel>
{
// 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()) // <FS:Ansariel> 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
}
// </FS:Ansariel>
// <FS:Ansariel> 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"));
}
// </FS:Ansariel>
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;
}
// <FS:Ansariel> Fix "outfits" context menu
//if (!isMarketplaceListingsFolder())
if (!isMarketplaceListingsFolder() && !model->isObjectDescendentOf(mUUID, outfits_id))
// </FS:Ansariel>
{
items.push_back(std::string("upload_def"));
//items.push_back(std::string("create_new")); // <FS:Ansariel> 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")); // <FS:Ansariel> 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
}
// </FS:Ansariel>
}
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")); // <FS:Ansariel> 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, LLPointer<LLI
// 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, 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<LLInventoryCallback> 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<LLInventoryCallback> 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);
}
// <FS:Ansariel> FIRE-1392: Allow dragging all asset types into Landmarks folder
//else if (user_confirm && (move_is_into_favorites || move_is_into_landmarks))

View File

@ -397,6 +397,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, LLFolderType::EType preferred_type, LLPointer<LLInventoryCallback> cb = NULL);
//--------------------------------------------------------------------
// Messy hacks for handling folder options

View File

@ -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)

View File

@ -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:

View File

@ -60,10 +60,12 @@ 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
// 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());
}

View File

@ -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"));
}

View File

@ -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 (...)
{

View File

@ -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())
{

View File

@ -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;
};

View File

@ -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<LLViewerInventoryCategory*>(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.
// <FS:ND> 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;
// </FS:ND>
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<LLUUID, std::string> names;

View File

@ -19,6 +19,7 @@
<menu_item_call label="Fundstücke ausleeren" name="Empty Lost And Found"/>
<menu_item_call label="Neuer Ordner" name="New Folder"/>
<menu_item_call label="Neuer Ordner" name="New Listing Folder"/>
<menu_item_call label="Neuer Ordner" name="New Outfit Folder"/>
<menu_item_call label="Neues Outfit" name="New Outfit"/>
<menu_item_call label="Neues Skript" name="New Script"/>
<menu_item_call label="Neue Notizkarte" name="New Note"/>

View File

@ -667,6 +667,9 @@ Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten ha
<string name="TooltipCantCreateOutfit">
Ein oder mehrere Objekte können nicht innerhalb von „Outfits“ verwendet werden.
</string>
<string name="TooltipCantMoveOutfitIntoOutfit">
Outfit kann nicht in ein anderes Outfit verschoben werden.
</string>
<string name="TooltipDragOntoOwnChild">
Sie können einen Ordner nicht in einen seiner untergeordneten Ordner verschieben
</string>

View File

@ -159,6 +159,14 @@
function="Inventory.DoCreate"
parameter="category" />
</menu_item_call>
<menu_item_call
label="New Folder"
layout="topleft"
name="New Outfit Folder">
<menu_item_call.on_click
function="Inventory.DoCreate"
parameter="category" />
</menu_item_call>
<menu_item_call
label="New Outfit"
layout="topleft"

View File

@ -320,6 +320,7 @@ If you feel this is an error, please contact support@secondlife.com</string>
<string name="TooltipOutboxMixedStock">All items in a stock folder must have the same type and permission</string>
<string name="TooltipOutfitNotInInventory">You can only put items or outfits from your personal inventory into "Outfits"</string>
<string name="TooltipCantCreateOutfit">One or more items can't be used inside "Outfits"</string>
<string name="TooltipCantMoveOutfitIntoOutfit">Can not move an outfit into another outfit</string>
<string name="TooltipDragOntoOwnChild">You can't move a folder into its child</string>
<string name="TooltipDragOntoSelf">You can't move a folder into itself</string>