浏览代码

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 年之前
父节点
当前提交
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)
             if (btn != PasteKey)
             {
             {
                 // The system ensures elsewhere that only either a resource or a scene object is selected, but not both
                 // 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>();
                     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)
             if (paths != null)
             {
             {
-                string[] filePaths = GetFiles(paths);
-
                 List<string> addedResources = new List<string>();
                 List<string> addedResources = new List<string>();
-                foreach (var path in filePaths)
+                foreach (var path in paths)
                 {
                 {
                     string absolutePath = path;
                     string absolutePath = path;
                     if (!Path.IsPathRooted(absolutePath))
                     if (!Path.IsPathRooted(absolutePath))
@@ -1404,19 +1402,24 @@ namespace BansheeEditor
                     if (PathEx.Compare(absolutePath, destination))
                     if (PathEx.Compare(absolutePath, destination))
                         continue;
                         continue;
 
 
-                    bool doCopy = !ProjectLibrary.Exists(absolutePath);
+                    bool newFile = !ProjectLibrary.Exists(absolutePath);
+                    if (!newFile)
+                    {
+                        if (ProjectLibrary.IsSubresource(absolutePath))
+                            continue;
+                    }
 
 
                     string uniqueDestination = LibraryUtility.GetUniquePath(destination);
                     string uniqueDestination = LibraryUtility.GetUniquePath(destination);
                     if (Directory.Exists(path))
                     if (Directory.Exists(path))
                     {
                     {
-                        if (doCopy)
+                        if (newFile)
                             DirectoryEx.Copy(absolutePath, uniqueDestination);
                             DirectoryEx.Copy(absolutePath, uniqueDestination);
                         else
                         else
                             DirectoryEx.Move(absolutePath, uniqueDestination);
                             DirectoryEx.Move(absolutePath, uniqueDestination);
                     }
                     }
                     else if (File.Exists(path))
                     else if (File.Exists(path))
                     {
                     {
-                        if (doCopy)
+                        if (newFile)
                             FileEx.Copy(absolutePath, uniqueDestination);
                             FileEx.Copy(absolutePath, uniqueDestination);
                         else
                         else
                             ProjectLibrary.Move(absolutePath, uniqueDestination);
                             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);
+	}
 }
 }