2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
30 #include "juce_FileBrowserComponent.h"
31 #include "../lookandfeel/juce_LookAndFeel.h"
32 #include "../../graphics/drawables/juce_DrawablePath.h"
33 #include "../../../text/juce_LocalisedStrings.h"
34 #include "../../../core/juce_SystemStats.h"
35 #include "juce_FileListComponent.h"
36 #include "juce_FileTreeComponent.h"
39 //==============================================================================
40 FileBrowserComponent::FileBrowserComponent (int flags_
,
41 const File
& initialFileOrDirectory
,
42 const FileFilter
* fileFilter_
,
43 FilePreviewComponent
* previewComp_
)
44 : FileFilter (String::empty
),
45 fileFilter (fileFilter_
),
47 previewComp (previewComp_
),
48 currentPathBox ("path"),
49 fileLabel ("f", TRANS ("file:")),
50 thread ("Juce FileBrowser")
52 // You need to specify one or other of the open/save flags..
53 jassert ((flags
& (saveMode
| openMode
)) != 0);
54 jassert ((flags
& (saveMode
| openMode
)) != (saveMode
| openMode
));
56 // You need to specify at least one of these flags..
57 jassert ((flags
& (canSelectFiles
| canSelectDirectories
)) != 0);
61 if (initialFileOrDirectory
== File::nonexistent
)
63 currentRoot
= File::getCurrentWorkingDirectory();
65 else if (initialFileOrDirectory
.isDirectory())
67 currentRoot
= initialFileOrDirectory
;
71 chosenFiles
.add (initialFileOrDirectory
);
72 currentRoot
= initialFileOrDirectory
.getParentDirectory();
73 filename
= initialFileOrDirectory
.getFileName();
76 fileList
= new DirectoryContentsList (this, thread
);
78 if ((flags
& useTreeView
) != 0)
80 FileTreeComponent
* const tree
= new FileTreeComponent (*fileList
);
81 fileListComponent
= tree
;
83 if ((flags
& canSelectMultipleItems
) != 0)
84 tree
->setMultiSelectEnabled (true);
86 addAndMakeVisible (tree
);
90 FileListComponent
* const list
= new FileListComponent (*fileList
);
91 fileListComponent
= list
;
92 list
->setOutlineThickness (1);
94 if ((flags
& canSelectMultipleItems
) != 0)
95 list
->setMultipleSelectionEnabled (true);
97 addAndMakeVisible (list
);
100 fileListComponent
->addListener (this);
102 addAndMakeVisible (¤tPathBox
);
103 currentPathBox
.setEditableText (true);
105 currentPathBox
.addListener (this);
107 addAndMakeVisible (&filenameBox
);
108 filenameBox
.setMultiLine (false);
109 filenameBox
.setSelectAllWhenFocused (true);
110 filenameBox
.setText (filename
, false);
111 filenameBox
.addListener (this);
112 filenameBox
.setReadOnly ((flags
& (filenameBoxIsReadOnly
| canSelectMultipleItems
)) != 0);
114 addAndMakeVisible (&fileLabel
);
115 fileLabel
.attachToComponent (&filenameBox
, true);
117 addAndMakeVisible (goUpButton
= getLookAndFeel().createFileBrowserGoUpButton());
118 goUpButton
->addListener (this);
119 goUpButton
->setTooltip (TRANS ("go up to parent directory"));
121 if (previewComp
!= nullptr)
122 addAndMakeVisible (previewComp
);
124 setRoot (currentRoot
);
126 thread
.startThread (4);
129 FileBrowserComponent::~FileBrowserComponent()
131 fileListComponent
= nullptr;
133 thread
.stopThread (10000);
136 //==============================================================================
137 void FileBrowserComponent::addListener (FileBrowserListener
* const newListener
)
139 listeners
.add (newListener
);
142 void FileBrowserComponent::removeListener (FileBrowserListener
* const listener
)
144 listeners
.remove (listener
);
147 //==============================================================================
148 bool FileBrowserComponent::isSaveMode() const noexcept
150 return (flags
& saveMode
) != 0;
153 int FileBrowserComponent::getNumSelectedFiles() const noexcept
155 if (chosenFiles
.size() == 0 && currentFileIsValid())
158 return chosenFiles
.size();
161 File
FileBrowserComponent::getSelectedFile (int index
) const noexcept
163 if ((flags
& canSelectDirectories
) != 0 && filenameBox
.getText().isEmpty())
166 if (! filenameBox
.isReadOnly())
167 return currentRoot
.getChildFile (filenameBox
.getText());
169 return chosenFiles
[index
];
172 bool FileBrowserComponent::currentFileIsValid() const
175 return ! getSelectedFile (0).isDirectory();
177 return getSelectedFile (0).exists();
180 File
FileBrowserComponent::getHighlightedFile() const noexcept
182 return fileListComponent
->getSelectedFile (0);
185 void FileBrowserComponent::deselectAllFiles()
187 fileListComponent
->deselectAllFiles();
190 //==============================================================================
191 bool FileBrowserComponent::isFileSuitable (const File
& file
) const
193 return (flags
& canSelectFiles
) != 0 && (fileFilter
== nullptr || fileFilter
->isFileSuitable (file
));
196 bool FileBrowserComponent::isDirectorySuitable (const File
&) const
201 bool FileBrowserComponent::isFileOrDirSuitable (const File
& f
) const
204 return (flags
& canSelectDirectories
) != 0
205 && (fileFilter
== nullptr || fileFilter
->isDirectorySuitable (f
));
207 return (flags
& canSelectFiles
) != 0 && f
.exists()
208 && (fileFilter
== nullptr || fileFilter
->isFileSuitable (f
));
211 //==============================================================================
212 const File
& FileBrowserComponent::getRoot() const
217 void FileBrowserComponent::setRoot (const File
& newRootDirectory
)
219 if (currentRoot
!= newRootDirectory
)
221 fileListComponent
->scrollToTop();
223 String
path (newRootDirectory
.getFullPathName());
226 path
= File::separatorString
;
228 StringArray rootNames
, rootPaths
;
229 getRoots (rootNames
, rootPaths
);
231 if (! rootPaths
.contains (path
, true))
233 bool alreadyListed
= false;
235 for (int i
= currentPathBox
.getNumItems(); --i
>= 0;)
237 if (currentPathBox
.getItemText (i
).equalsIgnoreCase (path
))
239 alreadyListed
= true;
245 currentPathBox
.addItem (path
, currentPathBox
.getNumItems() + 2);
249 currentRoot
= newRootDirectory
;
250 fileList
->setDirectory (currentRoot
, true, true);
252 String
currentRootName (currentRoot
.getFullPathName());
253 if (currentRootName
.isEmpty())
254 currentRootName
= File::separatorString
;
256 currentPathBox
.setText (currentRootName
, true);
258 goUpButton
->setEnabled (currentRoot
.getParentDirectory().isDirectory()
259 && currentRoot
.getParentDirectory() != currentRoot
);
262 void FileBrowserComponent::resetRecentPaths()
264 currentPathBox
.clear();
266 StringArray rootNames
, rootPaths
;
267 getRoots (rootNames
, rootPaths
);
269 for (int i
= 0; i
< rootNames
.size(); ++i
)
271 if (rootNames
[i
].isEmpty())
272 currentPathBox
.addSeparator();
274 currentPathBox
.addItem (rootNames
[i
], i
+ 1);
277 currentPathBox
.addSeparator();
280 void FileBrowserComponent::goUp()
282 setRoot (getRoot().getParentDirectory());
285 void FileBrowserComponent::refresh()
290 void FileBrowserComponent::setFileFilter (const FileFilter
* const newFileFilter
)
292 if (fileFilter
!= newFileFilter
)
294 fileFilter
= newFileFilter
;
299 const String
FileBrowserComponent::getActionVerb() const
301 return isSaveMode() ? TRANS("Save") : TRANS("Open");
304 FilePreviewComponent
* FileBrowserComponent::getPreviewComponent() const noexcept
309 //==============================================================================
310 void FileBrowserComponent::resized()
313 .layoutFileBrowserComponent (*this, fileListComponent
, previewComp
,
314 ¤tPathBox
, &filenameBox
, goUpButton
);
317 //==============================================================================
318 void FileBrowserComponent::sendListenerChangeMessage()
320 Component::BailOutChecker
checker (this);
322 if (previewComp
!= nullptr)
323 previewComp
->selectedFileChanged (getSelectedFile (0));
325 // You shouldn't delete the browser when the file gets changed!
326 jassert (! checker
.shouldBailOut());
328 listeners
.callChecked (checker
, &FileBrowserListener::selectionChanged
);
331 void FileBrowserComponent::selectionChanged()
333 StringArray newFilenames
;
334 bool resetChosenFiles
= true;
336 for (int i
= 0; i
< fileListComponent
->getNumSelectedFiles(); ++i
)
338 const File
f (fileListComponent
->getSelectedFile (i
));
340 if (isFileOrDirSuitable (f
))
342 if (resetChosenFiles
)
345 resetChosenFiles
= false;
349 newFilenames
.add (f
.getRelativePathFrom (getRoot()));
353 if (newFilenames
.size() > 0)
354 filenameBox
.setText (newFilenames
.joinIntoString (", "), false);
356 sendListenerChangeMessage();
359 void FileBrowserComponent::fileClicked (const File
& f
, const MouseEvent
& e
)
361 Component::BailOutChecker
checker (this);
362 listeners
.callChecked (checker
, &FileBrowserListener::fileClicked
, f
, e
);
365 void FileBrowserComponent::fileDoubleClicked (const File
& f
)
371 if ((flags
& canSelectDirectories
) != 0)
372 filenameBox
.setText (String::empty
);
376 Component::BailOutChecker
checker (this);
377 listeners
.callChecked (checker
, &FileBrowserListener::fileDoubleClicked
, f
);
381 bool FileBrowserComponent::keyPressed (const KeyPress
& key
)
385 #if JUCE_LINUX || JUCE_WINDOWS
386 if (key
.getModifiers().isCommandDown()
387 && (key
.getKeyCode() == 'H' || key
.getKeyCode() == 'h'))
389 fileList
->setIgnoresHiddenFiles (! fileList
->ignoresHiddenFiles());
398 //==============================================================================
399 void FileBrowserComponent::textEditorTextChanged (TextEditor
&)
401 sendListenerChangeMessage();
404 void FileBrowserComponent::textEditorReturnKeyPressed (TextEditor
&)
406 if (filenameBox
.getText().containsChar (File::separator
))
408 const File
f (currentRoot
.getChildFile (filenameBox
.getText()));
414 filenameBox
.setText (String::empty
);
418 setRoot (f
.getParentDirectory());
421 filenameBox
.setText (f
.getFileName());
426 fileDoubleClicked (getSelectedFile (0));
430 void FileBrowserComponent::textEditorEscapeKeyPressed (TextEditor
&)
434 void FileBrowserComponent::textEditorFocusLost (TextEditor
&)
440 //==============================================================================
441 void FileBrowserComponent::buttonClicked (Button
*)
446 void FileBrowserComponent::comboBoxChanged (ComboBox
*)
448 const String
newText (currentPathBox
.getText().trim().unquoted());
450 if (newText
.isNotEmpty())
452 const int index
= currentPathBox
.getSelectedId() - 1;
454 StringArray rootNames
, rootPaths
;
455 getRoots (rootNames
, rootPaths
);
457 if (rootPaths
[index
].isNotEmpty())
459 setRoot (File (rootPaths
[index
]));
473 if (f
.getParentDirectory() == f
)
476 f
= f
.getParentDirectory();
482 void FileBrowserComponent::getRoots (StringArray
& rootNames
, StringArray
& rootPaths
)
486 File::findFileSystemRoots (roots
);
489 for (int i
= 0; i
< roots
.size(); ++i
)
491 const File
& drive
= roots
.getReference(i
);
493 String
name (drive
.getFullPathName());
494 rootPaths
.add (name
);
496 if (drive
.isOnHardDisk())
498 String
volume (drive
.getVolumeLabel());
500 if (volume
.isEmpty())
501 volume
= TRANS("Hard Drive");
503 name
<< " [" << volume
<< ']';
505 else if (drive
.isOnCDRomDrive())
507 name
<< TRANS(" [CD/DVD drive]");
510 rootNames
.add (name
);
513 rootPaths
.add (String::empty
);
514 rootNames
.add (String::empty
);
516 rootPaths
.add (File::getSpecialLocation (File::userDocumentsDirectory
).getFullPathName());
517 rootNames
.add ("Documents");
518 rootPaths
.add (File::getSpecialLocation (File::userDesktopDirectory
).getFullPathName());
519 rootNames
.add ("Desktop");
522 rootPaths
.add (File::getSpecialLocation (File::userHomeDirectory
).getFullPathName());
523 rootNames
.add ("Home folder");
524 rootPaths
.add (File::getSpecialLocation (File::userDocumentsDirectory
).getFullPathName());
525 rootNames
.add ("Documents");
526 rootPaths
.add (File::getSpecialLocation (File::userDesktopDirectory
).getFullPathName());
527 rootNames
.add ("Desktop");
529 rootPaths
.add (String::empty
);
530 rootNames
.add (String::empty
);
532 Array
<File
> volumes
;
533 File
vol ("/Volumes");
534 vol
.findChildFiles (volumes
, File::findDirectories
, false);
536 for (int i
= 0; i
< volumes
.size(); ++i
)
538 const File
& volume
= volumes
.getReference(i
);
540 if (volume
.isDirectory() && ! volume
.getFileName().startsWithChar ('.'))
542 rootPaths
.add (volume
.getFullPathName());
543 rootNames
.add (volume
.getFileName());
550 rootPaths
.add (File::getSpecialLocation (File::userHomeDirectory
).getFullPathName());
551 rootNames
.add ("Home folder");
552 rootPaths
.add (File::getSpecialLocation (File::userDesktopDirectory
).getFullPathName());
553 rootNames
.add ("Desktop");