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.

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