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.

309 lines
9.3KB

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