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
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
23 ==============================================================================
29 //==============================================================================
30 class FileListTreeItem
: public TreeViewItem
,
31 private TimeSliceClient
,
33 private ChangeListener
36 FileListTreeItem (FileTreeComponent
& treeComp
,
37 DirectoryContentsList
* parentContents
,
43 parentContentsList (parentContents
),
44 indexInContentsList (indexInContents
),
45 subContentsList (nullptr, false),
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
;
63 ~FileListTreeItem() override
65 thread
.removeTimeSliceClient (this);
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
83 isDirectory
= file
.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
)
124 setSelected (true, true);
128 if (target
.isAChildOf (file
))
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
))
139 // if we've just opened and the contents are still loading, wait for it..
140 if (subContentsList
!= nullptr && subContentsList
->isStillLoading())
143 rebuildItemsFromContentList();
155 void changeListenerCallback (ChangeBroadcaster
*) override
157 rebuildItemsFromContentList();
160 void rebuildItemsFromContentList()
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
);
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
219 void handleAsyncUpdate() override
227 FileTreeComponent
& owner
;
228 DirectoryContentsList
* parentContentsList
;
229 int indexInContentsList
;
230 OptionalScopedPointer
<DirectoryContentsList
> subContentsList
;
232 TimeSliceThread
& thread
;
233 CriticalSection iconUpdate
;
235 String fileSize
, modTime
;
237 void updateIcon (const bool onlyUpdateIfCached
)
241 auto hashCode
= (file
.getFullPathName() + "_iconCacheSalt").hashCode();
242 auto im
= ImageCache::getFromHashCode (hashCode
);
244 if (im
.isNull() && ! onlyUpdateIfCached
)
246 im
= juce_createIconForFile (file
);
249 ImageCache::addImageToCache (im
, hashCode
);
255 ScopedLock
lock (iconUpdate
);
259 triggerAsyncUpdate();
264 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem
)
267 //==============================================================================
268 FileTreeComponent::FileTreeComponent (DirectoryContentsList
& listToShow
)
269 : DirectoryContentsDisplayComponent (listToShow
),
272 setRootItemVisible (false);
276 FileTreeComponent::~FileTreeComponent()
281 void FileTreeComponent::refresh()
285 auto root
= new FileListTreeItem (*this, nullptr, 0, directoryContentsList
.getDirectory(),
286 directoryContentsList
.getTimeSliceThread());
288 root
->setSubContentsList (&directoryContentsList
, false);
292 //==============================================================================
293 File
FileTreeComponent::getSelectedFile (const int index
) const
295 if (auto* item
= dynamic_cast<const FileListTreeItem
*> (getSelectedItem (index
)))
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();