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