/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-11 by Raw Material Software Ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License (Version 2), as published by the Free Software Foundation. A copy of the license is included in the JUCE distribution, or can be found online at www.gnu.org/licenses. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ Image juce_createIconForFile (const File& file); //============================================================================== FileListComponent::FileListComponent (DirectoryContentsList& listToShow) : ListBox (String::empty, nullptr), DirectoryContentsDisplayComponent (listToShow) { setModel (this); fileList.addChangeListener (this); } FileListComponent::~FileListComponent() { fileList.removeChangeListener (this); } int FileListComponent::getNumSelectedFiles() const { return getNumSelectedRows(); } File FileListComponent::getSelectedFile (int index) const { return fileList.getFile (getSelectedRow (index)); } void FileListComponent::deselectAllFiles() { deselectAllRows(); } void FileListComponent::scrollToTop() { getVerticalScrollBar()->setCurrentRangeStart (0); } void FileListComponent::setSelectedFile (const File& f) { for (int i = fileList.getNumFiles(); --i >= 0;) { if (fileList.getFile(i) == f) { selectRow (i); return; } } deselectAllRows(); } //============================================================================== void FileListComponent::changeListenerCallback (ChangeBroadcaster*) { updateContent(); if (lastDirectory != fileList.getDirectory()) { lastDirectory = fileList.getDirectory(); deselectAllRows(); } } //============================================================================== class FileListComponent::ItemComponent : public Component, private TimeSliceClient, private AsyncUpdater { public: ItemComponent (FileListComponent& owner_, TimeSliceThread& thread_) : owner (owner_), thread (thread_), index (0), highlighted (false) { } ~ItemComponent() { thread.removeTimeSliceClient (this); } //============================================================================== void paint (Graphics& g) { getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(), file.getFileName(), &icon, fileSize, modTime, isDirectory, highlighted, index, owner); } void mouseDown (const MouseEvent& e) { owner.selectRowsBasedOnModifierKeys (index, e.mods, false); owner.sendMouseClickMessage (file, e); } void mouseDoubleClick (const MouseEvent&) { owner.sendDoubleClickMessage (file); } void update (const File& root, const DirectoryContentsList::FileInfo* const fileInfo, const int index_, const bool highlighted_) { thread.removeTimeSliceClient (this); if (highlighted_ != highlighted || index_ != index) { index = index_; highlighted = highlighted_; repaint(); } File newFile; String newFileSize, newModTime; if (fileInfo != nullptr) { newFile = root.getChildFile (fileInfo->filename); newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize); newModTime = fileInfo->modificationTime.formatted ("%d %b '%y %H:%M"); } if (newFile != file || fileSize != newFileSize || modTime != newModTime) { file = newFile; fileSize = newFileSize; modTime = newModTime; icon = Image::null; isDirectory = fileInfo != nullptr && fileInfo->isDirectory; repaint(); } if (file != File::nonexistent && icon.isNull() && ! isDirectory) { updateIcon (true); if (! icon.isValid()) thread.addTimeSliceClient (this); } } int useTimeSlice() { updateIcon (false); return -1; } void handleAsyncUpdate() { repaint(); } private: //============================================================================== FileListComponent& owner; TimeSliceThread& thread; File file; String fileSize, modTime; Image icon; int index; bool highlighted, isDirectory; void updateIcon (const bool onlyUpdateIfCached) { if (icon.isNull()) { const int hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode(); Image im (ImageCache::getFromHashCode (hashCode)); if (im.isNull() && ! onlyUpdateIfCached) { im = juce_createIconForFile (file); if (im.isValid()) ImageCache::addImageToCache (im, hashCode); } if (im.isValid()) { icon = im; triggerAsyncUpdate(); } } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent); }; //============================================================================== int FileListComponent::getNumRows() { return fileList.getNumFiles(); } void FileListComponent::paintListBoxItem (int, Graphics&, int, int, bool) { } Component* FileListComponent::refreshComponentForRow (int row, bool isSelected, Component* existingComponentToUpdate) { jassert (existingComponentToUpdate == nullptr || dynamic_cast (existingComponentToUpdate) != nullptr); ItemComponent* comp = static_cast (existingComponentToUpdate); if (comp == nullptr) comp = new ItemComponent (*this, fileList.getTimeSliceThread()); DirectoryContentsList::FileInfo fileInfo; comp->update (fileList.getDirectory(), fileList.getFileInfo (row, fileInfo) ? &fileInfo : nullptr, row, isSelected); return comp; } void FileListComponent::selectedRowsChanged (int /*lastRowSelected*/) { sendSelectionChangeMessage(); } void FileListComponent::deleteKeyPressed (int /*currentSelectedRow*/) { } void FileListComponent::returnKeyPressed (int currentSelectedRow) { sendDoubleClickMessage (fileList.getFile (currentSelectedRow)); }