diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 11fb33c26..1b3fae454 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3743,6 +3743,7 @@ void LLAppViewer::idle() gEventNotifier.update(); gIdleCallbacks.callFunctions(); + gInventory.idleNotifyObservers(); } if (gDisconnected) diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index c6c205f86..94bb6f853 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -221,22 +221,40 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item) LLInventoryModel gInventory; // Default constructor -LLInventoryModel::LLInventoryModel() : - mModifyMask(LLInventoryObserver::ALL), +LLInventoryModel::LLInventoryModel() +: mModifyMask(LLInventoryObserver::ALL), + mChangedItemIDs(), + mCategoryMap(), + mItemMap(), + mCategoryLock(), + mItemLock(), mLastItem(NULL), + mParentChildCategoryTree(), + mParentChildItemTree(), + mObservers(), + mIsNotifyObservers(FALSE), mIsAgentInvUsable(false) { } // Destroys the object LLInventoryModel::~LLInventoryModel() +{ + cleanupInventory(); +} + +void LLInventoryModel::cleanupInventory() { empty(); - for (observer_list_t::iterator iter = mObservers.begin(); - iter != mObservers.end(); ++iter) + // Deleting one observer might erase others from the list, so always pop off the front + while (!mObservers.empty()) { - delete *iter; + observer_list_t::iterator iter = mObservers.begin(); + LLInventoryObserver* observer = *iter; + mObservers.erase(iter); + delete observer; } + mObservers.clear(); } // This is a convenience function to check if one object has a parent @@ -245,6 +263,7 @@ BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id, const BOOL break_on_recursion) const { + if (obj_id == cat_id) return TRUE; LLInventoryObject* obj = getObject(obj_id); int depthCounter = 0; while(obj) @@ -758,12 +777,23 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) return mask; } + // We're hiding mesh types +#if 0 + if (item->getType() == LLAssetType::AT_MESH) + { + return mask; + } +#endif + LLViewerInventoryItem* old_item = getItem(item->getUUID()); + LLPointer new_item; if(old_item) { - // We already have an old item, modify it's values + // We already have an old item, modify its values + new_item = old_item; LLUUID old_parent_id = old_item->getParentUUID(); LLUUID new_parent_id = item->getParentUUID(); + if(old_parent_id != new_parent_id) { // need to update the parent-child tree @@ -790,7 +820,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) else { // Simply add this item - LLPointer new_item = new LLViewerInventoryItem(item); + new_item = new LLViewerInventoryItem(item); addItem(new_item); if(item->getParentUUID().isNull()) @@ -850,11 +880,40 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) } mask |= LLInventoryObserver::ADD; } - if(item->getType() == LLAssetType::AT_CALLINGCARD) + if(new_item->getType() == LLAssetType::AT_CALLINGCARD) { mask |= LLInventoryObserver::CALLING_CARD; + // Handle user created calling cards. + // Target ID is stored in the description field of the card. + LLUUID id; + std::string desc = new_item->getDescription(); + BOOL isId = desc.empty() ? FALSE : id.set(desc, FALSE); + if (isId) + { + // Valid UUID; set the item UUID and rename it + new_item->setCreator(id); + std::string avatar_name; + + if (gCacheName->getFullName(id, avatar_name)) + { + new_item->rename(avatar_name); + mask |= LLInventoryObserver::LABEL; + } + else + { + // Fetch the current name + gCacheName->get(id, FALSE, + boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(), + _1, _2, _3)); + } + + } } - addChangedMask(mask, item->getUUID()); + /*else if (new_item->getType() == LLAssetType::AT_GESTURE) + { + mask |= LLInventoryObserver::GESTURE; + }*/ + addChangedMask(mask, new_item->getUUID()); return mask; } @@ -1001,39 +1060,40 @@ void LLInventoryModel::deleteObject(const LLUUID& id) return; } - lldebugs << "Deleting inventory object " << id << llendl; - mLastItem = NULL; - LLUUID parent_id = obj->getParentUUID(); - mCategoryMap.erase(id); - mItemMap.erase(id); - //mInventory.erase(id); - item_array_t* item_list = getUnlockedItemArray(parent_id); - if(item_list) - { - LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj); - item_list->removeObj(item); - } - cat_array_t* cat_list = getUnlockedCatArray(parent_id); - if(cat_list) - { - LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); - cat_list->removeObj(cat); - } - item_list = getUnlockedItemArray(id); - if(item_list) - { - delete item_list; - mParentChildItemTree.erase(id); - } - cat_list = getUnlockedCatArray(id); - if(cat_list) - { - delete cat_list; - mParentChildCategoryTree.erase(id); - } - addChangedMask(LLInventoryObserver::REMOVE, id); - obj = NULL; // delete obj + lldebugs << "Deleting inventory object " << id << llendl; + mLastItem = NULL; + LLUUID parent_id = obj->getParentUUID(); + mCategoryMap.erase(id); + mItemMap.erase(id); + //mInventory.erase(id); + item_array_t* item_list = getUnlockedItemArray(parent_id); + if(item_list) + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj); + item_list->removeObj(item); } + cat_array_t* cat_list = getUnlockedCatArray(parent_id); + if(cat_list) + { + LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); + cat_list->removeObj(cat); + } + item_list = getUnlockedItemArray(id); + if(item_list) + { + delete item_list; + mParentChildItemTree.erase(id); + } + cat_list = getUnlockedCatArray(id); + if(cat_list) + { + delete cat_list; + mParentChildCategoryTree.erase(id); + } + addChangedMask(LLInventoryObserver::REMOVE, id); + obj = NULL; // delete obj + gInventory.notifyObservers(); +} // Delete a particular inventory item by ID, and remove it from the server. void LLInventoryModel::purgeObject(const LLUUID &id) @@ -1219,39 +1279,56 @@ BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const return mObservers.find(observer) != mObservers.end(); } -// Call this method when it's time to update everyone on a new state, -// by default, the inventory model will not update observers -// automatically. -// The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328] -void LLInventoryModel::notifyObservers(const std::string service_name) +void LLInventoryModel::idleNotifyObservers() { + if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0)) + { + return; + } + notifyObservers(); +} + +// Call this method when it's time to update everyone on a new state. +void LLInventoryModel::notifyObservers() +{ + if (mIsNotifyObservers) + { + // Within notifyObservers, something called notifyObservers + // again. This type of recursion is unsafe because it causes items to be + // processed twice, and this can easily lead to infinite loops. + llwarns << "Call was made to notifyObservers within notifyObservers!" << llendl; + return; + } + + mIsNotifyObservers = TRUE; for (observer_list_t::iterator iter = mObservers.begin(); iter != mObservers.end(); ) { LLInventoryObserver* observer = *iter; - if (service_name.empty()) - { - observer->changed(mModifyMask); - } - else - { - observer->mMessageName = service_name; - observer->changed(mModifyMask); - } + observer->changed(mModifyMask); - // safe way to incrament since changed may delete entries! (@!##%@!@&*!) + // safe way to increment since changed may delete entries! (@!##%@!@&*!) iter = mObservers.upper_bound(observer); } mModifyMask = LLInventoryObserver::NONE; mChangedItemIDs.clear(); + mIsNotifyObservers = FALSE; } // store flag for change // and id of object change applies to void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) { + if (mIsNotifyObservers) + { + // Something marked an item for change within a call to notifyObservers + // (which is in the process of processing the list of items marked for change). + // This means the change may fail to be processed. + llwarns << "Adding changed mask within notify observers! Change will likely be lost." << llendl; + } + mModifyMask |= mask; if (referent.notNull()) { @@ -1266,32 +1343,7 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) } } -// This method to prepares a set of mock inventory which provides -// minimal functionality before the actual arrival of inventory. -/* -void LLInventoryModel::mock(const LLUUID& root_id) -{ - llinfos << "LLInventoryModel::mock() " << root_id << llendl; - if(root_id.isNull()) - { - llwarns << "Not a valid root id" << llendl; - return; - } - LLPointer cat = new LLViewerInventoryCategory( - root_id, - LLUUID::null, - LLAssetType::AT_CATEGORY, - NEW_CATEGORY_NAMES[LLFolderType::FT_ROOT_CATEGORY], - gAgent.getID()); - addCategory(cat); - gInventory.buildParentChildMap(); -} -*/ - -//If we get back a normal response, handle it here -// Note: this is the responder used in "fetchInventory" cap, -// this is not responder for "WebFetchInventoryDescendents" or "agent/inventory" cap - +// If we get back a normal response, handle it here void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content) { start_new_inventory_observer(); @@ -1352,16 +1404,16 @@ void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content) { changes |= gInventory.updateItem(*it); } - gInventory.notifyObservers("fetchinventory"); + gInventory.notifyObservers(); gViewerWindow->getWindow()->decBusyCount(); } //If we get back an error (not found, etc...), handle it here void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::string& reason) { - LL_INFOS("Inventory") << "fetchInventory::error " - << status << ": " << reason << LL_ENDL; - gInventory.notifyObservers("fetchinventory"); + llinfos << "fetchInventory::error " + << status << ": " << reason << llendl; + gInventory.notifyObservers(); } bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const @@ -1462,7 +1514,7 @@ void fetchDescendentsResponder::result(const LLSD& content) titem->setParent(lost_uuid); titem->updateParentOnServer(FALSE); gInventory.updateItem(titem); - gInventory.notifyObservers("fetchDescendents"); + gInventory.notifyObservers(); } } @@ -1539,7 +1591,7 @@ void fetchDescendentsResponder::result(const LLSD& content) LLInventoryModel::stopBackgroundFetch(); } - gInventory.notifyObservers("fetchDescendents"); + gInventory.notifyObservers(); } //If we get back an error (not found, etc...), handle it here @@ -1572,7 +1624,7 @@ void fetchDescendentsResponder::error(U32 status, const std::string& reason) LLInventoryModel::stopBackgroundFetch(); } } - gInventory.notifyObservers("fetchDescendents"); + gInventory.notifyObservers(); } //static Bundle up a bunch of requests to send all at once. @@ -1954,6 +2006,11 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) //llinfos << "LLInventoryModel::addCategory()" << llendl; if(category) { + // We aren't displaying the Meshes folder + if (category->getPreferredType() == LLFolderType::FT_MESH) + { + return; + } // Insert category uniquely into the map mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one //mInventory[category->getUUID()] = category; @@ -1962,7 +2019,7 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) void LLInventoryModel::addItem(LLViewerInventoryItem* item) { - //llinfos << "LLInventoryModel::addItem()" << llendl; + llassert(item); if(item) { // This can happen if assettype enums from llassettype.h ever change. @@ -2030,8 +2087,8 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const descendents_actual += update.mDescendentDelta; cat->setDescendentCount(descendents_actual); cat->setVersion(++version); - llinfos << "accounted: '" << cat->getName() << "' " - << version << " with " << descendents_actual + lldebugs << "accounted: '" << cat->getName() << "' " + << version << " with " << descendents_actual << " descendents." << llendl; } } @@ -3214,7 +3271,9 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) } LLUUID tid; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid); +#ifndef LL_RELEASE_FOR_DOWNLOAD llinfos << "Bulk inventory: " << tid << llendl; +#endif update_map_t update; cat_array_t folders; @@ -3342,7 +3401,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) LLInventoryView::sWearNewClothing = FALSE; } - if (tid == LLInventoryView::sWearNewClothingTransactionID) + if (tid.notNull() && tid == LLInventoryView::sWearNewClothingTransactionID) { count = wearable_ids.size(); for (i = 0; i < count; ++i) @@ -3428,6 +3487,9 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) { cat->setVersion(version); cat->setDescendentCount(descendents); + // Get this UUID on the changed list so that whatever's listening for it + // will get triggered. + gInventory.addChangedMask(LLInventoryObserver::INTERNAL, cat->getUUID()); } gInventory.notifyObservers(); } diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index f8e927d4a..d3d525655 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -131,6 +131,7 @@ public: public: LLInventoryModel(); ~LLInventoryModel(); + void cleanupInventory(); // //protected: // @@ -375,7 +376,7 @@ public: LLUUID createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& name); - + // Internal methods that add inventory and make sure that all of // the internal data structures are consistent. These methods // should be passed pointers of newly created objects, and the @@ -446,10 +447,13 @@ public: **/ public: - // Call to explicitly update everyone on a new state. The optional argument - // 'service_name' is used by Agent Inventory Service [DEV-20328] - void notifyObservers(const std::string service_name=""); + // Called by the idle loop. Only updates if new state is detected. Call + // notifyObservers() manually to update regardless of whether state change + // has been indicated. + void idleNotifyObservers(); + // Call to explicitly update everyone on a new state. + void notifyObservers(); // Allows outsiders to tell the inventory if something has // been changed 'under the hood', but outside the control of the // inventory. The next notify will include that notification. @@ -459,6 +463,9 @@ protected: // Updates all linked items pointing to this id. void addChangedMaskForLinks(const LLUUID& object_id, U32 mask); private: + // Flag set when notifyObservers is being called, to look for bugs + // where it's called recursively. + BOOL mIsNotifyObservers; // Variables used to track what has changed since the last notify. U32 mModifyMask; changed_items_t mChangedItemIDs; diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 2a521e018..022398eb9 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1077,3 +1077,9 @@ LLViewerInventoryCategory *LLViewerInventoryItem::getLinkedCategory() const } return NULL; } +void LLViewerInventoryItem::onCallingCardNameLookup(const LLUUID& id, const std::string& name, bool is_group) +{ + rename(name); + gInventory.addChangedMask(LLInventoryObserver::LABEL, getUUID()); + gInventory.notifyObservers(); +} diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 00782eb25..bb63824bb 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -145,6 +145,8 @@ public: LLViewerInventoryItem *getLinkedItem() const; LLViewerInventoryCategory *getLinkedCategory() const; + // callback + void onCallingCardNameLookup(const LLUUID& id, const std::string& name, bool is_group); protected: BOOL mIsComplete; LLTransactionID mTransactionID;