BsGUIResourceTreeView.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. #include "BsGUIResourceTreeView.h"
  2. #include "BsGUISkin.h"
  3. #include "BsProjectLibrary.h"
  4. #include "BsDragAndDropManager.h"
  5. #include "CmResources.h"
  6. #include "CmResourceManifest.h"
  7. #include "BsProjectLibrary.h"
  8. #include "CmFileSystem.h"
  9. using namespace CamelotFramework;
  10. using namespace BansheeEngine;
  11. namespace BansheeEditor
  12. {
  13. GUIResourceTreeView::InternalDraggedResources::InternalDraggedResources(UINT32 numObjects)
  14. :numObjects(numObjects)
  15. {
  16. resourcePaths = cm_newN<WPath>(numObjects);
  17. }
  18. GUIResourceTreeView::InternalDraggedResources::~InternalDraggedResources()
  19. {
  20. cm_deleteN(resourcePaths, numObjects);
  21. resourcePaths = nullptr;
  22. }
  23. GUIResourceTreeView::GUIResourceTreeView(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle,
  24. GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, GUIElementStyle* editBoxStyle,
  25. BS::GUIElementStyle* dragHighlightStyle, BS::GUIElementStyle* dragSepHighlightStyle, const GUILayoutOptions& layoutOptions)
  26. :GUITreeView(parent, backgroundStyle, elementBtnStyle, foldoutBtnStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle,
  27. dragSepHighlightStyle, layoutOptions), mDraggedResources(nullptr)
  28. {
  29. struct StackElem
  30. {
  31. StackElem(const ProjectLibrary::LibraryEntry* entry, ResourceTreeElement* treeElem)
  32. :entry(entry), treeElem(treeElem)
  33. { }
  34. const ProjectLibrary::LibraryEntry* entry;
  35. ResourceTreeElement* treeElem;
  36. };
  37. ProjectLibrary::instance().onEntryAdded.connect(boost::bind(&GUIResourceTreeView::entryAdded, this, _1));
  38. ProjectLibrary::instance().onEntryRemoved.connect(boost::bind(&GUIResourceTreeView::entryRemoved, this, _1));
  39. const ProjectLibrary::LibraryEntry* rootEntry = ProjectLibrary::instance().getRootEntry();
  40. mRootElement.mIsExpanded = true;
  41. Stack<StackElem>::type todo;
  42. todo.push(StackElem(rootEntry, &mRootElement));
  43. while(!todo.empty())
  44. {
  45. StackElem curElem = todo.top();
  46. todo.top();
  47. const ProjectLibrary::DirectoryEntry* dirEntry = static_cast<const ProjectLibrary::DirectoryEntry*>(curElem.entry);
  48. for(auto& child : dirEntry->mChildren)
  49. {
  50. ResourceTreeElement* newChild = addTreeElement(curElem.treeElem, curElem.entry->path);
  51. if(child->type == ProjectLibrary::LibraryEntryType::Directory)
  52. todo.push(StackElem(child, newChild));
  53. }
  54. sortTreeElement(curElem.treeElem);
  55. }
  56. }
  57. GUIResourceTreeView::~GUIResourceTreeView()
  58. {
  59. }
  60. GUIResourceTreeView* GUIResourceTreeView::create(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle,
  61. GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, GUIElementStyle* editBoxStyle, GUIElementStyle* dragHighlightStyle,
  62. GUIElementStyle* dragSepHighlightStyle)
  63. {
  64. return new (cm_alloc<GUIResourceTreeView, PoolAlloc>()) GUIResourceTreeView(parent, backgroundStyle, elementBtnStyle, foldoutBtnStyle,
  65. selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUILayoutOptions::create(&GUISkin::DefaultStyle));
  66. }
  67. GUIResourceTreeView* GUIResourceTreeView::create(GUIWidget& parent, const GUIOptions& options, GUIElementStyle* backgroundStyle,
  68. GUIElementStyle* elementBtnStyle, GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle,
  69. GUIElementStyle* editBoxStyle, GUIElementStyle* dragHighlightStyle, GUIElementStyle* dragSepHighlightStyle)
  70. {
  71. return new (cm_alloc<GUIResourceTreeView, PoolAlloc>()) GUIResourceTreeView(parent, backgroundStyle, elementBtnStyle,
  72. foldoutBtnStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUILayoutOptions::create(options, &GUISkin::DefaultStyle));
  73. }
  74. void GUIResourceTreeView::updateTreeElementHierarchy()
  75. {
  76. // Do nothing, updates are handled via callbacks
  77. }
  78. void GUIResourceTreeView::renameTreeElement(GUITreeView::TreeElement* element, const CM::WString& name)
  79. {
  80. ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(element);
  81. WPath oldPath = resourceTreeElement->mFullPath;
  82. WPath newPath = PathUtil::combine(PathUtil::parentPath(oldPath), toPath(name));
  83. ProjectLibrary::instance().moveEntry(oldPath, findUniquePath(newPath));
  84. }
  85. GUIResourceTreeView::ResourceTreeElement* GUIResourceTreeView::addTreeElement(ResourceTreeElement* parent, const CM::WPath& fullPath)
  86. {
  87. ResourceTreeElement* newChild = cm_new<ResourceTreeElement>();
  88. newChild->mParent = parent;
  89. newChild->mName = toString(PathUtil::getFilename(fullPath));
  90. newChild->mFullPath = fullPath;
  91. newChild->mSortedIdx = (UINT32)parent->mChildren.size();
  92. newChild->mIsDirty = true;
  93. parent->mChildren.push_back(newChild);
  94. return newChild;
  95. }
  96. void GUIResourceTreeView::deleteTreeElement(ResourceTreeElement* element)
  97. {
  98. closeTemporarilyExpandedElements(); // In case this element is one of them
  99. if(element->mIsSelected)
  100. unselectElement(element);
  101. Stack<ResourceTreeElement*>::type todo;
  102. todo.push(element);
  103. while(!todo.empty())
  104. {
  105. ResourceTreeElement* cur = todo.top();
  106. todo.pop();
  107. for(auto& child : cur->mChildren)
  108. todo.push(static_cast<ResourceTreeElement*>(child));
  109. }
  110. if(element->mParent != nullptr)
  111. {
  112. auto iterFind = std::find(element->mParent->mChildren.begin(), element->mParent->mChildren.end(), element);
  113. if(iterFind != element->mParent->mChildren.end())
  114. element->mParent->mChildren.erase(iterFind);
  115. }
  116. cm_delete(element);
  117. }
  118. void GUIResourceTreeView::sortTreeElement(ResourceTreeElement* element)
  119. {
  120. if(element->mChildren.size() > 0)
  121. quicksortTreeElements(element->mChildren, 0, (INT32)element->mChildren.size() - 1);
  122. }
  123. void GUIResourceTreeView::quicksortTreeElements(CM::Vector<TreeElement*>::type& elements, INT32 first, INT32 last)
  124. {
  125. if(first < last)
  126. {
  127. int pivot = first + std::rand() % (last - first);
  128. std::swap(elements[pivot]->mSortedIdx, elements[last]->mSortedIdx);
  129. pivot = last;
  130. int i = first;
  131. for(int j = first; j < last; j++)
  132. {
  133. INT32 stringCompare = elements[j]->mName.compare(elements[pivot]->mName);
  134. if(stringCompare < 0)
  135. {
  136. std::swap(elements[j]->mSortedIdx, elements[i]->mSortedIdx);
  137. i++;
  138. }
  139. }
  140. std::swap(elements[last]->mSortedIdx, elements[pivot]->mSortedIdx);
  141. quicksortTreeElements(elements, first, i - 1);
  142. quicksortTreeElements(elements, i + 1, last);
  143. }
  144. }
  145. GUIResourceTreeView::ResourceTreeElement* GUIResourceTreeView::findTreeElement(const CM::WPath& fullPath)
  146. {
  147. auto pathIter = fullPath.begin();
  148. auto rootIter = mRootElement.mFullPath.begin();
  149. while(*pathIter == *rootIter)
  150. {
  151. ++pathIter;
  152. ++rootIter;
  153. }
  154. if(pathIter == fullPath.begin()) // Supplied path not part of the root path
  155. return nullptr;
  156. --pathIter;
  157. Stack<ResourceTreeElement*>::type todo;
  158. todo.push(&mRootElement);
  159. while(!todo.empty())
  160. {
  161. ResourceTreeElement* current = todo.top();
  162. todo.pop();
  163. if(*pathIter == *(--(current->mFullPath.end())))
  164. {
  165. for(auto& child : current->mChildren)
  166. todo.push(static_cast<ResourceTreeElement*>(child));
  167. if(pathIter == fullPath.end())
  168. return current;
  169. ++pathIter;
  170. }
  171. }
  172. return nullptr;
  173. }
  174. void GUIResourceTreeView::entryAdded(const WPath& path)
  175. {
  176. WPath parentPath = PathUtil::parentPath(path);
  177. ResourceTreeElement* parentElement = findTreeElement(parentPath);
  178. assert(parentElement != nullptr);
  179. addTreeElement(parentElement, path);
  180. sortTreeElement(parentElement);
  181. markContentAsDirty();
  182. }
  183. void GUIResourceTreeView::entryRemoved(const WPath& path)
  184. {
  185. ResourceTreeElement* treeElement = findTreeElement(path);
  186. if(treeElement != nullptr)
  187. deleteTreeElement(treeElement);
  188. }
  189. CM::WPath GUIResourceTreeView::findUniquePath(const CM::WPath& path)
  190. {
  191. if(FileSystem::exists(path))
  192. {
  193. WPath noExtensionPath = path;
  194. WPath extension = PathUtil::getExtension(path);
  195. PathUtil::replaceExtension(noExtensionPath, L"");
  196. WPath newPath;
  197. UINT32 cnt = 1;
  198. do
  199. {
  200. newPath = PathUtil::combine(PathUtil::combine(noExtensionPath, toPath(L" " + toWString(cnt))), extension);
  201. cnt++;
  202. } while (FileSystem::exists(newPath));
  203. }
  204. else
  205. return path;
  206. }
  207. bool GUIResourceTreeView::acceptDragAndDrop() const
  208. {
  209. return DragAndDropManager::instance().isDragInProgress() && DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::Resources;
  210. }
  211. void GUIResourceTreeView::dragAndDropStart()
  212. {
  213. assert(mDraggedResources == nullptr);
  214. DraggedResources* draggedResources = cm_new<DraggedResources>();
  215. InternalDraggedResources* internalDraggedResources = cm_new<InternalDraggedResources>((UINT32)mSelectedElements.size());
  216. ResourceManifestPtr resourceManifest = gResources().getResourceManifest();
  217. UINT32 cnt = 0;
  218. UINT32 numValidResources = 0;
  219. for(auto& selectedElement : mSelectedElements)
  220. {
  221. ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(selectedElement.element);
  222. internalDraggedResources->resourcePaths[cnt] = resourceTreeElement->mFullPath;
  223. cnt++;
  224. if(resourceManifest->filePathExists(internalDraggedResources->resourcePaths[cnt]))
  225. draggedResources->resourceUUIDs.push_back(resourceManifest->filePathToUUID(internalDraggedResources->resourcePaths[cnt]));
  226. }
  227. mDraggedResources = internalDraggedResources;
  228. DragAndDropManager::instance().startDrag(HTexture(), (UINT32)DragAndDropType::Resources, (void*)draggedResources,
  229. boost::bind(&GUIResourceTreeView::dragAndDropFinalize, this));
  230. }
  231. void GUIResourceTreeView::dragAndDropEnded(TreeElement* overTreeElement)
  232. {
  233. if(overTreeElement != nullptr && mDraggedResources != nullptr)
  234. {
  235. ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(overTreeElement);
  236. WPath destDir = resourceTreeElement->mFullPath;
  237. if(FileSystem::isFile(destDir))
  238. destDir = PathUtil::parentPath(destDir);
  239. for(UINT32 i = 0; i < mDraggedResources->numObjects; i++)
  240. {
  241. WPath filename = PathUtil::getFilename(mDraggedResources->resourcePaths[i]);
  242. WPath newPath = PathUtil::combine(destDir, filename);
  243. ProjectLibrary::instance().moveEntry(mDraggedResources->resourcePaths[i], findUniquePath(newPath));
  244. }
  245. }
  246. }
  247. void GUIResourceTreeView::dragAndDropFinalize()
  248. {
  249. mDragInProgress = false;
  250. markContentAsDirty();
  251. DraggedResources* draggedResources = reinterpret_cast<DraggedResources*>(DragAndDropManager::instance().getDragData());
  252. cm_delete(draggedResources);
  253. if(mDraggedResources != nullptr)
  254. {
  255. cm_delete(mDraggedResources);
  256. mDraggedResources = nullptr;
  257. }
  258. }
  259. const String& GUIResourceTreeView::getGUITypeName()
  260. {
  261. static String typeName = "ResourceTreeView";
  262. return typeName;
  263. }
  264. }