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.

270 lines
8.2KB

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