Kaynağa Gözat

Fixed rename for folders in library window
Fixed name display for folders in library window
Fixed library window drag and drop
Managed OSDropTarget now properly reports onEnter event

BearishSun 9 yıl önce
ebeveyn
işleme
26f9470325

+ 387 - 386
Source/BansheeCore/Include/Win32/BsWin32DropTarget.h

@@ -1,387 +1,388 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "Shellapi.h"
-
-// This is just a helper include for BsPlatformImpl.cpp, it's not meant to be used on its own
-
-namespace BansheeEngine
-{
-	/** @cond INTERNAL */
-	/** @addtogroup Platform
-	 *  @{
-	 */
-
-	/**
-	* Called by the OS when various drag and drop actions are performed over a window this control is registered for.
-	* 			
-	* @note		
-	* This class queues all messages receives by the OS (from the core thread), and then executes the queue on sim thread.
-	* You should be wary of which methods are allowed to be called from which thread.
-	*/
-	class Win32DropTarget : public IDropTarget
-	{
-		/** Type of drag and drop event. */
-		enum class DropOpType
-		{
-			DragOver,
-			Drop,
-			Leave
-		};
-
-		/** Type of data that a drag and drop operation contains. */
-		enum class DropOpDataType
-		{
-			FileList,
-			None
-		};
-
-		/**	Structure describing a drag and drop operation. */
-		struct DropTargetOp
-		{
-			DropTargetOp(DropOpType _type, const Vector2I& _pos)
-				:type(_type), position(_pos), dataType(DropOpDataType::None)
-			{ }
-
-			DropOpType type;
-			Vector2I position;
-
-			DropOpDataType dataType;
-			union 
-			{
-				Vector<WString>* mFileList;
-			};
-		};
-
-	public:
-		Win32DropTarget(HWND hWnd)
-			:mHWnd(hWnd), mRefCount(1), mAcceptDrag(false)
-		{ }
-
-		~Win32DropTarget()
-		{
-			BS_LOCK_MUTEX(mSync);
-
-			for(auto& fileList : mFileLists)
-			{
-				bs_delete(fileList);
-			}
-
-			mFileLists.clear();
-			mQueuedDropOps.clear();
-		}
-
-		/** Registers the drop target with the operating system. Monitoring for drag and drop operations starts. */
-		void registerWithOS()
-		{
-			CoLockObjectExternal(this, TRUE, FALSE);
-			HRESULT hr = RegisterDragDrop(mHWnd, this);
-		}
-
-		/** Unregisters the drop target with the operating system. Monitoring for drag and drop operations stops. */
-		void unregisterWithOS()
-		{
-			RevokeDragDrop(mHWnd);
-			CoLockObjectExternal(this, FALSE, FALSE);
-		}
-
-		/** COM requirement. Returns instance of an interface of the provided type. */
-		HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject) override
-		{
-			if(iid == IID_IDropTarget || iid == IID_IUnknown)
-			{
-				AddRef();
-				*ppvObject = this;
-				return S_OK;
-			}
-			else
-			{
-				*ppvObject = nullptr;
-				return E_NOINTERFACE;
-			}
-		}
-
-		/**	COM requirement. Increments objects reference count. */
-		ULONG __stdcall AddRef() override
-		{
-			return InterlockedIncrement(&mRefCount);
-		} 
-
-		/** COM requirement. Decreases the objects reference count and deletes the object if its zero. */
-		ULONG __stdcall Release() override
-		{
-			LONG count = InterlockedDecrement(&mRefCount);
-
-			if(count == 0)
-			{
-				bs_delete(this);
-				return 0;
-			}
-			else
-			{
-				return count;
-			}
-		} 
-
-		/**
-		 * Called by the OS when user enters the drop target area while dragging an object.
-		 * 			
-		 * @note	Called on core thread.
-		 */
-		HRESULT __stdcall DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
-		{
-			*pdwEffect = DROPEFFECT_LINK;
-
-			mAcceptDrag = isDataValid(pDataObj);
-			if(!mAcceptDrag)
-				return S_OK;
-
-			{
-				BS_LOCK_MUTEX(mSync);
-
-				mFileLists.push_back(getFileListFromData(pDataObj));
-
-				ScreenToClient(mHWnd, (POINT *)&pt);
-				mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
-
-				DropTargetOp& op = mQueuedDropOps.back();
-				op.dataType = DropOpDataType::FileList;
-				op.mFileList = mFileLists.back();
-			}
-			
-			return S_OK;
-		}
-
-		/**
-		 * Called by the OS while user continues to drag an object over the drop target.
-		 * 			
-		 * @note	Called on core thread.
-		 */
-		HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
-		{
-			*pdwEffect = DROPEFFECT_LINK;
-
-			if(!mAcceptDrag)
-				return S_OK;
-
-			{
-				BS_LOCK_MUTEX(mSync);
-
-				ScreenToClient(mHWnd, (POINT *)&pt);
-				mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
-
-				DropTargetOp& op = mQueuedDropOps.back();
-				op.dataType = DropOpDataType::FileList;
-				op.mFileList = mFileLists.back();
-			}
-			
-			return S_OK;
-		} 
-
-		/**
-		 * Called by the OS when user leaves the drop target.
-		 * 			
-		 * @note	Called on core thread.
-		 */
-		HRESULT __stdcall DragLeave() override
-		{
-			{
-				BS_LOCK_MUTEX(mSync);
-
-				mQueuedDropOps.push_back(DropTargetOp(DropOpType::Leave, Vector2I()));
-
-				DropTargetOp& op = mQueuedDropOps.back();
-				op.dataType = DropOpDataType::FileList;
-				op.mFileList = mFileLists.back();
-			}
-
-			return S_OK;
-		}
-
-		/**
-		 * Called by the OS when the user ends the drag operation while over the drop target.
-		 * 			
-		 * @note	Called on core thread.
-		 */
-		HRESULT __stdcall Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
-		{
-			*pdwEffect = DROPEFFECT_LINK;
-			mAcceptDrag = false;
-
-			if(!isDataValid(pDataObj))
-				return S_OK;
-
-			{
-				BS_LOCK_MUTEX(mSync);
-
-				mFileLists.push_back(getFileListFromData(pDataObj));
-
-				ScreenToClient(mHWnd, (POINT *)&pt);
-				mQueuedDropOps.push_back(DropTargetOp(DropOpType::Drop, Vector2I((int)pt.x, (int)pt.y)));
-
-				DropTargetOp& op = mQueuedDropOps.back();
-				op.dataType = DropOpDataType::FileList;
-				op.mFileList = mFileLists.back();
-			}
-
-			return S_OK;
-		}
-
-		/**
-		 * Registers a new drop target to monitor.
-		 *
-		 * @note	Sim thread only.
-		 */
-		void registerDropTarget(OSDropTarget* dropTarget)
-		{
-			mDropTargets.push_back(dropTarget);
-		}
-
-		/**
-		 * Unregisters an existing drop target and stops monitoring it.
-		 *
-		 * @note	Sim thread only.
-		 */
-		void unregisterDropTarget(OSDropTarget* dropTarget)
-		{
-			auto findIter = std::find(begin(mDropTargets), end(mDropTargets), dropTarget);
-			if(findIter != mDropTargets.end())
-				mDropTargets.erase(findIter);
-		}
-
-		/**
-		 * Gets the total number of monitored drop targets.
-		 * 			
-		 * @note	Sim thread only.
-		 */
-		unsigned int getNumDropTargets() const 
-		{ 
-			return (unsigned int)mDropTargets.size(); 
-		}
-
-		/** Called every frame by the sim thread. Internal method. */
-		void update()
-		{
-			BS_LOCK_MUTEX(mSync);
-
-			for(auto& op: mQueuedDropOps)
-			{
-				for(auto& target : mDropTargets)
-				{
-					if(op.type != DropOpType::Leave)
-					{
-						if(target->_isInside(op.position))
-						{
-							if(!target->_isActive())
-							{
-								target->_setFileList(*op.mFileList);
-								target->onEnter(op.position.x, op.position.y);
-							}
-
-							if(op.type == DropOpType::DragOver)
-								target->onDragOver(op.position.x, op.position.y);
-							else if(op.type == DropOpType::Drop)
-							{
-								target->_setFileList(*op.mFileList);
-								target->onDrop(op.position.x, op.position.y);
-							}
-						}
-						else
-						{
-							if(target->_isActive())
-							{
-								target->onLeave();
-								target->_clear();
-								target->_setActive(false);
-							}
-						}
-					}
-					else
-					{
-						if(target->_isActive())
-						{
-							target->onLeave();
-							target->_clear();
-							target->_setActive(false);
-						}
-					}
-				}
-
-				if(op.type == DropOpType::Leave || op.type == DropOpType::Drop)
-				{
-					while (!mFileLists.empty())
-					{
-						bool done = mFileLists[0] == op.mFileList;
-
-						bs_delete(mFileLists[0]);
-						mFileLists.erase(mFileLists.begin());
-
-						if (done)
-							break;
-					}
-				}
-			}
-
-			mQueuedDropOps.clear();
-		}
-	private:
-		/**	Check if we support the data in the provided drag and drop data object. */
-		bool isDataValid(IDataObject* data)
-		{
-			// TODO - Currently only supports file drag and drop, so only CF_HDROP is used
-			FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
-
-			return data->QueryGetData(&fmtetc) == S_OK ? true : false;
-		}
-
-		/**	Gets a file list from data. Caller must ensure that the data actually contains a file list. */
-		Vector<WString>* getFileListFromData(IDataObject* data)
-		{
-			FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
-			STGMEDIUM stgmed;
-
-			Vector<WString>* files = bs_new<Vector<WString>>();
-			if(data->GetData(&fmtetc, &stgmed) == S_OK)
-			{
-				PVOID data = GlobalLock(stgmed.hGlobal);
-
-				HDROP hDrop = (HDROP)data;
-				UINT numFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0);
-
-				files->resize(numFiles);
-				for(UINT i = 0; i < numFiles; i++)
-				{
-					UINT numChars = DragQueryFileW(hDrop, i, nullptr, 0) + 1;
-					wchar_t* buffer = (wchar_t*)bs_alloc((UINT32)numChars * sizeof(wchar_t));
-
-					DragQueryFileW(hDrop, i, buffer, numChars);
-
-					(*files)[i] = WString(buffer);
-
-					bs_free(buffer);
-				}
-
-				GlobalUnlock(stgmed.hGlobal);
-				ReleaseStgMedium(&stgmed);
-			}
-
-			return files;
-		}
-
-	private:
-		Vector<OSDropTarget*> mDropTargets;
-
-		LONG mRefCount;
-		HWND mHWnd;
-		bool mAcceptDrag;
-
-		Vector<DropTargetOp> mQueuedDropOps;
-		Vector<Vector<WString>*> mFileLists; 
-
-		BS_MUTEX(mSync);
-	};
-
-	/** @} */
-	/** @endcond */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "Shellapi.h"
+
+// This is just a helper include for BsPlatformImpl.cpp, it's not meant to be used on its own
+
+namespace BansheeEngine
+{
+	/** @cond INTERNAL */
+	/** @addtogroup Platform
+	 *  @{
+	 */
+
+	/**
+	* Called by the OS when various drag and drop actions are performed over a window this control is registered for.
+	* 			
+	* @note		
+	* This class queues all messages receives by the OS (from the core thread), and then executes the queue on sim thread.
+	* You should be wary of which methods are allowed to be called from which thread.
+	*/
+	class Win32DropTarget : public IDropTarget
+	{
+		/** Type of drag and drop event. */
+		enum class DropOpType
+		{
+			DragOver,
+			Drop,
+			Leave
+		};
+
+		/** Type of data that a drag and drop operation contains. */
+		enum class DropOpDataType
+		{
+			FileList,
+			None
+		};
+
+		/**	Structure describing a drag and drop operation. */
+		struct DropTargetOp
+		{
+			DropTargetOp(DropOpType _type, const Vector2I& _pos)
+				:type(_type), position(_pos), dataType(DropOpDataType::None)
+			{ }
+
+			DropOpType type;
+			Vector2I position;
+
+			DropOpDataType dataType;
+			union 
+			{
+				Vector<WString>* mFileList;
+			};
+		};
+
+	public:
+		Win32DropTarget(HWND hWnd)
+			:mHWnd(hWnd), mRefCount(1), mAcceptDrag(false)
+		{ }
+
+		~Win32DropTarget()
+		{
+			BS_LOCK_MUTEX(mSync);
+
+			for(auto& fileList : mFileLists)
+			{
+				bs_delete(fileList);
+			}
+
+			mFileLists.clear();
+			mQueuedDropOps.clear();
+		}
+
+		/** Registers the drop target with the operating system. Monitoring for drag and drop operations starts. */
+		void registerWithOS()
+		{
+			CoLockObjectExternal(this, TRUE, FALSE);
+			HRESULT hr = RegisterDragDrop(mHWnd, this);
+		}
+
+		/** Unregisters the drop target with the operating system. Monitoring for drag and drop operations stops. */
+		void unregisterWithOS()
+		{
+			RevokeDragDrop(mHWnd);
+			CoLockObjectExternal(this, FALSE, FALSE);
+		}
+
+		/** COM requirement. Returns instance of an interface of the provided type. */
+		HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject) override
+		{
+			if(iid == IID_IDropTarget || iid == IID_IUnknown)
+			{
+				AddRef();
+				*ppvObject = this;
+				return S_OK;
+			}
+			else
+			{
+				*ppvObject = nullptr;
+				return E_NOINTERFACE;
+			}
+		}
+
+		/**	COM requirement. Increments objects reference count. */
+		ULONG __stdcall AddRef() override
+		{
+			return InterlockedIncrement(&mRefCount);
+		} 
+
+		/** COM requirement. Decreases the objects reference count and deletes the object if its zero. */
+		ULONG __stdcall Release() override
+		{
+			LONG count = InterlockedDecrement(&mRefCount);
+
+			if(count == 0)
+			{
+				bs_delete(this);
+				return 0;
+			}
+			else
+			{
+				return count;
+			}
+		} 
+
+		/**
+		 * Called by the OS when user enters the drop target area while dragging an object.
+		 * 			
+		 * @note	Called on core thread.
+		 */
+		HRESULT __stdcall DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
+		{
+			*pdwEffect = DROPEFFECT_LINK;
+
+			mAcceptDrag = isDataValid(pDataObj);
+			if(!mAcceptDrag)
+				return S_OK;
+
+			{
+				BS_LOCK_MUTEX(mSync);
+
+				mFileLists.push_back(getFileListFromData(pDataObj));
+
+				ScreenToClient(mHWnd, (POINT *)&pt);
+				mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
+
+				DropTargetOp& op = mQueuedDropOps.back();
+				op.dataType = DropOpDataType::FileList;
+				op.mFileList = mFileLists.back();
+			}
+			
+			return S_OK;
+		}
+
+		/**
+		 * Called by the OS while user continues to drag an object over the drop target.
+		 * 			
+		 * @note	Called on core thread.
+		 */
+		HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
+		{
+			*pdwEffect = DROPEFFECT_LINK;
+
+			if(!mAcceptDrag)
+				return S_OK;
+
+			{
+				BS_LOCK_MUTEX(mSync);
+
+				ScreenToClient(mHWnd, (POINT *)&pt);
+				mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
+
+				DropTargetOp& op = mQueuedDropOps.back();
+				op.dataType = DropOpDataType::FileList;
+				op.mFileList = mFileLists.back();
+			}
+			
+			return S_OK;
+		} 
+
+		/**
+		 * Called by the OS when user leaves the drop target.
+		 * 			
+		 * @note	Called on core thread.
+		 */
+		HRESULT __stdcall DragLeave() override
+		{
+			{
+				BS_LOCK_MUTEX(mSync);
+
+				mQueuedDropOps.push_back(DropTargetOp(DropOpType::Leave, Vector2I()));
+
+				DropTargetOp& op = mQueuedDropOps.back();
+				op.dataType = DropOpDataType::FileList;
+				op.mFileList = mFileLists.back();
+			}
+
+			return S_OK;
+		}
+
+		/**
+		 * Called by the OS when the user ends the drag operation while over the drop target.
+		 * 			
+		 * @note	Called on core thread.
+		 */
+		HRESULT __stdcall Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
+		{
+			*pdwEffect = DROPEFFECT_LINK;
+			mAcceptDrag = false;
+
+			if(!isDataValid(pDataObj))
+				return S_OK;
+
+			{
+				BS_LOCK_MUTEX(mSync);
+
+				mFileLists.push_back(getFileListFromData(pDataObj));
+
+				ScreenToClient(mHWnd, (POINT *)&pt);
+				mQueuedDropOps.push_back(DropTargetOp(DropOpType::Drop, Vector2I((int)pt.x, (int)pt.y)));
+
+				DropTargetOp& op = mQueuedDropOps.back();
+				op.dataType = DropOpDataType::FileList;
+				op.mFileList = mFileLists.back();
+			}
+
+			return S_OK;
+		}
+
+		/**
+		 * Registers a new drop target to monitor.
+		 *
+		 * @note	Sim thread only.
+		 */
+		void registerDropTarget(OSDropTarget* dropTarget)
+		{
+			mDropTargets.push_back(dropTarget);
+		}
+
+		/**
+		 * Unregisters an existing drop target and stops monitoring it.
+		 *
+		 * @note	Sim thread only.
+		 */
+		void unregisterDropTarget(OSDropTarget* dropTarget)
+		{
+			auto findIter = std::find(begin(mDropTargets), end(mDropTargets), dropTarget);
+			if(findIter != mDropTargets.end())
+				mDropTargets.erase(findIter);
+		}
+
+		/**
+		 * Gets the total number of monitored drop targets.
+		 * 			
+		 * @note	Sim thread only.
+		 */
+		unsigned int getNumDropTargets() const 
+		{ 
+			return (unsigned int)mDropTargets.size(); 
+		}
+
+		/** Called every frame by the sim thread. Internal method. */
+		void update()
+		{
+			BS_LOCK_MUTEX(mSync);
+
+			for(auto& op: mQueuedDropOps)
+			{
+				for(auto& target : mDropTargets)
+				{
+					if(op.type != DropOpType::Leave)
+					{
+						if(target->_isInside(op.position))
+						{
+							if(!target->_isActive())
+							{
+								target->_setFileList(*op.mFileList);
+								target->_setActive(true);
+								target->onEnter(op.position.x, op.position.y);
+							}
+
+							if(op.type == DropOpType::DragOver)
+								target->onDragOver(op.position.x, op.position.y);
+							else if(op.type == DropOpType::Drop)
+							{
+								target->_setFileList(*op.mFileList);
+								target->onDrop(op.position.x, op.position.y);
+							}
+						}
+						else
+						{
+							if(target->_isActive())
+							{
+								target->onLeave();
+								target->_clear();
+								target->_setActive(false);
+							}
+						}
+					}
+					else
+					{
+						if(target->_isActive())
+						{
+							target->onLeave();
+							target->_clear();
+							target->_setActive(false);
+						}
+					}
+				}
+
+				if(op.type == DropOpType::Leave || op.type == DropOpType::Drop)
+				{
+					while (!mFileLists.empty())
+					{
+						bool done = mFileLists[0] == op.mFileList;
+
+						bs_delete(mFileLists[0]);
+						mFileLists.erase(mFileLists.begin());
+
+						if (done)
+							break;
+					}
+				}
+			}
+
+			mQueuedDropOps.clear();
+		}
+	private:
+		/**	Check if we support the data in the provided drag and drop data object. */
+		bool isDataValid(IDataObject* data)
+		{
+			// TODO - Currently only supports file drag and drop, so only CF_HDROP is used
+			FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+
+			return data->QueryGetData(&fmtetc) == S_OK ? true : false;
+		}
+
+		/**	Gets a file list from data. Caller must ensure that the data actually contains a file list. */
+		Vector<WString>* getFileListFromData(IDataObject* data)
+		{
+			FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+			STGMEDIUM stgmed;
+
+			Vector<WString>* files = bs_new<Vector<WString>>();
+			if(data->GetData(&fmtetc, &stgmed) == S_OK)
+			{
+				PVOID data = GlobalLock(stgmed.hGlobal);
+
+				HDROP hDrop = (HDROP)data;
+				UINT numFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0);
+
+				files->resize(numFiles);
+				for(UINT i = 0; i < numFiles; i++)
+				{
+					UINT numChars = DragQueryFileW(hDrop, i, nullptr, 0) + 1;
+					wchar_t* buffer = (wchar_t*)bs_alloc((UINT32)numChars * sizeof(wchar_t));
+
+					DragQueryFileW(hDrop, i, buffer, numChars);
+
+					(*files)[i] = WString(buffer);
+
+					bs_free(buffer);
+				}
+
+				GlobalUnlock(stgmed.hGlobal);
+				ReleaseStgMedium(&stgmed);
+			}
+
+			return files;
+		}
+
+	private:
+		Vector<OSDropTarget*> mDropTargets;
+
+		LONG mRefCount;
+		HWND mHWnd;
+		bool mAcceptDrag;
+
+		Vector<DropTargetOp> mQueuedDropOps;
+		Vector<Vector<WString>*> mFileLists; 
+
+		BS_MUTEX(mSync);
+	};
+
+	/** @} */
+	/** @endcond */
 }

+ 1 - 1
Source/MBansheeEditor/EditorApplication.cs

@@ -341,7 +341,7 @@ namespace BansheeEditor
             if (btn != PasteKey)
             {
                 // The system ensures elsewhere that only either a resource or a scene object is selected, but not both
-                if (Selection.ResourceUUIDs.Length > 0)
+                if (Selection.ResourcePaths.Length > 0)
                 {
                     window = EditorWindow.GetWindow<LibraryWindow>();
                 }

+ 288 - 288
Source/MBansheeEditor/Library/LibraryDropTarget.cs

@@ -1,288 +1,288 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Handles various types of drag and drop operations used by the library window.
-    /// </summary>
-    public class LibraryDropTarget
-    {
-        private const int DragStartDistancePx = 3;
-
-        /// <summary>
-        /// Triggered when a resource drag and drop operation occured over the drop target.
-        /// </summary>
-        public Action<Vector2I, string[]> OnDropResource;
-
-        /// <summary>
-        /// Triggered when a scene object drag and drop operation occured over the drop target.
-        /// </summary>
-        public Action<Vector2I, SceneObject[]> OnDropSceneObject;
-
-        /// <summary>
-        /// Triggered when a local drag and drop operation is starting (pointer has been pressed and is being dragged while
-        /// within the drop target).
-        /// </summary>
-        public Action<Vector2I> OnStart;
-
-        /// <summary>
-        /// Triggered when a pointer enters the drop target area while a drag operation is in progress.
-        /// </summary>
-        public Action<Vector2I> OnEnter;
-
-        /// <summary>
-        /// Triggered when a pointer leaves the drop target area while a drag operation is in progress.
-        /// </summary>
-        public Action OnLeave;
-
-        /// <summary>
-        /// Triggered when a pointer moves within the drop target area while a drag operation is in progress.
-        /// </summary>
-        public Action<Vector2I> OnDrag;
-
-        /// <summary>
-        /// Triggered when a locally initiated (started within the drop target) drag and drop operation ends.
-        /// </summary>
-        public Action<Vector2I> OnEnd;
-
-        private readonly EditorWindow parentWindow;
-
-        private OSDropTarget dropTargetOS;
-        private Rect2I bounds;
-
-        private bool isMouseDown;
-        private bool isLocalDragInProgress;
-        private bool triggerStartLocalDrag;
-        private bool triggerEndLocalDrag;
-        private bool isDragInBounds;
-        private bool isOSDragActive;
-        private Vector2I mouseDownScreenPos;
-        private Vector2I lastDragWindowPos;
-
-        /// <summary>
-        /// Area inside which drag and drop operations are allowed to occurr. Relative to the parent library window.
-        /// </summary>
-        public Rect2I Bounds
-        {
-            get { return bounds; }
-            set
-            {
-                bounds = value;
-                dropTargetOS.Bounds = bounds;
-            }
-        }
-
-        /// <summary>
-        /// Creates a new library drop target.
-        /// </summary>
-        /// <param name="window">Window the drop target is part of.</param>
-        public LibraryDropTarget(EditorWindow window)
-        {
-            parentWindow = window;
-
-            dropTargetOS = new OSDropTarget(window);
-            dropTargetOS.OnDrag += DoOnOSDrag;
-            dropTargetOS.OnDrop += DoOnOSDrop;
-            dropTargetOS.OnEnter += DoOnOSDragEnter;
-            dropTargetOS.OnLeave += DoOnOSDragLeave;
-
-            EditorInput.OnPointerPressed += Input_OnPointerPressed;
-            EditorInput.OnPointerReleased += Input_OnPointerReleased;
-            EditorInput.OnPointerMoved += Input_OnPointerMoved;
-        }
-
-        /// <summary>
-        /// Triggered when the pointer moved.
-        /// </summary>
-        /// <param name="ev">Data about the pointer move event.</param>
-        void Input_OnPointerMoved(PointerEvent ev)
-        {
-            Vector2I currentWindowPos = parentWindow.ScreenToWindowPos(ev.ScreenPos);
-
-            if (isMouseDown && !isLocalDragInProgress)
-            {
-                Vector2I startWindowPos = parentWindow.ScreenToWindowPos(mouseDownScreenPos);
-                if (!Bounds.Contains(startWindowPos))
-                    return;
-
-                int distance = Vector2I.Distance(startWindowPos, currentWindowPos);
-                if (distance >= DragStartDistancePx)
-                    triggerStartLocalDrag = true;
-            }
-        }
-
-        /// <summary>
-        /// Triggered when the pointer is released.
-        /// </summary>
-        /// <param name="ev">Data about the pointer release event.</param>
-        void Input_OnPointerReleased(PointerEvent ev)
-        {
-            if (isLocalDragInProgress)
-                triggerEndLocalDrag = true;
-
-            isLocalDragInProgress = false;
-            isMouseDown = false;
-            isDragInBounds = false;
-            triggerStartLocalDrag = false;
-        }
-
-        /// <summary>
-        /// Triggered when the pointer is pressed.
-        /// </summary>
-        /// <param name="ev">Data about the pointer press event.</param>
-        void Input_OnPointerPressed(PointerEvent ev)
-        {
-            Vector2I currentWindowPos = parentWindow.ScreenToWindowPos(ev.ScreenPos);
-
-            if (Bounds.Contains(currentWindowPos))
-            {
-                isMouseDown = true;
-                mouseDownScreenPos = ev.ScreenPos;
-            }
-        }
-
-        /// <summary>
-        /// Triggers events and queries for changes in drag and drop operations. Should be called every frame.
-        /// </summary>
-        public void Update()
-        {
-            Vector2I currentWindowPos = parentWindow.ScreenToWindowPos(Input.PointerPosition);
-
-            if (triggerStartLocalDrag)
-            {
-                isLocalDragInProgress = true;
-                triggerStartLocalDrag = false;
-
-                if (OnStart != null)
-                    OnStart(currentWindowPos);
-            }
-
-            if (triggerEndLocalDrag)
-            {
-                triggerEndLocalDrag = false;
-
-                if (OnEnd != null)
-                    OnEnd(currentWindowPos);
-            }
-
-            if (isOSDragActive)
-                return;
-
-            bool externalDragInProgress = DragDrop.DragInProgress && DragDrop.Type == DragDropType.SceneObject;
-            if (isLocalDragInProgress || externalDragInProgress)
-            {
-                if (lastDragWindowPos != currentWindowPos)
-                {
-                    if (!isDragInBounds)
-                    {
-                        if (Bounds.Contains(currentWindowPos))
-                        {
-                            isDragInBounds = true;
-                            if (OnEnter != null)
-                                OnEnter(currentWindowPos);
-                        }
-                    }
-
-                    if (OnDrag != null)
-                        OnDrag(currentWindowPos);
-
-                    if (isDragInBounds)
-                    {
-                        if (!Bounds.Contains(currentWindowPos))
-                        {
-                            isDragInBounds = false;
-                            if (OnLeave != null)
-                                OnLeave();
-                        }
-                    }
-
-                    lastDragWindowPos = currentWindowPos;
-                }
-            }
-
-            if (DragDrop.DropInProgress && Bounds.Contains(currentWindowPos))
-            {
-                if (DragDrop.Type == DragDropType.Resource)
-                {
-                    if (OnDropResource != null)
-                    {
-                        ResourceDragDropData resourceDragDrop = (ResourceDragDropData)DragDrop.Data;
-                        OnDropResource(currentWindowPos, resourceDragDrop.Paths);
-                    }
-                }
-                else if (DragDrop.Type == DragDropType.SceneObject)
-                {
-                    if (OnDropSceneObject != null)
-                    {
-                        SceneObjectDragDropData sceneDragDrop = (SceneObjectDragDropData)DragDrop.Data;
-                        OnDropSceneObject(currentWindowPos, sceneDragDrop.Objects);
-                    }
-                }
-
-                isDragInBounds = false;
-            }
-        }
-
-        /// <summary>
-        /// Destroy the drop target. You should call this when done with the drop target.
-        /// </summary>
-        public void Destroy()
-        {
-            dropTargetOS.Destroy();
-            dropTargetOS = null;
-        }
-
-        /// <summary>
-        /// Triggered when an OS drag event has entered the drop target area.
-        /// </summary>
-        /// <param name="x">X coordinate of the pointer relative to the parent window.</param>
-        /// <param name="y">Y coordinate of the pointer relative to the parent window.</param>
-        private void DoOnOSDragEnter(int x, int y)
-        {
-            isOSDragActive = true;
-
-            if (OnEnter != null)
-                OnEnter(new Vector2I(x, y));
-        }
-
-        /// <summary>
-        /// Triggered when an OS drag event has left the parent window.
-        /// </summary>
-        private void DoOnOSDragLeave()
-        {
-            isOSDragActive = false;
-
-            if (OnLeave != null)
-                OnLeave();
-        }
-
-        /// <summary>
-        /// Triggered when a pointer moves while an OS drag event is occuring over the drop target area.
-        /// </summary>
-        /// <param name="x">X coordinate of the pointer relative to the parent window.</param>
-        /// <param name="y">Y coordinate of the pointer relative to the parent window.</param>
-        private void DoOnOSDrag(int x, int y)
-        {
-            if (OnDrag != null)
-                OnDrag(new Vector2I(x, y));
-        }
-
-        /// <summary>
-        /// Triggered when the pointer was released during an OS drag and drop operation while over the drop target area.
-        /// </summary>
-        /// <param name="x">X coordinate of the pointer relative to the parent window.</param>
-        /// <param name="y">Y coordinate of the pointer relative to the parent window.</param>
-        private void DoOnOSDrop(int x, int y)
-        {
-            isOSDragActive = false;
-
-            if (OnDropResource != null)
-                OnDropResource(new Vector2I(x, y), dropTargetOS.FilePaths);
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Handles various types of drag and drop operations used by the library window.
+    /// </summary>
+    public class LibraryDropTarget
+    {
+        private const int DragStartDistancePx = 3;
+
+        /// <summary>
+        /// Triggered when a resource drag and drop operation occured over the drop target.
+        /// </summary>
+        public Action<Vector2I, string[]> OnDropResource;
+
+        /// <summary>
+        /// Triggered when a scene object drag and drop operation occured over the drop target.
+        /// </summary>
+        public Action<Vector2I, SceneObject[]> OnDropSceneObject;
+
+        /// <summary>
+        /// Triggered when a local drag and drop operation is starting (pointer has been pressed and is being dragged while
+        /// within the drop target).
+        /// </summary>
+        public Action<Vector2I> OnStart;
+
+        /// <summary>
+        /// Triggered when a pointer enters the drop target area while a drag operation is in progress.
+        /// </summary>
+        public Action<Vector2I> OnEnter;
+
+        /// <summary>
+        /// Triggered when a pointer leaves the drop target area while a drag operation is in progress.
+        /// </summary>
+        public Action OnLeave;
+
+        /// <summary>
+        /// Triggered when a pointer moves within the drop target area while a drag operation is in progress.
+        /// </summary>
+        public Action<Vector2I> OnDrag;
+
+        /// <summary>
+        /// Triggered when a locally initiated (started within the drop target) drag and drop operation ends.
+        /// </summary>
+        public Action<Vector2I> OnEnd;
+
+        private readonly EditorWindow parentWindow;
+
+        private OSDropTarget dropTargetOS;
+        private Rect2I bounds;
+
+        private bool isMouseDown;
+        private bool isLocalDragInProgress;
+        private bool triggerStartLocalDrag;
+        private bool triggerEndLocalDrag;
+        private bool isDragInBounds;
+        private bool isOSDragActive;
+        private Vector2I mouseDownScreenPos;
+        private Vector2I lastDragWindowPos;
+
+        /// <summary>
+        /// Area inside which drag and drop operations are allowed to occurr. Relative to the parent library window.
+        /// </summary>
+        public Rect2I Bounds
+        {
+            get { return bounds; }
+            set
+            {
+                bounds = value;
+                dropTargetOS.Bounds = bounds;
+            }
+        }
+
+        /// <summary>
+        /// Creates a new library drop target.
+        /// </summary>
+        /// <param name="window">Window the drop target is part of.</param>
+        public LibraryDropTarget(EditorWindow window)
+        {
+            parentWindow = window;
+
+            dropTargetOS = new OSDropTarget(window);
+            dropTargetOS.OnDrag += DoOnOSDrag;
+            dropTargetOS.OnDrop += DoOnOSDrop;
+            dropTargetOS.OnEnter += DoOnOSDragEnter;
+            dropTargetOS.OnLeave += DoOnOSDragLeave;
+
+            EditorInput.OnPointerPressed += Input_OnPointerPressed;
+            EditorInput.OnPointerReleased += Input_OnPointerReleased;
+            EditorInput.OnPointerMoved += Input_OnPointerMoved;
+        }
+
+        /// <summary>
+        /// Triggered when the pointer moved.
+        /// </summary>
+        /// <param name="ev">Data about the pointer move event.</param>
+        void Input_OnPointerMoved(PointerEvent ev)
+        {
+            Vector2I currentWindowPos = parentWindow.ScreenToWindowPos(ev.ScreenPos);
+
+            if (isMouseDown && !isLocalDragInProgress)
+            {
+                Vector2I startWindowPos = parentWindow.ScreenToWindowPos(mouseDownScreenPos);
+                if (!Bounds.Contains(startWindowPos))
+                    return;
+
+                int distance = Vector2I.Distance(startWindowPos, currentWindowPos);
+                if (distance >= DragStartDistancePx)
+                    triggerStartLocalDrag = true;
+            }
+        }
+
+        /// <summary>
+        /// Triggered when the pointer is released.
+        /// </summary>
+        /// <param name="ev">Data about the pointer release event.</param>
+        void Input_OnPointerReleased(PointerEvent ev)
+        {
+            if (isLocalDragInProgress)
+                triggerEndLocalDrag = true;
+
+            isLocalDragInProgress = false;
+            isMouseDown = false;
+            isDragInBounds = false;
+            triggerStartLocalDrag = false;
+        }
+
+        /// <summary>
+        /// Triggered when the pointer is pressed.
+        /// </summary>
+        /// <param name="ev">Data about the pointer press event.</param>
+        void Input_OnPointerPressed(PointerEvent ev)
+        {
+            Vector2I currentWindowPos = parentWindow.ScreenToWindowPos(ev.ScreenPos);
+
+            if (Bounds.Contains(currentWindowPos))
+            {
+                isMouseDown = true;
+                mouseDownScreenPos = ev.ScreenPos;
+            }
+        }
+
+        /// <summary>
+        /// Triggers events and queries for changes in drag and drop operations. Should be called every frame.
+        /// </summary>
+        public void Update()
+        {
+            Vector2I currentWindowPos = parentWindow.ScreenToWindowPos(Input.PointerPosition);
+
+            if (triggerStartLocalDrag)
+            {
+                isLocalDragInProgress = true;
+                triggerStartLocalDrag = false;
+
+                if (OnStart != null)
+                    OnStart(currentWindowPos);
+            }
+
+            if (triggerEndLocalDrag)
+            {
+                triggerEndLocalDrag = false;
+
+                if (OnEnd != null)
+                    OnEnd(currentWindowPos);
+            }
+
+            if (isOSDragActive)
+                return;
+
+            bool externalDragInProgress = DragDrop.DragInProgress && DragDrop.Type == DragDropType.SceneObject;
+            if (isLocalDragInProgress || externalDragInProgress)
+            {
+                if (lastDragWindowPos != currentWindowPos)
+                {
+                    if (!isDragInBounds)
+                    {
+                        if (Bounds.Contains(currentWindowPos))
+                        {
+                            isDragInBounds = true;
+                            if (OnEnter != null)
+                                OnEnter(currentWindowPos);
+                        }
+                    }
+
+                    if (OnDrag != null)
+                        OnDrag(currentWindowPos);
+
+                    if (isDragInBounds)
+                    {
+                        if (!Bounds.Contains(currentWindowPos))
+                        {
+                            isDragInBounds = false;
+                            if (OnLeave != null)
+                                OnLeave();
+                        }
+                    }
+
+                    lastDragWindowPos = currentWindowPos;
+                }
+            }
+
+            if (DragDrop.DropInProgress && Bounds.Contains(currentWindowPos))
+            {
+                if (DragDrop.Type == DragDropType.Resource)
+                {
+                    if (OnDropResource != null)
+                    {
+                        ResourceDragDropData resourceDragDrop = (ResourceDragDropData)DragDrop.Data;
+                        OnDropResource(currentWindowPos, resourceDragDrop.Paths);
+                    }
+                }
+                else if (DragDrop.Type == DragDropType.SceneObject)
+                {
+                    if (OnDropSceneObject != null)
+                    {
+                        SceneObjectDragDropData sceneDragDrop = (SceneObjectDragDropData)DragDrop.Data;
+                        OnDropSceneObject(currentWindowPos, sceneDragDrop.Objects);
+                    }
+                }
+
+                isDragInBounds = false;
+            }
+        }
+
+        /// <summary>
+        /// Destroy the drop target. You should call this when done with the drop target.
+        /// </summary>
+        public void Destroy()
+        {
+            dropTargetOS.Destroy();
+            dropTargetOS = null;
+        }
+
+        /// <summary>
+        /// Triggered when an OS drag event has entered the drop target area.
+        /// </summary>
+        /// <param name="x">X coordinate of the pointer relative to the parent window.</param>
+        /// <param name="y">Y coordinate of the pointer relative to the parent window.</param>
+        private void DoOnOSDragEnter(int x, int y)
+        {
+            isOSDragActive = true;
+
+            if (OnEnter != null)
+                OnEnter(new Vector2I(x, y));
+        }
+
+        /// <summary>
+        /// Triggered when an OS drag event has left the parent window.
+        /// </summary>
+        private void DoOnOSDragLeave()
+        {
+            isOSDragActive = false;
+
+            if (OnLeave != null)
+                OnLeave();
+        }
+
+        /// <summary>
+        /// Triggered when a pointer moves while an OS drag event is occuring over the drop target area.
+        /// </summary>
+        /// <param name="x">X coordinate of the pointer relative to the parent window.</param>
+        /// <param name="y">Y coordinate of the pointer relative to the parent window.</param>
+        private void DoOnOSDrag(int x, int y)
+        {
+            if (OnDrag != null)
+                OnDrag(new Vector2I(x, y));
+        }
+
+        /// <summary>
+        /// Triggered when the pointer was released during an OS drag and drop operation while over the drop target area.
+        /// </summary>
+        /// <param name="x">X coordinate of the pointer relative to the parent window.</param>
+        /// <param name="y">Y coordinate of the pointer relative to the parent window.</param>
+        private void DoOnOSDrop(int x, int y)
+        {
+            isOSDragActive = false;
+
+            if (OnDropResource != null)
+                OnDropResource(new Vector2I(x, y), dropTargetOS.FilePaths);
+        }
+    }
+}

+ 9 - 6
Source/MBansheeEditor/Library/LibraryWindow.cs

@@ -1383,10 +1383,8 @@ namespace BansheeEditor
 
             if (paths != null)
             {
-                string[] filePaths = GetFiles(paths);
-
                 List<string> addedResources = new List<string>();
-                foreach (var path in filePaths)
+                foreach (var path in paths)
                 {
                     string absolutePath = path;
                     if (!Path.IsPathRooted(absolutePath))
@@ -1404,19 +1402,24 @@ namespace BansheeEditor
                     if (PathEx.Compare(absolutePath, destination))
                         continue;
 
-                    bool doCopy = !ProjectLibrary.Exists(absolutePath);
+                    bool newFile = !ProjectLibrary.Exists(absolutePath);
+                    if (!newFile)
+                    {
+                        if (ProjectLibrary.IsSubresource(absolutePath))
+                            continue;
+                    }
 
                     string uniqueDestination = LibraryUtility.GetUniquePath(destination);
                     if (Directory.Exists(path))
                     {
-                        if (doCopy)
+                        if (newFile)
                             DirectoryEx.Copy(absolutePath, uniqueDestination);
                         else
                             DirectoryEx.Move(absolutePath, uniqueDestination);
                     }
                     else if (File.Exists(path))
                     {
-                        if (doCopy)
+                        if (newFile)
                             FileEx.Copy(absolutePath, uniqueDestination);
                         else
                             ProjectLibrary.Move(absolutePath, uniqueDestination);

+ 78 - 78
Source/MBansheeEngine/PathEx.cs

@@ -1,78 +1,78 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System.IO;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Contains helper methods dealing with file and directory paths, extending the functionality provided by 
-    /// System.IO.Path.
-    /// </summary>
-    public static class PathEx
-    {
-        /// <summary>
-        /// Checks if two paths are equal.
-        /// </summary>
-        /// <param name="a">First path.</param>
-        /// <param name="b">Second path.</param>
-        /// <returns>True if both paths point to the same place.</returns>
-        public static bool Compare(string a, string b)
-        {
-            return Path.GetFullPath(a) == Path.GetFullPath(b);
-        }
-
-        /// <summary>
-        /// Checks if one path is part of an another path.
-        /// </summary>
-        /// <param name="path">Path to check if it's part of <paramref name="parent"/>.</param>
-        /// <param name="parent">Parent that might contain <paramref name="path"/>.</param>
-        /// <returns>True if <paramref name="parent"/> contains <paramref name="path"/>.</returns>
-        public static bool IsPartOf(string path, string parent)
-        {
-            return Path.GetFullPath(path).StartsWith(Path.GetFullPath(parent));
-        }
-
-        /// <summary>
-        /// Returns the last entry in the path, regardless if that is a directory or a filename.
-        /// </summary>
-        /// <param name="path">Path to get the tail of.</param>
-        /// <returns>Tail of the path. This might be a directory or a filename.</returns>
-        public static string GetTail(string path)
-        {
-            if (string.IsNullOrEmpty(path))
-                return "";
-
-            if (path[path.Length - 1] == Path.DirectorySeparatorChar ||
-                path[path.Length - 1] == Path.AltDirectorySeparatorChar)
-            {
-                return Path.GetDirectoryName(path);
-            }
-
-            return Path.GetFileName(path);
-        }
-
-        /// <summary>
-        /// Returns the parent of the provided path.
-        /// </summary>
-        /// <param name="path">Path to return the parent of.</param>
-        /// <returns>Parent of the path. This usually means the folder containing the file or folder in the current path.
-        /// </returns>
-        public static string GetParent(string path)
-        {
-            string tail = GetTail(path);
-
-            return path.Remove(path.Length - tail.Length);
-        }
-
-        /// <summary>
-        /// Checks if the provided name can be used as a file name.
-        /// </summary>
-        /// <param name="name">Name to check.</param>
-        /// <returns>True if the name can be used as a file name.</returns>
-        public static bool IsValidFileName(string name)
-        {
-            return !string.IsNullOrWhiteSpace(name) &&
-                   name.IndexOfAny(Path.GetInvalidFileNameChars()) < 0;
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System.IO;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Contains helper methods dealing with file and directory paths, extending the functionality provided by 
+    /// System.IO.Path.
+    /// </summary>
+    public static class PathEx
+    {
+        /// <summary>
+        /// Checks if two paths are equal.
+        /// </summary>
+        /// <param name="a">First path.</param>
+        /// <param name="b">Second path.</param>
+        /// <returns>True if both paths point to the same place.</returns>
+        public static bool Compare(string a, string b)
+        {
+            return Path.GetFullPath(a) == Path.GetFullPath(b);
+        }
+
+        /// <summary>
+        /// Checks if one path is part of an another path.
+        /// </summary>
+        /// <param name="path">Path to check if it's part of <paramref name="parent"/>.</param>
+        /// <param name="parent">Parent that might contain <paramref name="path"/>.</param>
+        /// <returns>True if <paramref name="parent"/> contains <paramref name="path"/>.</returns>
+        public static bool IsPartOf(string path, string parent)
+        {
+            return Path.GetFullPath(path).StartsWith(Path.GetFullPath(parent));
+        }
+
+        /// <summary>
+        /// Returns the last entry in the path, regardless if that is a directory or a filename.
+        /// </summary>
+        /// <param name="path">Path to get the tail of.</param>
+        /// <returns>Tail of the path. This might be a directory or a filename.</returns>
+        public static string GetTail(string path)
+        {
+            if (string.IsNullOrEmpty(path))
+                return "";
+
+            if (path[path.Length - 1] == Path.DirectorySeparatorChar ||
+                path[path.Length - 1] == Path.AltDirectorySeparatorChar)
+            {
+                return Path.GetFileName(path.Substring(0, path.Length - 1));
+            }
+
+            return Path.GetFileName(path);
+        }
+
+        /// <summary>
+        /// Returns the parent of the provided path.
+        /// </summary>
+        /// <param name="path">Path to return the parent of.</param>
+        /// <returns>Parent of the path. This usually means the folder containing the file or folder in the current path.
+        /// </returns>
+        public static string GetParent(string path)
+        {
+            string tail = GetTail(path);
+
+            return path.Remove(path.Length - tail.Length);
+        }
+
+        /// <summary>
+        /// Checks if the provided name can be used as a file name.
+        /// </summary>
+        /// <param name="name">Name to check.</param>
+        /// <returns>True if the name can be used as a file name.</returns>
+        public static bool IsValidFileName(string name)
+        {
+            return !string.IsNullOrWhiteSpace(name) &&
+                   name.IndexOfAny(Path.GetInvalidFileNameChars()) < 0;
+        }
+    }
+}

+ 239 - 239
Source/SBansheeEditor/Source/BsScriptOSDropTarget.cpp

@@ -1,240 +1,240 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptOSDropTarget.h"
-#include "BsScriptMeta.h"
-#include "BsMonoField.h"
-#include "BsMonoClass.h"
-#include "BsMonoManager.h"
-#include "BsMonoMethod.h"
-#include "BsMonoUtil.h"
-#include "BsMonoArray.h"
-#include "BsRTTIType.h"
-#include "BsPlatform.h"
-#include "BsEditorWidget.h"
-#include "BsEditorWindowBase.h"
-#include "BsEditorWidgetContainer.h"
-#include "BsScriptEditorWindow.h"
-
-using namespace std::placeholders;
-
-namespace BansheeEngine
-{
-	ScriptOSDropTarget::OnEnterThunkDef ScriptOSDropTarget::onEnterThunk;
-	ScriptOSDropTarget::OnMoveDef ScriptOSDropTarget::onMoveThunk;
-	ScriptOSDropTarget::OnLeaveDef ScriptOSDropTarget::onLeaveThunk;
-	ScriptOSDropTarget::OnDropThunkDef ScriptOSDropTarget::onDropThunk;
-
-	ScriptOSDropTarget::ScriptOSDropTarget(MonoObject* instance, ScriptEditorWindow* parent)
-		:ScriptObject(instance), mDropTarget(nullptr), mIsDestroyed(false), mParent(parent)
-	{
-		EditorWidgetBase* parentWidget = getParentWidget();
-
-		if (parentWidget != nullptr)
-		{
-			mWidgetParentChangedConn = parentWidget->onParentChanged.connect(std::bind(&ScriptOSDropTarget::widgetParentChanged, this, _1));
-			mWidgetResizedConn = parentWidget->onResized.connect(std::bind(&ScriptOSDropTarget::widgetResized, this, _1, _2));
-			mWidgetMovedConn = parentWidget->onMoved.connect(std::bind(&ScriptOSDropTarget::widgetMoved, this, _1, _2));
-
-			EditorWindowBase* parentWindow = parentWidget->getParentWindow();
-
-			if (parentWindow != nullptr)
-				setDropTarget(parentWindow->getRenderWindow(), 0, 0, 0, 0);
-
-			mParentArea = parentWidget->getBounds();
-		}
-	}
-
-	ScriptOSDropTarget::~ScriptOSDropTarget()
-	{
-		if (!mIsDestroyed)
-			destroy();
-	}
-
-	void ScriptOSDropTarget::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptOSDropTarget::internal_CreateInstance);
-		metaData.scriptClass->addInternalCall("Internal_Destroy", &ScriptOSDropTarget::internal_Destroy);
-		metaData.scriptClass->addInternalCall("Internal_SetBounds", &ScriptOSDropTarget::internal_SetBounds);
-		metaData.scriptClass->addInternalCall("Internal_GetFilePaths", &ScriptOSDropTarget::internal_GetFilePaths);
-
-		onEnterThunk = (OnEnterThunkDef)metaData.scriptClass->getMethod("InternalDoOnEnter", 2)->getThunk();
-		onMoveThunk = (OnMoveDef)metaData.scriptClass->getMethod("InternalDoOnDrag", 2)->getThunk();
-		onLeaveThunk = (OnLeaveDef)metaData.scriptClass->getMethod("InternalDoOnLeave")->getThunk();
-		onDropThunk = (OnDropThunkDef)metaData.scriptClass->getMethod("InternalDoOnDrop", 2)->getThunk();
-	}
-
-	void ScriptOSDropTarget::internal_CreateInstance(MonoObject* instance, ScriptEditorWindow* editorWindow)
-	{
-		ScriptOSDropTarget* nativeInstance = new (bs_alloc<ScriptOSDropTarget>()) ScriptOSDropTarget(instance, editorWindow);
-	}
-
-	void ScriptOSDropTarget::internal_Destroy(ScriptOSDropTarget* nativeInstance)
-	{
-		if (nativeInstance->mIsDestroyed)
-			return;
-
-		nativeInstance->destroy();
-	}
-
-	void ScriptOSDropTarget::internal_SetBounds(ScriptOSDropTarget* nativeInstance, Rect2I* bounds)
-	{
-		if (nativeInstance->mIsDestroyed)
-			return;
-
-		nativeInstance->setBounds(*bounds);
-	}
-
-	MonoArray* ScriptOSDropTarget::internal_GetFilePaths(ScriptOSDropTarget* nativeInstance)
-	{
-		OSDropTarget* dropTarget = nativeInstance->mDropTarget;
-
-		if (nativeInstance->mIsDestroyed || dropTarget == nullptr || dropTarget->getDropType() != OSDropType::FileList)
-			return ScriptArray::create<String>(0).getInternal();
-
-		Vector<WString> fileList = dropTarget->getFileList();
-		ScriptArray output = ScriptArray::create<WString>((UINT32)fileList.size());
-
-		UINT32 idx = 0;
-		for (auto& path : fileList)
-		{
-			output.set(idx, path);
-			idx++;
-		}
-
-		return output.getInternal();
-	}
-
-	void ScriptOSDropTarget::destroy()
-	{
-		mIsDestroyed = true;
-
-		mWidgetParentChangedConn.disconnect();
-		mWidgetResizedConn.disconnect();
-
-		setDropTarget(nullptr, 0, 0, 0, 0);
-	}
-
-	EditorWidgetBase* ScriptOSDropTarget::getParentWidget() const
-	{
-		EditorWidgetBase* parentWidget = nullptr;
-
-		if (mParent != nullptr && !mParent->isDestroyed())
-			parentWidget = mParent->getEditorWidget();
-
-		return parentWidget;
-	}
-
-	Rect2I ScriptOSDropTarget::getDropTargetArea() const
-	{
-		Rect2I dropTargetArea = mArea;
-		dropTargetArea.x += mParentArea.x;
-		dropTargetArea.y += mParentArea.y;
-
-		dropTargetArea.clip(mParentArea);
-
-		return dropTargetArea;
-	}
-
-	void ScriptOSDropTarget::setDropTarget(const RenderWindowPtr& parentWindow, INT32 x, INT32 y, UINT32 width, UINT32 height)
-	{
-		if (mDropTarget != nullptr)
-		{
-			Platform::destroyDropTarget(*mDropTarget);
-
-			mDropTargetEnterConn.disconnect();
-			mDropTargetLeaveConn.disconnect();
-			mDropTargetMoveConn.disconnect();
-			mDropTargetDroppedConn.disconnect();
-		}
-
-		if (parentWindow != nullptr)
-		{
-			mDropTarget = &Platform::createDropTarget(parentWindow.get(), x, y, width, height);
-
-			mDropTargetEnterConn = mDropTarget->onEnter.connect(std::bind(&ScriptOSDropTarget::dropTargetDragMove, this, _1, _2));
-			mDropTargetMoveConn = mDropTarget->onDragOver.connect(std::bind(&ScriptOSDropTarget::dropTargetDragMove, this, _1, _2));
-			mDropTargetLeaveConn = mDropTarget->onLeave.connect(std::bind(&ScriptOSDropTarget::dropTargetDragLeave, this));
-			mDropTargetDroppedConn = mDropTarget->onDrop.connect(std::bind(&ScriptOSDropTarget::dropTargetDragDropped, this, _1, _2));
-		}
-		else
-			mDropTarget = nullptr;
-	}
-
-	void ScriptOSDropTarget::setBounds(const Rect2I& bounds)
-	{
-		mArea = bounds;
-		Rect2I dropTargetArea = getDropTargetArea();
-
-		if (mDropTarget != nullptr)
-			mDropTarget->setArea(dropTargetArea.x, dropTargetArea.y, dropTargetArea.width, dropTargetArea.height);
-	}
-
-	void ScriptOSDropTarget::dropTargetDragEnter(ScriptOSDropTarget* thisPtr, INT32 x, INT32 y)
-	{
-		if (thisPtr->mIsDestroyed)
-			return;
-
-		MonoUtil::invokeThunk(onEnterThunk, thisPtr->getManagedInstance(), 
-			x - thisPtr->mParentArea.x, y - thisPtr->mParentArea.y);
-	}
-
-	void ScriptOSDropTarget::dropTargetDragMove(ScriptOSDropTarget* thisPtr, INT32 x, INT32 y)
-	{
-		if (thisPtr->mIsDestroyed)
-			return;
-
-		MonoUtil::invokeThunk(onMoveThunk, thisPtr->getManagedInstance(), 
-			x - thisPtr->mParentArea.x, y - thisPtr->mParentArea.y);
-	}
-
-	void ScriptOSDropTarget::dropTargetDragLeave(ScriptOSDropTarget* thisPtr)
-	{
-		if (thisPtr->mIsDestroyed)
-			return;
-
-		MonoUtil::invokeThunk(onLeaveThunk, thisPtr->getManagedInstance());
-	}
-
-	void ScriptOSDropTarget::dropTargetDragDropped(ScriptOSDropTarget* thisPtr, INT32 x, INT32 y)
-	{
-		if (thisPtr->mIsDestroyed)
-			return;
-
-		MonoUtil::invokeThunk(onDropThunk, thisPtr->getManagedInstance(), 
-			x - thisPtr->mParentArea.x, y - thisPtr->mParentArea.y);
-	}
-
-	void ScriptOSDropTarget::widgetParentChanged(EditorWidgetContainer* parent)
-	{
-		RenderWindowPtr parentRenderWindow;
-		if (parent != nullptr)
-		{
-			EditorWindowBase* parentWindow = parent->getParentWindow();
-
-			if (parentWindow != nullptr)
-				parentRenderWindow = parentWindow->getRenderWindow();
-		}
-
-		if (parentRenderWindow == nullptr)
-			mParentArea = Rect2I();
-		
-		Rect2I dropTargetArea = getDropTargetArea();
-		setDropTarget(parentRenderWindow, dropTargetArea.x, dropTargetArea.y, dropTargetArea.width, dropTargetArea.height);
-	}
-
-	void ScriptOSDropTarget::widgetResized(UINT32 width, UINT32 height)
-	{
-		mParentArea.width = width;
-		mParentArea.height = height;
-
-		setBounds(mArea);
-	}
-
-	void ScriptOSDropTarget::widgetMoved(INT32 x, INT32 y)
-	{
-		mParentArea.x = x;
-		mParentArea.y = y;
-
-		setBounds(mArea);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptOSDropTarget.h"
+#include "BsScriptMeta.h"
+#include "BsMonoField.h"
+#include "BsMonoClass.h"
+#include "BsMonoManager.h"
+#include "BsMonoMethod.h"
+#include "BsMonoUtil.h"
+#include "BsMonoArray.h"
+#include "BsRTTIType.h"
+#include "BsPlatform.h"
+#include "BsEditorWidget.h"
+#include "BsEditorWindowBase.h"
+#include "BsEditorWidgetContainer.h"
+#include "BsScriptEditorWindow.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	ScriptOSDropTarget::OnEnterThunkDef ScriptOSDropTarget::onEnterThunk;
+	ScriptOSDropTarget::OnMoveDef ScriptOSDropTarget::onMoveThunk;
+	ScriptOSDropTarget::OnLeaveDef ScriptOSDropTarget::onLeaveThunk;
+	ScriptOSDropTarget::OnDropThunkDef ScriptOSDropTarget::onDropThunk;
+
+	ScriptOSDropTarget::ScriptOSDropTarget(MonoObject* instance, ScriptEditorWindow* parent)
+		:ScriptObject(instance), mDropTarget(nullptr), mIsDestroyed(false), mParent(parent)
+	{
+		EditorWidgetBase* parentWidget = getParentWidget();
+
+		if (parentWidget != nullptr)
+		{
+			mWidgetParentChangedConn = parentWidget->onParentChanged.connect(std::bind(&ScriptOSDropTarget::widgetParentChanged, this, _1));
+			mWidgetResizedConn = parentWidget->onResized.connect(std::bind(&ScriptOSDropTarget::widgetResized, this, _1, _2));
+			mWidgetMovedConn = parentWidget->onMoved.connect(std::bind(&ScriptOSDropTarget::widgetMoved, this, _1, _2));
+
+			EditorWindowBase* parentWindow = parentWidget->getParentWindow();
+
+			if (parentWindow != nullptr)
+				setDropTarget(parentWindow->getRenderWindow(), 0, 0, 0, 0);
+
+			mParentArea = parentWidget->getBounds();
+		}
+	}
+
+	ScriptOSDropTarget::~ScriptOSDropTarget()
+	{
+		if (!mIsDestroyed)
+			destroy();
+	}
+
+	void ScriptOSDropTarget::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptOSDropTarget::internal_CreateInstance);
+		metaData.scriptClass->addInternalCall("Internal_Destroy", &ScriptOSDropTarget::internal_Destroy);
+		metaData.scriptClass->addInternalCall("Internal_SetBounds", &ScriptOSDropTarget::internal_SetBounds);
+		metaData.scriptClass->addInternalCall("Internal_GetFilePaths", &ScriptOSDropTarget::internal_GetFilePaths);
+
+		onEnterThunk = (OnEnterThunkDef)metaData.scriptClass->getMethod("InternalDoOnEnter", 2)->getThunk();
+		onMoveThunk = (OnMoveDef)metaData.scriptClass->getMethod("InternalDoOnDrag", 2)->getThunk();
+		onLeaveThunk = (OnLeaveDef)metaData.scriptClass->getMethod("InternalDoOnLeave")->getThunk();
+		onDropThunk = (OnDropThunkDef)metaData.scriptClass->getMethod("InternalDoOnDrop", 2)->getThunk();
+	}
+
+	void ScriptOSDropTarget::internal_CreateInstance(MonoObject* instance, ScriptEditorWindow* editorWindow)
+	{
+		ScriptOSDropTarget* nativeInstance = new (bs_alloc<ScriptOSDropTarget>()) ScriptOSDropTarget(instance, editorWindow);
+	}
+
+	void ScriptOSDropTarget::internal_Destroy(ScriptOSDropTarget* nativeInstance)
+	{
+		if (nativeInstance->mIsDestroyed)
+			return;
+
+		nativeInstance->destroy();
+	}
+
+	void ScriptOSDropTarget::internal_SetBounds(ScriptOSDropTarget* nativeInstance, Rect2I* bounds)
+	{
+		if (nativeInstance->mIsDestroyed)
+			return;
+
+		nativeInstance->setBounds(*bounds);
+	}
+
+	MonoArray* ScriptOSDropTarget::internal_GetFilePaths(ScriptOSDropTarget* nativeInstance)
+	{
+		OSDropTarget* dropTarget = nativeInstance->mDropTarget;
+
+		if (nativeInstance->mIsDestroyed || dropTarget == nullptr || dropTarget->getDropType() != OSDropType::FileList)
+			return ScriptArray::create<String>(0).getInternal();
+
+		Vector<WString> fileList = dropTarget->getFileList();
+		ScriptArray output = ScriptArray::create<WString>((UINT32)fileList.size());
+
+		UINT32 idx = 0;
+		for (auto& path : fileList)
+		{
+			output.set(idx, path);
+			idx++;
+		}
+
+		return output.getInternal();
+	}
+
+	void ScriptOSDropTarget::destroy()
+	{
+		mIsDestroyed = true;
+
+		mWidgetParentChangedConn.disconnect();
+		mWidgetResizedConn.disconnect();
+
+		setDropTarget(nullptr, 0, 0, 0, 0);
+	}
+
+	EditorWidgetBase* ScriptOSDropTarget::getParentWidget() const
+	{
+		EditorWidgetBase* parentWidget = nullptr;
+
+		if (mParent != nullptr && !mParent->isDestroyed())
+			parentWidget = mParent->getEditorWidget();
+
+		return parentWidget;
+	}
+
+	Rect2I ScriptOSDropTarget::getDropTargetArea() const
+	{
+		Rect2I dropTargetArea = mArea;
+		dropTargetArea.x += mParentArea.x;
+		dropTargetArea.y += mParentArea.y;
+
+		dropTargetArea.clip(mParentArea);
+
+		return dropTargetArea;
+	}
+
+	void ScriptOSDropTarget::setDropTarget(const RenderWindowPtr& parentWindow, INT32 x, INT32 y, UINT32 width, UINT32 height)
+	{
+		if (mDropTarget != nullptr)
+		{
+			Platform::destroyDropTarget(*mDropTarget);
+
+			mDropTargetEnterConn.disconnect();
+			mDropTargetLeaveConn.disconnect();
+			mDropTargetMoveConn.disconnect();
+			mDropTargetDroppedConn.disconnect();
+		}
+
+		if (parentWindow != nullptr)
+		{
+			mDropTarget = &Platform::createDropTarget(parentWindow.get(), x, y, width, height);
+
+			mDropTargetEnterConn = mDropTarget->onEnter.connect(std::bind(&ScriptOSDropTarget::dropTargetDragEnter, this, _1, _2));
+			mDropTargetMoveConn = mDropTarget->onDragOver.connect(std::bind(&ScriptOSDropTarget::dropTargetDragMove, this, _1, _2));
+			mDropTargetLeaveConn = mDropTarget->onLeave.connect(std::bind(&ScriptOSDropTarget::dropTargetDragLeave, this));
+			mDropTargetDroppedConn = mDropTarget->onDrop.connect(std::bind(&ScriptOSDropTarget::dropTargetDragDropped, this, _1, _2));
+		}
+		else
+			mDropTarget = nullptr;
+	}
+
+	void ScriptOSDropTarget::setBounds(const Rect2I& bounds)
+	{
+		mArea = bounds;
+		Rect2I dropTargetArea = getDropTargetArea();
+
+		if (mDropTarget != nullptr)
+			mDropTarget->setArea(dropTargetArea.x, dropTargetArea.y, dropTargetArea.width, dropTargetArea.height);
+	}
+
+	void ScriptOSDropTarget::dropTargetDragEnter(ScriptOSDropTarget* thisPtr, INT32 x, INT32 y)
+	{
+		if (thisPtr->mIsDestroyed)
+			return;
+
+		MonoUtil::invokeThunk(onEnterThunk, thisPtr->getManagedInstance(), 
+			x - thisPtr->mParentArea.x, y - thisPtr->mParentArea.y);
+	}
+
+	void ScriptOSDropTarget::dropTargetDragMove(ScriptOSDropTarget* thisPtr, INT32 x, INT32 y)
+	{
+		if (thisPtr->mIsDestroyed)
+			return;
+
+		MonoUtil::invokeThunk(onMoveThunk, thisPtr->getManagedInstance(), 
+			x - thisPtr->mParentArea.x, y - thisPtr->mParentArea.y);
+	}
+
+	void ScriptOSDropTarget::dropTargetDragLeave(ScriptOSDropTarget* thisPtr)
+	{
+		if (thisPtr->mIsDestroyed)
+			return;
+
+		MonoUtil::invokeThunk(onLeaveThunk, thisPtr->getManagedInstance());
+	}
+
+	void ScriptOSDropTarget::dropTargetDragDropped(ScriptOSDropTarget* thisPtr, INT32 x, INT32 y)
+	{
+		if (thisPtr->mIsDestroyed)
+			return;
+
+		MonoUtil::invokeThunk(onDropThunk, thisPtr->getManagedInstance(), 
+			x - thisPtr->mParentArea.x, y - thisPtr->mParentArea.y);
+	}
+
+	void ScriptOSDropTarget::widgetParentChanged(EditorWidgetContainer* parent)
+	{
+		RenderWindowPtr parentRenderWindow;
+		if (parent != nullptr)
+		{
+			EditorWindowBase* parentWindow = parent->getParentWindow();
+
+			if (parentWindow != nullptr)
+				parentRenderWindow = parentWindow->getRenderWindow();
+		}
+
+		if (parentRenderWindow == nullptr)
+			mParentArea = Rect2I();
+		
+		Rect2I dropTargetArea = getDropTargetArea();
+		setDropTarget(parentRenderWindow, dropTargetArea.x, dropTargetArea.y, dropTargetArea.width, dropTargetArea.height);
+	}
+
+	void ScriptOSDropTarget::widgetResized(UINT32 width, UINT32 height)
+	{
+		mParentArea.width = width;
+		mParentArea.height = height;
+
+		setBounds(mArea);
+	}
+
+	void ScriptOSDropTarget::widgetMoved(INT32 x, INT32 y)
+	{
+		mParentArea.x = x;
+		mParentArea.y = y;
+
+		setBounds(mArea);
+	}
 }