/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-7 by Raw Material Software ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License, as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with JUCE; if not, visit www.gnu.org/licenses or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ If you'd like to release a closed-source product which uses JUCE, commercial licenses are also available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ #include "../../../../juce_core/basics/juce_StandardHeader.h" BEGIN_JUCE_NAMESPACE #include "juce_FileTreeComponent.h" #include "../lookandfeel/juce_LookAndFeel.h" #include "../../graphics/imaging/juce_ImageCache.h" #include "../../../events/juce_AsyncUpdater.h" Image* juce_createIconForFile (const File& file); //============================================================================== class FileListTreeItem : public TreeViewItem, public TimeSliceClient, public AsyncUpdater, public ChangeListener { public: //============================================================================== FileListTreeItem (FileTreeComponent& owner_, DirectoryContentsList* const parentContentsList_, const int indexInContentsList_, const File& file_, TimeSliceThread& thread_) throw() : file (file_), owner (owner_), parentContentsList (parentContentsList_), indexInContentsList (indexInContentsList_), subContentsList (0), canDeleteSubContentsList (false), thread (thread_), icon (0) { DirectoryContentsList::FileInfo fileInfo; if (parentContentsList_ != 0 && parentContentsList_->getFileInfo (indexInContentsList_, fileInfo)) { fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize); modTime = fileInfo.modificationTime.formatted (T("%d %b '%y %H:%M")); isDirectory = fileInfo.isDirectory; } else { isDirectory = true; } } ~FileListTreeItem() throw() { thread.removeTimeSliceClient (this); clearSubItems(); ImageCache::release (icon); if (canDeleteSubContentsList) delete subContentsList; } //============================================================================== bool mightContainSubItems() { return isDirectory; } const String getUniqueName() const { return file.getFullPathName(); } int getItemHeight() const { return 22; } const String getDragSourceDescription() { return owner.getDragAndDropDescription(); } void itemOpennessChanged (bool isNowOpen) { if (isNowOpen) { clearSubItems(); isDirectory = file.isDirectory(); if (isDirectory) { if (subContentsList == 0) { jassert (parentContentsList != 0); DirectoryContentsList* const l = new DirectoryContentsList (parentContentsList->getFilter(), thread); l->setDirectory (file, true, true); setSubContentsList (l); canDeleteSubContentsList = true; } changeListenerCallback (0); } } } void setSubContentsList (DirectoryContentsList* newList) throw() { jassert (subContentsList == 0); subContentsList = newList; newList->addChangeListener (this); } void changeListenerCallback (void*) { clearSubItems(); if (isOpen() && subContentsList != 0) { for (int i = 0; i < subContentsList->getNumFiles(); ++i) { FileListTreeItem* const item = new FileListTreeItem (owner, subContentsList, i, subContentsList->getFile(i), thread); addSubItem (item); } } } void paintItem (Graphics& g, int width, int height) { if (file != File::nonexistent && ! isDirectory) { updateIcon (true); if (icon == 0) thread.addTimeSliceClient (this); } owner.getLookAndFeel() .drawFileBrowserRow (g, width, height, file.getFileName(), icon, fileSize, modTime, isDirectory, isSelected(), indexInContentsList); } void itemClicked (const MouseEvent& e) { owner.sendMouseClickMessage (file, e); } void itemDoubleClicked (const MouseEvent& e) { TreeViewItem::itemDoubleClicked (e); owner.sendDoubleClickMessage (file); } void itemSelectionChanged (bool) { owner.sendSelectionChangeMessage(); } bool useTimeSlice() { updateIcon (false); thread.removeTimeSliceClient (this); return false; } void handleAsyncUpdate() { owner.repaint(); } const File file; //============================================================================== juce_UseDebuggingNewOperator private: FileTreeComponent& owner; DirectoryContentsList* parentContentsList; int indexInContentsList; DirectoryContentsList* subContentsList; bool isDirectory, canDeleteSubContentsList; TimeSliceThread& thread; Image* icon; String fileSize; String modTime; void updateIcon (const bool onlyUpdateIfCached) throw() { if (icon == 0) { const int hashCode = (file.getFullPathName() + T("_iconCacheSalt")).hashCode(); Image* im = ImageCache::getFromHashCode (hashCode); if (im == 0 && ! onlyUpdateIfCached) { im = juce_createIconForFile (file); if (im != 0) ImageCache::addImageToCache (im, hashCode); } if (im != 0) { icon = im; triggerAsyncUpdate(); } } } }; //============================================================================== FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow) : DirectoryContentsDisplayComponent (listToShow) { FileListTreeItem* const root = new FileListTreeItem (*this, 0, 0, listToShow.getDirectory(), listToShow.getTimeSliceThread()); root->setSubContentsList (&listToShow); setRootItemVisible (false); setRootItem (root); } FileTreeComponent::~FileTreeComponent() { TreeViewItem* const root = getRootItem(); setRootItem (0); delete root; } //============================================================================== const File FileTreeComponent::getSelectedFile() const { return getSelectedFile (0); } const File FileTreeComponent::getSelectedFile (const int index) const throw() { const FileListTreeItem* const item = dynamic_cast (getSelectedItem (index)); if (item != 0) return item->file; return File::nonexistent; } void FileTreeComponent::scrollToTop() { getViewport()->getVerticalScrollBar()->setCurrentRangeStart (0); } void FileTreeComponent::setDragAndDropDescription (const String& description) throw() { dragAndDropDescription = description; } END_JUCE_NAMESPACE