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-engine
meow-7.2.2
Igor Borovkov 2010-04-21 12:43:03 +03:00
parent 5acf8a5f13
commit ccd0418ddf
9 changed files with 218 additions and 41 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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