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.

259 lines
7.5KB

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