diff options
Diffstat (limited to 'indra/newview/llappearancemgr.cpp')
-rw-r--r-- | indra/newview/llappearancemgr.cpp | 396 |
1 files changed, 304 insertions, 92 deletions
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 4d4a89bcd4..eb4a47664b 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -35,6 +35,7 @@ #include "llagent.h" #include "llagentwearables.h" #include "llappearancemgr.h" +#include "llcommandhandler.h" #include "llfloatercustomize.h" #include "llgesturemgr.h" #include "llinventorybridge.h" @@ -47,6 +48,48 @@ #include "llviewerregion.h" #include "llwearablelist.h" +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(); + } + } +} + +// 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 + LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD()); + return true; + } +}; +LLAppearanceHandler gAppearanceHandler; + class LLWearInventoryCategoryCallback : public LLInventoryCallback { public: @@ -70,6 +113,8 @@ public: 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() ) @@ -107,12 +152,15 @@ protected: void LLOutfitObserver::done() { + llinfos << "done 2nd stage fetch" << llendl; gInventory.removeObserver(this); doOnIdle(boost::bind(&LLOutfitObserver::doWearCategory,this)); } void LLOutfitObserver::doWearCategory() { + llinfos << "starting" << llendl; + // We now have an outfit ready to be copied to agent inventory. Do // it, and wear that outfit normally. if(mCopyItems) @@ -201,6 +249,8 @@ void LLOutfitFetch::done() // What we do here is get the complete information on the items in // the library, and set up an observer that will wait for that to // happen. + llinfos << "done first stage fetch" << llendl; + LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; gInventory.collectDescendents(mCompleteFolders.front(), @@ -261,11 +311,17 @@ public: virtual ~LLUpdateAppearanceOnDestroy() { - LLAppearanceManager::instance().updateAppearanceFromCOF(); + llinfos << "done update appearance on destroy" << llendl; + + if (!LLApp::isExiting()) + { + LLAppearanceManager::instance().updateAppearanceFromCOF(); + } } /* virtual */ void fire(const LLUUID& inv_item) { + llinfos << "callback fired" << llendl; mFireCount++; } private: @@ -274,10 +330,11 @@ private: struct LLFoundData { + LLFoundData() : mAssetType(LLAssetType::AT_NONE), mWearable(NULL) {} LLFoundData(const LLUUID& item_id, - const LLUUID& asset_id, - const std::string& name, - LLAssetType::EType asset_type) : + const LLUUID& asset_id, + const std::string& name, + LLAssetType::EType asset_type) : mItemID(item_id), mAssetID(asset_id), mName(name), @@ -292,20 +349,103 @@ struct LLFoundData }; -struct LLWearableHoldingPattern +class LLWearableHoldingPattern { - LLWearableHoldingPattern() : mResolved(0) {} - ~LLWearableHoldingPattern() - { - for_each(mFoundList.begin(), mFoundList.end(), DeletePointer()); - mFoundList.clear(); - } - typedef std::list<LLFoundData*> found_list_t; +public: + LLWearableHoldingPattern(); + ~LLWearableHoldingPattern(); + + bool pollCompletion(); + bool isFetchCompleted(); + bool isTimedOut(); + + typedef std::list<LLFoundData> found_list_t; found_list_t mFoundList; + LLInventoryModel::item_array_t mObjItems; + LLInventoryModel::item_array_t mGestItems; S32 mResolved; - bool append; + LLTimer mWaitTime; + bool mFired; }; +LLWearableHoldingPattern::LLWearableHoldingPattern(): + mResolved(0), + mFired(false) +{ +} + +LLWearableHoldingPattern::~LLWearableHoldingPattern() +{ +} + +bool LLWearableHoldingPattern::isFetchCompleted() +{ + return (mResolved >= (S32)mFoundList.size()); // have everything we were waiting for? +} + +bool LLWearableHoldingPattern::isTimedOut() +{ + static F32 max_wait_time = 20.0; // give up if wearable fetches haven't completed in max_wait_time seconds. + return mWaitTime.getElapsedTimeF32() > max_wait_time; +} + +bool LLWearableHoldingPattern::pollCompletion() +{ + bool completed = isFetchCompleted(); + bool timed_out = isTimedOut(); + bool done = completed || timed_out; + + llinfos << "polling, done status: " << completed << " timed out? " << timed_out << " elapsed " << mWaitTime.getElapsedTimeF32() << llendl; + + if (done) + { + mFired = true; + + if (timed_out) + { + llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl; + } + + // Activate all gestures in this folder + if (mGestItems.count() > 0) + { + llinfos << "Activating " << mGestItems.count() << " gestures" << llendl; + + LLGestureManager::instance().activateGestures(mGestItems); + + // Update the inventory item labels to reflect the fact + // they are active. + LLViewerInventoryCategory* catp = + gInventory.getCategory(LLAppearanceManager::instance().getCOF()); + + if (catp) + { + gInventory.updateCategory(catp); + gInventory.notifyObservers(); + } + } + + // Update wearables. + llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl; + LLAppearanceManager::instance().updateAgentWearables(this, false); + + // Update attachments to match those requested. + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if( avatar ) + { + llinfos << "Updating " << mObjItems.count() << " attachments" << llendl; + LLAgentWearables::userUpdateAttachments(mObjItems); + } + + if (completed) + { + // Only safe to delete if all wearable callbacks completed. + delete this; + } + } + return done; +} + static void removeDuplicateItems(LLInventoryModel::item_array_t& items) { LLInventoryModel::item_array_t new_items; @@ -336,29 +476,28 @@ static void removeDuplicateItems(LLInventoryModel::item_array_t& items) static void onWearableAssetFetch(LLWearable* wearable, void* data) { LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data; - bool append = holder->append; - + if (holder->mFired) + { + llwarns << "called after holder fired" << llendl; + } + if(wearable) { for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin(); iter != holder->mFoundList.end(); ++iter) { - LLFoundData* data = *iter; - if(wearable->getAssetID() == data->mAssetID) + LLFoundData& data = *iter; + if(wearable->getAssetID() == data.mAssetID) { - data->mWearable = wearable; + data.mWearable = wearable; break; } } } holder->mResolved += 1; - if(holder->mResolved >= (S32)holder->mFoundList.size()) - { - LLAppearanceManager::instance().updateAgentWearables(holder, append); - } } -LLUUID LLAppearanceManager::getCOF() +const LLUUID LLAppearanceManager::getCOF() const { return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); } @@ -392,6 +531,21 @@ const LLViewerInventoryItem* LLAppearanceManager::getBaseOutfitLink() return NULL; } +bool LLAppearanceManager::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; +} + // Update appearance from outfit folder. void LLAppearanceManager::changeOutfit(bool proceed, const LLUUID& category, bool append) { @@ -400,9 +554,33 @@ void LLAppearanceManager::changeOutfit(bool proceed, const LLUUID& category, boo LLAppearanceManager::instance().updateCOF(category,append); } +// Create a copy of src_id + contents as a subfolder of dst_id. void LLAppearanceManager::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; + } + 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 LLAppearanceManager::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, + LLPointer<LLInventoryCallback> cb) +{ LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; gInventory.collectDescendents(src_id, cats, items, @@ -498,6 +676,11 @@ void LLAppearanceManager::filterWearableItems( if (!item->isWearableType()) continue; EWearableType type = item->getWearableType(); + if(type < 0 || type >= WT_COUNT) + { + LL_WARNS("Appearance") << "Invalid wearable type. Inventory type does not match wearable flag bitfield." << LL_ENDL; + continue; + } items_by_type[type].push_back(item); } @@ -535,6 +718,8 @@ void LLAppearanceManager::linkAll(const LLUUID& category, void LLAppearanceManager::updateCOF(const LLUUID& category, bool append) { + llinfos << "starting" << llendl; + const LLUUID cof = getCOF(); // Deactivate currently active gestures in the COF, if replacing outfit @@ -592,6 +777,7 @@ void LLAppearanceManager::updateCOF(const LLUUID& category, bool append) gInventory.notifyObservers(); // Create links to new COF contents. + llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl; LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy; linkAll(cof, body_items, link_waiter); @@ -604,6 +790,7 @@ void LLAppearanceManager::updateCOF(const LLUUID& category, bool append) { createBaseOutfitLink(category, link_waiter); } + llinfos << "waiting for LLUpdateAppearanceOnDestroy" << llendl; } void LLAppearanceManager::updatePanelOutfitName(const std::string& name) @@ -630,6 +817,7 @@ void LLAppearanceManager::createBaseOutfitLink(const LLUUID& category, LLPointer LLAssetType::AT_LINK_FOLDER, link_waiter); new_outfit_name = catp->getName(); } + updatePanelOutfitName(new_outfit_name); } @@ -646,12 +834,12 @@ void LLAppearanceManager::updateAgentWearables(LLWearableHoldingPattern* holder, for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin(); iter != holder->mFoundList.end(); ++iter) { - LLFoundData* data = *iter; - LLWearable* wearable = data->mWearable; + LLFoundData& data = *iter; + LLWearable* wearable = data.mWearable; if( wearable && ((S32)wearable->getType() == i) ) { LLViewerInventoryItem* item; - item = (LLViewerInventoryItem*)gInventory.getItem(data->mItemID); + item = (LLViewerInventoryItem*)gInventory.getItem(data.mItemID); if( item && (item->getAssetUUID() == wearable->getAssetID()) ) { items.put(item); @@ -667,8 +855,6 @@ void LLAppearanceManager::updateAgentWearables(LLWearableHoldingPattern* holder, gAgentWearables.setWearableOutfit(items, wearables, !append); } - delete holder; - // dec_busy_count(); } @@ -676,6 +862,8 @@ void LLAppearanceManager::updateAppearanceFromCOF() { // update dirty flag to see if the state of the COF matches // the saved outfit stored as a folder link + llinfos << "starting" << llendl; + updateIsDirty(); dumpCat(getCOF(),"COF, start"); @@ -690,86 +878,66 @@ void LLAppearanceManager::updateAppearanceFromCOF() LLInventoryModel::item_array_t gest_items; getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items, follow_folder_links); - if( !wear_items.count() && !obj_items.count() && !gest_items.count()) + if(!wear_items.count()) { LLNotificationsUtil::add("CouldNotPutOnOutfit"); return; } - - // Processes that take time should show the busy cursor - //inc_busy_count(); // BAP this is currently a no-op in llinventorybridge.cpp - do we need it? - - // Activate all gestures in this folder - if (gest_items.count() > 0) - { - llinfos << "Activating " << gest_items.count() << " gestures" << llendl; - LLGestureManager::instance().activateGestures(gest_items); + LLWearableHoldingPattern* holder = new LLWearableHoldingPattern; - // Update the inventory item labels to reflect the fact - // they are active. - LLViewerInventoryCategory* catp = gInventory.getCategory(current_outfit_id); - if (catp) + holder->mObjItems = obj_items; + holder->mGestItems = 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(). + LLDynamicArray<LLFoundData> found_container; + for(S32 i = 0; i < wear_items.count(); ++i) + { + LLViewerInventoryItem *item = wear_items.get(i); + LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL; + if (item && linked_item) { - gInventory.updateCategory(catp); - gInventory.notifyObservers(); + LLFoundData found(linked_item->getUUID(), + linked_item->getAssetUUID(), + linked_item->getName(), + linked_item->getType()); + holder->mFoundList.push_front(found); + found_container.put(found); } - } - - if(wear_items.count() > 0) - { - // Note: can't do normal iteration, because if all the - // wearables can be resolved immediately, then the - // callback will be called (and this object deleted) - // before the final getNextData(). - LLWearableHoldingPattern* holder = new LLWearableHoldingPattern; - LLFoundData* found; - LLDynamicArray<LLFoundData*> found_container; - for(S32 i = 0; i < wear_items.count(); ++i) + else { - LLViewerInventoryItem *item = wear_items.get(i); - LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL; - if (item && linked_item) + if (!item) { - found = new LLFoundData(linked_item->getUUID(), - linked_item->getAssetUUID(), - linked_item->getName(), - linked_item->getType()); - holder->mFoundList.push_front(found); - found_container.put(found); + llwarns << "attempt to wear a null item " << llendl; } - else + else if (!linked_item) { - if (!item) - { - llwarns << "attempt to wear a null item " << llendl; - } - else if (!linked_item) - { - llwarns << "attempt to wear a broken link " << item->getName() << llendl; - } + llwarns << "attempt to wear a broken link " << item->getName() << llendl; } } - for(S32 i = 0; i < found_container.count(); ++i) - { - holder->append = false; - found = found_container.get(i); + } + + for(S32 i = 0; i < found_container.count(); ++i) + { + LLFoundData& found = found_container.get(i); - // Fetch the wearables about to be worn. - LLWearableList::instance().getAsset(found->mAssetID, - found->mName, - found->mAssetType, - onWearableAssetFetch, - (void*)holder); - } + // Fetch the wearables about to be worn. + LLWearableList::instance().getAsset(found.mAssetID, + found.mName, + found.mAssetType, + onWearableAssetFetch, + (void*)holder); + } - // Update attachments to match those requested. - LLVOAvatar* avatar = gAgent.getAvatarObject(); - if( avatar ) + if (!holder->pollCompletion()) { - LLAgentWearables::userUpdateAttachments(obj_items); + doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollCompletion,holder)); } + } void LLAppearanceManager::getDescendentsOfAssetType(const LLUUID& category, @@ -826,8 +994,9 @@ void LLAppearanceManager::wearInventoryCategory(LLInventoryCategory* category, b { if(!category) return; - lldebugs << "wearInventoryCategory( " << category->getName() + llinfos << "wearInventoryCategory( " << category->getName() << " )" << llendl; + // What we do here is get the complete information on the items in // the inventory, and set up an observer that will wait for that to // happen. @@ -856,7 +1025,8 @@ void LLAppearanceManager::wearInventoryCategoryOnAvatar( LLInventoryCategory* ca // this up front to avoid having to deal with the case of multiple // wearables being dirty. if(!category) return; - lldebugs << "wearInventoryCategoryOnAvatar( " << category->getName() + + llinfos << "wearInventoryCategoryOnAvatar( " << category->getName() << " )" << llendl; if( gFloaterCustomize ) @@ -999,7 +1169,6 @@ void LLAppearanceManager::addCOFItemLink(const LLInventoryItem *item, bool do_up // MULTI-WEARABLES: revisit if more than one per type is allowed. else if (areMatchingWearables(vitem,inv_item)) { - gAgentWearables.removeWearable(inv_item->getWearableType(),true,0); if (inv_item->getIsLinkType()) { gInventory.purgeObject(inv_item->getUUID()); @@ -1134,6 +1303,23 @@ void LLAppearanceManager::updateIsDirty() } } +void LLAppearanceManager::onFirstFullyVisible() +{ + // If this is the very first time the user has logged into viewer2+ (from a legacy viewer, or new account) + // then auto-populate outfits from the library into the My Outfits folder. + + llinfos << "avatar fully visible" << llendl; + + static bool check_populate_my_outfits = true; + if (check_populate_my_outfits && + (LLInventoryModel::getIsFirstTimeInViewer2() + || gSavedSettings.getBOOL("MyOutfitsAutofill"))) + { + gAgentWearables.populateMyOutfitsFolder(); + } + check_populate_my_outfits = false; +} + //#define DUMP_CAT_VERBOSE void LLAppearanceManager::dumpCat(const LLUUID& cat_id, const std::string& msg) @@ -1247,3 +1433,29 @@ void LLAppearanceManager::linkRegisteredAttachments() } mRegisteredAttachments.clear(); } + +BOOL LLAppearanceManager::getIsInCOF(const LLUUID& obj_id) const +{ + return gInventory.isObjectDescendentOf(obj_id, getCOF()); +} + +BOOL LLAppearanceManager::getIsProtectedCOFItem(const LLUUID& obj_id) const +{ + if (!getIsInCOF(obj_id)) return FALSE; + + // For now, don't allow direct deletion from the COF. Instead, force users + // to choose "Detach" or "Take Off". + return TRUE; + /* + const LLInventoryObject *obj = gInventory.getObject(obj_id); + if (!obj) return FALSE; + + // Can't delete bodyparts, since this would be equivalent to removing the item. + if (obj->getType() == LLAssetType::AT_BODYPART) return TRUE; + + // Can't delete the folder link, since this is saved for bookkeeping. + if (obj->getActualType() == LLAssetType::AT_LINK_FOLDER) return TRUE; + + return FALSE; + */ +} |