diff options
Diffstat (limited to 'indra/newview/llappearancemgr.cpp')
-rw-r--r-- | indra/newview/llappearancemgr.cpp | 222 |
1 files changed, 201 insertions, 21 deletions
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index c417f8bdf5..a7206095d3 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -38,11 +38,13 @@ #include "llagentwearables.h" #include "llappearancemgr.h" #include "llcommandhandler.h" +#include "lleventtimer.h" #include "llgesturemgr.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventoryobserver.h" #include "llnotificationsutil.h" +#include "lloutfitobserver.h" #include "llpaneloutfitsinventory.h" #include "llselectmgr.h" #include "llsidepanelappearance.h" @@ -55,6 +57,34 @@ char ORDER_NUMBER_SEPARATOR('@'); +class LLOutfitUnLockTimer: public LLEventTimer +{ +public: + LLOutfitUnLockTimer(F32 period) : LLEventTimer(period) + { + // restart timer on BOF changed event + LLOutfitObserver::instance().addBOFChangedCallback(boost::bind( + &LLOutfitUnLockTimer::reset, this)); + stop(); + } + + /*virtual*/ + BOOL tick() + { + if(mEventTimer.hasExpired()) + { + LLAppearanceMgr::instance().setOutfitLocked(false); + } + return FALSE; + } + void stop() { mEventTimer.stop(); } + void start() { mEventTimer.start(); } + void reset() { mEventTimer.reset(); } + BOOL getStarted() { return mEventTimer.getStarted(); } + + LLTimer& getEventTimer() { return mEventTimer;} +}; + // support for secondlife:///app/appearance SLapps class LLAppearanceHandler : public LLCommandHandler { @@ -276,12 +306,14 @@ void LLWearableHoldingPattern::checkMissingWearables() if (found_by_type[type] > 0) continue; if ( - // Need to recover if at least one wearable of that type - // was requested but none was found (prevent missing - // pants) - (requested_by_type[type] > 0) || - // or if type is a body part and no wearables were found. - ((type == LLWearableType::WT_SHAPE) || (type == LLWearableType::WT_SKIN) || (type == LLWearableType::WT_HAIR) || (type == LLWearableType::WT_EYES))) + // If at least one wearable of certain types (pants/shirt/skirt) + // was requested but none was found, create a default asset as a replacement. + // In all other cases, don't do anything. + // For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud + // due to logic in LLVOAvatarSelf::getIsCloud(). + // For non-critical types (tatoo, socks, etc.) the wearable will just be missing. + (requested_by_type[type] > 0) && + ((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT))) { mTypesToRecover.insert(type); mTypesToLink.insert(type); @@ -612,6 +644,13 @@ const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink() const LLViewerInventoryCategory *cat = item->getLinkedCategory(); if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) { + const LLUUID parent_id = cat->getParentUUID(); + LLViewerInventoryCategory* parent_cat = gInventory.getCategory(parent_id); + // if base outfit moved to trash it means that we don't have base outfit + if (parent_cat != NULL && parent_cat->getPreferredType() == LLFolderType::FT_TRASH) + { + return NULL; + } return item; } } @@ -654,15 +693,37 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up { if (item_id_to_wear.isNull()) return false; - //only the item from a user's inventory is allowed - if (!gInventory.isObjectDescendentOf(item_id_to_wear, gInventory.getRootFolderID())) return false; - LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear); if (!item_to_wear) return false; + if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID())) + { + LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(replace); + copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(),cb); + return false; + } + else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID())) + { + return false; // not in library and not in agent's inventory + } + else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH))) + { + LLNotificationsUtil::add("CannotWearTrash"); + return false; + } + switch (item_to_wear->getType()) { case LLAssetType::AT_CLOTHING: + if (gAgentWearables.areWearablesLoaded()) + { + S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType()); + if ((replace && wearable_count != 0) || + (wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) ) + { + removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1), false); + } + } case LLAssetType::AT_BODYPART: // Don't wear anything until initial wearables are loaded, can // destroy clothing items. @@ -674,7 +735,7 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up // Remove the existing wearables of the same type. // Remove existing body parts anyway because we must not be able to wear e.g. two skins. - if (replace || item_to_wear->getType() == LLAssetType::AT_BODYPART) + if (item_to_wear->getType() == LLAssetType::AT_BODYPART) { removeCOFLinksOfType(item_to_wear->getWearableType(), false); } @@ -704,6 +765,61 @@ void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit) wearInventoryCategory(cat, false, false); } +// Open outfit renaming dialog. +void LLAppearanceMgr::renameOutfit(const LLUUID& outfit_id) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id); + if (!cat) + { + return; + } + + LLSD args; + args["NAME"] = cat->getName(); + + LLSD payload; + payload["cat_id"] = outfit_id; + + LLNotificationsUtil::add("RenameOutfit", args, payload, boost::bind(onOutfitRename, _1, _2)); +} + +// User typed new outfit name. +// static +void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; // canceled + + std::string outfit_name = response["new_name"].asString(); + LLStringUtil::trim(outfit_name); + if (!outfit_name.empty()) + { + LLUUID cat_id = notification["payload"]["cat_id"].asUUID(); + rename_category(&gInventory, cat_id, outfit_name); + } +} + +void LLAppearanceMgr::setOutfitLocked(bool locked) +{ + if (mOutfitLocked == locked) + { + return; + } + + mOutfitLocked = locked; + if (locked) + { + mUnlockOutfitTimer->reset(); + mUnlockOutfitTimer->start(); + } + else + { + mUnlockOutfitTimer->stop(); + } + + LLOutfitObserver::instance().notifyOutfitLockChanged(); +} + void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id) { LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); @@ -714,7 +830,7 @@ void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id) { LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - LLFindWearables collector; + LLFindWorn collector; gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector); @@ -842,6 +958,33 @@ BOOL LLAppearanceMgr::getCanMakeFolderIntoOutfit(const LLUUID& folder_id) return ((required_wearables & folder_wearables) == required_wearables); } +bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id) +{ + // Disallow removing the base outfit. + if (outfit_cat_id == getBaseOutfitUUID()) + { + return false; + } + + // Check if the outfit folder itself is removable. + if (!get_is_category_removable(&gInventory, outfit_cat_id)) + { + return false; + } + + // Check for the folder's non-removable descendants. + LLFindNonRemovableObjects filter_non_removable; + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLInventoryModel::item_array_t::const_iterator it; + gInventory.collectDescendentsIf(outfit_cat_id, cats, items, false, filter_non_removable); + if (!cats.empty() || !items.empty()) + { + return false; + } + + return true; +} void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category) { @@ -1264,6 +1407,7 @@ void LLAppearanceMgr::getUserDescendents(const LLUUID& category, void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append) { + gAgentWearables.notifyLoadingStarted(); if(!category) return; llinfos << "wearInventoryCategory( " << category->getName() @@ -1488,6 +1632,7 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update item_array, LLInventoryModel::EXCLUDE_TRASH); bool linked_already = false; + U32 count = 0; for (S32 i=0; i<item_array.count(); i++) { // Are these links to the same object? @@ -1505,15 +1650,21 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update } // Are these links to different items of the same body part // type? If so, new item will replace old. - // TODO: MULTI-WEARABLE: check for wearable limit for clothing types - else if (is_body_part && (vitem->isWearableType()) && (vitem->getWearableType() == wearable_type)) + else if ((vitem->isWearableType()) && (vitem->getWearableType() == wearable_type)) { - if (inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type)) + ++count; + if (is_body_part && inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type)) { gInventory.purgeObject(inv_item->getUUID()); } + else if (count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) + { + // MULTI-WEARABLES: make sure we don't go over MAX_CLOTHING_PER_TYPE + gInventory.purgeObject(inv_item->getUUID()); + } } } + if (linked_already) { if (do_update) @@ -1710,6 +1861,14 @@ void LLAppearanceMgr::onFirstFullyVisible() bool LLAppearanceMgr::updateBaseOutfit() { + if (isOutfitLocked()) + { + // don't allow modify locked outfit + llassert(!isOutfitLocked()); + return false; + } + setOutfitLocked(true); + const LLUUID base_outfit_id = getBaseOutfitUUID(); if (base_outfit_id.isNull()) return false; @@ -1790,10 +1949,16 @@ struct WearablesOrderComparator U32 mControlSize; }; -void LLAppearanceMgr::updateClothingOrderingInfo() +void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id) { + if (cat_id.isNull()) + { + cat_id = getCOF(); + } + + // COF is processed if cat_id is not specified LLInventoryModel::item_array_t wear_items; - getDescendentsOfAssetType(getCOF(), wear_items, LLAssetType::AT_CLOTHING, false); + getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING, false); wearables_by_type_t items_by_type(LLWearableType::WT_COUNT); divvyWearablesByType(wear_items, items_by_type); @@ -1835,13 +2000,19 @@ void LLAppearanceMgr::updateClothingOrderingInfo() class LLShowCreatedOutfit: public LLInventoryCallback { public: - LLShowCreatedOutfit(LLUUID& folder_id): mFolderID(folder_id) + LLShowCreatedOutfit(LLUUID& folder_id, bool show_panel = true): mFolderID(folder_id), mShowPanel(show_panel) {} virtual ~LLShowCreatedOutfit() { LLSD key; - LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); + + //EXT-7727. For new accounts LLShowCreatedOutfit is created during login process + // add may be processed after login process is finished + if (mShowPanel) + { + LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); + } LLPanelOutfitsInventory *outfit_panel = dynamic_cast<LLPanelOutfitsInventory*>(LLSideTray::getInstance()->getPanel("panel_outfits_inventory")); if (outfit_panel) @@ -1865,9 +2036,10 @@ public: private: LLUUID mFolderID; + bool mShowPanel; }; -LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name) +LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, bool show_panel) { if (!isAgentAvatarValid()) return LLUUID::null; @@ -1880,7 +2052,7 @@ LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name) updateClothingOrderingInfo(); - LLPointer<LLInventoryCallback> cb = new LLShowCreatedOutfit(folder_id); + LLPointer<LLInventoryCallback> cb = new LLShowCreatedOutfit(folder_id,show_panel); shallowCopyCategoryContents(getCOF(),folder_id, cb); createBaseOutfitLink(folder_id, cb); @@ -1974,7 +2146,7 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b bool result = false; if (result = gAgentWearables.moveWearable(item, closer_to_body)) { - gAgentAvatarp->wearableUpdated(item->getWearableType(), TRUE); + gAgentAvatarp->wearableUpdated(item->getWearableType(), FALSE); } setOutfitDirty(true); @@ -2032,6 +2204,14 @@ LLAppearanceMgr::LLAppearanceMgr(): mAttachmentInvLinkEnabled(false), mOutfitIsDirty(false) { + LLOutfitObserver& outfit_observer = LLOutfitObserver::instance(); + + // unlock outfit on save operation completed + outfit_observer.addCOFSavedCallback(boost::bind( + &LLAppearanceMgr::setOutfitLocked, this, false)); + + mUnlockOutfitTimer.reset(new LLOutfitUnLockTimer(gSavedSettings.getS32( + "OutfitOperationsTimeout"))); } LLAppearanceMgr::~LLAppearanceMgr() |