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.

268 lines
8.1KB

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