phoenix-firestorm/indra/newview/llappearancemgr.cpp

934 lines
26 KiB
C++

/**
* @file llappearancemgr.cpp
* @brief Manager for initiating appearance changes on the viewer
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llappearancemgr.h"
#include "llinventorymodel.h"
#include "llnotifications.h"
#include "llgesturemgr.h"
#include "llinventorybridge.h"
#include "llwearablelist.h"
#include "llagentwearables.h"
#include "llagent.h"
#include "llvoavatar.h"
#include "llvoavatarself.h"
#include "llviewerregion.h"
#include "llfloatercustomize.h"
class LLWearInventoryCategoryCallback : public LLInventoryCallback
{
public:
LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append)
{
mCatID = cat_id;
mAppend = append;
}
void fire(const LLUUID& item_id)
{
/*
* Do nothing. We only care about the destructor
*
* The reason for this is that this callback is used in a hack where the
* same callback is given to dozens of items, and the destructor is called
* after the last item has fired the event and dereferenced it -- if all
* the events actually fire!
*/
}
protected:
~LLWearInventoryCategoryCallback()
{
// Is the destructor called by ordinary dereference, or because the app's shutting down?
// If the inventory callback manager goes away, we're shutting down, no longer want the callback.
if( LLInventoryCallbackManager::is_instantiated() )
{
LLAppearanceManager::wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend);
}
else
{
llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
}
}
private:
LLUUID mCatID;
bool mAppend;
};
class LLOutfitObserver : public LLInventoryFetchObserver
{
public:
LLOutfitObserver(const LLUUID& cat_id, bool copy_items, bool append) :
mCatID(cat_id),
mCopyItems(copy_items),
mAppend(append)
{}
~LLOutfitObserver() {}
virtual void done(); //public
protected:
LLUUID mCatID;
bool mCopyItems;
bool mAppend;
};
void LLOutfitObserver::done()
{
// We now have an outfit ready to be copied to agent inventory. Do
// it, and wear that outfit normally.
if(mCopyItems)
{
LLInventoryCategory* cat = gInventory.getCategory(mCatID);
std::string name;
if(!cat)
{
// should never happen.
name = "New Outfit";
}
else
{
name = cat->getName();
}
LLViewerInventoryItem* item = NULL;
item_ref_t::iterator it = mComplete.begin();
item_ref_t::iterator end = mComplete.end();
LLUUID pid;
for(; it < end; ++it)
{
item = (LLViewerInventoryItem*)gInventory.getItem(*it);
if(item)
{
if(LLInventoryType::IT_GESTURE == item->getInventoryType())
{
pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_GESTURE);
}
else
{
pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING);
}
break;
}
}
if(pid.isNull())
{
pid = gInventory.getRootFolderID();
}
LLUUID cat_id = gInventory.createNewCategory(
pid,
LLAssetType::AT_NONE,
name);
mCatID = cat_id;
LLPointer<LLInventoryCallback> cb = new LLWearInventoryCategoryCallback(mCatID, mAppend);
it = mComplete.begin();
for(; it < end; ++it)
{
item = (LLViewerInventoryItem*)gInventory.getItem(*it);
if(item)
{
copy_inventory_item(
gAgent.getID(),
item->getPermissions().getOwner(),
item->getUUID(),
cat_id,
std::string(),
cb);
}
}
}
else
{
// Wear the inventory category.
LLAppearanceManager::wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend);
}
}
class LLOutfitFetch : public LLInventoryFetchDescendentsObserver
{
public:
LLOutfitFetch(bool copy_items, bool append) : mCopyItems(copy_items), mAppend(append) {}
~LLOutfitFetch() {}
virtual void done();
protected:
bool mCopyItems;
bool mAppend;
};
void LLOutfitFetch::done()
{
// What we do here is get the complete information on the items in
// the library, and set up an observer that will wait for that to
// happen.
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
gInventory.collectDescendents(mCompleteFolders.front(),
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH);
S32 count = item_array.count();
if(!count)
{
llwarns << "Nothing fetched in category " << mCompleteFolders.front()
<< llendl;
//dec_busy_count();
gInventory.removeObserver(this);
delete this;
return;
}
LLOutfitObserver* outfit_observer = new LLOutfitObserver(mCompleteFolders.front(), mCopyItems, mAppend);
LLInventoryFetchObserver::item_ref_t ids;
for(S32 i = 0; i < count; ++i)
{
ids.push_back(item_array.get(i)->getUUID());
}
// clean up, and remove this as an observer since the call to the
// outfit could notify observers and throw us into an infinite
// loop.
//dec_busy_count();
gInventory.removeObserver(this);
delete this;
// increment busy count and either tell the inventory to check &
// call done, or add this object to the inventory for observation.
//inc_busy_count();
// do the fetch
outfit_observer->fetchItems(ids);
if(outfit_observer->isEverythingComplete())
{
// everything is already here - call done.
outfit_observer->done();
}
else
{
// it's all on it's way - add an observer, and the inventory
// will call done for us when everything is here.
gInventory.addObserver(outfit_observer);
}
}
class LLUpdateAppearanceOnDestroy: public LLInventoryCallback
{
public:
LLUpdateAppearanceOnDestroy():
mFireCount(0)
{
}
virtual ~LLUpdateAppearanceOnDestroy()
{
LLAppearanceManager::updateAppearanceFromCOF();
}
/* virtual */ void fire(const LLUUID& inv_item)
{
mFireCount++;
}
private:
U32 mFireCount;
};
struct LLFoundData
{
LLFoundData(const LLUUID& item_id,
const LLUUID& asset_id,
const std::string& name,
LLAssetType::EType asset_type) :
mItemID(item_id),
mAssetID(asset_id),
mName(name),
mAssetType(asset_type),
mWearable( NULL ) {}
LLUUID mItemID;
LLUUID mAssetID;
std::string mName;
LLAssetType::EType mAssetType;
LLWearable* mWearable;
};
struct LLWearableHoldingPattern
{
LLWearableHoldingPattern() : mResolved(0) {}
~LLWearableHoldingPattern()
{
for_each(mFoundList.begin(), mFoundList.end(), DeletePointer());
mFoundList.clear();
}
typedef std::list<LLFoundData*> found_list_t;
found_list_t mFoundList;
S32 mResolved;
bool append;
};
void removeDuplicateItems(LLInventoryModel::item_array_t& dst, const LLInventoryModel::item_array_t& src)
{
LLInventoryModel::item_array_t new_dst;
std::set<LLUUID> mark_inventory;
std::set<LLUUID> mark_asset;
S32 inventory_dups = 0;
S32 asset_dups = 0;
for (LLInventoryModel::item_array_t::const_iterator src_pos = src.begin();
src_pos != src.end();
++src_pos)
{
LLUUID src_item_id = (*src_pos)->getLinkedUUID();
mark_inventory.insert(src_item_id);
LLUUID src_asset_id = (*src_pos)->getAssetUUID();
mark_asset.insert(src_asset_id);
}
for (LLInventoryModel::item_array_t::const_iterator dst_pos = dst.begin();
dst_pos != dst.end();
++dst_pos)
{
LLUUID dst_item_id = (*dst_pos)->getLinkedUUID();
if (mark_inventory.find(dst_item_id) == mark_inventory.end())
{
}
else
{
inventory_dups++;
}
LLUUID dst_asset_id = (*dst_pos)->getAssetUUID();
if (mark_asset.find(dst_asset_id) == mark_asset.end())
{
// Item is not already present in COF.
new_dst.put(*dst_pos);
mark_asset.insert(dst_item_id);
}
else
{
asset_dups++;
}
}
llinfos << "removeDups, original " << dst.count() << " final " << new_dst.count()
<< " inventory dups " << inventory_dups << " asset_dups " << asset_dups << llendl;
dst = new_dst;
}
/* static */ LLUUID LLAppearanceManager::getCOF()
{
return gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
}
// Update appearance from outfit folder.
/* static */ void LLAppearanceManager::changeOutfit(bool proceed, const LLUUID& category, bool append)
{
if (!proceed)
return;
if (append)
{
updateCOFFromCategory(category, append); // append is true - add non-duplicates to COF.
}
else
{
LLViewerInventoryCategory* catp = gInventory.getCategory(category);
if (catp->getPreferredType() == LLAssetType::AT_NONE ||
LLAssetType::lookupIsEnsembleCategoryType(catp->getPreferredType()))
{
updateCOFFromCategory(category, append); // append is false - rebuild COF.
}
else if (catp->getPreferredType() == LLAssetType::AT_OUTFIT)
{
rebuildCOFFromOutfit(category);
}
}
}
// Append to current COF contents by recursively traversing a folder.
/* static */ void LLAppearanceManager::updateCOFFromCategory(const LLUUID& category, bool append)
{
// BAP consolidate into one "get all 3 types of descendents" function, use both places.
LLInventoryModel::item_array_t wear_items;
LLInventoryModel::item_array_t obj_items;
LLInventoryModel::item_array_t gest_items;
bool follow_folder_links = false;
getUserDescendents(category, wear_items, obj_items, gest_items, follow_folder_links);
// Find all the wearables that are in the category's subtree.
lldebugs << "appendCOFFromCategory()" << llendl;
if( !wear_items.count() && !obj_items.count() && !gest_items.count())
{
LLNotifications::instance().add("CouldNotPutOnOutfit");
return;
}
const LLUUID &current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
// Processes that take time should show the busy cursor
//inc_busy_count();
LLInventoryModel::cat_array_t cof_cats;
LLInventoryModel::item_array_t cof_items;
gInventory.collectDescendents(current_outfit_id, cof_cats, cof_items,
LLInventoryModel::EXCLUDE_TRASH);
// Remove duplicates
if (append)
{
removeDuplicateItems(wear_items, cof_items);
removeDuplicateItems(obj_items, cof_items);
removeDuplicateItems(gest_items, cof_items);
}
S32 total_links = gest_items.count() + wear_items.count() + obj_items.count();
if (!append && total_links > 0)
{
// Remove all current outfit folder links since we're now replacing the contents.
for (S32 i = 0; i < cof_items.count(); ++i)
{
gInventory.purgeObject(cof_items.get(i)->getUUID());
}
}
LLPointer<LLUpdateAppearanceOnDestroy> link_waiter = new LLUpdateAppearanceOnDestroy;
// Link all gestures in this folder
if (gest_items.count() > 0)
{
llinfos << "Linking " << gest_items.count() << " gestures" << llendl;
for (S32 i = 0; i < gest_items.count(); ++i)
{
const LLInventoryItem* gest_item = gest_items.get(i).get();
link_inventory_item(gAgent.getID(), gest_item->getLinkedUUID(), current_outfit_id,
gest_item->getName(),
LLAssetType::AT_LINK, link_waiter);
}
}
// Link all wearables
if(wear_items.count() > 0)
{
llinfos << "Linking " << wear_items.count() << " wearables" << llendl;
for(S32 i = 0; i < wear_items.count(); ++i)
{
// Populate the current outfit folder with links to the newly added wearables
const LLInventoryItem* wear_item = wear_items.get(i).get();
link_inventory_item(gAgent.getID(),
wear_item->getLinkedUUID(), // If this item is a link, then we'll use the linked item's UUID.
current_outfit_id,
wear_item->getName(),
LLAssetType::AT_LINK,
link_waiter);
}
}
// Link all attachments.
if( obj_items.count() > 0 )
{
llinfos << "Linking " << obj_items.count() << " attachments" << llendl;
LLVOAvatar* avatar = gAgent.getAvatarObject();
if( avatar )
{
for(S32 i = 0; i < obj_items.count(); ++i)
{
const LLInventoryItem* obj_item = obj_items.get(i).get();
link_inventory_item(gAgent.getID(),
obj_item->getLinkedUUID(), // If this item is a link, then we'll use the linked item's UUID.
current_outfit_id,
obj_item->getName(),
LLAssetType::AT_LINK, link_waiter);
}
}
}
}
/* static */ void LLAppearanceManager::shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel::cat_array_t cats;
LLInventoryModel::item_array_t items;
gInventory.collectDescendents(src_id, cats, items,
LLInventoryModel::EXCLUDE_TRASH);
for (S32 i = 0; i < items.count(); ++i)
{
const LLViewerInventoryItem* item = items.get(i).get();
if (item->getActualType() == LLAssetType::AT_LINK)
{
link_inventory_item(gAgent.getID(),
item->getLinkedUUID(),
dst_id,
item->getName(),
LLAssetType::AT_LINK, cb);
}
else if (item->getActualType() == LLAssetType::AT_LINK_FOLDER)
{
link_inventory_item(gAgent.getID(),
item->getLinkedUUID(),
dst_id,
item->getName(),
LLAssetType::AT_LINK_FOLDER, cb);
}
else
{
copy_inventory_item(
gAgent.getID(),
item->getPermissions().getOwner(),
item->getUUID(),
dst_id,
item->getName(),
cb);
}
}
}
// Replace COF contents from a given outfit folder.
/* static */ void LLAppearanceManager::rebuildCOFFromOutfit(const LLUUID& category)
{
lldebugs << "rebuildCOFFromOutfit()" << llendl;
// Find all the wearables that are in the category's subtree.
LLInventoryModel::item_array_t items;
getCOFValidDescendents(category, items);
if( items.count() == 0)
{
LLNotifications::instance().add("CouldNotPutOnOutfit");
return;
}
const LLUUID &current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
// Processes that take time should show the busy cursor
//inc_busy_count();
LLInventoryModel::cat_array_t cof_cats;
LLInventoryModel::item_array_t cof_items;
gInventory.collectDescendents(current_outfit_id, cof_cats, cof_items,
LLInventoryModel::EXCLUDE_TRASH);
if (items.count() > 0)
{
// Remove all current outfit folder links since we're now replacing the contents.
for (S32 i = 0; i < cof_items.count(); ++i)
{
gInventory.purgeObject(cof_items.get(i)->getUUID());
}
}
LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy;
LLAppearanceManager::shallowCopyCategory(category, current_outfit_id, link_waiter);
// Create a link to the outfit that we wore.
LLViewerInventoryCategory* catp = gInventory.getCategory(category);
if (catp && catp->getPreferredType() == LLAssetType::AT_OUTFIT)
{
link_inventory_item(gAgent.getID(), category, current_outfit_id, catp->getName(),
LLAssetType::AT_LINK_FOLDER, LLPointer<LLInventoryCallback>(NULL));
}
}
/* static */
void LLAppearanceManager::onWearableAssetFetch(LLWearable* wearable, void* data)
{
LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
bool append = holder->append;
if(wearable)
{
for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
iter != holder->mFoundList.end(); ++iter)
{
LLFoundData* data = *iter;
if(wearable->getAssetID() == data->mAssetID)
{
data->mWearable = wearable;
break;
}
}
}
holder->mResolved += 1;
if(holder->mResolved >= (S32)holder->mFoundList.size())
{
LLAppearanceManager::updateAgentWearables(holder, append);
}
}
/* static */
void LLAppearanceManager::updateAgentWearables(LLWearableHoldingPattern* holder, bool append)
{
lldebugs << "updateAgentWearables()" << llendl;
LLInventoryItem::item_array_t items;
LLDynamicArray< LLWearable* > wearables;
// For each wearable type, find the first instance in the category
// that we recursed through.
for( S32 i = 0; i < WT_COUNT; i++ )
{
for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
iter != holder->mFoundList.end(); ++iter)
{
LLFoundData* data = *iter;
LLWearable* wearable = data->mWearable;
if( wearable && ((S32)wearable->getType() == i) )
{
LLViewerInventoryItem* item;
item = (LLViewerInventoryItem*)gInventory.getItem(data->mItemID);
if( item && (item->getAssetUUID() == wearable->getAssetID()) )
{
items.put(item);
wearables.put(wearable);
}
break;
}
}
}
if(wearables.count() > 0)
{
gAgentWearables.setWearableOutfit(items, wearables, !append);
gInventory.notifyObservers();
}
delete holder;
// dec_busy_count();
}
/* static */ void LLAppearanceManager::updateAppearanceFromCOF()
{
bool follow_folder_links = true;
LLUUID current_outfit_id = getCOF();
// Find all the wearables that are in the COF's subtree.
lldebugs << "LLAppearanceManager::updateFromCOF()" << llendl;
LLInventoryModel::item_array_t wear_items;
LLInventoryModel::item_array_t obj_items;
LLInventoryModel::item_array_t gest_items;
getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items, follow_folder_links);
if( !wear_items.count() && !obj_items.count() && !gest_items.count())
{
LLNotifications::instance().add("CouldNotPutOnOutfit");
return;
}
// Processes that take time should show the busy cursor
//inc_busy_count(); // BAP this is currently a no-op in llinventorybridge.cpp - do we need it?
// Activate all gestures in this folder
if (gest_items.count() > 0)
{
llinfos << "Activating " << gest_items.count() << " gestures" << llendl;
LLGestureManager::instance().activateGestures(gest_items);
// Update the inventory item labels to reflect the fact
// they are active.
LLViewerInventoryCategory* catp = gInventory.getCategory(current_outfit_id);
if (catp)
{
gInventory.updateCategory(catp);
gInventory.notifyObservers();
}
}
if(wear_items.count() > 0)
{
// Note: can't do normal iteration, because if all the
// wearables can be resolved immediately, then the
// callback will be called (and this object deleted)
// before the final getNextData().
LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
LLFoundData* found;
LLDynamicArray<LLFoundData*> found_container;
for(S32 i = 0; i < wear_items.count(); ++i)
{
found = new LLFoundData(wear_items.get(i)->getUUID(),
wear_items.get(i)->getAssetUUID(),
wear_items.get(i)->getName(),
wear_items.get(i)->getType());
holder->mFoundList.push_front(found);
found_container.put(found);
}
for(S32 i = 0; i < wear_items.count(); ++i)
{
holder->append = false;
found = found_container.get(i);
// Fetch the wearables about to be worn.
LLWearableList::instance().getAsset(found->mAssetID,
found->mName,
found->mAssetType,
LLAppearanceManager::onWearableAssetFetch,
(void*)holder);
}
}
//If the folder doesn't contain only gestures, take off all attachments.
if (!(wear_items.count() == 0 && obj_items.count() == 0 && gest_items.count() > 0) )
{
LLAgentWearables::userRemoveAllAttachments();
}
if( obj_items.count() > 0 )
{
// We've found some attachments. Add these.
LLVOAvatar* avatar = gAgent.getAvatarObject();
if( avatar )
{
LLAgentWearables::userAttachMultipleAttachments(obj_items);
}
}
}
/* static */
void LLAppearanceManager::getCOFValidDescendents(const LLUUID& category,
LLInventoryModel::item_array_t& items)
{
LLInventoryModel::cat_array_t cats;
LLFindCOFValidItems is_cof_valid;
bool follow_folder_links = false;
gInventory.collectDescendentsIf(category,
cats,
items,
LLInventoryModel::EXCLUDE_TRASH,
is_cof_valid,
follow_folder_links);
}
/* static */ void LLAppearanceManager::getUserDescendents(const LLUUID& category,
LLInventoryModel::item_array_t& wear_items,
LLInventoryModel::item_array_t& obj_items,
LLInventoryModel::item_array_t& gest_items,
bool follow_folder_links)
{
LLInventoryModel::cat_array_t wear_cats;
LLFindWearables is_wearable;
gInventory.collectDescendentsIf(category,
wear_cats,
wear_items,
LLInventoryModel::EXCLUDE_TRASH,
is_wearable,
follow_folder_links);
LLInventoryModel::cat_array_t obj_cats;
LLIsType is_object( LLAssetType::AT_OBJECT );
gInventory.collectDescendentsIf(category,
obj_cats,
obj_items,
LLInventoryModel::EXCLUDE_TRASH,
is_object,
follow_folder_links);
// Find all gestures in this folder
LLInventoryModel::cat_array_t gest_cats;
LLIsType is_gesture( LLAssetType::AT_GESTURE );
gInventory.collectDescendentsIf(category,
gest_cats,
gest_items,
LLInventoryModel::EXCLUDE_TRASH,
is_gesture,
follow_folder_links);
}
void LLAppearanceManager::wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append)
{
if(!category) return;
lldebugs << "wearInventoryCategory( " << category->getName()
<< " )" << llendl;
// What we do here is get the complete information on the items in
// the inventory, and set up an observer that will wait for that to
// happen.
LLOutfitFetch* outfit_fetcher = new LLOutfitFetch(copy, append);
LLInventoryFetchDescendentsObserver::folder_ref_t folders;
folders.push_back(category->getUUID());
outfit_fetcher->fetchDescendents(folders);
//inc_busy_count();
if(outfit_fetcher->isEverythingComplete())
{
// everything is already here - call done.
outfit_fetcher->done();
}
else
{
// it's all on it's way - add an observer, and the inventory
// will call done for us when everything is here.
gInventory.addObserver(outfit_fetcher);
}
}
// *NOTE: hack to get from avatar inventory to avatar
/* static */
void LLAppearanceManager::wearInventoryCategoryOnAvatar( LLInventoryCategory* category, bool append )
{
// Avoid unintentionally overwriting old wearables. We have to do
// this up front to avoid having to deal with the case of multiple
// wearables being dirty.
if(!category) return;
lldebugs << "wearInventoryCategoryOnAvatar( " << category->getName()
<< " )" << llendl;
if( gFloaterCustomize )
{
gFloaterCustomize->askToSaveIfDirty(boost::bind(LLAppearanceManager::changeOutfit, _1, category->getUUID(), append));
}
else
{
LLAppearanceManager::changeOutfit(TRUE, category->getUUID(), append);
}
}
/* static */
void LLAppearanceManager::wearOutfitByName(const std::string& name)
{
llinfos << "Wearing category " << name << llendl;
//inc_busy_count();
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
LLNameCategoryCollector has_name(name);
gInventory.collectDescendentsIf(gInventory.getRootFolderID(),
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH,
has_name);
bool copy_items = false;
LLInventoryCategory* cat = NULL;
if (cat_array.count() > 0)
{
// Just wear the first one that matches
cat = cat_array.get(0);
}
else
{
gInventory.collectDescendentsIf(LLUUID::null,
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH,
has_name);
if(cat_array.count() > 0)
{
cat = cat_array.get(0);
copy_items = true;
}
}
if(cat)
{
LLAppearanceManager::wearInventoryCategory(cat, copy_items, false);
}
else
{
llwarns << "Couldn't find outfit " <<name<< " in wearOutfitByName()"
<< llendl;
}
//dec_busy_count();
}
/* static */
void LLAppearanceManager::wearItem( LLInventoryItem* item, bool do_update )
{
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
gInventory.collectDescendents(LLAppearanceManager::getCOF(),
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH);
bool linked_already = false;
for (S32 i=0; i<item_array.count(); i++)
{
const LLInventoryItem* inv_item = item_array.get(i).get();
if (inv_item->getLinkedUUID() == item->getLinkedUUID())
{
linked_already = true;
break;
}
}
if (linked_already)
{
if (do_update)
LLAppearanceManager::updateAppearanceFromCOF();
}
else
{
LLPointer<LLInventoryCallback> cb = do_update ? new ModifiedCOFCallback : 0;
link_inventory_item( gAgent.getID(),
item->getLinkedUUID(),
getCOF(),
item->getName(),
LLAssetType::AT_LINK,
cb);
}
}
/* static */
void LLAppearanceManager::wearEnsemble( LLInventoryCategory* cat, bool do_update )
{
// BAP add check for already in COF.
LLPointer<LLInventoryCallback> cb = do_update ? new ModifiedCOFCallback : 0;
link_inventory_item( gAgent.getID(),
cat->getLinkedUUID(),
getCOF(),
cat->getName(),
LLAssetType::AT_LINK_FOLDER,
cb);
}
/* static */
void LLAppearanceManager::removeItemLinks(LLUUID& item_id, bool do_update)
{
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
gInventory.collectDescendents(LLAppearanceManager::getCOF(),
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH);
for (S32 i=0; i<item_array.count(); i++)
{
const LLInventoryItem* item = item_array.get(i).get();
if (item->getLinkedUUID() == item_id)
{
gInventory.purgeObject(item_array.get(i)->getUUID());
}
}
if (do_update)
{
LLAppearanceManager::updateAppearanceFromCOF();
}
}