1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 //==============================================================================
30 /**
31     Listens for events happening to a FilenameComponent.
32 
33     Use FilenameComponent::addListener() and FilenameComponent::removeListener() to
34     register one of these objects for event callbacks when the filename is changed.
35 
36     @see FilenameComponent
37 
38     @tags{GUI}
39 */
40 class JUCE_API  FilenameComponentListener
41 {
42 public:
43     /** Destructor. */
44     virtual ~FilenameComponentListener() = default;
45 
46     /** This method is called after the FilenameComponent's file has been changed. */
47     virtual void filenameComponentChanged (FilenameComponent* fileComponentThatHasChanged) = 0;
48 };
49 
50 
51 //==============================================================================
52 /**
53     Shows a filename as an editable text box, with a 'browse' button and a
54     drop-down list for recently selected files.
55 
56     A handy component for dialogue boxes where you want the user to be able to
57     select a file or directory.
58 
59     Attach an FilenameComponentListener using the addListener() method, and it will
60     get called each time the user changes the filename, either by browsing for a file
61     and clicking 'ok', or by typing a new filename into the box and pressing return.
62 
63     @see FileChooser, ComboBox
64 
65     @tags{GUI}
66 */
67 class JUCE_API  FilenameComponent  : public Component,
68                                      public SettableTooltipClient,
69                                      public FileDragAndDropTarget,
70                                      private AsyncUpdater
71 {
72 public:
73     //==============================================================================
74     /** Creates a FilenameComponent.
75 
76         @param name             the name for this component.
77         @param currentFile      the file to initially show in the box
78         @param canEditFilename  if true, the user can manually edit the filename; if false,
79                                 they can only change it by browsing for a new file
80         @param isDirectory      if true, the file will be treated as a directory, and
81                                 an appropriate directory browser used
82         @param isForSaving      if true, the file browser will allow non-existent files to
83                                 be picked, as the file is assumed to be used for saving rather
84                                 than loading
85         @param fileBrowserWildcard  a wildcard pattern to use in the file browser - e.g. "*.txt;*.foo".
86                                 If an empty string is passed in, then the pattern is assumed to be "*"
87         @param enforcedSuffix   if this is non-empty, it is treated as a suffix that will be added
88                                 to any filenames that are entered or chosen
89         @param textWhenNothingSelected  the message to display in the box before any filename is entered. (This
90                                 will only appear if the initial file isn't valid)
91     */
92     FilenameComponent (const String& name,
93                        const File& currentFile,
94                        bool canEditFilename,
95                        bool isDirectory,
96                        bool isForSaving,
97                        const String& fileBrowserWildcard,
98                        const String& enforcedSuffix,
99                        const String& textWhenNothingSelected);
100 
101     /** Destructor. */
102     ~FilenameComponent() override;
103 
104     //==============================================================================
105     /** Returns the currently displayed filename. */
106     File getCurrentFile() const;
107 
108     /** Returns the raw text that the user has entered. */
109     String getCurrentFileText() const;
110 
111     /** Changes the current filename.
112 
113         @param newFile                the new filename to use
114         @param addToRecentlyUsedList  if true, the filename will also be added to the
115                                       drop-down list of recent files.
116         @param notification           whether to send a notification of the change to listeners.
117                                       A notification will only be sent if the filename has changed.
118     */
119     void setCurrentFile (File newFile,
120                          bool addToRecentlyUsedList,
121                          NotificationType notification = sendNotificationAsync);
122 
123     /** Changes whether the use can type into the filename box.
124     */
125     void setFilenameIsEditable (bool shouldBeEditable);
126 
127     /** Sets a file or directory to be the default starting point for the browser to show.
128 
129         This is only used if the current file hasn't been set.
130     */
131     void setDefaultBrowseTarget (const File& newDefaultDirectory);
132 
133     /** This can be overridden to return a custom location that you want the dialog box
134         to show when the browse button is pushed.
135         The default implementation of this method will return either the current file
136         (if one has been chosen) or the location that was set by setDefaultBrowseTarget().
137     */
138     virtual File getLocationToBrowse();
139 
140     /** Returns all the entries on the recent files list.
141 
142         This can be used in conjunction with setRecentlyUsedFilenames() for saving the
143         state of this list.
144 
145         @see setRecentlyUsedFilenames
146     */
147     StringArray getRecentlyUsedFilenames() const;
148 
149     /** Sets all the entries on the recent files list.
150 
151         This can be used in conjunction with getRecentlyUsedFilenames() for saving the
152         state of this list.
153 
154         @see getRecentlyUsedFilenames, addRecentlyUsedFile
155     */
156     void setRecentlyUsedFilenames (const StringArray& filenames);
157 
158     /** Adds an entry to the recently-used files dropdown list.
159 
160         If the file is already in the list, it will be moved to the top. A limit
161         is also placed on the number of items that are kept in the list.
162 
163         @see getRecentlyUsedFilenames, setRecentlyUsedFilenames, setMaxNumberOfRecentFiles
164     */
165     void addRecentlyUsedFile (const File& file);
166 
167     /** Changes the limit for the number of files that will be stored in the recent-file list.
168     */
169     void setMaxNumberOfRecentFiles (int newMaximum);
170 
171     /** Changes the text shown on the 'browse' button.
172 
173         By default this button just says "..." but you can change it. The button itself
174         can be changed using the look-and-feel classes, so it might not actually have any
175         text on it.
176     */
177     void setBrowseButtonText (const String& browseButtonText);
178 
179     //==============================================================================
180     /** Adds a listener that will be called when the selected file is changed. */
181     void addListener (FilenameComponentListener* listener);
182 
183     /** Removes a previously-registered listener. */
184     void removeListener (FilenameComponentListener* listener);
185 
186     /** Gives the component a tooltip. */
187     void setTooltip (const String& newTooltip) override;
188 
189     //==============================================================================
190     /** This abstract base class is implemented by LookAndFeel classes. */
191     struct JUCE_API  LookAndFeelMethods
192     {
193         virtual ~LookAndFeelMethods() = default;
194 
195         virtual Button* createFilenameComponentBrowseButton (const String& text) = 0;
196         virtual void layoutFilenameComponent (FilenameComponent&, ComboBox* filenameBox, Button* browseButton) =  0;
197     };
198 
199     //==============================================================================
200     /** @internal */
201     void paintOverChildren (Graphics&) override;
202     /** @internal */
203     void resized() override;
204     /** @internal */
205     void lookAndFeelChanged() override;
206     /** @internal */
207     bool isInterestedInFileDrag (const StringArray&) override;
208     /** @internal */
209     void filesDropped (const StringArray&, int, int) override;
210     /** @internal */
211     void fileDragEnter (const StringArray&, int, int) override;
212     /** @internal */
213     void fileDragExit (const StringArray&) override;
214     /** @internal */
215     KeyboardFocusTraverser* createFocusTraverser() override;
216 
217 private:
218     //==============================================================================
219     ComboBox filenameBox;
220     String lastFilename;
221     std::unique_ptr<Button> browseButton;
222     int maxRecentFiles = 30;
223     bool isDir, isSaving, isFileDragOver = false;
224     String wildcard, enforcedSuffix, browseButtonText;
225     ListenerList <FilenameComponentListener> listeners;
226     File defaultBrowseFile;
227 
228     void showChooser();
229     void handleAsyncUpdate() override;
230 
231     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilenameComponent)
232 };
233 
234 } // namespace juce
235