VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_gui_basics / filebrowser / juce_FileTreeComponent.cpp
blobf5f1e6438cb100d46c0464a37492a34004713df8
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
23 ==============================================================================
26 namespace juce
29 //==============================================================================
30 class FileListTreeItem : public TreeViewItem,
31 private TimeSliceClient,
32 private AsyncUpdater,
33 private ChangeListener
35 public:
36 FileListTreeItem (FileTreeComponent& treeComp,
37 DirectoryContentsList* parentContents,
38 int indexInContents,
39 const File& f,
40 TimeSliceThread& t)
41 : file (f),
42 owner (treeComp),
43 parentContentsList (parentContents),
44 indexInContentsList (indexInContents),
45 subContentsList (nullptr, false),
46 thread (t)
48 DirectoryContentsList::FileInfo fileInfo;
50 if (parentContents != nullptr
51 && parentContents->getFileInfo (indexInContents, fileInfo))
53 fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize);
54 modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M");
55 isDirectory = fileInfo.isDirectory;
57 else
59 isDirectory = true;
63 ~FileListTreeItem() override
65 thread.removeTimeSliceClient (this);
66 clearSubItems();
67 removeSubContentsList();
70 //==============================================================================
71 bool mightContainSubItems() override { return isDirectory; }
72 String getUniqueName() const override { return file.getFullPathName(); }
73 int getItemHeight() const override { return owner.getItemHeight(); }
75 var getDragSourceDescription() override { return owner.getDragAndDropDescription(); }
77 void itemOpennessChanged (bool isNowOpen) override
79 if (isNowOpen)
81 clearSubItems();
83 isDirectory = file.isDirectory();
85 if (isDirectory)
87 if (subContentsList == nullptr && parentContentsList != nullptr)
89 auto l = new DirectoryContentsList (parentContentsList->getFilter(), thread);
91 l->setDirectory (file,
92 parentContentsList->isFindingDirectories(),
93 parentContentsList->isFindingFiles());
95 setSubContentsList (l, true);
98 changeListenerCallback (nullptr);
103 void removeSubContentsList()
105 if (subContentsList != nullptr)
107 subContentsList->removeChangeListener (this);
108 subContentsList.reset();
112 void setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList)
114 removeSubContentsList();
116 subContentsList = OptionalScopedPointer<DirectoryContentsList> (newList, canDeleteList);
117 newList->addChangeListener (this);
120 bool selectFile (const File& target)
122 if (file == target)
124 setSelected (true, true);
125 return true;
128 if (target.isAChildOf (file))
130 setOpen (true);
132 for (int maxRetries = 500; --maxRetries > 0;)
134 for (int i = 0; i < getNumSubItems(); ++i)
135 if (auto* f = dynamic_cast<FileListTreeItem*> (getSubItem (i)))
136 if (f->selectFile (target))
137 return true;
139 // if we've just opened and the contents are still loading, wait for it..
140 if (subContentsList != nullptr && subContentsList->isStillLoading())
142 Thread::sleep (10);
143 rebuildItemsFromContentList();
145 else
147 break;
152 return false;
155 void changeListenerCallback (ChangeBroadcaster*) override
157 rebuildItemsFromContentList();
160 void rebuildItemsFromContentList()
162 clearSubItems();
164 if (isOpen() && subContentsList != nullptr)
166 for (int i = 0; i < subContentsList->getNumFiles(); ++i)
167 addSubItem (new FileListTreeItem (owner, subContentsList, i,
168 subContentsList->getFile(i), thread));
172 void paintItem (Graphics& g, int width, int height) override
174 ScopedLock lock (iconUpdate);
176 if (file != File())
178 updateIcon (true);
180 if (icon.isNull())
181 thread.addTimeSliceClient (this);
184 owner.getLookAndFeel().drawFileBrowserRow (g, width, height,
185 file, file.getFileName(),
186 &icon, fileSize, modTime,
187 isDirectory, isSelected(),
188 indexInContentsList, owner);
191 String getAccessibilityName() override
193 return file.getFileName();
196 void itemClicked (const MouseEvent& e) override
198 owner.sendMouseClickMessage (file, e);
201 void itemDoubleClicked (const MouseEvent& e) override
203 TreeViewItem::itemDoubleClicked (e);
205 owner.sendDoubleClickMessage (file);
208 void itemSelectionChanged (bool) override
210 owner.sendSelectionChangeMessage();
213 int useTimeSlice() override
215 updateIcon (false);
216 return -1;
219 void handleAsyncUpdate() override
221 owner.repaint();
224 const File file;
226 private:
227 FileTreeComponent& owner;
228 DirectoryContentsList* parentContentsList;
229 int indexInContentsList;
230 OptionalScopedPointer<DirectoryContentsList> subContentsList;
231 bool isDirectory;
232 TimeSliceThread& thread;
233 CriticalSection iconUpdate;
234 Image icon;
235 String fileSize, modTime;
237 void updateIcon (const bool onlyUpdateIfCached)
239 if (icon.isNull())
241 auto hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
242 auto im = ImageCache::getFromHashCode (hashCode);
244 if (im.isNull() && ! onlyUpdateIfCached)
246 im = juce_createIconForFile (file);
248 if (im.isValid())
249 ImageCache::addImageToCache (im, hashCode);
252 if (im.isValid())
255 ScopedLock lock (iconUpdate);
256 icon = im;
259 triggerAsyncUpdate();
264 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem)
267 //==============================================================================
268 FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
269 : DirectoryContentsDisplayComponent (listToShow),
270 itemHeight (22)
272 setRootItemVisible (false);
273 refresh();
276 FileTreeComponent::~FileTreeComponent()
278 deleteRootItem();
281 void FileTreeComponent::refresh()
283 deleteRootItem();
285 auto root = new FileListTreeItem (*this, nullptr, 0, directoryContentsList.getDirectory(),
286 directoryContentsList.getTimeSliceThread());
288 root->setSubContentsList (&directoryContentsList, false);
289 setRootItem (root);
292 //==============================================================================
293 File FileTreeComponent::getSelectedFile (const int index) const
295 if (auto* item = dynamic_cast<const FileListTreeItem*> (getSelectedItem (index)))
296 return item->file;
298 return {};
301 void FileTreeComponent::deselectAllFiles()
303 clearSelectedItems();
306 void FileTreeComponent::scrollToTop()
308 getViewport()->getVerticalScrollBar().setCurrentRangeStart (0);
311 void FileTreeComponent::setDragAndDropDescription (const String& description)
313 dragAndDropDescription = description;
316 void FileTreeComponent::setSelectedFile (const File& target)
318 if (auto* t = dynamic_cast<FileListTreeItem*> (getRootItem()))
319 if (! t->selectFile (target))
320 clearSelectedItems();
323 void FileTreeComponent::setItemHeight (int newHeight)
325 if (itemHeight != newHeight)
327 itemHeight = newHeight;
329 if (auto* root = getRootItem())
330 root->treeHasChanged();
334 } // namespace juce