diff options
Diffstat (limited to 'indra/newview/llagentwearables.cpp')
-rwxr-xr-x[-rw-r--r--] | indra/newview/llagentwearables.cpp | 2033 |
1 files changed, 814 insertions, 1219 deletions
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 223f8779ec..5589ab4aa7 100644..100755 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -2,133 +2,180 @@ * @file llagentwearables.cpp * @brief LLAgentWearables class implementation * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * 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 + * 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. * - * 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 + * 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. * - * 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. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" - -#include "llagent.h" #include "llagentwearables.h" -#include "llfloatercustomize.h" -#include "llfloaterinventory.h" +#include "llaccordionctrltab.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llappearancemgr.h" +#include "llcallbacklist.h" +#include "llfloatersidepanelcontainer.h" +#include "llgesturemgr.h" #include "llinventorybridge.h" -#include "llinventorymodel.h" -#include "llnotify.h" +#include "llinventoryfunctions.h" +#include "llinventoryobserver.h" +#include "llinventorypanel.h" +#include "lllocaltextureobject.h" +#include "llmd5.h" +#include "llnotificationsutil.h" +#include "lloutfitobserver.h" +#include "llsidepanelappearance.h" +#include "lltexlayer.h" +#include "lltooldraganddrop.h" #include "llviewerregion.h" #include "llvoavatarself.h" -#include "llwearable.h" +#include "llviewerwearable.h" #include "llwearablelist.h" -#include "llgesturemgr.h" -#include "llappearancemgr.h" -#include "lltexlayer.h" +#include "llfloaterperms.h" #include <boost/scoped_ptr.hpp> -#define USE_CURRENT_OUTFIT_FOLDER +LLAgentWearables gAgentWearables; + +BOOL LLAgentWearables::mInitialWearablesUpdateReceived = FALSE; + +using namespace LLAvatarAppearanceDefines; + +/////////////////////////////////////////////////////////////////////////////// -//-------------------------------------------------------------------- -// Classes for fetching initial wearables data -//-------------------------------------------------------------------- -// Outfit folder fetching callback structure. -class LLInitialWearablesFetch : public LLInventoryFetchDescendentsObserver +// Callback to wear and start editing an item that has just been created. +void wear_and_edit_cb(const LLUUID& inv_item) { -public: - LLInitialWearablesFetch() {} - ~LLInitialWearablesFetch() {} - virtual void done(); + if (inv_item.isNull()) return; + + LLViewerInventoryItem* item = gInventory.getItem(inv_item); + if (!item) return; - struct InitialWearableData - { - EWearableType mType; - LLUUID mItemID; - LLUUID mAssetID; - InitialWearableData(EWearableType type, LLUUID itemID, LLUUID assetID) : - mType(type), mItemID(itemID), mAssetID(assetID) { } - }; + LLPermissions perm = item->getPermissions(); + perm.setMaskNext(LLFloaterPerms::getNextOwnerPerms("Wearables")); + perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Wearables")); + perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Wearables")); + item->setPermissions(perm); - typedef std::vector<InitialWearableData> initial_wearable_data_vec_t; - initial_wearable_data_vec_t mCOFInitialWearables; // Wearables from the Current Outfit Folder - initial_wearable_data_vec_t mAgentInitialWearables; // Wearables from the old agent wearables msg + item->updateServer(FALSE); + gInventory.updateItem(item); + gInventory.notifyObservers(); -protected: - void processWearablesMessage(); -}; + // Request editing the item after it gets worn. + gAgentWearables.requestEditingWearable(inv_item); + + // Wear it. + LLAppearanceMgr::instance().wearItemOnAvatar(inv_item,true); +} -LLAgentWearables gAgentWearables; +void wear_cb(const LLUUID& inv_item) +{ + if (!inv_item.isNull()) + { + LLViewerInventoryItem* item = gInventory.getItem(inv_item); + if (item) + { + LLPermissions perm = item->getPermissions(); + perm.setMaskNext(LLFloaterPerms::getNextOwnerPerms("Wearables")); + perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Wearables")); + perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Wearables")); + item->setPermissions(perm); + + item->updateServer(FALSE); + gInventory.updateItem(item); + gInventory.notifyObservers(); + } + } +} -BOOL LLAgentWearables::mInitialWearablesUpdateReceived = FALSE; +/////////////////////////////////////////////////////////////////////////////// -using namespace LLVOAvatarDefines; +// HACK: For EXT-3923: Pants item shows in inventory with skin icon and messes with "current look" +// Some db items are corrupted, have inventory flags = 0, implying wearable type = shape, even though +// wearable type stored in asset is some other value. +// Calling this function whenever a wearable is added to increase visibility if this problem +// turns up in other inventories. +void checkWearableAgainstInventory(LLViewerWearable *wearable) +{ + if (wearable->getItemID().isNull()) + return; + + // Check for wearable type consistent with inventory item wearable type. + LLViewerInventoryItem *item = gInventory.getItem(wearable->getItemID()); + if (item) + { + if (!item->isWearableType()) + { + LL_WARNS() << "wearable associated with non-wearable item" << LL_ENDL; + } + if (item->getWearableType() != wearable->getType()) + { + LL_WARNS() << "type mismatch: wearable " << wearable->getName() + << " has type " << wearable->getType() + << " but inventory item " << item->getName() + << " has type " << item->getWearableType() << LL_ENDL; + } + } + else + { + LL_WARNS() << "wearable inventory item not found" << wearable->getName() + << " itemID " << wearable->getItemID().asString() << LL_ENDL; + } +} void LLAgentWearables::dump() { - llinfos << "LLAgentWearablesDump" << llendl; - for (S32 i = 0; i < WT_COUNT; i++) + LL_INFOS() << "LLAgentWearablesDump" << LL_ENDL; + for (S32 i = 0; i < LLWearableType::WT_COUNT; i++) { - U32 count = getWearableCount((EWearableType)i); - llinfos << "Type: " << i << " count " << count << llendl; + U32 count = getWearableCount((LLWearableType::EType)i); + LL_INFOS() << "Type: " << i << " count " << count << LL_ENDL; for (U32 j=0; j<count; j++) { - LLWearable* wearable = getWearable((EWearableType)i,j); + LLViewerWearable* wearable = getViewerWearable((LLWearableType::EType)i,j); if (wearable == NULL) { - llinfos << " " << j << " NULL wearable" << llendl; + LL_INFOS() << " " << j << " NULL wearable" << LL_ENDL; } - llinfos << " " << j << " Name " << wearable->getName() - << " description " << wearable->getDescription() << llendl; + LL_INFOS() << " " << j << " Name " << wearable->getName() + << " description " << wearable->getDescription() << LL_ENDL; } } - llinfos << "Total items awaiting wearable update " << mItemsAwaitingWearableUpdate.size() << llendl; - for (std::set<LLUUID>::iterator it = mItemsAwaitingWearableUpdate.begin(); - it != mItemsAwaitingWearableUpdate.end(); - ++it) - { - llinfos << (*it).asString() << llendl; - } } -// MULTI-WEARABLE: debugging struct LLAgentDumper { LLAgentDumper(std::string name): mName(name) { - llinfos << llendl; - llinfos << "LLAgentDumper " << mName << llendl; + LL_INFOS() << LL_ENDL; + LL_INFOS() << "LLAgentDumper " << mName << LL_ENDL; gAgentWearables.dump(); } ~LLAgentDumper() { - llinfos << llendl; - llinfos << "~LLAgentDumper " << mName << llendl; + LL_INFOS() << LL_ENDL; + LL_INFOS() << "~LLAgentDumper " << mName << LL_ENDL; gAgentWearables.dump(); } @@ -136,8 +183,9 @@ struct LLAgentDumper }; LLAgentWearables::LLAgentWearables() : - mWearablesLoaded(FALSE), - mAvatarObject(NULL) + LLWearableData(), + mWearablesLoaded(FALSE) +, mCOFChangeInProgress(false) { } @@ -148,27 +196,20 @@ LLAgentWearables::~LLAgentWearables() void LLAgentWearables::cleanup() { - mAvatarObject = NULL; -} - -void LLAgentWearables::setAvatarObject(LLVOAvatarSelf *avatar) -{ - mAvatarObject = avatar; - if (avatar) - { - sendAgentWearablesRequest(); - } } -// wearables -LLAgentWearables::createStandardWearablesAllDoneCallback::~createStandardWearablesAllDoneCallback() +// static +void LLAgentWearables::initClass() { - gAgentWearables.createStandardWearablesAllDone(); + // this can not be called from constructor because its instance is global and is created too early. + // Subscribe to "COF is Saved" signal to notify observers about this (Loading indicator for ex.). + LLOutfitObserver::instance().addCOFSavedCallback(boost::bind(&LLAgentWearables::notifyLoadingFinished, &gAgentWearables)); } -LLAgentWearables::sendAgentWearablesUpdateCallback::~sendAgentWearablesUpdateCallback() +void LLAgentWearables::setAvatarObject(LLVOAvatarSelf *avatar) { - gAgentWearables.sendAgentWearablesUpdate(); + llassert(avatar); + setAvatarAppearance(avatar); } /** @@ -176,73 +217,73 @@ LLAgentWearables::sendAgentWearablesUpdateCallback::~sendAgentWearablesUpdateCal * * Would like to pass the agent in here, but we can't safely * count on it being around later. Just use gAgent directly. - * @param cb callback to execute on completion (??? unused ???) + * @param cb callback to execute on completion (? unused ?) * @param type Type for the wearable in the agent * @param wearable The wearable data. * @param todo Bitmask of actions to take on completion. */ -LLAgentWearables::addWearableToAgentInventoryCallback::addWearableToAgentInventoryCallback( - LLPointer<LLRefCount> cb, S32 type, U32 index, LLWearable* wearable, U32 todo) : +LLAgentWearables::AddWearableToAgentInventoryCallback::AddWearableToAgentInventoryCallback( + LLPointer<LLRefCount> cb, LLWearableType::EType type, U32 index, LLViewerWearable* wearable, U32 todo, const std::string description) : mType(type), mIndex(index), mWearable(wearable), mTodo(todo), - mCB(cb) + mCB(cb), + mDescription(description) { + LL_INFOS() << "constructor" << LL_ENDL; } -void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& inv_item) +void LLAgentWearables::AddWearableToAgentInventoryCallback::fire(const LLUUID& inv_item) { if (inv_item.isNull()) return; gAgentWearables.addWearabletoAgentInventoryDone(mType, mIndex, inv_item, mWearable); - if (mTodo & CALL_UPDATE) - { - gAgentWearables.sendAgentWearablesUpdate(); - } - if (mTodo & CALL_RECOVERDONE) - { - gAgentWearables.recoverMissingWearableDone(); - } /* * Do this for every one in the loop */ - if (mTodo & CALL_CREATESTANDARDDONE) - { - gAgentWearables.createStandardWearablesDone(mType, mIndex); - } if (mTodo & CALL_MAKENEWOUTFITDONE) { gAgentWearables.makeNewOutfitDone(mType, mIndex); } + if (mTodo & CALL_WEARITEM) + { + LLAppearanceMgr::instance().addCOFItemLink(inv_item, + new LLUpdateAppearanceAndEditWearableOnDestroy(inv_item), mDescription); + } } -void LLAgentWearables::addWearabletoAgentInventoryDone(const S32 type, +void LLAgentWearables::addWearabletoAgentInventoryDone(const LLWearableType::EType type, const U32 index, const LLUUID& item_id, - LLWearable* wearable) + LLViewerWearable* wearable) { + LL_INFOS() << "type " << type << " index " << index << " item " << item_id.asString() << LL_ENDL; + if (item_id.isNull()) return; - LLUUID old_item_id = getWearableItemID((EWearableType)type,index); + LLUUID old_item_id = getWearableItemID(type,index); + if (wearable) { wearable->setItemID(item_id); - } - if (old_item_id.notNull()) - { - gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id); - setWearable((EWearableType)type,index,wearable); - } - else - { - pushWearable((EWearableType)type,wearable); + if (old_item_id.notNull()) + { + gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id); + setWearable(type,index,wearable); + } + else + { + pushWearable(type,wearable); + } } + gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); + LLViewerInventoryItem* item = gInventory.getItem(item_id); if (item && wearable) { @@ -257,95 +298,33 @@ void LLAgentWearables::addWearabletoAgentInventoryDone(const S32 type, gInventory.notifyObservers(); } -void LLAgentWearables::sendAgentWearablesUpdate() +void LLAgentWearables::saveWearable(const LLWearableType::EType type, const U32 index, + const std::string new_name) { - // MULTI-WEARABLE: call i "type" or something. - // First make sure that we have inventory items for each wearable - for (S32 type=0; type < WT_COUNT; ++type) - { - for (U32 j=0; j < getWearableCount((EWearableType)type); ++j) - { - LLWearable* wearable = getWearable((EWearableType)type,j); - if (wearable) - { - if (wearable->getItemID().isNull()) - { - LLPointer<LLInventoryCallback> cb = - new addWearableToAgentInventoryCallback( - LLPointer<LLRefCount>(NULL), - type, - j, - wearable, - addWearableToAgentInventoryCallback::CALL_NONE); - addWearableToAgentInventory(cb, wearable); - } - else - { - gInventory.addChangedMask(LLInventoryObserver::LABEL, - wearable->getItemID()); - } - } - } - } - - // Then make sure the inventory is in sync with the avatar. - gInventory.notifyObservers(); - - // Send the AgentIsNowWearing - gMessageSystem->newMessageFast(_PREHASH_AgentIsNowWearing); - - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - - lldebugs << "sendAgentWearablesUpdate()" << llendl; - // MULTI-WEARABLE: update for multi-wearables after server-side support is in. - for (S32 type=0; type < WT_COUNT; ++type) - { - gMessageSystem->nextBlockFast(_PREHASH_WearableData); - - U8 type_u8 = (U8)type; - gMessageSystem->addU8Fast(_PREHASH_WearableType, type_u8); - - // MULTI-WEARABLE: TODO: hacked index to 0, needs to loop over all once messages support this. - LLWearable* wearable = getWearable((EWearableType)type, 0); - if (wearable) - { - //llinfos << "Sending wearable " << wearable->getName() << llendl; - LLUUID item_id = wearable->getItemID(); - const LLViewerInventoryItem *item = gInventory.getItem(item_id); - if (item && item->getIsLinkType()) - { - // Get the itemID that this item points to. i.e. make sure - // we are storing baseitems, not their links, in the database. - item_id = item->getLinkedUUID(); - } - gMessageSystem->addUUIDFast(_PREHASH_ItemID, item_id); - } - else - { - //llinfos << "Not wearing wearable type " << LLWearableDictionary::getInstance()->getWearable((EWearableType)i) << llendl; - gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID::null); - } - - lldebugs << " " << LLWearableDictionary::getTypeLabel((EWearableType)type) << ": " << (wearable ? wearable->getAssetID() : LLUUID::null) << llendl; - } - gAgent.sendReliableMessage(); -} - -void LLAgentWearables::saveWearable(const EWearableType type, const U32 index, BOOL send_update) -{ - LLWearable* old_wearable = getWearable(type, index); - if (old_wearable && (old_wearable->isDirty() || old_wearable->isOldVersion())) + LLViewerWearable* old_wearable = getViewerWearable(type, index); + if(!old_wearable) return; + bool name_changed = !new_name.empty() && (new_name != old_wearable->getName()); + if (name_changed || old_wearable->isDirty() || old_wearable->isOldVersion()) { LLUUID old_item_id = old_wearable->getItemID(); - LLWearable* new_wearable = LLWearableList::instance().createCopy(old_wearable); - new_wearable->setItemID(old_item_id); // should this be in LLWearable::copyDataFrom()? + LLViewerWearable* new_wearable = LLWearableList::instance().createCopy(old_wearable); + new_wearable->setItemID(old_item_id); // should this be in LLViewerWearable::copyDataFrom()? setWearable(type,index,new_wearable); + // old_wearable may still be referred to by other inventory items. Revert + // unsaved changes so other inventory items aren't affected by the changes + // that were just saved. + old_wearable->revertValues(); + LLInventoryItem* item = gInventory.getItem(old_item_id); if (item) { + std::string item_name = item->getName(); + if (name_changed) + { + LL_INFOS() << "saveWearable changing name from " << item->getName() << " to " << new_name << LL_ENDL; + item_name = new_name; + } // Update existing inventory item LLPointer<LLViewerInventoryItem> template_item = new LLViewerInventoryItem(item->getUUID(), @@ -354,27 +333,22 @@ void LLAgentWearables::saveWearable(const EWearableType type, const U32 index, B new_wearable->getAssetID(), new_wearable->getAssetType(), item->getInventoryType(), - item->getName(), + item_name, item->getDescription(), item->getSaleInfo(), item->getFlags(), item->getCreationDate()); template_item->setTransactionID(new_wearable->getTransactionID()); - template_item->updateServer(FALSE); - gInventory.updateItem(template_item); + update_inventory_item(template_item, gAgentAvatarp->mEndCustomizeCallback); } else { // Add a new inventory item (shouldn't ever happen here) - U32 todo = addWearableToAgentInventoryCallback::CALL_NONE; - if (send_update) - { - todo |= addWearableToAgentInventoryCallback::CALL_UPDATE; - } + U32 todo = AddWearableToAgentInventoryCallback::CALL_NONE; LLPointer<LLInventoryCallback> cb = - new addWearableToAgentInventoryCallback( + new AddWearableToAgentInventoryCallback( LLPointer<LLRefCount>(NULL), - (S32)type, + type, index, new_wearable, todo); @@ -382,55 +356,54 @@ void LLAgentWearables::saveWearable(const EWearableType type, const U32 index, B return; } - gAgent.getAvatarObject()->wearableUpdated( type ); - - if (send_update) - { - sendAgentWearablesUpdate(); - } + gAgentAvatarp->wearableUpdated(type); } } -void LLAgentWearables::saveWearableAs(const EWearableType type, +void LLAgentWearables::saveWearableAs(const LLWearableType::EType type, const U32 index, const std::string& new_name, + const std::string& description, BOOL save_in_lost_and_found) { if (!isWearableCopyable(type, index)) { - llwarns << "LLAgent::saveWearableAs() not copyable." << llendl; + LL_WARNS() << "LLAgent::saveWearableAs() not copyable." << LL_ENDL; return; } - LLWearable* old_wearable = getWearable(type, index); + LLViewerWearable* old_wearable = getViewerWearable(type, index); if (!old_wearable) { - llwarns << "LLAgent::saveWearableAs() no old wearable." << llendl; + LL_WARNS() << "LLAgent::saveWearableAs() no old wearable." << LL_ENDL; return; } LLInventoryItem* item = gInventory.getItem(getWearableItemID(type,index)); if (!item) { - llwarns << "LLAgent::saveWearableAs() no inventory item." << llendl; + LL_WARNS() << "LLAgent::saveWearableAs() no inventory item." << LL_ENDL; return; } std::string trunc_name(new_name); LLStringUtil::truncate(trunc_name, DB_INV_ITEM_NAME_STR_LEN); - LLWearable* new_wearable = LLWearableList::instance().createCopy( + LLViewerWearable* new_wearable = LLWearableList::instance().createCopy( old_wearable, trunc_name); + LLPointer<LLInventoryCallback> cb = - new addWearableToAgentInventoryCallback( + new AddWearableToAgentInventoryCallback( LLPointer<LLRefCount>(NULL), type, index, new_wearable, - addWearableToAgentInventoryCallback::CALL_UPDATE); + AddWearableToAgentInventoryCallback::CALL_WEARITEM, + description + ); LLUUID category_id; if (save_in_lost_and_found) { category_id = gInventory.findCategoryUUIDForType( - LLAssetType::AT_LOST_AND_FOUND); + LLFolderType::FT_LOST_AND_FOUND); } else { @@ -445,14 +418,21 @@ void LLAgentWearables::saveWearableAs(const EWearableType type, category_id, new_name, cb); + + // old_wearable may still be referred to by other inventory items. Revert + // unsaved changes so other inventory items aren't affected by the changes + // that were just saved. + old_wearable->revertValues(); } -void LLAgentWearables::revertWearable(const EWearableType type, const U32 index) +void LLAgentWearables::revertWearable(const LLWearableType::EType type, const U32 index) { - LLWearable* wearable = getWearable(type, index); - wearable->revertValues(); - - gAgent.sendAgentSetAppearance(); + LLViewerWearable* wearable = getViewerWearable(type, index); + llassert(wearable); + if (wearable) + { + wearable->revertValues(); + } } void LLAgentWearables::saveAllWearables() @@ -462,30 +442,30 @@ void LLAgentWearables::saveAllWearables() // return; //} - for (S32 i=0; i < WT_COUNT; i++) + for (S32 i=0; i < LLWearableType::WT_COUNT; i++) { - for (U32 j=0; j < getWearableCount((EWearableType)i); j++) - saveWearable((EWearableType)i, j, FALSE); + for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++) + saveWearable((LLWearableType::EType)i, j); } - sendAgentWearablesUpdate(); } // Called when the user changes the name of a wearable inventory item that is currently being worn. void LLAgentWearables::setWearableName(const LLUUID& item_id, const std::string& new_name) { - for (S32 i=0; i < WT_COUNT; i++) + for (S32 i=0; i < LLWearableType::WT_COUNT; i++) { - for (U32 j=0; j < getWearableCount((EWearableType)i); j++) + for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++) { - LLUUID curr_item_id = getWearableItemID((EWearableType)i,j); + LLUUID curr_item_id = getWearableItemID((LLWearableType::EType)i,j); if (curr_item_id == item_id) { - LLWearable* old_wearable = getWearable((EWearableType)i,j); + LLViewerWearable* old_wearable = getViewerWearable((LLWearableType::EType)i,j); llassert(old_wearable); + if (!old_wearable) continue; std::string old_name = old_wearable->getName(); old_wearable->setName(new_name); - LLWearable* new_wearable = LLWearableList::instance().createCopy(old_wearable); + LLViewerWearable* new_wearable = LLWearableList::instance().createCopy(old_wearable); new_wearable->setItemID(item_id); LLInventoryItem* item = gInventory.getItem(item_id); if (item) @@ -494,8 +474,7 @@ void LLAgentWearables::setWearableName(const LLUUID& item_id, const std::string& } old_wearable->setName(old_name); - setWearable((EWearableType)i,j,new_wearable); - sendAgentWearablesUpdate(); + setWearable((LLWearableType::EType)i,j,new_wearable); break; } } @@ -503,12 +482,18 @@ void LLAgentWearables::setWearableName(const LLUUID& item_id, const std::string& } -BOOL LLAgentWearables::isWearableModifiable(EWearableType type, U32 index) const +BOOL LLAgentWearables::isWearableModifiable(LLWearableType::EType type, U32 index) const { LLUUID item_id = getWearableItemID(type, index); - if (!item_id.isNull()) + return item_id.notNull() ? isWearableModifiable(item_id) : FALSE; +} + +BOOL LLAgentWearables::isWearableModifiable(const LLUUID& item_id) const +{ + const LLUUID& linked_id = gInventory.getLinkedItemID(item_id); + if (linked_id.notNull()) { - LLInventoryItem* item = gInventory.getItem(item_id); + LLInventoryItem* item = gInventory.getItem(linked_id); if (item && item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID())) { @@ -518,7 +503,7 @@ BOOL LLAgentWearables::isWearableModifiable(EWearableType type, U32 index) const return FALSE; } -BOOL LLAgentWearables::isWearableCopyable(EWearableType type, U32 index) const +BOOL LLAgentWearables::isWearableCopyable(LLWearableType::EType type, U32 index) const { LLUUID item_id = getWearableItemID(type, index); if (!item_id.isNull()) @@ -534,7 +519,7 @@ BOOL LLAgentWearables::isWearableCopyable(EWearableType type, U32 index) const } /* - U32 LLAgentWearables::getWearablePermMask(EWearableType type) + U32 LLAgentWearables::getWearablePermMask(LLWearableType::EType type) { LLUUID item_id = getWearableItemID(type); if (!item_id.isNull()) @@ -549,7 +534,7 @@ BOOL LLAgentWearables::isWearableCopyable(EWearableType type, U32 index) const } */ -LLInventoryItem* LLAgentWearables::getWearableInventoryItem(EWearableType type, U32 index) +LLInventoryItem* LLAgentWearables::getWearableInventoryItem(LLWearableType::EType type, U32 index) { LLUUID item_id = getWearableItemID(type,index); LLInventoryItem* item = NULL; @@ -560,14 +545,15 @@ LLInventoryItem* LLAgentWearables::getWearableInventoryItem(EWearableType type, return item; } -const LLWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) const +const LLViewerWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) const { - for (S32 i=0; i < WT_COUNT; i++) + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); + for (S32 i=0; i < LLWearableType::WT_COUNT; i++) { - for (U32 j=0; j < getWearableCount((EWearableType)i); j++) + for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++) { - const LLWearable * curr_wearable = getWearable((EWearableType)i, j); - if (curr_wearable && (curr_wearable->getItemID() == item_id)) + const LLViewerWearable * curr_wearable = getViewerWearable((LLWearableType::EType)i, j); + if (curr_wearable && (curr_wearable->getItemID() == base_item_id)) { return curr_wearable; } @@ -576,14 +562,15 @@ const LLWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) return NULL; } -const LLWearable* LLAgentWearables::getWearableFromAssetID(const LLUUID& asset_id) const +LLViewerWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) { - for (S32 i=0; i < WT_COUNT; i++) + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); + for (S32 i=0; i < LLWearableType::WT_COUNT; i++) { - for (U32 j=0; j < getWearableCount((EWearableType)i); j++) + for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++) { - const LLWearable * curr_wearable = getWearable((EWearableType)i, j); - if (curr_wearable && (curr_wearable->getAssetID() == asset_id)) + LLViewerWearable * curr_wearable = getViewerWearable((LLWearableType::EType)i, j); + if (curr_wearable && (curr_wearable->getItemID() == base_item_id)) { return curr_wearable; } @@ -592,696 +579,281 @@ const LLWearable* LLAgentWearables::getWearableFromAssetID(const LLUUID& asset_i return NULL; } -void LLAgentWearables::sendAgentWearablesRequest() -{ - gMessageSystem->newMessageFast(_PREHASH_AgentWearablesRequest); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gAgent.sendReliableMessage(); -} - -// static -BOOL LLAgentWearables::selfHasWearable(EWearableType type) -{ - return (gAgentWearables.getWearableCount(type) > 0); -} - -LLWearable* LLAgentWearables::getWearable(const EWearableType type, U32 index) -{ - wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(type); - if (wearable_iter == mWearableDatas.end()) - { - return NULL; - } - wearableentry_vec_t& wearable_vec = wearable_iter->second; - if (index>=wearable_vec.size()) - { - return NULL; - } - else - { - return wearable_vec[index]; - } -} - -void LLAgentWearables::setWearable(const EWearableType type, U32 index, LLWearable *wearable) +LLViewerWearable* LLAgentWearables::getWearableFromAssetID(const LLUUID& asset_id) { - if (!getWearable(type,index)) - { - pushWearable(type,wearable); - return; - } - wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(type); - if (wearable_iter == mWearableDatas.end()) - { - llwarns << "invalid type, type " << type << " index " << index << llendl; - return; - } - wearableentry_vec_t& wearable_vec = wearable_iter->second; - if (index>=wearable_vec.size()) - { - llwarns << "invalid index, type " << type << " index " << index << llendl; - } - else + for (S32 i=0; i < LLWearableType::WT_COUNT; i++) { - wearable_vec[index] = wearable; + for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++) + { + LLViewerWearable * curr_wearable = getViewerWearable((LLWearableType::EType)i, j); + if (curr_wearable && (curr_wearable->getAssetID() == asset_id)) + { + return curr_wearable; + } + } } + return NULL; } -U32 LLAgentWearables::pushWearable(const EWearableType type, LLWearable *wearable) +LLViewerWearable* LLAgentWearables::getViewerWearable(const LLWearableType::EType type, U32 index /*= 0*/) { - if (wearable == NULL) - { - // no null wearables please! - //TODO: insert llwarns - return MAX_ATTACHMENTS_PER_TYPE; - } - if (type < WT_COUNT) - { - mWearableDatas[type].push_back(wearable); - return mWearableDatas[type].size()-1; - } - return MAX_ATTACHMENTS_PER_TYPE; + return dynamic_cast<LLViewerWearable*> (getWearable(type, index)); } -void LLAgentWearables::popWearable(const EWearableType type, LLWearable *wearable) +const LLViewerWearable* LLAgentWearables::getViewerWearable(const LLWearableType::EType type, U32 index /*= 0*/) const { - U32 index = getWearableIndex(type, wearable); - if (index < MAX_ATTACHMENTS_PER_TYPE && index < getWearableCount(type)) - { - popWearable(type, index); - } + return dynamic_cast<const LLViewerWearable*> (getWearable(type, index)); } -void LLAgentWearables::popWearable(const EWearableType type, U32 index) +// static +BOOL LLAgentWearables::selfHasWearable(LLWearableType::EType type) { - if (getWearable(type, index)) - { - mWearableDatas[type].erase(mWearableDatas[type].begin() + index); - } + return (gAgentWearables.getWearableCount(type) > 0); } -U32 LLAgentWearables::getWearableIndex(const EWearableType type, LLWearable *wearable) +// virtual +void LLAgentWearables::wearableUpdated(LLWearable *wearable, BOOL removed) { - wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type); - if (wearable_iter == mWearableDatas.end()) + if (isAgentAvatarValid()) { - llwarns << "tried to get wearable index with an invalid type!" << llendl; - return MAX_ATTACHMENTS_PER_TYPE; - } - const wearableentry_vec_t& wearable_vec = wearable_iter->second; - for(U32 index = 0; index < wearable_vec.size(); index++) - { - if (wearable_vec[index] == wearable) - { - return index; - } + gAgentAvatarp->wearableUpdated(wearable->getType()); } - return MAX_ATTACHMENTS_PER_TYPE; -} + LLWearableData::wearableUpdated(wearable, removed); -const LLWearable* LLAgentWearables::getWearable(const EWearableType type, U32 index) const -{ - wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type); - if (wearable_iter == mWearableDatas.end()) - { - return NULL; - } - const wearableentry_vec_t& wearable_vec = wearable_iter->second; - if (index>=wearable_vec.size()) + if (!removed) { - return NULL; - } - else - { - return wearable_vec[index]; - } -} + LLViewerWearable* viewer_wearable = dynamic_cast<LLViewerWearable*>(wearable); + viewer_wearable->refreshName(); -LLWearable* LLAgentWearables::getTopWearable(const EWearableType type) -{ - U32 count = getWearableCount(type); - if ( count == 0) - { - return NULL; - } - - return getWearable(type, count-1); -} + // Hack pt 2. If the wearable we just loaded has definition version 24, + // then force a re-save of this wearable after slamming the version number to 22. + // This number was incorrectly incremented for internal builds before release, and + // this fix will ensure that the affected wearables are re-saved with the right version number. + // the versions themselves are compatible. This code can be removed before release. + if( wearable->getDefinitionVersion() == 24 ) + { + U32 index; + if (getWearableIndex(wearable,index)) + { + LL_INFOS() << "forcing wearable type " << wearable->getType() << " to version 22 from 24" << LL_ENDL; + wearable->setDefinitionVersion(22); + saveWearable(wearable->getType(),index); + } + } -U32 LLAgentWearables::getWearableCount(const EWearableType type) const -{ - wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type); - if (wearable_iter == mWearableDatas.end()) - { - return 0; + checkWearableAgainstInventory(viewer_wearable); } - const wearableentry_vec_t& wearable_vec = wearable_iter->second; - return wearable_vec.size(); -} - -U32 LLAgentWearables::getWearableCount(const U32 tex_index) const -{ - const EWearableType wearable_type = LLVOAvatarDictionary::getTEWearableType((LLVOAvatarDefines::ETextureIndex)tex_index); - return getWearableCount(wearable_type); -} - - -BOOL LLAgentWearables::itemUpdatePending(const LLUUID& item_id) const -{ - return mItemsAwaitingWearableUpdate.find(item_id) != mItemsAwaitingWearableUpdate.end(); } -U32 LLAgentWearables::itemUpdatePendingCount() const +const LLUUID LLAgentWearables::getWearableItemID(LLWearableType::EType type, U32 index) const { - return mItemsAwaitingWearableUpdate.size(); -} - -const LLUUID LLAgentWearables::getWearableItemID(EWearableType type, U32 index) const -{ - const LLWearable *wearable = getWearable(type,index); + const LLViewerWearable *wearable = getViewerWearable(type,index); if (wearable) return wearable->getItemID(); else return LLUUID(); } -const LLUUID LLAgentWearables::getWearableAssetID(EWearableType type, U32 index) const +const LLUUID LLAgentWearables::getWearableAssetID(LLWearableType::EType type, U32 index) const { - const LLWearable *wearable = getWearable(type,index); + const LLViewerWearable *wearable = getViewerWearable(type,index); if (wearable) return wearable->getAssetID(); else return LLUUID(); } -// Warning: include_linked_items = TRUE makes this operation expensive. -BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id, BOOL include_linked_items) const +BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id) const { - if (getWearableFromItemID(item_id) != NULL) return TRUE; - if (include_linked_items) - { - LLInventoryModel::item_array_t item_array; - gInventory.collectLinkedItems(item_id, item_array); - for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); - iter != item_array.end(); - iter++) - { - LLViewerInventoryItem *linked_item = (*iter); - const LLUUID &linked_item_id = linked_item->getUUID(); - if (getWearableFromItemID(linked_item_id) != NULL) return TRUE; - } - } - return FALSE; + return getWearableFromItemID(item_id) != NULL; } -// MULTI-WEARABLE: update for multiple -// static -// ! BACKWARDS COMPATIBILITY ! When we stop supporting viewer1.23, we can assume -// that viewers have a Current Outfit Folder and won't need this message, and thus -// we can remove/ignore this whole function. -void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgsys, void** user_data) -{ - // We should only receive this message a single time. Ignore subsequent AgentWearablesUpdates - // that may result from AgentWearablesRequest having been sent more than once. - if (mInitialWearablesUpdateReceived) - return; - mInitialWearablesUpdateReceived = true; - - LLUUID agent_id; - gMessageSystem->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); - - LLVOAvatar* avatar = gAgent.getAvatarObject(); - if (avatar && (agent_id == avatar->getID())) +void LLAgentWearables::addLocalTextureObject(const LLWearableType::EType wearable_type, const LLAvatarAppearanceDefines::ETextureIndex texture_type, U32 wearable_index) +{ + LLViewerWearable* wearable = getViewerWearable((LLWearableType::EType)wearable_type, wearable_index); + if (!wearable) { - gMessageSystem->getU32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, gAgentQueryManager.mUpdateSerialNum); - - S32 num_wearables = gMessageSystem->getNumberOfBlocksFast(_PREHASH_WearableData); - if (num_wearables < 4) - { - // Transitional state. Avatars should always have at least their body parts (hair, eyes, shape and skin). - // The fact that they don't have any here (only a dummy is sent) implies that this account existed - // before we had wearables, or that the database has gotten messed up. - return; - } - - // Get the UUID of the current outfit folder (will be created if it doesn't exist) - LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); - - LLInitialWearablesFetch* outfit = new LLInitialWearablesFetch(); - - //lldebugs << "processAgentInitialWearablesUpdate()" << llendl; - // Add wearables - // MULTI-WEARABLE: TODO: update once messages change. Currently use results to populate the zeroth element. - gAgentWearables.mItemsAwaitingWearableUpdate.clear(); - for (S32 i=0; i < num_wearables; i++) - { - // Parse initial wearables data from message system - U8 type_u8 = 0; - gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i); - if (type_u8 >= WT_COUNT) - { - continue; - } - const EWearableType type = (EWearableType) type_u8; - - LLUUID item_id; - gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_ItemID, item_id, i); - - LLUUID asset_id; - gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_AssetID, asset_id, i); - if (asset_id.isNull()) - { - LLWearable::removeFromAvatar(type, FALSE); - } - else - { - LLAssetType::EType asset_type = LLWearableDictionary::getAssetType(type); - if (asset_type == LLAssetType::AT_NONE) - { - continue; - } - - // MULTI-WEARABLE: TODO: update once messages change. Currently use results to populate the zeroth element. - - // Store initial wearables data until we know whether we have the current outfit folder or need to use the data. - LLInitialWearablesFetch::InitialWearableData wearable_data(type, item_id, asset_id); // MULTI-WEARABLE: update - outfit->mAgentInitialWearables.push_back(wearable_data); - - } - - lldebugs << " " << LLWearableDictionary::getTypeLabel(type) << 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. - LLInventoryFetchDescendentsObserver::folder_ref_t folders; - folders.push_back(current_outfit_id); - outfit->fetchDescendents(folders); - if(outfit->isEverythingComplete()) - { - // everything is already here - call done. - outfit->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); - } + LL_ERRS() << "Tried to add local texture object to invalid wearable with type " << wearable_type << " and index " << wearable_index << LL_ENDL; + return; } + LLLocalTextureObject lto; + wearable->setLocalTextureObject(texture_type, lto); } -// A single wearable that the avatar was wearing on start-up has arrived from the database. -// static -void LLAgentWearables::onInitialWearableAssetArrived(LLWearable* wearable, void* userdata) +class OnWearableItemCreatedCB: public LLInventoryCallback { - boost::scoped_ptr<LLInitialWearablesFetch::InitialWearableData> wear_data((LLInitialWearablesFetch::InitialWearableData*)userdata); - const EWearableType type = wear_data->mType; - U32 index = 0; - - LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); - if (!avatar) +public: + OnWearableItemCreatedCB(): + mWearablesAwaitingItems(LLWearableType::WT_COUNT,NULL) { - return; + LL_INFOS() << "created callback" << LL_ENDL; } - - if (wearable) + /* virtual */ void fire(const LLUUID& inv_item) { - llassert(type == wearable->getType()); - wearable->setItemID(wear_data->mItemID); - index = gAgentWearables.pushWearable(type, wearable); - gAgentWearables.mItemsAwaitingWearableUpdate.erase(wear_data->mItemID); - - // disable composites if initial textures are baked - avatar->setupComposites(); - - avatar->setCompositeUpdatesEnabled(TRUE); - gInventory.addChangedMask(LLInventoryObserver::LABEL, wearable->getItemID()); + LL_INFOS() << "One item created " << inv_item.asString() << LL_ENDL; + LLConstPointer<LLInventoryObject> item = gInventory.getItem(inv_item); + mItemsToLink.push_back(item); + updatePendingWearable(inv_item); } - else + ~OnWearableItemCreatedCB() { - // Somehow the asset doesn't exist in the database. - gAgentWearables.recoverMissingWearable(type,index); + LL_INFOS() << "All items created" << LL_ENDL; + LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy; + link_inventory_array(LLAppearanceMgr::instance().getCOF(), + mItemsToLink, + link_waiter); } - - gInventory.notifyObservers(); - - // Have all the wearables that the avatar was wearing at log-in arrived? - // MULTI-WEARABLE: update when multiple wearables can arrive per type. - - gAgentWearables.updateWearablesLoaded(); - if (gAgentWearables.areWearablesLoaded()) + void addPendingWearable(LLViewerWearable *wearable) { - - // Can't query cache until all wearables have arrived, so calling this earlier is a no-op. - gAgentWearables.queryWearableCache(); - - // Make sure that the server's idea of the avatar's wearables actually match the wearables. - gAgent.sendAgentSetAppearance(); - - // Check to see if there are any baked textures that we hadn't uploaded before we logged off last time. - // If there are any, schedule them to be uploaded as soon as the layer textures they depend on arrive. - if (gAgent.cameraCustomizeAvatar()) + if (!wearable) { - avatar->requestLayerSetUploads(); + LL_WARNS() << "no wearable" << LL_ENDL; + return; + } + LLWearableType::EType type = wearable->getType(); + if (type<LLWearableType::WT_COUNT) + { + mWearablesAwaitingItems[type] = wearable; + } + else + { + LL_WARNS() << "invalid type " << type << LL_ENDL; } } -} - -// Normally, all wearables referred to "AgentWearablesUpdate" will correspond to actual assets in the -// database. If for some reason, we can't load one of those assets, we can try to reconstruct it so that -// the user isn't left without a shape, for example. (We can do that only after the inventory has loaded.) -void LLAgentWearables::recoverMissingWearable(const EWearableType type, U32 index) -{ - // Try to recover by replacing missing wearable with a new one. - LLNotifications::instance().add("ReplacedMissingWearable"); - lldebugs << "Wearable " << LLWearableDictionary::getTypeLabel(type) << " could not be downloaded. Replaced inventory item with default wearable." << llendl; - LLWearable* new_wearable = LLWearableList::instance().createNewWearable(type); - - S32 type_s32 = (S32) type; - setWearable(type,index,new_wearable); - //new_wearable->writeToAvatar(TRUE); - - // Add a new one in the lost and found folder. - // (We used to overwrite the "not found" one, but that could potentially - // destory content.) JC - LLUUID lost_and_found_id = - gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); - LLPointer<LLInventoryCallback> cb = - new addWearableToAgentInventoryCallback( - LLPointer<LLRefCount>(NULL), - type_s32, - index, - new_wearable, - addWearableToAgentInventoryCallback::CALL_RECOVERDONE); - addWearableToAgentInventory(cb, new_wearable, lost_and_found_id, TRUE); -} - -void LLAgentWearables::recoverMissingWearableDone() -{ - // Have all the wearables that the avatar was wearing at log-in arrived or been fabricated? - updateWearablesLoaded(); - if (areWearablesLoaded()) - { - // Make sure that the server's idea of the avatar's wearables actually match the wearables. - gAgent.sendAgentSetAppearance(); - } - else - { - gInventory.addChangedMask(LLInventoryObserver::LABEL, LLUUID::null); - gInventory.notifyObservers(); - } -} - -void LLAgentWearables::addLocalTextureObject(const EWearableType wearable_type, const LLVOAvatarDefines::ETextureIndex texture_type, U32 wearable_index) -{ - LLWearable* wearable = getWearable((EWearableType)wearable_type, wearable_index); - if (!wearable) - { - llerrs << "Tried to add local texture object to invalid wearable with type " << wearable_type << " and index " << wearable_index << llendl; - } - LLLocalTextureObject* lto = new LLLocalTextureObject(); - wearable->setLocalTextureObject(texture_type, lto); -} - -void LLAgentWearables::createStandardWearables(BOOL female) -{ - llwarns << "Creating Standard " << (female ? "female" : "male") - << " Wearables" << llendl; - - if (mAvatarObject.isNull()) + void updatePendingWearable(const LLUUID& inv_item) { - return; - } - - mAvatarObject->setSex(female ? SEX_FEMALE : SEX_MALE); - - const BOOL create[WT_COUNT] = + LLViewerInventoryItem *item = gInventory.getItem(inv_item); + if (!item) { - TRUE, //WT_SHAPE - TRUE, //WT_SKIN - TRUE, //WT_HAIR - TRUE, //WT_EYES - TRUE, //WT_SHIRT - TRUE, //WT_PANTS - TRUE, //WT_SHOES - TRUE, //WT_SOCKS - FALSE, //WT_JACKET - FALSE, //WT_GLOVES - TRUE, //WT_UNDERSHIRT - TRUE, //WT_UNDERPANTS - FALSE //WT_SKIRT - }; - - for (S32 i=0; i < WT_COUNT; i++) - { - bool once = false; - LLPointer<LLRefCount> donecb = NULL; - if (create[i]) + LL_WARNS() << "no item found" << LL_ENDL; + return; + } + if (!item->isWearableType()) { - if (!once) + LL_WARNS() << "non-wearable item found" << LL_ENDL; + return; + } + if (item && item->isWearableType()) + { + LLWearableType::EType type = item->getWearableType(); + if (type < LLWearableType::WT_COUNT) { - once = true; - donecb = new createStandardWearablesAllDoneCallback; + LLViewerWearable *wearable = mWearablesAwaitingItems[type]; + if (wearable) + wearable->setItemID(inv_item); + } + else + { + LL_WARNS() << "invalid wearable type " << type << LL_ENDL; } - llassert(getWearableCount((EWearableType)i) == 0); - LLWearable* wearable = LLWearableList::instance().createNewWearable((EWearableType)i); - U32 index = pushWearable((EWearableType)i,wearable); - // no need to update here... - LLPointer<LLInventoryCallback> cb = - new addWearableToAgentInventoryCallback( - donecb, - i, - index, - wearable, - addWearableToAgentInventoryCallback::CALL_CREATESTANDARDDONE); - addWearableToAgentInventory(cb, wearable, LLUUID::null, FALSE); } } -} + +private: + LLInventoryObject::const_object_list_t mItemsToLink; + std::vector<LLViewerWearable*> mWearablesAwaitingItems; +}; -void LLAgentWearables::createStandardWearablesDone(S32 type, U32 index) +void LLAgentWearables::createStandardWearables() { - if (mAvatarObject) - { - mAvatarObject->updateVisualParams(); - } -} + LL_WARNS() << "Creating standard wearables" << LL_ENDL; -void LLAgentWearables::createStandardWearablesAllDone() -{ - // ... because sendAgentWearablesUpdate will notify inventory - // observers. - mWearablesLoaded = TRUE; - checkWearablesLoaded(); - - updateServer(); + if (!isAgentAvatarValid()) return; - // Treat this as the first texture entry message, if none received yet - mAvatarObject->onFirstTEMessageReceived(); -} + const BOOL create[LLWearableType::WT_COUNT] = + { + TRUE, //LLWearableType::WT_SHAPE + TRUE, //LLWearableType::WT_SKIN + TRUE, //LLWearableType::WT_HAIR + TRUE, //LLWearableType::WT_EYES + TRUE, //LLWearableType::WT_SHIRT + TRUE, //LLWearableType::WT_PANTS + TRUE, //LLWearableType::WT_SHOES + TRUE, //LLWearableType::WT_SOCKS + FALSE, //LLWearableType::WT_JACKET + FALSE, //LLWearableType::WT_GLOVES + TRUE, //LLWearableType::WT_UNDERSHIRT + TRUE, //LLWearableType::WT_UNDERPANTS + FALSE //LLWearableType::WT_SKIRT + }; -// MULTI-WEARABLE: Properly handle multiwearables later. -void LLAgentWearables::getAllWearablesArray(LLDynamicArray<S32>& wearables) -{ - for( S32 i = 0; i < WT_COUNT; ++i ) + LLPointer<LLInventoryCallback> cb = new OnWearableItemCreatedCB; + for (S32 i=0; i < LLWearableType::WT_COUNT; i++) { - if (getWearableCount( (EWearableType) i) != 0 ) + if (create[i]) { - wearables.push_back(i); + llassert(getWearableCount((LLWearableType::EType)i) == 0); + LLViewerWearable* wearable = LLWearableList::instance().createNewWearable((LLWearableType::EType)i, gAgentAvatarp); + ((OnWearableItemCreatedCB*)(&(*cb)))->addPendingWearable(wearable); + // no need to update here... + LLUUID category_id = LLUUID::null; + create_inventory_item(gAgent.getID(), + gAgent.getSessionID(), + category_id, + wearable->getTransactionID(), + wearable->getName(), + wearable->getDescription(), + wearable->getAssetType(), + LLInventoryType::IT_WEARABLE, + wearable->getType(), + wearable->getPermissions().getMaskNextOwner(), + cb); } } } -// Note: wearables_to_include should be a list of EWearableType types -// attachments_to_include should be a list of attachment points -void LLAgentWearables::makeNewOutfit(const std::string& new_folder_name, - const LLDynamicArray<S32>& wearables_to_include, - const LLDynamicArray<S32>& attachments_to_include, - BOOL rename_clothing) +// We no longer need this message in the current viewer, but send +// it for now to maintain compatibility with release viewers. Can +// remove this function once the SH-3455 changesets are universally deployed. +void LLAgentWearables::sendDummyAgentWearablesUpdate() { - if (mAvatarObject.isNull()) - { - return; - } - - // First, make a folder in the Clothes directory. - LLUUID folder_id = gInventory.createNewCategory( - gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING), - LLAssetType::AT_NONE, - new_folder_name); - - bool found_first_item = false; + LL_DEBUGS("Avatar") << "sendAgentWearablesUpdate()" << LL_ENDL; - /////////////////// - // Wearables - - if (wearables_to_include.count()) - { - // Then, iterate though each of the wearables and save copies of them in the folder. - S32 i; - S32 count = wearables_to_include.count(); - LLDynamicArray<LLUUID> delete_items; - LLPointer<LLRefCount> cbdone = NULL; - for (i = 0; i < count; ++i) - { - const S32 type = wearables_to_include[i]; - for (U32 j=0; j<getWearableCount((EWearableType)i); j++) - { - LLWearable* old_wearable = getWearable((EWearableType)type, j); - if (old_wearable) - { - std::string new_name; - LLWearable* new_wearable; - new_wearable = LLWearableList::instance().createCopy(old_wearable); - if (rename_clothing) - { - new_name = new_folder_name; - new_name.append(" "); - new_name.append(old_wearable->getTypeLabel()); - LLStringUtil::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN); - new_wearable->setName(new_name); - } - - LLViewerInventoryItem* item = gInventory.getItem(getWearableItemID((EWearableType)type,j)); - S32 todo = addWearableToAgentInventoryCallback::CALL_NONE; - if (!found_first_item) - { - found_first_item = true; - /* set the focus to the first item */ - todo |= addWearableToAgentInventoryCallback::CALL_MAKENEWOUTFITDONE; - /* send the agent wearables update when done */ - cbdone = new sendAgentWearablesUpdateCallback; - } - LLPointer<LLInventoryCallback> cb = - new addWearableToAgentInventoryCallback( - cbdone, - type, - j, - new_wearable, - todo); - if (isWearableCopyable((EWearableType)type, j)) - { - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - folder_id, - new_name, - cb); - } - else - { - move_inventory_item( - gAgent.getID(), - gAgent.getSessionID(), - item->getUUID(), - folder_id, - new_name, - cb); - } - } - } - } - gInventory.notifyObservers(); - } - - - /////////////////// - // Attachments - - if (attachments_to_include.count()) - { - BOOL msg_started = FALSE; - LLMessageSystem* msg = gMessageSystem; - for (S32 i = 0; i < attachments_to_include.count(); i++) - { - S32 attachment_pt = attachments_to_include[i]; - LLViewerJointAttachment* attachment = get_if_there(mAvatarObject->mAttachmentPoints, attachment_pt, (LLViewerJointAttachment*)NULL); - if (!attachment) continue; - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - LLViewerObject *attached_object = (*attachment_iter); - if(!attached_object) continue; - const LLUUID& item_id = (*attachment_iter)->getItemID(); - if(item_id.isNull()) continue; - LLInventoryItem* item = gInventory.getItem(item_id); - if(!item) continue; - if(!msg_started) - { - msg_started = TRUE; - msg->newMessage("CreateNewOutfitAttachments"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("HeaderData"); - msg->addUUID("NewFolderID", folder_id); - } - msg->nextBlock("ObjectData"); - msg->addUUID("OldItemID", item_id); - msg->addUUID("OldFolderID", item->getParentUUID()); - } - } - - if (msg_started) - { - gAgent.sendReliableMessage(); - } + // Send the AgentIsNowWearing + gMessageSystem->newMessageFast(_PREHASH_AgentIsNowWearing); + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } -} + // Send 4 standardized nonsense item ids (same as returned by the modified sim, not that it especially matters). + gMessageSystem->nextBlockFast(_PREHASH_WearableData); + gMessageSystem->addU8Fast(_PREHASH_WearableType, U8(1)); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID("db5a4e5f-9da3-44c8-992d-1181c5795498")); -LLUUID LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name) -{ - if (mAvatarObject.isNull()) - { - return LLUUID::null; - } + gMessageSystem->nextBlockFast(_PREHASH_WearableData); + gMessageSystem->addU8Fast(_PREHASH_WearableType, U8(2)); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID("6969c7cc-f72f-4a76-a19b-c293cce8ce4f")); - // First, make a folder in the My Outfits directory. - LLUUID parent_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_MY_OUTFITS); - LLUUID folder_id = gInventory.createNewCategory( - parent_id, - LLAssetType::AT_OUTFIT, - new_folder_name); + gMessageSystem->nextBlockFast(_PREHASH_WearableData); + gMessageSystem->addU8Fast(_PREHASH_WearableType, U8(3)); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID("7999702b-b291-48f9-8903-c91dfb828408")); - LLAppearanceManager::shallowCopyCategory(LLAppearanceManager::getCOF(),folder_id, NULL); - -#if 0 // BAP - fix to go into rename state automatically after outfit is created. - LLViewerInventoryCategory *parent_category = gInventory.getCategory(parent_id); - if (parent_category) - { - parent_category->setSelectionByID(folder_id,TRUE); - parent_category->setNeedsAutoRename(TRUE); - } -#endif + gMessageSystem->nextBlockFast(_PREHASH_WearableData); + gMessageSystem->addU8Fast(_PREHASH_WearableType, U8(4)); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID("566cb59e-ef60-41d7-bfa6-e0f293fbea40")); - return folder_id; + gAgent.sendReliableMessage(); } void LLAgentWearables::makeNewOutfitDone(S32 type, U32 index) { - LLUUID first_item_id = getWearableItemID((EWearableType)type, index); + LLUUID first_item_id = getWearableItemID((LLWearableType::EType)type, index); // Open the inventory and select the first item we added. if (first_item_id.notNull()) { - LLFloaterInventory* view = LLFloaterInventory::getActiveInventory(); - if (view) + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (active_panel) { - view->getPanel()->setSelection(first_item_id, TAKE_FOCUS_NO); + active_panel->setSelection(first_item_id, TAKE_FOCUS_NO); } } } void LLAgentWearables::addWearableToAgentInventory(LLPointer<LLInventoryCallback> cb, - LLWearable* wearable, + LLViewerWearable* wearable, const LLUUID& category_id, BOOL notify) { @@ -1298,15 +870,20 @@ void LLAgentWearables::addWearableToAgentInventory(LLPointer<LLInventoryCallback cb); } -void LLAgentWearables::removeWearable(const EWearableType type, bool do_remove_all, U32 index) +void LLAgentWearables::removeWearable(const LLWearableType::EType type, bool do_remove_all, U32 index) { - if ((gAgent.isTeen()) - && (type == WT_UNDERSHIRT || type == WT_UNDERPANTS)) + if (gAgent.isTeen() && + (type == LLWearableType::WT_UNDERSHIRT || type == LLWearableType::WT_UNDERPANTS)) { // Can't take off underclothing in simple UI mode or on PG accounts // TODO: enable the removing of a single undershirt/underpants if multiple are worn. - Nyx return; } + if (getWearableCount(type) == 0) + { + // no wearables to remove + return; + } if (do_remove_all) { @@ -1314,7 +891,7 @@ void LLAgentWearables::removeWearable(const EWearableType type, bool do_remove_a } else { - LLWearable* old_wearable = getWearable(type,index); + LLViewerWearable* old_wearable = getViewerWearable(type,index); if (old_wearable) { @@ -1322,8 +899,9 @@ void LLAgentWearables::removeWearable(const EWearableType type, bool do_remove_a { LLSD payload; payload["wearable_type"] = (S32)type; + payload["wearable_index"] = (S32)index; // Bring up view-modal dialog: Save changes? Yes, No, Cancel - LLNotifications::instance().add("WearableSave", LLSD(), payload, &LLAgentWearables::onRemoveWearableDialog); + LLNotificationsUtil::add("WearableSave", LLSD(), payload, &LLAgentWearables::onRemoveWearableDialog); return; } else @@ -1335,22 +913,21 @@ void LLAgentWearables::removeWearable(const EWearableType type, bool do_remove_a } -// MULTI_WEARABLE: assuming one wearable per type. -// MULTI_WEARABLE: hardwiring 0th elt for now - notification needs to change. // static bool LLAgentWearables::onRemoveWearableDialog(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); - EWearableType type = (EWearableType)notification["payload"]["wearable_type"].asInteger(); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + LLWearableType::EType type = (LLWearableType::EType)notification["payload"]["wearable_type"].asInteger(); + S32 index = (S32)notification["payload"]["wearable_index"].asInteger(); switch(option) { case 0: // "Save" - gAgentWearables.saveWearable(type, 0); - gAgentWearables.removeWearableFinal(type, false, 0); + gAgentWearables.saveWearable(type, index); + gAgentWearables.removeWearableFinal(type, false, index); break; case 1: // "Don't Save" - gAgentWearables.removeWearableFinal(type, false, 0); + gAgentWearables.removeWearableFinal(type, false, index); break; case 2: // "Cancel" @@ -1364,182 +941,209 @@ bool LLAgentWearables::onRemoveWearableDialog(const LLSD& notification, const LL } // Called by removeWearable() and onRemoveWearableDialog() to actually do the removal. -void LLAgentWearables::removeWearableFinal(const EWearableType type, bool do_remove_all, U32 index) +void LLAgentWearables::removeWearableFinal(const LLWearableType::EType type, bool do_remove_all, U32 index) { //LLAgentDumper dumper("removeWearable"); if (do_remove_all) { - S32 max_entry = mWearableDatas[type].size()-1; + S32 max_entry = getWearableCount(type)-1; for (S32 i=max_entry; i>=0; i--) { - LLWearable* old_wearable = getWearable(type,i); - gInventory.addChangedMask(LLInventoryObserver::LABEL, getWearableItemID(type,i)); - popWearable(type,i); - - //queryWearableCache(); // moved below + LLViewerWearable* old_wearable = getViewerWearable(type,i); if (old_wearable) { - old_wearable->removeFromAvatar(TRUE); + eraseWearable(old_wearable); + old_wearable->removeFromAvatar(); } } - mWearableDatas[type].clear(); + clearWearableType(type); } else { - LLWearable* old_wearable = getWearable(type, index); - - gInventory.addChangedMask(LLInventoryObserver::LABEL, getWearableItemID(type,index)); - popWearable(type, index); - - //queryWearableCache(); // moved below + LLViewerWearable* old_wearable = getViewerWearable(type, index); if (old_wearable) { - old_wearable->removeFromAvatar(TRUE); + eraseWearable(old_wearable); + old_wearable->removeFromAvatar(); } } - queryWearableCache(); - - // Update the server - updateServer(); gInventory.notifyObservers(); } // Assumes existing wearables are not dirty. -// MULTI_WEARABLE: assumes one wearable per type. void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& items, - const LLDynamicArray< LLWearable* >& wearables, - BOOL remove) -{ - lldebugs << "setWearableOutfit() start" << llendl; - - BOOL wearables_to_remove[WT_COUNT]; - wearables_to_remove[WT_SHAPE] = FALSE; - wearables_to_remove[WT_SKIN] = FALSE; - wearables_to_remove[WT_HAIR] = FALSE; - wearables_to_remove[WT_EYES] = FALSE; - wearables_to_remove[WT_SHIRT] = remove; - wearables_to_remove[WT_PANTS] = remove; - wearables_to_remove[WT_SHOES] = remove; - wearables_to_remove[WT_SOCKS] = remove; - wearables_to_remove[WT_JACKET] = remove; - wearables_to_remove[WT_GLOVES] = remove; - wearables_to_remove[WT_UNDERSHIRT] = (!gAgent.isTeen()) & remove; - wearables_to_remove[WT_UNDERPANTS] = (!gAgent.isTeen()) & remove; - wearables_to_remove[WT_SKIRT] = remove; - - S32 count = wearables.count(); - llassert(items.count() == count); - - S32 i; - for (i = 0; i < count; i++) - { - LLWearable* new_wearable = wearables[i]; - LLPointer<LLInventoryItem> new_item = items[i]; + const std::vector< LLViewerWearable* >& wearables) +{ + LL_INFOS() << "setWearableOutfit() start" << LL_ENDL; - const EWearableType type = new_wearable->getType(); - wearables_to_remove[type] = FALSE; + S32 count = wearables.size(); + llassert(items.size() == count); - // MULTI_WEARABLE: using 0th - LLWearable* old_wearable = getWearable(type, 0); - if (old_wearable) - { - const LLUUID& old_item_id = getWearableItemID(type, 0); - if ((old_wearable->getAssetID() == new_wearable->getAssetID()) && - (old_item_id == new_item->getUUID())) - { - lldebugs << "No change to wearable asset and item: " << LLWearableDictionary::getInstance()->getWearableEntry(type) << llendl; - continue; - } + // Check for whether outfit already matches the one requested + S32 matched = 0, mismatched = 0; + const S32 arr_size = LLWearableType::WT_COUNT; + S32 type_counts[arr_size]; + std::fill(type_counts,type_counts+arr_size,0); + for (S32 i = 0; i < count; i++) + { + LLViewerWearable* new_wearable = wearables[i]; + LLPointer<LLInventoryItem> new_item = items[i]; - gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id); + const LLWearableType::EType type = new_wearable->getType(); + if (type < 0 || type>=LLWearableType::WT_COUNT) + { + LL_WARNS() << "invalid type " << type << LL_ENDL; + mismatched++; + continue; + } + S32 index = type_counts[type]; + type_counts[type]++; - // Assumes existing wearables are not dirty. - if (old_wearable->isDirty()) - { - llassert(0); - continue; - } + LLViewerWearable *curr_wearable = dynamic_cast<LLViewerWearable*>(getWearable(type,index)); + if (!new_wearable || !curr_wearable || + new_wearable->getAssetID() != curr_wearable->getAssetID()) + { + LL_DEBUGS("Avatar") << "mismatch, type " << type << " index " << index + << " names " << (curr_wearable ? curr_wearable->getName() : "NONE") << "," + << " names " << (new_wearable ? new_wearable->getName() : "NONE") << LL_ENDL; + mismatched++; + continue; } - setWearable(type,0,new_wearable); - if (new_wearable) - new_wearable->setItemID(new_item->getUUID()); + // Don't care about this case - ordering of wearables with the same asset id has no effect. + // Causes the two-alphas error case in MAINT-4158. + // We should actually disallow wearing two wearables with the same asset id. +#if 0 + if (curr_wearable->getName() != new_item->getName() || + curr_wearable->getItemID() != new_item->getUUID()) + { + LL_DEBUGS("Avatar") << "mismatch on name or inventory id, names " + << curr_wearable->getName() << " vs " << new_item->getName() + << " item ids " << curr_wearable->getItemID() << " vs " << new_item->getUUID() + << LL_ENDL; + mismatched++; + continue; + } +#endif + // If we got here, everything matches. + matched++; + } + LL_DEBUGS("Avatar") << "matched " << matched << " mismatched " << mismatched << LL_ENDL; + for (S32 j=0; j<LLWearableType::WT_COUNT; j++) + { + LLWearableType::EType type = (LLWearableType::EType) j; + if (getWearableCount(type) != type_counts[j]) + { + LL_DEBUGS("Avatar") << "count mismatch for type " << j << " current " << getWearableCount(j) << " requested " << type_counts[j] << LL_ENDL; + mismatched++; + } + } + if (mismatched == 0) + { + LL_DEBUGS("Avatar") << "no changes, bailing out" << LL_ENDL; + mCOFChangeInProgress = false; + return; + } + + + // TODO: Removed check for ensuring that teens don't remove undershirt and underwear. Handle later + // note: shirt is the first non-body part wearable item. Update if wearable order changes. + // This loop should remove all clothing, but not any body parts + for (S32 j = 0; j < (S32)LLWearableType::WT_COUNT; j++) + { + if (LLWearableType::getAssetType((LLWearableType::EType)j) == LLAssetType::AT_CLOTHING) + { + removeWearable((LLWearableType::EType)j, true, 0); + } } - std::vector<LLWearable*> wearables_being_removed; - - for (i = 0; i < WT_COUNT; i++) + for (S32 i = 0; i < count; i++) { - if (wearables_to_remove[i]) + LLViewerWearable* new_wearable = wearables[i]; + LLPointer<LLInventoryItem> new_item = items[i]; + + llassert(new_wearable); + if (new_wearable) { - // MULTI_WEARABLE: assuming 0th - LLWearable* wearable = getWearable((EWearableType)i, 0); - gInventory.addChangedMask(LLInventoryObserver::LABEL, getWearableItemID((EWearableType)i,0)); - if (wearable) + const LLWearableType::EType type = new_wearable->getType(); + + new_wearable->setName(new_item->getName()); + new_wearable->setItemID(new_item->getUUID()); + + if (LLWearableType::getAssetType(type) == LLAssetType::AT_BODYPART) + { + // exactly one wearable per body part + setWearable(type,0,new_wearable); + } + else { - wearables_being_removed.push_back(wearable); + pushWearable(type,new_wearable); } - removeWearable((EWearableType)i,true,0); + const BOOL removed = FALSE; + wearableUpdated(new_wearable, removed); } } gInventory.notifyObservers(); - queryWearableCache(); - - std::vector<LLWearable*>::iterator wearable_iter; - - for (wearable_iter = wearables_being_removed.begin(); - wearable_iter != wearables_being_removed.end(); - ++wearable_iter) + if (isAgentAvatarValid()) { - LLWearable* wearablep = *wearable_iter; - if (wearablep) + gAgentAvatarp->setCompositeUpdatesEnabled(TRUE); + + // If we have not yet declouded, we may want to use + // baked texture UUIDs sent from the first objectUpdate message + // don't overwrite these. If we have already declouded, we've saved + // these ids as the last known good textures and can invalidate without + // re-clouding. + if (!gAgentAvatarp->getIsCloud()) { - wearablep->removeFromAvatar(TRUE); + gAgentAvatarp->invalidateAll(); } } - if (mAvatarObject) - { - mAvatarObject->updateVisualParams(); - } - // Start rendering & update the server mWearablesLoaded = TRUE; - checkWearablesLoaded(); - updateServer(); - lldebugs << "setWearableOutfit() end" << llendl; + notifyLoadingFinished(); + + // Copy wearable params to avatar. + gAgentAvatarp->writeWearablesToAvatar(); + + // Then update the avatar based on the copied params. + gAgentAvatarp->updateVisualParams(); + + gAgentAvatarp->dumpAvatarTEs("setWearableOutfit"); + + LL_DEBUGS("Avatar") << "setWearableOutfit() end" << LL_ENDL; } // User has picked "wear on avatar" from a menu. -void LLAgentWearables::setWearableItem(LLInventoryItem* new_item, LLWearable* new_wearable, bool do_append) +void LLAgentWearables::setWearableItem(LLInventoryItem* new_item, LLViewerWearable* new_wearable, bool do_append) { //LLAgentDumper dumper("setWearableItem"); if (isWearingItem(new_item->getUUID())) { - llwarns << "wearable " << new_item->getUUID() << " is already worn" << llendl; + LL_WARNS() << "wearable " << new_item->getUUID() << " is already worn" << LL_ENDL; return; } - const EWearableType type = new_wearable->getType(); + const LLWearableType::EType type = new_wearable->getType(); if (!do_append) { // Remove old wearable, if any // MULTI_WEARABLE: hardwired to 0 - LLWearable* old_wearable = getWearable(type,0); + LLViewerWearable* old_wearable = getViewerWearable(type,0); if (old_wearable) { const LLUUID& old_item_id = old_wearable->getItemID(); if ((old_wearable->getAssetID() == new_wearable->getAssetID()) && (old_item_id == new_item->getUUID())) { - lldebugs << "No change to wearable asset and item: " << LLWearableDictionary::getInstance()->getWearableEntry(type) << llendl; + LL_DEBUGS() << "No change to wearable asset and item: " << LLWearableType::getTypeName(type) << LL_ENDL; return; } @@ -1548,7 +1152,7 @@ void LLAgentWearables::setWearableItem(LLInventoryItem* new_item, LLWearable* ne // Bring up modal dialog: Save changes? Yes, No, Cancel LLSD payload; payload["item_id"] = new_item->getUUID(); - LLNotifications::instance().add("WearableSave", LLSD(), payload, boost::bind(onSetWearableDialog, _1, _2, new_wearable)); + LLNotificationsUtil::add("WearableSave", LLSD(), payload, boost::bind(onSetWearableDialog, _1, _2, new_wearable)); return; } } @@ -1558,10 +1162,17 @@ void LLAgentWearables::setWearableItem(LLInventoryItem* new_item, LLWearable* ne } // static -bool LLAgentWearables::onSetWearableDialog(const LLSD& notification, const LLSD& response, LLWearable* wearable) +bool LLAgentWearables::onSetWearableDialog(const LLSD& notification, const LLSD& response, LLViewerWearable* wearable) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLInventoryItem* new_item = gInventory.getItem(notification["payload"]["item_id"].asUUID()); + U32 index; + if (!gAgentWearables.getWearableIndex(wearable,index)) + { + LL_WARNS() << "Wearable not found" << LL_ENDL; + delete wearable; + return false; + } if (!new_item) { delete wearable; @@ -1571,8 +1182,7 @@ bool LLAgentWearables::onSetWearableDialog(const LLSD& notification, const LLSD& switch(option) { case 0: // "Save" -// MULTI_WEARABLE: assuming 0th - gAgentWearables.saveWearable(wearable->getType(),0); + gAgentWearables.saveWearable(wearable->getType(),index); gAgentWearables.setWearableFinal(new_item, wearable); break; @@ -1594,23 +1204,25 @@ bool LLAgentWearables::onSetWearableDialog(const LLSD& notification, const LLSD& // Called from setWearableItem() and onSetWearableDialog() to actually set the wearable. // MULTI_WEARABLE: unify code after null objects are gone. -void LLAgentWearables::setWearableFinal(LLInventoryItem* new_item, LLWearable* new_wearable, bool do_append) +void LLAgentWearables::setWearableFinal(LLInventoryItem* new_item, LLViewerWearable* new_wearable, bool do_append) { - const EWearableType type = new_wearable->getType(); + const LLWearableType::EType type = new_wearable->getType(); if (do_append && getWearableItemID(type,0).notNull()) { new_wearable->setItemID(new_item->getUUID()); - mWearableDatas[type].push_back(new_wearable); - llinfos << "Added additional wearable for type " << type - << " size is now " << mWearableDatas[type].size() << llendl; + const bool trigger_updated = false; + pushWearable(type, new_wearable, trigger_updated); + LL_INFOS() << "Added additional wearable for type " << type + << " size is now " << getWearableCount(type) << LL_ENDL; + checkWearableAgainstInventory(new_wearable); } else { // Replace the old wearable with a new one. llassert(new_item->getAssetUUID() == new_wearable->getAssetID()); - LLWearable *old_wearable = getWearable(type,0); + LLViewerWearable *old_wearable = getViewerWearable(type,0); LLUUID old_item_id; if (old_wearable) { @@ -1624,150 +1236,58 @@ void LLAgentWearables::setWearableFinal(LLInventoryItem* new_item, LLWearable* n gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id); gInventory.notifyObservers(); } - llinfos << "Replaced current element 0 for type " << type - << " size is now " << mWearableDatas[type].size() << llendl; + LL_INFOS() << "Replaced current element 0 for type " << type + << " size is now " << getWearableCount(type) << LL_ENDL; } - - //llinfos << "LLVOAvatar::setWearableItem()" << llendl; - queryWearableCache(); - //new_wearable->writeToAvatar(TRUE); - - updateServer(); } -void LLAgentWearables::queryWearableCache() -{ - if (!areWearablesLoaded()) - { - return; - } - - // Look up affected baked textures. - // If they exist: - // disallow updates for affected layersets (until dataserver responds with cache request.) - // If cache miss, turn updates back on and invalidate composite. - // If cache hit, modify baked texture entries. - // - // Cache requests contain list of hashes for each baked texture entry. - // Response is list of valid baked texture assets. (same message) - - gMessageSystem->newMessageFast(_PREHASH_AgentCachedTexture); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->addS32Fast(_PREHASH_SerialNum, gAgentQueryManager.mWearablesCacheQueryID); - - S32 num_queries = 0; - for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) - { - const LLVOAvatarDictionary::BakedEntry *baked_dict = LLVOAvatarDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index); - LLUUID hash; - for (U8 i=0; i < baked_dict->mWearables.size(); i++) - { - const EWearableType baked_type = baked_dict->mWearables[i]; - // MULTI_WEARABLE: not order-dependent - const U32 num_wearables = getWearableCount(baked_type); - for (U32 index = 0; index < num_wearables; ++index) - { - const LLWearable* wearable = getWearable(baked_type,index); - if (wearable) - { - hash ^= wearable->getAssetID(); - } - } - } - if (hash.notNull()) - { - hash ^= baked_dict->mWearablesHashID; - num_queries++; - // *NOTE: make sure at least one request gets packed - - //llinfos << "Requesting texture for hash " << hash << " in baked texture slot " << baked_index << llendl; - gMessageSystem->nextBlockFast(_PREHASH_WearableData); - gMessageSystem->addUUIDFast(_PREHASH_ID, hash); - gMessageSystem->addU8Fast(_PREHASH_TextureIndex, (U8)baked_index); - } - - gAgentQueryManager.mActiveCacheQueries[baked_index] = gAgentQueryManager.mWearablesCacheQueryID; - } - - llinfos << "Requesting texture cache entry for " << num_queries << " baked textures" << llendl; - gMessageSystem->sendReliable(gAgent.getRegion()->getHost()); - gAgentQueryManager.mNumPendingQueries++; - gAgentQueryManager.mWearablesCacheQueryID++; -} - -// MULTI_WEARABLE: need a way to specify by wearable rather than by type. // User has picked "remove from avatar" from a menu. // static -void LLAgentWearables::userRemoveWearable(void* userdata) +void LLAgentWearables::userRemoveWearable(const LLWearableType::EType &type, const U32 &index) { - EWearableType type = (EWearableType)(intptr_t)userdata; - - if (!(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR)) //&& - //!((!gAgent.isTeen()) && (type==WT_UNDERPANTS || type==WT_UNDERSHIRT))) + if (!(type==LLWearableType::WT_SHAPE || type==LLWearableType::WT_SKIN || type==LLWearableType::WT_HAIR || type==LLWearableType::WT_EYES)) //&& + //!((!gAgent.isTeen()) && (type==LLWearableType::WT_UNDERPANTS || type==LLWearableType::WT_UNDERSHIRT))) { - // MULTI_WEARABLE: fixed to 0th for now. - gAgentWearables.removeWearable(type,false,0); + gAgentWearables.removeWearable(type,false,index); } } -// static -void LLAgentWearables::userRemoveAllClothes(void* userdata) +//static +void LLAgentWearables::userRemoveWearablesOfType(const LLWearableType::EType &type) { - // We have to do this up front to avoid having to deal with the case of multiple wearables being dirty. - if (gFloaterCustomize) - { - gFloaterCustomize->askToSaveIfDirty(userRemoveAllClothesStep2); - } - else + if (!(type==LLWearableType::WT_SHAPE || type==LLWearableType::WT_SKIN || type==LLWearableType::WT_HAIR || type==LLWearableType::WT_EYES)) //&& + //!((!gAgent.isTeen()) && (type==LLWearableType::WT_UNDERPANTS || type==LLWearableType::WT_UNDERSHIRT))) { - userRemoveAllClothesStep2(TRUE); + gAgentWearables.removeWearable(type,true,0); } } -// static -void LLAgentWearables::userRemoveAllClothesStep2(BOOL proceed) -{ - if (proceed) - { - gAgentWearables.removeWearable(WT_SHIRT,true,0); - gAgentWearables.removeWearable(WT_PANTS,true,0); - gAgentWearables.removeWearable(WT_SHOES,true,0); - gAgentWearables.removeWearable(WT_SOCKS,true,0); - gAgentWearables.removeWearable(WT_JACKET,true,0); - gAgentWearables.removeWearable(WT_GLOVES,true,0); - gAgentWearables.removeWearable(WT_UNDERSHIRT,true,0); - gAgentWearables.removeWearable(WT_UNDERPANTS,true,0); - gAgentWearables.removeWearable(WT_SKIRT,true,0); - } -} - -// Combines userRemoveAllAttachments() and userAttachMultipleAttachments() logic to -// get attachments into desired state with minimal number of adds/removes. -void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array) +// Given a desired set of attachments, find what objects need to be +// removed, and what additional inventory items need to be added. +void LLAgentWearables::findAttachmentsAddRemoveInfo(LLInventoryModel::item_array_t& obj_item_array, + llvo_vec_t& objects_to_remove, + llvo_vec_t& objects_to_retain, + LLInventoryModel::item_array_t& items_to_add) { // Possible cases: // already wearing but not in request set -> take off. // already wearing and in request set -> leave alone. // not wearing and in request set -> put on. - LLVOAvatar* avatarp = gAgent.getAvatarObject(); - if (!avatarp) - { - llwarns << "No avatar found." << llendl; - return; - } + if (!isAgentAvatarValid()) return; std::set<LLUUID> requested_item_ids; std::set<LLUUID> current_item_ids; - for (S32 i=0; i<obj_item_array.count(); i++) - requested_item_ids.insert(obj_item_array[i].get()->getLinkedUUID()); + for (S32 i=0; i<obj_item_array.size(); i++) + { + const LLUUID & requested_id = obj_item_array[i].get()->getLinkedUUID(); + //LL_INFOS() << "Requested attachment id " << requested_id << LL_ENDL; + requested_item_ids.insert(requested_id); + } // Build up list of objects to be removed and items currently attached. - llvo_vec_t objects_to_remove; - for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin(); - iter != avatarp->mAttachmentPoints.end();) + for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end();) { LLVOAvatar::attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; @@ -1778,23 +1298,34 @@ void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj LLViewerObject *objectp = (*attachment_iter); if (objectp) { - LLUUID object_item_id = objectp->getItemID(); + LLUUID object_item_id = objectp->getAttachmentItemID(); + + bool remove_attachment = true; if (requested_item_ids.find(object_item_id) != requested_item_ids.end()) - { - // Object currently worn, was requested. + { // Object currently worn, was requested to keep it // Flag as currently worn so we won't have to add it again. - current_item_ids.insert(object_item_id); + remove_attachment = false; } - else + else if (objectp->isTempAttachment()) + { // Check if we should keep this temp attachment + remove_attachment = LLAppearanceMgr::instance().shouldRemoveTempAttachment(objectp->getID()); + } + + if (remove_attachment) { - // object currently worn, not requested. + // LL_INFOS() << "found object to remove, id " << objectp->getID() << ", item " << objectp->getAttachmentItemID() << LL_ENDL; objects_to_remove.push_back(objectp); } + else + { + // LL_INFOS() << "found object to keep, id " << objectp->getID() << ", item " << objectp->getAttachmentItemID() << LL_ENDL; + current_item_ids.insert(object_item_id); + objects_to_retain.push_back(objectp); + } } } } - LLInventoryModel::item_array_t items_to_add; for (LLInventoryModel::item_array_t::iterator it = obj_item_array.begin(); it != obj_item_array.end(); ++it) @@ -1812,23 +1343,12 @@ void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj } // S32 remove_count = objects_to_remove.size(); // S32 add_count = items_to_add.size(); - // llinfos << "remove " << remove_count << " add " << add_count << llendl; - - // Remove everything in objects_to_remove - userRemoveMultipleAttachments(objects_to_remove); - - // Add everything in items_to_add - userAttachMultipleAttachments(items_to_add); + // LL_INFOS() << "remove " << remove_count << " add " << add_count << LL_ENDL; } void LLAgentWearables::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remove) { - LLVOAvatar* avatarp = gAgent.getAvatarObject(); - if (!avatarp) - { - llwarns << "No avatar found." << llendl; - return; - } + if (!isAgentAvatarValid()) return; if (objects_to_remove.empty()) return; @@ -1843,46 +1363,17 @@ void LLAgentWearables::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remo ++it) { LLViewerObject *objectp = *it; + //gAgentAvatarp->resetJointPositionsOnDetach(objectp); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, objectp->getLocalID()); } gMessageSystem->sendReliable(gAgent.getRegionHost()); } -void LLAgentWearables::userRemoveAllAttachments() -{ - LLVOAvatar* avatarp = gAgent.getAvatarObject(); - if (!avatarp) - { - llwarns << "No avatar found." << llendl; - return; - } - - llvo_vec_t objects_to_remove; - - for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin(); - iter != avatarp->mAttachmentPoints.end();) - { - LLVOAvatar::attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - LLViewerObject *attached_object = (*attachment_iter); - if (attached_object) - { - objects_to_remove.push_back(attached_object); - } - } - } - userRemoveMultipleAttachments(objects_to_remove); -} - void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_array_t& obj_item_array) { // Build a compound message to send all the objects that need to be rezzed. - S32 obj_count = obj_item_array.count(); + S32 obj_count = obj_item_array.size(); // Limit number of packets to send const S32 MAX_PACKETS_TO_SEND = 10; @@ -1913,11 +1404,11 @@ void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_arra msg->addBOOLFast(_PREHASH_FirstDetachAll, false ); } - const LLInventoryItem* item = obj_item_array.get(i).get(); + const LLInventoryItem* item = obj_item_array.at(i).get(); msg->nextBlockFast(_PREHASH_ObjectData ); msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID()); msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner()); - msg->addU8Fast(_PREHASH_AttachmentPt, 0 ); // Wear at the previous or default attachment point + msg->addU8Fast(_PREHASH_AttachmentPt, 0 | ATTACHMENT_ADD); // Wear at the previous or default attachment point pack_permissions_slam(msg, item->getFlags(), item->getPermissions()); msg->addStringFast(_PREHASH_Name, item->getName()); msg->addStringFast(_PREHASH_Description, item->getDescription()); @@ -1930,102 +1421,206 @@ void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_arra } } -void LLAgentWearables::checkWearablesLoaded() const +// Returns false if the given wearable is already topmost/bottommost +// (depending on closer_to_body parameter). +bool LLAgentWearables::canMoveWearable(const LLUUID& item_id, bool closer_to_body) const { -#ifdef SHOW_ASSERT - U32 item_pend_count = itemUpdatePendingCount(); - if (mWearablesLoaded) - { - llassert(item_pend_count==0); - } -#endif + const LLWearable* wearable = getWearableFromItemID(item_id); + if (!wearable) return false; + + LLWearableType::EType wtype = wearable->getType(); + const LLWearable* marginal_wearable = closer_to_body ? getBottomWearable(wtype) : getTopWearable(wtype); + if (!marginal_wearable) return false; + + return wearable != marginal_wearable; } BOOL LLAgentWearables::areWearablesLoaded() const { - checkWearablesLoaded(); return mWearablesLoaded; } -// MULTI-WEARABLE: update for multiple indices. -void LLAgentWearables::updateWearablesLoaded() -{ - mWearablesLoaded = (itemUpdatePendingCount()==0); -} - -bool LLAgentWearables::canWearableBeRemoved(const LLWearable* wearable) const +bool LLAgentWearables::canWearableBeRemoved(const LLViewerWearable* wearable) const { if (!wearable) return false; - EWearableType type = wearable->getType(); + LLWearableType::EType type = wearable->getType(); // Make sure the user always has at least one shape, skin, eyes, and hair type currently worn. - return !(((type == WT_SHAPE) || (type == WT_SKIN) || (type == WT_HAIR) || (type == WT_EYES)) + return !(((type == LLWearableType::WT_SHAPE) || (type == LLWearableType::WT_SKIN) || (type == LLWearableType::WT_HAIR) || (type == LLWearableType::WT_EYES)) && (getWearableCount(type) <= 1) ); } +void LLAgentWearables::animateAllWearableParams(F32 delta) +{ + for( S32 type = 0; type < LLWearableType::WT_COUNT; ++type ) + { + for (S32 count = 0; count < (S32)getWearableCount((LLWearableType::EType)type); ++count) + { + LLViewerWearable *wearable = getViewerWearable((LLWearableType::EType)type,count); + llassert(wearable); + if (wearable) + { + wearable->animateParams(delta); + } + } + } +} -void LLAgentWearables::updateServer() +bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool closer_to_body) { - sendAgentWearablesUpdate(); - gAgent.sendAgentSetAppearance(); + if (!item) return false; + if (!item->isWearableType()) return false; + + LLWearableType::EType type = item->getWearableType(); + U32 wearable_count = getWearableCount(type); + if (0 == wearable_count) return false; + + const LLUUID& asset_id = item->getAssetUUID(); + + //nowhere to move if the wearable is already on any boundary (closest to the body/furthest from the body) + if (closer_to_body) + { + LLViewerWearable* bottom_wearable = dynamic_cast<LLViewerWearable*>( getBottomWearable(type) ); + if (bottom_wearable->getAssetID() == asset_id) + { + return false; + } + } + else // !closer_to_body + { + LLViewerWearable* top_wearable = dynamic_cast<LLViewerWearable*>( getTopWearable(type) ); + if (top_wearable->getAssetID() == asset_id) + { + return false; + } + } + + for (U32 i = 0; i < wearable_count; ++i) + { + LLViewerWearable* wearable = getViewerWearable(type, i); + if (!wearable) continue; + if (wearable->getAssetID() != asset_id) continue; + + //swapping wearables + U32 swap_i = closer_to_body ? i-1 : i+1; + swapWearables(type, i, swap_i); + return true; + } + + return false; } -void LLInitialWearablesFetch::done() +// static +void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id) { - // No longer need this observer hanging around. - gInventory.removeObserver(this); + if (type == LLWearableType::WT_INVALID || type == LLWearableType::WT_NONE) return; - // Fetch the wearable items from the Current Outfit Folder - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t wearable_array; - LLFindWearables is_wearable; - gInventory.collectDescendentsIf(mCompleteFolders.front(), cat_array, wearable_array, - LLInventoryModel::EXCLUDE_TRASH, is_wearable); - - if (wearable_array.count() > 0) + LLViewerWearable* wearable = LLWearableList::instance().createNewWearable(type, gAgentAvatarp); + LLAssetType::EType asset_type = wearable->getAssetType(); + LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; + LLPointer<LLInventoryCallback> cb; + if(wear) + { + cb = new LLBoostFuncInventoryCallback(wear_and_edit_cb); + } + else + { + cb = new LLBoostFuncInventoryCallback(wear_cb); + } + + LLUUID folder_id; + + if (parent_id.notNull()) { - LLAppearanceManager::instance().updateAppearanceFromCOF(); + folder_id = parent_id; } else { - processWearablesMessage(); + LLFolderType::EType folder_type = LLFolderType::assetTypeToFolderType(asset_type); + folder_id = gInventory.findCategoryUUIDForType(folder_type); } - delete this; + + create_inventory_item(gAgent.getID(), + gAgent.getSessionID(), + folder_id, + wearable->getTransactionID(), + wearable->getName(), + wearable->getDescription(), + asset_type, inv_type, + wearable->getType(), + LLFloaterPerms::getNextOwnerPerms("Wearables"), + cb); } -void LLInitialWearablesFetch::processWearablesMessage() +// static +void LLAgentWearables::editWearable(const LLUUID& item_id) { - if (!mAgentInitialWearables.empty()) // We have an empty current outfit folder, use the message data instead. + LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); + if (!item) { - LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); - for (U8 i = 0; i < mAgentInitialWearables.size(); ++i) - { - // Populate the current outfit folder with links to the wearables passed in the message - InitialWearableData *wearable_data = new InitialWearableData(mAgentInitialWearables[i]); // This will be deleted in the callback. - - if (wearable_data->mAssetID.notNull()) - { -#ifdef USE_CURRENT_OUTFIT_FOLDER - const std::string link_name = "WearableLink"; // Unimportant what this is named, it isn't exposed. - link_inventory_item(gAgent.getID(), wearable_data->mItemID, current_outfit_id, link_name, - LLAssetType::AT_LINK, LLPointer<LLInventoryCallback>(NULL)); -#endif - // Fetch the wearables - LLWearableList::instance().getAsset(wearable_data->mAssetID, - LLStringUtil::null, - LLWearableDictionary::getAssetType(wearable_data->mType), - LLAgentWearables::onInitialWearableAssetArrived, (void*)(wearable_data)); - } - else - { - llinfos << "Invalid wearable, type " << wearable_data->mType << " itemID " - << wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl; - } - } + LL_WARNS() << "Failed to get linked item" << LL_ENDL; + return; } - else + + LLViewerWearable* wearable = gAgentWearables.getWearableFromItemID(item_id); + if (!wearable) + { + LL_WARNS() << "Cannot get wearable" << LL_ENDL; + return; + } + + if (!gAgentWearables.isWearableModifiable(item->getUUID())) { - LL_WARNS("Wearables") << "No current outfit folder items found and no initial wearables fallback message received." << LL_ENDL; + LL_WARNS() << "Cannot modify wearable" << LL_ENDL; + return; } + + const BOOL disable_camera_switch = LLWearableType::getDisableCameraSwitch(wearable->getType()); + LLPanel* panel = LLFloaterSidePanelContainer::getPanel("appearance"); + LLSidepanelAppearance::editWearable(wearable, panel, disable_camera_switch); } +// Request editing the item after it gets worn. +void LLAgentWearables::requestEditingWearable(const LLUUID& item_id) +{ + mItemToEdit = gInventory.getLinkedItemID(item_id); +} +// Start editing the item if previously requested. +void LLAgentWearables::editWearableIfRequested(const LLUUID& item_id) +{ + if (mItemToEdit.notNull() && + mItemToEdit == gInventory.getLinkedItemID(item_id)) + { + LLAgentWearables::editWearable(item_id); + mItemToEdit.setNull(); + } +} + +boost::signals2::connection LLAgentWearables::addLoadingStartedCallback(loading_started_callback_t cb) +{ + return mLoadingStartedSignal.connect(cb); +} + +boost::signals2::connection LLAgentWearables::addLoadedCallback(loaded_callback_t cb) +{ + return mLoadedSignal.connect(cb); +} + +bool LLAgentWearables::changeInProgress() const +{ + return mCOFChangeInProgress; +} + +void LLAgentWearables::notifyLoadingStarted() +{ + mCOFChangeInProgress = true; + mCOFChangeTimer.reset(); + mLoadingStartedSignal(); +} + +void LLAgentWearables::notifyLoadingFinished() +{ + mCOFChangeInProgress = false; + mLoadedSignal(); +} +// EOF |