Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_FileTreeComponent.cpp 9.5KB

7 years ago
7 years ago
7 years ago
7 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. Image juce_createIconForFile (const File&);
  22. //==============================================================================
  23. class FileListTreeItem : public TreeViewItem,
  24. private TimeSliceClient,
  25. private AsyncUpdater,
  26. private ChangeListener
  27. {
  28. public:
  29. FileListTreeItem (FileTreeComponent& treeComp,
  30. DirectoryContentsList* parentContents,
  31. int indexInContents,
  32. const File& f,
  33. TimeSliceThread& t)
  34. : file (f),
  35. owner (treeComp),
  36. parentContentsList (parentContents),
  37. indexInContentsList (indexInContents),
  38. subContentsList (nullptr, false),
  39. thread (t)
  40. {
  41. DirectoryContentsList::FileInfo fileInfo;
  42. if (parentContents != nullptr
  43. && parentContents->getFileInfo (indexInContents, fileInfo))
  44. {
  45. fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize);
  46. modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M");
  47. isDirectory = fileInfo.isDirectory;
  48. }
  49. else
  50. {
  51. isDirectory = true;
  52. }
  53. }
  54. ~FileListTreeItem()
  55. {
  56. thread.removeTimeSliceClient (this);
  57. clearSubItems();
  58. removeSubContentsList();
  59. }
  60. //==============================================================================
  61. bool mightContainSubItems() override { return isDirectory; }
  62. String getUniqueName() const override { return file.getFullPathName(); }
  63. int getItemHeight() const override { return owner.getItemHeight(); }
  64. var getDragSourceDescription() override { return owner.getDragAndDropDescription(); }
  65. void itemOpennessChanged (bool isNowOpen) override
  66. {
  67. if (isNowOpen)
  68. {
  69. clearSubItems();
  70. isDirectory = file.isDirectory();
  71. if (isDirectory)
  72. {
  73. if (subContentsList == nullptr)
  74. {
  75. jassert (parentContentsList != nullptr);
  76. auto l = new DirectoryContentsList (parentContentsList->getFilter(), thread);
  77. l->setDirectory (file,
  78. parentContentsList->isFindingDirectories(),
  79. parentContentsList->isFindingFiles());
  80. setSubContentsList (l, true);
  81. }
  82. changeListenerCallback (nullptr);
  83. }
  84. }
  85. }
  86. void removeSubContentsList()
  87. {
  88. if (subContentsList != nullptr)
  89. {
  90. subContentsList->removeChangeListener (this);
  91. subContentsList.clear();
  92. }
  93. }
  94. void setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList)
  95. {
  96. removeSubContentsList();
  97. OptionalScopedPointer<DirectoryContentsList> newPointer (newList, canDeleteList);
  98. subContentsList = newPointer;
  99. newList->addChangeListener (this);
  100. }
  101. bool selectFile (const File& target)
  102. {
  103. if (file == target)
  104. {
  105. setSelected (true, true);
  106. return true;
  107. }
  108. if (target.isAChildOf (file))
  109. {
  110. setOpen (true);
  111. for (int maxRetries = 500; --maxRetries > 0;)
  112. {
  113. for (int i = 0; i < getNumSubItems(); ++i)
  114. if (auto* f = dynamic_cast<FileListTreeItem*> (getSubItem (i)))
  115. if (f->selectFile (target))
  116. return true;
  117. // if we've just opened and the contents are still loading, wait for it..
  118. if (subContentsList != nullptr && subContentsList->isStillLoading())
  119. {
  120. Thread::sleep (10);
  121. rebuildItemsFromContentList();
  122. }
  123. else
  124. {
  125. break;
  126. }
  127. }
  128. }
  129. return false;
  130. }
  131. void changeListenerCallback (ChangeBroadcaster*) override
  132. {
  133. rebuildItemsFromContentList();
  134. }
  135. void rebuildItemsFromContentList()
  136. {
  137. clearSubItems();
  138. if (isOpen() && subContentsList != nullptr)
  139. {
  140. for (int i = 0; i < subContentsList->getNumFiles(); ++i)
  141. addSubItem (new FileListTreeItem (owner, subContentsList, i,
  142. subContentsList->getFile(i), thread));
  143. }
  144. }
  145. void paintItem (Graphics& g, int width, int height) override
  146. {
  147. if (file != File())
  148. {
  149. updateIcon (true);
  150. if (icon.isNull())
  151. thread.addTimeSliceClient (this);
  152. }
  153. owner.getLookAndFeel().drawFileBrowserRow (g, width, height,
  154. file, file.getFileName(),
  155. &icon, fileSize, modTime,
  156. isDirectory, isSelected(),
  157. indexInContentsList, owner);
  158. }
  159. void itemClicked (const MouseEvent& e) override
  160. {
  161. owner.sendMouseClickMessage (file, e);
  162. }
  163. void itemDoubleClicked (const MouseEvent& e) override
  164. {
  165. TreeViewItem::itemDoubleClicked (e);
  166. owner.sendDoubleClickMessage (file);
  167. }
  168. void itemSelectionChanged (bool) override
  169. {
  170. owner.sendSelectionChangeMessage();
  171. }
  172. int useTimeSlice() override
  173. {
  174. updateIcon (false);
  175. return -1;
  176. }
  177. void handleAsyncUpdate() override
  178. {
  179. owner.repaint();
  180. }
  181. const File file;
  182. private:
  183. FileTreeComponent& owner;
  184. DirectoryContentsList* parentContentsList;
  185. int indexInContentsList;
  186. OptionalScopedPointer<DirectoryContentsList> subContentsList;
  187. bool isDirectory;
  188. TimeSliceThread& thread;
  189. Image icon;
  190. String fileSize, modTime;
  191. void updateIcon (const bool onlyUpdateIfCached)
  192. {
  193. if (icon.isNull())
  194. {
  195. auto hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
  196. auto im = ImageCache::getFromHashCode (hashCode);
  197. if (im.isNull() && ! onlyUpdateIfCached)
  198. {
  199. im = juce_createIconForFile (file);
  200. if (im.isValid())
  201. ImageCache::addImageToCache (im, hashCode);
  202. }
  203. if (im.isValid())
  204. {
  205. icon = im;
  206. triggerAsyncUpdate();
  207. }
  208. }
  209. }
  210. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem)
  211. };
  212. //==============================================================================
  213. FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
  214. : DirectoryContentsDisplayComponent (listToShow),
  215. itemHeight (22)
  216. {
  217. setRootItemVisible (false);
  218. refresh();
  219. }
  220. FileTreeComponent::~FileTreeComponent()
  221. {
  222. deleteRootItem();
  223. }
  224. void FileTreeComponent::refresh()
  225. {
  226. deleteRootItem();
  227. auto root = new FileListTreeItem (*this, nullptr, 0, directoryContentsList.getDirectory(),
  228. directoryContentsList.getTimeSliceThread());
  229. root->setSubContentsList (&directoryContentsList, false);
  230. setRootItem (root);
  231. }
  232. //==============================================================================
  233. File FileTreeComponent::getSelectedFile (const int index) const
  234. {
  235. if (auto* item = dynamic_cast<const FileListTreeItem*> (getSelectedItem (index)))
  236. return item->file;
  237. return {};
  238. }
  239. void FileTreeComponent::deselectAllFiles()
  240. {
  241. clearSelectedItems();
  242. }
  243. void FileTreeComponent::scrollToTop()
  244. {
  245. getViewport()->getVerticalScrollBar().setCurrentRangeStart (0);
  246. }
  247. void FileTreeComponent::setDragAndDropDescription (const String& description)
  248. {
  249. dragAndDropDescription = description;
  250. }
  251. void FileTreeComponent::setSelectedFile (const File& target)
  252. {
  253. if (auto* t = dynamic_cast<FileListTreeItem*> (getRootItem()))
  254. if (! t->selectFile (target))
  255. clearSelectedItems();
  256. }
  257. void FileTreeComponent::setItemHeight (int newHeight)
  258. {
  259. if (itemHeight != newHeight)
  260. {
  261. itemHeight = newHeight;
  262. if (auto* root = getRootItem())
  263. root->treeHasChanged();
  264. }
  265. }
  266. } // namespace juce