/indra/newview/llappearancemgr.cpp
C++ | 2239 lines | 1776 code | 267 blank | 196 comment | 280 complexity | ad524e20cc0a499602b9525c211390b7 MD5 | raw file
Possible License(s): LGPL-2.1
- /**
- * @file llappearancemgr.cpp
- * @brief Manager for initiating appearance changes on the viewer
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "llaccordionctrltab.h"
- #include "llagent.h"
- #include "llagentcamera.h"
- #include "llagentwearables.h"
- #include "llappearancemgr.h"
- #include "llattachmentsmgr.h"
- #include "llcommandhandler.h"
- #include "lleventtimer.h"
- #include "llfloatersidepanelcontainer.h"
- #include "llgesturemgr.h"
- #include "llinventorybridge.h"
- #include "llinventoryfunctions.h"
- #include "llinventoryobserver.h"
- #include "llnotificationsutil.h"
- #include "lloutfitobserver.h"
- #include "lloutfitslist.h"
- #include "llselectmgr.h"
- #include "llsidepanelappearance.h"
- #include "llviewerobjectlist.h"
- #include "llvoavatar.h"
- #include "llvoavatarself.h"
- #include "llviewerregion.h"
- #include "llwearablelist.h"
- // RAII thingy to guarantee that a variable gets reset when the Setter
- // goes out of scope. More general utility would be handy - TODO:
- // check boost.
- class BoolSetter
- {
- public:
- BoolSetter(bool& var):
- mVar(var)
- {
- mVar = true;
- }
- ~BoolSetter()
- {
- mVar = false;
- }
- private:
- bool& mVar;
- };
- char ORDER_NUMBER_SEPARATOR('@');
- class LLOutfitUnLockTimer: public LLEventTimer
- {
- public:
- LLOutfitUnLockTimer(F32 period) : LLEventTimer(period)
- {
- // restart timer on BOF changed event
- LLOutfitObserver::instance().addBOFChangedCallback(boost::bind(
- &LLOutfitUnLockTimer::reset, this));
- stop();
- }
- /*virtual*/
- BOOL tick()
- {
- if(mEventTimer.hasExpired())
- {
- LLAppearanceMgr::instance().setOutfitLocked(false);
- }
- return FALSE;
- }
- void stop() { mEventTimer.stop(); }
- void start() { mEventTimer.start(); }
- void reset() { mEventTimer.reset(); }
- BOOL getStarted() { return mEventTimer.getStarted(); }
- LLTimer& getEventTimer() { return mEventTimer;}
- };
- // support for secondlife:///app/appearance SLapps
- class LLAppearanceHandler : public LLCommandHandler
- {
- public:
- // requests will be throttled from a non-trusted browser
- LLAppearanceHandler() : LLCommandHandler("appearance", UNTRUSTED_THROTTLE) {}
- bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web)
- {
- // support secondlife:///app/appearance/show, but for now we just
- // make all secondlife:///app/appearance SLapps behave this way
- if (!LLUI::sSettingGroups["config"]->getBOOL("EnableAppearance"))
- {
- LLNotificationsUtil::add("NoAppearance", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit"));
- return true;
- }
- LLFloaterSidePanelContainer::showPanel("appearance", LLSD());
- return true;
- }
- };
- LLAppearanceHandler gAppearanceHandler;
- LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id, const std::string& name)
- {
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- LLNameCategoryCollector has_name(name);
- gInventory.collectDescendentsIf(parent_id,
- cat_array,
- item_array,
- LLInventoryModel::EXCLUDE_TRASH,
- has_name);
- if (0 == cat_array.count())
- return LLUUID();
- else
- {
- LLViewerInventoryCategory *cat = cat_array.get(0);
- if (cat)
- return cat->getUUID();
- else
- {
- llwarns << "null cat" << llendl;
- return LLUUID();
- }
- }
- }
- 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()
- {
- llinfos << "done all inventory callbacks" << llendl;
-
- // 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() )
- {
- LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend);
- }
- else
- {
- llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
- }
- }
- private:
- LLUUID mCatID;
- bool mAppend;
- };
- //Inventory callback updating "dirty" state when destroyed
- class LLUpdateDirtyState: public LLInventoryCallback
- {
- public:
- LLUpdateDirtyState() {}
- virtual ~LLUpdateDirtyState()
- {
- if (LLAppearanceMgr::instanceExists())
- {
- LLAppearanceMgr::getInstance()->updateIsDirty();
- }
- }
- virtual void fire(const LLUUID&) {}
- };
- LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering):
- mFireCount(0),
- mUpdateBaseOrder(update_base_outfit_ordering)
- {
- }
- LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
- {
- llinfos << "done update appearance on destroy" << llendl;
-
- if (!LLApp::isExiting())
- {
- LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder);
- }
- }
- void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
- {
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item);
- const std::string item_name = item ? item->getName() : "ITEM NOT FOUND";
- #ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << llendl;
- #endif
- mFireCount++;
- }
- struct LLFoundData
- {
- LLFoundData() :
- mAssetType(LLAssetType::AT_NONE),
- mWearableType(LLWearableType::WT_INVALID),
- mWearable(NULL) {}
- LLFoundData(const LLUUID& item_id,
- const LLUUID& asset_id,
- const std::string& name,
- const LLAssetType::EType& asset_type,
- const LLWearableType::EType& wearable_type,
- const bool is_replacement = false
- ) :
- mItemID(item_id),
- mAssetID(asset_id),
- mName(name),
- mAssetType(asset_type),
- mWearableType(wearable_type),
- mIsReplacement(is_replacement),
- mWearable( NULL ) {}
-
- LLUUID mItemID;
- LLUUID mAssetID;
- std::string mName;
- LLAssetType::EType mAssetType;
- LLWearableType::EType mWearableType;
- LLWearable* mWearable;
- bool mIsReplacement;
- };
-
- class LLWearableHoldingPattern
- {
- public:
- LLWearableHoldingPattern();
- ~LLWearableHoldingPattern();
- bool pollFetchCompletion();
- void onFetchCompletion();
- bool isFetchCompleted();
- bool isTimedOut();
- void checkMissingWearables();
- bool pollMissingWearables();
- bool isMissingCompleted();
- void recoverMissingWearable(LLWearableType::EType type);
- void clearCOFLinksForMissingWearables();
-
- void onWearableAssetFetch(LLWearable *wearable);
- void onAllComplete();
- typedef std::list<LLFoundData> found_list_t;
- found_list_t& getFoundList();
- void eraseTypeToLink(LLWearableType::EType type);
- void eraseTypeToRecover(LLWearableType::EType type);
- void setObjItems(const LLInventoryModel::item_array_t& items);
- void setGestItems(const LLInventoryModel::item_array_t& items);
- bool isMostRecent();
- void handleLateArrivals();
- void resetTime(F32 timeout);
-
- private:
- found_list_t mFoundList;
- LLInventoryModel::item_array_t mObjItems;
- LLInventoryModel::item_array_t mGestItems;
- typedef std::set<S32> type_set_t;
- type_set_t mTypesToRecover;
- type_set_t mTypesToLink;
- S32 mResolved;
- LLTimer mWaitTime;
- bool mFired;
- typedef std::set<LLWearableHoldingPattern*> type_set_hp;
- static type_set_hp sActiveHoldingPatterns;
- bool mIsMostRecent;
- std::set<LLWearable*> mLateArrivals;
- bool mIsAllComplete;
- };
- LLWearableHoldingPattern::type_set_hp LLWearableHoldingPattern::sActiveHoldingPatterns;
- LLWearableHoldingPattern::LLWearableHoldingPattern():
- mResolved(0),
- mFired(false),
- mIsMostRecent(true),
- mIsAllComplete(false)
- {
- if (sActiveHoldingPatterns.size()>0)
- {
- llinfos << "Creating LLWearableHoldingPattern when "
- << sActiveHoldingPatterns.size()
- << " other attempts are active."
- << " Flagging others as invalid."
- << llendl;
- for (type_set_hp::iterator it = sActiveHoldingPatterns.begin();
- it != sActiveHoldingPatterns.end();
- ++it)
- {
- (*it)->mIsMostRecent = false;
- }
-
- }
- sActiveHoldingPatterns.insert(this);
- }
- LLWearableHoldingPattern::~LLWearableHoldingPattern()
- {
- sActiveHoldingPatterns.erase(this);
- }
- bool LLWearableHoldingPattern::isMostRecent()
- {
- return mIsMostRecent;
- }
- LLWearableHoldingPattern::found_list_t& LLWearableHoldingPattern::getFoundList()
- {
- return mFoundList;
- }
- void LLWearableHoldingPattern::eraseTypeToLink(LLWearableType::EType type)
- {
- mTypesToLink.erase(type);
- }
- void LLWearableHoldingPattern::eraseTypeToRecover(LLWearableType::EType type)
- {
- mTypesToRecover.erase(type);
- }
- void LLWearableHoldingPattern::setObjItems(const LLInventoryModel::item_array_t& items)
- {
- mObjItems = items;
- }
- void LLWearableHoldingPattern::setGestItems(const LLInventoryModel::item_array_t& items)
- {
- mGestItems = items;
- }
- bool LLWearableHoldingPattern::isFetchCompleted()
- {
- return (mResolved >= (S32)getFoundList().size()); // have everything we were waiting for?
- }
- bool LLWearableHoldingPattern::isTimedOut()
- {
- return mWaitTime.hasExpired();
- }
- void LLWearableHoldingPattern::checkMissingWearables()
- {
- if (!isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
-
- std::vector<S32> found_by_type(LLWearableType::WT_COUNT,0);
- std::vector<S32> requested_by_type(LLWearableType::WT_COUNT,0);
- for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it)
- {
- LLFoundData &data = *it;
- if (data.mWearableType < LLWearableType::WT_COUNT)
- requested_by_type[data.mWearableType]++;
- if (data.mWearable)
- found_by_type[data.mWearableType]++;
- }
- for (S32 type = 0; type < LLWearableType::WT_COUNT; ++type)
- {
- if (requested_by_type[type] > found_by_type[type])
- {
- llwarns << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << llendl;
- }
- if (found_by_type[type] > 0)
- continue;
- if (
- // If at least one wearable of certain types (pants/shirt/skirt)
- // was requested but none was found, create a default asset as a replacement.
- // In all other cases, don't do anything.
- // For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud
- // due to logic in LLVOAvatarSelf::getIsCloud().
- // For non-critical types (tatoo, socks, etc.) the wearable will just be missing.
- (requested_by_type[type] > 0) &&
- ((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT)))
- {
- mTypesToRecover.insert(type);
- mTypesToLink.insert(type);
- recoverMissingWearable((LLWearableType::EType)type);
- llwarns << "need to replace " << type << llendl;
- }
- }
- resetTime(60.0F);
- if (!pollMissingWearables())
- {
- doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this));
- }
- }
- void LLWearableHoldingPattern::onAllComplete()
- {
- if (!isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
- // Activate all gestures in this folder
- if (mGestItems.count() > 0)
- {
- llinfos << "Activating " << mGestItems.count() << " gestures" << llendl;
-
- LLGestureMgr::instance().activateGestures(mGestItems);
-
- // Update the inventory item labels to reflect the fact
- // they are active.
- LLViewerInventoryCategory* catp =
- gInventory.getCategory(LLAppearanceMgr::instance().getCOF());
-
- if (catp)
- {
- gInventory.updateCategory(catp);
- gInventory.notifyObservers();
- }
- }
- // Update wearables.
- llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl;
- LLAppearanceMgr::instance().updateAgentWearables(this, false);
-
- // Update attachments to match those requested.
- if (isAgentAvatarValid())
- {
- llinfos << "Updating " << mObjItems.count() << " attachments" << llendl;
- LLAgentWearables::userUpdateAttachments(mObjItems);
- }
- if (isFetchCompleted() && isMissingCompleted())
- {
- // Only safe to delete if all wearable callbacks and all missing wearables completed.
- delete this;
- }
- else
- {
- mIsAllComplete = true;
- handleLateArrivals();
- }
- }
- void LLWearableHoldingPattern::onFetchCompletion()
- {
- if (!isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
- checkMissingWearables();
- }
- // Runs as an idle callback until all wearables are fetched (or we time out).
- bool LLWearableHoldingPattern::pollFetchCompletion()
- {
- if (!isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
- bool completed = isFetchCompleted();
- bool timed_out = isTimedOut();
- bool done = completed || timed_out;
- if (done)
- {
- llinfos << "polling, done status: " << completed << " timed out " << timed_out
- << " elapsed " << mWaitTime.getElapsedTimeF32() << llendl;
- mFired = true;
-
- if (timed_out)
- {
- llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl;
- }
- onFetchCompletion();
- }
- return done;
- }
- class RecoveredItemLinkCB: public LLInventoryCallback
- {
- public:
- RecoveredItemLinkCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
- mHolder(holder),
- mWearable(wearable),
- mType(type)
- {
- }
- void fire(const LLUUID& item_id)
- {
- if (!mHolder->isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
- llinfos << "Recovered item link for type " << mType << llendl;
- mHolder->eraseTypeToLink(mType);
- // Add wearable to FoundData for actual wearing
- LLViewerInventoryItem *item = gInventory.getItem(item_id);
- LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL;
- if (linked_item)
- {
- gInventory.addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
-
- if (item)
- {
- LLFoundData found(linked_item->getUUID(),
- linked_item->getAssetUUID(),
- linked_item->getName(),
- linked_item->getType(),
- linked_item->isWearableType() ? linked_item->getWearableType() : LLWearableType::WT_INVALID,
- true // is replacement
- );
- found.mWearable = mWearable;
- mHolder->getFoundList().push_front(found);
- }
- else
- {
- llwarns << "inventory item not found for recovered wearable" << llendl;
- }
- }
- else
- {
- llwarns << "inventory link not found for recovered wearable" << llendl;
- }
- }
- private:
- LLWearableHoldingPattern* mHolder;
- LLWearable *mWearable;
- LLWearableType::EType mType;
- };
- class RecoveredItemCB: public LLInventoryCallback
- {
- public:
- RecoveredItemCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
- mHolder(holder),
- mWearable(wearable),
- mType(type)
- {
- }
- void fire(const LLUUID& item_id)
- {
- if (!mHolder->isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
- llinfos << "Recovered item for type " << mType << llendl;
- LLViewerInventoryItem *itemp = gInventory.getItem(item_id);
- mWearable->setItemID(item_id);
- LLPointer<LLInventoryCallback> cb = new RecoveredItemLinkCB(mType,mWearable,mHolder);
- mHolder->eraseTypeToRecover(mType);
- llassert(itemp);
- if (itemp)
- {
- link_inventory_item( gAgent.getID(),
- item_id,
- LLAppearanceMgr::instance().getCOF(),
- itemp->getName(),
- itemp->getDescription(),
- LLAssetType::AT_LINK,
- cb);
- }
- }
- private:
- LLWearableHoldingPattern* mHolder;
- LLWearable *mWearable;
- LLWearableType::EType mType;
- };
- void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type)
- {
- if (!isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
-
- // Try to recover by replacing missing wearable with a new one.
- LLNotificationsUtil::add("ReplacedMissingWearable");
- lldebugs << "Wearable " << LLWearableType::getTypeLabel(type)
- << " could not be downloaded. Replaced inventory item with default wearable." << llendl;
- LLWearable* wearable = LLWearableList::instance().createNewWearable(type);
- // Add a new one in the lost and found folder.
- const LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
- LLPointer<LLInventoryCallback> cb = new RecoveredItemCB(type,wearable,this);
- create_inventory_item(gAgent.getID(),
- gAgent.getSessionID(),
- lost_and_found_id,
- wearable->getTransactionID(),
- wearable->getName(),
- wearable->getDescription(),
- wearable->getAssetType(),
- LLInventoryType::IT_WEARABLE,
- wearable->getType(),
- wearable->getPermissions().getMaskNextOwner(),
- cb);
- }
- bool LLWearableHoldingPattern::isMissingCompleted()
- {
- return mTypesToLink.size()==0 && mTypesToRecover.size()==0;
- }
- void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
- {
- for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it)
- {
- LLFoundData &data = *it;
- if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable))
- {
- // Wearable link that was never resolved; remove links to it from COF
- llinfos << "removing link for unresolved item " << data.mItemID.asString() << llendl;
- LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false);
- }
- }
- }
- bool LLWearableHoldingPattern::pollMissingWearables()
- {
- if (!isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
-
- bool timed_out = isTimedOut();
- bool missing_completed = isMissingCompleted();
- bool done = timed_out || missing_completed;
- if (!done)
- {
- llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size()
- << " links " << mTypesToLink.size()
- << " wearables, timed out " << timed_out
- << " elapsed " << mWaitTime.getElapsedTimeF32()
- << " done " << done << llendl;
- }
- if (done)
- {
- gAgentAvatarp->debugWearablesLoaded();
- // BAP - if we don't call clearCOFLinksForMissingWearables()
- // here, we won't have to add the link back in later if the
- // wearable arrives late. This is to avoid corruption of
- // wearable ordering info. Also has the effect of making
- // unworn item links visible in the COF under some
- // circumstances.
- //clearCOFLinksForMissingWearables();
- onAllComplete();
- }
- return done;
- }
- // Handle wearables that arrived after the timeout period expired.
- void LLWearableHoldingPattern::handleLateArrivals()
- {
- // Only safe to run if we have previously finished the missing
- // wearables and other processing - otherwise we could be in some
- // intermediate state - but have not been superceded by a later
- // outfit change request.
- if (mLateArrivals.size() == 0)
- {
- // Nothing to process.
- return;
- }
- if (!isMostRecent())
- {
- llwarns << "Late arrivals not handled - outfit change no longer valid" << llendl;
- }
- if (!mIsAllComplete)
- {
- llwarns << "Late arrivals not handled - in middle of missing wearables processing" << llendl;
- }
- llinfos << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << llendl;
- // Update mFoundList using late-arriving wearables.
- std::set<LLWearableType::EType> replaced_types;
- for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin();
- iter != getFoundList().end(); ++iter)
- {
- LLFoundData& data = *iter;
- for (std::set<LLWearable*>::iterator wear_it = mLateArrivals.begin();
- wear_it != mLateArrivals.end();
- ++wear_it)
- {
- LLWearable *wearable = *wear_it;
- if(wearable->getAssetID() == data.mAssetID)
- {
- data.mWearable = wearable;
- replaced_types.insert(data.mWearableType);
- // BAP - if we didn't call
- // clearCOFLinksForMissingWearables() earlier, we
- // don't need to restore the link here. Fixes
- // wearable ordering problems.
- // LLAppearanceMgr::instance().addCOFItemLink(data.mItemID,false);
- // BAP failing this means inventory or asset server
- // are corrupted in a way we don't handle.
- llassert((data.mWearableType < LLWearableType::WT_COUNT) && (wearable->getType() == data.mWearableType));
- break;
- }
- }
- }
- // Remove COF links for any default wearables previously used to replace the late arrivals.
- // All this pussyfooting around with a while loop and explicit
- // iterator incrementing is to allow removing items from the list
- // without clobbering the iterator we're using to navigate.
- LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin();
- while (iter != getFoundList().end())
- {
- LLFoundData& data = *iter;
- // If an item of this type has recently shown up, removed the corresponding replacement wearable from COF.
- if (data.mWearable && data.mIsReplacement &&
- replaced_types.find(data.mWearableType) != replaced_types.end())
- {
- LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false);
- std::list<LLFoundData>::iterator clobber_ator = iter;
- ++iter;
- getFoundList().erase(clobber_ator);
- }
- else
- {
- ++iter;
- }
- }
- // Clear contents of late arrivals.
- mLateArrivals.clear();
- // Update appearance based on mFoundList
- LLAppearanceMgr::instance().updateAgentWearables(this, false);
- }
- void LLWearableHoldingPattern::resetTime(F32 timeout)
- {
- mWaitTime.reset();
- mWaitTime.setTimerExpirySec(timeout);
- }
- void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
- {
- if (!isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
-
- mResolved += 1; // just counting callbacks, not successes.
- llinfos << "resolved " << mResolved << "/" << getFoundList().size() << llendl;
- if (!wearable)
- {
- llwarns << "no wearable found" << llendl;
- }
- if (mFired)
- {
- llwarns << "called after holder fired" << llendl;
- if (wearable)
- {
- mLateArrivals.insert(wearable);
- if (mIsAllComplete)
- {
- handleLateArrivals();
- }
- }
- return;
- }
- if (!wearable)
- {
- return;
- }
- for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin();
- iter != getFoundList().end(); ++iter)
- {
- LLFoundData& data = *iter;
- if(wearable->getAssetID() == data.mAssetID)
- {
- // Failing this means inventory or asset server are corrupted in a way we don't handle.
- if ((data.mWearableType >= LLWearableType::WT_COUNT) || (wearable->getType() != data.mWearableType))
- {
- llwarns << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << llendl;
- break;
- }
- data.mWearable = wearable;
- }
- }
- }
- static void onWearableAssetFetch(LLWearable* wearable, void* data)
- {
- LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
- holder->onWearableAssetFetch(wearable);
- }
- static void removeDuplicateItems(LLInventoryModel::item_array_t& items)
- {
- LLInventoryModel::item_array_t new_items;
- std::set<LLUUID> items_seen;
- std::deque<LLViewerInventoryItem*> tmp_list;
- // Traverse from the front and keep the first of each item
- // encountered, so we actually keep the *last* of each duplicate
- // item. This is needed to give the right priority when adding
- // duplicate items to an existing outfit.
- for (S32 i=items.count()-1; i>=0; i--)
- {
- LLViewerInventoryItem *item = items.get(i);
- LLUUID item_id = item->getLinkedUUID();
- if (items_seen.find(item_id)!=items_seen.end())
- continue;
- items_seen.insert(item_id);
- tmp_list.push_front(item);
- }
- for (std::deque<LLViewerInventoryItem*>::iterator it = tmp_list.begin();
- it != tmp_list.end();
- ++it)
- {
- new_items.put(*it);
- }
- items = new_items;
- }
- const LLUUID LLAppearanceMgr::getCOF() const
- {
- return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
- }
- const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink()
- {
- const LLUUID& current_outfit_cat = getCOF();
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- // Can't search on FT_OUTFIT since links to categories return FT_CATEGORY for type since they don't
- // return preferred type.
- LLIsType is_category( LLAssetType::AT_CATEGORY );
- gInventory.collectDescendentsIf(current_outfit_cat,
- cat_array,
- item_array,
- false,
- is_category,
- false);
- for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
- iter != item_array.end();
- iter++)
- {
- const LLViewerInventoryItem *item = (*iter);
- const LLViewerInventoryCategory *cat = item->getLinkedCategory();
- if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT)
- {
- const LLUUID parent_id = cat->getParentUUID();
- LLViewerInventoryCategory* parent_cat = gInventory.getCategory(parent_id);
- // if base outfit moved to trash it means that we don't have base outfit
- if (parent_cat != NULL && parent_cat->getPreferredType() == LLFolderType::FT_TRASH)
- {
- return NULL;
- }
- return item;
- }
- }
- return NULL;
- }
- bool LLAppearanceMgr::getBaseOutfitName(std::string& name)
- {
- const LLViewerInventoryItem* outfit_link = getBaseOutfitLink();
- if(outfit_link)
- {
- const LLViewerInventoryCategory *cat = outfit_link->getLinkedCategory();
- if (cat)
- {
- name = cat->getName();
- return true;
- }
- }
- return false;
- }
- const LLUUID LLAppearanceMgr::getBaseOutfitUUID()
- {
- const LLViewerInventoryItem* outfit_link = getBaseOutfitLink();
- if (!outfit_link || !outfit_link->getIsLinkType()) return LLUUID::null;
- const LLViewerInventoryCategory* outfit_cat = outfit_link->getLinkedCategory();
- if (!outfit_cat) return LLUUID::null;
- if (outfit_cat->getPreferredType() != LLFolderType::FT_OUTFIT)
- {
- llwarns << "Expected outfit type:" << LLFolderType::FT_OUTFIT << " but got type:" << outfit_cat->getType() << " for folder name:" << outfit_cat->getName() << llendl;
- return LLUUID::null;
- }
- return outfit_cat->getUUID();
- }
- bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace, LLPointer<LLInventoryCallback> cb)
- {
- if (item_id_to_wear.isNull()) return false;
- // *TODO: issue with multi-wearable should be fixed:
- // in this case this method will be called N times - loading started for each item
- // and than N times will be called - loading completed for each item.
- // That means subscribers will be notified that loading is done after first item in a batch is worn.
- // (loading indicator disappears for example before all selected items are worn)
- // Have not fix this issue for 2.1 because of stability reason. EXT-7777.
- // Disabled for now because it is *not* acceptable to call updateAppearanceFromCOF() multiple times
- // gAgentWearables.notifyLoadingStarted();
- LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear);
- if (!item_to_wear) return false;
- if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID()))
- {
- LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(replace);
- copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(),cb);
- return false;
- }
- else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID()))
- {
- return false; // not in library and not in agent's inventory
- }
- else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)))
- {
- LLNotificationsUtil::add("CannotWearTrash");
- return false;
- }
- else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), LLAppearanceMgr::instance().getCOF())) // EXT-84911
- {
- return false;
- }
- switch (item_to_wear->getType())
- {
- case LLAssetType::AT_CLOTHING:
- if (gAgentWearables.areWearablesLoaded())
- {
- S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType());
- if ((replace && wearable_count != 0) ||
- (wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) )
- {
- removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1), false);
- }
- addCOFItemLink(item_to_wear, do_update, cb);
- }
- break;
- case LLAssetType::AT_BODYPART:
- // TODO: investigate wearables may not be loaded at this point EXT-8231
-
- // Remove the existing wearables of the same type.
- // Remove existing body parts anyway because we must not be able to wear e.g. two skins.
- removeCOFLinksOfType(item_to_wear->getWearableType(), false);
- addCOFItemLink(item_to_wear, do_update, cb);
- break;
- case LLAssetType::AT_OBJECT:
- rez_attachment(item_to_wear, NULL, replace);
- break;
- default: return false;;
- }
- return true;
- }
- // Update appearance from outfit folder.
- void LLAppearanceMgr::changeOutfit(bool proceed, const LLUUID& category, bool append)
- {
- if (!proceed)
- return;
- LLAppearanceMgr::instance().updateCOF(category,append);
- }
- void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit)
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(new_outfit);
- wearInventoryCategory(cat, false, false);
- }
- // Open outfit renaming dialog.
- void LLAppearanceMgr::renameOutfit(const LLUUID& outfit_id)
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id);
- if (!cat)
- {
- return;
- }
- LLSD args;
- args["NAME"] = cat->getName();
- LLSD payload;
- payload["cat_id"] = outfit_id;
- LLNotificationsUtil::add("RenameOutfit", args, payload, boost::bind(onOutfitRename, _1, _2));
- }
- // User typed new outfit name.
- // static
- void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& response)
- {
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option != 0) return; // canceled
- std::string outfit_name = response["new_name"].asString();
- LLStringUtil::trim(outfit_name);
- if (!outfit_name.empty())
- {
- LLUUID cat_id = notification["payload"]["cat_id"].asUUID();
- rename_category(&gInventory, cat_id, outfit_name);
- }
- }
- void LLAppearanceMgr::setOutfitLocked(bool locked)
- {
- if (mOutfitLocked == locked)
- {
- return;
- }
- mOutfitLocked = locked;
- if (locked)
- {
- mUnlockOutfitTimer->reset();
- mUnlockOutfitTimer->start();
- }
- else
- {
- mUnlockOutfitTimer->stop();
- }
- LLOutfitObserver::instance().notifyOutfitLockChanged();
- }
- void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id)
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
- wearInventoryCategory(cat, false, true);
- }
- void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id)
- {
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLFindWearablesEx collector(/*is_worn=*/ true, /*include_body_parts=*/ false);
- gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector);
- LLInventoryModel::item_array_t::const_iterator it = items.begin();
- const LLInventoryModel::item_array_t::const_iterator it_end = items.end();
- for( ; it_end != it; ++it)
- {
- LLViewerInventoryItem* item = *it;
- removeItemFromAvatar(item->getUUID());
- }
- }
- // Create a copy of src_id + contents as a subfolder of dst_id.
- void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
- LLPointer<LLInventoryCallback> cb)
- {
- LLInventoryCategory *src_cat = gInventory.getCategory(src_id);
- if (!src_cat)
- {
- llwarns << "folder not found for src " << src_id.asString() << llendl;
- return;
- }
- llinfos << "starting, src_id " << src_id << " name " << src_cat->getName() << " dst_id " << dst_id << llendl;
- LLUUID parent_id = dst_id;
- if(parent_id.isNull())
- {
- parent_id = gInventory.getRootFolderID();
- }
- LLUUID subfolder_id = gInventory.createNewCategory( parent_id,
- LLFolderType::FT_NONE,
- src_cat->getName());
- shallowCopyCategoryContents(src_id, subfolder_id, cb);
- gInventory.notifyObservers();
- }
- // Copy contents of src_id to dst_id.
- void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id,
- LLPointer<LLInventoryCallback> cb)
- {
- LLInventoryModel::cat_array_t* cats;
- LLInventoryModel::item_array_t* items;
- gInventory.getDirectDescendentsOf(src_id, cats, items);
- llinfos << "copying " << items->count() << " items" << llendl;
- for (LLInventoryModel::item_array_t::const_iterator iter = items->begin();
- iter != items->end();
- ++iter)
- {
- const LLViewerInventoryItem* item = (*iter);
- switch (item->getActualType())
- {
- 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;
- }
- case LLAssetType::AT_LINK_FOLDER:
- {
- LLViewerInventoryCategory *catp = item->getLinkedCategory();
- // Skip copying outfit links.
- if (catp && catp->getPreferredType() != LLFolderType::FT_OUTFIT)
- {
- link_inventory_item(gAgent.getID(),
- item->getLinkedUUID(),
- dst_id,
- item->getName(),
- item->getDescription(),
- LLAssetType::AT_LINK_FOLDER, cb);
- }
- break;
- }
- case LLAssetType::AT_CLOTHING:
- case LLAssetType::AT_OBJECT:
- case LLAssetType::AT_BODYPART:
- case LLAssetType::AT_GESTURE:
- {
- llinfos << "copying inventory item " << item->getName() << llendl;
- copy_inventory_item(gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- dst_id,
- item->getName(),
- cb);
- break;
- }
- default:
- // Ignore non-outfit asset types
- break;
- }
- }
- }
- BOOL LLAppearanceMgr::getCanMakeFolderIntoOutfit(const LLUUID& folder_id)
- {
- // These are the wearable items that are required for considering this
- // folder as containing a complete outfit.
- U32 required_wearables = 0;
- required_wearables |= 1LL << LLWearableType::WT_SHAPE;
- required_wearables |= 1LL << LLWearableType::WT_SKIN;
- required_wearables |= 1LL << LLWearableType::WT_HAIR;
- required_wearables |= 1LL << LLWearableType::WT_EYES;
- // These are the wearables that the folder actually contains.
- U32 folder_wearables = 0;
- LLInventoryModel::cat_array_t* cats;
- LLInventoryModel::item_array_t* items;
- gInventory.getDirectDescendentsOf(folder_id, cats, items);
- for (LLInventoryModel::item_array_t::const_iterator iter = items->begin();
- iter != items->end();
- ++iter)
- {
- const LLViewerInventoryItem* item = (*iter);
- if (item->isWearableType())
- {
- const LLWearableType::EType wearable_type = item->getWearableType();
- folder_wearables |= 1LL << wearable_type;
- }
- }
- // If the folder contains the required wearables, return TRUE.
- return ((required_wearables & folder_wearables) == required_wearables);
- }
- bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id)
- {
- // Disallow removing the base outfit.
- if (outfit_cat_id == getBaseOutfitUUID())
- {
- return false;
- }
- // Check if the outfit folder itself is removable.
- if (!get_is_category_removable(&gInventory, outfit_cat_id))
- {
- return false;
- }
- // Check for the folder's non-removable descendants.
- LLFindNonRemovableObjects filter_non_removable;
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLInventoryModel::item_array_t::const_iterator it;
- gInventory.collectDescendentsIf(outfit_cat_id, cats, items, false, filter_non_removable);
- if (!cats.empty() || !items.empty())
- {
- return false;
- }
- return true;
- }
- // static
- bool LLAppearanceMgr::getCanRemoveFromCOF(const LLUUID& outfit_cat_id)
- {
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLFindWearablesEx is_worn(/*is_worn=*/ true, /*include_body_parts=*/ false);
- gInventory.collectDescendentsIf(outfit_cat_id,
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- is_worn);
- return items.size() > 0;
- }
- // static
- bool LLAppearanceMgr::getCanAddToCOF(const LLUUID& outfit_cat_id)
- {
- if (gAgentWearables.isCOFChangeInProgress())
- {
- return false;
- }
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false);
- gInventory.collectDescendentsIf(outfit_cat_id,
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- not_worn);
- return items.size() > 0;
- }
- bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id)
- {
- // Don't allow wearing anything while we're changing appearance.
- if (gAgentWearables.isCOFChangeInProgress())
- {
- return false;
- }
- // Check whether it's the base outfit.
- if (outfit_cat_id.isNull() || outfit_cat_id == getBaseOutfitUUID())
- {
- return false;
- }
- // Check whether the outfit contains any wearables we aren't wearing already (STORM-702).
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLFindWearablesEx is_worn(/*is_worn=*/ false, /*include_body_parts=*/ true);
- gInventory.collectDescendentsIf(outfit_cat_id,
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- is_worn);
- return items.size() > 0;
- }
- void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category)
- {
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- gInventory.collectDescendents(category, cats, items,
- LLInventoryModel::EXCLUDE_TRASH);
- for (S32 i = 0; i < items.count(); ++i)
- {
- LLViewerInventoryItem *item = items.get(i);
- if (item->getActualType() != LLAssetType::AT_LINK_FOLDER)
- continue;
- if (item->getIsLinkType())
- {
- LLViewerInventoryCategory* catp = item->getLinkedCategory();
- if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
- {
- gInventory.purgeObject(item->getUUID());
- }
- }
- }
- }
- void LLAppearanceMgr::purgeCategory(const LLUUID& category, bool keep_outfit_links)
- {
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- gInventory.collectDescendents(category, cats, items,
- LLInventoryModel::EXCLUDE_TRASH);
- for (S32 i = 0; i < items.count(); ++i)
- {
- LLViewerInventoryItem *item = items.get(i);
- if (keep_outfit_links && (item->getActualType() == LLAssetType::AT_LINK_FOLDER))
- continue;
- if (item->getIsLinkType())
- {
- gInventory.purgeObject(item->getUUID());
- }
- }
- }
- // Keep the last N wearables of each type. For viewer 2.0, N is 1 for
- // both body parts and clothing items.
- void LLAppearanceMgr::filterWearableItems(
- LLInventoryModel::item_array_t& items, S32 max_per_type)
- {
- // Divvy items into arrays by wearable type.
- std::vector<LLInventoryModel::item_array_t> items_by_type(LLWearableType::WT_COUNT);
- divvyWearablesByType(items, items_by_type);
- // rebuild items list, retaining the last max_per_type of each array
- items.clear();
- for (S32 i=0; i<LLWearableType::WT_COUNT; i++)
- {
- S32 size = items_by_type[i].size();
- if (size <= 0)
- continue;
- S32 start_index = llmax(0,size-max_per_type);
- for (S32 j = start_index; j<size; j++)
- {
- items.push_back(items_by_type[i][j]);
- }
- }
- }
- // Create links to all listed items.
- void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
- LLInventoryModel::item_array_t& items,
- LLPointer<LLInventoryCallback> cb)
- {
- for (S32 i=0; i<items.count(); i++)
- {
- const LLInventoryItem* item = items.get(i).get();
- link_inventory_item(gAgent.getID(),
- item->getLinkedUUID(),
- cat_uuid,
- item->getName(),
- item->LLInventoryItem::getDescription(),
- LLAssetType::AT_LINK,
- cb);
- const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid);
- const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND";
- #ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl;
- #endif
- }
- }
- void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
- {
- LLViewerInventoryCategory *pcat = gInventory.getCategory(category);
- llinfos << "starting, cat " << (pcat ? pcat->getName() : "[UNKNOWN]") << llendl;
- const LLUUID cof = getCOF();
- // Deactivate currently active gestures in the COF, if replacing outfit
- if (!append)
- {
- LLInventoryModel::item_array_t gest_items;
- getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false);
- for(S32 i = 0; i < gest_items.count(); ++i)
- {
- LLViewerInventoryItem *gest_item = gest_items.get(i);
- if ( LLGestureMgr::instance().isGestureActive( gest_item->getLinkedUUID()) )
- {
- LLGestureMgr::instance().deactivateGesture( gest_item->getLinkedUUID() );
- }
- }
- }
-
- // Collect and filter descendents to determine new COF contents.
- // - Body parts: always include COF contents as a fallback in case any
- // required parts are missing.
- // Preserve body parts from COF if appending.
- LLInventoryModel::item_array_t body_items;
- getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART, false);
- getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART, false);
- if (append)
- reverse(body_items.begin(), body_items.end());
- // Reduce body items to max of one per type.
- removeDuplicateItems(body_items);
- filterWearableItems(body_items, 1);
- // - Wearables: include COF contents only if appending.
- LLInventoryModel::item_array_t wear_items;
- if (append)
- getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING, false);
- getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING, false);
- // Reduce wearables to max of one per type.
- removeDuplicateItems(wear_items);
- filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE);
- // - Attachments: include COF contents only if appending.
- LLInventoryModel::item_array_t obj_items;
- if (append)
- getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT, false);
- getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT, false);
- removeDuplicateItems(obj_items);
- // - Gestures: include COF contents only if appending.
- LLInventoryModel::item_array_t gest_items;
- if (append)
- getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false);
- getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE, false);
- removeDuplicateItems(gest_items);
-
- // Remove current COF contents.
- bool keep_outfit_links = append;
- purgeCategory(cof, keep_outfit_links);
- gInventory.notifyObservers();
- // Create links to new COF contents.
- llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl;
- LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy(!append);
- #ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking body items" << llendl;
- #endif
- linkAll(cof, body_items, link_waiter);
- #ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking wear items" << llendl;
- #endif
- linkAll(cof, wear_items, link_waiter);
- #ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking obj items" << llendl;
- #endif
- linkAll(cof, obj_items, link_waiter);
- #ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking gesture items" << llendl;
- #endif
- linkAll(cof, gest_items, link_waiter);
- // Add link to outfit if category is an outfit.
- if (!append)
- {
- createBaseOutfitLink(category, link_waiter);
- }
- llinfos << "waiting for LLUpdateAppearanceOnDestroy" << llendl;
- }
- void LLAppearanceMgr::updatePanelOutfitName(const std::string& name)
- {
- LLSidepanelAppearance* panel_appearance =
- dynamic_cast<LLSidepanelAppearance *>(LLFloaterSidePanelContainer::getPanel("appearance"));
- if (panel_appearance)
- {
- panel_appearance->refreshCurrentOutfitName(name);
- }
- }
- void LLAppearanceMgr::createBaseOutfitLink(const LLUUID& category, LLPointer<LLInventoryCallback> link_waiter)
- {
- const LLUUID cof = getCOF();
- LLViewerInventoryCategory* catp = gInventory.getCategory(category);
- std::string new_outfit_name = "";
- purgeBaseOutfitLink(cof);
- if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
- {
- link_inventory_item(gAgent.getID(), category, cof, catp->getName(), "",
- LLAssetType::AT_LINK_FOLDER, link_waiter);
- new_outfit_name = catp->getName();
- }
-
- updatePanelOutfitName(new_outfit_name);
- }
- void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, bool append)
- {
- lldebugs << "updateAgentWearables()" << llendl;
- LLInventoryItem::item_array_t items;
- LLDynamicArray< LLWearable* > wearables;
- // For each wearable type, find the wearables of that type.
- for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ )
- {
- for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->getFoundList().begin();
- iter != holder->getFoundList().end(); ++iter)
- {
- LLFoundData& data = *iter;
- LLWearable* wearable = data.mWearable;
- if( wearable && ((S32)wearable->getType() == i) )
- {
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(data.mItemID);
- if( item && (item->getAssetUUID() == wearable->getAssetID()) )
- {
- items.put(item);
- wearables.put(wearable);
- }
- }
- }
- }
- if(wearables.count() > 0)
- {
- gAgentWearables.setWearableOutfit(items, wearables, !append);
- }
- // dec_busy_count();
- }
- static void remove_non_link_items(LLInventoryModel::item_array_t &items)
- {
- LLInventoryModel::item_array_t pruned_items;
- for (LLInventoryModel::item_array_t::const_iterator iter = items.begin();
- iter != items.end();
- ++iter)
- {
- const LLViewerInventoryItem *item = (*iter);
- if (item && item->getIsLinkType())
- {
- pruned_items.push_back((*iter));
- }
- }
- 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 item_array_diff(LLInventoryModel::item_array_t& full_list,
- LLInventoryModel::item_array_t& keep_list,
- LLInventoryModel::item_array_t& kill_list)
-
- {
- for (LLInventoryModel::item_array_t::iterator it = full_list.begin();
- it != full_list.end();
- ++it)
- {
- LLViewerInventoryItem *item = *it;
- if (keep_list.find(item) < 0) // Why on earth does LLDynamicArray need to redefine find()?
- {
- kill_list.push_back(item);
- }
- }
- }
- S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,
- LLAssetType::EType type,
- S32 max_items,
- LLInventoryModel::item_array_t& items_to_kill)
- {
- S32 to_kill_count = 0;
- LLInventoryModel::item_array_t items;
- getDescendentsOfAssetType(cat_id, items, type, false);
- LLInventoryModel::item_array_t curr_items = items;
- removeDuplicateItems(items);
- if (max_items > 0)
- {
- filterWearableItems(items, max_items);
- }
- LLInventoryModel::item_array_t kill_items;
- item_array_diff(curr_items,items,kill_items);
- for (LLInventoryModel::item_array_t::iterator it = kill_items.begin();
- it != kill_items.end();
- ++it)
- {
- items_to_kill.push_back(*it);
- to_kill_count++;
- }
- return to_kill_count;
- }
-
-
- void LLAppearanceMgr::enforceItemRestrictions()
- {
- S32 purge_count = 0;
- LLInventoryModel::item_array_t items_to_kill;
- purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_BODYPART,
- 1, items_to_kill);
- purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_CLOTHING,
- LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill);
- purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_OBJECT,
- -1, items_to_kill);
- if (items_to_kill.size()>0)
- {
- for (LLInventoryModel::item_array_t::iterator it = items_to_kill.begin();
- it != items_to_kill.end();
- ++it)
- {
- LLViewerInventoryItem *item = *it;
- llinfos << "purging duplicate or excess item " << item->getName() << llendl;
- gInventory.purgeObject(item->getUUID());
- }
- gInventory.notifyObservers();
- }
- }
- void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
- {
- if (mIsInUpdateAppearanceFromCOF)
- {
- llwarns << "Called updateAppearanceFromCOF inside updateAppearanceFromCOF, skipping" << llendl;
- return;
- }
- BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF);
- llinfos << "starting" << llendl;
- //checking integrity of the COF in terms of ordering of wearables,
- //checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state)
- updateClothingOrderingInfo(LLUUID::null, update_base_outfit_ordering);
- // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but
- // this should catch anything that gets through.
- enforceItemRestrictions();
-
- // update dirty flag to see if the state of the COF matches
- // the saved outfit stored as a folder link
- updateIsDirty();
- //dumpCat(getCOF(),"COF, start");
- bool follow_folder_links = true;
- LLUUID current_outfit_id = getCOF();
- // Find all the wearables that are in the COF's subtree.
- lldebugs << "LLAppearanceMgr::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);
- // Get rid of non-links in case somehow the COF was corrupted.
- remove_non_link_items(wear_items);
- remove_non_link_items(obj_items);
- remove_non_link_items(gest_items);
- dumpItemArray(wear_items,"asset_dump: wear_item");
- dumpItemArray(obj_items,"asset_dump: obj_item");
- if(!wear_items.count())
- {
- LLNotificationsUtil::add("CouldNotPutOnOutfit");
- return;
- }
- //preparing the list of wearables in the correct order for LLAgentWearables
- sortItemsByActualDescription(wear_items);
- LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
- holder->setObjItems(obj_items);
- holder->setGestItems(gest_items);
-
- // 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().
- for(S32 i = 0; i < wear_items.count(); ++i)
- {
- LLViewerInventoryItem *item = wear_items.get(i);
- LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL;
- // Fault injection: use debug setting to test asset
- // fetch failures (should be replaced by new defaults in
- // lost&found).
- U32 skip_type = gSavedSettings.getU32("ForceAssetFail");
- if (item && item->getIsLinkType() && linked_item)
- {
- LLFoundData found(linked_item->getUUID(),
- linked_item->getAssetUUID(),
- linked_item->getName(),
- linked_item->getType(),
- linked_item->isWearableType() ? linked_item->getWearableType() : LLWearableType::WT_INVALID
- );
- if (skip_type != LLWearableType::WT_INVALID && skip_type == found.mWearableType)
- {
- found.mAssetID.generate(); // Replace with new UUID, guaranteed not to exist in DB
- }
- //pushing back, not front, to preserve order of wearables for LLAgentWearables
- holder->getFoundList().push_back(found);
- }
- else
- {
- if (!item)
- {
- llwarns << "Attempt to wear a null item " << llendl;
- }
- else if (!linked_item)
- {
- llwarns << "Attempt to wear a broken link [ name:" << item->getName() << " ] " << llendl;
- }
- }
- }
- for (LLWearableHoldingPattern::found_list_t::iterator it = holder->getFoundList().begin();
- it != holder->getFoundList().end(); ++it)
- {
- LLFoundData& found = *it;
- lldebugs << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl;
- // Fetch the wearables about to be worn.
- LLWearableList::instance().getAsset(found.mAssetID,
- found.mName,
- found.mAssetType,
- onWearableAssetFetch,
- (void*)holder);
- }
- holder->resetTime(gSavedSettings.getF32("MaxWearableWaitTime"));
- if (!holder->pollFetchCompletion())
- {
- doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollFetchCompletion,holder));
- }
- }
- void LLAppearanceMgr::getDescendentsOfAssetType(const LLUUID& category,
- LLInventoryModel::item_array_t& items,
- LLAssetType::EType type,
- bool follow_folder_links)
- {
- LLInventoryModel::cat_array_t cats;
- LLIsType is_of_type(type);
- gInventory.collectDescendentsIf(category,
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- is_of_type,
- follow_folder_links);
- }
- void LLAppearanceMgr::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 LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append)
- {
- if(!category) return;
- gAgentWearables.notifyLoadingStarted();
- llinfos << "wearInventoryCategory( " << category->getName()
- << " )" << llendl;
- callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal,
- &LLAppearanceMgr::instance(),
- category->getUUID(), copy, append));
- }
- void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append)
- {
- llinfos << "starting" << llendl;
-
- // We now have an outfit ready to be copied to agent inventory. Do
- // it, and wear that outfit normally.
- LLInventoryCategory* cat = gInventory.getCategory(cat_id);
- if(copy_items)
- {
- LLInventoryModel::cat_array_t* cats;
- LLInventoryModel::item_array_t* items;
- gInventory.getDirectDescendentsOf(cat_id, cats, items);
- std::string name;
- if(!cat)
- {
- // should never happen.
- name = "New Outfit";
- }
- else
- {
- name = cat->getName();
- }
- LLViewerInventoryItem* item = NULL;
- LLInventoryModel::item_array_t::const_iterator it = items->begin();
- LLInventoryModel::item_array_t::const_iterator end = items->end();
- LLUUID pid;
- for(; it < end; ++it)
- {
- item = *it;
- if(item)
- {
- if(LLInventoryType::IT_GESTURE == item->getInventoryType())
- {
- pid = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE);
- }
- else
- {
- pid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING);
- }
- break;
- }
- }
- if(pid.isNull())
- {
- pid = gInventory.getRootFolderID();
- }
-
- LLUUID new_cat_id = gInventory.createNewCategory(
- pid,
- LLFolderType::FT_NONE,
- name);
- LLPointer<LLInventoryCallback> cb = new LLWearInventoryCategoryCallback(new_cat_id, append);
- it = items->begin();
- for(; it < end; ++it)
- {
- item = *it;
- if(item)
- {
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- new_cat_id,
- std::string(),
- cb);
- }
- }
- // BAP fixes a lag in display of created dir.
- gInventory.notifyObservers();
- }
- else
- {
- // Wear the inventory category.
- LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(cat, append);
- }
- }
- // *NOTE: hack to get from avatar inventory to avatar
- void LLAppearanceMgr::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;
- llinfos << "wearInventoryCategoryOnAvatar( " << category->getName()
- << " )" << llendl;
-
- if (gAgentCamera.cameraCustomizeAvatar())
- {
- // switching to outfit editor should automagically save any currently edited wearable
- LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_outfit"));
- }
- LLAppearanceMgr::changeOutfit(TRUE, category->getUUID(), append);
- }
- void LLAppearanceMgr::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)
- {
- LLAppearanceMgr::wearInventoryCategory(cat, copy_items, false);
- }
- else
- {
- llwarns << "Couldn't find outfit " <<name<< " in wearOutfitByName()"
- << llendl;
- }
- //dec_busy_count();
- }
- bool areMatchingWearables(const LLViewerInventoryItem *a, const LLViewerInventoryItem *b)
- {
- return (a->isWearableType() && b->isWearableType() &&
- (a->getWearableType() == b->getWearableType()));
- }
- class LLDeferredCOFLinkObserver: public LLInventoryObserver
- {
- public:
- LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update, LLPointer<LLInventoryCallback> cb = NULL):
- mItemID(item_id),
- mDoUpdate(do_update),
- mCallback(cb)
- {
- }
- ~LLDeferredCOFLinkObserver()
- {
- }
-
- /* virtual */ void changed(U32 mask)
- {
- const LLInventoryItem *item = gInventory.getItem(mItemID);
- if (item)
- {
- gInventory.removeObserver(this);
- LLAppearanceMgr::instance().addCOFItemLink(item,mDoUpdate,mCallback);
- delete this;
- }
- }
- private:
- const LLUUID mItemID;
- bool mDoUpdate;
- LLPointer<LLInventoryCallback> mCallback;
- };
- // BAP - note that this runs asynchronously if the item is not already loaded from inventory.
- // Dangerous if caller assumes link will exist after calling the function.
- void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, bool do_update, LLPointer<LLInventoryCallback> cb)
- {
- const LLInventoryItem *item = gInventory.getItem(item_id);
- if (!item)
- {
- LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, do_update, cb);
- gInventory.addObserver(observer);
- }
- else
- {
- addCOFItemLink(item, do_update, cb);
- }
- }
- void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update, LLPointer<LLInventoryCallback> cb)
- {
- const LLViewerInventoryItem *vitem = dynamic_cast<const LLViewerInventoryItem*>(item);
- if (!vitem)
- {
- llwarns << "not an llviewerinventoryitem, failed" << llendl;
- return;
- }
- gInventory.addChangedMask(LLInventoryObserver::LABEL, vitem->getLinkedUUID());
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- gInventory.collectDescendents(LLAppearanceMgr::getCOF(),
- cat_array,
- item_array,
- LLInventoryModel::EXCLUDE_TRASH);
- bool linked_already = false;
- U32 count = 0;
- for (S32 i=0; i<item_array.count(); i++)
- {
- // Are these links to the same object?
- const LLViewerInventoryItem* inv_item = item_array.get(i).get();
- const LLWearableType::EType wearable_type = inv_item->getWearableType();
- const bool is_body_part = (wearable_type == LLWearableType::WT_SHAPE)
- || (wearable_type == LLWearableType::WT_HAIR)
- || (wearable_type == LLWearableType::WT_EYES)
- || (wearable_type == LLWearableType::WT_SKIN);
- if (inv_item->getLinkedUUID() == vitem->getLinkedUUID())
- {
- linked_already = true;
- }
- // Are these links to different items of the same body part
- // type? If so, new item will replace old.
- else if ((vitem->isWearableType()) && (vitem->getWearableType() == wearable_type))
- {
- ++count;
- if (is_body_part && inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type))
- {
- gInventory.purgeObject(inv_item->getUUID());
- }
- else if (count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE)
- {
- // MULTI-WEARABLES: make sure we don't go over MAX_CLOTHING_PER_TYPE
- gInventory.purgeObject(inv_item->getUUID());
- }
- }
- }
- if (linked_already)
- {
- if (do_update)
- {
- LLAppearanceMgr::updateAppearanceFromCOF();
- }
- return;
- }
- else
- {
- if(do_update && cb.isNull())
- {
- cb = new ModifiedCOFCallback;
- }
- const std::string description = vitem->getIsLinkType() ? vitem->getDescription() : "";
- link_inventory_item( gAgent.getID(),
- vitem->getLinkedUUID(),
- getCOF(),
- vitem->getName(),
- description,
- LLAssetType::AT_LINK,
- cb);
- }
- return;
- }
- // BAP remove ensemble code for 2.1?
- void LLAppearanceMgr::addEnsembleLink( LLInventoryCategory* cat, bool do_update )
- {
- #if SUPPORT_ENSEMBLES
- // 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(),
- cat->getDescription(),
- LLAssetType::AT_LINK_FOLDER,
- cb);
- #endif
- }
- void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, bool do_update)
- {
- gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- gInventory.collectDescendents(LLAppearanceMgr::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->getIsLinkType() && item->getLinkedUUID() == item_id)
- {
- gInventory.purgeObject(item->getUUID());
- }
- }
- if (do_update)
- {
- LLAppearanceMgr::updateAppearanceFromCOF();
- }
- }
- void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_update)
- {
- LLFindWearablesOfType filter_wearables_of_type(type);
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLInventoryModel::item_array_t::const_iterator it;
- gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type);
- for (it = items.begin(); it != items.end(); ++it)
- {
- const LLViewerInventoryItem* item = *it;
- if (item->getIsLinkType()) // we must operate on links only
- {
- gInventory.purgeObject(item->getUUID());
- }
- }
- if (do_update)
- {
- updateAppearanceFromCOF();
- }
- }
- bool sort_by_linked_uuid(const LLViewerInventoryItem* item1, const LLViewerInventoryItem* item2)
- {
- if (!item1 || !item2)
- {
- llwarning("item1, item2 cannot be null, something is very wrong", 0);
- return true;
- }
- return item1->getLinkedUUID() < item2->getLinkedUUID();
- }
- void LLAppearanceMgr::updateIsDirty()
- {
- LLUUID cof = getCOF();
- LLUUID base_outfit;
- // find base outfit link
- const LLViewerInventoryItem* base_outfit_item = getBaseOutfitLink();
- LLViewerInventoryCategory* catp = NULL;
- if (base_outfit_item && base_outfit_item->getIsLinkType())
- {
- catp = base_outfit_item->getLinkedCategory();
- }
- if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
- {
- base_outfit = catp->getUUID();
- }
- // Set dirty to "false" if no base outfit found to disable "Save"
- // and leave only "Save As" enabled in My Outfits.
- mOutfitIsDirty = false;
- if (base_outfit.notNull())
- {
- LLIsOfAssetType collector = LLIsOfAssetType(LLAssetType::AT_LINK);
- LLInventoryModel::cat_array_t cof_cats;
- LLInventoryModel::item_array_t cof_items;
- gInventory.collectDescendentsIf(cof, cof_cats, cof_items,
- LLInventoryModel::EXCLUDE_TRASH, collector);
- LLInventoryModel::cat_array_t outfit_cats;
- LLInventoryModel::item_array_t outfit_items;
- gInventory.collectDescendentsIf(base_outfit, outfit_cats, outfit_items,
- LLInventoryModel::EXCLUDE_TRASH, collector);
- if(outfit_items.cou