/** * @file llfloaterchangeitemthumbnail.cpp * @brief LLFloaterChangeItemThumbnail class implementation * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, 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$ */ #include "llviewerprecompiledheaders.h" #include "llfloaterchangeitemthumbnail.h" #include "llbutton.h" #include "llclipboard.h" #include "lliconctrl.h" #include "llinventoryfunctions.h" #include "llinventoryicon.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" #include "llfloaterreg.h" #include "llfloatersimplesnapshot.h" #include "lllineeditor.h" #include "llnotificationsutil.h" #include "lltextbox.h" #include "lltexturectrl.h" #include "llthumbnailctrl.h" #include "llviewerfoldertype.h" #include "llviewermenufile.h" #include "llviewerobjectlist.h" #include "llviewertexturelist.h" #include "llwindow.h" class LLThumbnailImagePicker : public LLFilePickerThread { public: LLThumbnailImagePicker(const LLUUID &item_id, LLFloaterSimpleSnapshot::completion_t callback); LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id, LLFloaterSimpleSnapshot::completion_t callback); ~LLThumbnailImagePicker(); void notify(const std::vector& filenames) override; private: LLUUID mInventoryId; LLUUID mTaskId; LLFloaterSimpleSnapshot::completion_t mCallback; }; LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id, LLFloaterSimpleSnapshot::completion_t callback) : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE) , mInventoryId(item_id) , mCallback(callback) { } LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id, LLFloaterSimpleSnapshot::completion_t callback) : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE) , mInventoryId(item_id) , mTaskId(task_id) , mCallback(callback) { } LLThumbnailImagePicker::~LLThumbnailImagePicker() { } void LLThumbnailImagePicker::notify(const std::vector& filenames) { if (filenames.empty()) { return; } std::string file_path = filenames[0]; if (file_path.empty()) { return; } LLFloaterSimpleSnapshot::uploadThumbnail(file_path, mInventoryId, mTaskId, mCallback); } LLFloaterChangeItemThumbnail::LLFloaterChangeItemThumbnail(const LLSD& key) : LLFloater(key) , mObserverInitialized(false) , mMultipleThumbnails(false) , mTooltipState(TOOLTIP_NONE) { } LLFloaterChangeItemThumbnail::~LLFloaterChangeItemThumbnail() { gInventory.removeObserver(this); removeVOInventoryListener(); } bool LLFloaterChangeItemThumbnail::postBuild() { mItemNameText = getChild("item_name"); mItemTypeIcon = getChild("item_type_icon"); mThumbnailCtrl = getChild("item_thumbnail"); mToolTipTextBox = getChild("tooltip_text"); mMultipleTextBox = getChild("multiple_lbl"); LLSD tooltip_text; mToolTipTextBox->setValue(tooltip_text); mMultipleTextBox->setVisible(false); LLButton *upload_local = getChild("upload_local"); upload_local->setClickedCallback(onUploadLocal, (void*)this); upload_local->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_LOCAL)); upload_local->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_LOCAL)); LLButton *upload_snapshot = getChild("upload_snapshot"); upload_snapshot->setClickedCallback(onUploadSnapshot, (void*)this); upload_snapshot->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT)); upload_snapshot->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT)); LLButton *use_texture = getChild("use_texture"); use_texture->setClickedCallback(onUseTexture, (void*)this); use_texture->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_USE_TEXTURE)); use_texture->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_USE_TEXTURE)); mCopyToClipboardBtn = getChild("copy_to_clipboard"); mCopyToClipboardBtn->setClickedCallback(onCopyToClipboard, (void*)this); mCopyToClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD)); mCopyToClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD)); mPasteFromClipboardBtn = getChild("paste_from_clipboard"); mPasteFromClipboardBtn->setClickedCallback(onPasteFromClipboard, (void*)this); mPasteFromClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD)); mPasteFromClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD)); mRemoveImageBtn = getChild("remove_image"); mRemoveImageBtn->setClickedCallback(onRemove, (void*)this); mRemoveImageBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_REMOVE)); mRemoveImageBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_REMOVE)); return LLFloater::postBuild(); } void LLFloaterChangeItemThumbnail::onOpen(const LLSD& key) { if (!key.has("item_id") && !key.isUUID() && !key.isArray()) { closeFloater(); } mItemList.clear(); mMultipleThumbnails = false; if (key.isArray()) { if (key.size() > 50) { // incident avoidance LLNotificationsUtil::add("ThumbnailSelectionTooLarge"); closeFloater(); } LLUUID image_id; for (LLSD::array_const_iterator it = key.beginArray(); it != key.endArray(); ++it) { LLInventoryObject* obj = gInventory.getObject(it->asUUID()); if (obj) { if (mItemList.empty()) { image_id = obj->getThumbnailUUID(); } mItemList.insert(it->asUUID()); if (image_id != obj->getThumbnailUUID()) { mMultipleThumbnails = true; } } } } else if (key.isUUID()) { mItemList.insert(key.asUUID()); } else { mItemList.insert(key["item_id"].asUUID()); mTaskId = key["task_id"].asUUID(); } if (mItemList.size() == 0) { closeFloater(); } refreshFromInventory(); } void LLFloaterChangeItemThumbnail::onFocusReceived() { mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents()); } void LLFloaterChangeItemThumbnail::onMouseEnter(S32 x, S32 y, MASK mask) { mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents()); } bool LLFloaterChangeItemThumbnail::handleDragAndDrop( S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string& tooltip_msg) { if (cargo_type == DAD_TEXTURE) { LLInventoryItem *item = (LLInventoryItem *)cargo_data; if (item->getAssetUUID().notNull()) { if (drop) { assignAndValidateAsset(item->getAssetUUID()); } *accept = ACCEPT_YES_SINGLE; } else { *accept = ACCEPT_NO; } } else { *accept = ACCEPT_NO; } LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFloaterChangeItemThumbnail " << getKey() << LL_ENDL; return true; } void LLFloaterChangeItemThumbnail::changed(U32 mask) { //LLInventoryObserver if (mTaskId.notNull() || mItemList.size() == 0) { // Task inventory or not set up yet return; } const std::set& mChangedItemIDs = gInventory.getChangedIDs(); std::set::const_iterator it; const LLUUID expected_id = *mItemList.begin(); for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++) { // check if there's a change we're interested in. if (*it == expected_id) { if ((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) { refreshFromInventory(); } } } } void LLFloaterChangeItemThumbnail::inventoryChanged(LLViewerObject* object, LLInventoryObject::object_list_t* inventory, S32 serial_num, void* user_data) { //LLVOInventoryListener refreshFromInventory(); } LLInventoryObject* LLFloaterChangeItemThumbnail::getInventoryObject() { if (mItemList.size() == 0) { return NULL; } const LLUUID item_id = *mItemList.begin(); LLInventoryObject* obj = NULL; if (mTaskId.isNull()) { // it is in agent inventory if (!mObserverInitialized) { gInventory.addObserver(this); mObserverInitialized = true; } obj = gInventory.getObject(item_id); } else { LLViewerObject* object = gObjectList.findObject(mTaskId); if (object) { if (!mObserverInitialized) { registerVOInventoryListener(object, NULL); mObserverInitialized = false; } obj = object->getInventoryObject(item_id); } } return obj; } void LLFloaterChangeItemThumbnail::refreshFromInventory() { LLInventoryObject* obj = getInventoryObject(); if (!obj) { closeFloater(); } if (obj) { const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); bool in_trash = gInventory.isObjectDescendentOf(obj->getUUID(), trash_id); if (in_trash && obj->getUUID() != trash_id) { // Close properties when moving to trash // Aren't supposed to view properties from trash closeFloater(); } else { refreshFromObject(obj); } } else { closeFloater(); } } class LLIsOutfitTextureType : public LLInventoryCollectFunctor { public: LLIsOutfitTextureType() {} virtual ~LLIsOutfitTextureType() {} virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); }; bool LLIsOutfitTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { return item && (item->getType() == LLAssetType::AT_TEXTURE); } void LLFloaterChangeItemThumbnail::refreshFromObject(LLInventoryObject* obj) { LLUIImagePtr icon_img; LLUUID thumbnail_id = obj->getThumbnailUUID(); LLViewerInventoryItem* item = dynamic_cast(obj); if (item) { setTitle(getString("title_item_thumbnail")); icon_img = LLInventoryIcon::getIcon(item->getType(), item->getInventoryType(), item->getFlags(), false); mRemoveImageBtn->setEnabled(thumbnail_id.notNull() && ((item->getActualType() != LLAssetType::AT_TEXTURE) || (item->getAssetUUID() != thumbnail_id))); } else { LLViewerInventoryCategory* cat = dynamic_cast(obj); if (cat) { setTitle(getString("title_folder_thumbnail")); icon_img = LLUI::getUIImage(LLViewerFolderType::lookupIconName(cat->getPreferredType(), true)); if (thumbnail_id.isNull() && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) { // Legacy support, check if there is an image inside LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; // Not LLIsOfAssetType, because we allow links LLIsOutfitTextureType f; gInventory.getDirectDescendentsOf(*mItemList.begin(), cats, items, f); if (1 == items.size()) { LLViewerInventoryItem* item = items.front(); if (item && item->getIsLinkType()) { item = item->getLinkedItem(); } if (item) { thumbnail_id = item->getAssetUUID(); if (thumbnail_id.notNull()) { // per SL-19188, set this image as a thumbnail LL_INFOS() << "Setting image " << thumbnail_id << " from outfit as a thumbnail for inventory object " << obj->getUUID() << LL_ENDL; assignAndValidateAsset(thumbnail_id, true); } } } } mRemoveImageBtn->setEnabled(thumbnail_id.notNull()); } } if (mItemList.size() == 1) { mItemTypeIcon->setImage(icon_img); mItemTypeIcon->setVisible(true); mMultipleTextBox->setVisible(false); mItemNameText->setValue(obj->getName()); mItemNameText->setToolTip(std::string()); } else { mItemTypeIcon->setVisible(false); mMultipleTextBox->setVisible(mMultipleThumbnails); mItemNameText->setValue(getString("multiple_item_names")); // Display first five names as a tooltip const S32 ITEMS_TO_SHOW = 5; std::string items_str; uuid_set_t::iterator iter = mItemList.begin(); uuid_set_t::iterator end = mItemList.end(); for (S32 i = 0; (iter != end) && (i < ITEMS_TO_SHOW); iter++, i++) { LLInventoryObject* pobj = gInventory.getObject(*iter); if (pobj) { items_str += pobj->getName(); items_str += '\n'; } } if (mItemList.size() > ITEMS_TO_SHOW) { items_str += "..."; } mItemNameText->setToolTip(items_str); } mThumbnailCtrl->setValue(thumbnail_id); mCopyToClipboardBtn->setEnabled(thumbnail_id.notNull() && !mMultipleThumbnails); mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents()); // todo: some elements might not support setting thumbnails // since they already have them // It is unclear how system folders should function } void LLFloaterChangeItemThumbnail::onUploadLocal(void *userdata) { LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; LLUUID task_id = self->mTaskId; uuid_set_t inventory_ids = self->mItemList; LLHandle handle = self->getHandle(); (new LLThumbnailImagePicker( *self->mItemList.begin(), self->mTaskId, [inventory_ids, task_id, handle](const LLUUID& asset_id) { onUploadComplete(asset_id, task_id, inventory_ids, handle); } ))->getFile(); LLFloater* floaterp = self->mPickerHandle.get(); if (floaterp) { floaterp->closeFloater(); } floaterp = self->mSnapshotHandle.get(); if (floaterp) { floaterp->closeFloater(); } } void LLFloaterChangeItemThumbnail::onUploadSnapshot(void *userdata) { LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; LLFloater* floaterp = self->mSnapshotHandle.get(); // Show the dialog if (floaterp) { floaterp->openFloater(); } else { LLSD key; key["item_id"] = *self->mItemList.begin(); key["task_id"] = self->mTaskId; LLFloaterSimpleSnapshot* snapshot_floater = (LLFloaterSimpleSnapshot*)LLFloaterReg::showInstance("simple_snapshot", key, true); if (snapshot_floater) { self->addDependentFloater(snapshot_floater); self->mSnapshotHandle = snapshot_floater->getHandle(); snapshot_floater->setOwner(self); LLUUID task_id = self->mTaskId; uuid_set_t inventory_ids = self->mItemList; LLHandle handle = self->getHandle(); snapshot_floater->setComplectionCallback( [inventory_ids, task_id, handle](const LLUUID& asset_id) { onUploadComplete(asset_id, task_id, inventory_ids, handle); }); } } floaterp = self->mPickerHandle.get(); if (floaterp) { floaterp->closeFloater(); } } void LLFloaterChangeItemThumbnail::onUseTexture(void *userdata) { LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; LLInventoryObject* obj = self->getInventoryObject(); if (obj) { self->showTexturePicker(obj->getThumbnailUUID()); } LLFloater* floaterp = self->mSnapshotHandle.get(); if (floaterp) { floaterp->closeFloater(); } } void LLFloaterChangeItemThumbnail::onCopyToClipboard(void *userdata) { LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; LLInventoryObject* obj = self->getInventoryObject(); if (obj) { LLClipboard::instance().reset(); LLClipboard::instance().addToClipboard(obj->getThumbnailUUID(), LLAssetType::AT_NONE); self->mPasteFromClipboardBtn->setEnabled(true); } } void LLFloaterChangeItemThumbnail::onPasteFromClipboard(void *userdata) { LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; std::vector objects; LLClipboard::instance().pasteFromClipboard(objects); if (objects.size() > 0) { LLUUID potential_uuid = objects[0]; LLUUID asset_id; if (potential_uuid.notNull()) { LLViewerInventoryItem* item = gInventory.getItem(potential_uuid); if (item) { // no point checking snapshot? if (item->getType() == LLAssetType::AT_TEXTURE) { bool copy = item->getPermissions().allowCopyBy(gAgent.getID()); bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); if (copy && xfer) { asset_id = item->getAssetUUID(); } else { LLNotificationsUtil::add("ThumbnailInsufficientPermissions"); return; } } } else { // assume that this is a texture asset_id = potential_uuid; } } LLInventoryObject* obj = self->getInventoryObject(); if (obj && obj->getThumbnailUUID() == asset_id) { // nothing to do return; } if (asset_id.notNull()) { self->assignAndValidateAsset(asset_id); } // else show 'buffer has no texture' warning? } } void LLFloaterChangeItemThumbnail::onRemove(void *userdata) { LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; LLSD payload; payload["item_id"] = *self->mItemList.begin(); payload["object_id"] = self->mTaskId; LLNotificationsUtil::add("DeleteThumbnail", LLSD(), payload, boost::bind(&LLFloaterChangeItemThumbnail::onRemovalConfirmation, _1, _2, self->getHandle())); } // static void LLFloaterChangeItemThumbnail::onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle handle) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (option == 0 && !handle.isDead() && !handle.get()->isDead()) { LLFloaterChangeItemThumbnail* self = (LLFloaterChangeItemThumbnail*)handle.get(); self->setThumbnailId(LLUUID::null); } } struct ImageLoadedData { LLUUID mThumbnailId; LLUUID mTaskId; uuid_set_t mItemIds; LLHandle mFloaterHandle; bool mSilent; // Keep image reference to prevent deletion on timeout LLPointer mTexturep; }; void LLFloaterChangeItemThumbnail::assignAndValidateAsset(const LLUUID &asset_id, bool silent) { LLPointer texturep = LLViewerTextureManager::getFetchedTexture(asset_id); if (texturep->isMissingAsset()) { LL_WARNS() << "Attempted to assign missing asset " << asset_id << LL_ENDL; if (!silent) { LLNotificationsUtil::add("ThumbnailDimentionsLimit"); } } else if (texturep->getFullWidth() == 0) { if (silent) { mExpectingAssetId = LLUUID::null; } else { // don't warn user multiple times if some textures took their time mExpectingAssetId = asset_id; } ImageLoadedData *data = new ImageLoadedData(); data->mTaskId = mTaskId; data->mItemIds = mItemList; data->mThumbnailId = asset_id; data->mFloaterHandle = getHandle(); data->mSilent = silent; data->mTexturep = texturep; texturep->setLoadedCallback(onImageDataLoaded, MAX_DISCARD_LEVEL, // Don't need full image, just size data false, false, (void*)data, NULL, false); } else { if (validateAsset(asset_id)) { setThumbnailId(asset_id); } else if (!silent) { LLNotificationsUtil::add("ThumbnailDimentionsLimit"); } } } bool LLFloaterChangeItemThumbnail::validateAsset(const LLUUID &asset_id) { if (asset_id.isNull()) { return false; } LLPointer texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD); if (!texturep) { return false; } if (texturep->isMissingAsset()) { return false; } if (texturep->getFullWidth() != texturep->getFullHeight()) { return false; } if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX || texturep->getFullHeight() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX) { return false; } if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN || texturep->getFullHeight() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN) { return false; } return true; } //static void LLFloaterChangeItemThumbnail::onImageDataLoaded( bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata) { if (!userdata) return; if (!final && success) return; //not done yet ImageLoadedData* data = (ImageLoadedData*)userdata; if (success) { // Update items, set thumnails even if floater is dead if (validateAsset(data->mThumbnailId)) { for (const LLUUID& id : data->mItemIds) { setThumbnailId(data->mThumbnailId, data->mTaskId, id); } } else if (!data->mSilent) { // Should this only appear if floater is alive? LLNotificationsUtil::add("ThumbnailDimentionsLimit"); } } // Update floater if (!data->mSilent && !data->mFloaterHandle.isDead()) { LLFloaterChangeItemThumbnail* self = static_cast(data->mFloaterHandle.get()); if (self && self->mExpectingAssetId == data->mThumbnailId) { self->mExpectingAssetId = LLUUID::null; } } delete data; } //static void LLFloaterChangeItemThumbnail::onFullImageLoaded( bool success, LLViewerFetchedTexture* src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata) { if (!userdata) return; if (!final && success) return; //not done yet ImageLoadedData* data = (ImageLoadedData*)userdata; if (success) { if (src_vi->getFullWidth() != src_vi->getFullHeight() || src_vi->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN) { if (!data->mSilent) { LLNotificationsUtil::add("ThumbnailDimentionsLimit"); } } else if (src_vi->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX) { LLUUID task_id = data->mTaskId; uuid_set_t inventory_ids = data->mItemIds; LLHandle handle = data->mFloaterHandle; LLFloaterSimpleSnapshot::uploadThumbnail(src, *data->mItemIds.begin(), task_id, [inventory_ids, task_id, handle](const LLUUID& asset_id) { onUploadComplete(asset_id, task_id, inventory_ids, handle); }); } else { for (const LLUUID& id : data->mItemIds) { setThumbnailId(data->mThumbnailId, data->mTaskId, id); } } } delete data; } void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id) { // show hourglass cursor when loading inventory window getWindow()->setCursor(UI_CURSOR_WAIT); LLFloater* floaterp = mPickerHandle.get(); // Show the dialog if (floaterp) { floaterp->openFloater(); } else { floaterp = new LLFloaterTexturePicker( this, thumbnail_id, thumbnail_id, thumbnail_id, false, true, "SELECT PHOTO", PERM_NONE, PERM_NONE, false, NULL, PICK_TEXTURE); mPickerHandle = floaterp->getHandle(); LLFloaterTexturePicker* texture_floaterp = dynamic_cast(floaterp); if (texture_floaterp) { //texture_floaterp->setTextureSelectedCallback(); //texture_floaterp->setOnUpdateImageStatsCallback(); texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource, const LLUUID&, const LLUUID&, const LLUUID&) { if (op == LLTextureCtrl::TEXTURE_SELECT) { onTexturePickerCommit(); } } ); texture_floaterp->setLocalTextureEnabled(false); texture_floaterp->setBakeTextureEnabled(false); texture_floaterp->setCanApplyImmediately(false); texture_floaterp->setCanApply(false, true, false /*Hide 'preview disabled'*/); texture_floaterp->setMinDimentionsLimits(LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN); addDependentFloater(texture_floaterp); } floaterp->openFloater(); } floaterp->setFocus(true); } void LLFloaterChangeItemThumbnail::onTexturePickerCommit() { LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mPickerHandle.get(); if (floaterp) { LLUUID asset_id = floaterp->getAssetID(); if (asset_id.isNull()) { setThumbnailId(asset_id); return; } LLInventoryObject* obj = getInventoryObject(); if (obj && obj->getThumbnailUUID() == asset_id) { // nothing to do return; } LLPointer texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD); if (!texturep) { LL_WARNS() << "Image " << asset_id << " doesn't exist" << LL_ENDL; return; } if (texturep->isMissingAsset()) { LL_WARNS() << "Image " << asset_id << " is missing" << LL_ENDL; return; } if (texturep->getFullWidth() != texturep->getFullHeight()) { LLNotificationsUtil::add("ThumbnailDimentionsLimit"); return; } if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN && texturep->getFullWidth() > 0) { LLNotificationsUtil::add("ThumbnailDimentionsLimit"); return; } if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX || texturep->getFullWidth() == 0) { if (texturep->isFullyLoaded() && (texturep->getRawImageLevel() == 0) && (texturep->isRawImageValid())) { LLUUID task_id = mTaskId; uuid_set_t inventory_ids = mItemList; LLHandle handle = getHandle(); LLFloaterSimpleSnapshot::completion_t callback = [inventory_ids, task_id, handle](const LLUUID& asset_id) { onUploadComplete(asset_id, task_id, inventory_ids, handle); }; LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getRawImage(), *mItemList.begin(), mTaskId, callback); } else { ImageLoadedData* data = new ImageLoadedData(); data->mTaskId = mTaskId; data->mItemIds = mItemList; data->mThumbnailId = asset_id; data->mFloaterHandle = getHandle(); data->mSilent = false; data->mTexturep = texturep; texturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW); texturep->setMinDiscardLevel(0); texturep->setLoadedCallback(onFullImageLoaded, 0, // Need best quality true, false, (void*)data, NULL, false); texturep->forceToSaveRawImage(0); } return; } setThumbnailId(asset_id); } } //static void LLFloaterChangeItemThumbnail::onUploadComplete(const LLUUID& asset_id, const LLUUID& task_id, const uuid_set_t& inventory_ids, LLHandle handle) { if (asset_id.isNull()) { // failure return; } uuid_set_t::iterator iter = inventory_ids.begin(); uuid_set_t::iterator end = inventory_ids.end(); if (iter == end) { LL_WARNS() << "Received empty item list!" << LL_ENDL; } else { iter++; // first element was set by upload for (; iter != end; iter++) { setThumbnailId(asset_id, task_id, *iter); } } if (!handle.isDead()) { LLFloaterChangeItemThumbnail* floater = (LLFloaterChangeItemThumbnail*)handle.get(); if (floater) { floater->mMultipleThumbnails = false; floater->mMultipleTextBox->setVisible(false); } } } void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID &new_thumbnail_id) { LLInventoryObject* obj = getInventoryObject(); if (!obj) { return; } if (mTaskId.notNull()) { LL_ERRS() << "Not implemented yet" << LL_ENDL; return; } for (const LLUUID &id : mItemList) { setThumbnailId(new_thumbnail_id, id, obj); } } void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& task_id, const LLUUID& inv_obj_id) { if (task_id.notNull()) { LL_WARNS() << "Not supported" << LL_ENDL; return; } LLInventoryObject* obj = gInventory.getObject(inv_obj_id); if (!obj) { return; } setThumbnailId(new_thumbnail_id, inv_obj_id, obj); } void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& inv_obj_id, LLInventoryObject* obj) { if (obj->getThumbnailUUID() != new_thumbnail_id) { LLSD updates; if (new_thumbnail_id.notNull()) { // At the moment server expects id as a string updates["thumbnail"] = LLSD().with("asset_id", new_thumbnail_id.asString()); } else { // No thumbnail isntead of 'null id thumbnail' updates["thumbnail"] = LLSD(); } LLViewerInventoryCategory* view_folder = dynamic_cast(obj); if (view_folder) { update_inventory_category(inv_obj_id, updates, NULL); } LLViewerInventoryItem* view_item = dynamic_cast(obj); if (view_item) { update_inventory_item(inv_obj_id, updates, NULL); } } } void LLFloaterChangeItemThumbnail::onButtonMouseEnter(LLUICtrl* button, const LLSD& param, EToolTipState state) { mTooltipState = state; std::string tooltip_text; std::string tooltip_name = "tooltip_" + button->getName(); if (hasString(tooltip_name)) { tooltip_text = getString(tooltip_name); } mToolTipTextBox->setValue(tooltip_text); } void LLFloaterChangeItemThumbnail::onButtonMouseLeave(LLUICtrl* button, const LLSD& param, EToolTipState state) { if (mTooltipState == state) { mTooltipState = TOOLTIP_NONE; LLSD tooltip_text; mToolTipTextBox->setValue(tooltip_text); } }