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
7.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. Image juce_createIconForFile (const File& file);
  21. //==============================================================================
  22. FileListComponent::FileListComponent (DirectoryContentsList& listToShow)
  23. : ListBox ({}, nullptr),
  24. DirectoryContentsDisplayComponent (listToShow),
  25. lastDirectory (listToShow.getDirectory())
  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. fileWaitingToBeSelected = File();
  57. selectRow (i);
  58. return;
  59. }
  60. }
  61. deselectAllRows();
  62. fileWaitingToBeSelected = f;
  63. }
  64. //==============================================================================
  65. void FileListComponent::changeListenerCallback (ChangeBroadcaster*)
  66. {
  67. updateContent();
  68. if (lastDirectory != directoryContentsList.getDirectory())
  69. {
  70. fileWaitingToBeSelected = File();
  71. lastDirectory = directoryContentsList.getDirectory();
  72. deselectAllRows();
  73. }
  74. if (fileWaitingToBeSelected != File())
  75. setSelectedFile (fileWaitingToBeSelected);
  76. }
  77. //==============================================================================
  78. class FileListComponent::ItemComponent : public Component,
  79. private TimeSliceClient,
  80. private AsyncUpdater
  81. {
  82. public:
  83. ItemComponent (FileListComponent& fc, TimeSliceThread& t)
  84. : owner (fc), thread (t)
  85. {
  86. }
  87. ~ItemComponent() override
  88. {
  89. thread.removeTimeSliceClient (this);
  90. }
  91. //==============================================================================
  92. void paint (Graphics& g) override
  93. {
  94. getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(),
  95. file, file.getFileName(),
  96. &icon, fileSize, modTime,
  97. isDirectory, highlighted,
  98. index, owner);
  99. }
  100. void mouseDown (const MouseEvent& e) override
  101. {
  102. owner.selectRowsBasedOnModifierKeys (index, e.mods, true);
  103. owner.sendMouseClickMessage (file, e);
  104. }
  105. void mouseDoubleClick (const MouseEvent&) override
  106. {
  107. owner.sendDoubleClickMessage (file);
  108. }
  109. void update (const File& root, const DirectoryContentsList::FileInfo* fileInfo,
  110. int newIndex, bool nowHighlighted)
  111. {
  112. thread.removeTimeSliceClient (this);
  113. if (nowHighlighted != highlighted || newIndex != index)
  114. {
  115. index = newIndex;
  116. highlighted = nowHighlighted;
  117. repaint();
  118. }
  119. File newFile;
  120. String newFileSize, newModTime;
  121. if (fileInfo != nullptr)
  122. {
  123. newFile = root.getChildFile (fileInfo->filename);
  124. newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize);
  125. newModTime = fileInfo->modificationTime.formatted ("%d %b '%y %H:%M");
  126. }
  127. if (newFile != file
  128. || fileSize != newFileSize
  129. || modTime != newModTime)
  130. {
  131. file = newFile;
  132. fileSize = newFileSize;
  133. modTime = newModTime;
  134. icon = Image();
  135. isDirectory = fileInfo != nullptr && fileInfo->isDirectory;
  136. repaint();
  137. }
  138. if (file != File() && icon.isNull() && ! isDirectory)
  139. {
  140. updateIcon (true);
  141. if (! icon.isValid())
  142. thread.addTimeSliceClient (this);
  143. }
  144. }
  145. int useTimeSlice() override
  146. {
  147. updateIcon (false);
  148. return -1;
  149. }
  150. void handleAsyncUpdate() override
  151. {
  152. repaint();
  153. }
  154. private:
  155. //==============================================================================
  156. FileListComponent& owner;
  157. TimeSliceThread& thread;
  158. File file;
  159. String fileSize, modTime;
  160. Image icon;
  161. int index = 0;
  162. bool highlighted = false, isDirectory = false;
  163. void updateIcon (const bool onlyUpdateIfCached)
  164. {
  165. if (icon.isNull())
  166. {
  167. auto hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
  168. auto im = ImageCache::getFromHashCode (hashCode);
  169. if (im.isNull() && ! onlyUpdateIfCached)
  170. {
  171. im = juce_createIconForFile (file);
  172. if (im.isValid())
  173. ImageCache::addImageToCache (im, hashCode);
  174. }
  175. if (im.isValid())
  176. {
  177. icon = im;
  178. triggerAsyncUpdate();
  179. }
  180. }
  181. }
  182. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent)
  183. };
  184. //==============================================================================
  185. int FileListComponent::getNumRows()
  186. {
  187. return directoryContentsList.getNumFiles();
  188. }
  189. void FileListComponent::paintListBoxItem (int, Graphics&, int, int, bool)
  190. {
  191. }
  192. Component* FileListComponent::refreshComponentForRow (int row, bool isSelected, Component* existingComponentToUpdate)
  193. {
  194. jassert (existingComponentToUpdate == nullptr || dynamic_cast<ItemComponent*> (existingComponentToUpdate) != nullptr);
  195. auto comp = static_cast<ItemComponent*> (existingComponentToUpdate);
  196. if (comp == nullptr)
  197. comp = new ItemComponent (*this, directoryContentsList.getTimeSliceThread());
  198. DirectoryContentsList::FileInfo fileInfo;
  199. comp->update (directoryContentsList.getDirectory(),
  200. directoryContentsList.getFileInfo (row, fileInfo) ? &fileInfo : nullptr,
  201. row, isSelected);
  202. return comp;
  203. }
  204. void FileListComponent::selectedRowsChanged (int /*lastRowSelected*/)
  205. {
  206. sendSelectionChangeMessage();
  207. }
  208. void FileListComponent::deleteKeyPressed (int /*currentSelectedRow*/)
  209. {
  210. }
  211. void FileListComponent::returnKeyPressed (int currentSelectedRow)
  212. {
  213. sendDoubleClickMessage (directoryContentsList.getFile (currentSelectedRow));
  214. }
  215. } // namespace juce