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.

297 lines
9.0KB

  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& owner_,
  27. DirectoryContentsList* const parentContentsList_,
  28. const int indexInContentsList_,
  29. const File& file_,
  30. TimeSliceThread& thread_)
  31. : file (file_),
  32. owner (owner_),
  33. parentContentsList (parentContentsList_),
  34. indexInContentsList (indexInContentsList_),
  35. subContentsList (nullptr, false),
  36. thread (thread_)
  37. {
  38. DirectoryContentsList::FileInfo fileInfo;
  39. if (parentContentsList_ != nullptr
  40. && parentContentsList_->getFileInfo (indexInContentsList_, 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. }
  56. //==============================================================================
  57. bool mightContainSubItems() { return isDirectory; }
  58. String getUniqueName() const { return file.getFullPathName(); }
  59. int getItemHeight() const { return 22; }
  60. var getDragSourceDescription() { return owner.getDragAndDropDescription(); }
  61. void itemOpennessChanged (bool isNowOpen)
  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 setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList)
  81. {
  82. OptionalScopedPointer<DirectoryContentsList> newPointer (newList, canDeleteList);
  83. subContentsList = newPointer;
  84. newList->addChangeListener (this);
  85. }
  86. bool selectFile (const File& target)
  87. {
  88. if (file == target)
  89. {
  90. setSelected (true, true);
  91. return true;
  92. }
  93. if (target.isAChildOf (file))
  94. {
  95. setOpen (true);
  96. for (int maxRetries = 500; --maxRetries > 0;)
  97. {
  98. for (int i = 0; i < getNumSubItems(); ++i)
  99. if (FileListTreeItem* f = dynamic_cast <FileListTreeItem*> (getSubItem (i)))
  100. if (f->selectFile (target))
  101. return true;
  102. // if we've just opened and the contents are still loading, wait for it..
  103. if (subContentsList != nullptr && subContentsList->isStillLoading())
  104. {
  105. Thread::sleep (10);
  106. rebuildItemsFromContentList();
  107. }
  108. else
  109. {
  110. break;
  111. }
  112. }
  113. }
  114. return false;
  115. }
  116. void changeListenerCallback (ChangeBroadcaster*)
  117. {
  118. rebuildItemsFromContentList();
  119. }
  120. void rebuildItemsFromContentList()
  121. {
  122. clearSubItems();
  123. if (isOpen() && subContentsList != nullptr)
  124. {
  125. for (int i = 0; i < subContentsList->getNumFiles(); ++i)
  126. addSubItem (new FileListTreeItem (owner, subContentsList, i,
  127. subContentsList->getFile(i), thread));
  128. }
  129. }
  130. void paintItem (Graphics& g, int width, int height)
  131. {
  132. if (file != File::nonexistent)
  133. {
  134. updateIcon (true);
  135. if (icon.isNull())
  136. thread.addTimeSliceClient (this);
  137. }
  138. owner.getLookAndFeel().drawFileBrowserRow (g, width, height,
  139. file.getFileName(),
  140. &icon, fileSize, modTime,
  141. isDirectory, isSelected(),
  142. indexInContentsList, owner);
  143. }
  144. void itemClicked (const MouseEvent& e)
  145. {
  146. owner.sendMouseClickMessage (file, e);
  147. }
  148. void itemDoubleClicked (const MouseEvent& e)
  149. {
  150. TreeViewItem::itemDoubleClicked (e);
  151. owner.sendDoubleClickMessage (file);
  152. }
  153. void itemSelectionChanged (bool)
  154. {
  155. owner.sendSelectionChangeMessage();
  156. }
  157. int useTimeSlice()
  158. {
  159. updateIcon (false);
  160. return -1;
  161. }
  162. void handleAsyncUpdate()
  163. {
  164. owner.repaint();
  165. }
  166. const File file;
  167. private:
  168. FileTreeComponent& owner;
  169. DirectoryContentsList* parentContentsList;
  170. int indexInContentsList;
  171. OptionalScopedPointer<DirectoryContentsList> subContentsList;
  172. bool isDirectory;
  173. TimeSliceThread& thread;
  174. Image icon;
  175. String fileSize, modTime;
  176. void updateIcon (const bool onlyUpdateIfCached)
  177. {
  178. if (icon.isNull())
  179. {
  180. const int hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
  181. Image im (ImageCache::getFromHashCode (hashCode));
  182. if (im.isNull() && ! onlyUpdateIfCached)
  183. {
  184. im = juce_createIconForFile (file);
  185. if (im.isValid())
  186. ImageCache::addImageToCache (im, hashCode);
  187. }
  188. if (im.isValid())
  189. {
  190. icon = im;
  191. triggerAsyncUpdate();
  192. }
  193. }
  194. }
  195. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem)
  196. };
  197. //==============================================================================
  198. FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
  199. : DirectoryContentsDisplayComponent (listToShow)
  200. {
  201. setRootItemVisible (false);
  202. refresh();
  203. }
  204. FileTreeComponent::~FileTreeComponent()
  205. {
  206. deleteRootItem();
  207. }
  208. void FileTreeComponent::refresh()
  209. {
  210. deleteRootItem();
  211. FileListTreeItem* const root
  212. = new FileListTreeItem (*this, nullptr, 0, fileList.getDirectory(),
  213. fileList.getTimeSliceThread());
  214. root->setSubContentsList (&fileList, false);
  215. setRootItem (root);
  216. }
  217. //==============================================================================
  218. File FileTreeComponent::getSelectedFile (const int index) const
  219. {
  220. if (const FileListTreeItem* const item = dynamic_cast <const FileListTreeItem*> (getSelectedItem (index)))
  221. return item->file;
  222. return File::nonexistent;
  223. }
  224. void FileTreeComponent::deselectAllFiles()
  225. {
  226. clearSelectedItems();
  227. }
  228. void FileTreeComponent::scrollToTop()
  229. {
  230. getViewport()->getVerticalScrollBar()->setCurrentRangeStart (0);
  231. }
  232. void FileTreeComponent::setDragAndDropDescription (const String& description)
  233. {
  234. dragAndDropDescription = description;
  235. }
  236. void FileTreeComponent::setSelectedFile (const File& target)
  237. {
  238. if (FileListTreeItem* t = dynamic_cast <FileListTreeItem*> (getRootItem()))
  239. if (! t->selectFile (target))
  240. clearSelectedItems();
  241. }