completed EXT-6719 Utilize the "description" field of outfit wearable links to store order information
- COF items get checked for correct ordering when COF is loaded (LLAppearanceMgr::updateAppearanceFromCOF..., LLAppearanceMgr::updateClothingOrderingInfo() ), and also when COF is saved to a new outfit, base outfit. - wearables are arranged in LLAgentWearables in a proper order * updateClothingOrderingInfo() [struggling with naming] does integrity checking and updates "invalid" items (items with wrong, gapped descriptions) * moved LLAgentWearables::makeNewOutfitLinks(...) and LLShowCreatedOutfit to a more natural place (llappearancemgr.cpp), because they use LLAppearanceMgr's methods a lot * changed link_inventory_item(...), added the "new_description" parameter Reviewed by Neal Orman at https://codereview.productengine.com/secondlife/r/268/ --HG-- branch : product-enginemeow-7.2.2
parent
5acf8a5f13
commit
ccd0418ddf
|
|
@ -1361,24 +1361,6 @@ private:
|
|||
LLUUID mFolderID;
|
||||
};
|
||||
|
||||
LLUUID LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name)
|
||||
{
|
||||
if (!isAgentAvatarValid()) return LLUUID::null;
|
||||
|
||||
// First, make a folder in the My Outfits directory.
|
||||
const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
|
||||
LLUUID folder_id = gInventory.createNewCategory(
|
||||
parent_id,
|
||||
LLFolderType::FT_OUTFIT,
|
||||
new_folder_name);
|
||||
|
||||
LLPointer<LLInventoryCallback> cb = new LLShowCreatedOutfit(folder_id);
|
||||
LLAppearanceMgr::instance().shallowCopyCategoryContents(LLAppearanceMgr::instance().getCOF(),folder_id, cb);
|
||||
LLAppearanceMgr::instance().createBaseOutfitLink(folder_id, cb);
|
||||
|
||||
return folder_id;
|
||||
}
|
||||
|
||||
void LLAgentWearables::makeNewOutfitDone(S32 type, U32 index)
|
||||
{
|
||||
LLUUID first_item_id = getWearableItemID((EWearableType)type, index);
|
||||
|
|
|
|||
|
|
@ -169,8 +169,7 @@ public:
|
|||
const LLDynamicArray<S32>& wearables_to_include,
|
||||
const LLDynamicArray<S32>& attachments_to_include,
|
||||
BOOL rename_clothing);
|
||||
|
||||
LLUUID makeNewOutfitLinks(const std::string& new_folder_name);
|
||||
|
||||
|
||||
// Should only be called if we *know* we've never done so before, since users may
|
||||
// not want the Library outfits to stay in their quick outfit selector and can delete them.
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ public:
|
|||
item->getLinkedUUID(),
|
||||
LLAppearanceMgr::instance().getCOF(),
|
||||
item->getName(),
|
||||
item->getDescription(),
|
||||
LLAssetType::AT_LINK,
|
||||
link_waiter);
|
||||
}
|
||||
|
|
@ -507,6 +508,7 @@ void LLLibraryOutfitsFetch::contentsDone()
|
|||
item->getLinkedUUID(),
|
||||
new_outfit_folder_id,
|
||||
item->getName(),
|
||||
item->getDescription(),
|
||||
LLAssetType::AT_LINK,
|
||||
NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
|
||||
#include "llaccordionctrltab.h"
|
||||
#include "llagent.h"
|
||||
#include "llagentwearables.h"
|
||||
#include "llappearancemgr.h"
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
#include "llinventoryfunctions.h"
|
||||
#include "llinventoryobserver.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "llpaneloutfitsinventory.h"
|
||||
#include "llselectmgr.h"
|
||||
#include "llsidepanelappearance.h"
|
||||
#include "llsidetray.h"
|
||||
|
|
@ -51,6 +53,8 @@
|
|||
#include "llviewerregion.h"
|
||||
#include "llwearablelist.h"
|
||||
|
||||
char ORDER_NUMBER_SEPARATOR('@');
|
||||
|
||||
LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id, const std::string& name)
|
||||
{
|
||||
LLInventoryModel::cat_array_t cat_array;
|
||||
|
|
@ -400,6 +404,7 @@ public:
|
|||
item_id,
|
||||
LLAppearanceMgr::instance().getCOF(),
|
||||
itemp->getName(),
|
||||
itemp->getDescription(),
|
||||
LLAssetType::AT_LINK,
|
||||
cb);
|
||||
}
|
||||
|
|
@ -691,10 +696,13 @@ void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LL
|
|||
{
|
||||
case LLAssetType::AT_LINK:
|
||||
{
|
||||
//LLInventoryItem::getDescription() is used for a new description
|
||||
//to propagate ordering information saved in descriptions of links
|
||||
link_inventory_item(gAgent.getID(),
|
||||
item->getLinkedUUID(),
|
||||
dst_id,
|
||||
item->getName(),
|
||||
item->LLInventoryItem::getDescription(),
|
||||
LLAssetType::AT_LINK, cb);
|
||||
break;
|
||||
}
|
||||
|
|
@ -708,6 +716,7 @@ void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LL
|
|||
item->getLinkedUUID(),
|
||||
dst_id,
|
||||
item->getName(),
|
||||
item->getDescription(),
|
||||
LLAssetType::AT_LINK_FOLDER, cb);
|
||||
}
|
||||
break;
|
||||
|
|
@ -811,20 +820,7 @@ void LLAppearanceMgr::filterWearableItems(
|
|||
{
|
||||
// Divvy items into arrays by wearable type.
|
||||
std::vector<LLInventoryModel::item_array_t> items_by_type(WT_COUNT);
|
||||
for (S32 i=0; i<items.count(); i++)
|
||||
{
|
||||
LLViewerInventoryItem *item = items.get(i);
|
||||
// Ignore non-wearables.
|
||||
if (!item->isWearableType())
|
||||
continue;
|
||||
EWearableType type = item->getWearableType();
|
||||
if(type < 0 || type >= WT_COUNT)
|
||||
{
|
||||
LL_WARNS("Appearance") << "Invalid wearable type. Inventory type does not match wearable flag bitfield." << LL_ENDL;
|
||||
continue;
|
||||
}
|
||||
items_by_type[type].push_back(item);
|
||||
}
|
||||
divvyWearablesByType(items, items_by_type);
|
||||
|
||||
// rebuild items list, retaining the last max_per_type of each array
|
||||
items.clear();
|
||||
|
|
@ -853,6 +849,7 @@ void LLAppearanceMgr::linkAll(const LLUUID& category,
|
|||
item->getLinkedUUID(),
|
||||
category,
|
||||
item->getName(),
|
||||
item->LLInventoryItem::getDescription(),
|
||||
LLAssetType::AT_LINK,
|
||||
cb);
|
||||
}
|
||||
|
|
@ -956,7 +953,7 @@ void LLAppearanceMgr::createBaseOutfitLink(const LLUUID& category, LLPointer<LLI
|
|||
|
||||
if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
|
||||
{
|
||||
link_inventory_item(gAgent.getID(), category, cof, catp->getName(),
|
||||
link_inventory_item(gAgent.getID(), category, cof, catp->getName(), "",
|
||||
LLAssetType::AT_LINK_FOLDER, link_waiter);
|
||||
new_outfit_name = catp->getName();
|
||||
}
|
||||
|
|
@ -1016,6 +1013,18 @@ static void remove_non_link_items(LLInventoryModel::item_array_t &items)
|
|||
items = pruned_items;
|
||||
}
|
||||
|
||||
//a predicate for sorting inventory items by actual descriptions
|
||||
bool sort_by_description(const LLInventoryItem* item1, const LLInventoryItem* item2)
|
||||
{
|
||||
if (!item1 || !item2)
|
||||
{
|
||||
llwarning("either item1 or item2 is NULL", 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return item1->LLInventoryItem::getDescription() < item2->LLInventoryItem::getDescription();
|
||||
}
|
||||
|
||||
void LLAppearanceMgr::updateAppearanceFromCOF()
|
||||
{
|
||||
// update dirty flag to see if the state of the COF matches
|
||||
|
|
@ -1026,6 +1035,8 @@ void LLAppearanceMgr::updateAppearanceFromCOF()
|
|||
|
||||
dumpCat(getCOF(),"COF, start");
|
||||
|
||||
updateClothingOrderingInfo();
|
||||
|
||||
bool follow_folder_links = true;
|
||||
LLUUID current_outfit_id = getCOF();
|
||||
|
||||
|
|
@ -1046,6 +1057,9 @@ void LLAppearanceMgr::updateAppearanceFromCOF()
|
|||
return;
|
||||
}
|
||||
|
||||
//preparing the list of wearables in the correct order for LLAgentWearables
|
||||
std::sort(wear_items.begin(), wear_items.end(), sort_by_description);
|
||||
|
||||
LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
|
||||
|
||||
holder->mObjItems = obj_items;
|
||||
|
|
@ -1079,8 +1093,8 @@ void LLAppearanceMgr::updateAppearanceFromCOF()
|
|||
|
||||
}
|
||||
#endif
|
||||
|
||||
holder->mFoundList.push_front(found);
|
||||
//pushing back, not front, to preserve order of wearables for LLAgentWearables
|
||||
holder->mFoundList.push_back(found);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1407,7 +1421,7 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update
|
|||
// Are these links to different items of the same wearable
|
||||
// type? If so, new item will replace old.
|
||||
// MULTI-WEARABLES: revisit if more than one per type is allowed.
|
||||
else if (areMatchingWearables(vitem,inv_item))
|
||||
else if (FALSE/*areMatchingWearables(vitem,inv_item)*/)
|
||||
{
|
||||
if (inv_item->getIsLinkType())
|
||||
{
|
||||
|
|
@ -1430,6 +1444,7 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update
|
|||
vitem->getLinkedUUID(),
|
||||
getCOF(),
|
||||
vitem->getName(),
|
||||
vitem->getDescription(),
|
||||
LLAssetType::AT_LINK,
|
||||
cb);
|
||||
}
|
||||
|
|
@ -1446,6 +1461,7 @@ void LLAppearanceMgr::addEnsembleLink( LLInventoryCategory* cat, bool do_update
|
|||
cat->getLinkedUUID(),
|
||||
getCOF(),
|
||||
cat->getName(),
|
||||
cat->getDescription(),
|
||||
LLAssetType::AT_LINK_FOLDER,
|
||||
cb);
|
||||
#endif
|
||||
|
|
@ -1572,6 +1588,8 @@ bool LLAppearanceMgr::updateBaseOutfit()
|
|||
const LLUUID base_outfit_id = getBaseOutfitUUID();
|
||||
if (base_outfit_id.isNull()) return false;
|
||||
|
||||
updateClothingOrderingInfo();
|
||||
|
||||
// in a Base Outfit we do not remove items, only links
|
||||
purgeCategory(base_outfit_id, false);
|
||||
|
||||
|
|
@ -1581,6 +1599,168 @@ bool LLAppearanceMgr::updateBaseOutfit()
|
|||
return true;
|
||||
}
|
||||
|
||||
void LLAppearanceMgr::divvyWearablesByType(const LLInventoryModel::item_array_t& items, wearables_by_type_t& items_by_type)
|
||||
{
|
||||
items_by_type.reserve(WT_COUNT);
|
||||
if (items.empty()) return;
|
||||
|
||||
for (S32 i=0; i<items.count(); i++)
|
||||
{
|
||||
LLViewerInventoryItem *item = items.get(i);
|
||||
// Ignore non-wearables.
|
||||
if (!item->isWearableType())
|
||||
continue;
|
||||
EWearableType type = item->getWearableType();
|
||||
if(type < 0 || type >= WT_COUNT)
|
||||
{
|
||||
LL_WARNS("Appearance") << "Invalid wearable type. Inventory type does not match wearable flag bitfield." << LL_ENDL;
|
||||
continue;
|
||||
}
|
||||
items_by_type[type].push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
std::string build_order_string(EWearableType type, U32 i)
|
||||
{
|
||||
std::ostringstream order_num;
|
||||
order_num << ORDER_NUMBER_SEPARATOR << type * 100 + i;
|
||||
return order_num.str();
|
||||
}
|
||||
|
||||
struct WearablesOrderComparator
|
||||
{
|
||||
WearablesOrderComparator(const EWearableType type)
|
||||
{
|
||||
mControlSize = build_order_string(type, 0).size();
|
||||
};
|
||||
|
||||
bool operator()(const LLInventoryItem* item1, const LLInventoryItem* item2)
|
||||
{
|
||||
if (!item1 || !item2)
|
||||
{
|
||||
llwarning("either item1 or item2 is NULL", 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& desc1 = item1->LLInventoryItem::getDescription();
|
||||
const std::string& desc2 = item2->LLInventoryItem::getDescription();
|
||||
|
||||
bool item1_valid = (desc1.size() == mControlSize) && (ORDER_NUMBER_SEPARATOR == desc1[0]);
|
||||
bool item2_valid = (desc2.size() == mControlSize) && (ORDER_NUMBER_SEPARATOR == desc2[0]);
|
||||
|
||||
if (item1_valid && item2_valid)
|
||||
return desc1 < desc2;
|
||||
|
||||
//we need to sink down invalid items: items with empty descriptions, items with "Broken link" descriptions,
|
||||
//items with ordering information but not for the associated wearables type
|
||||
if (!item1_valid && item2_valid)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 mControlSize;
|
||||
};
|
||||
|
||||
void LLAppearanceMgr::updateClothingOrderingInfo()
|
||||
{
|
||||
LLInventoryModel::item_array_t wear_items;
|
||||
getDescendentsOfAssetType(getCOF(), wear_items, LLAssetType::AT_CLOTHING, false);
|
||||
|
||||
wearables_by_type_t items_by_type(WT_COUNT);
|
||||
divvyWearablesByType(wear_items, items_by_type);
|
||||
|
||||
bool inventory_changed = false;
|
||||
for (U32 type = WT_SHIRT; type < WT_COUNT; type++)
|
||||
{
|
||||
|
||||
U32 size = items_by_type[type].size();
|
||||
if (!size) continue;
|
||||
|
||||
//sinking down invalid items which need reordering
|
||||
std::sort(items_by_type[type].begin(), items_by_type[type].end(), WearablesOrderComparator((EWearableType) type));
|
||||
|
||||
//requesting updates only for those links which don't have "valid" descriptions
|
||||
for (U32 i = 0; i < size; i++)
|
||||
{
|
||||
LLViewerInventoryItem* item = items_by_type[type][i];
|
||||
if (!item) continue;
|
||||
|
||||
std::string new_order_str = build_order_string((EWearableType)type, i);
|
||||
if (new_order_str == item->LLInventoryItem::getDescription()) continue;
|
||||
|
||||
item->setDescription(new_order_str);
|
||||
item->setComplete(TRUE);
|
||||
item->updateServer(FALSE);
|
||||
gInventory.updateItem(item);
|
||||
inventory_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
//*TODO do we really need to notify observers?
|
||||
if (inventory_changed) gInventory.notifyObservers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class LLShowCreatedOutfit: public LLInventoryCallback
|
||||
{
|
||||
public:
|
||||
LLShowCreatedOutfit(LLUUID& folder_id): mFolderID(folder_id)
|
||||
{}
|
||||
|
||||
virtual ~LLShowCreatedOutfit()
|
||||
{
|
||||
LLSD key;
|
||||
LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key);
|
||||
LLPanelOutfitsInventory *outfit_panel =
|
||||
dynamic_cast<LLPanelOutfitsInventory*>(LLSideTray::getInstance()->getPanel("panel_outfits_inventory"));
|
||||
if (outfit_panel)
|
||||
{
|
||||
outfit_panel->getRootFolder()->clearSelection();
|
||||
outfit_panel->getRootFolder()->setSelectionByID(mFolderID, TRUE);
|
||||
}
|
||||
|
||||
LLAccordionCtrlTab* tab_outfits = outfit_panel ? outfit_panel->findChild<LLAccordionCtrlTab>("tab_outfits") : 0;
|
||||
if (tab_outfits && !tab_outfits->getDisplayChildren())
|
||||
{
|
||||
tab_outfits->changeOpenClose(tab_outfits->getDisplayChildren());
|
||||
}
|
||||
|
||||
LLAppearanceMgr::getInstance()->updateIsDirty();
|
||||
LLAppearanceMgr::getInstance()->updatePanelOutfitName("");
|
||||
}
|
||||
|
||||
virtual void fire(const LLUUID&)
|
||||
{}
|
||||
|
||||
private:
|
||||
LLUUID mFolderID;
|
||||
};
|
||||
|
||||
LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name)
|
||||
{
|
||||
if (!isAgentAvatarValid()) return LLUUID::null;
|
||||
|
||||
// First, make a folder in the My Outfits directory.
|
||||
const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
|
||||
LLUUID folder_id = gInventory.createNewCategory(
|
||||
parent_id,
|
||||
LLFolderType::FT_OUTFIT,
|
||||
new_folder_name);
|
||||
|
||||
updateClothingOrderingInfo();
|
||||
|
||||
LLPointer<LLInventoryCallback> cb = new LLShowCreatedOutfit(folder_id);
|
||||
shallowCopyCategoryContents(getCOF(),folder_id, cb);
|
||||
createBaseOutfitLink(folder_id, cb);
|
||||
|
||||
dumpCat(folder_id,"COF, new outfit");
|
||||
|
||||
return folder_id;
|
||||
}
|
||||
|
||||
void LLAppearanceMgr::wearBaseOutfit()
|
||||
{
|
||||
const LLUUID& base_outfit_id = getBaseOutfitUUID();
|
||||
|
|
|
|||
|
|
@ -138,12 +138,23 @@ public:
|
|||
//Remove clothing or detach an object from the agent (a bodypart cannot be removed)
|
||||
void removeItemFromAvatar(const LLUUID& item_id);
|
||||
|
||||
|
||||
LLUUID makeNewOutfitLinks(const std::string& new_folder_name);
|
||||
|
||||
protected:
|
||||
LLAppearanceMgr();
|
||||
~LLAppearanceMgr();
|
||||
|
||||
private:
|
||||
|
||||
typedef std::vector<LLInventoryModel::item_array_t> wearables_by_type_t;
|
||||
|
||||
//Divvy items into arrays by wearable type
|
||||
static void divvyWearablesByType(const LLInventoryModel::item_array_t& items, wearables_by_type_t& items_by_type);
|
||||
|
||||
//Check ordering information on wearables stored in links' descriptions and update if it is invalid
|
||||
void updateClothingOrderingInfo();
|
||||
|
||||
void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type);
|
||||
|
||||
void getDescendentsOfAssetType(const LLUUID& category,
|
||||
|
|
|
|||
|
|
@ -2520,6 +2520,7 @@ void LLFolderBridge::pasteLinkFromClipboard()
|
|||
item->getLinkedUUID(),
|
||||
parent_id,
|
||||
item->getName(),
|
||||
item->getDescription(),
|
||||
LLAssetType::AT_LINK,
|
||||
LLPointer<LLInventoryCallback>(NULL));
|
||||
}
|
||||
|
|
@ -3166,6 +3167,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
|
|||
inv_item->getLinkedUUID(),
|
||||
mUUID,
|
||||
inv_item->getName(),
|
||||
inv_item->getDescription(),
|
||||
LLAssetType::AT_LINK,
|
||||
cb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ bool LLPanelOutfitsInventory::onSaveCommit(const LLSD& notification, const LLSD&
|
|||
LLStringUtil::trim(outfit_name);
|
||||
if( !outfit_name.empty() )
|
||||
{
|
||||
LLUUID outfit_folder = gAgentWearables.makeNewOutfitLinks(outfit_name);
|
||||
LLUUID outfit_folder = LLAppearanceMgr::getInstance()->makeNewOutfitLinks(outfit_name);
|
||||
|
||||
LLSidepanelAppearance* panel_appearance =
|
||||
dynamic_cast<LLSidepanelAppearance *>(LLSideTray::getInstance()->getPanel("sidepanel_appearance"));
|
||||
|
|
|
|||
|
|
@ -923,6 +923,7 @@ void link_inventory_item(
|
|||
const LLUUID& item_id,
|
||||
const LLUUID& parent_id,
|
||||
const std::string& new_name,
|
||||
const std::string& new_description,
|
||||
const LLAssetType::EType asset_type,
|
||||
LLPointer<LLInventoryCallback> cb)
|
||||
{
|
||||
|
|
@ -948,7 +949,6 @@ void link_inventory_item(
|
|||
}
|
||||
|
||||
LLUUID transaction_id;
|
||||
std::string desc = "Broken link"; // This should only show if the object can't find its baseobj.
|
||||
LLInventoryType::EType inv_type = LLInventoryType::IT_NONE;
|
||||
if (dynamic_cast<const LLInventoryCategory *>(baseobj))
|
||||
{
|
||||
|
|
@ -979,7 +979,7 @@ void link_inventory_item(
|
|||
msg->addS8Fast(_PREHASH_Type, (S8)asset_type);
|
||||
msg->addS8Fast(_PREHASH_InvType, (S8)inv_type);
|
||||
msg->addStringFast(_PREHASH_Name, new_name);
|
||||
msg->addStringFast(_PREHASH_Description, desc);
|
||||
msg->addStringFast(_PREHASH_Description, new_description);
|
||||
}
|
||||
gAgent.sendReliableMessage();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -339,6 +339,7 @@ void link_inventory_item(
|
|||
const LLUUID& item_id,
|
||||
const LLUUID& parent_id,
|
||||
const std::string& new_name,
|
||||
const std::string& new_description,
|
||||
const LLAssetType::EType asset_type,
|
||||
LLPointer<LLInventoryCallback> cb);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue