From 1310fac3b10b3927cb502008c156c16631b97663 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 17 Jun 2020 23:38:55 +0300
Subject: [PATCH] SL-13359 #5 Implemented revised changes

---
 indra/newview/llfloatermodelpreview.cpp       |   1 +
 indra/newview/llpanelface.cpp                 |  64 +----
 indra/newview/llpanelface.h                   |   7 -
 indra/newview/llpanelobject.cpp               | 219 +++++++++++++++---
 indra/newview/llpanelobject.h                 |  11 +-
 indra/newview/lltexturectrl.cpp               |  55 +++++
 indra/newview/lltexturectrl.h                 |  10 +
 .../textures/icons/ClipboardMenu_Disabled.png | Bin 0 -> 231 bytes
 .../textures/icons/ClipboardMenu_Off.png      | Bin 0 -> 231 bytes
 .../textures/icons/ClipboardMenu_Press.png    | Bin 0 -> 224 bytes
 .../skins/default/textures/textures.xml       |   3 +
 .../skins/default/xui/en/floater_tools.xml    |  31 ++-
 .../xui/en/menu_copy_paste_generic.xml        |  21 ++
 .../default/xui/en/menu_copy_paste_pos.xml    |  26 ++-
 .../default/xui/en/menu_copy_paste_rot.xml    |  26 ++-
 .../default/xui/en/menu_copy_paste_size.xml   |  26 ++-
 16 files changed, 382 insertions(+), 118 deletions(-)
 create mode 100644 indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png
 create mode 100644 indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png
 create mode 100644 indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png
 create mode 100644 indra/newview/skins/default/xui/en/menu_copy_paste_generic.xml

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index bc44e37c5ad..f44dd92ddbf 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -4379,6 +4379,7 @@ void LLModelPreview::textureLoadedCallback(
 
 	if(final && preview->mModelLoader)
 	{
+		// for areTexturesReady()
 		if(preview->mModelLoader->mNumOfFetchingTextures > 0)
 		{
 			preview->mModelLoader->mNumOfFetchingTextures-- ;
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index 369861fa25b..6e99a10b987 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -54,7 +54,6 @@
 #include "llmediaentry.h"
 #include "llmenubutton.h"
 #include "llnotificationsutil.h"
-#include "llpanelobject.h" // LLPanelObject::canCopyTexture
 #include "llradiogroup.h"
 #include "llresmgr.h"
 #include "llselectmgr.h"
@@ -2929,7 +2928,7 @@ void LLPanelFace::onCopyFaces()
                 {
                     LLUUID item_id;
                     LLUUID id = te_data["te"]["imageid"].asUUID();
-                    bool full_perm = LLPanelFace::isLibraryTexture(id) || (objectp->permCopy() && objectp->permTransfer() && objectp->permModify());
+                    bool full_perm = get_is_library_texture(id) || (objectp->permCopy() && objectp->permTransfer() && objectp->permModify());
 
                     if (id.notNull() && !full_perm)
                     {
@@ -2944,7 +2943,7 @@ void LLPanelFace::onCopyFaces()
                             // as result it is Hightly unreliable, leaves little control to user, borderline hack
                             // but there are little options to preserve permissions - multiple inventory
                             // items might reference same asset and inventory search is expensive.
-                            item_id = LLPanelFace::getCopyPermInventoryTextureId(id);
+                            item_id = get_copy_free_item_by_asset_id(id);
                             // record value to avoid repeating inventory search when possible
                             asset_item_map[id] = item_id;
                         }
@@ -3022,7 +3021,7 @@ void LLPanelFace::onCopyFaces()
                     if (mat_data.has("NormMap"))
                     {
                         LLUUID id = mat_data["NormMap"].asUUID();
-                        if (id.notNull() && !LLPanelFace::canCopyTexture(id))
+                        if (id.notNull() && !get_can_copy_texture(id))
                         {
                             mat_data["NormMap"] = LLUUID(gSavedSettings.getString( "DefaultObjectTexture" ));
                             mat_data["NormMapNoCopy"] = true;
@@ -3032,7 +3031,7 @@ void LLPanelFace::onCopyFaces()
                     if (mat_data.has("SpecMap"))
                     {
                         LLUUID id = mat_data["SpecMap"].asUUID();
-                        if (id.notNull() && !LLPanelFace::canCopyTexture(id))
+                        if (id.notNull() && !get_can_copy_texture(id))
                         {
                             mat_data["SpecMap"]  = LLUUID(gSavedSettings.getString( "DefaultObjectTexture" ));
                             mat_data["SpecMapNoCopy"] = true;
@@ -3504,58 +3503,3 @@ bool LLPanelFace::pasteEnabletMenuItem(const LLSD& userdata)
 
     return true;
 }
-
-//static
-bool LLPanelFace::isLibraryTexture(LLUUID image_id)
-{
-    if (gInventory.isObjectDescendentOf(image_id, gInventory.getLibraryRootFolderID())
-        || image_id == LLUUID(gSavedSettings.getString("DefaultObjectTexture"))
-        || image_id == LLUUID(gSavedSettings.getString("UIImgWhiteUUID"))
-        || image_id == LLUUID(gSavedSettings.getString("UIImgInvisibleUUID"))
-        || image_id == LLUUID(SCULPT_DEFAULT_TEXTURE))
-    {
-        return true;
-    }
-    return false;
-}
-
-//static
-LLUUID LLPanelFace::getCopyPermInventoryTextureId(LLUUID image_id)
-{
-    LLViewerInventoryCategory::cat_array_t cats;
-    LLViewerInventoryItem::item_array_t items;
-    LLAssetIDMatches asset_id_matches(image_id);
-    gInventory.collectDescendentsIf(LLUUID::null,
-        cats,
-        items,
-        LLInventoryModel::INCLUDE_TRASH,
-        asset_id_matches);
-    if (items.size())
-    {
-        for (S32 i = 0; i < items.size(); i++)
-        {
-            LLViewerInventoryItem* itemp = items[i];
-            if (itemp)
-            {
-                LLPermissions item_permissions = itemp->getPermissions();
-                if (item_permissions.allowOperationBy(PERM_COPY,
-                    gAgent.getID(),
-                    gAgent.getGroupID()))
-                {
-                    return itemp->getUUID();
-                }
-            }
-        }
-    }
-    return LLUUID::null;
-}
-
-// Static
-bool LLPanelFace::canCopyTexture(LLUUID image_id)
-{
-    // User is allowed to copy a texture if:
-    // library asset or default texture,
-    // or copy perm asset exists in user's inventory
-
-    return isLibraryTexture(image_id) || getCopyPermInventoryTextureId(image_id).notNull();
-}
diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h
index dbf3531332e..770f10e2ee5 100644
--- a/indra/newview/llpanelface.h
+++ b/indra/newview/llpanelface.h
@@ -115,13 +115,6 @@ class LLPanelFace : public LLPanel
 	LLRender::eTexIndex getTextureChannelToEdit();
 
     void            pasteFace(LLViewerObject* object, S32 te);
-    static bool     isLibraryTexture(LLUUID image_id);
-
-    // Finds copy-enabled texture with specified asset from inventory
-    // This can be performance unfriendly and doesn't warranty that
-    // the texture is original source of asset
-    static LLUUID   getCopyPermInventoryTextureId(LLUUID image_id);
-    static bool     canCopyTexture(LLUUID image_id);
 
 protected:
 	void			getState();
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index 6fa2da7bacd..f1426ddf33f 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -159,6 +159,8 @@ BOOL	LLPanelObject::postBuild()
 	mComboBaseType = getChild<LLComboBox>("comboBaseType");
 	childSetCommitCallback("comboBaseType",onCommitParametric,this);
 
+	mMenuClipboardParams = getChild<LLMenuButton>("clipboard_params_btn");
+
 	// Cut
 	mLabelCut = getChild<LLTextBox>("text cut");
 	mSpinCutBegin = getChild<LLSpinCtrl>("cut begin");
@@ -289,9 +291,10 @@ LLPanelObject::LLPanelObject()
 	mSelectedType(MI_BOX),
 	mSculptTextureRevert(LLUUID::null),
 	mSculptTypeRevert(0),
-    mHasPosClipboard(FALSE),
-    mHasSizeClipboard(FALSE),
-    mHasRotClipboard(FALSE),
+    mHasClipboardPos(false),
+    mHasClipboardSize(false),
+    mHasClipboardRot(false),
+    mHasClipboardParams(false),
 	mSizeChanged(FALSE)
 {
     mCommitCallbackRegistrar.add("PanelObject.menuDoToSelected", boost::bind(&LLPanelObject::menuDoToSelected, this, _2));
@@ -618,7 +621,7 @@ void LLPanelObject::getState( )
 		}
 		else
 		{
-			LL_INFOS() << "Unknown path " << (S32) path << " profile " << (S32) profile << " in getState" << LL_ENDL;
+			LL_INFOS("FloaterTools") << "Unknown path " << (S32) path << " profile " << (S32) profile << " in getState" << LL_ENDL;
 			selected_item = MI_BOX;
 		}
 
@@ -944,6 +947,7 @@ void LLPanelObject::getState( )
 
 	// Update field enablement
 	mComboBaseType	->setEnabled( enabled );
+	mMenuClipboardParams->setEnabled(enabled);
 
 	mLabelCut		->setEnabled( enabled );
 	mSpinCutBegin	->setEnabled( enabled );
@@ -1104,7 +1108,8 @@ void LLPanelObject::getState( )
 			}
 
 			mComboBaseType->setEnabled(!isMesh);
-			
+			mMenuClipboardParams->setEnabled(!isMesh);
+
 			if (mCtrlSculptType)
 			{
 				if (sculpt_stitching == LL_SCULPT_TYPE_NONE)
@@ -1168,11 +1173,11 @@ void LLPanelObject::sendIsPhysical()
 		LLSelectMgr::getInstance()->selectionUpdatePhysics(value);
 		mIsPhysical = value;
 
-		LL_INFOS() << "update physics sent" << LL_ENDL;
+		LL_INFOS("FloaterTools") << "update physics sent" << LL_ENDL;
 	}
 	else
 	{
-		LL_INFOS() << "update physics not changed" << LL_ENDL;
+		LL_INFOS("FloaterTools") << "update physics not changed" << LL_ENDL;
 	}
 }
 
@@ -1184,11 +1189,11 @@ void LLPanelObject::sendIsTemporary()
 		LLSelectMgr::getInstance()->selectionUpdateTemporary(value);
 		mIsTemporary = value;
 
-		LL_INFOS() << "update temporary sent" << LL_ENDL;
+		LL_INFOS("FloaterTools") << "update temporary sent" << LL_ENDL;
 	}
 	else
 	{
-		LL_INFOS() << "update temporary not changed" << LL_ENDL;
+		LL_INFOS("FloaterTools") << "update temporary not changed" << LL_ENDL;
 	}
 }
 
@@ -1201,11 +1206,11 @@ void LLPanelObject::sendIsPhantom()
 		LLSelectMgr::getInstance()->selectionUpdatePhantom(value);
 		mIsPhantom = value;
 
-		LL_INFOS() << "update phantom sent" << LL_ENDL;
+		LL_INFOS("FloaterTools") << "update phantom sent" << LL_ENDL;
 	}
 	else
 	{
-		LL_INFOS() << "update phantom not changed" << LL_ENDL;
+		LL_INFOS("FloaterTools") << "update phantom not changed" << LL_ENDL;
 	}
 }
 
@@ -1315,7 +1320,7 @@ void LLPanelObject::getVolumeParams(LLVolumeParams& volume_params)
 		break;
 		
 	default:
-		LL_WARNS() << "Unknown base type " << selected_type 
+		LL_WARNS("FloaterTools") << "Unknown base type " << selected_type 
 			<< " in getVolumeParams()" << LL_ENDL;
 		// assume a box
 		selected_type = MI_BOX;
@@ -2019,7 +2024,13 @@ void LLPanelObject::menuDoToSelected(const LLSD& userdata)
     std::string command = userdata.asString();
 
     // paste
-    if (command == "pos_paste")
+    if (command == "psr_paste")
+    {
+        onPastePos();
+        onPasteSize();
+        onPasteRot();
+    }
+    else if (command == "pos_paste")
     {
         onPastePos();
     }
@@ -2031,7 +2042,17 @@ void LLPanelObject::menuDoToSelected(const LLSD& userdata)
     {
         onPasteRot();
     }
+    else if (command == "params_paste")
+    {
+        onPasteParams();
+    }
     // copy
+    else if (command == "psr_copy")
+    {
+        onCopyPos();
+        onCopySize();
+        onCopyRot();
+    }
     else if (command == "pos_copy")
     {
         onCopyPos();
@@ -2044,23 +2065,71 @@ void LLPanelObject::menuDoToSelected(const LLSD& userdata)
     {
         onCopyRot();
     }
+    else if (command == "params_copy")
+    {
+        onCopyParams();
+    }
 }
 
 bool LLPanelObject::menuEnableItem(const LLSD& userdata)
 {
     std::string command = userdata.asString();
 
-    if (command == "pos_paste")
+    // paste options
+    if (command == "psr_paste")
+    {
+        S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
+        BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode(LL_PCODE_VOLUME))
+            && (selected_count == 1);
+
+        if (!single_volume)
+        {
+            return false;
+        }
+
+        bool enable_move;
+        bool enable_modify;
+
+        LLSelectMgr::getInstance()->selectGetEditMoveLinksetPermissions(enable_move, enable_modify);
+
+        return enable_move && enable_modify && mHasClipboardPos && mHasClipboardSize && mHasClipboardRot;
+    }
+    else if (command == "pos_paste")
     {
-        return mHasPosClipboard;
+        // assumes that menu won't be active if there is no move permission
+        return mHasClipboardPos;
     }
     else if (command == "size_paste")
     {
-        return mHasSizeClipboard;
+        return mHasClipboardSize;
     }
     else if (command == "rot_paste")
     {
-        return mHasRotClipboard;
+        return mHasClipboardRot;
+    }
+    else if (command == "params_paste")
+    {
+        return mHasClipboardParams;
+    }
+    // copy options
+    else if (command == "psr_copy")
+    {
+        S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
+        BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode(LL_PCODE_VOLUME))
+            && (selected_count == 1);
+
+        if (!single_volume)
+        {
+            return false;
+        }
+
+        bool enable_move;
+        bool enable_modify;
+
+        LLSelectMgr::getInstance()->selectGetEditMoveLinksetPermissions(enable_move, enable_modify);
+
+        // since we forbid seeing values we also should forbid copying them
+        return enable_move && enable_modify;
     }
     return false;
 }
@@ -2072,7 +2141,7 @@ void LLPanelObject::onCopyPos()
     std::string stringVec = llformat("<%g, %g, %g>", mClipboardPos.mV[VX], mClipboardPos.mV[VY], mClipboardPos.mV[VZ]);
     LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec));
 
-    mHasPosClipboard = TRUE;
+    mHasClipboardPos = true;
 }
 
 void LLPanelObject::onCopySize()
@@ -2082,7 +2151,7 @@ void LLPanelObject::onCopySize()
     std::string stringVec = llformat("<%g, %g, %g>", mClipboardSize.mV[VX], mClipboardSize.mV[VY], mClipboardSize.mV[VZ]);
     LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec));
 
-    mHasSizeClipboard = TRUE;
+    mHasClipboardSize = true;
 }
 
 void LLPanelObject::onCopyRot()
@@ -2092,12 +2161,12 @@ void LLPanelObject::onCopyRot()
     std::string stringVec = llformat("<%g, %g, %g>", mClipboardRot.mV[VX], mClipboardRot.mV[VY], mClipboardRot.mV[VZ]);
     LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec));
 
-    mHasRotClipboard = TRUE;
+    mHasClipboardRot = true;
 }
 
 void LLPanelObject::onPastePos()
 {
-    if (!mHasPosClipboard) return;
+    if (!mHasClipboardPos) return;
     if (mObject.isNull()) return;
 
     LLViewerRegion* regionp = mObject->getRegion();
@@ -2122,26 +2191,118 @@ void LLPanelObject::onPastePos()
 
 void LLPanelObject::onPasteSize()
 {
-    if (!mHasSizeClipboard) return;
+    if (!mHasClipboardSize) return;
 
     mClipboardSize.mV[VX] = llclamp(mClipboardSize.mV[VX], MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE);
     mClipboardSize.mV[VY] = llclamp(mClipboardSize.mV[VY], MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE);
     mClipboardSize.mV[VZ] = llclamp(mClipboardSize.mV[VZ], MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE);
 
-    mCtrlScaleX->set( mClipboardSize.mV[VX] );
-    mCtrlScaleY->set( mClipboardSize.mV[VY] );
-    mCtrlScaleZ->set( mClipboardSize.mV[VZ] );
+    mCtrlScaleX->set(mClipboardSize.mV[VX]);
+    mCtrlScaleY->set(mClipboardSize.mV[VY]);
+    mCtrlScaleZ->set(mClipboardSize.mV[VZ]);
 
     sendScale(FALSE);
 }
 
 void LLPanelObject::onPasteRot()
 {
-    if (!mHasRotClipboard) return;
+    if (!mHasClipboardRot) return;
 
-    mCtrlRotX->set( mClipboardRot.mV[VX] );
-    mCtrlRotY->set( mClipboardRot.mV[VY] );
-    mCtrlRotZ->set( mClipboardRot.mV[VZ] );
+    mCtrlRotX->set(mClipboardRot.mV[VX]);
+    mCtrlRotY->set(mClipboardRot.mV[VY]);
+    mCtrlRotZ->set(mClipboardRot.mV[VZ]);
 
     sendRotation(FALSE);
 }
+
+void LLPanelObject::onCopyParams()
+{
+    LLViewerObject* objectp = mObject;
+    if (!objectp)
+    {
+        return;
+    }
+
+    mClipboardParams.clear();
+
+    // Parametrics
+    if (!objectp->isMesh())
+    {
+        LLVolumeParams params;
+        getVolumeParams(params);
+        mClipboardParams["volume_params"] = params.asLLSD();
+    }
+
+    // Sculpted Prim
+    if (objectp->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT))
+    {
+        LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+
+        if (!objectp->isMesh())
+        {
+            LLUUID texture_id = sculpt_params->getSculptTexture();
+            if (get_can_copy_texture(texture_id))
+            {
+                LL_DEBUGS("FloaterTools") << "Recording texture" << LL_ENDL;
+                mClipboardParams["sculpt"]["id"] = texture_id;
+            }
+            else
+            {
+                mClipboardParams["sculpt"]["id"] = LLUUID(SCULPT_DEFAULT_TEXTURE);
+            }
+
+            mClipboardParams["sculpt"]["type"] = sculpt_params->getSculptType();
+        }
+    }
+
+    mHasClipboardParams = TRUE;
+}
+
+void LLPanelObject::onPasteParams()
+{
+    LLViewerObject* objectp = mObject;
+    if (!objectp || !mHasClipboardParams)
+    {
+        return;
+    }
+
+    // Sculpted Prim
+    if (mClipboardParams.has("sculpt"))
+    {
+        LLSculptParams sculpt_params;
+        LLUUID sculpt_id = mClipboardParams["sculpt"]["id"].asUUID();
+        U8 sculpt_type = (U8)mClipboardParams["sculpt"]["type"].asInteger();
+        sculpt_params.setSculptTexture(sculpt_id, sculpt_type);
+        objectp->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, TRUE);
+    }
+    else
+    {
+        LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+        if (sculpt_params)
+        {
+            objectp->setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, FALSE, TRUE);
+        }
+    }
+
+    // volume params
+    // make sure updateVolume() won't affect flexible
+    if (mClipboardParams.has("volume_params"))
+    {
+        LLVolumeParams params;
+        params.fromLLSD(mClipboardParams["volume_params"]);
+        LLVOVolume *volobjp = (LLVOVolume *)objectp;
+        if (volobjp->isFlexible())
+        {
+            if (params.getPathParams().getCurveType() == LL_PCODE_PATH_LINE)
+            {
+                params.getPathParams().setCurveType(LL_PCODE_PATH_FLEXIBLE);
+            }
+        }
+        else if (params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
+        {
+            params.getPathParams().setCurveType(LL_PCODE_PATH_LINE);
+        }
+
+        objectp->updateVolume(params);
+    }
+}
diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h
index 764c0d8af47..5ea3d07699a 100644
--- a/indra/newview/llpanelobject.h
+++ b/indra/newview/llpanelobject.h
@@ -73,6 +73,8 @@ class LLPanelObject : public LLPanel
     void            onPasteSize();
     void            onCopyRot();
     void            onPasteRot();
+    void            onCopyParams();
+    void            onPasteParams();
 	static void 	onCommitParametric(LLUICtrl* ctrl, void* userdata);
 
 
@@ -102,6 +104,7 @@ class LLPanelObject : public LLPanel
 protected:
 	// Per-object options
 	LLComboBox*		mComboBaseType;
+	LLMenuButton*	mMenuClipboardParams;
 
 	LLTextBox*		mLabelCut;
 	LLSpinCtrl*		mSpinCutBegin;
@@ -183,10 +186,12 @@ class LLPanelObject : public LLPanel
     LLVector3       mClipboardPos;
     LLVector3       mClipboardSize;
     LLVector3       mClipboardRot;
+    LLSD            mClipboardParams;
 
-    BOOL            mHasPosClipboard;
-    BOOL            mHasSizeClipboard;
-    BOOL            mHasRotClipboard;
+    bool            mHasClipboardPos;
+    bool            mHasClipboardSize;
+    bool            mHasClipboardRot;
+    bool            mHasClipboardParams;
 
 	LLPointer<LLViewerObject> mObject;
 	LLPointer<LLViewerObject> mRootObject;
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 6a0464c657d..2f5be2b32ef 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -79,6 +79,61 @@ static const S32 LOCAL_TRACKING_ID_COLUMN = 1;
 //static const char WHITE_IMAGE_NAME[] = "Blank Texture";
 //static const char NO_IMAGE_NAME[] = "None";
 
+
+
+//static
+bool get_is_library_texture(LLUUID image_id)
+{
+    if (gInventory.isObjectDescendentOf(image_id, gInventory.getLibraryRootFolderID())
+        || image_id == LLUUID(gSavedSettings.getString("DefaultObjectTexture"))
+        || image_id == LLUUID(gSavedSettings.getString("UIImgWhiteUUID"))
+        || image_id == LLUUID(gSavedSettings.getString("UIImgInvisibleUUID"))
+        || image_id == LLUUID(SCULPT_DEFAULT_TEXTURE))
+    {
+        return true;
+    }
+    return false;
+}
+
+LLUUID get_copy_free_item_by_asset_id(LLUUID image_id)
+{
+    LLViewerInventoryCategory::cat_array_t cats;
+    LLViewerInventoryItem::item_array_t items;
+    LLAssetIDMatches asset_id_matches(image_id);
+    gInventory.collectDescendentsIf(LLUUID::null,
+        cats,
+        items,
+        LLInventoryModel::INCLUDE_TRASH,
+        asset_id_matches);
+    if (items.size())
+    {
+        for (S32 i = 0; i < items.size(); i++)
+        {
+            LLViewerInventoryItem* itemp = items[i];
+            if (itemp)
+            {
+                LLPermissions item_permissions = itemp->getPermissions();
+                if (item_permissions.allowOperationBy(PERM_COPY,
+                    gAgent.getID(),
+                    gAgent.getGroupID()))
+                {
+                    return itemp->getUUID();
+                }
+            }
+        }
+    }
+    return LLUUID::null;
+}
+
+bool get_can_copy_texture(LLUUID image_id)
+{
+    // User is allowed to copy a texture if:
+    // library asset or default texture,
+    // or copy perm asset exists in user's inventory
+
+    return get_is_library_texture(image_id) || get_copy_free_item_by_asset_id(image_id).notNull();
+}
+
 LLFloaterTexturePicker::LLFloaterTexturePicker(	
 	LLView* owner,
 	LLUUID image_asset_id,
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
index b2a34a37c48..2b2c5fa2377 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -53,6 +53,16 @@ class LLViewerFetchedTexture;
 typedef boost::function<BOOL (LLUICtrl*, LLInventoryItem*)> drag_n_drop_callback;
 typedef boost::function<void (LLInventoryItem*)> texture_selected_callback;
 
+// Helper functions for UI that work with picker
+bool get_is_library_texture(LLUUID image_id);
+
+// texture picker works by asset ids since objects normaly do
+// not retain inventory ids as result these functions are looking
+// for textures in inventory by asset ids
+// This search can be performance unfriendly and doesn't warranty
+// that the texture is original source of asset
+LLUUID get_copy_free_item_by_asset_id(LLUUID image_id);
+bool get_can_copy_texture(LLUUID image_id);
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // LLTextureCtrl
diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png
new file mode 100644
index 0000000000000000000000000000000000000000..9a81c5f94b65713dcac65f5e743639162bcb8481
GIT binary patch
literal 231
zcmeAS@N?(olHy`uVBq!ia0vp^VnEE#!3HEB<l1KdDaPU;cPEB*=VV?2IV|apzK#qG
z8~eHcB(eheoCO|{#S9F5hd`K7RKu$QC|Kj^;uvD#pZw?le|u)32`xz}A7r{bnG~8Y
z@>;VmO+3Ki)y!~lS_2PTW{afTiK8d7J~B@@xZr_;knq}RoLP2P8PD>#@h3Ym#OiLd
zU~GIoWtT*>7Grmyvc?47_RT9rj~Os0iUpW{deGLya5m`Z1!IY5#TNbrE&_Q64lpph
Y+ny!yzyIJWpgR~mUHx3vIVCg!0Kv#f<p2Nx

literal 0
HcmV?d00001

diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png
new file mode 100644
index 0000000000000000000000000000000000000000..88012cf8d124c6a6cb083d597fb92c689f1f9c5f
GIT binary patch
literal 231
zcmeAS@N?(olHy`uVBq!ia0vp^VnEE#!3HEB<l1KdDaPU;cPEB*=VV?2IV|apzK#qG
z8~eHcB(eheoCO|{#S9F5hd`K7RKu$QC|Kj^;uvD#|8~+v-Ub5>my3omlPzO5XDg(y
z5>h(k?)&G%q;}8hol*@hM-F)vKREL{D4=GB`OV#*lr_wBekZ@KO}TWbBO_2ttNQsr
z*>&gHHaC5%km%LpciH**SL6G`eP;wGe&?_^=SX!i*pU5m{q3dKn=U55QCgAlRz>L+
ZgPq=P>-f}=eL#0Gc)I$ztaD0e0s!B^RI~s9

literal 0
HcmV?d00001

diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab02e7d42d798f997296e721023759f7e6600d1f
GIT binary patch
literal 224
zcmeAS@N?(olHy`uVBq!ia0vp^VnEE#!3HEB<l1KdDaPU;cPEB*=VV?2IV|apzK#qG
z8~eHcB(eheoCO|{#S9F5hd`K7RKu$QC|K(0;uvD#e|1VC*8v3%x4Z9d@a>en^s%+g
z)in9v4LJ|NuROhGX==`sHpB>dJKQ<NCpk6P?SUVYA>XPuFOQYIm+1)POW*PR-6Q_z
zo0GmUm*y+)75yIP8P~1RI(2$wNN?y0PN&)7OB8})KH3+)7oHVpn!$6C_jzWT{LW=F
Rt^ggt;OXk;vd$@?2>^&mO+x?x

literal 0
HcmV?d00001

diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index b7fa1e72f8e..473b074213f 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -441,6 +441,9 @@ with the same filename but different name
   <texture name="ClipboardSmallMenu_Disabled" file_name="icons/ClipboardSmallMenu_Disabled.png" preload="false" />
   <texture name="ClipboardSmallMenu_Off" file_name="icons/ClipboardSmallMenu_Off.png" preload="false" />
   <texture name="ClipboardSmallMenu_Press" file_name="icons/ClipboardSmallMenu_Press.png" preload="false" />
+  <texture name="ClipboardMenu_Disabled" file_name="icons/ClipboardMenu_Disabled.png" preload="false" />
+  <texture name="ClipboardMenu_Off" file_name="icons/ClipboardMenu_Off.png" preload="false" />
+  <texture name="ClipboardMenu_Press" file_name="icons/ClipboardMenu_Press.png" preload="false" />
 
   <texture name="OutboxStatus_Success" file_name="green_checkmark.png" preload="false" />
   <texture name="OutboxStatus_Warning" file_name="icons/pop_up_caution.png" preload="false" />
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index afc61555852..0b2b1abeb93 100644
--- a/indra/newview/skins/default/xui/en/floater_tools.xml
+++ b/indra/newview/skins/default/xui/en/floater_tools.xml
@@ -1436,7 +1436,7 @@ even though the user gets a free copy.
              height="0"
              layout="topleft"
              left_delta="0"
-             name="lod_tab_border"
+             name="object_horizontal"
              top_pad="10"
              width="95" />
             <menu_button
@@ -1666,13 +1666,23 @@ even though the user gets a free copy.
              width="150">
                 Prim Type
             </text>-->
+
+            <view_border
+             bevel_style="none"
+             follows="top|left"
+             layout="topleft"
+             name="object_vertical"
+             left="117"
+             top="6"
+             height="500"
+             width="0"/>
             <combo_box
              height="19"
              layout="topleft"
              name="comboBaseType"
              top="6"
              left="125"
-             width="150">
+             width="125">
                 <combo_box.item
                  label="Box"
                  name="Box"
@@ -1706,15 +1716,28 @@ even though the user gets a free copy.
                  name="Sculpted"
                  value="Sculpted" />
             </combo_box>
+            <menu_button
+              menu_filename="menu_copy_paste_generic.xml"
+              follows="top|left"
+              height="15"
+              image_disabled="ClipboardMenu_Disabled"
+              image_selected="ClipboardMenu_Press"
+              image_unselected="ClipboardMenu_Off"
+              layout="topleft"
+              left_pad="8"
+              top_delta="2"
+              name="clipboard_params_btn"
+              tool_tip="Paste options"
+              width="22"/>
             <text
              type="string"
              length="1"
              follows="left|top"
              height="10"
              layout="topleft"
-             left_delta="0"
+             left="125"
              name="text cut"
-             top_pad="5"
+             top_pad="7"
              width="150">
                 Path Cut (begin/end)
             </text>
diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_generic.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_generic.xml
new file mode 100644
index 00000000000..8e016e4a1cd
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_copy_paste_generic.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ layout="topleft"
+ name="Copy Paste Generic Menu">
+    <menu_item_call
+     label="Copy"
+     layout="topleft"
+     name="params_copy"
+     visible="true">
+       <on_click function="PanelObject.menuDoToSelected" parameter="params_copy" />
+    </menu_item_call>
+    <menu_item_call
+     label="Paste"
+     layout="topleft"
+     name="params_paste"
+     visible="true">
+       <on_click function="PanelObject.menuDoToSelected" parameter="params_paste" />
+       <on_enable function="PanelObject.menuEnable" parameter="params_paste" />
+    </menu_item_call>
+</toggleable_menu>
+
diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_pos.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_pos.xml
index c2763af603e..3ea95b281f0 100644
--- a/indra/newview/skins/default/xui/en/menu_copy_paste_pos.xml
+++ b/indra/newview/skins/default/xui/en/menu_copy_paste_pos.xml
@@ -3,19 +3,35 @@
  layout="topleft"
  name="Copy Paste Position Menu">
     <menu_item_call
-     label="Copy Position"
+     label="Copy all"
+     layout="topleft"
+     name="psr_copy"
+     visible="true">
+       <on_click function="PanelObject.menuDoToSelected" parameter="psr_copy" />
+       <on_enable function="PanelObject.menuEnable" parameter="psr_copy" />
+    </menu_item_call>
+    <menu_item_call
+     label="Copy position"
      layout="topleft"
      name="pos_copy"
      visible="true">
-        <on_click function="PanelObject.menuDoToSelected" parameter="pos_copy" />
+       <on_click function="PanelObject.menuDoToSelected" parameter="pos_copy" />
+    </menu_item_call>
+    <menu_item_call
+     label="Paste all"
+     layout="topleft"
+     name="psr_paste"
+     visible="true">
+       <on_click function="PanelObject.menuDoToSelected" parameter="psr_paste" />
+       <on_enable function="PanelObject.menuEnable" parameter="psr_paste" />
     </menu_item_call>
     <menu_item_call
-     label="Paste Position"
+     label="Paste position"
      layout="topleft"
      name="pos_paste"
      visible="true">
-      <on_click function="PanelObject.menuDoToSelected" parameter="pos_paste" />
-      <on_enable function="PanelObject.menuEnable" parameter="pos_paste" />
+       <on_click function="PanelObject.menuDoToSelected" parameter="pos_paste" />
+       <on_enable function="PanelObject.menuEnable" parameter="pos_paste" />
     </menu_item_call>
 </toggleable_menu>
 
diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_rot.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_rot.xml
index dcfb3faeca5..06ce80f8977 100644
--- a/indra/newview/skins/default/xui/en/menu_copy_paste_rot.xml
+++ b/indra/newview/skins/default/xui/en/menu_copy_paste_rot.xml
@@ -3,19 +3,35 @@
  layout="topleft"
  name="Copy Paste Rotation Menu">
     <menu_item_call
-     label="Copy Rotation"
+     label="Copy all"
+     layout="topleft"
+     name="psr_copy"
+     visible="true">
+       <on_click function="PanelObject.menuDoToSelected" parameter="psr_copy" />
+       <on_enable function="PanelObject.menuEnable" parameter="rot_paste" />
+    </menu_item_call>
+    <menu_item_call
+     label="Copy rotation"
      layout="topleft"
      name="rot_copy"
      visible="true">
-        <on_click function="PanelObject.menuDoToSelected" parameter="rot_copy" />
+       <on_click function="PanelObject.menuDoToSelected" parameter="rot_copy" />
+    </menu_item_call>
+    <menu_item_call
+     label="Paste all"
+     layout="topleft"
+     name="psr_paste"
+     visible="true">
+       <on_click function="PanelObject.menuDoToSelected" parameter="psr_paste" />
+       <on_enable function="PanelObject.menuEnable" parameter="psr_paste" />
     </menu_item_call>
     <menu_item_call
-     label="Paste Rotation"
+     label="Paste rotation"
      layout="topleft"
      name="rot_paste"
      visible="true">
-      <on_click function="PanelObject.menuDoToSelected" parameter="rot_paste" />
-      <on_enable function="PanelObject.menuEnable" parameter="rot_paste" />
+       <on_click function="PanelObject.menuDoToSelected" parameter="rot_paste" />
+       <on_enable function="PanelObject.menuEnable" parameter="rot_paste" />
     </menu_item_call>
 </toggleable_menu>
 
diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_size.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_size.xml
index 58d71b12d54..7082a0e65b3 100644
--- a/indra/newview/skins/default/xui/en/menu_copy_paste_size.xml
+++ b/indra/newview/skins/default/xui/en/menu_copy_paste_size.xml
@@ -3,19 +3,35 @@
  layout="topleft"
  name="Copy Paste Size Menu">
     <menu_item_call
-     label="Copy Size"
+     label="Copy all"
+     layout="topleft"
+     name="psr_copy"
+     visible="true">
+       <on_click function="PanelObject.menuDoToSelected" parameter="psr_copy" />
+       <on_enable function="PanelObject.menuEnable" parameter="psr_copy" />
+    </menu_item_call>
+    <menu_item_call
+     label="Copy size"
      layout="topleft"
      name="size_copy"
      visible="true">
-        <on_click function="PanelObject.menuDoToSelected" parameter="size_copy" />
+       <on_click function="PanelObject.menuDoToSelected" parameter="size_copy" />
+    </menu_item_call>
+    <menu_item_call
+     label="Paste all"
+     layout="topleft"
+     name="psr_paste"
+     visible="true">
+       <on_click function="PanelObject.menuDoToSelected" parameter="psr_paste" />
+       <on_enable function="PanelObject.menuEnable" parameter="psr_paste" />
     </menu_item_call>
     <menu_item_call
-     label="Paste Size"
+     label="Paste size"
      layout="topleft"
      name="size_paste"
      visible="true">
-      <on_click function="PanelObject.menuDoToSelected" parameter="size_paste" />
-      <on_enable function="PanelObject.menuEnable" parameter="size_paste" />
+       <on_click function="PanelObject.menuDoToSelected" parameter="size_paste" />
+       <on_enable function="PanelObject.menuEnable" parameter="size_paste" />
     </menu_item_call>
 </toggleable_menu>
 
-- 
GitLab