001/* A JFileChooser or FileDialog.
002
003 Copyright (c) 2011-2018 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026 */
027package ptolemy.gui;
028
029import java.awt.Container;
030import java.awt.FileDialog;
031import java.awt.Frame;
032import java.io.File;
033import java.io.IOException;
034
035import javax.swing.JFileChooser;
036
037import ptolemy.util.StringUtilities;
038
039///////////////////////////////////////////////////////////////////
040//// PtFileChooser
041
042/**
043 * A JFileChooser or FileDialog.
044 *
045 * <p>If {@link ptolemy.gui.PtGUIUtilities#useFileDialog()} returns
046 * true, then a java.awt.FileDialog is used.  Otherwise a
047 * javax.swing.JFileChooser is used.  In general Mac OS X is the only
048 * platform that where useFileDialog() will return true.</p>
049 *
050 * <p> See {@link ptolemy.gui.PtGUIUtilities#useFileDialog()} for how
051 * to set a runtime Java property to control whether FileDialog or
052 * JFileChooser is used.</p>
053 *
054 * <p> Note that this class should be wrapped in a try/finally block,
055 * otherwise, under Windows, white boxes will appear in the common pane.
056 * See {@link ptolemy.gui.JFileChooserBugFix}.  Below is an example: </p>
057 * <pre>
058 * // Swap backgrounds and avoid white boxes in "common places" dialog
059 * JFileChooserBugFix jFileChooserBugFix = new JFileChooserBugFix();
060 * Color background = null;
061 * PtFileChooser ptFileChooser;
062 * try {
063 *     ptFileChooser = new PtFileChooser(_basicGraphFrame, title,
064 *              JFileChooser.OPEN_DIALOG);
065 *     ptFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
066 *     ptFileChooser.setCurrentDirectory(modelDirectory);
067 *     ptFileChooser.addChoosableFileFilter(new DirectoryFilter());
068 *     int returnVal = ptFileChooser.showDialog(_basicGraphFrame, "Export HTML");
069 *
070 *     if (returnVal == JFileChooser.APPROVE_OPTION) {
071 *         directory = ptFileChooser.getSelectedFile();
072 *     }
073 * } finally {
074 *     try {
075 *         if (ptFileChooser != null) {
076 *             ptFileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
077 *          }
078 *     } finally {
079 *          jFileChooserBugFix.restoreBackground(background);
080 *     }
081 * }
082 * </pre>
083 *
084 * <p>Only a subset of the methods in JFileChooser are declared.
085 * The method names follow the JFileChoose API because that API is more
086 * common.</p>
087 *
088 * @author Christopher Brooks
089 * @version $Id$
090 * @since Ptolemy II 10.0
091 * @Pt.ProposedRating Red (cxh)
092 * @Pt.AcceptedRating Red (cxh)
093 */
094@SuppressWarnings("serial")
095public class PtFileChooser extends Container {
096    // java.awt.Container is the common baseclass between
097    // java.awt.FileDialog and javax.swing.JFileChooser
098    // Maybe someday this should extend JFileChooser, but only if the
099    // method implementations make sense when used with a FileDialog.
100
101    /** Construct a PtFileChooser.
102     *
103     *  <p>If {@link ptolemy.gui.PtGUIUtilities#useFileDialog()}
104     *  returns true, then a java.awt.FileDialog is used.  Otherwise a
105     *  javax.swing.JFileChooser is used.  The effect of the mode
106     *  argument varies depending on which type of dialog is used.</p>
107     *
108     *  @param parent The parent component.  Used with FileDialog,
109     *  ignored with JFileChooser.
110     *  @param title The title of the dialog
111     *  @param mode  JFileChooser.OPEN_DIALOG, JFileChooser.SAVE_DIALOG,
112     *  or JFileChooser.CUSTOM_DIALOG.  CUSTOM_DIALOG is ignored by FileDialog.
113     *  OPEN_DIALOG is a good default.
114     */
115    public PtFileChooser(Frame parent, String title, int mode) {
116        _mode = mode;
117        if (PtGUIUtilities.useFileDialog()) {
118            _useFileDialog = true;
119            if (mode == 2) {
120                mode = FileDialog.LOAD;
121            }
122            _fileDialog = new FileDialog(parent, title, mode);
123        } else {
124            _jFileChooser = new JFileChooser();
125            _jFileChooser.setDialogTitle(title);
126            _jFileChooser.setDialogType(mode);
127        }
128    }
129
130    /** Set the file name filter for the dialog.
131     *  @param filter The FilenameFilter to be used.
132     */
133    public void addChoosableFileFilter(PtFilenameFilter filter) {
134        if (_useFileDialog) {
135            _fileDialog.setFilenameFilter(filter);
136        } else {
137            _jFileChooser.addChoosableFileFilter(filter);
138        }
139    }
140
141    /** Return the current directory.
142     *  @return The current directory.
143     *  @see #setCurrentDirectory(File)
144     */
145    public File getCurrentDirectory() {
146        if (_useFileDialog) {
147            return new File(_fileDialog.getDirectory());
148        } else {
149            return _jFileChooser.getCurrentDirectory();
150        }
151    }
152
153    /** Return the selected file as an absolute File (a File that is not relative).
154     *  @return the selected file.
155     *  @see #setSelectedFile(File)
156     */
157    public File getSelectedFile() {
158        if (_useFileDialog) {
159            return new File(_fileDialog.getDirectory(), _fileDialog.getFile());
160        } else {
161            return _jFileChooser.getSelectedFile();
162        }
163    }
164
165    /** Set the current directory.
166     *  If the directory parameter is null, then the value of the "user.dir"
167     *  property is used.
168     *  @param directory The current directory.
169     *  @see #getCurrentDirectory()
170     */
171    public void setCurrentDirectory(File directory) {
172        boolean fail = false;
173        if (directory != null) {
174            if (_useFileDialog) {
175                try {
176                    _fileDialog.setDirectory(directory.getCanonicalPath());
177                } catch (IOException ex) {
178                    fail = true;
179                }
180            } else {
181                _jFileChooser.setCurrentDirectory(directory);
182            }
183        }
184        if (fail || directory == null) {
185            // The default on Windows is to open at user.home, which is
186            // typically an absurd directory inside the O/S installation.
187            // So we use the current directory instead.
188            // This will throw a security exception in an applet.
189            // FIXME: we should support users under applets opening files
190            // on the server.
191            String currentWorkingDirectory = StringUtilities
192                    .getProperty("user.dir");
193
194            if (currentWorkingDirectory != null) {
195                if (_useFileDialog) {
196                    _fileDialog.setDirectory(currentWorkingDirectory);
197                } else {
198                    _jFileChooser.setCurrentDirectory(
199                            new File(currentWorkingDirectory));
200                }
201            }
202        }
203    }
204
205    /** Set the file selection mode.
206     *
207     *  <p>If FileDialog is being used, then
208     *  DIRECTORIES_ONLY sets the apple.awt.fileDialogForDirectories parameter.
209     *  See <a href="https://developer.apple.com/library/mac/#documentation/Java/Reference/Java_PropertiesRef/Articles/JavaSystemProperties.html">https://developer.apple.com/library/mac/#documentation/Java/Reference/Java_PropertiesRef/Articles/JavaSystemProperties.html</a>.</p>
210     *
211     *  <p>If this method is called with
212     *  JFileChooser.DIRECTORIES_ONLY, then it should be called again
213     *  with JFileChooser.FILES_AND_DIRECTORIES.  Typically, the first
214     *  call is in a try block and the second is in a finally
215     *  block.</p>
216     *
217     *  <p> Note that if FileDialog is used, and mode is
218     *  DIRECTORIES_ONLY, then this class must have been instantiated
219     *  with a mode of FileBrowser.LOAD or JFileChooser.OPEN_DIALOG in
220     *  the constructor.</p>
221
222     *  @param mode One of JFileChooser.FILES_ONLY, JFileChooser.DIRECTORIES_ONLY or
223     *  JFileChooser.FILES_AND_DIRECTORIES.
224     */
225    public void setFileSelectionMode(int mode) {
226        if (_useFileDialog) {
227            if (mode == JFileChooser.DIRECTORIES_ONLY) {
228                if (_mode != 0) {
229                    if (!_printedDirectoryWarning) {
230                        _printedDirectoryWarning = true;
231                        System.out.println(
232                                "Warning: The PtFileChooser was instantiated with "
233                                        + "a mode other than 0, but setFileSelectionMode(DIRECTORIES_ONLY) "
234                                        + "was called.  This is likely to not work.");
235                    }
236                }
237                // Mac Specific: allow the user to select a directory.
238                // Note that apple.awt.fileDialogForDirectories only
239                // works with FileDialog.LOAD, not FileDialog.SAVE.
240                // See
241                // https://developer.apple.com/library/mac/#documentation/Java/Reference/Java_PropertiesRef/Articles/JavaSystemProperties.html
242                System.setProperty("apple.awt.fileDialogForDirectories",
243                        "true");
244            } else {
245                System.setProperty("apple.awt.fileDialogForDirectories",
246                        "false");
247            }
248        } else {
249            _jFileChooser.setFileSelectionMode(mode);
250        }
251    }
252
253    /** Set the selected file.
254     *  @param file The file to be selected
255     *  @see #getSelectedFile()
256     */
257    public void setSelectedFile(File file) {
258        if (_useFileDialog) {
259            _fileDialog.setFile(file.getName());
260        } else {
261            _jFileChooser.setSelectedFile(file);
262        }
263    }
264
265    /** Show the dialog.
266     *  @param parent Ignored with FileDialog, used with JFileChooser.
267     *  @param approveButtonText The text for the approve button if JFileChooser is used.
268     *  If FileDialog is used, then this argument is ignored.
269     *  @return One of
270     *  JFileChooser.CANCEL_OPTION, JFileChooser.APPROVE_OPTION
271     *  JFileChooser.ERROR_OPTION is returned.  If FileDialog is used, then either CANCEL_OPTION
272     *  or APPROVE_OPTION is returned.
273     */
274    public int showDialog(Container parent, String approveButtonText) {
275        if (_useFileDialog) {
276            _fileDialog.show();
277            if (_fileDialog.getFile() == null) {
278                return JFileChooser.CANCEL_OPTION;
279            } else {
280                return JFileChooser.APPROVE_OPTION;
281            }
282        } else {
283            return _jFileChooser.showDialog(parent, approveButtonText);
284        }
285    }
286
287    ///////////////////////////////////////////////////////////////////
288    ////                         private fields                    ////
289
290    /** The java.awt.FileDialog that is used if _useFileDialog is true. */
291    private FileDialog _fileDialog;
292
293    /** The javax.swing.JFileChooser that is used if _useFileDialog is false. */
294    private JFileChooser _jFileChooser;
295
296    /** The mode of the dialog.  One of
297     *  JFileChooser.OPEN_DIALOG, JFileChooser.SAVE_DIALOG, JFileChooser.CUSTOM_DIALOG.
298     *  CUSTOM_DIALOG is not supported with FileDialogs.
299     */
300    private int _mode;
301
302    /** True if the directory warning was printed. */
303    private static boolean _printedDirectoryWarning;
304
305    /** True if PtGUIUtilities.useFileDialog() returned true. */
306    private boolean _useFileDialog;
307
308}