Commits

Vadim Savchuk  committed f9a628a

STORM-1543 WIP Fixed auto-moving offered inventory items to trash.

It turned out impossible to properly remove an inventory item
from within LLDiscardAgentOffer::done(), because that would lead to
nested LLInventoryModel::notifyObservers() calls.

Fixed that by deferring removal until the next LLAppViewer::idle() iteration.

Also elimiteed duplicated code.

  • Participants
  • Parent commits 3a56fbe

Comments (0)

Files changed (3)

File indra/newview/llappviewer.cpp

 // Used on Win32 for other apps to identify our window (eg, win_setup)
 const char* const VIEWER_WINDOW_CLASSNAME = "Second Life";
 
+//-- LLDeferredTaskList ------------------------------------------------------
+
+/**
+ * A list of deferred tasks.
+ *
+ * We sometimes need to defer execution of some code until the viewer gets idle,
+ * e.g. removing an inventory item from within notifyObservers() may not work out.
+ *
+ * Tasks added to this list will be executed in the next LLAppViewer::idle() iteration.
+ * All tasks are executed only once.
+ */
+class LLDeferredTaskList:
+	public LLSingleton<LLDeferredTaskList>,
+	private LLDestroyClass<LLDeferredTaskList>
+{
+	LOG_CLASS(LLDeferredTaskList);
+	friend class LLAppViewer;
+	friend class LLDestroyClass<LLDeferredTaskList>;
+	typedef boost::signals2::signal<void()> signal_t;
+
+	void addTask(const signal_t::slot_type& cb)
+	{
+		mSignal.connect(cb);
+	}
+
+	void run()
+	{
+		if (mSignal.empty()) return;
+
+		mSignal();
+		mSignal.disconnect_all_slots();
+	}
+
+	static void destroyClass()
+	{
+		instance().mSignal.disconnect_all_slots();
+	}
+
+	signal_t mSignal;
+};
+
 //----------------------------------------------------------------------------
 
 // List of entries from strings.xml to always replace
 	}
 }
 
+void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb)
+{
+	LLDeferredTaskList::instance().addTask(cb);
+}
+
 void LLAppViewer::purgeCache()
 {
 	LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << LL_ENDL;
 			gAudiop->idle(max_audio_decode_time);
 		}
 	}
+
+	LLDeferredTaskList::instance().run();
 	
 	// Handle shutdown process, for example, 
 	// wait for floaters to close, send quit message,

File indra/newview/llappviewer.h

 	login_completed_signal_t mOnLoginCompleted;
 	boost::signals2::connection setOnLoginCompletedCallback( const login_completed_signal_t::slot_type& cb ) { return mOnLoginCompleted.connect(cb); } 
 
+	void addOnIdleCallback(const boost::function<void()>& cb); // add a callback to fire (once) when idle
+
 	void purgeCache(); // Clear the local cache. 
 	
 	// mute/unmute the system's master audio

File indra/newview/llviewermessage.cpp

 	virtual void done()
 	{
 		LL_DEBUGS("Messaging") << "LLDiscardAgentOffer::done()" << LL_ENDL;
-		const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
-		bool notify = false;
-		if(trash_id.notNull() && mObjectID.notNull())
-		{
-			LLInventoryModel::update_list_t update;
-			LLInventoryModel::LLCategoryUpdate old_folder(mFolderID, -1);
-			update.push_back(old_folder);
-			LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1);
-			update.push_back(new_folder);
-			gInventory.accountForUpdate(update);
-			gInventory.moveObject(mObjectID, trash_id);
-			LLInventoryObject* obj = gInventory.getObject(mObjectID);
-			if(obj)
-			{
-				// no need to restamp since this is already a freshly
-				// stamped item.
-				obj->updateParentOnServer(FALSE);
-				notify = true;
-			}
-		}
-		else
-		{
-			LL_WARNS("Messaging") << "DiscardAgentOffer unable to find: "
-					<< (trash_id.isNull() ? "trash " : "")
-					<< (mObjectID.isNull() ? "object" : "") << LL_ENDL;
-		}
+
+		LLAppViewer::instance()->addOnIdleCallback(boost::bind(&LLInventoryModel::removeItem, &gInventory, mObjectID));
 		gInventory.removeObserver(this);
-		if(notify)
-		{
-			gInventory.notifyObservers();
-		}
 		delete this;
 	}
 protected: