The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

308 lines
9.2KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. Image juce_createIconForFile (const File& file);
  18. //==============================================================================
  19. class FileListTreeItem : public TreeViewItem,
  20. private TimeSliceClient,
  21. private AsyncUpdater,
  22. private ChangeListener
  23. {
  24. public:
  25. FileListTreeItem (FileTreeComponent& treeComp,
  26. DirectoryContentsList* const parentContents,
  27. const int indexInContents,
  28. const File& f,
  29. TimeSliceThread& t)
  30. : file (f),
  31. owner (treeComp),
  32. parentContentsList (parentContents),
  33. indexInContentsList (indexInContents),
  34. subContentsList (nullptr, false),
  35. thread (t)
  36. {
  37. DirectoryContentsList::FileInfo fileInfo;
  38. if (parentContents != nullptr
  39. && parentContents->getFileInfo (indexInContents, fileInfo))
  40. {
  41. fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize);
  42. modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M");
  43. isDirectory = fileInfo.isDirectory;
  44. }
  45. else
  46. {
  47. isDirectory = true;
  48. }
  49. }
  50. ~FileListTreeItem()
  51. {
  52. thread.removeTimeSliceClient (this);
  53. clearSubItems();
  54. removeSubContentsList();
  55. }
  56. //==============================================================================
  57. bool mightContainSubItems() override { return isDirectory; }
  58. String getUniqueName() const override { return file.getFullPathName(); }
  59. int getItemHeight() const override { return 22; }
  60. var getDragSourceDescription() override { return owner.getDragAndDropDescription(); }
  61. void itemOpennessChanged (bool isNowOpen) override
  62. {
  63. if (isNowOpen)
  64. {
  65. clearSubItems();
  66. isDirectory = file.isDirectory();
  67. if (isDirectory)
  68. {
  69. if (subContentsList == nullptr)
  70. {
  71. jassert (parentContentsList != nullptr);
  72. DirectoryContentsList* const l = new DirectoryContentsList (parentContentsList->getFilter(), thread);
  73. l->setDirectory (file, true, true);
  74. setSubContentsList (l, true);
  75. }
  76. changeListenerCallback (nullptr);
  77. }
  78. }
  79. }
  80. void removeSubContentsList()
  81. {
  82. if (subContentsList != nullptr)
  83. {
  84. subContentsList->removeChangeListener (this);
  85. subContentsList.clear();
  86. }
  87. }
  88. void setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList)
  89. {
  90. removeSubContentsList();
  91. OptionalScopedPointer<DirectoryContentsList> newPointer (newList, canDeleteList);
  92. subContentsList = newPointer;
  93. newList->addChangeListener (this);
  94. }
  95. bool selectFile (const File& target)
  96. {
  97. if (file == target)
  98. {
  99. setSelected (true, true);
  100. return true;
  101. }
  102. if (target.isAChildOf (file))
  103. {
  104. setOpen (true);
  105. for (int maxRetries = 500; --maxRetries > 0;)
  106. {
  107. for (int i = 0; i < getNumSubItems(); ++i)
  108. if (FileListTreeItem* f = dynamic_cast <FileListTreeItem*> (getSubItem (i)))
  109. if (f->selectFile (target))
  110. return true;
  111. // if we've just opened and the contents are still loading, wait for it..
  112. if (subContentsList != nullptr && subContentsList->isStillLoading())
  113. {
  114. Thread::sleep (10);
  115. rebuildItemsFromContentList();
  116. }
  117. else
  118. {
  119. break;
  120. }
  121. }
  122. }
  123. return false;
  124. }
  125. void changeListenerCallback (ChangeBroadcaster*) override
  126. {
  127. rebuildItemsFromContentList();
  128. }
  129. void rebuildItemsFromContentList()
  130. {
  131. clearSubItems();
  132. if (isOpen() && subContentsList != nullptr)
  133. {
  134. for (int i = 0; i < subContentsList->getNumFiles(); ++i)
  135. addSubItem (new FileListTreeItem (owner, subContentsList, i,
  136. subContentsList->getFile(i), thread));
  137. }
  138. }
  139. void paintItem (Graphics& g, int width, int height) override
  140. {
  141. if (file != File::nonexistent)
  142. {
  143. updateIcon (true);
  144. if (icon.isNull())
  145. thread.addTimeSliceClient (this);
  146. }
  147. owner.getLookAndFeel().drawFileBrowserRow (g, width, height,
  148. file.getFileName(),
  149. &icon, fileSize, modTime,
  150. isDirectory, isSelected(),
  151. indexInContentsList, owner);
  152. }
  153. void itemClicked (const MouseEvent& e) override
  154. {
  155. owner.sendMouseClickMessage (file, e);
  156. }
  157. void itemDoubleClicked (const MouseEvent& e) override
  158. {
  159. TreeViewItem::itemDoubleClicked (e);
  160. owner.sendDoubleClickMessage (file);
  161. }
  162. void itemSelectionChanged (bool) override
  163. {
  164. owner.sendSelectionChangeMessage();
  165. }
  166. int useTimeSlice() override
  167. {
  168. updateIcon (false);
  169. return -1;
  170. }
  171. void handleAsyncUpdate() override
  172. {
  173. owner.repaint();
  174. }
  175. const File file;
  176. private:
  177. FileTreeComponent& owner;
  178. DirectoryContentsList* parentContentsList;
  179. int indexInContentsList;
  180. OptionalScopedPointer<DirectoryContentsList> subContentsList;
  181. bool isDirectory;
  182. TimeSliceThread& thread;
  183. Image icon;
  184. String fileSize, modTime;
  185. void updateIcon (const bool onlyUpdateIfCached)
  186. {
  187. if (icon.isNull())
  188. {
  189. const int hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
  190. Image im (ImageCache::getFromHashCode (hashCode));
  191. if (im.isNull() && ! onlyUpdateIfCached)
  192. {
  193. im = juce_createIconForFile (file);
  194. if (im.isValid())
  195. ImageCache::addImageToCache (im, hashCode);
  196. }
  197. if (im.isValid())
  198. {
  199. icon = im;
  200. triggerAsyncUpdate();
  201. }
  202. }
  203. }
  204. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem)
  205. };
  206. //==============================================================================
  207. FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
  208. : DirectoryContentsDisplayComponent (listToShow)
  209. {
  210. setRootItemVisible (false);
  211. refresh();
  212. }
  213. FileTreeComponent::~FileTreeComponent()
  214. {
  215. deleteRootItem();
  216. }
  217. void FileTreeComponent::refresh()
  218. {
  219. deleteRootItem();
  220. FileListTreeItem* const root
  221. = new FileListTreeItem (*this, nullptr, 0, fileList.getDirectory(),
  222. fileList.getTimeSliceThread());
  223. root->setSubContentsList (&fileList, false);
  224. setRootItem (root);
  225. }
  226. //==============================================================================
  227. File FileTreeComponent::getSelectedFile (const int index) const
  228. {
  229. if (const FileListTreeItem* const item = dynamic_cast <const FileListTreeItem*> (getSelectedItem (index)))
  230. return item->file;
  231. return File::nonexistent;
  232. }
  233. void FileTreeComponent::deselectAllFiles()
  234. {
  235. clearSelectedItems();
  236. }
  237. void FileTreeComponent::scrollToTop()
  238. {
  239. getViewport()->getVerticalScrollBar()->setCurrentRangeStart (0);
  240. }
  241. void FileTreeComponent::setDragAndDropDescription (const String& description)
  242. {
  243. dragAndDropDescription = description;
  244. }
  245. void FileTreeComponent::setSelectedFile (const File& target)
  246. {
  247. if (FileListTreeItem* t = dynamic_cast <FileListTreeItem*> (getRootItem()))
  248. if (! t->selectFile (target))
  249. clearSelectedItems();
  250. }