From 2d39f05cc1403da307be05edc972d2ebcec6cfc7 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Fri, 11 Aug 2023 18:07:13 -0700 Subject: SL-20109: Now that it seems that the tool might be useful, make a new version with a proper name and start worknig on it. Prototype bulky thumbs one will be removed after new version is working --- .../newview/llfloaterinventorythumbnailshelper.cpp | 390 +++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 indra/newview/llfloaterinventorythumbnailshelper.cpp (limited to 'indra/newview/llfloaterinventorythumbnailshelper.cpp') diff --git a/indra/newview/llfloaterinventorythumbnailshelper.cpp b/indra/newview/llfloaterinventorythumbnailshelper.cpp new file mode 100644 index 0000000000..31ec144615 --- /dev/null +++ b/indra/newview/llfloaterinventorythumbnailshelper.cpp @@ -0,0 +1,390 @@ +/** + * @file llfloaterinventorythumbnailshelper.cpp + * @author Callum Prentice + * @brief LLFloaterInventoryThumbnailsHelper class implementation + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/** + * Floater that appears when buying an object, giving a preview + * of its contents and their permissions. + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterinventorythumbnailshelper.h" +#include "lluictrlfactory.h" +#include "llclipboard.h" +#include "llinventorymodel.h" +#include "llinventoryfunctions.h" +#include "lltexteditor.h" +#include "llmediactrl.h" +#include "lluuid.h" +#include "llaisapi.h" + +LLFloaterInventoryThumbnailsHelper::LLFloaterInventoryThumbnailsHelper(const LLSD& key) + : LLFloater("floater_inventory_thumbnails_helper") +{ +} + +LLFloaterInventoryThumbnailsHelper::~LLFloaterInventoryThumbnailsHelper() +{ +} + +BOOL LLFloaterInventoryThumbnailsHelper::postBuild() +{ + mPasteItemsBtn = getChild("paste_items_btn"); + mPasteItemsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onPasteItems, this)); + + mPasteTexturesBtn = getChild("paste_textures_btn"); + mPasteTexturesBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onPasteTextures, this)); + + mOutputLog = getChild("output_log"); + mOutputLog->setMaxTextLength(0xffff * 0x10); + + mMergeItemsTexturesBtn = getChild("merge_items_textures"); + mMergeItemsTexturesBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onMergeItemsTextures, this)); + mMergeItemsTexturesBtn->setEnabled(false); + + mWriteThumbnailsBtn = getChild("write_items_thumbnails"); + mWriteThumbnailsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onWriteThumbnails, this)); + mWriteThumbnailsBtn->setEnabled(false); + + return true; +} + +void LLFloaterInventoryThumbnailsHelper::recordInventoryItemEntry(LLViewerInventoryItem* item) +{ + const std::string name = item->getName(); + + std::map::iterator iter = mItemNamesIDs.find(name); + if (iter == mItemNamesIDs.end()) + { + LLUUID id = item->getUUID(); + mItemNamesIDs.insert({name, id}); + + mOutputLog->appendText( + STRINGIZE( + "ITEM " << mItemNamesIDs.size() << "> " << + name << + //" | " << + //id.asString() << + std::endl + ), false); + } + else + { + // dupe - do not save + } +} + +void LLFloaterInventoryThumbnailsHelper::onPasteItems() +{ + if (!LLClipboard::instance().hasContents()) + { + return; + } + + mOutputLog->appendText( + STRINGIZE( + "\n==== Pasting items from inventory ====" << + std::endl + ), false); + + std::vector objects; + LLClipboard::instance().pasteFromClipboard(objects); + size_t count = objects.size(); + + for (size_t i = 0; i < count; i++) + { + const LLUUID& entry = objects.at(i); + + const LLInventoryCategory* cat = gInventory.getCategory(entry); + if (cat) + { + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + LLIsType is_object(LLAssetType::AT_OBJECT); + gInventory.collectDescendentsIf(cat->getUUID(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_object); + + LLIsType is_bodypart(LLAssetType::AT_BODYPART); + gInventory.collectDescendentsIf(cat->getUUID(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_bodypart); + + for (size_t i = 0; i < item_array.size(); i++) + { + LLViewerInventoryItem* item = item_array.at(i); + recordInventoryItemEntry(item); + } + } + + LLViewerInventoryItem* item = gInventory.getItem(entry); + if (item) + { + const LLAssetType::EType item_type = item->getType(); + if (item_type == LLAssetType::AT_OBJECT || item_type == LLAssetType::AT_BODYPART) + { + recordInventoryItemEntry(item); + } + } + } + + mOutputLog->setCursorAndScrollToEnd(); + + mMergeItemsTexturesBtn->setEnabled(true); +} + +void LLFloaterInventoryThumbnailsHelper::recordTextureItemEntry(LLViewerInventoryItem* item) +{ + const std::string name = item->getName(); + + std::map::iterator iter = mTextureNamesIDs.find(name); + if (iter == mTextureNamesIDs.end()) + { + LLUUID id = item->getAssetUUID(); + mTextureNamesIDs.insert({name, id}); + + mOutputLog->appendText( + STRINGIZE( + "TEXTURE " << mTextureNamesIDs.size() << "> " << + name << + //" | " << + //id.asString() << + std::endl + ), false); + } + else + { + // dupe - do not save + } +} + +void LLFloaterInventoryThumbnailsHelper::onPasteTextures() +{ + if (!LLClipboard::instance().hasContents()) + { + return; + } + + mOutputLog->appendText( + STRINGIZE( + "\n==== Pasting textures from inventory ====" << + std::endl + ), false); + + std::vector objects; + LLClipboard::instance().pasteFromClipboard(objects); + size_t count = objects.size(); + + for (size_t i = 0; i < count; i++) + { + const LLUUID& entry = objects.at(i); + + const LLInventoryCategory* cat = gInventory.getCategory(entry); + if (cat) + { + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + LLIsType is_object(LLAssetType::AT_TEXTURE); + gInventory.collectDescendentsIf(cat->getUUID(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_object); + + for (size_t i = 0; i < item_array.size(); i++) + { + LLViewerInventoryItem* item = item_array.at(i); + recordTextureItemEntry(item); + } + } + + LLViewerInventoryItem* item = gInventory.getItem(entry); + if (item) + { + const LLAssetType::EType item_type = item->getType(); + if (item_type == LLAssetType::AT_TEXTURE) + { + recordTextureItemEntry(item); + } + } + } + + mOutputLog->setCursorAndScrollToEnd(); + + mMergeItemsTexturesBtn->setEnabled(true); +} + +void LLFloaterInventoryThumbnailsHelper::onMergeItemsTextures() +{ + mOutputLog->appendText( + STRINGIZE( + "\n==== Matching items and textures for " << + mItemNamesIDs.size() << + " entries ====" << + std::endl + ), false); + + std::map::iterator item_iter = mItemNamesIDs.begin(); + size_t index = 1; + + while (item_iter != mItemNamesIDs.end()) + { + std::string item_name = (*item_iter).first; + + mOutputLog->appendText( + STRINGIZE( + "MATCHING ITEM (" << index++ << "/" << mItemNamesIDs.size() << ") " << item_name << "> " + ), false); + + std::map::iterator texture_iter = mTextureNamesIDs.find(item_name); + if (texture_iter != mTextureNamesIDs.end()) + { + mOutputLog->appendText( + STRINGIZE( + "MATCHED" << + std::endl + ), false); + + mNameItemIDTextureId.insert({item_name, {(*item_iter).second, (*texture_iter).second}}); + } + else + { + mOutputLog->appendText( + STRINGIZE( + "NO MATCH FOUND" << + std::endl + ), false); + } + + ++item_iter; + } + + mOutputLog->appendText( + STRINGIZE( + "==== Matched list of items and textures has " << + mNameItemIDTextureId.size() << + " entries ====" << + std::endl + ), true); + + //std::map>::iterator iter = mNameItemIDTextureId.begin(); + //while (iter != mNameItemIDTextureId.end()) + //{ + // std::string output_line = (*iter).first; + // output_line += "\n"; + // output_line += "item ID: "; + // output_line += ((*iter).second).first.asString(); + // output_line += "\n"; + // output_line += "thumbnail texture ID: "; + // output_line += ((*iter).second).second.asString(); + // output_line += "\n"; + // mOutputLog->appendText(output_line, true); + + // ++iter; + //} + mOutputLog->setCursorAndScrollToEnd(); + + mWriteThumbnailsBtn->setEnabled(true); +} + +#if 1 +// *TODO$: LLInventoryCallback should be deprecated to conform to the new boost::bind/coroutine model. +// temp code in transition +void bulkyInventoryCb(LLPointer cb, LLUUID id) +{ + if (cb.notNull()) + { + cb->fire(id); + } +} +#endif + +bool writeThumbnailID(LLUUID item_id, LLUUID thumbnail_asset_id) +{ + if (AISAPI::isAvailable()) + { + + LLSD updates; + updates["thumbnail"] = LLSD().with("asset_id", thumbnail_asset_id.asString()); + + LLPointer cb; + + AISAPI::completion_t cr = boost::bind(&bulkyInventoryCb, cb, _1); + AISAPI::UpdateItem(item_id, updates, cr); + + return true; + } + else + { + LL_WARNS() << "Unable to write inventory thumbnail because the AIS API is not available" << LL_ENDL; + return false; + } +} + +void LLFloaterInventoryThumbnailsHelper::onWriteThumbnails() +{ + mOutputLog->appendText( + STRINGIZE( + "\n==== Writing thumbnails for " << + mNameItemIDTextureId.size() << + " entries ====" << + std::endl + ), false); + + std::map>::iterator iter = mNameItemIDTextureId.begin(); + size_t index = 1; + + while (iter != mNameItemIDTextureId.end()) + { + mOutputLog->appendText( + STRINGIZE( + "WRITING THUMB (" << index++ << "/" << mNameItemIDTextureId.size() << ")> " << + (*iter).first << + "\n" << + "item ID: " << + ((*iter).second).first.asString() << + "\n" << + "thumbnail texture ID: " << + ((*iter).second).second.asString() << + "\n" + ), true); + + LLUUID item_id = ((*iter).second).first; + LLUUID thumbnail_asset_id = ((*iter).second).second; + + writeThumbnailID(item_id, thumbnail_asset_id); + + ++iter; + } + mOutputLog->setCursorAndScrollToEnd(); +} -- cgit v1.2.3 From abb8d0402c8498dc5ce8dfec1438f2fedbaecce9 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Fri, 11 Aug 2023 18:31:21 -0700 Subject: SL-20109: Some small UI tweaks before moving onto the meat of the functionality --- .../newview/llfloaterinventorythumbnailshelper.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'indra/newview/llfloaterinventorythumbnailshelper.cpp') diff --git a/indra/newview/llfloaterinventorythumbnailshelper.cpp b/indra/newview/llfloaterinventorythumbnailshelper.cpp index 31ec144615..abd5e01e5d 100644 --- a/indra/newview/llfloaterinventorythumbnailshelper.cpp +++ b/indra/newview/llfloaterinventorythumbnailshelper.cpp @@ -62,11 +62,11 @@ BOOL LLFloaterInventoryThumbnailsHelper::postBuild() mOutputLog = getChild("output_log"); mOutputLog->setMaxTextLength(0xffff * 0x10); - mMergeItemsTexturesBtn = getChild("merge_items_textures"); - mMergeItemsTexturesBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onMergeItemsTextures, this)); - mMergeItemsTexturesBtn->setEnabled(false); + //mMergeItemsTexturesBtn = getChild("merge_items_textures"); + //mMergeItemsTexturesBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onMergeItemsTextures, this)); + //mMergeItemsTexturesBtn->setEnabled(false); - mWriteThumbnailsBtn = getChild("write_items_thumbnails"); + mWriteThumbnailsBtn = getChild("write_thumbnails_btn"); mWriteThumbnailsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onWriteThumbnails, this)); mWriteThumbnailsBtn->setEnabled(false); @@ -158,8 +158,6 @@ void LLFloaterInventoryThumbnailsHelper::onPasteItems() } mOutputLog->setCursorAndScrollToEnd(); - - mMergeItemsTexturesBtn->setEnabled(true); } void LLFloaterInventoryThumbnailsHelper::recordTextureItemEntry(LLViewerInventoryItem* item) @@ -240,11 +238,9 @@ void LLFloaterInventoryThumbnailsHelper::onPasteTextures() } mOutputLog->setCursorAndScrollToEnd(); - - mMergeItemsTexturesBtn->setEnabled(true); } -void LLFloaterInventoryThumbnailsHelper::onMergeItemsTextures() +void LLFloaterInventoryThumbnailsHelper::mergeItemsTextures() { mOutputLog->appendText( STRINGIZE( @@ -320,7 +316,7 @@ void LLFloaterInventoryThumbnailsHelper::onMergeItemsTextures() #if 1 // *TODO$: LLInventoryCallback should be deprecated to conform to the new boost::bind/coroutine model. // temp code in transition -void bulkyInventoryCb(LLPointer cb, LLUUID id) +void inventoryThumbnailsHelperCb(LLPointer cb, LLUUID id) { if (cb.notNull()) { @@ -329,7 +325,7 @@ void bulkyInventoryCb(LLPointer cb, LLUUID id) } #endif -bool writeThumbnailID(LLUUID item_id, LLUUID thumbnail_asset_id) +bool writeInventoryThumbnailID(LLUUID item_id, LLUUID thumbnail_asset_id) { if (AISAPI::isAvailable()) { @@ -339,7 +335,7 @@ bool writeThumbnailID(LLUUID item_id, LLUUID thumbnail_asset_id) LLPointer cb; - AISAPI::completion_t cr = boost::bind(&bulkyInventoryCb, cb, _1); + AISAPI::completion_t cr = boost::bind(&inventoryThumbnailsHelperCb, cb, _1); AISAPI::UpdateItem(item_id, updates, cr); return true; @@ -382,7 +378,7 @@ void LLFloaterInventoryThumbnailsHelper::onWriteThumbnails() LLUUID item_id = ((*iter).second).first; LLUUID thumbnail_asset_id = ((*iter).second).second; - writeThumbnailID(item_id, thumbnail_asset_id); + writeInventoryThumbnailID(item_id, thumbnail_asset_id); ++iter; } -- cgit v1.2.3 From cf60c97f6f232f97666884ef692779403fb449f8 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Mon, 14 Aug 2023 17:37:10 -0700 Subject: SL-20109: New, helper version coming together slowly modulo XUI pain --- .../newview/llfloaterinventorythumbnailshelper.cpp | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'indra/newview/llfloaterinventorythumbnailshelper.cpp') diff --git a/indra/newview/llfloaterinventorythumbnailshelper.cpp b/indra/newview/llfloaterinventorythumbnailshelper.cpp index abd5e01e5d..9697fc3d51 100644 --- a/indra/newview/llfloaterinventorythumbnailshelper.cpp +++ b/indra/newview/llfloaterinventorythumbnailshelper.cpp @@ -38,6 +38,7 @@ #include "llinventorymodel.h" #include "llinventoryfunctions.h" #include "lltexteditor.h" +#include "llscrolllistctrl.h" #include "llmediactrl.h" #include "lluuid.h" #include "llaisapi.h" @@ -66,6 +67,10 @@ BOOL LLFloaterInventoryThumbnailsHelper::postBuild() //mMergeItemsTexturesBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onMergeItemsTextures, this)); //mMergeItemsTexturesBtn->setEnabled(false); + mInventoryThumbnailsList = getChild("inventory_thumbnails_list"); + mInventoryThumbnailsList->setAllowMultipleSelection(true); + mInventoryThumbnailsList->deleteAllItems(); + mWriteThumbnailsBtn = getChild("write_thumbnails_btn"); mWriteThumbnailsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onWriteThumbnails, this)); mWriteThumbnailsBtn->setEnabled(false); @@ -91,6 +96,24 @@ void LLFloaterInventoryThumbnailsHelper::recordInventoryItemEntry(LLViewerInvent //id.asString() << std::endl ), false); + + // TODO: use this ID to get name of texture and display that + const LLUUID current_thumbnail_id = item->getThumbnailUUID(); + + std::string texture_display = std::string("Not Present"); + if (!current_thumbnail_id.isNull()) + { + texture_display = current_thumbnail_id.asString(); + } + + LLSD row; + row["columns"][0]["column"] = "name"; + row["columns"][0]["type"] = "text"; + row["columns"][0]["value"] = name; + row["columns"][1]["column"] = "texture"; + row["columns"][1]["type"] = "text"; + row["columns"][1]["value"] = texture_display; + mInventoryThumbnailsList->addElement(row); } else { @@ -238,6 +261,31 @@ void LLFloaterInventoryThumbnailsHelper::onPasteTextures() } mOutputLog->setCursorAndScrollToEnd(); + + populateThumbnailNames(); +} + + +void LLFloaterInventoryThumbnailsHelper::populateThumbnailNames() +{ + std::map::iterator item_iter = mItemNamesIDs.begin(); + + while (item_iter != mItemNamesIDs.end()) + { + std::string item_name = (*item_iter).first; + + std::map::iterator texture_iter = mTextureNamesIDs.find(item_name); + if (texture_iter != mTextureNamesIDs.end()) + { + const bool case_sensitive = true; + LLScrollListItem* entry = mInventoryThumbnailsList->getItemByLabel(item_name, case_sensitive); + + const std::string texture_name = (*texture_iter).first; + entry->getColumn(1)->setValue(LLSD(texture_name)); + } + + ++item_iter; + } } void LLFloaterInventoryThumbnailsHelper::mergeItemsTextures() -- cgit v1.2.3 From 10276ee967a5ad860fbf8750ce1012db3c5ae78a Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 17 Aug 2023 15:49:50 -0700 Subject: Small updates to bulky thumbs but mainly, this is about the first version of the Inventory Thumbnail Helper tool - see source file for a link to Confluence page listing how to use it --- .../newview/llfloaterinventorythumbnailshelper.cpp | 416 +++++++++++++-------- 1 file changed, 256 insertions(+), 160 deletions(-) (limited to 'indra/newview/llfloaterinventorythumbnailshelper.cpp') diff --git a/indra/newview/llfloaterinventorythumbnailshelper.cpp b/indra/newview/llfloaterinventorythumbnailshelper.cpp index 9697fc3d51..15e1c88572 100644 --- a/indra/newview/llfloaterinventorythumbnailshelper.cpp +++ b/indra/newview/llfloaterinventorythumbnailshelper.cpp @@ -3,6 +3,9 @@ * @author Callum Prentice * @brief LLFloaterInventoryThumbnailsHelper class implementation * + * Usage instructions and some brief notes can be found in Confluence here: + * https://lindenlab.atlassian.net/wiki/spaces/~174746736/pages/2928672843/Inventory+Thumbnail+Helper+Tool + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. @@ -25,23 +28,20 @@ * $/LicenseInfo$ */ -/** - * Floater that appears when buying an object, giving a preview - * of its contents and their permissions. - */ - #include "llviewerprecompiledheaders.h" -#include "llfloaterinventorythumbnailshelper.h" -#include "lluictrlfactory.h" +#include "llaisapi.h" #include "llclipboard.h" -#include "llinventorymodel.h" #include "llinventoryfunctions.h" -#include "lltexteditor.h" +#include "llinventorymodel.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" #include "llscrolllistctrl.h" -#include "llmediactrl.h" +#include "lltexteditor.h" +#include "lluictrlfactory.h" #include "lluuid.h" -#include "llaisapi.h" + +#include "llfloaterinventorythumbnailshelper.h" LLFloaterInventoryThumbnailsHelper::LLFloaterInventoryThumbnailsHelper(const LLSD& key) : LLFloater("floater_inventory_thumbnails_helper") @@ -54,66 +54,53 @@ LLFloaterInventoryThumbnailsHelper::~LLFloaterInventoryThumbnailsHelper() BOOL LLFloaterInventoryThumbnailsHelper::postBuild() { - mPasteItemsBtn = getChild("paste_items_btn"); - mPasteItemsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onPasteItems, this)); - - mPasteTexturesBtn = getChild("paste_textures_btn"); - mPasteTexturesBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onPasteTextures, this)); + mInventoryThumbnailsList = getChild("inventory_thumbnails_list"); + mInventoryThumbnailsList->setAllowMultipleSelection(true); mOutputLog = getChild("output_log"); mOutputLog->setMaxTextLength(0xffff * 0x10); - //mMergeItemsTexturesBtn = getChild("merge_items_textures"); - //mMergeItemsTexturesBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onMergeItemsTextures, this)); - //mMergeItemsTexturesBtn->setEnabled(false); + mPasteItemsBtn = getChild("paste_items_btn"); + mPasteItemsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onPasteItems, this)); + mPasteItemsBtn->setEnabled(true); - mInventoryThumbnailsList = getChild("inventory_thumbnails_list"); - mInventoryThumbnailsList->setAllowMultipleSelection(true); - mInventoryThumbnailsList->deleteAllItems(); + mPasteTexturesBtn = getChild("paste_textures_btn"); + mPasteTexturesBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onPasteTextures, this)); + mPasteTexturesBtn->setEnabled(true); mWriteThumbnailsBtn = getChild("write_thumbnails_btn"); mWriteThumbnailsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onWriteThumbnails, this)); mWriteThumbnailsBtn->setEnabled(false); + mLogMissingThumbnailsBtn = getChild("log_missing_thumbnails_btn"); + mLogMissingThumbnailsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onLogMissingThumbnails, this)); + mLogMissingThumbnailsBtn->setEnabled(false); + + mClearThumbnailsBtn = getChild("clear_thumbnails_btn"); + mClearThumbnailsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onClearThumbnails, this)); + mClearThumbnailsBtn->setEnabled(false); + return true; } +// Records an entry in the pasted items - saves it to a map and writes it to the log +// window for later confirmation/validation - since it uses a map, duplicates (based on +// the name) are discarded void LLFloaterInventoryThumbnailsHelper::recordInventoryItemEntry(LLViewerInventoryItem* item) { const std::string name = item->getName(); - std::map::iterator iter = mItemNamesIDs.find(name); - if (iter == mItemNamesIDs.end()) + std::map::iterator iter = mItemNamesItems.find(name); + if (iter == mItemNamesItems.end()) { - LLUUID id = item->getUUID(); - mItemNamesIDs.insert({name, id}); + mItemNamesItems.insert({name, item}); - mOutputLog->appendText( + writeToLog( STRINGIZE( - "ITEM " << mItemNamesIDs.size() << "> " << + "ITEM " << mItemNamesItems.size() << "> " << name << - //" | " << - //id.asString() << std::endl ), false); - - // TODO: use this ID to get name of texture and display that - const LLUUID current_thumbnail_id = item->getThumbnailUUID(); - - std::string texture_display = std::string("Not Present"); - if (!current_thumbnail_id.isNull()) - { - texture_display = current_thumbnail_id.asString(); - } - - LLSD row; - row["columns"][0]["column"] = "name"; - row["columns"][0]["type"] = "text"; - row["columns"][0]["value"] = name; - row["columns"][1]["column"] = "texture"; - row["columns"][1]["type"] = "text"; - row["columns"][1]["value"] = texture_display; - mInventoryThumbnailsList->addElement(row); } else { @@ -121,6 +108,10 @@ void LLFloaterInventoryThumbnailsHelper::recordInventoryItemEntry(LLViewerInvent } } +// Called when the user has copied items from their inventory and selects the Paste Items button +// in the UI - iterates over items and folders and saves details of each one. +// The first use of this tool is for updating NUX items and as such, only looks for OBJECTS, +// CLOTHING and BODYPARTS - later versions of this tool should make that selection editable. void LLFloaterInventoryThumbnailsHelper::onPasteItems() { if (!LLClipboard::instance().hasContents()) @@ -128,7 +119,7 @@ void LLFloaterInventoryThumbnailsHelper::onPasteItems() return; } - mOutputLog->appendText( + writeToLog( STRINGIZE( "\n==== Pasting items from inventory ====" << std::endl @@ -142,6 +133,7 @@ void LLFloaterInventoryThumbnailsHelper::onPasteItems() { const LLUUID& entry = objects.at(i); + // Check for a folder const LLInventoryCategory* cat = gInventory.getCategory(entry); if (cat) { @@ -162,6 +154,13 @@ void LLFloaterInventoryThumbnailsHelper::onPasteItems() LLInventoryModel::EXCLUDE_TRASH, is_bodypart); + LLIsType is_clothing(LLAssetType::AT_CLOTHING); + gInventory.collectDescendentsIf(cat->getUUID(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_clothing); + for (size_t i = 0; i < item_array.size(); i++) { LLViewerInventoryItem* item = item_array.at(i); @@ -169,20 +168,28 @@ void LLFloaterInventoryThumbnailsHelper::onPasteItems() } } + // Check for an item LLViewerInventoryItem* item = gInventory.getItem(entry); if (item) { const LLAssetType::EType item_type = item->getType(); - if (item_type == LLAssetType::AT_OBJECT || item_type == LLAssetType::AT_BODYPART) + if (item_type == LLAssetType::AT_OBJECT || item_type == LLAssetType::AT_BODYPART || item_type == LLAssetType::AT_CLOTHING) { recordInventoryItemEntry(item); } } } - mOutputLog->setCursorAndScrollToEnd(); + // update the main list view based on what we found + updateDisplayList(); + + // update the buttons enabled state based on what we found/saved + updateButtonStates(); } +// Records a entry in the pasted textures - saves it to a map and writes it to the log +// window for later confirmation/validation - since it uses a map, duplicates (based on +// the name) are discarded void LLFloaterInventoryThumbnailsHelper::recordTextureItemEntry(LLViewerInventoryItem* item) { const std::string name = item->getName(); @@ -193,7 +200,7 @@ void LLFloaterInventoryThumbnailsHelper::recordTextureItemEntry(LLViewerInventor LLUUID id = item->getAssetUUID(); mTextureNamesIDs.insert({name, id}); - mOutputLog->appendText( + writeToLog( STRINGIZE( "TEXTURE " << mTextureNamesIDs.size() << "> " << name << @@ -208,6 +215,8 @@ void LLFloaterInventoryThumbnailsHelper::recordTextureItemEntry(LLViewerInventor } } +// Called when the user has copied textures from their inventory and selects the Paste Textures +// button in the UI - iterates over textures and folders and saves details of each one. void LLFloaterInventoryThumbnailsHelper::onPasteTextures() { if (!LLClipboard::instance().hasContents()) @@ -215,7 +224,7 @@ void LLFloaterInventoryThumbnailsHelper::onPasteTextures() return; } - mOutputLog->appendText( + writeToLog( STRINGIZE( "\n==== Pasting textures from inventory ====" << std::endl @@ -260,105 +269,65 @@ void LLFloaterInventoryThumbnailsHelper::onPasteTextures() } } - mOutputLog->setCursorAndScrollToEnd(); + // update the main list view based on what we found + updateDisplayList(); - populateThumbnailNames(); + // update the buttons enabled state based on what we found/saved + updateButtonStates(); } - -void LLFloaterInventoryThumbnailsHelper::populateThumbnailNames() +// Updates the main list of entries in the UI based on what is in the maps/storage +void LLFloaterInventoryThumbnailsHelper::updateDisplayList() { - std::map::iterator item_iter = mItemNamesIDs.begin(); + mInventoryThumbnailsList->deleteAllItems(); - while (item_iter != mItemNamesIDs.end()) + std::map::iterator item_iter = mItemNamesItems.begin(); + while (item_iter != mItemNamesItems.end()) { std::string item_name = (*item_iter).first; - std::map::iterator texture_iter = mTextureNamesIDs.find(item_name); - if (texture_iter != mTextureNamesIDs.end()) + std::string existing_texture_name = std::string(); + LLUUID existing_thumbnail_id = (*item_iter).second->getThumbnailUUID(); + if (existing_thumbnail_id != LLUUID::null) { - const bool case_sensitive = true; - LLScrollListItem* entry = mInventoryThumbnailsList->getItemByLabel(item_name, case_sensitive); - - const std::string texture_name = (*texture_iter).first; - entry->getColumn(1)->setValue(LLSD(texture_name)); + existing_texture_name = existing_thumbnail_id.asString(); + } + else + { + existing_texture_name = "none"; } - ++item_iter; - } -} - -void LLFloaterInventoryThumbnailsHelper::mergeItemsTextures() -{ - mOutputLog->appendText( - STRINGIZE( - "\n==== Matching items and textures for " << - mItemNamesIDs.size() << - " entries ====" << - std::endl - ), false); - - std::map::iterator item_iter = mItemNamesIDs.begin(); - size_t index = 1; - - while (item_iter != mItemNamesIDs.end()) - { - std::string item_name = (*item_iter).first; - - mOutputLog->appendText( - STRINGIZE( - "MATCHING ITEM (" << index++ << "/" << mItemNamesIDs.size() << ") " << item_name << "> " - ), false); - + std::string new_texture_name = std::string(); std::map::iterator texture_iter = mTextureNamesIDs.find(item_name); if (texture_iter != mTextureNamesIDs.end()) { - mOutputLog->appendText( - STRINGIZE( - "MATCHED" << - std::endl - ), false); - - mNameItemIDTextureId.insert({item_name, {(*item_iter).second, (*texture_iter).second}}); + new_texture_name = (*texture_iter).first; } else { - mOutputLog->appendText( - STRINGIZE( - "NO MATCH FOUND" << - std::endl - ), false); + new_texture_name = "missing"; } - ++item_iter; - } + LLSD row; + row["columns"][EListColumnNum::NAME]["column"] = "item_name"; + row["columns"][EListColumnNum::NAME]["type"] = "text"; + row["columns"][EListColumnNum::NAME]["value"] = item_name; + row["columns"][EListColumnNum::NAME]["font"]["name"] = "Monospace"; - mOutputLog->appendText( - STRINGIZE( - "==== Matched list of items and textures has " << - mNameItemIDTextureId.size() << - " entries ====" << - std::endl - ), true); - - //std::map>::iterator iter = mNameItemIDTextureId.begin(); - //while (iter != mNameItemIDTextureId.end()) - //{ - // std::string output_line = (*iter).first; - // output_line += "\n"; - // output_line += "item ID: "; - // output_line += ((*iter).second).first.asString(); - // output_line += "\n"; - // output_line += "thumbnail texture ID: "; - // output_line += ((*iter).second).second.asString(); - // output_line += "\n"; - // mOutputLog->appendText(output_line, true); - - // ++iter; - //} - mOutputLog->setCursorAndScrollToEnd(); + row["columns"][EListColumnNum::EXISTING_TEXTURE]["column"] = "existing_texture"; + row["columns"][EListColumnNum::EXISTING_TEXTURE]["type"] = "text"; + row["columns"][EListColumnNum::EXISTING_TEXTURE]["font"]["name"] = "Monospace"; + row["columns"][EListColumnNum::EXISTING_TEXTURE]["value"] = existing_texture_name; + + row["columns"][EListColumnNum::NEW_TEXTURE]["column"] = "new_texture"; + row["columns"][EListColumnNum::NEW_TEXTURE]["type"] = "text"; + row["columns"][EListColumnNum::NEW_TEXTURE]["font"]["name"] = "Monospace"; + row["columns"][EListColumnNum::NEW_TEXTURE]["value"] = new_texture_name; + + mInventoryThumbnailsList->addElement(row); - mWriteThumbnailsBtn->setEnabled(true); + ++item_iter; + } } #if 1 @@ -373,6 +342,9 @@ void inventoryThumbnailsHelperCb(LLPointer cb, LLUUID id) } #endif +// Makes calls to the AIS v3 API to record the local changes made to the thumbnails. +// If this is not called, the operations (e.g. set thumbnail or clear thumbnail) +// appear to work but do not push the changes back to the inventory (local cache view only) bool writeInventoryThumbnailID(LLUUID item_id, LLUUID thumbnail_asset_id) { if (AISAPI::isAvailable()) @@ -395,40 +367,164 @@ bool writeInventoryThumbnailID(LLUUID item_id, LLUUID thumbnail_asset_id) } } +// Called when the Write Thumbanils button is pushed. Iterates over the name/item and +// name/.texture maps and where it finds a common name, extracts what is needed and +// writes the thumbnail accordingly. void LLFloaterInventoryThumbnailsHelper::onWriteThumbnails() { - mOutputLog->appendText( - STRINGIZE( - "\n==== Writing thumbnails for " << - mNameItemIDTextureId.size() << - " entries ====" << - std::endl - ), false); + std::map::iterator item_iter = mItemNamesItems.begin(); + while (item_iter != mItemNamesItems.end()) + { + std::string item_name = (*item_iter).first; + + std::map::iterator texture_iter = mTextureNamesIDs.find(item_name); + if (texture_iter != mTextureNamesIDs.end()) + { + LLUUID item_id = (*item_iter).second->getUUID(); + + LLUUID thumbnail_asset_id = (*texture_iter).second; - std::map>::iterator iter = mNameItemIDTextureId.begin(); - size_t index = 1; + writeToLog( + STRINGIZE( + "WRITING THUMB " << + (*item_iter).first << + "\n" << + "item ID: " << + item_id << + "\n" << + "thumbnail texture ID: " << + thumbnail_asset_id << + "\n" + ), true); + + + (*item_iter).second->setThumbnailUUID(thumbnail_asset_id); + + // This additional step (notifying AIS API) is required + // to make the changes persist outside of the local cache + writeInventoryThumbnailID(item_id, thumbnail_asset_id); + } - while (iter != mNameItemIDTextureId.end()) + ++item_iter; + } + + updateDisplayList(); +} + +// Called when the Log Items with Missing Thumbnails is selected. This merely writes +// a list of all the items for which the thumbnail ID is Null. Typical use case is to +// copy from the log window, pasted to Slack to illustrate which items are missing +// a thumbnail +void LLFloaterInventoryThumbnailsHelper::onLogMissingThumbnails() +{ + std::map::iterator item_iter = mItemNamesItems.begin(); + while (item_iter != mItemNamesItems.end()) { - mOutputLog->appendText( - STRINGIZE( - "WRITING THUMB (" << index++ << "/" << mNameItemIDTextureId.size() << ")> " << - (*iter).first << - "\n" << - "item ID: " << - ((*iter).second).first.asString() << - "\n" << - "thumbnail texture ID: " << - ((*iter).second).second.asString() << - "\n" - ), true); - - LLUUID item_id = ((*iter).second).first; - LLUUID thumbnail_asset_id = ((*iter).second).second; - - writeInventoryThumbnailID(item_id, thumbnail_asset_id); - - ++iter; + LLUUID thumbnail_id = (*item_iter).second->getThumbnailUUID(); + + if (thumbnail_id == LLUUID::null) + { + writeToLog( + STRINGIZE( + "Missing thumbnail: " << + (*item_iter).first << + std::endl + ), true); + } + + ++item_iter; + } +} + +// Called when the Clear Thumbnail button is selected. Code to perform the clear (really +// just writing a NULL UUID into the thumbnail field) is behind an "Are you Sure?" dialog +// since it cannot be undone and potentinally, you could remove the thumbnails from your +// whole inventory this way. +void LLFloaterInventoryThumbnailsHelper::onClearThumbnails() +{ + // create and show confirmation (Yes/No) textbox since this is a destructive operation + LLNotificationsUtil::add("ClearInventoryThumbnailsWarning", LLSD(), LLSD(), + [&](const LLSD & notif, const LLSD & resp) + { + S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); + if (opt == 0) + { + std::map::iterator item_iter = mItemNamesItems.begin(); + while (item_iter != mItemNamesItems.end()) + { + (*item_iter).second->setThumbnailUUID(LLUUID::null); + + // This additional step (notifying AIS API) is required + // to make the changes persist outside of the local cache + const LLUUID item_id = (*item_iter).second->getUUID(); + writeInventoryThumbnailID(item_id, LLUUID::null); + + ++item_iter; + } + + updateDisplayList(); + } + else + { + LL_INFOS() << "Clearing on thumbnails was canceled" << LL_ENDL; + } + }); +} + +// Update the endabled state of some of the UI buttons based on what has +// been recorded so far. For example, if there are no valid item/texture pairs, +// then the Write Thumbnails button is not enabled. +void LLFloaterInventoryThumbnailsHelper::updateButtonStates() +{ + size_t found_count = 0; + + std::map::iterator item_iter = mItemNamesItems.begin(); + while (item_iter != mItemNamesItems.end()) + { + std::string item_name = (*item_iter).first; + + std::map::iterator texture_iter = mTextureNamesIDs.find(item_name); + if (texture_iter != mTextureNamesIDs.end()) + { + found_count++; + } + + ++item_iter; + } + + // the "Write Thumbnails" button is only enabled when there is at least one + // item with a matching texture ready to be written to the thumbnail field + if (found_count > 0) + { + mWriteThumbnailsBtn->setEnabled(true); } + else + { + mWriteThumbnailsBtn->setEnabled(false); + } + + // The "Log Missing Items" and "Clear Thumbnails" buttons are only enabled + // when there is at least 1 item that was pasted from inventory (doesn't need + // to have a matching texture for these operations) + if (mItemNamesItems.size() > 0) + { + mLogMissingThumbnailsBtn->setEnabled(true); + mClearThumbnailsBtn->setEnabled(true); + } + else + { + mLogMissingThumbnailsBtn->setEnabled(false); + mClearThumbnailsBtn->setEnabled(false); + } +} + +// Helper function for writing a line to the log window. Currently the only additional +// feature is that it scrolls to the bottom each time a line is written but it +// is envisaged that other common actions will be added here eventually - E.G. write eavh +// line to the Second Life log too for example. +void LLFloaterInventoryThumbnailsHelper::writeToLog(std::string logline, bool prepend_newline) +{ + mOutputLog->appendText(logline, prepend_newline); + mOutputLog->setCursorAndScrollToEnd(); } -- cgit v1.2.3 From d2bb4cd19d3a044276cc4c60770cff607d697b35 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Mon, 21 Aug 2023 10:58:52 -0700 Subject: SL-20172: Add in an 'Are you sure Y/N' type dialog before writing thumbnails (as well as for clearing thumbnails) since it's also a destructive, non-undoiable operation --- .../newview/llfloaterinventorythumbnailshelper.cpp | 71 +++++++++++++--------- 1 file changed, 42 insertions(+), 29 deletions(-) (limited to 'indra/newview/llfloaterinventorythumbnailshelper.cpp') diff --git a/indra/newview/llfloaterinventorythumbnailshelper.cpp b/indra/newview/llfloaterinventorythumbnailshelper.cpp index 15e1c88572..814f88e9b9 100644 --- a/indra/newview/llfloaterinventorythumbnailshelper.cpp +++ b/indra/newview/llfloaterinventorythumbnailshelper.cpp @@ -5,7 +5,7 @@ * * Usage instructions and some brief notes can be found in Confluence here: * https://lindenlab.atlassian.net/wiki/spaces/~174746736/pages/2928672843/Inventory+Thumbnail+Helper+Tool - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. @@ -372,43 +372,56 @@ bool writeInventoryThumbnailID(LLUUID item_id, LLUUID thumbnail_asset_id) // writes the thumbnail accordingly. void LLFloaterInventoryThumbnailsHelper::onWriteThumbnails() { - std::map::iterator item_iter = mItemNamesItems.begin(); - while (item_iter != mItemNamesItems.end()) + // create and show confirmation (Yes/No) textbox since this is a destructive operation + LLNotificationsUtil::add("WriteInventoryThumbnailsWarning", LLSD(), LLSD(), + [&](const LLSD & notif, const LLSD & resp) { - std::string item_name = (*item_iter).first; - - std::map::iterator texture_iter = mTextureNamesIDs.find(item_name); - if (texture_iter != mTextureNamesIDs.end()) + S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); + if (opt == 0) { - LLUUID item_id = (*item_iter).second->getUUID(); + std::map::iterator item_iter = mItemNamesItems.begin(); + while (item_iter != mItemNamesItems.end()) + { + std::string item_name = (*item_iter).first; - LLUUID thumbnail_asset_id = (*texture_iter).second; + std::map::iterator texture_iter = mTextureNamesIDs.find(item_name); + if (texture_iter != mTextureNamesIDs.end()) + { + LLUUID item_id = (*item_iter).second->getUUID(); - writeToLog( - STRINGIZE( - "WRITING THUMB " << - (*item_iter).first << - "\n" << - "item ID: " << - item_id << - "\n" << - "thumbnail texture ID: " << - thumbnail_asset_id << - "\n" - ), true); + LLUUID thumbnail_asset_id = (*texture_iter).second; + writeToLog( + STRINGIZE( + "WRITING THUMB " << + (*item_iter).first << + "\n" << + "item ID: " << + item_id << + "\n" << + "thumbnail texture ID: " << + thumbnail_asset_id << + "\n" + ), true); - (*item_iter).second->setThumbnailUUID(thumbnail_asset_id); - // This additional step (notifying AIS API) is required - // to make the changes persist outside of the local cache - writeInventoryThumbnailID(item_id, thumbnail_asset_id); - } + (*item_iter).second->setThumbnailUUID(thumbnail_asset_id); - ++item_iter; - } + // This additional step (notifying AIS API) is required + // to make the changes persist outside of the local cache + writeInventoryThumbnailID(item_id, thumbnail_asset_id); + } - updateDisplayList(); + ++item_iter; + } + + updateDisplayList(); + } + else + { + LL_INFOS() << "Writing new thumbnails was canceled" << LL_ENDL; + } + }); } // Called when the Log Items with Missing Thumbnails is selected. This merely writes -- cgit v1.2.3