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.

282 lines
8.5KB

  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 i = 0; i < getNumSubItems(); ++i)
  97. if (FileListTreeItem* f = dynamic_cast <FileListTreeItem*> (getSubItem (i)))
  98. if (f->selectFile (target))
  99. return true;
  100. }
  101. return false;
  102. }
  103. void changeListenerCallback (ChangeBroadcaster*)
  104. {
  105. clearSubItems();
  106. if (isOpen() && subContentsList != nullptr)
  107. {
  108. for (int i = 0; i < subContentsList->getNumFiles(); ++i)
  109. {
  110. FileListTreeItem* const item
  111. = new FileListTreeItem (owner, subContentsList, i, subContentsList->getFile(i), thread);
  112. addSubItem (item);
  113. }
  114. }
  115. }
  116. void paintItem (Graphics& g, int width, int height)
  117. {
  118. if (file != File::nonexistent)
  119. {
  120. updateIcon (true);
  121. if (icon.isNull())
  122. thread.addTimeSliceClient (this);
  123. }
  124. owner.getLookAndFeel().drawFileBrowserRow (g, width, height,
  125. file.getFileName(),
  126. &icon, fileSize, modTime,
  127. isDirectory, isSelected(),
  128. indexInContentsList, owner);
  129. }
  130. void itemClicked (const MouseEvent& e)
  131. {
  132. owner.sendMouseClickMessage (file, e);
  133. }
  134. void itemDoubleClicked (const MouseEvent& e)
  135. {
  136. TreeViewItem::itemDoubleClicked (e);
  137. owner.sendDoubleClickMessage (file);
  138. }
  139. void itemSelectionChanged (bool)
  140. {
  141. owner.sendSelectionChangeMessage();
  142. }
  143. int useTimeSlice()
  144. {
  145. updateIcon (false);
  146. return -1;
  147. }
  148. void handleAsyncUpdate()
  149. {
  150. owner.repaint();
  151. }
  152. const File file;
  153. private:
  154. FileTreeComponent& owner;
  155. DirectoryContentsList* parentContentsList;
  156. int indexInContentsList;
  157. OptionalScopedPointer<DirectoryContentsList> subContentsList;
  158. bool isDirectory;
  159. TimeSliceThread& thread;
  160. Image icon;
  161. String fileSize, modTime;
  162. void updateIcon (const bool onlyUpdateIfCached)
  163. {
  164. if (icon.isNull())
  165. {
  166. const int hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
  167. Image im (ImageCache::getFromHashCode (hashCode));
  168. if (im.isNull() && ! onlyUpdateIfCached)
  169. {
  170. im = juce_createIconForFile (file);
  171. if (im.isValid())
  172. ImageCache::addImageToCache (im, hashCode);
  173. }
  174. if (im.isValid())
  175. {
  176. icon = im;
  177. triggerAsyncUpdate();
  178. }
  179. }
  180. }
  181. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem)
  182. };
  183. //==============================================================================
  184. FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
  185. : DirectoryContentsDisplayComponent (listToShow)
  186. {
  187. setRootItemVisible (false);
  188. refresh();
  189. }
  190. FileTreeComponent::~FileTreeComponent()
  191. {
  192. deleteRootItem();
  193. }
  194. void FileTreeComponent::refresh()
  195. {
  196. deleteRootItem();
  197. FileListTreeItem* const root
  198. = new FileListTreeItem (*this, nullptr, 0, fileList.getDirectory(),
  199. fileList.getTimeSliceThread());
  200. root->setSubContentsList (&fileList, false);
  201. setRootItem (root);
  202. }
  203. //==============================================================================
  204. File FileTreeComponent::getSelectedFile (const int index) const
  205. {
  206. if (const FileListTreeItem* const item = dynamic_cast <const FileListTreeItem*> (getSelectedItem (index)))
  207. return item->file;
  208. return File::nonexistent;
  209. }
  210. void FileTreeComponent::deselectAllFiles()
  211. {
  212. clearSelectedItems();
  213. }
  214. void FileTreeComponent::scrollToTop()
  215. {
  216. getViewport()->getVerticalScrollBar()->setCurrentRangeStart (0);
  217. }
  218. void FileTreeComponent::setDragAndDropDescription (const String& description)
  219. {
  220. dragAndDropDescription = description;
  221. }
  222. void FileTreeComponent::setSelectedFile (const File& target)
  223. {
  224. if (FileListTreeItem* t = dynamic_cast <FileListTreeItem*> (getRootItem()))
  225. if (! t->selectFile (target))
  226. clearSelectedItems();
  227. }