Removal of discarded inventory offers moved to deferred (idle) tick. Nested gInventory.notifyObservers() don't work.
This commit is contained in:
@@ -331,6 +331,40 @@ static std::string gLaunchFileOnQuit;
|
||||
// Used on Win32 for other apps to identify our window (eg, win_setup)
|
||||
const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; // Don't change
|
||||
|
||||
//-- 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>
|
||||
{
|
||||
LOG_CLASS(LLDeferredTaskList);
|
||||
|
||||
friend class LLAppViewer;
|
||||
typedef boost::signals2::signal<void()> signal_t;
|
||||
|
||||
void addTask(const signal_t::slot_type& cb)
|
||||
{
|
||||
mSignal.connect(cb);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
if (!mSignal.empty())
|
||||
{
|
||||
mSignal();
|
||||
mSignal.disconnect_all_slots();
|
||||
}
|
||||
}
|
||||
|
||||
signal_t mSignal;
|
||||
};
|
||||
//----------------------------------------------------------------------------
|
||||
// File scope definitons
|
||||
const char *VFS_DATA_FILE_BASE = "data.db2.x.";
|
||||
@@ -3348,6 +3382,11 @@ bool LLAppViewer::initCache()
|
||||
}
|
||||
}
|
||||
|
||||
void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb)
|
||||
{
|
||||
LLDeferredTaskList::instance().addTask(cb);
|
||||
}
|
||||
|
||||
void LLAppViewer::purgeCache()
|
||||
{
|
||||
LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << llendl;
|
||||
@@ -3932,6 +3971,9 @@ void LLAppViewer::idle()
|
||||
gAudiop->idle(max_audio_decode_time);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute deferred tasks.
|
||||
LLDeferredTaskList::instance().run();
|
||||
|
||||
// Handle shutdown process, for example,
|
||||
// wait for floaters to close, send quit message,
|
||||
|
||||
@@ -158,6 +158,7 @@ public:
|
||||
// *NOTE:Mani Fix this for login abstraction!!
|
||||
void handleLoginComplete();
|
||||
|
||||
void addOnIdleCallback(const boost::function<void()>& cb); // add a callback to fire (once) when idle
|
||||
|
||||
void purgeCache(); // Clear the local cache.
|
||||
protected:
|
||||
|
||||
@@ -3548,6 +3548,28 @@ void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLInventoryModel::removeItem(const LLUUID& item_id)
|
||||
{
|
||||
LLViewerInventoryItem* item = getItem(item_id);
|
||||
const LLUUID new_parent = findCategoryUUIDForType(LLFolderType::FT_TRASH);
|
||||
if(item && new_parent.notNull() && item->getParentUUID() != new_parent)
|
||||
{
|
||||
LLInventoryModel::update_list_t update;
|
||||
LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(), -1);
|
||||
update.push_back(old_folder);
|
||||
LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
|
||||
update.push_back(new_folder);
|
||||
accountForUpdate(update);
|
||||
|
||||
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
|
||||
new_item->setParent(new_parent);
|
||||
new_item->updateParentOnServer(TRUE);
|
||||
updateItem(new_item);
|
||||
notifyObservers();
|
||||
}
|
||||
}
|
||||
|
||||
const LLUUID &LLInventoryModel::getRootFolderID() const
|
||||
{
|
||||
return mRootFolderID;
|
||||
|
||||
@@ -343,6 +343,7 @@ public:
|
||||
// consistent internal state. No cache accounting, observer
|
||||
// notification, or server update is performed.
|
||||
void deleteObject(const LLUUID& id);
|
||||
void removeItem(const LLUUID& item_id);
|
||||
|
||||
// Delete a particular inventory object by ID, and delete it from
|
||||
// the server. Also updates linked items.
|
||||
|
||||
@@ -865,37 +865,13 @@ public:
|
||||
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;
|
||||
}
|
||||
|
||||
// We're invoked from LLInventoryModel::notifyObservers().
|
||||
// If we now try to remove the inventory item, it will cause a nested
|
||||
// notifyObservers() call, which won't work.
|
||||
// So defer moving the item to trash until viewer gets idle (in a moment).
|
||||
LLAppViewer::instance()->addOnIdleCallback(boost::bind(&LLInventoryModel::removeItem, &gInventory, mObjectID));
|
||||
gInventory.removeObserver(this);
|
||||
if(notify)
|
||||
{
|
||||
gInventory.notifyObservers();
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
protected:
|
||||
|
||||
Reference in New Issue
Block a user