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.6KB

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