From 8ed39a469b1b7337855c113f785fb64e66cf3b8f Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 29 Mar 2023 21:30:46 +0300
Subject: SL-19504 Allow drag and drop of textures into thumbnail floater

---
 indra/newview/llfloaterchangeitemthumbnail.cpp     | 151 ++++++++++++++++++---
 indra/newview/llfloaterchangeitemthumbnail.h       |  21 +++
 .../newview/skins/default/xui/en/notifications.xml |  11 ++
 3 files changed, 166 insertions(+), 17 deletions(-)

diff --git a/indra/newview/llfloaterchangeitemthumbnail.cpp b/indra/newview/llfloaterchangeitemthumbnail.cpp
index a10de8ba23..8f6d984aa9 100644
--- a/indra/newview/llfloaterchangeitemthumbnail.cpp
+++ b/indra/newview/llfloaterchangeitemthumbnail.cpp
@@ -45,6 +45,7 @@
 #include "llviewerfoldertype.h"
 #include "llviewermenufile.h"
 #include "llviewerobjectlist.h"
+#include "llviewertexturelist.h"
 #include "llwindow.h"
 
 
@@ -169,6 +170,53 @@ void LLFloaterChangeItemThumbnail::onOpen(const LLSD& key)
     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
@@ -342,9 +390,11 @@ void LLFloaterChangeItemThumbnail::refreshFromObject(LLInventoryObject* obj)
     mThumbnailCtrl->setValue(thumbnail_id);
 
     mCopyToClipboardBtn->setEnabled(thumbnail_id.notNull());
+    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)
@@ -418,7 +468,8 @@ void LLFloaterChangeItemThumbnail::onCopyToClipboard(void *userdata)
     LLInventoryObject* obj = self->getInventoryObject();
     if (obj)
     {
-        LLClipboard::instance().addToClipboard(obj->getThumbnailUUID());
+        LLClipboard::instance().addToClipboard(obj->getThumbnailUUID(), LLAssetType::AT_NONE);
+        self->mPasteFromClipboardBtn->setEnabled(true);
     }
 }
 
@@ -440,29 +491,29 @@ void LLFloaterChangeItemThumbnail::onPasteFromClipboard(void *userdata)
                 // no point checking snapshot?
                 if (item->getType() == LLAssetType::AT_TEXTURE)
                 {
-                    asset_id = item->getAssetUUID();
+                    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
             {
-                LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::getFetchedTexture(potential_uuid);
-                if (texturep)
-                {
-                    asset_id = potential_uuid;
-                }
+                // assume that this is a texture
+                asset_id = potential_uuid;
             }
         }
         if (asset_id.notNull())
         {
-            // todo: if texture isn't loaded subscribe, or preload it in some way.
-            if (validateAsset(asset_id))
-            {
-                self->setThumbnailId(asset_id);
-            }
-            else
-            {
-                LLNotificationsUtil::add("ThumbnailDimentionsLimit");
-            }
+            self->assignAndValidateAsset(asset_id);
         }
         // else show 'buffer has no texture' warning?
     }
@@ -489,15 +540,45 @@ void LLFloaterChangeItemThumbnail::onRemovalConfirmation(const LLSD& notificatio
     }
 }
 
-bool LLFloaterChangeItemThumbnail::validateAsset(const LLUUID &asset_id)
+void LLFloaterChangeItemThumbnail::assignAndValidateAsset(const LLUUID &asset_id)
 {
     LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::getFetchedTexture(asset_id);
+    if (texturep->getFullWidth() == 0 && !texturep->isFullyLoaded() && !texturep->isMissingAsset())
+    {
+        texturep->setLoadedCallback(onImageLoaded,
+            MAX_DISCARD_LEVEL, // don't actually need max one, 3 or 4 should be enough
+            FALSE,
+            FALSE,
+            new LLHandle<LLFloater>(getHandle()),
+            NULL,
+            FALSE);
+    }
+    else
+    {
+        if (validateAsset(asset_id))
+        {
+            setThumbnailId(asset_id);
+        }
+        else
+        {
+            LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+        }
+    }
+}
+bool LLFloaterChangeItemThumbnail::validateAsset(const LLUUID &asset_id)
+{
+    LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD);
 
     if (!texturep)
     {
         return false;
     }
 
+    if (texturep->isMissingAsset())
+    {
+        return false;
+    }
+
     if (texturep->getFullWidth() != texturep->getFullHeight())
     {
         return false;
@@ -517,6 +598,42 @@ bool LLFloaterChangeItemThumbnail::validateAsset(const LLUUID &asset_id)
     return true;
 }
 
+//static
+void LLFloaterChangeItemThumbnail::onImageLoaded(
+    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
+
+    LLHandle<LLFloater>* handle = (LLHandle<LLFloater>*)userdata;
+
+    if (success && !handle->isDead())
+    {
+        LLFloaterChangeItemThumbnail* self = static_cast<LLFloaterChangeItemThumbnail*>(handle->get());
+        if (self)
+        {
+            LLUUID asset_id = src_vi->getID();
+            if (validateAsset(asset_id))
+            {
+                self->setThumbnailId(asset_id);
+            }
+            else
+            {
+                LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+            }
+        }
+    }
+
+    delete handle;
+}
+
 void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id)
 {
     // show hourglass cursor when loading inventory window
diff --git a/indra/newview/llfloaterchangeitemthumbnail.h b/indra/newview/llfloaterchangeitemthumbnail.h
index 50c0c951d4..18cf2f7a85 100644
--- a/indra/newview/llfloaterchangeitemthumbnail.h
+++ b/indra/newview/llfloaterchangeitemthumbnail.h
@@ -37,6 +37,7 @@ class LLTextBox;
 class LLThumbnailCtrl;
 class LLUICtrl;
 class LLViewerInventoryItem;
+class LLViewerFetchedTexture;
 
 class LLFloaterChangeItemThumbnail : public LLFloater, public LLInventoryObserver, public LLVOInventoryListener
 {
@@ -46,6 +47,18 @@ public:
 
     BOOL postBuild() override;
     void onOpen(const LLSD& key) override;
+    void onFocusReceived() override;
+    void onMouseEnter(S32 x, S32 y, MASK mask) override;
+
+    BOOL handleDragAndDrop(
+        S32 x,
+        S32 y,
+        MASK mask,
+        BOOL drop,
+        EDragAndDropType cargo_type,
+        void *cargo_data,
+        EAcceptance *accept,
+        std::string& tooltip_msg) override;
 
     void changed(U32 mask) override;
     void inventoryChanged(LLViewerObject* object,
@@ -67,7 +80,15 @@ private:
     static void onRemove(void*);
     static void onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle<LLFloater> handle);
 
+    void assignAndValidateAsset(const LLUUID &asset_id);
     static bool validateAsset(const LLUUID &asset_id);
+    static void onImageLoaded(BOOL success,
+        LLViewerFetchedTexture *src_vi,
+        LLImageRaw* src,
+        LLImageRaw* aux_src,
+        S32 discard_level,
+        BOOL final,
+        void* userdata);
 
     void showTexturePicker(const LLUUID &thumbnail_id);
     void onTexturePickerCommit(LLUUID id);
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 0ea13e7d63..73ce5a6096 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -6143,6 +6143,17 @@ Are you sure you want to delete them?
      yestext="OK"/>
   </notification>
 
+  <notification
+   icon="alertmodal.tga"
+   name="ThumbnailInsufficientPermissions"
+   type="alertmodal">
+    <unique/>
+    Only copy and transfer free images can be assigned as thumbnails.
+    <usetemplate
+     name="okbutton"
+     yestext="OK"/>
+  </notification>
+
   <notification
      icon="alertmodal.tga"
      name="ConfirmUnlink"
-- 
cgit v1.2.3