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.

254 lines
7.8KB

  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. BEGIN_JUCE_NAMESPACE
  19. Image juce_createIconForFile (const File& file);
  20. //==============================================================================
  21. class FileListTreeItem : public TreeViewItem,
  22. public TimeSliceClient,
  23. public AsyncUpdater,
  24. public ChangeListener
  25. {
  26. public:
  27. //==============================================================================
  28. FileListTreeItem (FileTreeComponent& owner_,
  29. DirectoryContentsList* const parentContentsList_,
  30. const int indexInContentsList_,
  31. const File& file_,
  32. TimeSliceThread& thread_)
  33. : file (file_),
  34. owner (owner_),
  35. parentContentsList (parentContentsList_),
  36. indexInContentsList (indexInContentsList_),
  37. subContentsList (nullptr, false),
  38. thread (thread_)
  39. {
  40. DirectoryContentsList::FileInfo fileInfo;
  41. if (parentContentsList_ != nullptr
  42. && parentContentsList_->getFileInfo (indexInContentsList_, fileInfo))
  43. {
  44. fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize);
  45. modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M");
  46. isDirectory = fileInfo.isDirectory;
  47. }
  48. else
  49. {
  50. isDirectory = true;
  51. }
  52. }
  53. ~FileListTreeItem()
  54. {
  55. thread.removeTimeSliceClient (this);
  56. clearSubItems();
  57. }
  58. //==============================================================================
  59. bool mightContainSubItems() { return isDirectory; }
  60. const String getUniqueName() const { return file.getFullPathName(); }
  61. int getItemHeight() const { return 22; }
  62. const var getDragSourceDescription() { return owner.getDragAndDropDescription(); }
  63. void itemOpennessChanged (bool isNowOpen)
  64. {
  65. if (isNowOpen)
  66. {
  67. clearSubItems();
  68. isDirectory = file.isDirectory();
  69. if (isDirectory)
  70. {
  71. if (subContentsList == nullptr)
  72. {
  73. jassert (parentContentsList != nullptr);
  74. DirectoryContentsList* const l = new DirectoryContentsList (parentContentsList->getFilter(), thread);
  75. l->setDirectory (file, true, true);
  76. setSubContentsList (l, true);
  77. }
  78. changeListenerCallback (nullptr);
  79. }
  80. }
  81. }
  82. void setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList)
  83. {
  84. OptionalScopedPointer<DirectoryContentsList> newPointer (newList, canDeleteList);
  85. subContentsList = newPointer;
  86. newList->addChangeListener (this);
  87. }
  88. void changeListenerCallback (ChangeBroadcaster*)
  89. {
  90. clearSubItems();
  91. if (isOpen() && subContentsList != nullptr)
  92. {
  93. for (int i = 0; i < subContentsList->getNumFiles(); ++i)
  94. {
  95. FileListTreeItem* const item
  96. = new FileListTreeItem (owner, subContentsList, i, subContentsList->getFile(i), thread);
  97. addSubItem (item);
  98. }
  99. }
  100. }
  101. void paintItem (Graphics& g, int width, int height)
  102. {
  103. if (file != File::nonexistent)
  104. {
  105. updateIcon (true);
  106. if (icon.isNull())
  107. thread.addTimeSliceClient (this);
  108. }
  109. owner.getLookAndFeel()
  110. .drawFileBrowserRow (g, width, height,
  111. file.getFileName(),
  112. &icon, fileSize, modTime,
  113. isDirectory, isSelected(),
  114. indexInContentsList, owner);
  115. }
  116. void itemClicked (const MouseEvent& e)
  117. {
  118. owner.sendMouseClickMessage (file, e);
  119. }
  120. void itemDoubleClicked (const MouseEvent& e)
  121. {
  122. TreeViewItem::itemDoubleClicked (e);
  123. owner.sendDoubleClickMessage (file);
  124. }
  125. void itemSelectionChanged (bool)
  126. {
  127. owner.sendSelectionChangeMessage();
  128. }
  129. int useTimeSlice()
  130. {
  131. updateIcon (false);
  132. return -1;
  133. }
  134. void handleAsyncUpdate()
  135. {
  136. owner.repaint();
  137. }
  138. const File file;
  139. private:
  140. FileTreeComponent& owner;
  141. DirectoryContentsList* parentContentsList;
  142. int indexInContentsList;
  143. OptionalScopedPointer<DirectoryContentsList> subContentsList;
  144. bool isDirectory;
  145. TimeSliceThread& thread;
  146. Image icon;
  147. String fileSize;
  148. String modTime;
  149. void updateIcon (const bool onlyUpdateIfCached)
  150. {
  151. if (icon.isNull())
  152. {
  153. const int hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
  154. Image im (ImageCache::getFromHashCode (hashCode));
  155. if (im.isNull() && ! onlyUpdateIfCached)
  156. {
  157. im = juce_createIconForFile (file);
  158. if (im.isValid())
  159. ImageCache::addImageToCache (im, hashCode);
  160. }
  161. if (im.isValid())
  162. {
  163. icon = im;
  164. triggerAsyncUpdate();
  165. }
  166. }
  167. }
  168. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem);
  169. };
  170. //==============================================================================
  171. FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
  172. : DirectoryContentsDisplayComponent (listToShow)
  173. {
  174. FileListTreeItem* const root
  175. = new FileListTreeItem (*this, 0, 0, listToShow.getDirectory(),
  176. listToShow.getTimeSliceThread());
  177. root->setSubContentsList (&listToShow, false);
  178. setRootItemVisible (false);
  179. setRootItem (root);
  180. }
  181. FileTreeComponent::~FileTreeComponent()
  182. {
  183. deleteRootItem();
  184. }
  185. //==============================================================================
  186. File FileTreeComponent::getSelectedFile (const int index) const
  187. {
  188. const FileListTreeItem* const item = dynamic_cast <const FileListTreeItem*> (getSelectedItem (index));
  189. return item != nullptr ? item->file
  190. : File::nonexistent;
  191. }
  192. void FileTreeComponent::deselectAllFiles()
  193. {
  194. clearSelectedItems();
  195. }
  196. void FileTreeComponent::scrollToTop()
  197. {
  198. getViewport()->getVerticalScrollBar()->setCurrentRangeStart (0);
  199. }
  200. void FileTreeComponent::setDragAndDropDescription (const String& description)
  201. {
  202. dragAndDropDescription = description;
  203. }
  204. END_JUCE_NAMESPACE