1 /*
   2  * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package java.awt;
  26 
  27 import java.awt.peer.FileDialogPeer;
  28 import java.io.FilenameFilter;
  29 import java.io.IOException;
  30 import java.io.ObjectInputStream;
  31 import java.io.File;
  32 import sun.awt.AWTAccessor;
  33 
  34 /**
  35  * The {@code FileDialog} class displays a dialog window
  36  * from which the user can select a file.
  37  * <p>
  38  * Since it is a modal dialog, when the application calls
  39  * its {@code show} method to display the dialog,
  40  * it blocks the rest of the application until the user has
  41  * chosen a file.
  42  *
  43  * @see Window#show
  44  *
  45  * @author      Sami Shaio
  46  * @author      Arthur van Hoff
  47  * @since       1.0
  48  */
  49 public class FileDialog extends Dialog {
  50 
  51     /**
  52      * This constant value indicates that the purpose of the file
  53      * dialog window is to locate a file from which to read.
  54      */
  55     public static final int LOAD = 0;
  56 
  57     /**
  58      * This constant value indicates that the purpose of the file
  59      * dialog window is to locate a file to which to write.
  60      */
  61     public static final int SAVE = 1;
  62 
  63     /*
  64      * There are two {@code FileDialog} modes: {@code LOAD} and
  65      * {@code SAVE}.
  66      * This integer will represent one or the other.
  67      * If the mode is not specified it will default to {@code LOAD}.
  68      *
  69      * @serial
  70      * @see getMode()
  71      * @see setMode()
  72      * @see java.awt.FileDialog#LOAD
  73      * @see java.awt.FileDialog#SAVE
  74      */
  75     int mode;
  76 
  77     /*
  78      * The string specifying the directory to display
  79      * in the file dialog.  This variable may be {@code null}.
  80      *
  81      * @serial
  82      * @see getDirectory()
  83      * @see setDirectory()
  84      */
  85     String dir;
  86 
  87     /*
  88      * The string specifying the initial value of the
  89      * filename text field in the file dialog.
  90      * This variable may be {@code null}.
  91      *
  92      * @serial
  93      * @see getFile()
  94      * @see setFile()
  95      */
  96     String file;
  97 
  98     /**
  99      * Contains the File instances for all the files that the user selects.
 100      *
 101      * @serial
 102      * @see #getFiles
 103      * @since 1.7
 104      */
 105     private File[] files;
 106 
 107     /**
 108      * Represents whether the file dialog allows the multiple file selection.
 109      *
 110      * @serial
 111      * @see #setMultipleMode
 112      * @see #isMultipleMode
 113      * @since 1.7
 114      */
 115     private boolean multipleMode = false;
 116 
 117     /*
 118      * The filter used as the file dialog's filename filter.
 119      * The file dialog will only be displaying files whose
 120      * names are accepted by this filter.
 121      * This variable may be {@code null}.
 122      *
 123      * @serial
 124      * @see #getFilenameFilter()
 125      * @see #setFilenameFilter()
 126      * @see FileNameFilter
 127      */
 128     @SuppressWarnings("serial") // Not statically typed as Serializable
 129     FilenameFilter filter;
 130 
 131     private static final String base = "filedlg";
 132     private static int nameCounter = 0;
 133 
 134     /*
 135      * JDK 1.1 serialVersionUID
 136      */
 137      private static final long serialVersionUID = 5035145889651310422L;
 138 
 139 
 140     static {
 141         /* ensure that the necessary native libraries are loaded */
 142         Toolkit.loadLibraries();
 143         if (!GraphicsEnvironment.isHeadless()) {
 144             initIDs();
 145         }
 146     }
 147 
 148     static {
 149         AWTAccessor.setFileDialogAccessor(
 150             new AWTAccessor.FileDialogAccessor() {
 151                 public void setFiles(FileDialog fileDialog, File[] files) {
 152                     fileDialog.setFiles(files);
 153                 }
 154                 public void setFile(FileDialog fileDialog, String file) {
 155                     fileDialog.file = ("".equals(file)) ? null : file;
 156                 }
 157                 public void setDirectory(FileDialog fileDialog, String directory) {
 158                     fileDialog.dir = ("".equals(directory)) ? null : directory;
 159                 }
 160                 public boolean isMultipleMode(FileDialog fileDialog) {
 161                     synchronized (fileDialog.getObjectLock()) {
 162                         return fileDialog.multipleMode;
 163                     }
 164                 }
 165             });
 166     }
 167 
 168     /**
 169      * Initialize JNI field and method IDs for fields that may be
 170        accessed from C.
 171      */
 172     private static native void initIDs();
 173 
 174     /**
 175      * Creates a file dialog for loading a file.  The title of the
 176      * file dialog is initially empty.  This is a convenience method for
 177      * {@code FileDialog(parent, "", LOAD)}.
 178      * <p>
 179      * <strong>Note:</strong> Some platforms may not support
 180      * showing the user-specified title in a file dialog.
 181      * In this situation, either no title will be displayed in the file dialog's
 182      * title bar or, on some systems, the file dialog's title bar will not be
 183      * displayed.
 184      *
 185      * @param parent the owner of the dialog
 186      * @since 1.1
 187      */
 188     public FileDialog(Frame parent) {
 189         this(parent, "", LOAD);
 190     }
 191 
 192     /**
 193      * Creates a file dialog window with the specified title for loading
 194      * a file. The files shown are those in the current directory.
 195      * This is a convenience method for
 196      * {@code FileDialog(parent, title, LOAD)}.
 197      * <p>
 198      * <strong>Note:</strong> Some platforms may not support
 199      * showing the user-specified title in a file dialog.
 200      * In this situation, either no title will be displayed in the file dialog's
 201      * title bar or, on some systems, the file dialog's title bar will not be
 202      * displayed.
 203      *
 204      * @param     parent   the owner of the dialog
 205      * @param     title    the title of the dialog
 206      */
 207     public FileDialog(Frame parent, String title) {
 208         this(parent, title, LOAD);
 209     }
 210 
 211     /**
 212      * Creates a file dialog window with the specified title for loading
 213      * or saving a file.
 214      * <p>
 215      * If the value of {@code mode} is {@code LOAD}, then the
 216      * file dialog is finding a file to read, and the files shown are those
 217      * in the current directory.   If the value of
 218      * {@code mode} is {@code SAVE}, the file dialog is finding
 219      * a place to write a file.
 220      * <p>
 221      * <strong>Note:</strong> Some platforms may not support
 222      * showing the user-specified title in a file dialog.
 223      * In this situation, either no title will be displayed in the file dialog's
 224      * title bar or, on some systems, the file dialog's title bar will not be
 225      * displayed.
 226      *
 227      * @param     parent   the owner of the dialog
 228      * @param     title   the title of the dialog
 229      * @param     mode   the mode of the dialog; either
 230      *            {@code FileDialog.LOAD} or {@code FileDialog.SAVE}
 231      * @exception  IllegalArgumentException if an illegal file
 232      *                 dialog mode is supplied
 233      * @see       java.awt.FileDialog#LOAD
 234      * @see       java.awt.FileDialog#SAVE
 235      */
 236     public FileDialog(Frame parent, String title, int mode) {
 237         super(parent, title, true);
 238         this.setMode(mode);
 239         setLayout(null);
 240     }
 241 
 242     /**
 243      * Creates a file dialog for loading a file.  The title of the
 244      * file dialog is initially empty.  This is a convenience method for
 245      * {@code FileDialog(parent, "", LOAD)}.
 246      * <p>
 247      * <strong>Note:</strong> Some platforms may not support
 248      * showing the user-specified title in a file dialog.
 249      * In this situation, either no title will be displayed in the file dialog's
 250      * title bar or, on some systems, the file dialog's title bar will not be
 251      * displayed.
 252      *
 253      * @param     parent   the owner of the dialog
 254      * @exception java.lang.IllegalArgumentException if the {@code parent}'s
 255      *            {@code GraphicsConfiguration}
 256      *            is not from a screen device;
 257      * @exception java.lang.IllegalArgumentException if {@code parent}
 258      *            is {@code null}; this exception is always thrown when
 259      *            {@code GraphicsEnvironment.isHeadless}
 260      *            returns {@code true}
 261      * @see java.awt.GraphicsEnvironment#isHeadless
 262      * @since 1.5
 263      */
 264     public FileDialog(Dialog parent) {
 265         this(parent, "", LOAD);
 266     }
 267 
 268     /**
 269      * Creates a file dialog window with the specified title for loading
 270      * a file. The files shown are those in the current directory.
 271      * This is a convenience method for
 272      * {@code FileDialog(parent, title, LOAD)}.
 273      * <p>
 274      * <strong>Note:</strong> Some platforms may not support
 275      * showing the user-specified title in a file dialog.
 276      * In this situation, either no title will be displayed in the file dialog's
 277      * title bar or, on some systems, the file dialog's title bar will not be
 278      * displayed.
 279      *
 280      * @param     parent   the owner of the dialog
 281      * @param     title    the title of the dialog; a {@code null} value
 282      *                     will be accepted without causing a
 283      *                     {@code NullPointerException} to be thrown
 284      * @exception java.lang.IllegalArgumentException if the {@code parent}'s
 285      *            {@code GraphicsConfiguration}
 286      *            is not from a screen device;
 287      * @exception java.lang.IllegalArgumentException if {@code parent}
 288      *            is {@code null}; this exception is always thrown when
 289      *            {@code GraphicsEnvironment.isHeadless}
 290      *            returns {@code true}
 291      * @see java.awt.GraphicsEnvironment#isHeadless
 292      * @since     1.5
 293      */
 294     public FileDialog(Dialog parent, String title) {
 295         this(parent, title, LOAD);
 296     }
 297 
 298     /**
 299      * Creates a file dialog window with the specified title for loading
 300      * or saving a file.
 301      * <p>
 302      * If the value of {@code mode} is {@code LOAD}, then the
 303      * file dialog is finding a file to read, and the files shown are those
 304      * in the current directory.   If the value of
 305      * {@code mode} is {@code SAVE}, the file dialog is finding
 306      * a place to write a file.
 307      * <p>
 308      * <strong>Note:</strong> Some platforms may not support
 309      * showing the user-specified title in a file dialog.
 310      * In this situation, either no title will be displayed in the file dialog's
 311      * title bar or, on some systems, the file dialog's title bar will not be
 312      * displayed.
 313      *
 314      * @param     parent   the owner of the dialog
 315      * @param     title    the title of the dialog; a {@code null} value
 316      *                     will be accepted without causing a
 317      *                     {@code NullPointerException} to be thrown
 318      * @param     mode     the mode of the dialog; either
 319      *                     {@code FileDialog.LOAD} or {@code FileDialog.SAVE}
 320      * @exception java.lang.IllegalArgumentException if an illegal
 321      *            file dialog mode is supplied;
 322      * @exception java.lang.IllegalArgumentException if the {@code parent}'s
 323      *            {@code GraphicsConfiguration}
 324      *            is not from a screen device;
 325      * @exception java.lang.IllegalArgumentException if {@code parent}
 326      *            is {@code null}; this exception is always thrown when
 327      *            {@code GraphicsEnvironment.isHeadless}
 328      *            returns {@code true}
 329      * @see       java.awt.GraphicsEnvironment#isHeadless
 330      * @see       java.awt.FileDialog#LOAD
 331      * @see       java.awt.FileDialog#SAVE
 332      * @since     1.5
 333      */
 334     public FileDialog(Dialog parent, String title, int mode) {
 335         super(parent, title, true);
 336         this.setMode(mode);
 337         setLayout(null);
 338     }
 339 
 340 
 341     /**
 342      * {@inheritDoc}
 343      * <p>
 344      * <strong>Note:</strong> Some platforms may not support
 345      * showing the user-specified title in a file dialog.
 346      * In this situation, either no title will be displayed in the file dialog's
 347      * title bar or, on some systems, the file dialog's title bar will not be
 348      * displayed.
 349      */
 350     @Override
 351     public void setTitle(String title) {
 352         super.setTitle(title);
 353     }
 354 
 355 
 356     /**
 357      * Constructs a name for this component. Called by {@code getName()}
 358      * when the name is {@code null}.
 359      */
 360     String constructComponentName() {
 361         synchronized (FileDialog.class) {
 362             return base + nameCounter++;
 363         }
 364     }
 365 
 366     /**
 367      * Creates the file dialog's peer.  The peer allows us to change the look
 368      * of the file dialog without changing its functionality.
 369      */
 370     public void addNotify() {
 371         synchronized(getTreeLock()) {
 372             if (parent != null && parent.peer == null) {
 373                 parent.addNotify();
 374             }
 375             if (peer == null)
 376                 peer = getComponentFactory().createFileDialog(this);
 377             super.addNotify();
 378         }
 379     }
 380 
 381     /**
 382      * Indicates whether this file dialog box is for loading from a file
 383      * or for saving to a file.
 384      *
 385      * @return   the mode of this file dialog window, either
 386      *               {@code FileDialog.LOAD} or
 387      *               {@code FileDialog.SAVE}
 388      * @see      java.awt.FileDialog#LOAD
 389      * @see      java.awt.FileDialog#SAVE
 390      * @see      java.awt.FileDialog#setMode
 391      */
 392     public int getMode() {
 393         return mode;
 394     }
 395 
 396     /**
 397      * Sets the mode of the file dialog.  If {@code mode} is not
 398      * a legal value, an exception will be thrown and {@code mode}
 399      * will not be set.
 400      *
 401      * @param      mode  the mode for this file dialog, either
 402      *                 {@code FileDialog.LOAD} or
 403      *                 {@code FileDialog.SAVE}
 404      * @see        java.awt.FileDialog#LOAD
 405      * @see        java.awt.FileDialog#SAVE
 406      * @see        java.awt.FileDialog#getMode
 407      * @exception  IllegalArgumentException if an illegal file
 408      *                 dialog mode is supplied
 409      * @since      1.1
 410      */
 411     public void setMode(int mode) {
 412         switch (mode) {
 413           case LOAD:
 414           case SAVE:
 415             this.mode = mode;
 416             break;
 417           default:
 418             throw new IllegalArgumentException("illegal file dialog mode");
 419         }
 420     }
 421 
 422     /**
 423      * Gets the directory of this file dialog.
 424      *
 425      * @return  the (potentially {@code null} or invalid)
 426      *          directory of this {@code FileDialog}
 427      * @see       java.awt.FileDialog#setDirectory
 428      */
 429     public String getDirectory() {
 430         return dir;
 431     }
 432 
 433     /**
 434      * Sets the directory of this file dialog window to be the
 435      * specified directory. Specifying a {@code null} or an
 436      * invalid directory implies an implementation-defined default.
 437      * This default will not be realized, however, until the user
 438      * has selected a file. Until this point, {@code getDirectory()}
 439      * will return the value passed into this method.
 440      * <p>
 441      * Specifying "" as the directory is exactly equivalent to
 442      * specifying {@code null} as the directory.
 443      *
 444      * @param     dir   the specified directory
 445      * @see       java.awt.FileDialog#getDirectory
 446      */
 447     public void setDirectory(String dir) {
 448         this.dir = (dir != null && dir.isEmpty()) ? null : dir;
 449         FileDialogPeer peer = (FileDialogPeer)this.peer;
 450         if (peer != null) {
 451             peer.setDirectory(this.dir);
 452         }
 453     }
 454 
 455     /**
 456      * Gets the selected file of this file dialog.  If the user
 457      * selected {@code CANCEL}, the returned file is {@code null}.
 458      *
 459      * @return    the currently selected file of this file dialog window,
 460      *                or {@code null} if none is selected
 461      * @see       java.awt.FileDialog#setFile
 462      */
 463     public String getFile() {
 464         return file;
 465     }
 466 
 467     /**
 468      * Returns files that the user selects.
 469      * <p>
 470      * If the user cancels the file dialog,
 471      * then the method returns an empty array.
 472      *
 473      * @return    files that the user selects or an empty array
 474      *            if the user cancels the file dialog.
 475      * @see       #setFile(String)
 476      * @see       #getFile
 477      * @since 1.7
 478      */
 479     public File[] getFiles() {
 480         synchronized (getObjectLock()) {
 481             if (files != null) {
 482                 return files.clone();
 483             } else {
 484                 return new File[0];
 485             }
 486         }
 487     }
 488 
 489     /**
 490      * Stores the names of all the files that the user selects.
 491      *
 492      * Note that the method is private and it's intended to be used
 493      * by the peers through the AWTAccessor API.
 494      *
 495      * @param files     the array that contains the short names of
 496      *                  all the files that the user selects.
 497      *
 498      * @see #getFiles
 499      * @since 1.7
 500      */
 501     private void setFiles(File[] files) {
 502         synchronized (getObjectLock()) {
 503             this.files = files;
 504         }
 505     }
 506 
 507     /**
 508      * Sets the selected file for this file dialog window to be the
 509      * specified file. This file becomes the default file if it is set
 510      * before the file dialog window is first shown.
 511      * <p>
 512      * When the dialog is shown, the specified file is selected. The kind of
 513      * selection depends on the file existence, the dialog type, and the native
 514      * platform. E.g., the file could be highlighted in the file list, or a
 515      * file name editbox could be populated with the file name.
 516      * <p>
 517      * This method accepts either a full file path, or a file name with an
 518      * extension if used together with the {@code setDirectory} method.
 519      * <p>
 520      * Specifying "" as the file is exactly equivalent to specifying
 521      * {@code null} as the file.
 522      *
 523      * @param    file   the file being set
 524      * @see      #getFile
 525      * @see      #getFiles
 526      */
 527     public void setFile(String file) {
 528         this.file = (file != null && file.isEmpty()) ? null : file;
 529         FileDialogPeer peer = (FileDialogPeer)this.peer;
 530         if (peer != null) {
 531             peer.setFile(this.file);
 532         }
 533     }
 534 
 535     /**
 536      * Enables or disables multiple file selection for the file dialog.
 537      *
 538      * @param enable    if {@code true}, multiple file selection is enabled;
 539      *                  {@code false} - disabled.
 540      * @see #isMultipleMode
 541      * @since 1.7
 542      */
 543     public void setMultipleMode(boolean enable) {
 544         synchronized (getObjectLock()) {
 545             this.multipleMode = enable;
 546         }
 547     }
 548 
 549     /**
 550      * Returns whether the file dialog allows the multiple file selection.
 551      *
 552      * @return          {@code true} if the file dialog allows the multiple
 553      *                  file selection; {@code false} otherwise.
 554      * @see #setMultipleMode
 555      * @since 1.7
 556      */
 557     public boolean isMultipleMode() {
 558         synchronized (getObjectLock()) {
 559             return multipleMode;
 560         }
 561     }
 562 
 563     /**
 564      * Determines this file dialog's filename filter. A filename filter
 565      * allows the user to specify which files appear in the file dialog
 566      * window.  Filename filters do not function in Sun's reference
 567      * implementation for Microsoft Windows.
 568      *
 569      * @return    this file dialog's filename filter
 570      * @see       java.io.FilenameFilter
 571      * @see       java.awt.FileDialog#setFilenameFilter
 572      */
 573     public FilenameFilter getFilenameFilter() {
 574         return filter;
 575     }
 576 
 577     /**
 578      * Sets the filename filter for this file dialog window to the
 579      * specified filter.
 580      * Filename filters do not function in Sun's reference
 581      * implementation for Microsoft Windows.
 582      *
 583      * @param   filter   the specified filter
 584      * @see     java.io.FilenameFilter
 585      * @see     java.awt.FileDialog#getFilenameFilter
 586      */
 587     public synchronized void setFilenameFilter(FilenameFilter filter) {
 588         this.filter = filter;
 589         FileDialogPeer peer = (FileDialogPeer)this.peer;
 590         if (peer != null) {
 591             peer.setFilenameFilter(filter);
 592         }
 593     }
 594 
 595     /**
 596      * Reads the {@code ObjectInputStream} and performs
 597      * a backwards compatibility check by converting
 598      * either a {@code dir} or a {@code file}
 599      * equal to an empty string to {@code null}.
 600      *
 601      * @param s the {@code ObjectInputStream} to read
 602      */
 603     private void readObject(ObjectInputStream s)
 604         throws ClassNotFoundException, IOException
 605     {
 606         s.defaultReadObject();
 607 
 608         // 1.1 Compatibility: "" is not converted to null in 1.1
 609         if (dir != null && dir.isEmpty()) {
 610             dir = null;
 611         }
 612         if (file != null && file.isEmpty()) {
 613             file = null;
 614         }
 615     }
 616 
 617     /**
 618      * Returns a string representing the state of this {@code FileDialog}
 619      * window. This method is intended to be used only for debugging purposes,
 620      * and the content and format of the returned string may vary between
 621      * implementations. The returned string may be empty but may not be
 622      * {@code null}.
 623      *
 624      * @return  the parameter string of this file dialog window
 625      */
 626     protected String paramString() {
 627         String str = super.paramString();
 628         str += ",dir= " + dir;
 629         str += ",file= " + file;
 630         return str + ((mode == LOAD) ? ",load" : ",save");
 631     }
 632 
 633     boolean postsOldMouseEvents() {
 634         return false;
 635     }
 636 }