From 4df30e26c68fa1b3a3b3122fbd529aea6b779a24 Mon Sep 17 00:00:00 2001 From: cinder <cinder@cinderblocks.biz> Date: Fri, 2 Dec 2022 13:05:40 -0600 Subject: [PATCH] Plumbing for Local Inventory --- indra/llinventory/llfoldertype.cpp | 1 + indra/llinventory/llfoldertype.h | 2 + .../newview/app_settings/settings_alchemy.xml | 11 +++ indra/newview/llinventorybridge.cpp | 60 +++++++++++- indra/newview/llinventorymodel.cpp | 91 +++++++++++++++++-- indra/newview/llinventorymodel.h | 2 +- indra/newview/llinventorypanel.cpp | 1 + indra/newview/llstartup.cpp | 8 ++ indra/newview/lltooldraganddrop.cpp | 7 +- indra/newview/llviewerfoldertype.cpp | 2 + indra/newview/llviewerinventory.cpp | 16 ++++ indra/newview/llviewerinventory.h | 3 + .../newview/skins/default/xui/en/strings.xml | 1 + 13 files changed, 189 insertions(+), 16 deletions(-) diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index e47ed958e1d..3c20892f53a 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -127,6 +127,7 @@ LLFolderDictionary::LLFolderDictionary() addEntry(LLFolderType::FT_ANIM_OVERRIDES, new FolderEntry("animover", TRUE, FALSE, FALSE)); addEntry(LLFolderType::FT_RLV, new FolderEntry("rlv", TRUE, FALSE, FALSE)); + addEntry(LLFolderType::FT_LOCAL, new FolderEntry("local", TRUE, FALSE, TRUE)); addEntry(LLFolderType::FT_NONE, new FolderEntry("-1", FALSE, FALSE, FALSE)); }; diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h index 600365c9ce3..fd4a7eced52 100644 --- a/indra/llinventory/llfoldertype.h +++ b/indra/llinventory/llfoldertype.h @@ -97,6 +97,8 @@ class LLFolderType FT_TOXIC = 58, FT_RLV = 59, + FT_LOCAL = 69, + FT_SUITCASE = 100, diff --git a/indra/newview/app_settings/settings_alchemy.xml b/indra/newview/app_settings/settings_alchemy.xml index 450f6c99615..21e728cac6c 100644 --- a/indra/newview/app_settings/settings_alchemy.xml +++ b/indra/newview/app_settings/settings_alchemy.xml @@ -749,6 +749,17 @@ <string>String</string> <key>Value</key> <string /> + </map> + <key>LocalInventoryEnabled</key> + <map> + <key>Comment</key> + <string>This enables the local inventory system. (Requires restart.)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> </map> <key>OpenSimSearchURL</key> <map> diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index a408ab965ae..1137d0da82b 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -517,6 +517,8 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& ba move_ids.push_back(item->getUUID()); --update[item->getParentUUID()]; ++update[trash_id]; + if(!gInventory.isObjectDescendentOf(item->getUUID(), gLocalInventory)) + { if(start_new_message) { start_new_message = false; @@ -537,6 +539,7 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& ba gInventory.accountForUpdate(update); update.clear(); } + } } } if(!start_new_message) @@ -558,6 +561,8 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& ba move_ids.push_back(cat->getUUID()); --update[cat->getParentUUID()]; ++update[trash_id]; + if(!gInventory.isObjectDescendentOf(cat->getUUID(), gLocalInventory)) + { if(start_new_message) { start_new_message = false; @@ -577,6 +582,7 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& ba gInventory.accountForUpdate(update); update.clear(); } + } } } if(!start_new_message) @@ -590,12 +596,43 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& ba uuid_vec_t::iterator end = move_ids.end(); for(; it != end; ++it) { + if(gInventory.isObjectDescendentOf(*it, gLocalInventory)) + { + // if it's a category, delete descendents + if(gInventory.getCategory(*it)) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(*it); + cat->setDescendentCount(0); + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(cat->getUUID(), + categories, + items, + false); // include trash? + S32 count = items.size(); + S32 i; + for(i = 0; i < count; ++i) + { + gInventory.deleteObject(items.at(i)->getUUID()); + } + count = categories.size(); + for(i = 0; i < count; ++i) + { + gInventory.deleteObject(categories.at(i)->getUUID()); + } + } + // delete it + gInventory.deleteObject(*it); + } + else + { gInventory.moveObject((*it), trash_id); LLViewerInventoryItem* item = gInventory.getItem(*it); if (item) { model->updateItem(item); } + } } // notify inventory observers. @@ -1840,7 +1877,7 @@ void copy_slurl_to_clipboard_callback_inv(const std::string& slurl) void LLItemBridge::selectItem() { LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem()); - if(item && !item->isFinished()) + if(item && !item->isFinished() && !(gInventory.isObjectDescendentOf(mUUID, gLocalInventory))) { //item->fetchFromServer(); LLInventoryModelBackgroundFetch::instance().start(item->getUUID(), false); @@ -2110,6 +2147,17 @@ BOOL LLItemBridge::removeItem() } // Already in trash if (model->isObjectDescendentOf(mUUID, trash_id)) return FALSE; + else + { + if(gInventory.isObjectDescendentOf(mUUID, gLocalInventory)) + { + LLInventoryModel::LLCategoryUpdate up(item->getParentUUID(), -1); + gInventory.deleteObject(mUUID); + gInventory.accountForUpdate(up); + gInventory.notifyObservers(); + return TRUE; + } + } LLNotification::Params params("ConfirmItemDeleteHasLinks"); params.functor.function(boost::bind(&LLItemBridge::confirmRemoveItem, this, _1, _2)); @@ -2402,7 +2450,7 @@ BOOL LLFolderBridge::isUpToDate() const return FALSE; } - return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; + return (category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) || (mUUID == gLocalInventory) || (gInventory.isObjectDescendentOf(mUUID, gLocalInventory)); } BOOL LLFolderBridge::isItemCopyable() const @@ -4362,8 +4410,12 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items { // it's all on its way - add an observer, and the inventory will call done for us when everything is here. gInventory.addObserver(fetch); - } - } + } + if (mUUID == gLocalInventory) + { + items.clear(); + } +} } void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index c9978a402c7..d2039136c6e 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -266,14 +266,33 @@ void LLInventoryModel::cleanupInventory() // This is a convenience function to check if one object has a parent // chain up to the category specified by UUID. BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id, - const LLUUID& cat_id) const + const LLUUID& cat_id, + const bool break_on_recursion) const { if (obj_id == cat_id) return TRUE; + if (cat_id.isNull()) return FALSE; + U32 depthCounter = 0; + const LLInventoryObject* obj = getObject(obj_id); while(obj) { const LLUUID& parent_id = obj->getParentUUID(); + if(break_on_recursion) + { + if (depthCounter++ > 100) + { + LL_WARNS(LOG_INV) << "Possibly recursive parenting!" << LL_ENDL; + LL_INFOS(LOG_INV) << obj->getName() << " : " << obj->getUUID() << " : " << parent_id << LL_ENDL; + return FALSE; + } + if (parent_id == obj->getUUID()) + { + // infinite loop... same thing as having no parent. + LL_WARNS(LOG_INV) << "Object has itself as parent! " << parent_id.asString() << ", " << obj->getName() << LL_ENDL; + return FALSE; + } + } if( parent_id.isNull() ) { return FALSE; @@ -727,7 +746,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, return id; } - if (preferred_type != LLFolderType::FT_NONE) + if (preferred_type != LLFolderType::FT_NONE && preferred_type != LLFolderType::FT_LOCAL) { // Ultimately this should only be done for non-singleton // types. Requires back-end changes to guarantee that others @@ -745,7 +764,21 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, { name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type)); } - + + // Local Inventory is local as such, finish up before server comms. + if (preferred_type == LLFolderType::FT_LOCAL || parent_id.isNull() || parent_id == gLocalInventory) + { + LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID()); + cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will increase version by 1 + cat->setDescendentCount(0); + LLCategoryUpdate update(cat->getParentUUID(), 1); + accountForUpdate(update); + updateCategory(cat); + + return id; + } + + // back to our regularly scheduled programming LLViewerRegion* viewer_region = gAgent.getRegion(); std::string url; if ( viewer_region ) @@ -1468,6 +1501,8 @@ void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item, const LLUUID& new_parent_id, BOOL restamp) { + bool send_parent_update = gInventory.isObjectDescendentOf(item->getUUID(), gInventory.getRootFolderID()); + if (item->getParentUUID() == new_parent_id) { LL_DEBUGS(LOG_INV) << "'" << item->getName() << "' (" << item->getUUID() @@ -1487,7 +1522,10 @@ void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item, LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); new_item->setParent(new_parent_id); - new_item->updateParentOnServer(restamp); + + if(send_parent_update) { + new_item->updateParentOnServer(restamp); + } updateItem(new_item); notifyObservers(); } @@ -2280,6 +2318,8 @@ LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren( bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const { + if((cat_id == gLocalInventory) || gInventory.isObjectDescendentOf(cat_id, gLocalInventory)) return true; + LLViewerInventoryCategory* cat = getCategory(cat_id); if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN)) { @@ -2525,7 +2565,7 @@ bool LLInventoryModel::loadSkeleton( // reason (e.g. one of the descendents was a broken link). for (cat_set_t::iterator invalid_cat_it = invalid_categories.begin(); invalid_cat_it != invalid_categories.end(); - invalid_cat_it++) + ++invalid_cat_it) { LLViewerInventoryCategory* cat = (*invalid_cat_it).get(); cat->setVersion(NO_VERSION); @@ -3860,10 +3900,45 @@ void LLInventoryModel::removeCategory(const LLUUID& category_id) LLViewerInventoryCategory* cat = getCategory(category_id); if (cat) { - const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH); - if (trash_id.notNull()) + if(gInventory.isObjectDescendentOf(cat->getUUID(), gLocalInventory)) { - changeCategoryParent(cat, trash_id, TRUE); + S32 descendents = cat->getDescendentCount(); + if(descendents > 0) + { + LLInventoryModel::LLCategoryUpdate up(cat->getUUID(), -descendents); + gInventory.accountForUpdate(up); + } + cat->setDescendentCount(0); + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(cat->getUUID(), + categories, + items, + false); // include trash? + S32 count = items.size(); + S32 i; + for(i = 0; i < count; ++i) + { + gInventory.deleteObject(items.at(i)->getUUID()); + } + count = categories.size(); + for(i = 0; i < count; ++i) + { + gInventory.deleteObject(categories.at(i)->getUUID()); + } + + LLInventoryModel::LLCategoryUpdate up(cat->getParentUUID(), -descendents); + gInventory.deleteObject(cat->getUUID()); + gInventory.accountForUpdate(up); + gInventory.notifyObservers(); + } + else + { + const LLUUID& trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH); + if (trash_id.notNull()) + { + changeCategoryParent(cat, trash_id, TRUE); + } } } diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index c48e4a81d03..f8c9905ca61 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -295,7 +295,7 @@ class LLInventoryModel item_array_t collectLinksTo(const LLUUID& item_id); // Check if one object has a parent chain up to the category specified by UUID. - BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id) const; + BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id, const bool break_on_recursion = false) const; enum EAnscestorResult{ ANSCESTOR_OK = 0, diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 761f2bf69b3..b2c33a0daa6 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -2157,5 +2157,6 @@ namespace LLInitParam declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_STOCK), LLFolderType::FT_MARKETPLACE_STOCK); declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_VERSION), LLFolderType::FT_MARKETPLACE_VERSION); declare(LLFolderType::lookup(LLFolderType::FT_SUITCASE), LLFolderType::FT_SUITCASE); + declare(LLFolderType::lookup(LLFolderType::FT_LOCAL), LLFolderType::FT_LOCAL); } } diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 89bddd44c6a..edad0e3e8e8 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -228,6 +228,8 @@ S32 gMaxAgentGroups; const std::string SCREEN_HOME_FILENAME = "screen_home%s.png"; const std::string SCREEN_LAST_FILENAME = "screen_last%s.png"; +extern const char* const LOCAL_INVENTORY_FOLDER_NAME; + LLPointer<LLViewerTexture> gStartTexture; // @@ -1930,6 +1932,12 @@ bool idle_startup() LL_INFOS() << "Requesting Agent Data" << LL_ENDL; gAgent.sendAgentDataUpdateRequest(); display_startup(); + if (gSavedSettings.getBOOL("LocalInventoryEnabled")) + { + gLocalInventory = gInventory.createNewCategory(LLUUID::null, + LLFolderType::FT_LOCAL, + LOCAL_INVENTORY_FOLDER_NAME); + } // Create the inventory views LL_INFOS() << "Creating Inventory Views" << LL_ENDL; LLFloaterReg::getInstance("inventory"); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 83d54fe0971..20b4fc2b497 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1481,7 +1481,8 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL if (!item || !obj) return ACCEPT_NO; // HACK: downcast LLViewerInventoryItem* vitem = (LLViewerInventoryItem*)item; - if (!vitem->isFinished() && (type != DAD_CATEGORY)) + if (!vitem->isFinished() && (type != DAD_CATEGORY) + && !(gInventory.isObjectDescendentOf(vitem->getUUID(), gLocalInventory))) { // Note: for DAD_CATEGORY we assume that folder version check passed and folder // is complete, meaning that items inside are up to date. @@ -1996,7 +1997,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezScript( LLViewerInventoryItem* item; LLViewerInventoryCategory* cat; locateInventory(item, cat); - if (!item || !item->isFinished()) return ACCEPT_NO; + if (!item || (!item->isFinished() && !(gInventory.isObjectDescendentOf(item->getUUID(), gLocalInventory)))) return ACCEPT_NO; EAcceptance rv = willObjectAcceptInventory(obj, item); if(drop && (ACCEPT_YES_SINGLE <= rv)) { @@ -2034,7 +2035,7 @@ EAcceptance LLToolDragAndDrop::dad3dApplyToObject( LLViewerInventoryItem* item; LLViewerInventoryCategory* cat; locateInventory(item, cat); - if (!item || !item->isFinished()) return ACCEPT_NO; + if( !item || (!item->isFinished() && !(gInventory.isObjectDescendentOf(item->getUUID(), gLocalInventory))) ) return ACCEPT_NO; EAcceptance rv = willObjectAcceptInventory(obj, item); if((mask & MASK_CONTROL)) { diff --git a/indra/newview/llviewerfoldertype.cpp b/indra/newview/llviewerfoldertype.cpp index 44ac1a99ce3..908efd51f11 100644 --- a/indra/newview/llviewerfoldertype.cpp +++ b/indra/newview/llviewerfoldertype.cpp @@ -150,6 +150,8 @@ LLViewerFolderDictionary::LLViewerFolderDictionary() addEntry(LLFolderType::FT_NONE, new ViewerFolderEntry("New Folder", "Inv_FolderOpen", "Inv_FolderClosed", FALSE, false, "default")); addEntry(LLFolderType::FT_TOXIC, new ViewerFolderEntry("Firstorm", "Inv_FolderOpenToxic", "Inv_FolderClosedToxic", FALSE, false)); addEntry(LLFolderType::FT_RLV, new ViewerFolderEntry("RlvRoot", "Inv_SysOpen", "Inv_SysClosed", FALSE, false)); + addEntry(LLFolderType::FT_LOCAL, new ViewerFolderEntry("Local Inventory", "Inv_SysOpen", "Inv_SysClosed", FALSE, true)); + for (U32 type = (U32)LLFolderType::FT_ENSEMBLE_START; type <= (U32)LLFolderType::FT_ENSEMBLE_END; ++type) { diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 565b9620765..f174835eccd 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -86,6 +86,9 @@ static const char * const LOG_NOTECARD("copy_inventory_from_notecard"); static const std::string INV_OWNER_ID("owner_id"); static const std::string INV_VERSION("version"); +LLUUID gLocalInventory; +const char* const LOCAL_INVENTORY_FOLDER_NAME("Local Inventory"); + #if 1 // *TODO$: LLInventoryCallback should be deprecated to conform to the new boost::bind/coroutine model. // temp code in transition @@ -184,6 +187,8 @@ LLLocalizedInventoryItemsDictionary::LLLocalizedInventoryItemsDictionary() mInventoryItemsDict["Speech Gestures"] = LLTrans::getString("Speech Gestures"); mInventoryItemsDict["Common Gestures"] = LLTrans::getString("Common Gestures"); + mInventoryItemsDict[LOCAL_INVENTORY_FOLDER_NAME] = LLTrans::getString(LOCAL_INVENTORY_FOLDER_NAME); + //predefined gestures //male @@ -418,6 +423,11 @@ void LLViewerInventoryItem::updateServer(BOOL is_new) const << LL_ENDL; return; } + + if((mParentUUID == gLocalInventory) || (gInventory.isObjectDescendentOf(mUUID, gLocalInventory))) + { + return; + } if(gAgent.getID() != mPermissions.getOwner()) { // *FIX: deal with this better. @@ -572,6 +582,8 @@ BOOL LLViewerInventoryItem::importLegacyStream(std::istream& input_stream) void LLViewerInventoryItem::updateParentOnServer(BOOL restamp) const { + if (gInventory.isObjectDescendentOf(mUUID, gLocalInventory)) return; + LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_MoveInventoryItem); msg->nextBlockFast(_PREHASH_AgentData); @@ -650,6 +662,8 @@ void LLViewerInventoryCategory::packMessage(LLMessageSystem* msg) const void LLViewerInventoryCategory::updateParentOnServer(BOOL restamp) const { + if(gInventory.isObjectDescendentOf(mUUID, gLocalInventory)) return; + LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_MoveInventoryFolder); msg->nextBlockFast(_PREHASH_AgentData); @@ -707,6 +721,8 @@ void LLViewerInventoryCategory::setVersion(S32 version) bool LLViewerInventoryCategory::fetch() { + if((mUUID == gLocalInventory) || (gInventory.isObjectDescendentOf(mUUID, gLocalInventory))) return false; + if((VERSION_UNKNOWN == getVersion()) && mDescendentsRequested.hasExpired()) //Expired check prevents multiple downloads. { diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 322df060250..01081977f39 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -42,6 +42,9 @@ class LLViewerInventoryCategory; class LLInventoryCallback; class LLAvatarName; +extern LLUUID gLocalInventory; +extern const char* const LOCAL_INVENTORY_FOLDER_NAME; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLViewerInventoryItem // diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 46eb2ca80b1..d261ce670a3 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3872,6 +3872,7 @@ Abuse Report</string> <string name="Common Gestures">Common Gestures</string> <!-- Additional inventory entries --> <string name="RlvRoot">#RLV</string> + <string name="Local Inventory">Local Inventory</string> <!-- gestures --> <string name="Male - Excuse me">Male - Excuse me</string> <string name="Male - Get lost">Male - Get lost</string> -- GitLab