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.

258 lines
7.3KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. Image juce_createIconForFile (const File& file);
  18. //==============================================================================
  19. FileListComponent::FileListComponent (DirectoryContentsList& listToShow)
  20. : ListBox (String(), nullptr),
  21. DirectoryContentsDisplayComponent (listToShow)
  22. {
  23. setModel (this);
  24. fileList.addChangeListener (this);
  25. }
  26. FileListComponent::~FileListComponent()
  27. {
  28. fileList.removeChangeListener (this);
  29. }
  30. int FileListComponent::getNumSelectedFiles() const
  31. {
  32. return getNumSelectedRows();
  33. }
  34. File FileListComponent::getSelectedFile (int index) const
  35. {
  36. return fileList.getFile (getSelectedRow (index));
  37. }
  38. void FileListComponent::deselectAllFiles()
  39. {
  40. deselectAllRows();
  41. }
  42. void FileListComponent::scrollToTop()
  43. {
  44. getVerticalScrollBar()->setCurrentRangeStart (0);
  45. }
  46. void FileListComponent::setSelectedFile (const File& f)
  47. {
  48. for (int i = fileList.getNumFiles(); --i >= 0;)
  49. {
  50. if (fileList.getFile(i) == f)
  51. {
  52. selectRow (i);
  53. return;
  54. }
  55. }
  56. deselectAllRows();
  57. }
  58. //==============================================================================
  59. void FileListComponent::changeListenerCallback (ChangeBroadcaster*)
  60. {
  61. updateContent();
  62. if (lastDirectory != fileList.getDirectory())
  63. {
  64. lastDirectory = fileList.getDirectory();
  65. deselectAllRows();
  66. }
  67. }
  68. //==============================================================================
  69. class FileListComponent::ItemComponent : public Component,
  70. private TimeSliceClient,
  71. private AsyncUpdater
  72. {
  73. public:
  74. ItemComponent (FileListComponent& fc, TimeSliceThread& t)
  75. : owner (fc), thread (t), index (0), highlighted (false)
  76. {
  77. }
  78. ~ItemComponent()
  79. {
  80. thread.removeTimeSliceClient (this);
  81. }
  82. //==============================================================================
  83. void paint (Graphics& g) override
  84. {
  85. getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(),
  86. file.getFileName(),
  87. &icon, fileSize, modTime,
  88. isDirectory, highlighted,
  89. index, owner);
  90. }
  91. void mouseDown (const MouseEvent& e) override
  92. {
  93. owner.selectRowsBasedOnModifierKeys (index, e.mods, true);
  94. owner.sendMouseClickMessage (file, e);
  95. }
  96. void mouseDoubleClick (const MouseEvent&) override
  97. {
  98. owner.sendDoubleClickMessage (file);
  99. }
  100. void update (const File& root,
  101. const DirectoryContentsList::FileInfo* const fileInfo,
  102. const int index_,
  103. const bool highlighted_)
  104. {
  105. thread.removeTimeSliceClient (this);
  106. if (highlighted_ != highlighted || index_ != index)
  107. {
  108. index = index_;
  109. highlighted = highlighted_;
  110. repaint();
  111. }
  112. File newFile;
  113. String newFileSize, newModTime;
  114. if (fileInfo != nullptr)
  115. {
  116. newFile = root.getChildFile (fileInfo->filename);
  117. newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize);
  118. newModTime = fileInfo->modificationTime.formatted ("%d %b '%y %H:%M");
  119. }
  120. if (newFile != file
  121. || fileSize != newFileSize
  122. || modTime != newModTime)
  123. {
  124. file = newFile;
  125. fileSize = newFileSize;
  126. modTime = newModTime;
  127. icon = Image();
  128. isDirectory = fileInfo != nullptr && fileInfo->isDirectory;
  129. repaint();
  130. }
  131. if (file != File() && icon.isNull() && ! isDirectory)
  132. {
  133. updateIcon (true);
  134. if (! icon.isValid())
  135. thread.addTimeSliceClient (this);
  136. }
  137. }
  138. int useTimeSlice() override
  139. {
  140. updateIcon (false);
  141. return -1;
  142. }
  143. void handleAsyncUpdate() override
  144. {
  145. repaint();
  146. }
  147. private:
  148. //==============================================================================
  149. FileListComponent& owner;
  150. TimeSliceThread& thread;
  151. File file;
  152. String fileSize, modTime;
  153. Image icon;
  154. int index;
  155. bool highlighted, isDirectory;
  156. void updateIcon (const bool onlyUpdateIfCached)
  157. {
  158. if (icon.isNull())
  159. {
  160. const int hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
  161. Image im (ImageCache::getFromHashCode (hashCode));
  162. if (im.isNull() && ! onlyUpdateIfCached)
  163. {
  164. im = juce_createIconForFile (file);
  165. if (im.isValid())
  166. ImageCache::addImageToCache (im, hashCode);
  167. }
  168. if (im.isValid())
  169. {
  170. icon = im;
  171. triggerAsyncUpdate();
  172. }
  173. }
  174. }
  175. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent)
  176. };
  177. //==============================================================================
  178. int FileListComponent::getNumRows()
  179. {
  180. return fileList.getNumFiles();
  181. }
  182. void FileListComponent::paintListBoxItem (int, Graphics&, int, int, bool)
  183. {
  184. }
  185. Component* FileListComponent::refreshComponentForRow (int row, bool isSelected, Component* existingComponentToUpdate)
  186. {
  187. jassert (existingComponentToUpdate == nullptr || dynamic_cast<ItemComponent*> (existingComponentToUpdate) != nullptr);
  188. ItemComponent* comp = static_cast<ItemComponent*> (existingComponentToUpdate);
  189. if (comp == nullptr)
  190. comp = new ItemComponent (*this, fileList.getTimeSliceThread());
  191. DirectoryContentsList::FileInfo fileInfo;
  192. comp->update (fileList.getDirectory(),
  193. fileList.getFileInfo (row, fileInfo) ? &fileInfo : nullptr,
  194. row, isSelected);
  195. return comp;
  196. }
  197. void FileListComponent::selectedRowsChanged (int /*lastRowSelected*/)
  198. {
  199. sendSelectionChangeMessage();
  200. }
  201. void FileListComponent::deleteKeyPressed (int /*currentSelectedRow*/)
  202. {
  203. }
  204. void FileListComponent::returnKeyPressed (int currentSelectedRow)
  205. {
  206. sendDoubleClickMessage (fileList.getFile (currentSelectedRow));
  207. }