diff options
-rw-r--r-- | indra/newview/app_settings/settings.xml | 11 | ||||
-rw-r--r-- | indra/newview/llappearancemgr.cpp | 67 | ||||
-rw-r--r-- | indra/newview/llappearancemgr.h | 14 | ||||
-rw-r--r-- | indra/newview/lloutfitobserver.cpp | 15 | ||||
-rw-r--r-- | indra/newview/lloutfitobserver.h | 14 | ||||
-rw-r--r-- | indra/newview/llpaneloutfitedit.cpp | 6 | ||||
-rw-r--r-- | indra/newview/llpaneloutfitsinventory.cpp | 10 |
7 files changed, 130 insertions, 7 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 864a79c38a..52e987dac9 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -11417,5 +11417,16 @@ <key>Value</key> <integer>1</integer> </map> + <key>OutfitOperationsTimeout</key> + <map> + <key>Comment</key> + <string>Timeout for outfit related operations.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>180</integer> + </map> </map> </llsd> diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index a899926938..135629bb8d 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 { @@ -762,6 +792,27 @@ void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& respo } } +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); @@ -1810,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; @@ -2138,6 +2197,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() diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index f1beef5857..2227a43cd8 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -42,10 +42,12 @@ class LLWearable; class LLWearableHoldingPattern; class LLInventoryCallback; +class LLOutfitUnLockTimer; class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr> { friend class LLSingleton<LLAppearanceMgr>; + friend class LLOutfitUnLockTimer; public: typedef std::vector<LLInventoryModel::item_array_t> wearables_by_type_t; @@ -162,6 +164,8 @@ public: // COF is processed if cat_id is not specified void updateClothingOrderingInfo(LLUUID cat_id = LLUUID::null); + bool isOutfitLocked() { return mOutfitLocked; } + protected: LLAppearanceMgr(); ~LLAppearanceMgr(); @@ -186,10 +190,20 @@ private: static void onOutfitRename(const LLSD& notification, const LLSD& response); + void setOutfitLocked(bool locked); + std::set<LLUUID> mRegisteredAttachments; bool mAttachmentInvLinkEnabled; bool mOutfitIsDirty; + /** + * Lock for blocking operations on outfit until server reply or timeout exceed + * to avoid unsynchronized outfit state or performing duplicate operations. + */ + bool mOutfitLocked; + + std::auto_ptr<LLOutfitUnLockTimer> mUnlockOutfitTimer; + ////////////////////////////////////////////////////////////////////////////////// // Item-specific convenience functions public: diff --git a/indra/newview/lloutfitobserver.cpp b/indra/newview/lloutfitobserver.cpp index 848b595613..5652a98981 100644 --- a/indra/newview/lloutfitobserver.cpp +++ b/indra/newview/lloutfitobserver.cpp @@ -56,9 +56,9 @@ void LLOutfitObserver::changed(U32 mask) if (!gInventory.isInventoryUsable()) return; - bool panel_updated = checkCOF(); + bool COF_changed = checkCOF(); - if (!panel_updated) + if (!COF_changed) { checkBaseOutfit(); } @@ -87,6 +87,7 @@ bool LLOutfitObserver::checkCOF() mCOFLastVersion = cof_version; + // dirtiness state should be updated before sending signal LLAppearanceMgr::getInstance()->updateIsDirty(); mCOFChanged(); @@ -120,6 +121,16 @@ void LLOutfitObserver::checkBaseOutfit() } LLAppearanceMgr& app_mgr = LLAppearanceMgr::instance(); + // dirtiness state should be updated before sending signal app_mgr.updateIsDirty(); mBOFChanged(); + + if (mLastOutfitDirtiness != app_mgr.isOutfitDirty()) + { + if(!app_mgr.isOutfitDirty()) + { + mCOFSaved(); + } + mLastOutfitDirtiness = app_mgr.isOutfitDirty(); + } } diff --git a/indra/newview/lloutfitobserver.h b/indra/newview/lloutfitobserver.h index 4cb40ead15..a4b5fbe04a 100644 --- a/indra/newview/lloutfitobserver.h +++ b/indra/newview/lloutfitobserver.h @@ -48,6 +48,8 @@ public: virtual void changed(U32 mask); + void notifyOutfitLockChanged() { mOutfitLockChanged(); } + typedef boost::signals2::signal<void (void)> signal_t; void addBOFReplacedCallback(const signal_t::slot_type& cb) { mBOFReplaced.connect(cb); } @@ -56,6 +58,10 @@ public: void addCOFChangedCallback(const signal_t::slot_type& cb) { mCOFChanged.connect(cb); } + void addCOFSavedCallback(const signal_t::slot_type& cb) { mCOFSaved.connect(cb); } + + void addOutfitLockChangedCallback(const signal_t::slot_type& cb) { mOutfitLockChanged.connect(cb); } + protected: LLOutfitObserver(); @@ -73,10 +79,18 @@ protected: S32 mBaseOutfitLastVersion; + bool mLastOutfitDirtiness; + private: signal_t mBOFReplaced; signal_t mBOFChanged; signal_t mCOFChanged; + signal_t mCOFSaved; + + /** + * Signal for changing state of outfit lock. + */ + signal_t mOutfitLockChanged; }; #endif /* LL_OUTFITOBSERVER_H */ diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index ce382541c6..f3f67f2b03 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -198,6 +198,7 @@ LLPanelOutfitEdit::LLPanelOutfitEdit() LLOutfitObserver& observer = LLOutfitObserver::instance(); observer.addBOFReplacedCallback(boost::bind(&LLPanelOutfitEdit::updateCurrentOutfitName, this)); observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this)); + observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this)); observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitEdit::update, this)); mLookItemTypes.reserve(NUM_LOOK_ITEM_TYPES); @@ -666,12 +667,13 @@ void LLPanelOutfitEdit::updateCurrentOutfitName() void LLPanelOutfitEdit::updateVerbs() { bool outfit_is_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty(); + bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked(); bool has_baseoutfit = LLAppearanceMgr::getInstance()->getBaseOutfitUUID().notNull(); - mSaveComboBtn->setSaveBtnEnabled(outfit_is_dirty); + mSaveComboBtn->setSaveBtnEnabled(!outfit_locked && outfit_is_dirty); childSetEnabled(REVERT_BTN, outfit_is_dirty && has_baseoutfit); - mSaveComboBtn->setMenuItemEnabled("save_outfit", outfit_is_dirty); + mSaveComboBtn->setMenuItemEnabled("save_outfit", !outfit_locked && outfit_is_dirty); mStatus->setText(outfit_is_dirty ? getString("unsaved_changes") : getString("now_editing")); diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 8836672f91..8b451c156c 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -209,6 +209,7 @@ LLPanelOutfitsInventory::LLPanelOutfitsInventory() : LLOutfitObserver& observer = LLOutfitObserver::instance(); observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); + observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); } LLPanelOutfitsInventory::~LLPanelOutfitsInventory() @@ -522,7 +523,7 @@ void LLPanelOutfitsInventory::updateListCommands() { bool trash_enabled = isActionEnabled("delete"); bool wear_enabled = isActionEnabled("wear"); - bool make_outfit_enabled = isActionEnabled("make_outfit"); + bool make_outfit_enabled = isActionEnabled("save_outfit"); mListCommands->childSetEnabled("trash_btn", trash_enabled); mListCommands->childSetEnabled("wear_btn", wear_enabled); @@ -668,9 +669,12 @@ BOOL LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) return FALSE; } } - if (command_name == "make_outfit") + if (command_name == "save_outfit") { - return LLAppearanceMgr::getInstance()->isOutfitDirty(); + bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked(); + bool outfit_dirty =LLAppearanceMgr::getInstance()->isOutfitDirty(); + // allow save only if outfit isn't locked and is dirty + return !outfit_locked && outfit_dirty; } if (command_name == "edit" || |