From 343a3ef54fdabeb9c24c515fbb7cc624378f3004 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 3 Oct 2023 17:18:57 -0400
Subject: [PATCH] Add Catznip-flavored drag and drop for files with an alchemy
 twist

---
 indra/llwindow/lldragdropwin32.cpp            | 232 ++++++++++++++----
 indra/llwindow/lldragdropwin32.h              |  16 +-
 indra/llwindow/llwindowcallbacks.cpp          |   8 +-
 indra/llwindow/llwindowcallbacks.h            |  14 +-
 indra/llwindow/llwindowmacosx.cpp             |   9 +-
 indra/llwindow/llwindowwin32.cpp              |  11 +-
 indra/llwindow/llwindowwin32.h                |   6 +-
 indra/newview/lllocalbitmaps.cpp              |  65 +++--
 indra/newview/lllocalbitmaps.h                |   5 +
 indra/newview/lllocalgltfmaterials.cpp        |  39 ++-
 indra/newview/lllocalgltfmaterials.h          |   7 +-
 indra/newview/llviewermenufile.cpp            |  89 ++++---
 indra/newview/llviewerwindow.cpp              | 226 ++++++++++++++++-
 indra/newview/llviewerwindow.h                |  16 +-
 .../skins/default/xui/en/notifications.xml    |   2 +
 15 files changed, 621 insertions(+), 124 deletions(-)

diff --git a/indra/llwindow/lldragdropwin32.cpp b/indra/llwindow/lldragdropwin32.cpp
index 0d1a47408bb..319bb460254 100644
--- a/indra/llwindow/lldragdropwin32.cpp
+++ b/indra/llwindow/lldragdropwin32.cpp
@@ -43,9 +43,12 @@ class LLDragDropWin32Target:
 		//
 		LLDragDropWin32Target( HWND  hWnd ) :
 			mRefCount( 1 ),
-			mAppWindowHandle( hWnd ),
-			mAllowDrop(false),
-			mIsSlurl(false)
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+			mAppWindowHandle(hWnd)
+// [/SL:KB]
+//			mAppWindowHandle( hWnd ),
+//			mAllowDrop(false),
+//			mIsSlurl(false)
 		{
 		};
 
@@ -98,39 +101,87 @@ class LLDragDropWin32Target:
 		//
 		HRESULT __stdcall DragEnter( IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect )
 		{
-			FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+			*pdwEffect = DROPEFFECT_NONE;
+			mDropType = LLWindowCallbacks::DNDT_NONE;
+			mDropData.clear();
 
-			// support CF_TEXT using a HGLOBAL?
-			if ( S_OK == pDataObject->QueryGetData( &fmtetc ) )
+			IEnumFORMATETC* pEnumFmt = NULL;
+			if ( (S_OK == pDataObject->EnumFormatEtc(DATADIR_GET, &pEnumFmt)) && (pEnumFmt) )
 			{
-				mAllowDrop = true;
-				mDropUrl = std::string();
-				mIsSlurl = false;
-
-				STGMEDIUM stgmed;
-				if( S_OK == pDataObject->GetData( &fmtetc, &stgmed ) )
+				FORMATETC fmtetc; bool fContinue = true;
+				while ( (fContinue) && (S_OK == pEnumFmt->Next(1, &fmtetc, NULL)) )
 				{
-					PVOID data = GlobalLock( stgmed.hGlobal );
-					mDropUrl = std::string( (char*)data );
-					// XXX MAJOR MAJOR HACK!
-					LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA );
-					if (NULL != window_imp)
+					switch (fmtetc.cfFormat)
 					{
-						LLCoordGL gl_coord( 0, 0 );
+						case CF_HDROP:	// Files dropped from Explorer
+							{
+								fContinue = false;
+
+								STGMEDIUM stgmed;
+								if (S_OK == pDataObject->GetData(&fmtetc, &stgmed))
+								{
+									wchar_t strFilePath[MAX_PATH];
+
+									HDROP hDrop = (HDROP)GlobalLock(stgmed.hGlobal);
+									for (int idxFile = 0, cntFile = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); idxFile < cntFile; idxFile++)
+									{
+										UINT nRet = DragQueryFile(hDrop, idxFile, strFilePath, sizeof(strFilePath));
+										if (nRet)
+										{
+											std::string meowstr = ll_convert_wide_to_string(strFilePath);
+											LL_INFOS() << meowstr << LL_ENDL;
+											mDropData.push_back(meowstr);
+										}
+									}
+									GlobalUnlock(stgmed.hGlobal);
+
+									ReleaseStgMedium(&stgmed);
+
+									mDropType = LLWindowCallbacks::DNDT_FILE;
+								}
+							}
+							break;
+						case CF_TEXT:
+							{
+								fContinue = false;
+
+								STGMEDIUM stgmed;
+								if (S_OK == pDataObject->GetData(&fmtetc, &stgmed))
+								{
+									void* pData = GlobalLock(stgmed.hGlobal);
+									mDropData.push_back(std::string((char*)pData));
+									GlobalUnlock( stgmed.hGlobal );
+
+									ReleaseStgMedium(&stgmed);
+
+									mDropType = LLWindowCallbacks::DNDT_DEFAULT;
+								}
+							}
+							break;
+						default:
+							break;
+					}
+				}
+			}
 
-						POINT pt2;
-						pt2.x = pt.x;
-						pt2.y = pt.y;
-						ScreenToClient( mAppWindowHandle, &pt2 );
+			if (LLWindowCallbacks::DNDT_NONE != mDropType)
+			{
+				// XXX MAJOR MAJOR HACK!
+				LLWindowWin32* window_imp = (LLWindowWin32*)GetWindowLongPtr(mAppWindowHandle, GWLP_USERDATA);
+				if (NULL != window_imp)
+				{
+					LLCoordGL gl_coord(0, 0);
 
-						LLCoordWindow cursor_coord_window( pt2.x, pt2.y );
-						MASK mask = gKeyboard->currentMask(TRUE);
+					POINT pt2  = { pt.x, pt.y };
+					ScreenToClient(mAppWindowHandle, &pt2);
 
-						LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, 
-							LLWindowCallbacks::DNDA_START_TRACKING, mDropUrl );
+					LLCoordWindow cursor_coord_window(pt2.x, pt2.y);
+					MASK mask = gKeyboard->currentMask(TRUE);
 
-						switch (result)
-						{
+					LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest(cursor_coord_window.convert(), mask, LLWindowCallbacks::DNDA_START_TRACKING, mDropType, mDropData);
+					switch (result)
+					{
 						case LLWindowCallbacks::DND_COPY:
 							*pdwEffect = DROPEFFECT_COPY;
 							break;
@@ -144,28 +195,85 @@ class LLDragDropWin32Target:
 						default:
 							*pdwEffect = DROPEFFECT_NONE;
 							break;
-						}
-					};
+					}
+				}
 
-					GlobalUnlock( stgmed.hGlobal );
-					ReleaseStgMedium( &stgmed );
-				};
 				SetFocus( mAppWindowHandle );
 			}
-			else
-			{
-				mAllowDrop = false;
-				*pdwEffect = DROPEFFECT_NONE;
-			};
-
 			return S_OK;
+// [/SL:KB]
+//			FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+//
+//			// support CF_TEXT using a HGLOBAL?
+//			if ( S_OK == pDataObject->QueryGetData( &fmtetc ) )
+//			{
+//				mAllowDrop = true;
+//				mDropUrl = std::string();
+//				mIsSlurl = false;
+//
+//				STGMEDIUM stgmed;
+//				if( S_OK == pDataObject->GetData( &fmtetc, &stgmed ) )
+//				{
+//					PVOID data = GlobalLock( stgmed.hGlobal );
+//					mDropUrl = std::string( (char*)data );
+//					// XXX MAJOR MAJOR HACK!
+//					LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(mAppWindowHandle, GWL_USERDATA);
+//					if (NULL != window_imp)
+//					{
+//						LLCoordGL gl_coord( 0, 0 );
+//
+//						POINT pt2;
+//						pt2.x = pt.x;
+//						pt2.y = pt.y;
+//						ScreenToClient( mAppWindowHandle, &pt2 );
+//
+//						LLCoordWindow cursor_coord_window( pt2.x, pt2.y );
+//						MASK mask = gKeyboard->currentMask(TRUE);
+//
+//						LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, 
+//							LLWindowCallbacks::DNDA_START_TRACKING, mDropUrl );
+//
+//						switch (result)
+//						{
+//						case LLWindowCallbacks::DND_COPY:
+//							*pdwEffect = DROPEFFECT_COPY;
+//							break;
+//						case LLWindowCallbacks::DND_LINK:
+//							*pdwEffect = DROPEFFECT_LINK;
+//							break;
+//						case LLWindowCallbacks::DND_MOVE:
+//							*pdwEffect = DROPEFFECT_MOVE;
+//							break;
+//						case LLWindowCallbacks::DND_NONE:
+//						default:
+//							*pdwEffect = DROPEFFECT_NONE;
+//							break;
+//						}
+//					};
+//
+//					GlobalUnlock( stgmed.hGlobal );
+//					ReleaseStgMedium( &stgmed );
+//				};
+//				SetFocus( mAppWindowHandle );
+//			}
+//			else
+//			{
+//				mAllowDrop = false;
+//				*pdwEffect = DROPEFFECT_NONE;
+//			};
+//
+//			return S_OK;
 		};
 
 		////////////////////////////////////////////////////////////////////////////////
 		//
 		HRESULT __stdcall DragOver( DWORD grfKeyState, POINTL pt, DWORD* pdwEffect )
 		{
-			if ( mAllowDrop )
+//			if ( mAllowDrop )
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+			*pdwEffect = DROPEFFECT_NONE;
+			if (LLWindowCallbacks::DNDT_NONE != mDropType)
+// [/SL:KB]
 			{
 				// XXX MAJOR MAJOR HACK!
 				LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA );
@@ -181,8 +289,11 @@ class LLDragDropWin32Target:
 					LLCoordWindow cursor_coord_window( pt2.x, pt2.y );
 					MASK mask = gKeyboard->currentMask(TRUE);
 
-					LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, 
-						LLWindowCallbacks::DNDA_TRACK, mDropUrl );
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+					LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest(cursor_coord_window.convert(), mask, LLWindowCallbacks::DNDA_TRACK, mDropType, mDropData);
+// [/SL:KB]
+//					LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, 
+//						LLWindowCallbacks::DNDA_TRACK, mDropUrl );
 					
 					switch (result)
 					{
@@ -202,10 +313,10 @@ class LLDragDropWin32Target:
 					}
 				};
 			}
-			else
-			{
-				*pdwEffect = DROPEFFECT_NONE;
-			};
+//			else
+//			{
+//				*pdwEffect = DROPEFFECT_NONE;
+//			};
 
 			return S_OK;
 		};
@@ -220,7 +331,10 @@ class LLDragDropWin32Target:
 			{
 				LLCoordGL gl_coord( 0, 0 );
 				MASK mask = gKeyboard->currentMask(TRUE);
-				window_imp->completeDragNDropRequest( gl_coord, mask, LLWindowCallbacks::DNDA_STOP_TRACKING, mDropUrl );
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+				window_imp->completeDragNDropRequest(gl_coord, mask, LLWindowCallbacks::DNDA_STOP_TRACKING, mDropType, mDropData);
+// [/SL:KB]
+//				window_imp->completeDragNDropRequest( gl_coord, mask, LLWindowCallbacks::DNDA_STOP_TRACKING, mDropUrl );
 			};
 			return S_OK;
 		};
@@ -229,7 +343,10 @@ class LLDragDropWin32Target:
 		//
 		HRESULT __stdcall Drop( IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect )
 		{
-			if ( mAllowDrop )
+//			if ( mAllowDrop )
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+			if (LLWindowCallbacks::DNDT_NONE != mDropType)
+// [/SL:KB]
 			{
 				// window impl stored in Window data (neat!)
 				LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA );
@@ -242,7 +359,7 @@ class LLDragDropWin32Target:
 
 					LLCoordWindow cursor_coord_window( pt_client.x, pt_client.y );
 					LLCoordGL gl_coord(cursor_coord_window.convert());
-					LL_INFOS() << "### (Drop) URL is: " << mDropUrl << LL_ENDL;
+//					LL_INFOS() << "### (Drop) URL is: " << mDropUrl << LL_ENDL;
 					LL_INFOS() << "###        raw coords are: " << pt.x << " x " << pt.y << LL_ENDL;
 					LL_INFOS() << "###	    client coords are: " << pt_client.x << " x " << pt_client.y << LL_ENDL;
 					LL_INFOS() << "###         GL coords are: " << gl_coord.mX << " x " << gl_coord.mY << LL_ENDL;
@@ -252,8 +369,11 @@ class LLDragDropWin32Target:
 					MASK mask = gKeyboard->currentMask( TRUE );
 
 					// actually do the drop
-					LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, mask, 
-						LLWindowCallbacks::DNDA_DROPPED, mDropUrl );
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+					LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest(gl_coord, mask, LLWindowCallbacks::DNDA_DROPPED, mDropType, mDropData);
+// [/SL:KB]
+//					LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, mask, 
+//						LLWindowCallbacks::DNDA_DROPPED, mDropUrl );
 
 					switch (result)
 					{
@@ -286,9 +406,13 @@ class LLDragDropWin32Target:
 	private:
 		LONG mRefCount;
 		HWND mAppWindowHandle;
-		bool mAllowDrop;
-		std::string mDropUrl;
-		bool mIsSlurl;
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+		LLWindowCallbacks::DragNDropType mDropType;
+		std::vector<std::string>         mDropData;
+// [/SL:KB]
+//		bool mAllowDrop;
+//		std::string mDropUrl;
+//		bool mIsSlurl;
 		friend class LLWindowWin32;
 };
 
diff --git a/indra/llwindow/lldragdropwin32.h b/indra/llwindow/lldragdropwin32.h
index 4673242cbab..89a7bba0f62 100644
--- a/indra/llwindow/lldragdropwin32.h
+++ b/indra/llwindow/lldragdropwin32.h
@@ -24,14 +24,17 @@
  * $/LicenseInfo$
  */
 
+#ifndef LL_LLDRAGDROP32_H
+#define LL_LLDRAGDROP32_H
+
 #if LL_WINDOWS
 
 #if LL_OS_DRAGDROP_ENABLED
 
-#ifndef LL_LLDRAGDROP32_H
-#define LL_LLDRAGDROP32_H
-
 #include "llwin32headerslean.h"
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+#include <shellapi.h>
+// [/SL:KB]
 #include <ole2.h>
 
 class LLDragDropWin32
@@ -47,13 +50,9 @@ class LLDragDropWin32
 		IDropTarget* mDropTarget;
 		HWND mDropWindowHandle;
 };
-#endif // LL_LLDRAGDROP32_H
 
 #else // LL_OS_DRAGDROP_ENABLED
 
-#ifndef LL_LLDRAGDROP32_H
-#define LL_LLDRAGDROP32_H
-
 #include "llwin32headerslean.h"
 #include <ole2.h>
 
@@ -67,8 +66,9 @@ class LLDragDropWin32
 		bool init( HWND hWnd ) { return false; };
 		void reset() { };
 };
-#endif // LL_LLDRAGDROP32_H
 
 #endif // LL_OS_DRAGDROP_ENABLED
 
 #endif // LL_WINDOWS
+
+#endif // LL_LLDRAGDROP32_H
\ No newline at end of file
diff --git a/indra/llwindow/llwindowcallbacks.cpp b/indra/llwindow/llwindowcallbacks.cpp
index 39e9f3a3a26..7279f1bcab7 100644
--- a/indra/llwindow/llwindowcallbacks.cpp
+++ b/indra/llwindow/llwindowcallbacks.cpp
@@ -170,10 +170,16 @@ void LLWindowCallbacks::handleDataCopy(LLWindow *window, S32 data_type, void *da
 {
 }
 
-LLWindowCallbacks::DragNDropResult LLWindowCallbacks::handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, std::string data )
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+LLWindowCallbacks::DragNDropResult LLWindowCallbacks::handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, DragNDropType type, const std::vector<std::string>& data)
 {
 	return LLWindowCallbacks::DND_NONE;
 }
+// [/SL:KB]
+//LLWindowCallbacks::DragNDropResult LLWindowCallbacks::handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, std::string data )
+//{
+//	return LLWindowCallbacks::DND_NONE;
+//}
 
 BOOL LLWindowCallbacks::handleTimerEvent(LLWindow *window)
 {
diff --git a/indra/llwindow/llwindowcallbacks.h b/indra/llwindow/llwindowcallbacks.h
index 15b0add5943..9f0befbad09 100644
--- a/indra/llwindow/llwindowcallbacks.h
+++ b/indra/llwindow/llwindowcallbacks.h
@@ -76,13 +76,25 @@ class LLWindowCallbacks
 		DNDA_DROPPED			// User dropped an incoming drag on the window (this is the "commit" event)
 	};
 	
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+	enum DragNDropType
+	{
+		DNDT_FILE,		// User is dragging one or more files over the window
+		DNDT_DEFAULT,	// Default LL behaviour, whatever that is
+		DNDT_NONE
+	};
+// [/SL:KB]
+
 	enum DragNDropResult {
 		DND_NONE = 0,	// No drop allowed
 		DND_MOVE,		// Drop accepted would result in a "move" operation
 		DND_COPY,		// Drop accepted would result in a "copy" operation
 		DND_LINK		// Drop accepted would result in a "link" operation
 	};
-	virtual DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, std::string data);
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+	virtual DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, DragNDropType type, const std::vector<std::string>& data);
+// [/SL:KB]
+//	virtual DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, std::string data);
 	
 	virtual void handlePingWatchdog(LLWindow *window, const char * msg);
 	virtual void handlePauseWatchdog(LLWindow *window);
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index b7ab75f992e..0602f79b4f7 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -2063,8 +2063,13 @@ void LLWindowMacOSX::handleDragNDrop(std::string url, LLWindowCallbacks::DragNDr
 	
 	if(!url.empty())
 	{
-		LLWindowCallbacks::DragNDropResult res =
-		mCallbacks->handleDragNDrop(this, gl_pos, mask, action, url);
+//		LLWindowCallbacks::DragNDropResult res =
+//		mCallbacks->handleDragNDrop(this, gl_pos, mask, action, url);
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+		std::vector<std::string> data;
+		data.push_back(url);
+		LLWindowCallbacks::DragNDropResult res = mCallbacks->handleDragNDrop(this, gl_pos, mask, action, LLWindowCallbacks::DNDT_DEFAULT, data);
+// [/SL:KB]
 		
 		switch (res) {
 			case LLWindowCallbacks::DND_NONE:		// No drop allowed
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 79475878001..9b5bd184332 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -4414,11 +4414,18 @@ static LLWString find_context(const LLWString & wtext, S32 focus, S32 focus_leng
 
 // final stage of handling drop requests - both from WM_DROPFILES message
 // for files and via IDropTarget interface requests.
-LLWindowCallbacks::DragNDropResult LLWindowWin32::completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url )
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+LLWindowCallbacks::DragNDropResult LLWindowWin32::completeDragNDropRequest(const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, 
+                                                                           LLWindowCallbacks::DragNDropType type, const std::vector<std::string>& data)
 {
     ASSERT_MAIN_THREAD();
-	return mCallbacks->handleDragNDrop( this, gl_coord, mask, action, url );
+	return mCallbacks->handleDragNDrop(this, gl_coord, mask, action, type, data);
 }
+// [/SL:KB]
+//LLWindowCallbacks::DragNDropResult LLWindowWin32::completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url )
+//{
+//	return mCallbacks->handleDragNDrop( this, gl_coord, mask, action, url );
+//}
 
 // Handle WM_IME_REQUEST message.
 // If it handled the message, returns TRUE.  Otherwise, FALSE.
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index 15873815d7b..e17d683f113 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -164,7 +164,11 @@ class LLWindowWin32 final : public LLWindow
 
 	/*virtual*/ F32 getSystemUISize();
 
-	LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url );
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+	LLWindowCallbacks::DragNDropResult completeDragNDropRequest(const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, 
+		LLWindowCallbacks::DragNDropType type, const std::vector<std::string>& data);
+// [/SL:KB]
+//	LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url );
 
 	static std::vector<std::string> getDisplaysResolutionList();
 	static std::vector<std::string> getDynamicFallbackFontList();
diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp
index a91eef480f6..e1ab95b7f31 100644
--- a/indra/newview/lllocalbitmaps.cpp
+++ b/indra/newview/lllocalbitmaps.cpp
@@ -944,46 +944,67 @@ LLLocalBitmapMgr::~LLLocalBitmapMgr()
 bool LLLocalBitmapMgr::addUnit(const std::vector<std::string>& filenames)
 {
     bool add_successful = false;
+	mTimer.stopTimer();
     std::vector<std::string>::const_iterator iter = filenames.begin();
     while (iter != filenames.end())
     {
-        if (!iter->empty() && addUnit(*iter).notNull())
+        if (!iter->empty() && addUnitInternal(*iter).notNull())
         {
             add_successful = true;
         }
         iter++;
     }
+	mTimer.startTimer();
     return add_successful;
 }
 
 LLUUID LLLocalBitmapMgr::addUnit(const std::string& filename)
 {
-    if (!checkTextureDimensions(filename))
-    {
-        return LLUUID::null;
-    }
+	mTimer.stopTimer();
+	LLUUID tracking_id = addUnitInternal(filename);
+	mTimer.startTimer();
+	return tracking_id;
+}
 
-    LLLocalBitmap* unit = new LLLocalBitmap(filename);
+LLUUID LLLocalBitmapMgr::addUnitInternal(const std::string& filename)
+{
+	if (!checkTextureDimensions(filename))
+	{
+		return LLUUID::null;
+	}
 
-    if (unit->getValid())
-    {
-        mBitmapList.push_back(unit);
-        return unit->getTrackingID();
-    }
-    else
-    {
-        LL_WARNS() << "Attempted to add invalid or unreadable image file, attempt cancelled.\n"
-            << "Filename: " << filename << LL_ENDL;
+	LLLocalBitmap* unit = new LLLocalBitmap(filename);
+	if (unit->getValid())
+	{
+		mBitmapList.push_back(unit);
+	}
+	else
+	{
+		LL_WARNS() << "Attempted to add invalid or unreadable image file, attempt cancelled.\n"
+				<< "Filename: " << filename << LL_ENDL;
 
-        LLSD notif_args;
-        notif_args["FNAME"] = filename;
-        LLNotificationsUtil::add("LocalBitmapsVerifyFail", notif_args);
+		LLSD notif_args;
+		notif_args["FNAME"] = filename;
+		LLNotificationsUtil::add("LocalBitmapsVerifyFail", notif_args);
 
-        delete unit;
-        unit = NULL;
-    }
+		delete unit;
+		unit = NULL;
+	}
 
-    return LLUUID::null;
+	return (unit) ? unit->getTrackingID() : LLUUID::null;
+}
+
+LLUUID LLLocalBitmapMgr::getUnitID(const std::string& filename)
+{
+	for (local_list_iter itBitmap = mBitmapList.begin(); mBitmapList.end() != itBitmap; ++itBitmap)
+	{
+		LLLocalBitmap* unit = *itBitmap;
+		if (filename == unit->getFilename())
+		{
+			return unit->getTrackingID();
+		}
+	}
+	return LLUUID::null;
 }
 
 bool LLLocalBitmapMgr::checkTextureDimensions(std::string filename)
diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h
index d20b1cfe692..c6ada6ac878 100644
--- a/indra/newview/lllocalbitmaps.h
+++ b/indra/newview/lllocalbitmaps.h
@@ -117,7 +117,12 @@ class LLLocalBitmapMgr final : public LLSingleton<LLLocalBitmapMgr>
 	~LLLocalBitmapMgr();
 public:
     bool         addUnit(const std::vector<std::string>& filenames);
+protected:
+	LLUUID       addUnitInternal(const std::string& filename);
+public:
     LLUUID       addUnit(const std::string& filename);
+	LLUUID       getUnitID(const std::string& filename);
+// [/SL:KB]
 	void         delUnit(LLUUID tracking_id);
 	bool 		checkTextureDimensions(std::string filename);
 
diff --git a/indra/newview/lllocalgltfmaterials.cpp b/indra/newview/lllocalgltfmaterials.cpp
index 8ca135fa8ee..a9e1ee63784 100644
--- a/indra/newview/lllocalgltfmaterials.cpp
+++ b/indra/newview/lllocalgltfmaterials.cpp
@@ -336,20 +336,37 @@ LLLocalGLTFMaterialMgr::~LLLocalGLTFMaterialMgr()
 
 S32 LLLocalGLTFMaterialMgr::addUnit(const std::vector<std::string>& filenames)
 {
+    mTimer.stopTimer();
     S32 add_count = 0;
+    LLUUID outid;
     std::vector<std::string>::const_iterator iter = filenames.begin();
     while (iter != filenames.end())
     {
         if (!iter->empty())
         {
-            add_count += addUnit(*iter);
+            add_count += addUnitInternal(*iter, outid);
         }
         iter++;
     }
+    mTimer.startTimer();
     return add_count;
 }
 
 S32 LLLocalGLTFMaterialMgr::addUnit(const std::string& filename)
+{
+    LLUUID out;
+    return addUnit(filename, out);
+}
+
+S32 LLLocalGLTFMaterialMgr::addUnit(const std::string& filename, LLUUID& outID)
+{
+    mTimer.stopTimer();
+    S32 res = addUnitInternal(filename, outID);
+    mTimer.startTimer();
+    return res;
+}
+
+S32 LLLocalGLTFMaterialMgr::addUnitInternal(const std::string& filename, LLUUID& outID)
 {
     tinygltf::Model model;
     LLTinyGLTFHelper::loadModel(filename, model);
@@ -372,6 +389,10 @@ S32 LLLocalGLTFMaterialMgr::addUnit(const std::string& filename)
         if (unit->updateSelf())
         {
             mMaterialList.emplace_back(unit);
+            if(loaded_materials == 0)
+            {
+                outID = unit->getTrackingID();
+            }
             loaded_materials++;
         }
         else
@@ -415,6 +436,22 @@ void LLLocalGLTFMaterialMgr::delUnit(LLUUID tracking_id)
     }
 }
 
+LLUUID LLLocalGLTFMaterialMgr::getUnitID(const std::string& filename, S32 index)
+{
+    if (!mMaterialList.empty())
+    {
+        for (local_list_iter itBitmap = mMaterialList.begin(); mMaterialList.end() != itBitmap; ++itBitmap)
+        {
+            LLLocalGLTFMaterial* unit = *itBitmap;
+            if (filename == unit->getFilename() && index == unit->getIndexInFile())
+            {
+                return unit->getTrackingID();
+            }
+        }
+    }
+    return LLUUID::null;
+}
+
 LLUUID LLLocalGLTFMaterialMgr::getWorldID(LLUUID tracking_id)
 {
     LLUUID world_id = LLUUID::null;
diff --git a/indra/newview/lllocalgltfmaterials.h b/indra/newview/lllocalgltfmaterials.h
index 1442b83a404..f0cffe117ae 100644
--- a/indra/newview/lllocalgltfmaterials.h
+++ b/indra/newview/lllocalgltfmaterials.h
@@ -103,8 +103,13 @@ class LLLocalGLTFMaterialMgr : public LLSingleton<LLLocalGLTFMaterialMgr>
     ~LLLocalGLTFMaterialMgr();
 public:
     S32          addUnit(const std::vector<std::string>& filenames);
-    S32          addUnit(const std::string& filename); // file can hold multiple materials
+    S32          addUnit(const std::string& filename);
+    S32          addUnit(const std::string& filename, LLUUID& outID); // returns first material id as outID
+protected:
+    S32          addUnitInternal(const std::string& filename, LLUUID& outID); // file can hold multiple materials
+public:
     void         delUnit(LLUUID tracking_id);
+    LLUUID       getUnitID(const std::string& filename, S32 index = 0);
 
     LLUUID       getWorldID(LLUUID tracking_id);
     bool         isLocal(LLUUID world_id);
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index a7faeea9877..1706ed8234c 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -499,41 +499,46 @@ const void upload_single_file(const std::vector<std::string>& filenames, LLFileP
 				LLNotificationsUtil::add(error_msg, args);
 				return;
 			}
-			else
-			{
-				LLFloaterReg::showInstance("upload_sound", LLSD(filename));
-			}
 		}
-		if (type == LLFilePicker::FFLOAD_IMAGE)
+
+		std::string floater_name;
+
+		switch (type)
 		{
-			LLFloaterReg::showInstance("upload_image", LLSD(filename));
-		}
-		if (type == LLFilePicker::FFLOAD_ANIM)
+		case LLFilePicker::FFLOAD_ANIM:
 		{
 			std::string filename_lc(filename);
 			LLStringUtil::toLower(filename_lc);
-			if (filename_lc.rfind(".anim") != std::string::npos)
-			{
-				LLFloaterReg::showInstance("upload_anim_anim", LLSD(filename));
-			}
-			else
-			{
-				LLFloaterReg::showInstance("upload_anim_bvh", LLSD(filename));
-			}
-		}		
+			floater_name = (filename_lc.rfind(".anim") != std::string::npos) ? "upload_anim_anim" : "upload_anim_bvh";
+		}
+		break;
+		case LLFilePicker::FFLOAD_IMAGE:
+			floater_name = "upload_image";
+			break;
+		case LLFilePicker::FFLOAD_WAV:
+			floater_name = "upload_sound";
+			break;
+		case LLFilePicker::FFLOAD_MODEL:
+			if (LLFloaterModelPreview* pFloaterModelPreview = LLFloaterReg::getTypedInstance<LLFloaterModelPreview>("upload_model"))
+				pFloaterModelPreview->loadModel(LLModel::LOD_HIGH, filename);
+			return;
+		case LLFilePicker::FFLOAD_GLTF:
+			LLMaterialEditor::loadMaterialFromFile(filename, -1);
+			return;
+		default:
+			break;
+		}
+
+		if (!floater_name.empty())
+		{
+			LLFloaterReg::showInstance(floater_name, LLSD(filename));
+		}
 	}
 	return;
 }
 
-void do_bulk_upload(std::vector<std::string> filenames, const LLSD& notification, const LLSD& response)
+void do_bulk_upload(std::vector<std::string> filenames)
 {
-	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
-	if (option != 0)
-	{
-		// Cancel upload
-		return;
-	}
-
 	for (std::vector<std::string>::const_iterator in_iter = filenames.begin(); in_iter != filenames.end(); ++in_iter)
 	{
 		std::string filename = (*in_iter);
@@ -585,6 +590,18 @@ void do_bulk_upload(std::vector<std::string> filenames, const LLSD& notification
     }
 }
 
+void do_bulk_upload(std::vector<std::string> filenames, const LLSD& notification, const LLSD& response)
+{
+	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+	if (option != 0)
+	{
+		// Cancel upload
+		return;
+	}
+
+	do_bulk_upload(filenames);
+}
+
 bool get_bulk_upload_expected_cost(const std::vector<std::string>& filenames, S32& total_cost, S32& file_count)
 {
 	total_cost = 0;
@@ -680,10 +697,24 @@ const void upload_bulk(const std::vector<std::string>& filenames, LLFilePicker::
 	S32 expected_upload_count;
 	if (get_bulk_upload_expected_cost(filtered_filenames, expected_upload_cost, expected_upload_count))
 	{
-		LLSD args;
-		args["COST"] = expected_upload_cost;
-		args["COUNT"] = expected_upload_count;
-		LLNotificationsUtil::add("BulkUploadCostConfirmation",  args, LLSD(), boost::bind(do_bulk_upload, filtered_filenames, _1, _2));
+		static LLCachedControl<bool> sPowerfulWizard(gSavedSettings, "AlchemyPowerfulWizard", false);
+		if (sPowerfulWizard && expected_upload_cost == 0)
+		{
+			do_bulk_upload(filtered_filenames);
+		}
+		else
+		{
+			LLSD args;
+			args["COST"] = expected_upload_cost;
+			args["COUNT"] = expected_upload_count;
+
+			std::string strUploadList;
+			for (const std::string& filename : filtered_filenames)
+				strUploadList += gDirUtilp->getBaseFileName(filename) + "\n";
+			args["FILES"] = strUploadList;
+
+			LLNotificationsUtil::add("BulkUploadCostConfirmation", args, LLSD(), boost::bind(do_bulk_upload, filtered_filenames, _1, _2));
+		}
 
 		if (filtered_filenames.size() > expected_upload_count)
 		{
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 7bc33a6ede7..e63fae128b3 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -87,6 +87,9 @@
 
 // newview includes
 #include "llagent.h"
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+#include "llagentbenefits.h"
+// [/SL:KB]
 #include "llbox.h"
 #include "llchicletbar.h"
 #include "llconsole.h"
@@ -128,6 +131,8 @@
 #include "llimageworker.h"
 #include "llkeyboard.h"
 #include "lllineeditor.h"
+#include "lllocalbitmaps.h"
+#include "lllocalgltfmaterials.h"
 #include "llmenugl.h"
 #include "llmenuoptionpathfindingrebakenavmesh.h"
 #include "llmodaldialog.h"
@@ -178,6 +183,9 @@
 #include "llviewermedia.h"
 #include "llviewermediafocus.h"
 #include "llviewermenu.h"
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+#include "llviewermenufile.h"
+// [/SL:KB]
 #include "llviewermessage.h"
 #include "llviewerobjectlist.h"
 #include "llviewerparcelmgr.h"
@@ -224,6 +232,9 @@
 #include "llwindowwin32.h" // For AltGr handling
 #endif
 
+const void upload_single_file(const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter type);
+const void upload_bulk(const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter type);
+
 //
 // Globals
 //
@@ -1284,7 +1295,29 @@ BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window,  LLCoordGL pos, MAS
 	return TRUE;
 }
 
-LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data)
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, 
+                                                                   LLWindowCallbacks::DragNDropType type, const std::vector<std::string>& data)
+{
+	LLWindowCallbacks::DragNDropResult result = LLWindowCallbacks::DND_NONE;
+	switch (type)
+	{
+		case LLWindowCallbacks::DNDT_FILE:
+			result = handleDragNDropFile(window, pos, mask, action, type, data);
+			break;
+		case LLWindowCallbacks::DNDT_DEFAULT:
+		default:
+			result = handleDragNDropDefault(window, pos, mask, action, (!data.empty()) ? data.front() : LLStringUtil::null);
+			break;
+	}
+	return result;
+}
+// [/SL:KB]
+
+//LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data)
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDropDefault(LLWindow* window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data)
+// [/SL:KB]
 {
 	LLWindowCallbacks::DragNDropResult result = LLWindowCallbacks::DND_NONE;
 
@@ -1430,6 +1463,197 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi
 	return result;
 }
 
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+// This really should be standardized somewhere instead of having extensions duplicated all over the code
+typedef std::tuple<const char*, LLAssetType::EType, LLFilePicker::ELoadFilter> dragdrop_type_lookup_t;
+static dragdrop_type_lookup_t s_DragDropTypesLookup[] = {
+	std::make_tuple("bmp", LLAssetType::AT_TEXTURE, LLFilePicker::FFLOAD_IMAGE),
+	std::make_tuple("jpg", LLAssetType::AT_TEXTURE, LLFilePicker::FFLOAD_IMAGE),
+	std::make_tuple("jpeg", LLAssetType::AT_TEXTURE, LLFilePicker::FFLOAD_IMAGE),
+	std::make_tuple("png", LLAssetType::AT_TEXTURE, LLFilePicker::FFLOAD_IMAGE),
+	std::make_tuple("tga", LLAssetType::AT_TEXTURE, LLFilePicker::FFLOAD_IMAGE),
+	std::make_tuple("webp", LLAssetType::AT_TEXTURE, LLFilePicker::FFLOAD_IMAGE),
+	std::make_tuple("bvh", LLAssetType::AT_ANIMATION, LLFilePicker::FFLOAD_ANIM),
+	std::make_tuple("anim", LLAssetType::AT_ANIMATION, LLFilePicker::FFLOAD_ANIM),
+	std::make_tuple("wav", LLAssetType::AT_SOUND_WAV, LLFilePicker::FFLOAD_WAV),
+	std::make_tuple("dae", LLAssetType::AT_MESH, LLFilePicker::FFLOAD_MODEL),
+	std::make_tuple("gltf", LLAssetType::AT_MATERIAL, LLFilePicker::FFLOAD_GLTF),
+	std::make_tuple("glb", LLAssetType::AT_MATERIAL, LLFilePicker::FFLOAD_GLTF),
+};
+
+static LLAssetType::EType getAssetTypeFromFilename(const std::string strFilename)
+{
+	const std::string& strExt = gDirUtilp->getExtension(strFilename);
+	for (int idxLookup = 0, cntLookup = sizeof(s_DragDropTypesLookup) / sizeof(dragdrop_type_lookup_t); idxLookup < cntLookup; idxLookup++)
+	{
+		if (strExt == std::get<0>(s_DragDropTypesLookup[idxLookup]))
+			return std::get<1>(s_DragDropTypesLookup[idxLookup]);
+	}
+	return LLAssetType::AT_NONE;
+}
+
+static LLFilePicker::ELoadFilter getLoadFilterFromFilename(const std::string strFilename)
+{
+	const std::string& strExt = gDirUtilp->getExtension(strFilename);
+	for (int idxLookup = 0, cntLookup = sizeof(s_DragDropTypesLookup) / sizeof(dragdrop_type_lookup_t); idxLookup < cntLookup; idxLookup++)
+	{
+		if (strExt == std::get<0>(s_DragDropTypesLookup[idxLookup]))
+			return std::get<2>(s_DragDropTypesLookup[idxLookup]);
+	}
+	return LLFilePicker::FFLOAD_ALL;
+}
+
+LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDropFile(LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action,
+                                                                       LLWindowCallbacks::DragNDropType type, const std::vector<std::string>& data)
+{
+	LL_INFOS() << "BARK" << LL_ENDL;
+	bool fDrop = (LLWindowCallbacks::DNDA_DROPPED == action);
+
+	LLWindowCallbacks::DragNDropResult result = LLWindowCallbacks::DND_NONE;
+	switch (action)
+	{
+		case LLWindowCallbacks::DNDA_START_TRACKING:
+			{
+				mDragItems.clear();
+				if (gLoggedInTime.getStarted())
+				{
+					for (std::vector<std::string>::const_iterator itFile = data.begin(); itFile != data.end(); ++itFile)
+					{
+						const std::string& strFile = *itFile;
+
+						LLAssetType::EType eAssetType = getAssetTypeFromFilename(strFile);
+						if (LLAssetType::AT_NONE != eAssetType)
+						{
+							LLPointer<LLViewerInventoryItem> pItem = new LLViewerInventoryItem(LLUUID::generateNewID(), LLUUID::null, gDirUtilp->getBaseFileName(strFile), LLInventoryType::defaultForAssetType(eAssetType));
+							pItem->setType(eAssetType);
+							mDragItems.push_back(drag_item_t(pItem, strFile));
+						}
+					}
+				}
+
+				if (!mDragItems.empty())
+				{
+					result = LLWindowCallbacks::DND_LINK;
+				}
+			}
+			// Fall through to tracking and dropping for now
+		case LLWindowCallbacks::DNDA_TRACK:
+		case LLWindowCallbacks::DNDA_DROPPED:
+			{
+				if (!LLToolMgr::getInstance()->inBuildMode())
+				{
+					if (!mDragItems.empty())
+					{
+						if (fDrop)
+						{
+							bool fCanBulkUpload = (mDragItems.size() > 1) && (std::all_of(mDragItems.begin(), mDragItems.end(), [](const drag_item_t& dragItem)
+																																{
+																																	auto asset_type = getAssetTypeFromFilename(dragItem.second);
+																																	return (asset_type == LLAssetType::AT_TEXTURE) 
+																																		|| (asset_type == LLAssetType::AT_ANIMATION) 
+																																		|| (asset_type == LLAssetType::AT_SOUND_WAV) 
+																																		|| (asset_type == LLAssetType::AT_MATERIAL);
+																																}));
+							if (!fCanBulkUpload)
+							{
+ 								if (gAgentCamera.cameraMouselook())
+								{
+									gAgentCamera.changeCameraToDefault();
+								}
+
+								for (const drag_item_t& dragItem : mDragItems)
+								{
+									std::vector<std::string> tmpvec;
+									tmpvec.push_back(dragItem.second);
+									upload_single_file(tmpvec, getLoadFilterFromFilename(dragItem.second));
+								}
+							}
+							else
+							{
+								std::vector<std::string> files;
+								for (const drag_item_t& dragItem : mDragItems)
+									files.push_back(dragItem.second);
+
+								upload_bulk(files, LLFilePicker::FFLOAD_ALL);
+							}
+						}
+						result = DND_COPY;
+					}
+				}
+				else
+				{
+					if ( (1 == mDragItems.size()) && (LLAssetType::AT_TEXTURE == mDragItems.front().first->getType() || LLAssetType::AT_MATERIAL == mDragItems.front().first->getType()) )
+					{
+						LLPickInfo pick = pickImmediate(pos.mX, pos.mY, TRUE);
+
+						LLViewerObject* pObj = pick.getObject();
+						if ( (pObj) && (pObj->permModify()) )
+						{
+							result = LLWindowCallbacks::DND_LINK;
+							if (!fDrop)
+							{
+								if (pObj != mDragHoveredObject)
+								{
+									LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
+									mDragHoveredObject = pObj;
+									LLSelectMgr::getInstance()->highlightObjectOnly(mDragHoveredObject);
+								}
+							}
+							else
+							{
+								LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
+								mDragHoveredObject = NULL;
+
+								const std::string& strFilename = mDragItems.front().second;
+
+								if (LLAssetType::AT_TEXTURE == mDragItems.front().first->getType())
+								{
+									LLUUID idLocalBitmap = LLLocalBitmapMgr::instance().getUnitID(strFilename);
+									if (idLocalBitmap.isNull())
+									{
+										idLocalBitmap = LLLocalBitmapMgr::instance().addUnit(strFilename);
+									}
+
+									if (idLocalBitmap.notNull())
+									{
+										pObj->setTETexture(pick.mObjectFace, LLLocalBitmapMgr::instance().getWorldID(idLocalBitmap));
+									}
+								}
+								else if (LLAssetType::AT_MATERIAL == mDragItems.front().first->getType())
+								{
+									LLUUID isLocalMat = LLLocalGLTFMaterialMgr::instance().getUnitID(strFilename, 0);
+									if (isLocalMat.isNull())
+									{
+										LLLocalGLTFMaterialMgr::instance().addUnit(strFilename, isLocalMat);
+									}
+
+									if (isLocalMat.notNull())
+									{
+										pObj->setRenderMaterialID(pick.mObjectFace, LLLocalGLTFMaterialMgr::instance().getWorldID(isLocalMat));
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+			break;
+		case LLWindowCallbacks::DNDA_STOP_TRACKING:
+			{
+				mDragItems.clear();
+			}
+			break;
+	}
+
+	if ( (LLWindowCallbacks::DND_NONE == result) && (mDragHoveredObject.notNull()) )
+	{
+		LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
+		mDragHoveredObject = NULL;
+	}
+	return result;
+}
+// [/SL:KB]
+
 BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window,  LLCoordGL pos, MASK mask)
 {
 	BOOL down = FALSE;
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index d1ea7172cca..04c4b7bb2f0 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -64,6 +64,9 @@ class LLHUDIcon;
 class LLWindow;
 class LLRootView;
 class LLWindowListener;
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-27 (Catznip-3.6)
+class LLViewerInventoryItem;
+// [/SL:KB]
 class LLViewerWindowListener;
 class LLVOPartGroup;
 class LLPopupView;
@@ -202,7 +205,14 @@ class LLViewerWindow final : public LLWindowCallbacks
 	/*virtual*/ BOOL handleOtherMouseDown(LLWindow *window, LLCoordGL pos, MASK mask, S32 button);
 	/*virtual*/ BOOL handleOtherMouseUp(LLWindow *window, LLCoordGL pos, MASK mask, S32 button);
 	BOOL handleOtherMouse(LLWindow *window, LLCoordGL pos, MASK mask, S32 button, bool down);
-	/*virtual*/ LLWindowCallbacks::DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data);
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-22 (Catznip-3.6)
+	/*virtual*/ LLWindowCallbacks::DragNDropResult handleDragNDrop(LLWindow* window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, 
+                                                                   LLWindowCallbacks::DragNDropType type, const std::vector<std::string>& data);
+	            LLWindowCallbacks::DragNDropResult handleDragNDropDefault(LLWindow* window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data);
+	            LLWindowCallbacks::DragNDropResult handleDragNDropFile(LLWindow* window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, 
+                                                                       LLWindowCallbacks::DragNDropType type, const std::vector<std::string>& data);
+// [/SL:KB]
+//	/*virtual*/ LLWindowCallbacks::DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data);
 				void handleMouseMove(LLWindow *window,  LLCoordGL pos, MASK mask);
 	/*virtual*/ void handleMouseLeave(LLWindow *window);
 	/*virtual*/ void handleResize(LLWindow *window,  S32 x,  S32 y);
@@ -546,6 +556,10 @@ class LLViewerWindow final : public LLWindowCallbacks
 
 	// Object temporarily hovered over while dragging
 	LLPointer<LLViewerObject>	mDragHoveredObject;
+// [SL:KB] - Patch: Build-DragNDrop | Checked: 2013-07-27 (Catznip-3.6)
+	typedef std::pair<LLPointer<LLViewerInventoryItem>, std::string> drag_item_t;
+	std::vector<drag_item_t> mDragItems;
+// [/SL:KB]
 
 	static LLTrace::SampleStatHandle<>	sMouseVelocityStat;
 };
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 662764fded7..cb4318f3159 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -8973,6 +8973,8 @@ Your voice has been muted by moderator.
     name="BulkUploadCostConfirmation"
     type="alertmodal">
 This will upload [COUNT] items at a total cost of L$[COST]. Do you wish to continue with the upload?
+
+[FILES]
     <usetemplate
      name="okcancelbuttons"
      notext="Cancel"
-- 
GitLab