30int threeWayCompare (
const T& a,
const T& b)
37int threeWayCompare (
const String& a,
const String& b);
38int threeWayCompare (
const String& a,
const String& b)
51 return b.value.
compare (a.value);
54template <
size_t position,
typename... Ts>
57 if constexpr (position ==
sizeof... (Ts))
69 return threeWayCompareImpl<position + 1> (a, b);
73template <
typename... Ts>
76 return threeWayCompareImpl<0> (a, b);
117 NullCheckedInvocation::invoke (onOpennessChanged, file, isNowOpen);
134 &icon, fileSize, modTime,
146 owner.sendMouseClickMessage (file, e);
153 owner.sendDoubleClickMessage (file);
158 owner.sendSelectionChangeMessage();
177 bool isDirectory =
false;
183 void updateIcon (
const bool onlyUpdateIfCached)
187 auto hashCode = (file.
getFullPathName() +
"_iconCacheSalt").hashCode();
190 if (im.isNull() && ! onlyUpdateIfCached)
192 im = detail::WindowingHelpers::createIconForFile (file);
220 virtual void rootChanged() = 0;
225 : root (rootIn), listener (listenerIn)
240 void open (
const File& f)
242 auto& contentsList = [&]() ->
auto&
244 if (
auto it = contentsLists.find (f); it != contentsLists.end())
250 root.getTimeSliceThread()));
251 return insertion.first->second;
254 contentsList.addChangeListener (
this);
255 contentsList.setDirectory (f,
true,
true);
256 contentsList.refresh();
259 void close (
const File& f)
261 if (
auto it = contentsLists.find (f); it != contentsLists.end())
262 contentsLists.erase (it);
265 File getRootDirectory()
const
270 bool isStillLoading()
const
276 return it.second.isStillLoading();
281 void changeListenerCallback (ChangeBroadcaster* source)
override
283 auto* sourceList =
static_cast<DirectoryContentsList*
> (source);
285 if (sourceList == &root)
289 contentsLists.clear();
290 listener.rootChanged();
294 for (
auto& contentsList : contentsLists)
295 contentsList.second.refresh();
299 listener.directoryChanged (*sourceList);
302 DirectoryContentsList& root;
313 int compareWindows (
const FileEntry& other)
const
315 const auto toTuple = [] (
const auto& x) {
return std::tuple (! x.isDirectory, x.path.toLowerCase()); };
316 return threeWayCompare (toTuple (*
this), toTuple (other));
319 int compareLinux (
const FileEntry& other)
const
322 return threeWayCompare (toTuple (*
this), toTuple (other));
325 int compareDefault (
const FileEntry& other)
const
335 : systemType (systemTypeIn)
341 return first.compareWindows (second);
343 if ((systemType & SystemStats::OperatingSystemType::Linux) != 0)
344 return first.compareLinux (second);
346 return first.compareDefault (second);
351 return compare (first, second) < 0;
378 void selectFile (
const File& target)
380 pendingFileSelection.emplace (target);
381 tryResolvePendingFileSelection();
385 template <
typename ItemCallback>
386 static void forEachItemRecursive (
TreeViewItem* item, ItemCallback&& cb)
395 forEachItemRecursive (item->
getSubItem (i), cb);
399 void rootChanged()
override
402 treeItemForFile.clear();
403 owner.
setRootItem (createNewItem (scanner.getRootDirectory()).release());
410 if (
auto it = treeItemForFile.find (contentsList.
getDirectory()); it != treeItemForFile.end())
416 if (parentItem ==
nullptr)
422 for (
int i = 0; i < contentsList.
getNumFiles(); ++i)
424 auto file = contentsList.
getFile (i);
431 if (
auto it = treeItemForFile.find (file); it != treeItemForFile.end())
434 auto* newItem = createNewItem (file).release();
435 parentItem->addSubItem (newItem);
440 scanner.open (item->file);
442 item->update (fileInfo);
450 for (
int i = 0; i < contentsList.
getNumFiles(); ++i)
453 for (
int i = 0; i < parentItem->getNumSubItems();)
455 auto* fileItem =
dynamic_cast<FileListTreeItem*
> (parentItem->getSubItem (i));
457 if (fileItem !=
nullptr && allFiles.
count (fileItem->file) == 0)
459 forEachItemRecursive (parentItem->getSubItem (i),
462 scanner.close (item->file);
463 treeItemForFile.erase (item->file);
466 parentItem->removeSubItem (i);
485 if (item1 ==
nullptr || item2 ==
nullptr)
490 return comparisonRules.compare ({ item1->file.getFullPathName(), item1->file.isDirectory() },
491 { item2->file.getFullPathName(), item2->file.isDirectory() });
495 static Comparator comparator;
496 parentItem->sortSubItems (comparator);
497 tryResolvePendingFileSelection();
506 newItem->onOpennessChanged = [
this, itemPtr = newItem.get()] (
const auto& f,
auto isOpen)
514 forEachItemRecursive (itemPtr,
517 scanner.close (item->file);
522 treeItemForFile[file] = newItem.get();
526 void tryResolvePendingFileSelection()
528 if (! pendingFileSelection.has_value())
531 if (
auto item = treeItemForFile.find (*pendingFileSelection); item != treeItemForFile.end())
534 pendingFileSelection.reset();
567 controller->refresh();
591 dragAndDropDescription = description;
596 controller->selectFile (target);
601 if (itemHeight != newHeight)
603 itemHeight = newHeight;
606 root->treeHasChanged();
612class FileTreeComponentTests final :
public UnitTest
616 FileTreeComponentTests() :
UnitTest (
"FileTreeComponentTests", UnitTestCategories::gui) {}
618 void runTest()
override
622 return std::equal (orderedFiles.begin(), orderedFiles.end(),
623 expected.begin(), expected.end(),
624 [] (
const auto& entry,
const auto& expectedPath) { return entry.path == expectedPath; });
627 const auto doSort = [] (
const auto platform,
auto& range)
629 std::sort (range.begin(), range.end(), OSDependentFileComparisonRules { platform });
632 beginTest (
"Test Linux filename ordering");
636 {
"atest",
false } };
638 doSort (SystemStats::OperatingSystemType::Linux, filesToOrder);
640 expect (checkOrder (filesToOrder, {
"atest",
"Atest",
"_test" }));
643 beginTest (
"Test Windows filename ordering");
646 {
"CMakeFiles",
true },
647 {
"JUCEConfig.cmake",
false },
649 {
"cmakefiles.cmake",
false } };
651 doSort (SystemStats::OperatingSystemType::Windows, filesToOrder);
653 expect (checkOrder (filesToOrder, {
"CMakeFiles",
655 "cmake_install.cmake",
657 "JUCEConfig.cmake" }));
660 beginTest (
"Test MacOS filename ordering");
663 {
"CMakeFiles",
true },
665 {
"JUCEConfig.cmake",
false } };
667 doSort (SystemStats::OperatingSystemType::MacOSX, filesToOrder);
669 expect (checkOrder (filesToOrder, {
"cmake_install.cmake",
677static FileTreeComponentTests fileTreeComponentTests;
Has a callback method that is triggered asynchronously.
void triggerAsyncUpdate()
Causes the callback to be triggered at a later time.
void addChangeListener(ChangeListener *listener)
Registers a listener to receive change callbacks from this broadcaster.
void removeChangeListener(ChangeListener *listener)
Unregisters a listener from the list.
Receives change event callbacks that are sent out by a ChangeBroadcaster.
void repaint()
Marks the whole component as needing to be redrawn.
LookAndFeel & getLookAndFeel() const noexcept
Finds the appropriate look-and-feel to use for this component.
A base class for components that display a list of the files in a directory.
DirectoryContentsList & directoryContentsList
The list that this component is displaying.
A class to asynchronously scan for details about the files in a directory.
const File & getDirectory() const noexcept
Returns the directory that's currently being used.
bool getFileInfo(int index, FileInfo &resultInfo) const
Returns the cached information about one of the files in the list.
bool isStillLoading() const
True if the background thread hasn't yet finished scanning for files.
int getNumFiles() const noexcept
Returns the number of files currently available in the list.
void refresh()
Clears the list and restarts scanning the directory for files.
File getFile(int index) const
Returns one of the files in the list.
const FileFilter * getFilter() const noexcept
Returns the file filter being used.
Time modificationTime
File modification time.
bool isDirectory
True if the file is a directory.
int64 fileSize
File size in bytes.
Contains cached information about one of the files in a DirectoryContentsList.
void paintItem(Graphics &g, int width, int height) override
Draws the item's contents.
int getItemHeight() const override
Must return the height required by this item.
void itemSelectionChanged(bool) override
Called when the item is selected or deselected.
void handleAsyncUpdate() override
Called back to do whatever your class needs to do.
var getDragSourceDescription() override
To allow items from your TreeView to be dragged-and-dropped, implement this method.
String getAccessibilityName() override
Use this to set the name for this item that will be read out by accessibility clients.
void itemDoubleClicked(const MouseEvent &e) override
Called when the user double-clicks on this item.
int useTimeSlice() override
Called back by a TimeSliceThread.
void itemOpennessChanged(bool isNowOpen) override
Called when an item is opened or closed.
bool mightContainSubItems() override
Tells the tree whether this item can potentially be opened.
void itemClicked(const MouseEvent &e) override
Called when the user clicks on this item.
String getUniqueName() const override
Returns a string to uniquely identify this item.
A component that displays the files in a directory as a treeview.
File getSelectedFile(int index=0) const override
Returns one of the files that the user has currently selected.
void refresh()
Updates the files in the list.
~FileTreeComponent() override
Destructor.
void setDragAndDropDescription(const String &description)
Setting a name for this allows tree items to be dragged.
void setItemHeight(int newHeight)
Changes the height of the treeview items.
FileTreeComponent(DirectoryContentsList &listToShow)
Creates a listbox to show the contents of a specified directory.
const String & getDragAndDropDescription() const noexcept
Returns the last value that was set by setDragAndDropDescription().
void deselectAllFiles() override
Deselects any files that are currently selected.
void scrollToTop() override
Scrolls the list to the top.
int getItemHeight() const noexcept
Returns the height of the treeview items.
void setSelectedFile(const File &) override
If the specified file is in the list, it will become the only selected item (and if the file isn't in...
Represents a local file or directory.
const String & getFullPathName() const noexcept
Returns the complete, absolute path of this file.
String getFileName() const
Returns the last section of the pathname.
static String descriptionOfSizeInBytes(int64 bytes)
Utility function to convert a file size in bytes to a neat string description.
Automatically locks and unlocks a mutex object.
A graphics context, used for drawing a component or image.
static void addImageToCache(const Image &image, int64 hashCode)
Adds an image to the cache with a user-defined hash-code.
static Image getFromHashCode(int64 hashCode)
Checks the cache for an image with a particular hashcode.
Holds a fixed-size bitmap.
bool isNull() const noexcept
Returns true if this image is not valid.
Contains position and status information about a mouse event.
String toLowerCase() const
Returns an lower-case version of this string.
int compare(const String &other) const noexcept
Case-sensitive comparison with another string.
OperatingSystemType
The set of possible results of the getOperatingSystemType() method.
@ Windows
To test whether any version of Windows is running, you can use the expression ((getOperatingSystemTyp...
Used by the TimeSliceThread class.
A thread that keeps a list of clients, and calls each one in turn, giving them all a chance to run so...
void removeTimeSliceClient(TimeSliceClient *clientToRemove)
Removes a client from the list.
void addTimeSliceClient(TimeSliceClient *clientToAdd, int millisecondsBeforeStarting=0)
Adds a client to the list.
String formatted(const String &format) const
Converts this date/time to a string with a user-defined format.
bool isSelected() const noexcept
True if this item is currently selected.
void clearSubItems()
Removes any sub-items.
int getNumSubItems() const noexcept
Returns the number of sub-items that have been added to this item.
int getIndexInParent() const noexcept
Returns the index of this item in its parent's sub-items.
virtual void itemDoubleClicked(const MouseEvent &)
Called when the user double-clicks on this item.
bool isOpen() const noexcept
True if this item is currently open in the TreeView.
void repaintItem() const
Sends a repaint message to redraw just this item.
TreeViewItem * getSubItem(int index) const noexcept
Returns one of the item's sub-items.
void setSelected(bool shouldBeSelected, bool deselectOtherItemsFirst, NotificationType shouldNotify=sendNotification)
Selects or deselects the item.
TreeViewItem * getSelectedItem(int index) const noexcept
Returns one of the selected items in the tree.
TreeViewItem * getRootItem() const noexcept
Returns the tree's root item.
Viewport * getViewport() const noexcept
Returns the TreeView's Viewport object.
void clearSelectedItems()
Deselects any items that are currently selected.
void deleteRootItem()
This will remove and delete the current root item.
void setRootItem(TreeViewItem *newRootItem)
Sets the item that is displayed in the TreeView.
void setRootItemVisible(bool shouldBeVisible)
Changes whether the tree's root item is shown or not.
This is a base class for classes that perform a unit test.
ScrollBar & getVerticalScrollBar() noexcept
Returns a reference to the scrollbar component being used.
A variant class, that can be used to hold a range of primitive values.
T forward_as_tuple(T... args)
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
void ignoreUnused(Types &&...) noexcept
Handy function for avoiding unused variables warning.