1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-31 03:00:05 +00:00

Added multiple-selection, and the ability to browse for both files and folders to the Juce file chooser dialog classes. This has involved changing a few methods, so if you're using these classes directly, you may need to tweak your code, but it should be very easy to do.

This commit is contained in:
Julian Storer 2009-12-08 16:07:47 +00:00
parent 71a12a140e
commit 1f6d9ec92c
13 changed files with 602 additions and 307 deletions

View file

@ -3911,6 +3911,27 @@ DynamicObject* var::getObject() const throw()
return type == objectType ? value.objectValue : 0;
}
bool var::operator== (const var& other) const throw()
{
switch (type)
{
case voidType: return other.isVoid();
case intType: return value.intValue == (int) other;
case boolType: return value.boolValue == (bool) other;
case doubleType: return value.doubleValue == (double) other;
case stringType: return (*(value.stringValue)) == other.toString();
case objectType: return value.objectValue == other.getObject();
default: jassertfalse; break;
}
return false;
}
bool var::operator!= (const var& other) const throw()
{
return ! operator== (other);
}
const var var::operator[] (const var::identifier& propertyName) const throw()
{
if (type == objectType && value.objectValue != 0)
@ -15311,7 +15332,7 @@ const String XmlElement::getText() const throw()
{
jassert (isTextElement()); // you're trying to get the text from an element that
// isn't actually a text element.. If this contains text sub-nodes, you
// can use getAllSubText instead to
// probably want to use getAllSubText instead.
return getStringAttribute (juce_xmltextContentAttributeName);
}
@ -45460,14 +45481,14 @@ namespace CppTokeniser
static bool isIdentifierStart (const tchar c) throw()
{
return CharacterFunctions::isLetter (c)
|| c == T('_');
|| c == T('_') || c == T('@');
}
static bool isIdentifierBody (const tchar c) throw()
{
return CharacterFunctions::isLetter (c)
|| CharacterFunctions::isDigit (c)
|| c == T('_');
|| c == T('_') || c == T('@');
}
static int parseIdentifier (CodeDocument::Iterator& source) throw()
@ -45494,7 +45515,9 @@ static int parseIdentifier (CodeDocument::Iterator& source) throw()
static const tchar* keywordsOther[] =
{ T("const_cast"), T("continue"), T("default"), T("explicit"), T("mutable"), T("namespace"),
T("operator"), T("private"), T("protected"), T("register"), T("reinterpret_cast"), T("static_cast"),
T("template"), T("typedef"), T("typename"), T("unsigned"), T("virtual"), T("volatile"), 0 };
T("template"), T("typedef"), T("typename"), T("unsigned"), T("virtual"), T("volatile"),
T("@implementation"), T("@interface"), T("@end"), T("@synthesize"), T("@dynamic"), T("@public"),
T("@private"), T("@property"), T("@protected"), T("@class"), 0 };
int tokenLength = 0;
tchar possibleIdentifier [19];
@ -56413,27 +56436,24 @@ END_JUCE_NAMESPACE
BEGIN_JUCE_NAMESPACE
class DirectoriesOnlyFilter : public FileFilter
{
public:
DirectoriesOnlyFilter() : FileFilter (String::empty) {}
bool isFileSuitable (const File&) const { return false; }
bool isDirectorySuitable (const File&) const { return true; }
};
FileBrowserComponent::FileBrowserComponent (FileChooserMode mode_,
FileBrowserComponent::FileBrowserComponent (int flags_,
const File& initialFileOrDirectory,
const FileFilter* fileFilter,
FilePreviewComponent* previewComp_,
const bool useTreeView,
const bool filenameTextBoxIsReadOnly)
: directoriesOnlyFilter (0),
mode (mode_),
const FileFilter* fileFilter_,
FilePreviewComponent* previewComp_)
: FileFilter (String::empty),
fileFilter (fileFilter_),
flags (flags_),
listeners (2),
previewComp (previewComp_),
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)
@ -56446,18 +56466,20 @@ FileBrowserComponent::FileBrowserComponent (FileChooserMode mode_,
}
else
{
chosenFiles.add (new File (initialFileOrDirectory));
currentRoot = initialFileOrDirectory.getParentDirectory();
filename = initialFileOrDirectory.getFileName();
}
if (mode_ == chooseDirectoryMode)
fileFilter = directoriesOnlyFilter = new DirectoriesOnlyFilter();
fileList = new DirectoryContentsList (this, thread);
fileList = new DirectoryContentsList (fileFilter, thread);
if (useTreeView)
if ((flags & useTreeView) != 0)
{
FileTreeComponent* const tree = new FileTreeComponent (*fileList);
if ((flags & canSelectMultipleItems) != 0)
tree->setMultiSelectEnabled (true);
addAndMakeVisible (tree);
fileListComponent = tree;
}
@ -56465,6 +56487,10 @@ FileBrowserComponent::FileBrowserComponent (FileChooserMode mode_,
{
FileListComponent* const list = new FileListComponent (*fileList);
list->setOutlineThickness (1);
if ((flags & canSelectMultipleItems) != 0)
list->setMultipleSelectionEnabled (true);
addAndMakeVisible (list);
fileListComponent = list;
}
@ -56492,11 +56518,11 @@ FileBrowserComponent::FileBrowserComponent (FileChooserMode mode_,
filenameBox->setMultiLine (false);
filenameBox->setSelectAllWhenFocused (true);
filenameBox->setText (filename, false);
filenameBox->addListener (this);
filenameBox->setReadOnly (filenameTextBoxIsReadOnly);
Label* label = new Label ("f", (mode == chooseDirectoryMode) ? TRANS("folder:")
: TRANS("file:"));
filenameBox->addListener (this);
filenameBox->setReadOnly ((flags & (filenameBoxIsReadOnly | canSelectMultipleItems)) != 0);
Label* label = new Label ("f", TRANS("file:"));
addAndMakeVisible (label);
label->attachToComponent (filenameBox, true);
@ -56519,10 +56545,7 @@ FileBrowserComponent::~FileBrowserComponent()
removeChildComponent (previewComp);
deleteAllChildren();
deleteAndZero (fileList);
delete directoriesOnlyFilter;
thread.stopThread (10000);
}
@ -56539,19 +56562,33 @@ void FileBrowserComponent::removeListener (FileBrowserListener* const listener)
listeners.removeValue (listener);
}
const File FileBrowserComponent::getCurrentFile() const throw()
bool FileBrowserComponent::isSaveMode() const throw()
{
return currentRoot.getChildFile (filenameBox->getText());
return (flags & saveMode) != 0;
}
int FileBrowserComponent::getNumSelectedFiles() const throw()
{
if (chosenFiles.size() == 0 && currentFileIsValid())
return 1;
return chosenFiles.size();
}
const File FileBrowserComponent::getSelectedFile (int index) const throw()
{
if (! filenameBox->isReadOnly())
return currentRoot.getChildFile (filenameBox->getText());
else
return chosenFiles[index] != 0 ? *chosenFiles[index] : File::nonexistent;
}
bool FileBrowserComponent::currentFileIsValid() const
{
if (mode == saveFileMode)
return ! getCurrentFile().isDirectory();
else if (mode == loadFileMode)
return getCurrentFile().existsAsFile();
else if (mode == chooseDirectoryMode)
return getCurrentFile().isDirectory();
if (isSaveMode())
return ! getSelectedFile (0).isDirectory();
else
return getSelectedFile (0).exists();
jassertfalse
return false;
@ -56559,7 +56596,27 @@ bool FileBrowserComponent::currentFileIsValid() const
const File FileBrowserComponent::getHighlightedFile() const throw()
{
return fileListComponent->getSelectedFile();
return fileListComponent->getSelectedFile (0);
}
bool FileBrowserComponent::isFileSuitable (const File& file) const
{
return (flags & canSelectFiles) != 0 ? (fileFilter == 0 || fileFilter->isFileSuitable (file))
: false;
}
bool FileBrowserComponent::isDirectorySuitable (const File&) const
{
return true;
}
bool FileBrowserComponent::isFileOrDirSuitable (const File& f) const
{
if (f.isDirectory())
return (flags & canSelectDirectories) != 0 && (fileFilter == 0 || fileFilter->isDirectorySuitable (f));
return (flags & canSelectFiles) != 0 && f.exists()
&& (fileFilter == 0 || fileFilter->isFileSuitable (f));
}
const File FileBrowserComponent::getRoot() const
@ -56573,9 +56630,6 @@ void FileBrowserComponent::setRoot (const File& newRootDirectory)
{
fileListComponent->scrollToTop();
if (mode == chooseDirectoryMode)
filenameBox->setText (String::empty, false);
String path (newRootDirectory.getFullPathName());
if (path.isEmpty())
@ -56627,8 +56681,7 @@ void FileBrowserComponent::refresh()
const String FileBrowserComponent::getActionVerb() const
{
return (mode == chooseDirectoryMode) ? TRANS("Choose")
: ((mode == saveFileMode) ? TRANS("Save") : TRANS("Open"));
return isSaveMode() ? TRANS("Save") : TRANS("Open");
}
FilePreviewComponent* FileBrowserComponent::getPreviewComponent() const throw()
@ -56649,7 +56702,7 @@ void FileBrowserComponent::sendListenerChangeMessage()
ComponentDeletionWatcher deletionWatcher (this);
if (previewComp != 0)
previewComp->selectedFileChanged (getCurrentFile());
previewComp->selectedFileChanged (getSelectedFile (0));
jassert (! deletionWatcher.hasBeenDeleted());
@ -56666,14 +56719,29 @@ void FileBrowserComponent::sendListenerChangeMessage()
void FileBrowserComponent::selectionChanged()
{
const File selected (fileListComponent->getSelectedFile());
StringArray newFilenames;
bool resetChosenFiles = true;
if ((mode == chooseDirectoryMode && selected.isDirectory())
|| selected.existsAsFile())
for (int i = 0; i < fileListComponent->getNumSelectedFiles(); ++i)
{
filenameBox->setText (selected.getRelativePathFrom (getRoot()), false);
const File f (fileListComponent->getSelectedFile (i));
if (isFileOrDirSuitable (f))
{
if (resetChosenFiles)
{
chosenFiles.clear();
resetChosenFiles = false;
}
chosenFiles.add (new File (f));
newFilenames.add (f.getRelativePathFrom (getRoot()));
}
}
if (newFilenames.size() > 0)
filenameBox->setText (newFilenames.joinIntoString (T(", ")), false);
sendListenerChangeMessage();
}
@ -56743,17 +56811,20 @@ void FileBrowserComponent::textEditorReturnKeyPressed (TextEditor&)
if (f.isDirectory())
{
setRoot (f);
chosenFiles.clear();
filenameBox->setText (String::empty);
}
else
{
setRoot (f.getParentDirectory());
chosenFiles.clear();
chosenFiles.add (new File (f));
filenameBox->setText (f.getFileName());
}
}
else
{
fileDoubleClicked (getCurrentFile());
fileDoubleClicked (getSelectedFile (0));
}
}
@ -56763,7 +56834,7 @@ void FileBrowserComponent::textEditorEscapeKeyPressed (TextEditor&)
void FileBrowserComponent::textEditorFocusLost (TextEditor&)
{
if (mode != saveFileMode)
if (! isSaveMode())
selectionChanged();
}
@ -56917,22 +56988,27 @@ FileChooser::~FileChooser()
bool FileChooser::browseForFileToOpen (FilePreviewComponent* previewComponent)
{
return showDialog (false, false, false, false, previewComponent);
return showDialog (false, true, false, false, false, previewComponent);
}
bool FileChooser::browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent)
{
return showDialog (false, false, false, true, previewComponent);
return showDialog (false, true, false, false, true, previewComponent);
}
bool FileChooser::browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComponent)
{
return showDialog (true, true, false, false, true, previewComponent);
}
bool FileChooser::browseForFileToSave (const bool warnAboutOverwritingExistingFiles)
{
return showDialog (false, true, warnAboutOverwritingExistingFiles, false, 0);
return showDialog (false, true, true, warnAboutOverwritingExistingFiles, false, 0);
}
bool FileChooser::browseForDirectory()
{
return showDialog (true, false, false, false, 0);
return showDialog (true, false, false, false, false, 0);
}
const File FileChooser::getResult() const
@ -56954,7 +57030,8 @@ const OwnedArray <File>& FileChooser::getResults() const
return results;
}
bool FileChooser::showDialog (const bool isDirectory,
bool FileChooser::showDialog (const bool selectsDirectories,
const bool selectsFiles,
const bool isSave,
const bool warnAboutOverwritingExistingFiles,
const bool selectMultipleFiles,
@ -56973,27 +57050,38 @@ bool FileChooser::showDialog (const bool isDirectory,
&& previewComponent->getHeight() > 10));
#if JUCE_WINDOWS
if (useNativeDialogBox)
#else
if (useNativeDialogBox && ! (selectsFiles && selectsDirectories))
#elif JUCE_MAC
if (useNativeDialogBox && (previewComponent == 0))
#else
if (false)
#endif
{
showPlatformDialog (results, title, startingFile, filters,
isDirectory, isSave,
selectsDirectories, selectsFiles, isSave,
warnAboutOverwritingExistingFiles,
selectMultipleFiles,
previewComponent);
}
else
{
jassert (! selectMultipleFiles); // not yet implemented for juce dialogs!
WildcardFileFilter wildcard (selectsFiles ? filters : String::empty,
selectsDirectories ? "*" : String::empty,
String::empty);
WildcardFileFilter wildcard (filters, String::empty);
int flags = isSave ? FileBrowserComponent::saveMode
: FileBrowserComponent::openMode;
FileBrowserComponent browserComponent (isDirectory ? FileBrowserComponent::chooseDirectoryMode
: (isSave ? FileBrowserComponent::saveFileMode
: FileBrowserComponent::loadFileMode),
startingFile, &wildcard, previewComponent);
if (selectsFiles)
flags |= FileBrowserComponent::canSelectFiles;
if (selectsDirectories)
flags |= FileBrowserComponent::canSelectDirectories;
if (selectMultipleFiles)
flags |= FileBrowserComponent::canSelectMultipleItems;
FileBrowserComponent browserComponent (flags, startingFile, &wildcard, previewComponent);
FileChooserDialogBox box (title, String::empty,
browserComponent,
@ -57001,7 +57089,10 @@ bool FileChooser::showDialog (const bool isDirectory,
browserComponent.findColour (AlertWindow::backgroundColourId));
if (box.show())
results.add (new File (browserComponent.getCurrentFile()));
{
for (int i = 0; i < browserComponent.getNumSelectedFiles(); ++i)
results.add (new File (browserComponent.getSelectedFile (i)));
}
}
if (currentlyFocused != 0 && ! currentlyFocusedChecker->hasBeenDeleted())
@ -57092,13 +57183,13 @@ void FileChooserDialogBox::buttonClicked (Button* button)
if (button == content->okButton)
{
if (warnAboutOverwritingExistingFiles
&& content->chooserComponent->getMode() == FileBrowserComponent::saveFileMode
&& content->chooserComponent->getCurrentFile().exists())
&& content->chooserComponent->isSaveMode()
&& content->chooserComponent->getSelectedFile(0).exists())
{
if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
TRANS("File already exists"),
TRANS("There's already a file called:\n\n")
+ content->chooserComponent->getCurrentFile().getFullPathName()
+ content->chooserComponent->getSelectedFile(0).getFullPathName()
+ T("\n\nAre you sure you want to overwrite it?"),
TRANS("overwrite"),
TRANS("cancel")))
@ -57214,9 +57305,14 @@ FileListComponent::~FileListComponent()
deleteAllChildren();
}
const File FileListComponent::getSelectedFile() const
int FileListComponent::getNumSelectedFiles() const
{
return fileList.getFile (getSelectedRow());
return getNumSelectedRows();
}
const File FileListComponent::getSelectedFile (int index) const
{
return fileList.getFile (getSelectedRow (index));
}
void FileListComponent::scrollToTop()
@ -58107,12 +58203,7 @@ FileTreeComponent::~FileTreeComponent()
delete root;
}
const File FileTreeComponent::getSelectedFile() const
{
return getSelectedFile (0);
}
const File FileTreeComponent::getSelectedFile (const int index) const throw()
const File FileTreeComponent::getSelectedFile (const int index) const
{
const FileListTreeItem* const item = dynamic_cast <const FileListTreeItem*> (getSelectedItem (index));
@ -58245,21 +58336,14 @@ END_JUCE_NAMESPACE
BEGIN_JUCE_NAMESPACE
WildcardFileFilter::WildcardFileFilter (const String& wildcardPatterns,
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns,
const String& directoryWildcardPatterns,
const String& description)
: FileFilter (description.isEmpty() ? wildcardPatterns
: (description + T(" (") + wildcardPatterns + T(")")))
: FileFilter (description.isEmpty() ? fileWildcardPatterns
: (description + T(" (") + fileWildcardPatterns + T(")")))
{
wildcards.addTokens (wildcardPatterns.toLowerCase(), T(";,"), T("\"'"));
wildcards.trim();
wildcards.removeEmptyStrings();
// special case for *.*, because people use it to mean "any file", but it
// would actually ignore files with no extension.
for (int i = wildcards.size(); --i >= 0;)
if (wildcards[i] == T("*.*"))
wildcards.set (i, T("*"));
parse (fileWildcardPatterns, fileWildcards);
parse (directoryWildcardPatterns, directoryWildcards);
}
WildcardFileFilter::~WildcardFileFilter()
@ -58267,6 +58351,30 @@ WildcardFileFilter::~WildcardFileFilter()
}
bool WildcardFileFilter::isFileSuitable (const File& file) const
{
return match (file, fileWildcards);
}
bool WildcardFileFilter::isDirectorySuitable (const File& file) const
{
return match (file, directoryWildcards);
}
void WildcardFileFilter::parse (const String& pattern, StringArray& result) throw()
{
result.addTokens (pattern.toLowerCase(), T(";,"), T("\"'"));
result.trim();
result.removeEmptyStrings();
// special case for *.*, because people use it to mean "any file", but it
// would actually ignore files with no extension.
for (int i = result.size(); --i >= 0;)
if (result[i] == T("*.*"))
result.set (i, T("*"));
}
bool WildcardFileFilter::match (const File& file, const StringArray& wildcards) throw()
{
const String filename (file.getFileName());
@ -58277,11 +58385,6 @@ bool WildcardFileFilter::isFileSuitable (const File& file) const
return false;
}
bool WildcardFileFilter::isDirectorySuitable (const File&) const
{
return true;
}
END_JUCE_NAMESPACE
/********* End of inlined file: juce_WildcardFileFilter.cpp *********/
@ -234860,6 +234963,14 @@ bool juce_launchFile (const String& fileName,
return hInstance > (HINSTANCE) 32;
}
void File::revealToUser() const throw()
{
if (isDirectory())
startAsProcess();
else if (getParentDirectory().exists())
getParentDirectory().startAsProcess();
}
struct NamedPipeInternal
{
HANDLE pipeH;
@ -239483,6 +239594,7 @@ void FileChooser::showPlatformDialog (OwnedArray<File>& results,
const File& currentFileOrDirectory,
const String& filter,
bool selectsDirectory,
bool selectsFiles,
bool isSaveDialogue,
bool warnAboutOverwritingExistingFiles,
bool selectMultipleFiles,
@ -250774,10 +250886,11 @@ bool juce_launchFile (const String& fileName,
cmdString << " " << parameters;
if (URL::isProbablyAWebsiteURL (fileName)
|| cmdString.startsWithIgnoreCase (T("file:"))
|| URL::isProbablyAnEmailAddress (fileName))
{
// create a command that tries to launch a bunch of likely browsers
const char* const browserNames[] = { "/etc/alternatives/x-www-browser", "firefox", "mozilla", "konqueror", "opera" };
const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", "konqueror", "opera" };
StringArray cmdLines;
@ -250787,9 +250900,6 @@ bool juce_launchFile (const String& fileName,
cmdString = cmdLines.joinIntoString (T(" || "));
}
if (cmdString.startsWithIgnoreCase (T("file:")))
cmdString = cmdString.substring (5);
const char* const argv[4] = { "/bin/sh", "-c", (const char*) cmdString.toUTF8(), 0 };
const int cpid = fork();
@ -250806,6 +250916,14 @@ bool juce_launchFile (const String& fileName,
return cpid >= 0;
}
void File::revealToUser() const throw()
{
if (isDirectory())
startAsProcess();
else if (getParentDirectory().exists())
getParentDirectory().startAsProcess();
}
#endif
/********* End of inlined file: juce_linux_Files.cpp *********/
@ -258067,7 +258185,7 @@ MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { re
/********* Start of inlined file: juce_linux_AudioCDReader.cpp *********/
// (This file gets included by juce_linux_NativeCode.cpp, rather than being
// compiled on its own).
#if JUCE_INCLUDED_FILE
#if JUCE_INCLUDED_FILE && JUCE_USE_CDREADER
AudioCDReader::AudioCDReader()
: AudioFormatReader (0, T("CD Audio"))
@ -258151,6 +258269,7 @@ void FileChooser::showPlatformDialog (OwnedArray<File>& results,
const File& file,
const String& filters,
bool isDirectory,
bool selectsFiles,
bool isSave,
bool warnAboutOverwritingExistingFiles,
bool selectMultipleFiles,
@ -260343,6 +260462,16 @@ bool juce_launchFile (const String& fileName,
#endif
}
void File::revealToUser() const throw()
{
#if ! JUCE_IPHONE
if (exists())
[[NSWorkspace sharedWorkspace] selectFile: juceStringToNS (getFullPathName()) inFileViewerRootedAtPath: @""];
else if (getParentDirectory().exists())
getParentDirectory().revealToUser();
#endif
}
#if ! JUCE_IPHONE
bool PlatformUtilities::makeFSRefFromPath (FSRef* destFSRef, const String& path)
{
@ -261505,18 +261634,27 @@ public:
bool clipToRectangleList (const RectangleList& clipRegion)
{
const int numRects = clipRegion.getNumRectangles();
CGRect* const rects = new CGRect [numRects];
for (int i = 0; i < numRects; ++i)
if (clipRegion.isEmpty())
{
const Rectangle& r = clipRegion.getRectangle(i);
rects[i] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight());
CGContextClipToRect (context, CGRectMake (0, 0, 0, 0));
return false;
}
else
{
const int numRects = clipRegion.getNumRectangles();
CGContextClipToRects (context, rects, numRects);
delete[] rects;
CGRect* const rects = new CGRect [numRects];
for (int i = 0; i < numRects; ++i)
{
const Rectangle& r = clipRegion.getRectangle(i);
rects[i] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight());
}
return ! isClipEmpty();
CGContextClipToRects (context, rects, numRects);
delete[] rects;
return ! isClipEmpty();
}
}
void excludeClipRectangle (const Rectangle& r)
@ -263211,6 +263349,7 @@ void FileChooser::showPlatformDialog (OwnedArray<File>& results,
const File& currentFileOrDirectory,
const String& filter,
bool selectsDirectory,
bool selectsFiles,
bool isSaveDialogue,
bool warnAboutOverwritingExistingFiles,
bool selectMultipleFiles,
@ -263235,7 +263374,7 @@ void FileChooser::showPlatformDialog (OwnedArray<File>& results,
{
NSOpenPanel* openPanel = (NSOpenPanel*) panel;
[openPanel setCanChooseDirectories: selectsDirectory];
[openPanel setCanChooseFiles: ! selectsDirectory];
[openPanel setCanChooseFiles: selectsFiles];
[openPanel setAllowsMultipleSelection: selectMultipleFiles];
}
@ -263283,6 +263422,7 @@ void FileChooser::showPlatformDialog (OwnedArray<File>& results,
const File& currentFileOrDirectory,
const String& filter,
bool selectsDirectory,
bool selectsFiles,
bool isSaveDialogue,
bool warnAboutOverwritingExistingFiles,
bool selectMultipleFiles,
@ -265962,18 +266102,27 @@ public:
bool clipToRectangleList (const RectangleList& clipRegion)
{
const int numRects = clipRegion.getNumRectangles();
CGRect* const rects = new CGRect [numRects];
for (int i = 0; i < numRects; ++i)
if (clipRegion.isEmpty())
{
const Rectangle& r = clipRegion.getRectangle(i);
rects[i] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight());
CGContextClipToRect (context, CGRectMake (0, 0, 0, 0));
return false;
}
else
{
const int numRects = clipRegion.getNumRectangles();
CGContextClipToRects (context, rects, numRects);
delete[] rects;
CGRect* const rects = new CGRect [numRects];
for (int i = 0; i < numRects; ++i)
{
const Rectangle& r = clipRegion.getRectangle(i);
rects[i] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight());
}
return ! isClipEmpty();
CGContextClipToRects (context, rects, numRects);
delete[] rects;
return ! isClipEmpty();
}
}
void excludeClipRectangle (const Rectangle& r)
@ -269433,6 +269582,7 @@ void FileChooser::showPlatformDialog (OwnedArray<File>& results,
const File& currentFileOrDirectory,
const String& filter,
bool selectsDirectory,
bool selectsFiles,
bool isSaveDialogue,
bool warnAboutOverwritingExistingFiles,
bool selectMultipleFiles,
@ -269457,7 +269607,7 @@ void FileChooser::showPlatformDialog (OwnedArray<File>& results,
{
NSOpenPanel* openPanel = (NSOpenPanel*) panel;
[openPanel setCanChooseDirectories: selectsDirectory];
[openPanel setCanChooseFiles: ! selectsDirectory];
[openPanel setCanChooseFiles: selectsFiles];
[openPanel setAllowsMultipleSelection: selectMultipleFiles];
}
@ -269505,6 +269655,7 @@ void FileChooser::showPlatformDialog (OwnedArray<File>& results,
const File& currentFileOrDirectory,
const String& filter,
bool selectsDirectory,
bool selectsFiles,
bool isSaveDialogue,
bool warnAboutOverwritingExistingFiles,
bool selectMultipleFiles,

View file

@ -6801,9 +6801,16 @@ public:
default viewer application.
- if it's a folder, it will be opened in Explorer, Finder, or equivalent.
@see revealToUser
*/
bool startAsProcess (const String& parameters = String::empty) const throw();
/** Opens Finder, Explorer, or whatever the OS uses, to show the user this file's location.
@see startAsProcess
*/
void revealToUser() const throw();
/** A set of types of location that can be passed to the getSpecialLocation() method.
*/
enum SpecialLocationType
@ -11983,6 +11990,9 @@ public:
bool isObject() const throw() { return type == objectType; }
bool isMethod() const throw() { return type == methodType; }
bool operator== (const var& other) const throw();
bool operator!= (const var& other) const throw();
class JUCE_API identifier
{
public:
@ -50715,11 +50725,16 @@ public:
/** Destructor. */
virtual ~DirectoryContentsDisplayComponent();
/** Returns the file that the user has currently selected.
Returns File::nonexistent if none is selected.
/** Returns the number of files the user has got selected.
@see getSelectedFile
*/
virtual const File getSelectedFile() const = 0;
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 const File getSelectedFile (int index) const = 0;
/** Scrolls this view to the top. */
virtual void scrollToTop() = 0;
@ -50838,22 +50853,26 @@ class JUCE_API FileBrowserComponent : public Component,
private FileBrowserListener,
private TextEditorListener,
private ButtonListener,
private ComboBoxListener
private ComboBoxListener,
private FileFilter
{
public:
/** Various modes that the browser can be used in.
/** Various options for the browser.
One of these is passed into the constructor.
A combination of these is passed into the FileBrowserComponent constructor.
*/
enum FileChooserMode
enum FileChooserFlags
{
loadFileMode, /**< the component should allow the user to choose an existing
file with the intention of opening it. */
saveFileMode, /**< the component should allow the user to specify the name of
a file that will be used to save something. */
chooseDirectoryMode /**< the component should allow the user to select an existing
directory. */
openMode = 1, /**< the component should allow the user to choose an existing
file with the intention of opening it. */
saveMode = 2, /**< the component should allow the user to specify the name of
a file that will be used to save something. */
canSelectFiles = 4, /**< */
canSelectDirectories = 8, /**< */
canSelectMultipleItems = 16, /**< */
useTreeView = 32, /**< */
filenameBoxIsReadOnly = 64 /**< */
};
/** Creates a FileBrowserComponent.
@ -50875,33 +50894,40 @@ public:
@param filenameTextBoxIsReadOnly if true, the user won't be allowed to type their own
text into the filename box.
*/
FileBrowserComponent (FileChooserMode browserMode,
FileBrowserComponent (int flags,
const File& initialFileOrDirectory,
const FileFilter* fileFilter,
FilePreviewComponent* previewComp,
const bool useTreeView = false,
const bool filenameTextBoxIsReadOnly = false);
FilePreviewComponent* previewComp);
/** Destructor. */
~FileBrowserComponent();
/** Returns the file that the user has currently chosen.
/** 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 throw();
/** 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
*/
const File getCurrentFile() const throw();
const File getSelectedFile (int index) const throw();
/** Returns true if the current file is usable.
/** 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, the current file is only valid if one has been selected and if the file
exists. In a "save" mode, a non-existent file would also be valid.
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 item in the view that is currently highlighted.
/** 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.
that is shown in the filename box, and if there are multiple selections,
this will only return one of them.
@see getCurrentFile
*/
const File getHighlightedFile() const throw();
@ -50918,9 +50944,6 @@ public:
/** Refreshes the directory that's currently being listed. */
void refresh();
/** Returns the browser's current mode. */
FileChooserMode getMode() const throw() { return mode; }
/** 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"
@ -50928,6 +50951,10 @@ public:
*/
virtual const String getActionVerb() const;
/** Returns true if the saveMode flag was set when this component was created.
*/
bool isSaveMode() const throw();
/** Adds a listener to be told when the user selects and clicks on files.
@see removeListener
@ -50962,6 +50989,10 @@ public:
void fileClicked (const File& f, const MouseEvent& e);
/** @internal */
void fileDoubleClicked (const File& f);
/** @internal */
bool isFileSuitable (const File& file) const;
/** @internal */
bool isDirectorySuitable (const File&) const;
/** @internal */
FilePreviewComponent* getPreviewComponent() const throw();
@ -50974,10 +51005,11 @@ protected:
private:
DirectoryContentsList* fileList;
FileFilter* directoriesOnlyFilter;
const FileFilter* fileFilter;
FileChooserMode mode;
int flags;
File currentRoot;
OwnedArray <File> chosenFiles;
SortedSet <void*> listeners;
DirectoryContentsDisplayComponent* fileListComponent;
@ -50989,6 +51021,7 @@ private:
TimeSliceThread thread;
void sendListenerChangeMessage();
bool isFileOrDirSuitable (const File& f) const;
FileBrowserComponent (const FileBrowserComponent&);
const FileBrowserComponent& operator= (const FileBrowserComponent&);
@ -51112,6 +51145,13 @@ public:
*/
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 = 0);
/** Returns the last file that was chosen by one of the browseFor methods.
After calling the appropriate browseFor... method, this method lets you
@ -51145,7 +51185,8 @@ private:
OwnedArray <File> results;
bool useNativeDialogBox;
bool showDialog (const bool isDirectory,
bool showDialog (const bool selectsDirectories,
const bool selectsFiles,
const bool isSave,
const bool warnAboutOverwritingExistingFiles,
const bool selectMultipleFiles,
@ -51155,7 +51196,8 @@ private:
const String& title,
const File& file,
const String& filters,
bool isDirectory,
bool selectsDirectories,
bool selectsFiles,
bool isSave,
bool warnAboutOverwritingExistingFiles,
bool selectMultipleFiles,
@ -51320,11 +51362,16 @@ public:
/** Destructor. */
~FileListComponent();
/** Returns the file that the user has currently selected.
Returns File::nonexistent if none is selected.
/** Returns the number of files the user has got selected.
@see getSelectedFile
*/
const File getSelectedFile() const;
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
*/
const File getSelectedFile (int index = 0) const;
/** Scrolls to the top of the list. */
void scrollToTop();
@ -51389,21 +51436,16 @@ public:
/** Destructor. */
~FileTreeComponent();
/** Returns the number of selected files in the tree.
/** Returns the number of files the user has got selected.
@see getSelectedFile
*/
int getNumSelectedFiles() const throw() { return TreeView::getNumSelectedItems(); }
int getNumSelectedFiles() const { return TreeView::getNumSelectedItems(); }
/** Returns one of the files that the user has currently selected.
Returns File::nonexistent if none is selected.
The index should be in the range 0 to (getNumSelectedFiles() - 1).
@see getNumSelectedFiles
*/
const File getSelectedFile (int index) const throw();
/** Returns the first of the files that the user has currently selected.
Returns File::nonexistent if none is selected.
*/
const File getSelectedFile() const;
const File getSelectedFile (int index = 0) const;
/** Scrolls the list to the top. */
void scrollToTop();
@ -51752,7 +51794,8 @@ public:
The description is a name to show the user in a list of possible patterns, so
for the wav/aiff example, your description might be "audio files".
*/
WildcardFileFilter (const String& wildcardPatterns,
WildcardFileFilter (const String& fileWildcardPatterns,
const String& directoryWildcardPatterns,
const String& description);
/** Destructor. */
@ -51767,7 +51810,10 @@ public:
juce_UseDebuggingNewOperator
private:
StringArray wildcards;
StringArray fileWildcards, directoryWildcards;
static void parse (const String& pattern, StringArray& result) throw();
static bool match (const File& file, const StringArray& wildcards) throw();
};
#endif // __JUCE_WILDCARDFILEFILTER_JUCEHEADER__

View file

@ -48,11 +48,16 @@ public:
virtual ~DirectoryContentsDisplayComponent();
//==============================================================================
/** Returns the file that the user has currently selected.
Returns File::nonexistent if none is selected.
/** Returns the number of files the user has got selected.
@see getSelectedFile
*/
virtual const File getSelectedFile() const = 0;
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 const File getSelectedFile (int index) const = 0;
/** Scrolls this view to the top. */
virtual void scrollToTop() = 0;

View file

@ -37,29 +37,24 @@ BEGIN_JUCE_NAMESPACE
//==============================================================================
class DirectoriesOnlyFilter : public FileFilter
{
public:
DirectoriesOnlyFilter() : FileFilter (String::empty) {}
bool isFileSuitable (const File&) const { return false; }
bool isDirectorySuitable (const File&) const { return true; }
};
//==============================================================================
FileBrowserComponent::FileBrowserComponent (FileChooserMode mode_,
FileBrowserComponent::FileBrowserComponent (int flags_,
const File& initialFileOrDirectory,
const FileFilter* fileFilter,
FilePreviewComponent* previewComp_,
const bool useTreeView,
const bool filenameTextBoxIsReadOnly)
: directoriesOnlyFilter (0),
mode (mode_),
const FileFilter* fileFilter_,
FilePreviewComponent* previewComp_)
: FileFilter (String::empty),
fileFilter (fileFilter_),
flags (flags_),
listeners (2),
previewComp (previewComp_),
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)
@ -72,18 +67,20 @@ FileBrowserComponent::FileBrowserComponent (FileChooserMode mode_,
}
else
{
chosenFiles.add (new File (initialFileOrDirectory));
currentRoot = initialFileOrDirectory.getParentDirectory();
filename = initialFileOrDirectory.getFileName();
}
if (mode_ == chooseDirectoryMode)
fileFilter = directoriesOnlyFilter = new DirectoriesOnlyFilter();
fileList = new DirectoryContentsList (this, thread);
fileList = new DirectoryContentsList (fileFilter, thread);
if (useTreeView)
if ((flags & useTreeView) != 0)
{
FileTreeComponent* const tree = new FileTreeComponent (*fileList);
if ((flags & canSelectMultipleItems) != 0)
tree->setMultiSelectEnabled (true);
addAndMakeVisible (tree);
fileListComponent = tree;
}
@ -91,6 +88,10 @@ FileBrowserComponent::FileBrowserComponent (FileChooserMode mode_,
{
FileListComponent* const list = new FileListComponent (*fileList);
list->setOutlineThickness (1);
if ((flags & canSelectMultipleItems) != 0)
list->setMultipleSelectionEnabled (true);
addAndMakeVisible (list);
fileListComponent = list;
}
@ -118,11 +119,11 @@ FileBrowserComponent::FileBrowserComponent (FileChooserMode mode_,
filenameBox->setMultiLine (false);
filenameBox->setSelectAllWhenFocused (true);
filenameBox->setText (filename, false);
filenameBox->addListener (this);
filenameBox->setReadOnly (filenameTextBoxIsReadOnly);
Label* label = new Label ("f", (mode == chooseDirectoryMode) ? TRANS("folder:")
: TRANS("file:"));
filenameBox->addListener (this);
filenameBox->setReadOnly ((flags & (filenameBoxIsReadOnly | canSelectMultipleItems)) != 0);
Label* label = new Label ("f", TRANS("file:"));
addAndMakeVisible (label);
label->attachToComponent (filenameBox, true);
@ -145,10 +146,7 @@ FileBrowserComponent::~FileBrowserComponent()
removeChildComponent (previewComp);
deleteAllChildren();
deleteAndZero (fileList);
delete directoriesOnlyFilter;
thread.stopThread (10000);
}
@ -167,19 +165,33 @@ void FileBrowserComponent::removeListener (FileBrowserListener* const listener)
}
//==============================================================================
const File FileBrowserComponent::getCurrentFile() const throw()
bool FileBrowserComponent::isSaveMode() const throw()
{
return currentRoot.getChildFile (filenameBox->getText());
return (flags & saveMode) != 0;
}
int FileBrowserComponent::getNumSelectedFiles() const throw()
{
if (chosenFiles.size() == 0 && currentFileIsValid())
return 1;
return chosenFiles.size();
}
const File FileBrowserComponent::getSelectedFile (int index) const throw()
{
if (! filenameBox->isReadOnly())
return currentRoot.getChildFile (filenameBox->getText());
else
return chosenFiles[index] != 0 ? *chosenFiles[index] : File::nonexistent;
}
bool FileBrowserComponent::currentFileIsValid() const
{
if (mode == saveFileMode)
return ! getCurrentFile().isDirectory();
else if (mode == loadFileMode)
return getCurrentFile().existsAsFile();
else if (mode == chooseDirectoryMode)
return getCurrentFile().isDirectory();
if (isSaveMode())
return ! getSelectedFile (0).isDirectory();
else
return getSelectedFile (0).exists();
jassertfalse
return false;
@ -187,7 +199,28 @@ bool FileBrowserComponent::currentFileIsValid() const
const File FileBrowserComponent::getHighlightedFile() const throw()
{
return fileListComponent->getSelectedFile();
return fileListComponent->getSelectedFile (0);
}
//==============================================================================
bool FileBrowserComponent::isFileSuitable (const File& file) const
{
return (flags & canSelectFiles) != 0 ? (fileFilter == 0 || fileFilter->isFileSuitable (file))
: false;
}
bool FileBrowserComponent::isDirectorySuitable (const File&) const
{
return true;
}
bool FileBrowserComponent::isFileOrDirSuitable (const File& f) const
{
if (f.isDirectory())
return (flags & canSelectDirectories) != 0 && (fileFilter == 0 || fileFilter->isDirectorySuitable (f));
return (flags & canSelectFiles) != 0 && f.exists()
&& (fileFilter == 0 || fileFilter->isFileSuitable (f));
}
//==============================================================================
@ -202,9 +235,6 @@ void FileBrowserComponent::setRoot (const File& newRootDirectory)
{
fileListComponent->scrollToTop();
if (mode == chooseDirectoryMode)
filenameBox->setText (String::empty, false);
String path (newRootDirectory.getFullPathName());
if (path.isEmpty())
@ -256,8 +286,7 @@ void FileBrowserComponent::refresh()
const String FileBrowserComponent::getActionVerb() const
{
return (mode == chooseDirectoryMode) ? TRANS("Choose")
: ((mode == saveFileMode) ? TRANS("Save") : TRANS("Open"));
return isSaveMode() ? TRANS("Save") : TRANS("Open");
}
FilePreviewComponent* FileBrowserComponent::getPreviewComponent() const throw()
@ -280,7 +309,7 @@ void FileBrowserComponent::sendListenerChangeMessage()
ComponentDeletionWatcher deletionWatcher (this);
if (previewComp != 0)
previewComp->selectedFileChanged (getCurrentFile());
previewComp->selectedFileChanged (getSelectedFile (0));
jassert (! deletionWatcher.hasBeenDeleted());
@ -297,14 +326,29 @@ void FileBrowserComponent::sendListenerChangeMessage()
void FileBrowserComponent::selectionChanged()
{
const File selected (fileListComponent->getSelectedFile());
StringArray newFilenames;
bool resetChosenFiles = true;
if ((mode == chooseDirectoryMode && selected.isDirectory())
|| selected.existsAsFile())
for (int i = 0; i < fileListComponent->getNumSelectedFiles(); ++i)
{
filenameBox->setText (selected.getRelativePathFrom (getRoot()), false);
const File f (fileListComponent->getSelectedFile (i));
if (isFileOrDirSuitable (f))
{
if (resetChosenFiles)
{
chosenFiles.clear();
resetChosenFiles = false;
}
chosenFiles.add (new File (f));
newFilenames.add (f.getRelativePathFrom (getRoot()));
}
}
if (newFilenames.size() > 0)
filenameBox->setText (newFilenames.joinIntoString (T(", ")), false);
sendListenerChangeMessage();
}
@ -375,17 +419,20 @@ void FileBrowserComponent::textEditorReturnKeyPressed (TextEditor&)
if (f.isDirectory())
{
setRoot (f);
chosenFiles.clear();
filenameBox->setText (String::empty);
}
else
{
setRoot (f.getParentDirectory());
chosenFiles.clear();
chosenFiles.add (new File (f));
filenameBox->setText (f.getFileName());
}
}
else
{
fileDoubleClicked (getCurrentFile());
fileDoubleClicked (getSelectedFile (0));
}
}
@ -395,7 +442,7 @@ void FileBrowserComponent::textEditorEscapeKeyPressed (TextEditor&)
void FileBrowserComponent::textEditorFocusLost (TextEditor&)
{
if (mode != saveFileMode)
if (! isSaveMode())
selectionChanged();
}

View file

@ -50,22 +50,26 @@ class JUCE_API FileBrowserComponent : public Component,
private FileBrowserListener,
private TextEditorListener,
private ButtonListener,
private ComboBoxListener
private ComboBoxListener,
private FileFilter
{
public:
//==============================================================================
/** Various modes that the browser can be used in.
/** Various options for the browser.
One of these is passed into the constructor.
A combination of these is passed into the FileBrowserComponent constructor.
*/
enum FileChooserMode
enum FileChooserFlags
{
loadFileMode, /**< the component should allow the user to choose an existing
file with the intention of opening it. */
saveFileMode, /**< the component should allow the user to specify the name of
a file that will be used to save something. */
chooseDirectoryMode /**< the component should allow the user to select an existing
directory. */
openMode = 1, /**< the component should allow the user to choose an existing
file with the intention of opening it. */
saveMode = 2, /**< the component should allow the user to specify the name of
a file that will be used to save something. */
canSelectFiles = 4, /**< */
canSelectDirectories = 8, /**< */
canSelectMultipleItems = 16, /**< */
useTreeView = 32, /**< */
filenameBoxIsReadOnly = 64 /**< */
};
//==============================================================================
@ -88,34 +92,41 @@ public:
@param filenameTextBoxIsReadOnly if true, the user won't be allowed to type their own
text into the filename box.
*/
FileBrowserComponent (FileChooserMode browserMode,
FileBrowserComponent (int flags,
const File& initialFileOrDirectory,
const FileFilter* fileFilter,
FilePreviewComponent* previewComp,
const bool useTreeView = false,
const bool filenameTextBoxIsReadOnly = false);
FilePreviewComponent* previewComp);
/** Destructor. */
~FileBrowserComponent();
//==============================================================================
/** Returns the file that the user has currently chosen.
/** 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 throw();
/** 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
*/
const File getCurrentFile() const throw();
const File getSelectedFile (int index) const throw();
/** Returns true if the current file is usable.
/** 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, the current file is only valid if one has been selected and if the file
exists. In a "save" mode, a non-existent file would also be valid.
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 item in the view that is currently highlighted.
/** 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.
that is shown in the filename box, and if there are multiple selections,
this will only return one of them.
@see getCurrentFile
*/
const File getHighlightedFile() const throw();
@ -133,9 +144,6 @@ public:
/** Refreshes the directory that's currently being listed. */
void refresh();
/** Returns the browser's current mode. */
FileChooserMode getMode() const throw() { return mode; }
/** 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"
@ -143,6 +151,10 @@ public:
*/
virtual const String getActionVerb() const;
/** Returns true if the saveMode flag was set when this component was created.
*/
bool isSaveMode() const throw();
//==============================================================================
/** Adds a listener to be told when the user selects and clicks on files.
@ -180,6 +192,10 @@ public:
void fileClicked (const File& f, const MouseEvent& e);
/** @internal */
void fileDoubleClicked (const File& f);
/** @internal */
bool isFileSuitable (const File& file) const;
/** @internal */
bool isDirectorySuitable (const File&) const;
/** @internal */
FilePreviewComponent* getPreviewComponent() const throw();
@ -192,10 +208,11 @@ protected:
private:
//==============================================================================
DirectoryContentsList* fileList;
FileFilter* directoriesOnlyFilter;
const FileFilter* fileFilter;
FileChooserMode mode;
int flags;
File currentRoot;
OwnedArray <File> chosenFiles;
SortedSet <void*> listeners;
DirectoryContentsDisplayComponent* fileListComponent;
@ -207,6 +224,7 @@ private:
TimeSliceThread thread;
void sendListenerChangeMessage();
bool isFileOrDirSuitable (const File& f) const;
FileBrowserComponent (const FileBrowserComponent&);
const FileBrowserComponent& operator= (const FileBrowserComponent&);

View file

@ -120,9 +120,11 @@ bool FileChooser::showDialog (const bool selectsDirectories,
&& previewComponent->getHeight() > 10));
#if JUCE_WINDOWS
if (useNativeDialogBox)
#else
if (useNativeDialogBox && ! (selectsFiles && selectsDirectories))
#elif JUCE_MAC
if (useNativeDialogBox && (previewComponent == 0))
#else
if (false)
#endif
{
showPlatformDialog (results, title, startingFile, filters,
@ -133,14 +135,23 @@ bool FileChooser::showDialog (const bool selectsDirectories,
}
else
{
jassert (! selectMultipleFiles); // not yet implemented for juce dialogs!
WildcardFileFilter wildcard (selectsFiles ? filters : String::empty,
selectsDirectories ? "*" : String::empty,
String::empty);
WildcardFileFilter wildcard (filters, String::empty);
int flags = isSave ? FileBrowserComponent::saveMode
: FileBrowserComponent::openMode;
FileBrowserComponent browserComponent (selectsDirectories ? FileBrowserComponent::chooseDirectoryMode
: (isSave ? FileBrowserComponent::saveFileMode
: FileBrowserComponent::loadFileMode),
startingFile, &wildcard, previewComponent);
if (selectsFiles)
flags |= FileBrowserComponent::canSelectFiles;
if (selectsDirectories)
flags |= FileBrowserComponent::canSelectDirectories;
if (selectMultipleFiles)
flags |= FileBrowserComponent::canSelectMultipleItems;
FileBrowserComponent browserComponent (flags, startingFile, &wildcard, previewComponent);
FileChooserDialogBox box (title, String::empty,
browserComponent,
@ -148,7 +159,10 @@ bool FileChooser::showDialog (const bool selectsDirectories,
browserComponent.findColour (AlertWindow::backgroundColourId));
if (box.show())
results.add (new File (browserComponent.getCurrentFile()));
{
for (int i = 0; i < browserComponent.getNumSelectedFiles(); ++i)
results.add (new File (browserComponent.getSelectedFile (i)));
}
}
if (currentlyFocused != 0 && ! currentlyFocusedChecker->hasBeenDeleted())

View file

@ -101,13 +101,13 @@ void FileChooserDialogBox::buttonClicked (Button* button)
if (button == content->okButton)
{
if (warnAboutOverwritingExistingFiles
&& content->chooserComponent->getMode() == FileBrowserComponent::saveFileMode
&& content->chooserComponent->getCurrentFile().exists())
&& content->chooserComponent->isSaveMode()
&& content->chooserComponent->getSelectedFile(0).exists())
{
if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
TRANS("File already exists"),
TRANS("There's already a file called:\n\n")
+ content->chooserComponent->getCurrentFile().getFullPathName()
+ content->chooserComponent->getSelectedFile(0).getFullPathName()
+ T("\n\nAre you sure you want to overwrite it?"),
TRANS("overwrite"),
TRANS("cancel")))

View file

@ -50,9 +50,14 @@ FileListComponent::~FileListComponent()
deleteAllChildren();
}
const File FileListComponent::getSelectedFile() const
int FileListComponent::getNumSelectedFiles() const
{
return fileList.getFile (getSelectedRow());
return getNumSelectedRows();
}
const File FileListComponent::getSelectedFile (int index) const
{
return fileList.getFile (getSelectedRow (index));
}
void FileListComponent::scrollToTop()

View file

@ -59,11 +59,16 @@ public:
~FileListComponent();
//==============================================================================
/** Returns the file that the user has currently selected.
Returns File::nonexistent if none is selected.
/** Returns the number of files the user has got selected.
@see getSelectedFile
*/
const File getSelectedFile() const;
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
*/
const File getSelectedFile (int index = 0) const;
/** Scrolls to the top of the list. */
void scrollToTop();

View file

@ -248,12 +248,7 @@ FileTreeComponent::~FileTreeComponent()
}
//==============================================================================
const File FileTreeComponent::getSelectedFile() const
{
return getSelectedFile (0);
}
const File FileTreeComponent::getSelectedFile (const int index) const throw()
const File FileTreeComponent::getSelectedFile (const int index) const
{
const FileListTreeItem* const item = dynamic_cast <const FileListTreeItem*> (getSelectedItem (index));

View file

@ -55,21 +55,16 @@ public:
~FileTreeComponent();
//==============================================================================
/** Returns the number of selected files in the tree.
/** Returns the number of files the user has got selected.
@see getSelectedFile
*/
int getNumSelectedFiles() const throw() { return TreeView::getNumSelectedItems(); }
int getNumSelectedFiles() const { return TreeView::getNumSelectedItems(); }
/** Returns one of the files that the user has currently selected.
Returns File::nonexistent if none is selected.
The index should be in the range 0 to (getNumSelectedFiles() - 1).
@see getNumSelectedFiles
*/
const File getSelectedFile (int index) const throw();
/** Returns the first of the files that the user has currently selected.
Returns File::nonexistent if none is selected.
*/
const File getSelectedFile() const;
const File getSelectedFile (int index = 0) const;
/** Scrolls the list to the top. */
void scrollToTop();

View file

@ -31,30 +31,46 @@ BEGIN_JUCE_NAMESPACE
//==============================================================================
WildcardFileFilter::WildcardFileFilter (const String& wildcardPatterns,
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns,
const String& directoryWildcardPatterns,
const String& description)
: FileFilter (description.isEmpty() ? wildcardPatterns
: (description + T(" (") + wildcardPatterns + T(")")))
: FileFilter (description.isEmpty() ? fileWildcardPatterns
: (description + T(" (") + fileWildcardPatterns + T(")")))
{
wildcards.addTokens (wildcardPatterns.toLowerCase(), T(";,"), T("\"'"));
wildcards.trim();
wildcards.removeEmptyStrings();
// special case for *.*, because people use it to mean "any file", but it
// would actually ignore files with no extension.
for (int i = wildcards.size(); --i >= 0;)
if (wildcards[i] == T("*.*"))
wildcards.set (i, T("*"));
parse (fileWildcardPatterns, fileWildcards);
parse (directoryWildcardPatterns, directoryWildcards);
}
WildcardFileFilter::~WildcardFileFilter()
{
}
bool WildcardFileFilter::isFileSuitable (const File& file) const
{
return match (file, fileWildcards);
}
bool WildcardFileFilter::isDirectorySuitable (const File& file) const
{
return match (file, directoryWildcards);
}
//==============================================================================
bool WildcardFileFilter::isFileSuitable (const File& file) const
void WildcardFileFilter::parse (const String& pattern, StringArray& result) throw()
{
result.addTokens (pattern.toLowerCase(), T(";,"), T("\"'"));
result.trim();
result.removeEmptyStrings();
// special case for *.*, because people use it to mean "any file", but it
// would actually ignore files with no extension.
for (int i = result.size(); --i >= 0;)
if (result[i] == T("*.*"))
result.set (i, T("*"));
}
bool WildcardFileFilter::match (const File& file, const StringArray& wildcards) throw()
{
const String filename (file.getFileName());
@ -65,10 +81,5 @@ bool WildcardFileFilter::isFileSuitable (const File& file) const
return false;
}
bool WildcardFileFilter::isDirectorySuitable (const File&) const
{
return true;
}
END_JUCE_NAMESPACE

View file

@ -52,7 +52,8 @@ public:
The description is a name to show the user in a list of possible patterns, so
for the wav/aiff example, your description might be "audio files".
*/
WildcardFileFilter (const String& wildcardPatterns,
WildcardFileFilter (const String& fileWildcardPatterns,
const String& directoryWildcardPatterns,
const String& description);
/** Destructor. */
@ -65,12 +66,14 @@ public:
/** This always returns true. */
bool isDirectorySuitable (const File& file) const;
//==============================================================================
juce_UseDebuggingNewOperator
private:
StringArray wildcards;
StringArray fileWildcards, directoryWildcards;
static void parse (const String& pattern, StringArray& result) throw();
static bool match (const File& file, const StringArray& wildcards) throw();
};