1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-13 00:04:19 +00:00

Added Animated App template and examples

This commit is contained in:
Felix Faire 2014-10-29 15:55:23 +00:00
parent fefcf7aca6
commit ff6520a89a
1141 changed files with 438491 additions and 94 deletions

View file

@ -0,0 +1,71 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
DirectoryContentsDisplayComponent::DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow)
: fileList (listToShow)
{
}
DirectoryContentsDisplayComponent::~DirectoryContentsDisplayComponent()
{
}
//==============================================================================
FileBrowserListener::~FileBrowserListener()
{
}
void DirectoryContentsDisplayComponent::addListener (FileBrowserListener* const listener)
{
listeners.add (listener);
}
void DirectoryContentsDisplayComponent::removeListener (FileBrowserListener* const listener)
{
listeners.remove (listener);
}
void DirectoryContentsDisplayComponent::sendSelectionChangeMessage()
{
Component::BailOutChecker checker (dynamic_cast <Component*> (this));
listeners.callChecked (checker, &FileBrowserListener::selectionChanged);
}
void DirectoryContentsDisplayComponent::sendMouseClickMessage (const File& file, const MouseEvent& e)
{
if (fileList.getDirectory().exists())
{
Component::BailOutChecker checker (dynamic_cast <Component*> (this));
listeners.callChecked (checker, &FileBrowserListener::fileClicked, file, e);
}
}
void DirectoryContentsDisplayComponent::sendDoubleClickMessage (const File& file)
{
if (fileList.getDirectory().exists())
{
Component::BailOutChecker checker (dynamic_cast <Component*> (this));
listeners.callChecked (checker, &FileBrowserListener::fileDoubleClicked, file);
}
}

View file

@ -0,0 +1,111 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_H_INCLUDED
#define JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_H_INCLUDED
//==============================================================================
/**
A base class for components that display a list of the files in a directory.
@see DirectoryContentsList
*/
class JUCE_API DirectoryContentsDisplayComponent
{
public:
//==============================================================================
/** Creates a DirectoryContentsDisplayComponent for a given list of files. */
DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow);
/** Destructor. */
virtual ~DirectoryContentsDisplayComponent();
//==============================================================================
/** Returns the number of files the user has got selected.
@see getSelectedFile
*/
virtual int getNumSelectedFiles() const = 0;
/** Returns one of the files that the user has currently selected.
The index should be in the range 0 to (getNumSelectedFiles() - 1).
@see getNumSelectedFiles
*/
virtual File getSelectedFile (int index) const = 0;
/** Deselects any selected files. */
virtual void deselectAllFiles() = 0;
/** Scrolls this view to the top. */
virtual void scrollToTop() = 0;
/** If the specified file is in the list, it will become the only selected item
(and if the file isn't in the list, all other items will be deselected). */
virtual void setSelectedFile (const File&) = 0;
//==============================================================================
/** Adds a listener to be told when files are selected or clicked.
@see removeListener
*/
void addListener (FileBrowserListener* listener);
/** Removes a listener.
@see addListener
*/
void removeListener (FileBrowserListener* listener);
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the list.
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
methods.
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
*/
enum ColourIds
{
highlightColourId = 0x1000540, /**< The colour to use to fill a highlighted row of the list. */
textColourId = 0x1000541, /**< The colour for the text. */
};
//==============================================================================
/** @internal */
void sendSelectionChangeMessage();
/** @internal */
void sendDoubleClickMessage (const File& file);
/** @internal */
void sendMouseClickMessage (const File& file, const MouseEvent& e);
protected:
//==============================================================================
DirectoryContentsList& fileList;
ListenerList <FileBrowserListener> listeners;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsDisplayComponent)
};
#endif // JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_H_INCLUDED

View file

@ -0,0 +1,258 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
DirectoryContentsList::DirectoryContentsList (const FileFilter* f, TimeSliceThread& t)
: fileFilter (f), thread (t),
fileTypeFlags (File::ignoreHiddenFiles | File::findFiles),
shouldStop (true)
{
}
DirectoryContentsList::~DirectoryContentsList()
{
stopSearching();
}
void DirectoryContentsList::setIgnoresHiddenFiles (const bool shouldIgnoreHiddenFiles)
{
setTypeFlags (shouldIgnoreHiddenFiles ? (fileTypeFlags | File::ignoreHiddenFiles)
: (fileTypeFlags & ~File::ignoreHiddenFiles));
}
bool DirectoryContentsList::ignoresHiddenFiles() const
{
return (fileTypeFlags & File::ignoreHiddenFiles) != 0;
}
//==============================================================================
void DirectoryContentsList::setDirectory (const File& directory,
const bool includeDirectories,
const bool includeFiles)
{
jassert (includeDirectories || includeFiles); // you have to speciify at least one of these!
if (directory != root)
{
clear();
root = directory;
changed();
// (this forces a refresh when setTypeFlags() is called, rather than triggering two refreshes)
fileTypeFlags &= ~(File::findDirectories | File::findFiles);
}
int newFlags = fileTypeFlags;
if (includeDirectories) newFlags |= File::findDirectories; else newFlags &= ~File::findDirectories;
if (includeFiles) newFlags |= File::findFiles; else newFlags &= ~File::findFiles;
setTypeFlags (newFlags);
}
void DirectoryContentsList::setTypeFlags (const int newFlags)
{
if (fileTypeFlags != newFlags)
{
fileTypeFlags = newFlags;
refresh();
}
}
void DirectoryContentsList::stopSearching()
{
shouldStop = true;
thread.removeTimeSliceClient (this);
fileFindHandle = nullptr;
}
void DirectoryContentsList::clear()
{
stopSearching();
if (files.size() > 0)
{
files.clear();
changed();
}
}
void DirectoryContentsList::refresh()
{
clear();
if (root.isDirectory())
{
fileFindHandle = new DirectoryIterator (root, false, "*", fileTypeFlags);
shouldStop = false;
thread.addTimeSliceClient (this);
}
}
void DirectoryContentsList::setFileFilter (const FileFilter* newFileFilter)
{
const ScopedLock sl (fileListLock);
fileFilter = newFileFilter;
}
//==============================================================================
bool DirectoryContentsList::getFileInfo (const int index, FileInfo& result) const
{
const ScopedLock sl (fileListLock);
if (const FileInfo* const info = files [index])
{
result = *info;
return true;
}
return false;
}
File DirectoryContentsList::getFile (const int index) const
{
const ScopedLock sl (fileListLock);
if (const FileInfo* const info = files [index])
return root.getChildFile (info->filename);
return File();
}
bool DirectoryContentsList::contains (const File& targetFile) const
{
const ScopedLock sl (fileListLock);
for (int i = files.size(); --i >= 0;)
if (root.getChildFile (files.getUnchecked(i)->filename) == targetFile)
return true;
return false;
}
bool DirectoryContentsList::isStillLoading() const
{
return fileFindHandle != nullptr;
}
void DirectoryContentsList::changed()
{
sendChangeMessage();
}
//==============================================================================
int DirectoryContentsList::useTimeSlice()
{
const uint32 startTime = Time::getApproximateMillisecondCounter();
bool hasChanged = false;
for (int i = 100; --i >= 0;)
{
if (! checkNextFile (hasChanged))
{
if (hasChanged)
changed();
return 500;
}
if (shouldStop || (Time::getApproximateMillisecondCounter() > startTime + 150))
break;
}
if (hasChanged)
changed();
return 0;
}
bool DirectoryContentsList::checkNextFile (bool& hasChanged)
{
if (fileFindHandle != nullptr)
{
bool fileFoundIsDir, isHidden, isReadOnly;
int64 fileSize;
Time modTime, creationTime;
if (fileFindHandle->next (&fileFoundIsDir, &isHidden, &fileSize,
&modTime, &creationTime, &isReadOnly))
{
if (addFile (fileFindHandle->getFile(), fileFoundIsDir,
fileSize, modTime, creationTime, isReadOnly))
{
hasChanged = true;
}
return true;
}
fileFindHandle = nullptr;
}
return false;
}
struct FileInfoComparator
{
static int compareElements (const DirectoryContentsList::FileInfo* const first,
const DirectoryContentsList::FileInfo* const second)
{
#if JUCE_WINDOWS
if (first->isDirectory != second->isDirectory)
return first->isDirectory ? -1 : 1;
#endif
return first->filename.compareNatural (second->filename);
}
};
bool DirectoryContentsList::addFile (const File& file, const bool isDir,
const int64 fileSize,
Time modTime, Time creationTime,
const bool isReadOnly)
{
const ScopedLock sl (fileListLock);
if (fileFilter == nullptr
|| ((! isDir) && fileFilter->isFileSuitable (file))
|| (isDir && fileFilter->isDirectorySuitable (file)))
{
ScopedPointer<FileInfo> info (new FileInfo());
info->filename = file.getFileName();
info->fileSize = fileSize;
info->modificationTime = modTime;
info->creationTime = creationTime;
info->isDirectory = isDir;
info->isReadOnly = isReadOnly;
for (int i = files.size(); --i >= 0;)
if (files.getUnchecked(i)->filename == info->filename)
return false;
FileInfoComparator comp;
files.addSorted (comp, info.release());
return true;
}
return false;
}

View file

@ -0,0 +1,223 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_DIRECTORYCONTENTSLIST_H_INCLUDED
#define JUCE_DIRECTORYCONTENTSLIST_H_INCLUDED
//==============================================================================
/**
A class to asynchronously scan for details about the files in a directory.
This keeps a list of files and some information about them, using a background
thread to scan for more files. As files are found, it broadcasts change messages
to tell any listeners.
@see FileListComponent, FileBrowserComponent
*/
class JUCE_API DirectoryContentsList : public ChangeBroadcaster,
private TimeSliceClient
{
public:
//==============================================================================
/** Creates a directory list.
To set the directory it should point to, use setDirectory(), which will
also start it scanning for files on the background thread.
When the background thread finds and adds new files to this list, the
ChangeBroadcaster class will send a change message, so you can register
listeners and update them when the list changes.
@param fileFilter an optional filter to select which files are
included in the list. If this is nullptr, then all files
and directories are included. Make sure that the filter
doesn't get deleted during the lifetime of this object
@param threadToUse a thread object that this list can use
to scan for files as a background task. Make sure
that the thread you give it has been started, or you
won't get any files!
*/
DirectoryContentsList (const FileFilter* fileFilter,
TimeSliceThread& threadToUse);
/** Destructor. */
~DirectoryContentsList();
//==============================================================================
/** Returns the directory that's currently being used. */
const File& getDirectory() const noexcept { return root; }
/** Sets the directory to look in for files.
If the directory that's passed in is different to the current one, this will
also start the background thread scanning it for files.
*/
void setDirectory (const File& directory,
bool includeDirectories,
bool includeFiles);
/** Returns true if this list contains directories.
@see setDirectory
*/
bool isFindingDirectories() const noexcept { return (fileTypeFlags & File::findDirectories) != 0; }
/** Returns true if this list contains files.
@see setDirectory
*/
bool isFindingFiles() const noexcept { return (fileTypeFlags & File::findFiles) != 0; }
/** Clears the list, and stops the thread scanning for files. */
void clear();
/** Clears the list and restarts scanning the directory for files. */
void refresh();
/** True if the background thread hasn't yet finished scanning for files. */
bool isStillLoading() const;
/** Tells the list whether or not to ignore hidden files.
By default these are ignored.
*/
void setIgnoresHiddenFiles (bool shouldIgnoreHiddenFiles);
/** Returns true if hidden files are ignored.
@see setIgnoresHiddenFiles
*/
bool ignoresHiddenFiles() const;
/** Replaces the current FileFilter.
This can be nullptr to have no filter. The DirectoryContentList does not take
ownership of this object - it just keeps a pointer to it, so you must manage its
lifetime.
Note that this only replaces the filter, it doesn't refresh the list - you'll
probably want to call refresh() after calling this.
*/
void setFileFilter (const FileFilter* newFileFilter);
//==============================================================================
/** Contains cached information about one of the files in a DirectoryContentsList.
*/
struct FileInfo
{
//==============================================================================
/** The filename.
This isn't a full pathname, it's just the last part of the path, same as you'd
get from File::getFileName().
To get the full pathname, use DirectoryContentsList::getDirectory().getChildFile (filename).
*/
String filename;
/** File size in bytes. */
int64 fileSize;
/** File modification time.
As supplied by File::getLastModificationTime().
*/
Time modificationTime;
/** File creation time.
As supplied by File::getCreationTime().
*/
Time creationTime;
/** True if the file is a directory. */
bool isDirectory;
/** True if the file is read-only. */
bool isReadOnly;
};
//==============================================================================
/** Returns the number of files currently available in the list.
The info about one of these files can be retrieved with getFileInfo() or getFile().
Obviously as the background thread runs and scans the directory for files, this
number will change.
@see getFileInfo, getFile
*/
int getNumFiles() const noexcept { return files.size(); }
/** Returns the cached information about one of the files in the list.
If the index is in-range, this will return true and will copy the file's details
to the structure that is passed-in.
If it returns false, then the index wasn't in range, and the structure won't
be affected.
@see getNumFiles, getFile
*/
bool getFileInfo (int index, FileInfo& resultInfo) const;
/** Returns one of the files in the list.
@param index should be less than getNumFiles(). If this is out-of-range, the
return value will be File::nonexistent
@see getNumFiles, getFileInfo
*/
File getFile (int index) const;
/** Returns the file filter being used.
The filter is specified in the constructor.
*/
const FileFilter* getFilter() const noexcept { return fileFilter; }
/** Returns true if the list contains the specified file. */
bool contains (const File&) const;
//==============================================================================
/** @internal */
TimeSliceThread& getTimeSliceThread() const noexcept { return thread; }
private:
File root;
const FileFilter* fileFilter;
TimeSliceThread& thread;
int fileTypeFlags;
CriticalSection fileListLock;
OwnedArray<FileInfo> files;
ScopedPointer<DirectoryIterator> fileFindHandle;
bool volatile shouldStop;
int useTimeSlice() override;
void stopSearching();
void changed();
bool checkNextFile (bool& hasChanged);
bool addFile (const File&, bool isDir, int64 fileSize, Time modTime,
Time creationTime, bool isReadOnly);
void setTypeFlags (int);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsList)
};
#endif // JUCE_DIRECTORYCONTENTSLIST_H_INCLUDED

View file

@ -0,0 +1,586 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
FileBrowserComponent::FileBrowserComponent (int flags_,
const File& initialFileOrDirectory,
const FileFilter* fileFilter_,
FilePreviewComponent* previewComp_)
: FileFilter (String::empty),
fileFilter (fileFilter_),
flags (flags_),
previewComp (previewComp_),
currentPathBox ("path"),
fileLabel ("f", TRANS ("file:")),
thread ("Juce FileBrowser")
{
// You need to specify one or other of the open/save flags..
jassert ((flags & (saveMode | openMode)) != 0);
jassert ((flags & (saveMode | openMode)) != (saveMode | openMode));
// You need to specify at least one of these flags..
jassert ((flags & (canSelectFiles | canSelectDirectories)) != 0);
String filename;
if (initialFileOrDirectory == File::nonexistent)
{
currentRoot = File::getCurrentWorkingDirectory();
}
else if (initialFileOrDirectory.isDirectory())
{
currentRoot = initialFileOrDirectory;
}
else
{
chosenFiles.add (initialFileOrDirectory);
currentRoot = initialFileOrDirectory.getParentDirectory();
filename = initialFileOrDirectory.getFileName();
}
fileList = new DirectoryContentsList (this, thread);
if ((flags & useTreeView) != 0)
{
FileTreeComponent* const tree = new FileTreeComponent (*fileList);
fileListComponent = tree;
if ((flags & canSelectMultipleItems) != 0)
tree->setMultiSelectEnabled (true);
addAndMakeVisible (tree);
}
else
{
FileListComponent* const list = new FileListComponent (*fileList);
fileListComponent = list;
list->setOutlineThickness (1);
if ((flags & canSelectMultipleItems) != 0)
list->setMultipleSelectionEnabled (true);
addAndMakeVisible (list);
}
fileListComponent->addListener (this);
addAndMakeVisible (currentPathBox);
currentPathBox.setEditableText (true);
resetRecentPaths();
currentPathBox.addListener (this);
addAndMakeVisible (filenameBox);
filenameBox.setMultiLine (false);
filenameBox.setSelectAllWhenFocused (true);
filenameBox.setText (filename, false);
filenameBox.addListener (this);
filenameBox.setReadOnly ((flags & (filenameBoxIsReadOnly | canSelectMultipleItems)) != 0);
addAndMakeVisible (fileLabel);
fileLabel.attachToComponent (&filenameBox, true);
addAndMakeVisible (goUpButton = getLookAndFeel().createFileBrowserGoUpButton());
goUpButton->addListener (this);
goUpButton->setTooltip (TRANS ("Go up to parent directory"));
if (previewComp != nullptr)
addAndMakeVisible (previewComp);
setRoot (currentRoot);
thread.startThread (4);
}
FileBrowserComponent::~FileBrowserComponent()
{
fileListComponent = nullptr;
fileList = nullptr;
thread.stopThread (10000);
}
//==============================================================================
void FileBrowserComponent::addListener (FileBrowserListener* const newListener)
{
listeners.add (newListener);
}
void FileBrowserComponent::removeListener (FileBrowserListener* const listener)
{
listeners.remove (listener);
}
//==============================================================================
bool FileBrowserComponent::isSaveMode() const noexcept
{
return (flags & saveMode) != 0;
}
int FileBrowserComponent::getNumSelectedFiles() const noexcept
{
if (chosenFiles.size() == 0 && currentFileIsValid())
return 1;
return chosenFiles.size();
}
File FileBrowserComponent::getSelectedFile (int index) const noexcept
{
if ((flags & canSelectDirectories) != 0 && filenameBox.getText().isEmpty())
return currentRoot;
if (! filenameBox.isReadOnly())
return currentRoot.getChildFile (filenameBox.getText());
return chosenFiles[index];
}
bool FileBrowserComponent::currentFileIsValid() const
{
const File f (getSelectedFile (0));
if (isSaveMode())
return (flags & canSelectDirectories) != 0 || ! f.isDirectory();
return f.exists();
}
File FileBrowserComponent::getHighlightedFile() const noexcept
{
return fileListComponent->getSelectedFile (0);
}
void FileBrowserComponent::deselectAllFiles()
{
fileListComponent->deselectAllFiles();
}
//==============================================================================
bool FileBrowserComponent::isFileSuitable (const File& file) const
{
return (flags & canSelectFiles) != 0
&& (fileFilter == nullptr || fileFilter->isFileSuitable (file));
}
bool FileBrowserComponent::isDirectorySuitable (const File&) const
{
return true;
}
bool FileBrowserComponent::isFileOrDirSuitable (const File& f) const
{
if (f.isDirectory())
return (flags & canSelectDirectories) != 0
&& (fileFilter == nullptr || fileFilter->isDirectorySuitable (f));
return (flags & canSelectFiles) != 0 && f.exists()
&& (fileFilter == nullptr || fileFilter->isFileSuitable (f));
}
//==============================================================================
const File& FileBrowserComponent::getRoot() const
{
return currentRoot;
}
void FileBrowserComponent::setRoot (const File& newRootDirectory)
{
bool callListeners = false;
if (currentRoot != newRootDirectory)
{
callListeners = true;
fileListComponent->scrollToTop();
String path (newRootDirectory.getFullPathName());
if (path.isEmpty())
path = File::separatorString;
StringArray rootNames, rootPaths;
getRoots (rootNames, rootPaths);
if (! rootPaths.contains (path, true))
{
bool alreadyListed = false;
for (int i = currentPathBox.getNumItems(); --i >= 0;)
{
if (currentPathBox.getItemText (i).equalsIgnoreCase (path))
{
alreadyListed = true;
break;
}
}
if (! alreadyListed)
currentPathBox.addItem (path, currentPathBox.getNumItems() + 2);
}
}
currentRoot = newRootDirectory;
fileList->setDirectory (currentRoot, true, true);
String currentRootName (currentRoot.getFullPathName());
if (currentRootName.isEmpty())
currentRootName = File::separatorString;
currentPathBox.setText (currentRootName, dontSendNotification);
goUpButton->setEnabled (currentRoot.getParentDirectory().isDirectory()
&& currentRoot.getParentDirectory() != currentRoot);
if (callListeners)
{
Component::BailOutChecker checker (this);
listeners.callChecked (checker, &FileBrowserListener::browserRootChanged, currentRoot);
}
}
void FileBrowserComponent::setFileName (const String& newName)
{
filenameBox.setText (newName, true);
fileListComponent->setSelectedFile (currentRoot.getChildFile (newName));
}
void FileBrowserComponent::resetRecentPaths()
{
currentPathBox.clear();
StringArray rootNames, rootPaths;
getRoots (rootNames, rootPaths);
for (int i = 0; i < rootNames.size(); ++i)
{
if (rootNames[i].isEmpty())
currentPathBox.addSeparator();
else
currentPathBox.addItem (rootNames[i], i + 1);
}
currentPathBox.addSeparator();
}
void FileBrowserComponent::goUp()
{
setRoot (getRoot().getParentDirectory());
}
void FileBrowserComponent::refresh()
{
fileList->refresh();
}
void FileBrowserComponent::setFileFilter (const FileFilter* const newFileFilter)
{
if (fileFilter != newFileFilter)
{
fileFilter = newFileFilter;
refresh();
}
}
String FileBrowserComponent::getActionVerb() const
{
return isSaveMode() ? ((flags & canSelectDirectories) != 0 ? TRANS("Choose")
: TRANS("Save"))
: TRANS("Open");
}
void FileBrowserComponent::setFilenameBoxLabel (const String& name)
{
fileLabel.setText (name, dontSendNotification);
}
FilePreviewComponent* FileBrowserComponent::getPreviewComponent() const noexcept
{
return previewComp;
}
DirectoryContentsDisplayComponent* FileBrowserComponent::getDisplayComponent() const noexcept
{
return fileListComponent;
}
//==============================================================================
void FileBrowserComponent::resized()
{
getLookAndFeel()
.layoutFileBrowserComponent (*this, fileListComponent, previewComp,
&currentPathBox, &filenameBox, goUpButton);
}
//==============================================================================
void FileBrowserComponent::sendListenerChangeMessage()
{
Component::BailOutChecker checker (this);
if (previewComp != nullptr)
previewComp->selectedFileChanged (getSelectedFile (0));
// You shouldn't delete the browser when the file gets changed!
jassert (! checker.shouldBailOut());
listeners.callChecked (checker, &FileBrowserListener::selectionChanged);
}
void FileBrowserComponent::selectionChanged()
{
StringArray newFilenames;
bool resetChosenFiles = true;
for (int i = 0; i < fileListComponent->getNumSelectedFiles(); ++i)
{
const File f (fileListComponent->getSelectedFile (i));
if (isFileOrDirSuitable (f))
{
if (resetChosenFiles)
{
chosenFiles.clear();
resetChosenFiles = false;
}
chosenFiles.add (f);
newFilenames.add (f.getRelativePathFrom (getRoot()));
}
}
if (newFilenames.size() > 0)
filenameBox.setText (newFilenames.joinIntoString (", "), false);
sendListenerChangeMessage();
}
void FileBrowserComponent::fileClicked (const File& f, const MouseEvent& e)
{
Component::BailOutChecker checker (this);
listeners.callChecked (checker, &FileBrowserListener::fileClicked, f, e);
}
void FileBrowserComponent::fileDoubleClicked (const File& f)
{
if (f.isDirectory())
{
setRoot (f);
if ((flags & canSelectDirectories) != 0)
filenameBox.setText (String::empty);
}
else
{
Component::BailOutChecker checker (this);
listeners.callChecked (checker, &FileBrowserListener::fileDoubleClicked, f);
}
}
void FileBrowserComponent::browserRootChanged (const File&) {}
bool FileBrowserComponent::keyPressed (const KeyPress& key)
{
(void) key;
#if JUCE_LINUX || JUCE_WINDOWS
if (key.getModifiers().isCommandDown()
&& (key.getKeyCode() == 'H' || key.getKeyCode() == 'h'))
{
fileList->setIgnoresHiddenFiles (! fileList->ignoresHiddenFiles());
fileList->refresh();
return true;
}
#endif
return false;
}
//==============================================================================
void FileBrowserComponent::textEditorTextChanged (TextEditor&)
{
sendListenerChangeMessage();
}
void FileBrowserComponent::textEditorReturnKeyPressed (TextEditor&)
{
if (filenameBox.getText().containsChar (File::separator))
{
const File f (currentRoot.getChildFile (filenameBox.getText()));
if (f.isDirectory())
{
setRoot (f);
chosenFiles.clear();
filenameBox.setText (String::empty);
}
else
{
setRoot (f.getParentDirectory());
chosenFiles.clear();
chosenFiles.add (f);
filenameBox.setText (f.getFileName());
}
}
else
{
fileDoubleClicked (getSelectedFile (0));
}
}
void FileBrowserComponent::textEditorEscapeKeyPressed (TextEditor&)
{
}
void FileBrowserComponent::textEditorFocusLost (TextEditor&)
{
if (! isSaveMode())
selectionChanged();
}
//==============================================================================
void FileBrowserComponent::buttonClicked (Button*)
{
goUp();
}
void FileBrowserComponent::comboBoxChanged (ComboBox*)
{
const String newText (currentPathBox.getText().trim().unquoted());
if (newText.isNotEmpty())
{
const int index = currentPathBox.getSelectedId() - 1;
StringArray rootNames, rootPaths;
getRoots (rootNames, rootPaths);
if (rootPaths [index].isNotEmpty())
{
setRoot (File (rootPaths [index]));
}
else
{
File f (newText);
for (;;)
{
if (f.isDirectory())
{
setRoot (f);
break;
}
if (f.getParentDirectory() == f)
break;
f = f.getParentDirectory();
}
}
}
}
void FileBrowserComponent::getDefaultRoots (StringArray& rootNames, StringArray& rootPaths)
{
#if JUCE_WINDOWS
Array<File> roots;
File::findFileSystemRoots (roots);
rootPaths.clear();
for (int i = 0; i < roots.size(); ++i)
{
const File& drive = roots.getReference(i);
String name (drive.getFullPathName());
rootPaths.add (name);
if (drive.isOnHardDisk())
{
String volume (drive.getVolumeLabel());
if (volume.isEmpty())
volume = TRANS("Hard Drive");
name << " [" << volume << ']';
}
else if (drive.isOnCDRomDrive())
{
name << " [" << TRANS("CD/DVD drive") << ']';
}
rootNames.add (name);
}
rootPaths.add (String::empty);
rootNames.add (String::empty);
rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName());
rootNames.add (TRANS("Documents"));
rootPaths.add (File::getSpecialLocation (File::userMusicDirectory).getFullPathName());
rootNames.add (TRANS("Music"));
rootPaths.add (File::getSpecialLocation (File::userPicturesDirectory).getFullPathName());
rootNames.add (TRANS("Pictures"));
rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName());
rootNames.add (TRANS("Desktop"));
#elif JUCE_MAC
rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
rootNames.add (TRANS("Home folder"));
rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName());
rootNames.add (TRANS("Documents"));
rootPaths.add (File::getSpecialLocation (File::userMusicDirectory).getFullPathName());
rootNames.add (TRANS("Music"));
rootPaths.add (File::getSpecialLocation (File::userPicturesDirectory).getFullPathName());
rootNames.add (TRANS("Pictures"));
rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName());
rootNames.add (TRANS("Desktop"));
rootPaths.add (String::empty);
rootNames.add (String::empty);
Array <File> volumes;
File vol ("/Volumes");
vol.findChildFiles (volumes, File::findDirectories, false);
for (int i = 0; i < volumes.size(); ++i)
{
const File& volume = volumes.getReference(i);
if (volume.isDirectory() && ! volume.getFileName().startsWithChar ('.'))
{
rootPaths.add (volume.getFullPathName());
rootNames.add (volume.getFileName());
}
}
#else
rootPaths.add ("/");
rootNames.add ("/");
rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
rootNames.add (TRANS("Home folder"));
rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName());
rootNames.add (TRANS("Desktop"));
#endif
}
void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPaths)
{
getDefaultRoots (rootNames, rootPaths);
}

View file

@ -0,0 +1,290 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILEBROWSERCOMPONENT_H_INCLUDED
#define JUCE_FILEBROWSERCOMPONENT_H_INCLUDED
//==============================================================================
/**
A component for browsing and selecting a file or directory to open or save.
This contains a FileListComponent and adds various boxes and controls for
navigating and selecting a file. It can work in different modes so that it can
be used for loading or saving a file, or for choosing a directory.
@see FileChooserDialogBox, FileChooser, FileListComponent
*/
class JUCE_API FileBrowserComponent : public Component,
private FileBrowserListener,
private TextEditorListener,
private ButtonListener,
private ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug)
private FileFilter
{
public:
//==============================================================================
/** Various options for the browser.
A combination of these is passed into the FileBrowserComponent constructor.
*/
enum FileChooserFlags
{
openMode = 1, /**< specifies that the component should allow the user to
choose an existing file with the intention of opening it. */
saveMode = 2, /**< specifies that the component should allow the user to specify
the name of a file that will be used to save something. */
canSelectFiles = 4, /**< specifies that the user can select files (can be used in
conjunction with canSelectDirectories). */
canSelectDirectories = 8, /**< specifies that the user can select directories (can be used in
conjuction with canSelectFiles). */
canSelectMultipleItems = 16, /**< specifies that the user can select multiple items. */
useTreeView = 32, /**< specifies that a tree-view should be shown instead of a file list. */
filenameBoxIsReadOnly = 64, /**< specifies that the user can't type directly into the filename box. */
warnAboutOverwriting = 128 /**< specifies that the dialog should warn about overwriting existing files (if possible). */
};
//==============================================================================
/** Creates a FileBrowserComponent.
@param flags A combination of flags from the FileChooserFlags enumeration, used to
specify the component's behaviour. The flags must contain either openMode
or saveMode, and canSelectFiles and/or canSelectDirectories.
@param initialFileOrDirectory The file or directory that should be selected when the component begins.
If this is File::nonexistent, a default directory will be chosen.
@param fileFilter an optional filter to use to determine which files are shown.
If this is nullptr then all files are displayed. Note that a pointer
is kept internally to this object, so make sure that it is not deleted
before the FileBrowserComponent object is deleted.
@param previewComp an optional preview component that will be used to show previews of
files that the user selects
*/
FileBrowserComponent (int flags,
const File& initialFileOrDirectory,
const FileFilter* fileFilter,
FilePreviewComponent* previewComp);
/** Destructor. */
~FileBrowserComponent();
//==============================================================================
/** Returns the number of files that the user has got selected.
If multiple select isn't active, this will only be 0 or 1. To get the complete
list of files they've chosen, pass an index to getCurrentFile().
*/
int getNumSelectedFiles() const noexcept;
/** Returns one of the files that the user has chosen.
If the box has multi-select enabled, the index lets you specify which of the files
to get - see getNumSelectedFiles() to find out how many files were chosen.
@see getHighlightedFile
*/
File getSelectedFile (int index) const noexcept;
/** Deselects any files that are currently selected.
*/
void deselectAllFiles();
/** Returns true if the currently selected file(s) are usable.
This can be used to decide whether the user can press "ok" for the
current file. What it does depends on the mode, so for example in an "open"
mode, this only returns true if a file has been selected and if it exists.
In a "save" mode, a non-existent file would also be valid.
*/
bool currentFileIsValid() const;
/** This returns the last item in the view that the user has highlighted.
This may be different from getCurrentFile(), which returns the value
that is shown in the filename box, and if there are multiple selections,
this will only return one of them.
@see getSelectedFile
*/
File getHighlightedFile() const noexcept;
//==============================================================================
/** Returns the directory whose contents are currently being shown in the listbox. */
const File& getRoot() const;
/** Changes the directory that's being shown in the listbox. */
void setRoot (const File& newRootDirectory);
/** Changes the name that is currently shown in the filename box. */
void setFileName (const String& newName);
/** Equivalent to pressing the "up" button to browse the parent directory. */
void goUp();
/** Refreshes the directory that's currently being listed. */
void refresh();
/** Changes the filter that's being used to sift the files. */
void setFileFilter (const FileFilter* newFileFilter);
/** Returns a verb to describe what should happen when the file is accepted.
E.g. if browsing in "load file" mode, this will be "Open", if in "save file"
mode, it'll be "Save", etc.
*/
virtual String getActionVerb() const;
/** Returns true if the saveMode flag was set when this component was created.
*/
bool isSaveMode() const noexcept;
/** Sets the label that will be displayed next to the filename entry box.
By default this is just "file", but you might want to change it to something more
appropriate for your app.
*/
void setFilenameBoxLabel (const String& name);
//==============================================================================
/** Adds a listener to be told when the user selects and clicks on files.
@see removeListener
*/
void addListener (FileBrowserListener* listener);
/** Removes a listener.
@see addListener
*/
void removeListener (FileBrowserListener* listener);
/** Returns a platform-specific list of names and paths for some suggested places the user
might want to use as root folders.
The list returned contains empty strings to indicate section breaks.
@see getRoots()
*/
static void getDefaultRoots (StringArray& rootNames, StringArray& rootPaths);
//==============================================================================
/** This abstract base class is implemented by LookAndFeel classes to provide
various file-browser layout and drawing methods.
*/
struct JUCE_API LookAndFeelMethods
{
virtual ~LookAndFeelMethods() {}
// These return a pointer to an internally cached drawable - make sure you don't keep
// a copy of this pointer anywhere, as it may become invalid in the future.
virtual const Drawable* getDefaultFolderImage() = 0;
virtual const Drawable* getDefaultDocumentFileImage() = 0;
virtual AttributedString createFileChooserHeaderText (const String& title,
const String& instructions) = 0;
virtual void drawFileBrowserRow (Graphics&, int width, int height,
const String& filename,
Image* optionalIcon,
const String& fileSizeDescription,
const String& fileTimeDescription,
bool isDirectory,
bool isItemSelected,
int itemIndex,
DirectoryContentsDisplayComponent&) = 0;
virtual Button* createFileBrowserGoUpButton() = 0;
virtual void layoutFileBrowserComponent (FileBrowserComponent& browserComp,
DirectoryContentsDisplayComponent* fileListComponent,
FilePreviewComponent* previewComp,
ComboBox* currentPathBox,
TextEditor* filenameBox,
Button* goUpButton) = 0;
};
//==============================================================================
/** @internal */
void resized() override;
/** @internal */
void buttonClicked (Button*) override;
/** @internal */
void comboBoxChanged (ComboBox*) override;
/** @internal */
void textEditorTextChanged (TextEditor&) override;
/** @internal */
void textEditorReturnKeyPressed (TextEditor&) override;
/** @internal */
void textEditorEscapeKeyPressed (TextEditor&) override;
/** @internal */
void textEditorFocusLost (TextEditor&) override;
/** @internal */
bool keyPressed (const KeyPress&) override;
/** @internal */
void selectionChanged() override;
/** @internal */
void fileClicked (const File&, const MouseEvent&) override;
/** @internal */
void fileDoubleClicked (const File&) override;
/** @internal */
void browserRootChanged (const File&) override;
/** @internal */
bool isFileSuitable (const File&) const override;
/** @internal */
bool isDirectorySuitable (const File&) const override;
/** @internal */
FilePreviewComponent* getPreviewComponent() const noexcept;
/** @internal */
DirectoryContentsDisplayComponent* getDisplayComponent() const noexcept;
protected:
/** Returns a list of names and paths for the default places the user might want to look.
By default this just calls getDefaultRoots(), but you may want to override it to
return a custom list.
*/
virtual void getRoots (StringArray& rootNames, StringArray& rootPaths);
/** Updates the items in the dropdown list of recent paths with the values from getRoots(). */
void resetRecentPaths();
private:
//==============================================================================
ScopedPointer <DirectoryContentsList> fileList;
const FileFilter* fileFilter;
int flags;
File currentRoot;
Array<File> chosenFiles;
ListenerList <FileBrowserListener> listeners;
ScopedPointer<DirectoryContentsDisplayComponent> fileListComponent;
FilePreviewComponent* previewComp;
ComboBox currentPathBox;
TextEditor filenameBox;
Label fileLabel;
ScopedPointer<Button> goUpButton;
TimeSliceThread thread;
void sendListenerChangeMessage();
bool isFileOrDirSuitable (const File& f) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBrowserComponent)
};
#endif // JUCE_FILEBROWSERCOMPONENT_H_INCLUDED

View file

@ -0,0 +1,57 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILEBROWSERLISTENER_H_INCLUDED
#define JUCE_FILEBROWSERLISTENER_H_INCLUDED
//==============================================================================
/**
A listener for user selection events in a file browser.
This is used by a FileBrowserComponent or FileListComponent.
*/
class JUCE_API FileBrowserListener
{
public:
//==============================================================================
/** Destructor. */
virtual ~FileBrowserListener();
//==============================================================================
/** Callback when the user selects a different file in the browser. */
virtual void selectionChanged() = 0;
/** Callback when the user clicks on a file in the browser. */
virtual void fileClicked (const File& file, const MouseEvent& e) = 0;
/** Callback when the user double-clicks on a file in the browser. */
virtual void fileDoubleClicked (const File& file) = 0;
/** Callback when the browser's root folder changes. */
virtual void browserRootChanged (const File& newRoot) = 0;
};
#endif // JUCE_FILEBROWSERLISTENER_H_INCLUDED

View file

@ -0,0 +1,145 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
FileChooser::FileChooser (const String& chooserBoxTitle,
const File& currentFileOrDirectory,
const String& fileFilters,
const bool useNativeBox)
: title (chooserBoxTitle),
filters (fileFilters),
startingFile (currentFileOrDirectory),
useNativeDialogBox (useNativeBox && isPlatformDialogAvailable())
{
if (! fileFilters.containsNonWhitespaceChars())
filters = "*";
}
FileChooser::~FileChooser() {}
#if JUCE_MODAL_LOOPS_PERMITTED
bool FileChooser::browseForFileToOpen (FilePreviewComponent* previewComp)
{
return showDialog (FileBrowserComponent::openMode
| FileBrowserComponent::canSelectFiles,
previewComp);
}
bool FileChooser::browseForMultipleFilesToOpen (FilePreviewComponent* previewComp)
{
return showDialog (FileBrowserComponent::openMode
| FileBrowserComponent::canSelectFiles
| FileBrowserComponent::canSelectMultipleItems,
previewComp);
}
bool FileChooser::browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComp)
{
return showDialog (FileBrowserComponent::openMode
| FileBrowserComponent::canSelectFiles
| FileBrowserComponent::canSelectDirectories
| FileBrowserComponent::canSelectMultipleItems,
previewComp);
}
bool FileChooser::browseForFileToSave (const bool warnAboutOverwrite)
{
return showDialog (FileBrowserComponent::saveMode
| FileBrowserComponent::canSelectFiles
| (warnAboutOverwrite ? FileBrowserComponent::warnAboutOverwriting : 0),
nullptr);
}
bool FileChooser::browseForDirectory()
{
return showDialog (FileBrowserComponent::openMode
| FileBrowserComponent::canSelectDirectories,
nullptr);
}
bool FileChooser::showDialog (const int flags, FilePreviewComponent* const previewComp)
{
FocusRestorer focusRestorer;
results.clear();
// the preview component needs to be the right size before you pass it in here..
jassert (previewComp == nullptr || (previewComp->getWidth() > 10
&& previewComp->getHeight() > 10));
const bool selectsDirectories = (flags & FileBrowserComponent::canSelectDirectories) != 0;
const bool selectsFiles = (flags & FileBrowserComponent::canSelectFiles) != 0;
const bool isSave = (flags & FileBrowserComponent::saveMode) != 0;
const bool warnAboutOverwrite = (flags & FileBrowserComponent::warnAboutOverwriting) != 0;
const bool selectMultiple = (flags & FileBrowserComponent::canSelectMultipleItems) != 0;
// You've set the flags for both saveMode and openMode!
jassert (! (isSave && (flags & FileBrowserComponent::openMode) != 0));
#if JUCE_WINDOWS
if (useNativeDialogBox && ! (selectsFiles && selectsDirectories))
#elif JUCE_MAC || JUCE_LINUX
if (useNativeDialogBox && (previewComp == nullptr))
#else
if (false)
#endif
{
showPlatformDialog (results, title, startingFile, filters,
selectsDirectories, selectsFiles, isSave,
warnAboutOverwrite, selectMultiple, previewComp);
}
else
{
WildcardFileFilter wildcard (selectsFiles ? filters : String::empty,
selectsDirectories ? "*" : String::empty,
String::empty);
FileBrowserComponent browserComponent (flags, startingFile, &wildcard, previewComp);
FileChooserDialogBox box (title, String::empty,
browserComponent, warnAboutOverwrite,
browserComponent.findColour (AlertWindow::backgroundColourId));
if (box.show())
{
for (int i = 0; i < browserComponent.getNumSelectedFiles(); ++i)
results.add (browserComponent.getSelectedFile (i));
}
}
return results.size() > 0;
}
#endif
File FileChooser::getResult() const
{
// if you've used a multiple-file select, you should use the getResults() method
// to retrieve all the files that were chosen.
jassert (results.size() <= 1);
return results.getFirst();
}
//==============================================================================
FilePreviewComponent::FilePreviewComponent() {}
FilePreviewComponent::~FilePreviewComponent() {}

View file

@ -0,0 +1,197 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILECHOOSER_H_INCLUDED
#define JUCE_FILECHOOSER_H_INCLUDED
//==============================================================================
/**
Creates a dialog box to choose a file or directory to load or save.
To use a FileChooser:
- create one (as a local stack variable is the neatest way)
- call one of its browseFor.. methods
- if this returns true, the user has selected a file, so you can retrieve it
with the getResult() method.
e.g. @code
void loadMooseFile()
{
FileChooser myChooser ("Please select the moose you want to load...",
File::getSpecialLocation (File::userHomeDirectory),
"*.moose");
if (myChooser.browseForFileToOpen())
{
File mooseFile (myChooser.getResult());
loadMoose (mooseFile);
}
}
@endcode
*/
class JUCE_API FileChooser
{
public:
//==============================================================================
/** Creates a FileChooser.
After creating one of these, use one of the browseFor... methods to display it.
@param dialogBoxTitle a text string to display in the dialog box to
tell the user what's going on
@param initialFileOrDirectory the file or directory that should be selected when
the dialog box opens. If this parameter is set to
File::nonexistent, a sensible default directory
will be used instead.
@param filePatternsAllowed a set of file patterns to specify which files can be
selected - each pattern should be separated by a
comma or semi-colon, e.g. "*" or "*.jpg;*.gif". An
empty string means that all files are allowed
@param useOSNativeDialogBox if true, then a native dialog box will be used if
possible; if false, then a Juce-based browser dialog
box will always be used
@see browseForFileToOpen, browseForFileToSave, browseForDirectory
*/
FileChooser (const String& dialogBoxTitle,
const File& initialFileOrDirectory = File::nonexistent,
const String& filePatternsAllowed = String::empty,
bool useOSNativeDialogBox = true);
/** Destructor. */
~FileChooser();
//==============================================================================
/** Shows a dialog box to choose a file to open.
This will display the dialog box modally, using an "open file" mode, so that
it won't allow non-existent files or directories to be chosen.
@param previewComponent an optional component to display inside the dialog
box to show special info about the files that the user
is browsing. The component will not be deleted by this
object, so the caller must take care of it.
@returns true if the user selected a file, in which case, use the getResult()
method to find out what it was. Returns false if they cancelled instead.
@see browseForFileToSave, browseForDirectory
*/
bool browseForFileToOpen (FilePreviewComponent* previewComponent = nullptr);
/** Same as browseForFileToOpen, but allows the user to select multiple files.
The files that are returned can be obtained by calling getResults(). See
browseForFileToOpen() for more info about the behaviour of this method.
*/
bool browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent = nullptr);
/** Shows a dialog box to choose a file to save.
This will display the dialog box modally, using an "save file" mode, so it
will allow non-existent files to be chosen, but not directories.
@param warnAboutOverwritingExistingFiles if true, the dialog box will ask
the user if they're sure they want to overwrite a file that already
exists
@returns true if the user chose a file and pressed 'ok', in which case, use
the getResult() method to find out what the file was. Returns false
if they cancelled instead.
@see browseForFileToOpen, browseForDirectory
*/
bool browseForFileToSave (bool warnAboutOverwritingExistingFiles);
/** Shows a dialog box to choose a directory.
This will display the dialog box modally, using an "open directory" mode, so it
will only allow directories to be returned, not files.
@returns true if the user chose a directory and pressed 'ok', in which case, use
the getResult() method to find out what they chose. Returns false
if they cancelled instead.
@see browseForFileToOpen, browseForFileToSave
*/
bool browseForDirectory();
/** Same as browseForFileToOpen, but allows the user to select multiple files and directories.
The files that are returned can be obtained by calling getResults(). See
browseForFileToOpen() for more info about the behaviour of this method.
*/
bool browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComponent = nullptr);
//==============================================================================
/** Runs a dialog box for the given set of option flags.
The flag values used are those in FileBrowserComponent::FileChooserFlags.
@returns true if the user chose a directory and pressed 'ok', in which case, use
the getResult() method to find out what they chose. Returns false
if they cancelled instead.
@see FileBrowserComponent::FileChooserFlags
*/
bool showDialog (int flags, FilePreviewComponent* previewComponent);
//==============================================================================
/** Returns the last file that was chosen by one of the browseFor methods.
After calling the appropriate browseFor... method, this method lets you
find out what file or directory they chose.
Note that the file returned is only valid if the browse method returned true (i.e.
if the user pressed 'ok' rather than cancelling).
If you're using a multiple-file select, then use the getResults() method instead,
to obtain the list of all files chosen.
@see getResults
*/
File getResult() const;
/** Returns a list of all the files that were chosen during the last call to a
browse method.
This array may be empty if no files were chosen, or can contain multiple entries
if multiple files were chosen.
@see getResult
*/
const Array<File>& getResults() const noexcept { return results; }
private:
//==============================================================================
String title, filters;
const File startingFile;
Array<File> results;
const bool useNativeDialogBox;
static void showPlatformDialog (Array<File>& results, const String& title, const File& file,
const String& filters, bool selectsDirectories, bool selectsFiles,
bool isSave, bool warnAboutOverwritingExistingFiles, bool selectMultipleFiles,
FilePreviewComponent* previewComponent);
static bool isPlatformDialogAvailable();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooser)
};
#endif // JUCE_FILECHOOSER_H_INCLUDED

View file

@ -0,0 +1,269 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
class FileChooserDialogBox::ContentComponent : public Component
{
public:
ContentComponent (const String& name, const String& desc, FileBrowserComponent& chooser)
: Component (name),
chooserComponent (chooser),
okButton (chooser.getActionVerb()),
cancelButton (TRANS ("Cancel")),
newFolderButton (TRANS ("New Folder")),
instructions (desc)
{
addAndMakeVisible (chooserComponent);
addAndMakeVisible (okButton);
okButton.addShortcut (KeyPress (KeyPress::returnKey));
addAndMakeVisible (cancelButton);
cancelButton.addShortcut (KeyPress (KeyPress::escapeKey));
addChildComponent (newFolderButton);
setInterceptsMouseClicks (false, true);
}
void paint (Graphics& g) override
{
text.draw (g, getLocalBounds().reduced (6)
.removeFromTop ((int) text.getHeight()).toFloat());
}
void resized() override
{
const int buttonHeight = 26;
Rectangle<int> area (getLocalBounds());
text.createLayout (getLookAndFeel().createFileChooserHeaderText (getName(), instructions),
getWidth() - 12.0f);
area.removeFromTop (roundToInt (text.getHeight()) + 10);
chooserComponent.setBounds (area.removeFromTop (area.getHeight() - buttonHeight - 20));
Rectangle<int> buttonArea (area.reduced (16, 10));
okButton.changeWidthToFitText (buttonHeight);
okButton.setBounds (buttonArea.removeFromRight (okButton.getWidth() + 16));
buttonArea.removeFromRight (16);
cancelButton.changeWidthToFitText (buttonHeight);
cancelButton.setBounds (buttonArea.removeFromRight (cancelButton.getWidth()));
newFolderButton.changeWidthToFitText (buttonHeight);
newFolderButton.setBounds (buttonArea.removeFromLeft (newFolderButton.getWidth()));
}
FileBrowserComponent& chooserComponent;
TextButton okButton, cancelButton, newFolderButton;
private:
String instructions;
TextLayout text;
};
//==============================================================================
FileChooserDialogBox::FileChooserDialogBox (const String& name,
const String& instructions,
FileBrowserComponent& chooserComponent,
const bool warnAboutOverwritingExistingFiles_,
Colour backgroundColour)
: ResizableWindow (name, backgroundColour, true),
warnAboutOverwritingExistingFiles (warnAboutOverwritingExistingFiles_)
{
content = new ContentComponent (name, instructions, chooserComponent);
setContentOwned (content, false);
setResizable (true, true);
setResizeLimits (300, 300, 1200, 1000);
content->okButton.addListener (this);
content->cancelButton.addListener (this);
content->newFolderButton.addListener (this);
content->chooserComponent.addListener (this);
FileChooserDialogBox::selectionChanged();
}
FileChooserDialogBox::~FileChooserDialogBox()
{
content->chooserComponent.removeListener (this);
}
//==============================================================================
#if JUCE_MODAL_LOOPS_PERMITTED
bool FileChooserDialogBox::show (int w, int h)
{
return showAt (-1, -1, w, h);
}
bool FileChooserDialogBox::showAt (int x, int y, int w, int h)
{
if (w <= 0) w = getDefaultWidth();
if (h <= 0) h = 500;
if (x < 0 || y < 0)
centreWithSize (w, h);
else
setBounds (x, y, w, h);
const bool ok = (runModalLoop() != 0);
setVisible (false);
return ok;
}
#endif
void FileChooserDialogBox::centreWithDefaultSize (Component* componentToCentreAround)
{
centreAroundComponent (componentToCentreAround, getDefaultWidth(), 500);
}
int FileChooserDialogBox::getDefaultWidth() const
{
if (Component* const previewComp = content->chooserComponent.getPreviewComponent())
return 400 + previewComp->getWidth();
return 600;
}
//==============================================================================
void FileChooserDialogBox::buttonClicked (Button* button)
{
if (button == &(content->okButton))
{
okButtonPressed();
}
else if (button == &(content->cancelButton))
{
closeButtonPressed();
}
else if (button == &(content->newFolderButton))
{
createNewFolder();
}
}
void FileChooserDialogBox::closeButtonPressed()
{
setVisible (false);
}
void FileChooserDialogBox::selectionChanged()
{
content->okButton.setEnabled (content->chooserComponent.currentFileIsValid());
content->newFolderButton.setVisible (content->chooserComponent.isSaveMode()
&& content->chooserComponent.getRoot().isDirectory());
}
void FileChooserDialogBox::fileDoubleClicked (const File&)
{
selectionChanged();
content->okButton.triggerClick();
}
void FileChooserDialogBox::fileClicked (const File&, const MouseEvent&) {}
void FileChooserDialogBox::browserRootChanged (const File&) {}
void FileChooserDialogBox::okToOverwriteFileCallback (int result, FileChooserDialogBox* box)
{
if (result != 0 && box != nullptr)
box->exitModalState (1);
}
void FileChooserDialogBox::okButtonPressed()
{
if (warnAboutOverwritingExistingFiles
&& content->chooserComponent.isSaveMode()
&& content->chooserComponent.getSelectedFile(0).exists())
{
AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
TRANS("File already exists"),
TRANS("There's already a file called: FLNM")
.replace ("FLNM", content->chooserComponent.getSelectedFile(0).getFullPathName())
+ "\n\n"
+ TRANS("Are you sure you want to overwrite it?"),
TRANS("Overwrite"),
TRANS("Cancel"),
this,
ModalCallbackFunction::forComponent (okToOverwriteFileCallback, this));
}
else
{
exitModalState (1);
}
}
void FileChooserDialogBox::createNewFolderCallback (int result, FileChooserDialogBox* box,
Component::SafePointer<AlertWindow> alert)
{
if (result != 0 && alert != nullptr && box != nullptr)
{
alert->setVisible (false);
box->createNewFolderConfirmed (alert->getTextEditorContents ("Folder Name"));
}
}
void FileChooserDialogBox::createNewFolder()
{
File parent (content->chooserComponent.getRoot());
if (parent.isDirectory())
{
AlertWindow* aw = new AlertWindow (TRANS("New Folder"),
TRANS("Please enter the name for the folder"),
AlertWindow::NoIcon, this);
aw->addTextEditor ("Folder Name", String::empty, String::empty, false);
aw->addButton (TRANS("Create Folder"), 1, KeyPress (KeyPress::returnKey));
aw->addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey));
aw->enterModalState (true,
ModalCallbackFunction::forComponent (createNewFolderCallback, this,
Component::SafePointer<AlertWindow> (aw)),
true);
}
}
void FileChooserDialogBox::createNewFolderConfirmed (const String& nameFromDialog)
{
const String name (File::createLegalFileName (nameFromDialog));
if (! name.isEmpty())
{
const File parent (content->chooserComponent.getRoot());
if (! parent.getChildFile (name).createDirectory())
{
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
TRANS ("New Folder"),
TRANS ("Couldn't create the folder!"));
}
content->chooserComponent.refresh();
}
}

View file

@ -0,0 +1,157 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILECHOOSERDIALOGBOX_H_INCLUDED
#define JUCE_FILECHOOSERDIALOGBOX_H_INCLUDED
//==============================================================================
/**
A file open/save dialog box.
This is a Juce-based file dialog box; to use a native file chooser, see the
FileChooser class.
To use one of these, create it and call its show() method. e.g.
@code
{
WildcardFileFilter wildcardFilter ("*.foo", String::empty, "Foo files");
FileBrowserComponent browser (FileBrowserComponent::canSelectFiles,
File::nonexistent,
&wildcardFilter,
nullptr);
FileChooserDialogBox dialogBox ("Open some kind of file",
"Please choose some kind of file that you want to open...",
browser,
false,
Colours::lightgrey);
if (dialogBox.show())
{
File selectedFile = browser.getSelectedFile (0);
...etc..
}
}
@endcode
@see FileChooser
*/
class JUCE_API FileChooserDialogBox : public ResizableWindow,
private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
private FileBrowserListener
{
public:
//==============================================================================
/** Creates a file chooser box.
@param title the main title to show at the top of the box
@param instructions an optional longer piece of text to show below the title in
a smaller font, describing in more detail what's required.
@param browserComponent a FileBrowserComponent that will be shown inside this dialog
box. Make sure you delete this after (but not before!) the
dialog box has been deleted.
@param warnAboutOverwritingExistingFiles if true, then the user will be asked to confirm
if they try to select a file that already exists. (This
flag is only used when saving files)
@param backgroundColour the background colour for the top level window
@see FileBrowserComponent, FilePreviewComponent
*/
FileChooserDialogBox (const String& title,
const String& instructions,
FileBrowserComponent& browserComponent,
bool warnAboutOverwritingExistingFiles,
Colour backgroundColour);
/** Destructor. */
~FileChooserDialogBox();
//==============================================================================
#if JUCE_MODAL_LOOPS_PERMITTED
/** Displays and runs the dialog box modally.
This will show the box with the specified size, returning true if the user
pressed 'ok', or false if they cancelled.
Leave the width or height as 0 to use the default size
*/
bool show (int width = 0, int height = 0);
/** Displays and runs the dialog box modally.
This will show the box with the specified size at the specified location,
returning true if the user pressed 'ok', or false if they cancelled.
Leave the width or height as 0 to use the default size.
*/
bool showAt (int x, int y, int width, int height);
#endif
/** Sets the size of this dialog box to its default and positions it either in the
centre of the screen, or centred around a component that is provided.
*/
void centreWithDefaultSize (Component* componentToCentreAround = nullptr);
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the box.
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
methods.
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
*/
enum ColourIds
{
titleTextColourId = 0x1000850, /**< The colour to use to draw the box's title. */
};
private:
class ContentComponent;
ContentComponent* content;
const bool warnAboutOverwritingExistingFiles;
void buttonClicked (Button*) override;
void closeButtonPressed();
void selectionChanged() override;
void fileClicked (const File&, const MouseEvent&) override;
void fileDoubleClicked (const File&) override;
void browserRootChanged (const File&) override;
int getDefaultWidth() const;
void okButtonPressed();
void createNewFolder();
void createNewFolderConfirmed (const String& name);
static void okToOverwriteFileCallback (int result, FileChooserDialogBox*);
static void createNewFolderCallback (int result, FileChooserDialogBox*, Component::SafePointer<AlertWindow>);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox)
};
#endif // JUCE_FILECHOOSERDIALOGBOX_H_INCLUDED

View file

@ -0,0 +1,257 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com 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& fc, TimeSliceThread& t)
: owner (fc), thread (t), index (0), highlighted (false)
{
}
~ItemComponent()
{
thread.removeTimeSliceClient (this);
}
//==============================================================================
void paint (Graphics& g) override
{
getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(),
file.getFileName(),
&icon, fileSize, modTime,
isDirectory, highlighted,
index, owner);
}
void mouseDown (const MouseEvent& e) override
{
owner.selectRowsBasedOnModifierKeys (index, e.mods, true);
owner.sendMouseClickMessage (file, e);
}
void mouseDoubleClick (const MouseEvent&) override
{
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() override
{
updateIcon (false);
return -1;
}
void handleAsyncUpdate() override
{
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<ItemComponent*> (existingComponentToUpdate) != nullptr);
ItemComponent* comp = static_cast<ItemComponent*> (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));
}

View file

@ -0,0 +1,96 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILELISTCOMPONENT_H_INCLUDED
#define JUCE_FILELISTCOMPONENT_H_INCLUDED
//==============================================================================
/**
A component that displays the files in a directory as a listbox.
This implements the DirectoryContentsDisplayComponent base class so that
it can be used in a FileBrowserComponent.
To attach a listener to it, use its DirectoryContentsDisplayComponent base
class and the FileBrowserListener class.
@see DirectoryContentsList, FileTreeComponent
*/
class JUCE_API FileListComponent : public ListBox,
public DirectoryContentsDisplayComponent,
private ListBoxModel,
private ChangeListener
{
public:
//==============================================================================
/** Creates a listbox to show the contents of a specified directory.
*/
FileListComponent (DirectoryContentsList& listToShow);
/** Destructor. */
~FileListComponent();
//==============================================================================
/** Returns the number of files the user has got selected.
@see getSelectedFile
*/
int getNumSelectedFiles() const;
/** Returns one of the files that the user has currently selected.
The index should be in the range 0 to (getNumSelectedFiles() - 1).
@see getNumSelectedFiles
*/
File getSelectedFile (int index = 0) const;
/** Deselects any files that are currently selected. */
void deselectAllFiles();
/** Scrolls to the top of the list. */
void scrollToTop();
/** If the specified file is in the list, it will become the only selected item
(and if the file isn't in the list, all other items will be deselected). */
void setSelectedFile (const File&);
private:
//==============================================================================
File lastDirectory;
class ItemComponent;
void changeListenerCallback (ChangeBroadcaster*) override;
int getNumRows() override;
void paintListBoxItem (int, Graphics&, int, int, bool) override;
Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate) override;
void selectedRowsChanged (int lastRowSelected) override;
void deleteKeyPressed (int currentSelectedRow) override;
void returnKeyPressed (int currentSelectedRow) override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListComponent)
};
#endif // JUCE_FILELISTCOMPONENT_H_INCLUDED

View file

@ -0,0 +1,65 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILEPREVIEWCOMPONENT_H_INCLUDED
#define JUCE_FILEPREVIEWCOMPONENT_H_INCLUDED
//==============================================================================
/**
Base class for components that live inside a file chooser dialog box and
show previews of the files that get selected.
One of these allows special extra information to be displayed for files
in a dialog box as the user selects them. Each time the current file or
directory is changed, the selectedFileChanged() method will be called
to allow it to update itself appropriately.
@see FileChooser, ImagePreviewComponent
*/
class JUCE_API FilePreviewComponent : public Component
{
public:
//==============================================================================
/** Creates a FilePreviewComponent. */
FilePreviewComponent();
/** Destructor. */
~FilePreviewComponent();
/** Called to indicate that the user's currently selected file has changed.
@param newSelectedFile the newly selected file or directory, which may be
File::nonexistent if none is selected.
*/
virtual void selectedFileChanged (const File& newSelectedFile) = 0;
private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePreviewComponent)
};
#endif // JUCE_FILEPREVIEWCOMPONENT_H_INCLUDED

View file

@ -0,0 +1,264 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
FileSearchPathListComponent::FileSearchPathListComponent()
: addButton ("+"),
removeButton ("-"),
changeButton (TRANS ("change...")),
upButton (String::empty, DrawableButton::ImageOnButtonBackground),
downButton (String::empty, DrawableButton::ImageOnButtonBackground)
{
listBox.setModel (this);
addAndMakeVisible (listBox);
listBox.setColour (ListBox::backgroundColourId, Colours::black.withAlpha (0.02f));
listBox.setColour (ListBox::outlineColourId, Colours::black.withAlpha (0.1f));
listBox.setOutlineThickness (1);
addAndMakeVisible (addButton);
addButton.addListener (this);
addButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop);
addAndMakeVisible (removeButton);
removeButton.addListener (this);
removeButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop);
addAndMakeVisible (changeButton);
changeButton.addListener (this);
addAndMakeVisible (upButton);
upButton.addListener (this);
{
Path arrowPath;
arrowPath.addArrow (Line<float> (50.0f, 100.0f, 50.0f, 0.0f), 40.0f, 100.0f, 50.0f);
DrawablePath arrowImage;
arrowImage.setFill (Colours::black.withAlpha (0.4f));
arrowImage.setPath (arrowPath);
upButton.setImages (&arrowImage);
}
addAndMakeVisible (downButton);
downButton.addListener (this);
{
Path arrowPath;
arrowPath.addArrow (Line<float> (50.0f, 0.0f, 50.0f, 100.0f), 40.0f, 100.0f, 50.0f);
DrawablePath arrowImage;
arrowImage.setFill (Colours::black.withAlpha (0.4f));
arrowImage.setPath (arrowPath);
downButton.setImages (&arrowImage);
}
updateButtons();
}
FileSearchPathListComponent::~FileSearchPathListComponent()
{
}
void FileSearchPathListComponent::updateButtons()
{
const bool anythingSelected = listBox.getNumSelectedRows() > 0;
removeButton.setEnabled (anythingSelected);
changeButton.setEnabled (anythingSelected);
upButton.setEnabled (anythingSelected);
downButton.setEnabled (anythingSelected);
}
void FileSearchPathListComponent::changed()
{
listBox.updateContent();
listBox.repaint();
updateButtons();
}
//==============================================================================
void FileSearchPathListComponent::setPath (const FileSearchPath& newPath)
{
if (newPath.toString() != path.toString())
{
path = newPath;
changed();
}
}
void FileSearchPathListComponent::setDefaultBrowseTarget (const File& newDefaultDirectory)
{
defaultBrowseTarget = newDefaultDirectory;
}
//==============================================================================
int FileSearchPathListComponent::getNumRows()
{
return path.getNumPaths();
}
void FileSearchPathListComponent::paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected)
{
if (rowIsSelected)
g.fillAll (findColour (TextEditor::highlightColourId));
g.setColour (findColour (ListBox::textColourId));
Font f (height * 0.7f);
f.setHorizontalScale (0.9f);
g.setFont (f);
g.drawText (path [rowNumber].getFullPathName(),
4, 0, width - 6, height,
Justification::centredLeft, true);
}
void FileSearchPathListComponent::deleteKeyPressed (int row)
{
if (isPositiveAndBelow (row, path.getNumPaths()))
{
path.remove (row);
changed();
}
}
void FileSearchPathListComponent::returnKeyPressed (int row)
{
#if JUCE_MODAL_LOOPS_PERMITTED
FileChooser chooser (TRANS("Change folder..."), path [row], "*");
if (chooser.browseForDirectory())
{
path.remove (row);
path.add (chooser.getResult(), row);
changed();
}
#endif
}
void FileSearchPathListComponent::listBoxItemDoubleClicked (int row, const MouseEvent&)
{
returnKeyPressed (row);
}
void FileSearchPathListComponent::selectedRowsChanged (int)
{
updateButtons();
}
void FileSearchPathListComponent::paint (Graphics& g)
{
g.fillAll (findColour (backgroundColourId));
}
void FileSearchPathListComponent::resized()
{
const int buttonH = 22;
const int buttonY = getHeight() - buttonH - 4;
listBox.setBounds (2, 2, getWidth() - 4, buttonY - 5);
addButton.setBounds (2, buttonY, buttonH, buttonH);
removeButton.setBounds (addButton.getRight(), buttonY, buttonH, buttonH);
changeButton.changeWidthToFitText (buttonH);
downButton.setSize (buttonH * 2, buttonH);
upButton.setSize (buttonH * 2, buttonH);
downButton.setTopRightPosition (getWidth() - 2, buttonY);
upButton.setTopRightPosition (downButton.getX() - 4, buttonY);
changeButton.setTopRightPosition (upButton.getX() - 8, buttonY);
}
bool FileSearchPathListComponent::isInterestedInFileDrag (const StringArray&)
{
return true;
}
void FileSearchPathListComponent::filesDropped (const StringArray& filenames, int, int mouseY)
{
for (int i = filenames.size(); --i >= 0;)
{
const File f (filenames[i]);
if (f.isDirectory())
{
const int row = listBox.getRowContainingPosition (0, mouseY - listBox.getY());
path.add (f, row);
changed();
}
}
}
void FileSearchPathListComponent::buttonClicked (Button* button)
{
const int currentRow = listBox.getSelectedRow();
if (button == &removeButton)
{
deleteKeyPressed (currentRow);
}
else if (button == &addButton)
{
File start (defaultBrowseTarget);
if (start == File::nonexistent)
start = path [0];
if (start == File::nonexistent)
start = File::getCurrentWorkingDirectory();
#if JUCE_MODAL_LOOPS_PERMITTED
FileChooser chooser (TRANS("Add a folder..."), start, "*");
if (chooser.browseForDirectory())
path.add (chooser.getResult(), currentRow);
#else
jassertfalse; // needs rewriting to deal with non-modal environments
#endif
}
else if (button == &changeButton)
{
returnKeyPressed (currentRow);
}
else if (button == &upButton)
{
if (currentRow > 0 && currentRow < path.getNumPaths())
{
const File f (path[currentRow]);
path.remove (currentRow);
path.add (f, currentRow - 1);
listBox.selectRow (currentRow - 1);
}
}
else if (button == &downButton)
{
if (currentRow >= 0 && currentRow < path.getNumPaths() - 1)
{
const File f (path[currentRow]);
path.remove (currentRow);
path.add (f, currentRow + 1);
listBox.selectRow (currentRow + 1);
}
}
changed();
}

View file

@ -0,0 +1,116 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILESEARCHPATHLISTCOMPONENT_H_INCLUDED
#define JUCE_FILESEARCHPATHLISTCOMPONENT_H_INCLUDED
//==============================================================================
/**
Shows a set of file paths in a list, allowing them to be added, removed or
re-ordered.
@see FileSearchPath
*/
class JUCE_API FileSearchPathListComponent : public Component,
public SettableTooltipClient,
public FileDragAndDropTarget,
private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
private ListBoxModel
{
public:
//==============================================================================
/** Creates an empty FileSearchPathListComponent. */
FileSearchPathListComponent();
/** Destructor. */
~FileSearchPathListComponent();
//==============================================================================
/** Returns the path as it is currently shown. */
const FileSearchPath& getPath() const noexcept { return path; }
/** Changes the current path. */
void setPath (const FileSearchPath& newPath);
/** Sets a file or directory to be the default starting point for the browser to show.
This is only used if the current file hasn't been set.
*/
void setDefaultBrowseTarget (const File& newDefaultDirectory);
/** A set of colour IDs to use to change the colour of various aspects of the label.
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
methods.
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
*/
enum ColourIds
{
backgroundColourId = 0x1004100, /**< The background colour to fill the component with.
Make this transparent if you don't want the background to be filled. */
};
//==============================================================================
/** @internal */
int getNumRows() override;
/** @internal */
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override;
/** @internal */
void deleteKeyPressed (int lastRowSelected) override;
/** @internal */
void returnKeyPressed (int lastRowSelected) override;
/** @internal */
void listBoxItemDoubleClicked (int row, const MouseEvent&) override;
/** @internal */
void selectedRowsChanged (int lastRowSelected) override;
/** @internal */
void resized() override;
/** @internal */
void paint (Graphics&) override;
/** @internal */
bool isInterestedInFileDrag (const StringArray&) override;
/** @internal */
void filesDropped (const StringArray& files, int, int) override;
/** @internal */
void buttonClicked (Button*) override;
private:
//==============================================================================
FileSearchPath path;
File defaultBrowseTarget;
ListBox listBox;
TextButton addButton, removeButton, changeButton;
DrawableButton upButton, downButton;
void changed();
void updateButtons();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileSearchPathListComponent)
};
#endif // JUCE_FILESEARCHPATHLISTCOMPONENT_H_INCLUDED

View file

@ -0,0 +1,322 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
Image juce_createIconForFile (const File&);
//==============================================================================
class FileListTreeItem : public TreeViewItem,
private TimeSliceClient,
private AsyncUpdater,
private ChangeListener
{
public:
FileListTreeItem (FileTreeComponent& treeComp,
DirectoryContentsList* const parentContents,
const int indexInContents,
const File& f,
TimeSliceThread& t)
: file (f),
owner (treeComp),
parentContentsList (parentContents),
indexInContentsList (indexInContents),
subContentsList (nullptr, false),
thread (t)
{
DirectoryContentsList::FileInfo fileInfo;
if (parentContents != nullptr
&& parentContents->getFileInfo (indexInContents, fileInfo))
{
fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize);
modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M");
isDirectory = fileInfo.isDirectory;
}
else
{
isDirectory = true;
}
}
~FileListTreeItem()
{
thread.removeTimeSliceClient (this);
clearSubItems();
removeSubContentsList();
}
//==============================================================================
bool mightContainSubItems() override { return isDirectory; }
String getUniqueName() const override { return file.getFullPathName(); }
int getItemHeight() const override { return owner.getItemHeight(); }
var getDragSourceDescription() override { return owner.getDragAndDropDescription(); }
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen)
{
clearSubItems();
isDirectory = file.isDirectory();
if (isDirectory)
{
if (subContentsList == nullptr)
{
jassert (parentContentsList != nullptr);
DirectoryContentsList* const l = new DirectoryContentsList (parentContentsList->getFilter(), thread);
l->setDirectory (file,
parentContentsList->isFindingDirectories(),
parentContentsList->isFindingFiles());
setSubContentsList (l, true);
}
changeListenerCallback (nullptr);
}
}
}
void removeSubContentsList()
{
if (subContentsList != nullptr)
{
subContentsList->removeChangeListener (this);
subContentsList.clear();
}
}
void setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList)
{
removeSubContentsList();
OptionalScopedPointer<DirectoryContentsList> newPointer (newList, canDeleteList);
subContentsList = newPointer;
newList->addChangeListener (this);
}
bool selectFile (const File& target)
{
if (file == target)
{
setSelected (true, true);
return true;
}
if (target.isAChildOf (file))
{
setOpen (true);
for (int maxRetries = 500; --maxRetries > 0;)
{
for (int i = 0; i < getNumSubItems(); ++i)
if (FileListTreeItem* f = dynamic_cast <FileListTreeItem*> (getSubItem (i)))
if (f->selectFile (target))
return true;
// if we've just opened and the contents are still loading, wait for it..
if (subContentsList != nullptr && subContentsList->isStillLoading())
{
Thread::sleep (10);
rebuildItemsFromContentList();
}
else
{
break;
}
}
}
return false;
}
void changeListenerCallback (ChangeBroadcaster*) override
{
rebuildItemsFromContentList();
}
void rebuildItemsFromContentList()
{
clearSubItems();
if (isOpen() && subContentsList != nullptr)
{
for (int i = 0; i < subContentsList->getNumFiles(); ++i)
addSubItem (new FileListTreeItem (owner, subContentsList, i,
subContentsList->getFile(i), thread));
}
}
void paintItem (Graphics& g, int width, int height) override
{
if (file != File::nonexistent)
{
updateIcon (true);
if (icon.isNull())
thread.addTimeSliceClient (this);
}
owner.getLookAndFeel().drawFileBrowserRow (g, width, height,
file.getFileName(),
&icon, fileSize, modTime,
isDirectory, isSelected(),
indexInContentsList, owner);
}
void itemClicked (const MouseEvent& e) override
{
owner.sendMouseClickMessage (file, e);
}
void itemDoubleClicked (const MouseEvent& e) override
{
TreeViewItem::itemDoubleClicked (e);
owner.sendDoubleClickMessage (file);
}
void itemSelectionChanged (bool) override
{
owner.sendSelectionChangeMessage();
}
int useTimeSlice() override
{
updateIcon (false);
return -1;
}
void handleAsyncUpdate() override
{
owner.repaint();
}
const File file;
private:
FileTreeComponent& owner;
DirectoryContentsList* parentContentsList;
int indexInContentsList;
OptionalScopedPointer<DirectoryContentsList> subContentsList;
bool isDirectory;
TimeSliceThread& thread;
Image icon;
String fileSize, modTime;
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 (FileListTreeItem)
};
//==============================================================================
FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
: DirectoryContentsDisplayComponent (listToShow),
itemHeight (22)
{
setRootItemVisible (false);
refresh();
}
FileTreeComponent::~FileTreeComponent()
{
deleteRootItem();
}
void FileTreeComponent::refresh()
{
deleteRootItem();
FileListTreeItem* const root
= new FileListTreeItem (*this, nullptr, 0, fileList.getDirectory(),
fileList.getTimeSliceThread());
root->setSubContentsList (&fileList, false);
setRootItem (root);
}
//==============================================================================
File FileTreeComponent::getSelectedFile (const int index) const
{
if (const FileListTreeItem* const item = dynamic_cast <const FileListTreeItem*> (getSelectedItem (index)))
return item->file;
return File::nonexistent;
}
void FileTreeComponent::deselectAllFiles()
{
clearSelectedItems();
}
void FileTreeComponent::scrollToTop()
{
getViewport()->getVerticalScrollBar()->setCurrentRangeStart (0);
}
void FileTreeComponent::setDragAndDropDescription (const String& description)
{
dragAndDropDescription = description;
}
void FileTreeComponent::setSelectedFile (const File& target)
{
if (FileListTreeItem* t = dynamic_cast <FileListTreeItem*> (getRootItem()))
if (! t->selectFile (target))
clearSelectedItems();
}
void FileTreeComponent::setItemHeight (int newHeight)
{
if (itemHeight != newHeight)
{
itemHeight = newHeight;
if (TreeViewItem* root = getRootItem())
root->treeHasChanged();
}
}

View file

@ -0,0 +1,104 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILETREECOMPONENT_H_INCLUDED
#define JUCE_FILETREECOMPONENT_H_INCLUDED
//==============================================================================
/**
A component that displays the files in a directory as a treeview.
This implements the DirectoryContentsDisplayComponent base class so that
it can be used in a FileBrowserComponent.
To attach a listener to it, use its DirectoryContentsDisplayComponent base
class and the FileBrowserListener class.
@see DirectoryContentsList, FileListComponent
*/
class JUCE_API FileTreeComponent : public TreeView,
public DirectoryContentsDisplayComponent
{
public:
//==============================================================================
/** Creates a listbox to show the contents of a specified directory.
*/
FileTreeComponent (DirectoryContentsList& listToShow);
/** Destructor. */
~FileTreeComponent();
//==============================================================================
/** Returns the number of files the user has got selected.
@see getSelectedFile
*/
int getNumSelectedFiles() const { return TreeView::getNumSelectedItems(); }
/** Returns one of the files that the user has currently selected.
The index should be in the range 0 to (getNumSelectedFiles() - 1).
@see getNumSelectedFiles
*/
File getSelectedFile (int index = 0) const;
/** Deselects any files that are currently selected. */
void deselectAllFiles();
/** Scrolls the list to the top. */
void scrollToTop();
/** If the specified file is in the list, it will become the only selected item
(and if the file isn't in the list, all other items will be deselected). */
void setSelectedFile (const File&);
/** Updates the files in the list. */
void refresh();
/** Setting a name for this allows tree items to be dragged.
The string that you pass in here will be returned by the getDragSourceDescription()
of the items in the tree. For more info, see TreeViewItem::getDragSourceDescription().
*/
void setDragAndDropDescription (const String& description);
/** Returns the last value that was set by setDragAndDropDescription().
*/
const String& getDragAndDropDescription() const noexcept { return dragAndDropDescription; }
/** Changes the height of the treeview items. */
void setItemHeight (int newHeight);
/** Returns the height of the treeview items. */
int getItemHeight() const noexcept { return itemHeight; }
private:
//==============================================================================
String dragAndDropDescription;
int itemHeight;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileTreeComponent)
};
#endif // JUCE_FILETREECOMPONENT_H_INCLUDED

View file

@ -0,0 +1,267 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
FilenameComponent::FilenameComponent (const String& name,
const File& currentFile,
const bool canEditFilename,
const bool isDirectory,
const bool isForSaving,
const String& fileBrowserWildcard,
const String& suffix,
const String& textWhenNothingSelected)
: Component (name),
maxRecentFiles (30),
isDir (isDirectory),
isSaving (isForSaving),
isFileDragOver (false),
wildcard (fileBrowserWildcard),
enforcedSuffix (suffix)
{
addAndMakeVisible (filenameBox);
filenameBox.setEditableText (canEditFilename);
filenameBox.addListener (this);
filenameBox.setTextWhenNothingSelected (textWhenNothingSelected);
filenameBox.setTextWhenNoChoicesAvailable (TRANS ("(no recently selected files)"));
setBrowseButtonText ("...");
setCurrentFile (currentFile, true, dontSendNotification);
}
FilenameComponent::~FilenameComponent()
{
}
//==============================================================================
void FilenameComponent::paintOverChildren (Graphics& g)
{
if (isFileDragOver)
{
g.setColour (Colours::red.withAlpha (0.2f));
g.drawRect (getLocalBounds(), 3);
}
}
void FilenameComponent::resized()
{
getLookAndFeel().layoutFilenameComponent (*this, &filenameBox, browseButton);
}
KeyboardFocusTraverser* FilenameComponent::createFocusTraverser()
{
// This prevents the sub-components from grabbing focus if the
// FilenameComponent has been set to refuse focus.
return getWantsKeyboardFocus() ? Component::createFocusTraverser() : nullptr;
}
void FilenameComponent::setBrowseButtonText (const String& newBrowseButtonText)
{
browseButtonText = newBrowseButtonText;
lookAndFeelChanged();
}
void FilenameComponent::lookAndFeelChanged()
{
browseButton = nullptr;
addAndMakeVisible (browseButton = getLookAndFeel().createFilenameComponentBrowseButton (browseButtonText));
browseButton->setConnectedEdges (Button::ConnectedOnLeft);
resized();
browseButton->addListener (this);
}
void FilenameComponent::setTooltip (const String& newTooltip)
{
SettableTooltipClient::setTooltip (newTooltip);
filenameBox.setTooltip (newTooltip);
}
void FilenameComponent::setDefaultBrowseTarget (const File& newDefaultDirectory)
{
defaultBrowseFile = newDefaultDirectory;
}
File FilenameComponent::getLocationToBrowse()
{
return getCurrentFile() == File::nonexistent ? defaultBrowseFile
: getCurrentFile();
}
void FilenameComponent::buttonClicked (Button*)
{
#if JUCE_MODAL_LOOPS_PERMITTED
FileChooser fc (isDir ? TRANS ("Choose a new directory")
: TRANS ("Choose a new file"),
getLocationToBrowse(),
wildcard);
if (isDir ? fc.browseForDirectory()
: (isSaving ? fc.browseForFileToSave (false)
: fc.browseForFileToOpen()))
{
setCurrentFile (fc.getResult(), true);
}
#else
jassertfalse; // needs rewriting to deal with non-modal environments
#endif
}
void FilenameComponent::comboBoxChanged (ComboBox*)
{
setCurrentFile (getCurrentFile(), true);
}
bool FilenameComponent::isInterestedInFileDrag (const StringArray&)
{
return true;
}
void FilenameComponent::filesDropped (const StringArray& filenames, int, int)
{
isFileDragOver = false;
repaint();
const File f (filenames[0]);
if (f.exists() && (f.isDirectory() == isDir))
setCurrentFile (f, true);
}
void FilenameComponent::fileDragEnter (const StringArray&, int, int)
{
isFileDragOver = true;
repaint();
}
void FilenameComponent::fileDragExit (const StringArray&)
{
isFileDragOver = false;
repaint();
}
//==============================================================================
String FilenameComponent::getCurrentFileText() const
{
return filenameBox.getText();
}
File FilenameComponent::getCurrentFile() const
{
File f (File::getCurrentWorkingDirectory().getChildFile (getCurrentFileText()));
if (enforcedSuffix.isNotEmpty())
f = f.withFileExtension (enforcedSuffix);
return f;
}
void FilenameComponent::setCurrentFile (File newFile,
const bool addToRecentlyUsedList,
NotificationType notification)
{
if (enforcedSuffix.isNotEmpty())
newFile = newFile.withFileExtension (enforcedSuffix);
if (newFile.getFullPathName() != lastFilename)
{
lastFilename = newFile.getFullPathName();
if (addToRecentlyUsedList)
addRecentlyUsedFile (newFile);
filenameBox.setText (lastFilename, dontSendNotification);
if (notification != dontSendNotification)
{
triggerAsyncUpdate();
if (notification == sendNotificationSync)
handleUpdateNowIfNeeded();
}
}
}
void FilenameComponent::setFilenameIsEditable (const bool shouldBeEditable)
{
filenameBox.setEditableText (shouldBeEditable);
}
StringArray FilenameComponent::getRecentlyUsedFilenames() const
{
StringArray names;
for (int i = 0; i < filenameBox.getNumItems(); ++i)
names.add (filenameBox.getItemText (i));
return names;
}
void FilenameComponent::setRecentlyUsedFilenames (const StringArray& filenames)
{
if (filenames != getRecentlyUsedFilenames())
{
filenameBox.clear();
for (int i = 0; i < jmin (filenames.size(), maxRecentFiles); ++i)
filenameBox.addItem (filenames[i], i + 1);
}
}
void FilenameComponent::setMaxNumberOfRecentFiles (const int newMaximum)
{
maxRecentFiles = jmax (1, newMaximum);
setRecentlyUsedFilenames (getRecentlyUsedFilenames());
}
void FilenameComponent::addRecentlyUsedFile (const File& file)
{
StringArray files (getRecentlyUsedFilenames());
if (file.getFullPathName().isNotEmpty())
{
files.removeString (file.getFullPathName(), true);
files.insert (0, file.getFullPathName());
setRecentlyUsedFilenames (files);
}
}
//==============================================================================
void FilenameComponent::addListener (FilenameComponentListener* const listener)
{
listeners.add (listener);
}
void FilenameComponent::removeListener (FilenameComponentListener* const listener)
{
listeners.remove (listener);
}
void FilenameComponent::handleAsyncUpdate()
{
Component::BailOutChecker checker (this);
listeners.callChecked (checker, &FilenameComponentListener::filenameComponentChanged, this);
}

View file

@ -0,0 +1,234 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_FILENAMECOMPONENT_H_INCLUDED
#define JUCE_FILENAMECOMPONENT_H_INCLUDED
//==============================================================================
/**
Listens for events happening to a FilenameComponent.
Use FilenameComponent::addListener() and FilenameComponent::removeListener() to
register one of these objects for event callbacks when the filename is changed.
@see FilenameComponent
*/
class JUCE_API FilenameComponentListener
{
public:
/** Destructor. */
virtual ~FilenameComponentListener() {}
/** This method is called after the FilenameComponent's file has been changed. */
virtual void filenameComponentChanged (FilenameComponent* fileComponentThatHasChanged) = 0;
};
//==============================================================================
/**
Shows a filename as an editable text box, with a 'browse' button and a
drop-down list for recently selected files.
A handy component for dialogue boxes where you want the user to be able to
select a file or directory.
Attach an FilenameComponentListener using the addListener() method, and it will
get called each time the user changes the filename, either by browsing for a file
and clicking 'ok', or by typing a new filename into the box and pressing return.
@see FileChooser, ComboBox
*/
class JUCE_API FilenameComponent : public Component,
public SettableTooltipClient,
public FileDragAndDropTarget,
private AsyncUpdater,
private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
private ComboBoxListener
{
public:
//==============================================================================
/** Creates a FilenameComponent.
@param name the name for this component.
@param currentFile the file to initially show in the box
@param canEditFilename if true, the user can manually edit the filename; if false,
they can only change it by browsing for a new file
@param isDirectory if true, the file will be treated as a directory, and
an appropriate directory browser used
@param isForSaving if true, the file browser will allow non-existent files to
be picked, as the file is assumed to be used for saving rather
than loading
@param fileBrowserWildcard a wildcard pattern to use in the file browser - e.g. "*.txt;*.foo".
If an empty string is passed in, then the pattern is assumed to be "*"
@param enforcedSuffix if this is non-empty, it is treated as a suffix that will be added
to any filenames that are entered or chosen
@param textWhenNothingSelected the message to display in the box before any filename is entered. (This
will only appear if the initial file isn't valid)
*/
FilenameComponent (const String& name,
const File& currentFile,
bool canEditFilename,
bool isDirectory,
bool isForSaving,
const String& fileBrowserWildcard,
const String& enforcedSuffix,
const String& textWhenNothingSelected);
/** Destructor. */
~FilenameComponent();
//==============================================================================
/** Returns the currently displayed filename. */
File getCurrentFile() const;
/** Returns the raw text that the user has entered. */
String getCurrentFileText() const;
/** Changes the current filename.
@param newFile the new filename to use
@param addToRecentlyUsedList if true, the filename will also be added to the
drop-down list of recent files.
@param notification whether to send a notification of the change to listeners
*/
void setCurrentFile (File newFile,
bool addToRecentlyUsedList,
NotificationType notification = sendNotificationAsync);
/** Changes whether the use can type into the filename box.
*/
void setFilenameIsEditable (bool shouldBeEditable);
/** Sets a file or directory to be the default starting point for the browser to show.
This is only used if the current file hasn't been set.
*/
void setDefaultBrowseTarget (const File& newDefaultDirectory);
/** This can be overridden to return a custom location that you want the dialog box
to show when the browse button is pushed.
The default implementation of this method will return either the current file
(if one has been chosen) or the location that was set by setDefaultBrowseTarget().
*/
virtual File getLocationToBrowse();
/** Returns all the entries on the recent files list.
This can be used in conjunction with setRecentlyUsedFilenames() for saving the
state of this list.
@see setRecentlyUsedFilenames
*/
StringArray getRecentlyUsedFilenames() const;
/** Sets all the entries on the recent files list.
This can be used in conjunction with getRecentlyUsedFilenames() for saving the
state of this list.
@see getRecentlyUsedFilenames, addRecentlyUsedFile
*/
void setRecentlyUsedFilenames (const StringArray& filenames);
/** Adds an entry to the recently-used files dropdown list.
If the file is already in the list, it will be moved to the top. A limit
is also placed on the number of items that are kept in the list.
@see getRecentlyUsedFilenames, setRecentlyUsedFilenames, setMaxNumberOfRecentFiles
*/
void addRecentlyUsedFile (const File& file);
/** Changes the limit for the number of files that will be stored in the recent-file list.
*/
void setMaxNumberOfRecentFiles (int newMaximum);
/** Changes the text shown on the 'browse' button.
By default this button just says "..." but you can change it. The button itself
can be changed using the look-and-feel classes, so it might not actually have any
text on it.
*/
void setBrowseButtonText (const String& browseButtonText);
//==============================================================================
/** Adds a listener that will be called when the selected file is changed. */
void addListener (FilenameComponentListener* listener);
/** Removes a previously-registered listener. */
void removeListener (FilenameComponentListener* listener);
/** Gives the component a tooltip. */
void setTooltip (const String& newTooltip) override;
//==============================================================================
/** This abstract base class is implemented by LookAndFeel classes. */
struct JUCE_API LookAndFeelMethods
{
virtual ~LookAndFeelMethods() {}
virtual Button* createFilenameComponentBrowseButton (const String& text) = 0;
virtual void layoutFilenameComponent (FilenameComponent&, ComboBox* filenameBox, Button* browseButton) = 0;
};
//==============================================================================
/** @internal */
void paintOverChildren (Graphics&) override;
/** @internal */
void resized() override;
/** @internal */
void lookAndFeelChanged() override;
/** @internal */
bool isInterestedInFileDrag (const StringArray&) override;
/** @internal */
void filesDropped (const StringArray&, int, int) override;
/** @internal */
void fileDragEnter (const StringArray&, int, int) override;
/** @internal */
void fileDragExit (const StringArray&) override;
/** @internal */
KeyboardFocusTraverser* createFocusTraverser() override;
private:
//==============================================================================
ComboBox filenameBox;
String lastFilename;
ScopedPointer<Button> browseButton;
int maxRecentFiles;
bool isDir, isSaving, isFileDragOver;
String wildcard, enforcedSuffix, browseButtonText;
ListenerList <FilenameComponentListener> listeners;
File defaultBrowseFile;
void comboBoxChanged (ComboBox*) override;
void buttonClicked (Button*) override;
void handleAsyncUpdate() override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilenameComponent)
};
#endif // JUCE_FILENAMECOMPONENT_H_INCLUDED

View file

@ -0,0 +1,114 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
ImagePreviewComponent::ImagePreviewComponent()
{
}
ImagePreviewComponent::~ImagePreviewComponent()
{
}
//==============================================================================
void ImagePreviewComponent::getThumbSize (int& w, int& h) const
{
const int availableW = proportionOfWidth (0.97f);
const int availableH = getHeight() - 13 * 4;
const double scale = jmin (1.0,
availableW / (double) w,
availableH / (double) h);
w = roundToInt (scale * w);
h = roundToInt (scale * h);
}
void ImagePreviewComponent::selectedFileChanged (const File& file)
{
if (fileToLoad != file)
{
fileToLoad = file;
startTimer (100);
}
}
void ImagePreviewComponent::timerCallback()
{
stopTimer();
currentThumbnail = Image::null;
currentDetails.clear();
repaint();
ScopedPointer<FileInputStream> in (fileToLoad.createInputStream());
if (in != nullptr)
{
if (ImageFileFormat* const format = ImageFileFormat::findImageFormatForStream (*in))
{
currentThumbnail = format->decodeImage (*in);
if (currentThumbnail.isValid())
{
int w = currentThumbnail.getWidth();
int h = currentThumbnail.getHeight();
currentDetails
<< fileToLoad.getFileName() << "\n"
<< format->getFormatName() << "\n"
<< w << " x " << h << " pixels\n"
<< File::descriptionOfSizeInBytes (fileToLoad.getSize());
getThumbSize (w, h);
currentThumbnail = currentThumbnail.rescaled (w, h);
}
}
}
}
void ImagePreviewComponent::paint (Graphics& g)
{
if (currentThumbnail.isValid())
{
g.setFont (13.0f);
int w = currentThumbnail.getWidth();
int h = currentThumbnail.getHeight();
getThumbSize (w, h);
const int numLines = 4;
const int totalH = 13 * numLines + h + 4;
const int y = (getHeight() - totalH) / 2;
g.drawImageWithin (currentThumbnail,
(getWidth() - w) / 2, y, w, h,
RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize,
false);
g.drawFittedText (currentDetails,
0, y + h + 4, getWidth(), 100,
Justification::centredTop, numLines);
}
}

View file

@ -0,0 +1,66 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found 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.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_IMAGEPREVIEWCOMPONENT_H_INCLUDED
#define JUCE_IMAGEPREVIEWCOMPONENT_H_INCLUDED
//==============================================================================
/**
A simple preview component that shows thumbnails of image files.
@see FileChooserDialogBox, FilePreviewComponent
*/
class JUCE_API ImagePreviewComponent : public FilePreviewComponent,
private Timer
{
public:
//==============================================================================
/** Creates an ImagePreviewComponent. */
ImagePreviewComponent();
/** Destructor. */
~ImagePreviewComponent();
//==============================================================================
/** @internal */
void selectedFileChanged (const File& newSelectedFile) override;
/** @internal */
void paint (Graphics&) override;
/** @internal */
void timerCallback() override;
private:
File fileToLoad;
Image currentThumbnail;
String currentDetails;
void getThumbSize (int& w, int& h) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePreviewComponent)
};
#endif // JUCE_IMAGEPREVIEWCOMPONENT_H_INCLUDED