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.

284 lines
8.0KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - 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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-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 ({}, this),
  24. DirectoryContentsDisplayComponent (listToShow),
  25. lastDirectory (listToShow.getDirectory())
  26. {
  27. setTitle ("Files");
  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. if (! directoryContentsList.isStillLoading())
  53. {
  54. for (int i = directoryContentsList.getNumFiles(); --i >= 0;)
  55. {
  56. if (directoryContentsList.getFile (i) == f)
  57. {
  58. fileWaitingToBeSelected = File();
  59. updateContent();
  60. selectRow (i);
  61. return;
  62. }
  63. }
  64. }
  65. deselectAllRows();
  66. fileWaitingToBeSelected = f;
  67. }
  68. //==============================================================================
  69. void FileListComponent::changeListenerCallback (ChangeBroadcaster*)
  70. {
  71. updateContent();
  72. if (lastDirectory != directoryContentsList.getDirectory())
  73. {
  74. fileWaitingToBeSelected = File();
  75. lastDirectory = directoryContentsList.getDirectory();
  76. deselectAllRows();
  77. }
  78. if (fileWaitingToBeSelected != File())
  79. setSelectedFile (fileWaitingToBeSelected);
  80. }
  81. //==============================================================================
  82. class FileListComponent::ItemComponent : public Component,
  83. private TimeSliceClient,
  84. private AsyncUpdater
  85. {
  86. public:
  87. ItemComponent (FileListComponent& fc, TimeSliceThread& t)
  88. : owner (fc), thread (t)
  89. {
  90. }
  91. ~ItemComponent() override
  92. {
  93. thread.removeTimeSliceClient (this);
  94. }
  95. //==============================================================================
  96. void paint (Graphics& g) override
  97. {
  98. getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(),
  99. file, file.getFileName(),
  100. &icon, fileSize, modTime,
  101. isDirectory, highlighted,
  102. index, owner);
  103. }
  104. void mouseDown (const MouseEvent& e) override
  105. {
  106. owner.selectRowsBasedOnModifierKeys (index, e.mods, true);
  107. owner.sendMouseClickMessage (file, e);
  108. }
  109. void mouseDoubleClick (const MouseEvent&) override
  110. {
  111. owner.sendDoubleClickMessage (file);
  112. }
  113. void update (const File& root, const DirectoryContentsList::FileInfo* fileInfo,
  114. int newIndex, bool nowHighlighted)
  115. {
  116. thread.removeTimeSliceClient (this);
  117. if (nowHighlighted != highlighted || newIndex != index)
  118. {
  119. index = newIndex;
  120. highlighted = nowHighlighted;
  121. repaint();
  122. }
  123. File newFile;
  124. String newFileSize, newModTime;
  125. if (fileInfo != nullptr)
  126. {
  127. newFile = root.getChildFile (fileInfo->filename);
  128. newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize);
  129. newModTime = fileInfo->modificationTime.formatted ("%d %b '%y %H:%M");
  130. }
  131. if (newFile != file
  132. || fileSize != newFileSize
  133. || modTime != newModTime)
  134. {
  135. file = newFile;
  136. fileSize = newFileSize;
  137. modTime = newModTime;
  138. icon = Image();
  139. isDirectory = fileInfo != nullptr && fileInfo->isDirectory;
  140. repaint();
  141. }
  142. if (file != File() && icon.isNull() && ! isDirectory)
  143. {
  144. updateIcon (true);
  145. if (! icon.isValid())
  146. thread.addTimeSliceClient (this);
  147. }
  148. }
  149. int useTimeSlice() override
  150. {
  151. updateIcon (false);
  152. return -1;
  153. }
  154. void handleAsyncUpdate() override
  155. {
  156. repaint();
  157. }
  158. private:
  159. //==============================================================================
  160. FileListComponent& owner;
  161. TimeSliceThread& thread;
  162. File file;
  163. String fileSize, modTime;
  164. Image icon;
  165. int index = 0;
  166. bool highlighted = false, isDirectory = false;
  167. std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
  168. {
  169. return createIgnoredAccessibilityHandler (*this);
  170. }
  171. void updateIcon (const bool onlyUpdateIfCached)
  172. {
  173. if (icon.isNull())
  174. {
  175. auto hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
  176. auto im = ImageCache::getFromHashCode (hashCode);
  177. if (im.isNull() && ! onlyUpdateIfCached)
  178. {
  179. im = juce_createIconForFile (file);
  180. if (im.isValid())
  181. ImageCache::addImageToCache (im, hashCode);
  182. }
  183. if (im.isValid())
  184. {
  185. icon = im;
  186. triggerAsyncUpdate();
  187. }
  188. }
  189. }
  190. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent)
  191. };
  192. //==============================================================================
  193. int FileListComponent::getNumRows()
  194. {
  195. return directoryContentsList.getNumFiles();
  196. }
  197. String FileListComponent::getNameForRow (int rowNumber)
  198. {
  199. return directoryContentsList.getFile (rowNumber).getFileName();
  200. }
  201. void FileListComponent::paintListBoxItem (int, Graphics&, int, int, bool)
  202. {
  203. }
  204. Component* FileListComponent::refreshComponentForRow (int row, bool isSelected, Component* existingComponentToUpdate)
  205. {
  206. jassert (existingComponentToUpdate == nullptr || dynamic_cast<ItemComponent*> (existingComponentToUpdate) != nullptr);
  207. auto comp = static_cast<ItemComponent*> (existingComponentToUpdate);
  208. if (comp == nullptr)
  209. comp = new ItemComponent (*this, directoryContentsList.getTimeSliceThread());
  210. DirectoryContentsList::FileInfo fileInfo;
  211. comp->update (directoryContentsList.getDirectory(),
  212. directoryContentsList.getFileInfo (row, fileInfo) ? &fileInfo : nullptr,
  213. row, isSelected);
  214. return comp;
  215. }
  216. void FileListComponent::selectedRowsChanged (int /*lastRowSelected*/)
  217. {
  218. sendSelectionChangeMessage();
  219. }
  220. void FileListComponent::deleteKeyPressed (int /*currentSelectedRow*/)
  221. {
  222. }
  223. void FileListComponent::returnKeyPressed (int currentSelectedRow)
  224. {
  225. sendDoubleClickMessage (directoryContentsList.getFile (currentSelectedRow));
  226. }
  227. } // namespace juce