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.

263 lines
7.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. Image juce_createIconForFile (const File& file);
  22. //==============================================================================
  23. FileListComponent::FileListComponent (DirectoryContentsList& listToShow)
  24. : ListBox ({}, nullptr),
  25. DirectoryContentsDisplayComponent (listToShow)
  26. {
  27. setModel (this);
  28. directoryContentsList.addChangeListener (this);
  29. }
  30. FileListComponent::~FileListComponent()
  31. {
  32. directoryContentsList.removeChangeListener (this);
  33. }
  34. int FileListComponent::getNumSelectedFiles() const
  35. {
  36. return getNumSelectedRows();
  37. }
  38. File FileListComponent::getSelectedFile (int index) const
  39. {
  40. return directoryContentsList.getFile (getSelectedRow (index));
  41. }
  42. void FileListComponent::deselectAllFiles()
  43. {
  44. deselectAllRows();
  45. }
  46. void FileListComponent::scrollToTop()
  47. {
  48. getVerticalScrollBar().setCurrentRangeStart (0);
  49. }
  50. void FileListComponent::setSelectedFile (const File& f)
  51. {
  52. for (int i = directoryContentsList.getNumFiles(); --i >= 0;)
  53. {
  54. if (directoryContentsList.getFile(i) == f)
  55. {
  56. selectRow (i);
  57. return;
  58. }
  59. }
  60. deselectAllRows();
  61. }
  62. //==============================================================================
  63. void FileListComponent::changeListenerCallback (ChangeBroadcaster*)
  64. {
  65. updateContent();
  66. if (lastDirectory != directoryContentsList.getDirectory())
  67. {
  68. lastDirectory = directoryContentsList.getDirectory();
  69. deselectAllRows();
  70. }
  71. }
  72. //==============================================================================
  73. class FileListComponent::ItemComponent : public Component,
  74. private TimeSliceClient,
  75. private AsyncUpdater
  76. {
  77. public:
  78. ItemComponent (FileListComponent& fc, TimeSliceThread& t)
  79. : owner (fc), thread (t)
  80. {
  81. }
  82. ~ItemComponent()
  83. {
  84. thread.removeTimeSliceClient (this);
  85. }
  86. //==============================================================================
  87. void paint (Graphics& g) override
  88. {
  89. getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(),
  90. file, file.getFileName(),
  91. &icon, fileSize, modTime,
  92. isDirectory, highlighted,
  93. index, owner);
  94. }
  95. void mouseDown (const MouseEvent& e) override
  96. {
  97. owner.selectRowsBasedOnModifierKeys (index, e.mods, true);
  98. owner.sendMouseClickMessage (file, e);
  99. }
  100. void mouseDoubleClick (const MouseEvent&) override
  101. {
  102. owner.sendDoubleClickMessage (file);
  103. }
  104. void update (const File& root, const DirectoryContentsList::FileInfo* fileInfo,
  105. int newIndex, bool nowHighlighted)
  106. {
  107. thread.removeTimeSliceClient (this);
  108. if (nowHighlighted != highlighted || newIndex != index)
  109. {
  110. index = newIndex;
  111. highlighted = nowHighlighted;
  112. repaint();
  113. }
  114. File newFile;
  115. String newFileSize, newModTime;
  116. if (fileInfo != nullptr)
  117. {
  118. newFile = root.getChildFile (fileInfo->filename);
  119. newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize);
  120. newModTime = fileInfo->modificationTime.formatted ("%d %b '%y %H:%M");
  121. }
  122. if (newFile != file
  123. || fileSize != newFileSize
  124. || modTime != newModTime)
  125. {
  126. file = newFile;
  127. fileSize = newFileSize;
  128. modTime = newModTime;
  129. icon = Image();
  130. isDirectory = fileInfo != nullptr && fileInfo->isDirectory;
  131. repaint();
  132. }
  133. if (file != File() && icon.isNull() && ! isDirectory)
  134. {
  135. updateIcon (true);
  136. if (! icon.isValid())
  137. thread.addTimeSliceClient (this);
  138. }
  139. }
  140. int useTimeSlice() override
  141. {
  142. updateIcon (false);
  143. return -1;
  144. }
  145. void handleAsyncUpdate() override
  146. {
  147. repaint();
  148. }
  149. private:
  150. //==============================================================================
  151. FileListComponent& owner;
  152. TimeSliceThread& thread;
  153. File file;
  154. String fileSize, modTime;
  155. Image icon;
  156. int index = 0;
  157. bool highlighted = false, isDirectory = false;
  158. void updateIcon (const bool onlyUpdateIfCached)
  159. {
  160. if (icon.isNull())
  161. {
  162. auto hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
  163. auto 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 (ItemComponent)
  178. };
  179. //==============================================================================
  180. int FileListComponent::getNumRows()
  181. {
  182. return directoryContentsList.getNumFiles();
  183. }
  184. void FileListComponent::paintListBoxItem (int, Graphics&, int, int, bool)
  185. {
  186. }
  187. Component* FileListComponent::refreshComponentForRow (int row, bool isSelected, Component* existingComponentToUpdate)
  188. {
  189. jassert (existingComponentToUpdate == nullptr || dynamic_cast<ItemComponent*> (existingComponentToUpdate) != nullptr);
  190. auto comp = static_cast<ItemComponent*> (existingComponentToUpdate);
  191. if (comp == nullptr)
  192. comp = new ItemComponent (*this, directoryContentsList.getTimeSliceThread());
  193. DirectoryContentsList::FileInfo fileInfo;
  194. comp->update (directoryContentsList.getDirectory(),
  195. directoryContentsList.getFileInfo (row, fileInfo) ? &fileInfo : nullptr,
  196. row, isSelected);
  197. return comp;
  198. }
  199. void FileListComponent::selectedRowsChanged (int /*lastRowSelected*/)
  200. {
  201. sendSelectionChangeMessage();
  202. }
  203. void FileListComponent::deleteKeyPressed (int /*currentSelectedRow*/)
  204. {
  205. }
  206. void FileListComponent::returnKeyPressed (int currentSelectedRow)
  207. {
  208. sendDoubleClickMessage (directoryContentsList.getFile (currentSelectedRow));
  209. }
  210. } // namespace juce