1 /*
   2  * Copyright (c) 2003, 2014, 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 
  26 package sun.awt.X11;
  27 
  28 import java.awt.*;
  29 import javax.swing.*;
  30 import java.awt.event.*;
  31 import java.awt.peer.*;
  32 import java.io.*;
  33 import java.util.Locale;
  34 import java.util.Arrays;
  35 import com.sun.java.swing.plaf.motif.*;
  36 import javax.swing.plaf.ComponentUI;
  37 import java.security.AccessController;
  38 import java.security.PrivilegedAction;
  39 import sun.util.logging.PlatformLogger;
  40 import sun.awt.AWTAccessor;
  41 
  42 class XFileDialogPeer extends XDialogPeer implements FileDialogPeer, ActionListener, ItemListener, KeyEventDispatcher, XChoicePeerListener {
  43     private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XFileDialogPeer");
  44 
  45     FileDialog  target;
  46 
  47     // This variable holds value exactly the same as value of the 'target.file' variable except:
  48     // 1) is changed to null after quit (see handleQuitButton())
  49     // 2) keep the same value if 'target.file' is incorrect (see setFile())
  50     // It's not clear HOW we used it
  51     // We should think about existence of this variable
  52     String      file;
  53 
  54     String      dir;
  55 
  56     String      title;
  57     int         mode;
  58     FilenameFilter  filter;
  59 
  60     private static final int PATH_CHOICE_WIDTH = 20;
  61 
  62     // Seems that the purpose of this variable is cashing of 'target.file' variable in order to help method show()
  63     // We should think about using 'target.file' instead of 'savedFile'
  64     // Perhaps, 'target.file' just more correct (see target.setFile())
  65     String      savedFile;
  66 
  67     // Holds value of the directory which was chosen before
  68     // We use it in order to restore previously selected directory
  69     // at the time of the next showing of the file dialog
  70     String      savedDir;
  71     // Holds value of the system property 'user.dir'
  72     // in order to init current directory
  73     String      userDir;
  74 
  75     Dialog      fileDialog;
  76 
  77     GridBagLayout       gbl;
  78     GridBagLayout       gblButtons;
  79     GridBagConstraints  gbc;
  80 
  81     // ************** Components in the fileDialogWindow ***************
  82 
  83     TextField   filterField;
  84 
  85     // This variable holds the current text of the file which user select through the navigation
  86     // It's important that updating of this variable must be correct
  87     // since this value is used at the time of the file dialog closing
  88     // Namely, we invoke target.setFile() and then user can get this value
  89     // We update this field in cases:
  90     // - ITEM_STATE_CHANGED was triggered on the file list component: set to the current selected item
  91     // - at the time of the 'show': set to savedFile
  92     // - at the time of the programmatically setting: set to new value
  93     TextField   selectionField;
  94 
  95     List        directoryList;
  96 
  97     // This is the list component which is used for the showing of the file list of the current directory
  98     List        fileList;
  99 
 100     Panel       buttons;
 101     Button      openButton;
 102     Button      filterButton;
 103     Button      cancelButton;
 104     Choice      pathChoice;
 105     TextField   pathField;
 106     Panel       pathPanel;
 107 
 108     String cancelButtonText = null;
 109     String enterFileNameLabelText = null;
 110     String filesLabelText= null;
 111     String foldersLabelText= null;
 112     String pathLabelText= null;
 113     String filterLabelText= null;
 114     String openButtonText= null;
 115     String saveButtonText= null;
 116     String actionButtonText= null;
 117 
 118 
 119     void installStrings() {
 120         Locale l = target.getLocale();
 121         UIDefaults uid = XToolkit.getUIDefaults();
 122         cancelButtonText = uid.getString("FileChooser.cancelButtonText",l);
 123         enterFileNameLabelText = uid.getString("FileChooser.enterFileNameLabelText",l);
 124         filesLabelText = uid.getString("FileChooser.filesLabelText",l);
 125         foldersLabelText = uid.getString("FileChooser.foldersLabelText",l);
 126         pathLabelText = uid.getString("FileChooser.pathLabelText",l);
 127         filterLabelText = uid.getString("FileChooser.filterLabelText",l);
 128         openButtonText = uid.getString("FileChooser.openButtonText",l);
 129         saveButtonText  = uid.getString("FileChooser.saveButtonText",l);
 130 
 131     }
 132 
 133     XFileDialogPeer(FileDialog target) {
 134         super((Dialog)target);
 135         this.target = target;
 136     }
 137 
 138     private void init(FileDialog target) {
 139         fileDialog = target; //new Dialog(target, target.getTitle(), false);
 140         this.title = target.getTitle();
 141         this.mode = target.getMode();
 142         this.target = target;
 143         this.filter = target.getFilenameFilter();
 144 
 145         savedFile = target.getFile();
 146         savedDir = target.getDirectory();
 147         // Shouldn't save 'user.dir' to 'savedDir'
 148         // since getDirectory() will be incorrect after handleCancel
 149         userDir = (String)AccessController.doPrivileged(
 150             new PrivilegedAction() {
 151                 public Object run() {
 152                     return System.getProperty("user.dir");
 153                 }
 154             });
 155 
 156         installStrings();
 157         gbl = new GridBagLayout();
 158         gblButtons = new GridBagLayout();
 159         gbc = new GridBagConstraints();
 160         fileDialog.setLayout(gbl);
 161 
 162         // create components
 163         buttons = new Panel();
 164         buttons.setLayout(gblButtons);
 165         actionButtonText = (target.getMode() == FileDialog.SAVE) ? saveButtonText : openButtonText;
 166         openButton = new Button(actionButtonText);
 167 
 168         filterButton = new Button(filterLabelText);
 169         cancelButton = new Button(cancelButtonText);
 170         directoryList = new List();
 171         fileList = new List();
 172         filterField = new TextField();
 173         selectionField = new TextField();
 174 
 175         boolean isMultipleMode =
 176             AWTAccessor.getFileDialogAccessor().isMultipleMode(target);
 177         fileList.setMultipleMode(isMultipleMode);
 178 
 179         // the insets used by the components in the fileDialog
 180         Insets noInset = new Insets(0, 0, 0, 0);
 181         Insets textFieldInset = new Insets(0, 8, 0, 8);
 182         Insets leftListInset = new Insets(0, 8, 0, 4);
 183         Insets rightListInset = new Insets(0, 4, 0, 8);
 184         Insets separatorInset = new Insets(8, 0, 0, 0);
 185         Insets labelInset = new Insets(0, 8, 0, 0);
 186         Insets buttonsInset = new Insets(10, 8, 10, 8);
 187 
 188         // add components to GridBagLayout "gbl"
 189 
 190         Font f = new Font(Font.DIALOG, Font.PLAIN, 12);
 191 
 192         Label label = new Label(pathLabelText);
 193         label.setFont(f);
 194         addComponent(label, gbl, gbc, 0, 0, 1,
 195                      GridBagConstraints.WEST, (Container)fileDialog,
 196                      1, 0, GridBagConstraints.NONE, labelInset);
 197 
 198         // Fixed 6260650: FileDialog.getDirectory() does not return null when file dialog is cancelled
 199         // After showing we should display 'user.dir' as current directory
 200         // if user didn't set directory programatically
 201         pathField = new TextField(savedDir != null ? savedDir : userDir);
 202 
 203         pathChoice = new Choice() {
 204                 public Dimension getPreferredSize() {
 205                     return new Dimension(PATH_CHOICE_WIDTH, pathField.getPreferredSize().height);
 206                 }
 207             };
 208         pathPanel = new Panel();
 209         pathPanel.setLayout(new BorderLayout());
 210 
 211         pathPanel.add(pathField,BorderLayout.CENTER);
 212         pathPanel.add(pathChoice,BorderLayout.EAST);
 213         //addComponent(pathField, gbl, gbc, 0, 1, 2,
 214         //             GridBagConstraints.WEST, (Container)fileDialog,
 215         //             1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
 216         //addComponent(pathChoice, gbl, gbc, 1, 1, GridBagConstraints.RELATIVE,
 217          //            GridBagConstraints.WEST, (Container)fileDialog,
 218           //           1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
 219         addComponent(pathPanel, gbl, gbc, 0, 1, 2,
 220                     GridBagConstraints.WEST, (Container)fileDialog,
 221                    1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
 222 
 223 
 224 
 225         label = new Label(filterLabelText);
 226 
 227         label.setFont(f);
 228         addComponent(label, gbl, gbc, 0, 2, 1,
 229                      GridBagConstraints.WEST, (Container)fileDialog,
 230                      1, 0, GridBagConstraints.NONE, labelInset);
 231         addComponent(filterField, gbl, gbc, 0, 3, 2,
 232                      GridBagConstraints.WEST, (Container)fileDialog,
 233                      1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
 234 
 235         label = new Label(foldersLabelText);
 236 
 237         label.setFont(f);
 238         addComponent(label, gbl, gbc, 0, 4, 1,
 239                      GridBagConstraints.WEST, (Container)fileDialog,
 240                      1, 0, GridBagConstraints.NONE, labelInset);
 241 
 242         label = new Label(filesLabelText);
 243 
 244         label.setFont(f);
 245         addComponent(label, gbl, gbc, 1, 4, 1,
 246                      GridBagConstraints.WEST, (Container)fileDialog,
 247                      1, 0, GridBagConstraints.NONE, labelInset);
 248         addComponent(directoryList, gbl, gbc, 0, 5, 1,
 249                      GridBagConstraints.WEST, (Container)fileDialog,
 250                      1, 1, GridBagConstraints.BOTH, leftListInset);
 251         addComponent(fileList, gbl, gbc, 1, 5, 1,
 252                      GridBagConstraints.WEST, (Container)fileDialog,
 253                      1, 1, GridBagConstraints.BOTH, rightListInset);
 254 
 255         label = new Label(enterFileNameLabelText);
 256 
 257         label.setFont(f);
 258         addComponent(label, gbl, gbc, 0, 6, 1,
 259                      GridBagConstraints.WEST, (Container)fileDialog,
 260                      1, 0, GridBagConstraints.NONE, labelInset);
 261         addComponent(selectionField, gbl, gbc, 0, 7, 2,
 262                      GridBagConstraints.WEST, (Container)fileDialog,
 263                      1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
 264         addComponent(new Separator(fileDialog.size().width, 2, Separator.HORIZONTAL), gbl, gbc, 0, 8, 15,
 265                      GridBagConstraints.WEST, (Container)fileDialog,
 266                      1, 0, GridBagConstraints.HORIZONTAL, separatorInset);
 267 
 268         // add buttons to GridBagLayout Buttons
 269         addComponent(openButton, gblButtons, gbc, 0, 0, 1,
 270                      GridBagConstraints.WEST, (Container)buttons,
 271                      1, 0, GridBagConstraints.NONE, noInset);
 272         addComponent(filterButton, gblButtons, gbc, 1, 0, 1,
 273                      GridBagConstraints.CENTER, (Container)buttons,
 274                      1, 0, GridBagConstraints.NONE, noInset);
 275         addComponent(cancelButton, gblButtons, gbc, 2, 0, 1,
 276                      GridBagConstraints.EAST, (Container)buttons,
 277                      1, 0, GridBagConstraints.NONE, noInset);
 278 
 279         // add ButtonPanel to the GridBagLayout of this class
 280         addComponent(buttons, gbl, gbc, 0, 9, 2,
 281                      GridBagConstraints.WEST, (Container)fileDialog,
 282                      1, 0, GridBagConstraints.HORIZONTAL, buttonsInset);
 283 
 284         fileDialog.setSize(400, 400);
 285 
 286         // Update choice's popup width
 287         XChoicePeer choicePeer = (XChoicePeer)pathChoice.getPeer();
 288         choicePeer.setDrawSelectedItem(false);
 289         choicePeer.setAlignUnder(pathField);
 290 
 291         filterField.addActionListener(this);
 292         selectionField.addActionListener(this);
 293         directoryList.addActionListener(this);
 294         directoryList.addItemListener(this);
 295         fileList.addItemListener(this);
 296         fileList.addActionListener(this);
 297         openButton.addActionListener(this);
 298         filterButton.addActionListener(this);
 299         cancelButton.addActionListener(this);
 300         pathChoice.addItemListener(this);
 301         pathField.addActionListener(this);
 302 
 303         // b6227750 FileDialog is not disposed when clicking the 'close' (X) button on the top right corner, XToolkit
 304         target.addWindowListener(
 305             new WindowAdapter(){
 306                 public void windowClosing(WindowEvent e){
 307                     handleCancel();
 308                 }
 309             }
 310         );
 311 
 312         // 6259434 PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit
 313         pathChoice.addItemListener(this);
 314 
 315     }
 316 
 317     public void updateMinimumSize() {
 318     }
 319 
 320     public void updateIconImages() {
 321         if (winAttr.icons == null){
 322             winAttr.iconsInherited = false;
 323             winAttr.icons = getDefaultIconInfo();
 324             setIconHints(winAttr.icons);
 325         }
 326     }
 327 
 328     /**
 329      * add Component comp to the container cont.
 330      * add the component to the correct GridBagLayout
 331      */
 332     void addComponent(Component comp, GridBagLayout gb, GridBagConstraints c, int gridx,
 333                       int gridy, int gridwidth, int anchor, Container cont, int weightx, int weighty,
 334                       int fill, Insets in) {
 335         c.gridx = gridx;
 336         c.gridy = gridy;
 337         c.gridwidth = gridwidth;
 338         c.anchor = anchor;
 339         c.weightx = weightx;
 340         c.weighty = weighty;
 341         c.fill = fill;
 342         c.insets = in;
 343         gb.setConstraints(comp, c);
 344         cont.add(comp);
 345     }
 346 
 347     /**
 348      * get fileName
 349      */
 350     String getFileName(String str) {
 351         if (str == null) {
 352             return "";
 353         }
 354 
 355         int index = str.lastIndexOf('/');
 356 
 357         if (index == -1) {
 358             return str;
 359         } else {
 360             return str.substring(index + 1);
 361         }
 362     }
 363 
 364     /** handleFilter
 365      *
 366      */
 367     void handleFilter(String f) {
 368 
 369         if (f == null) {
 370             return;
 371         }
 372         setFilterEntry(dir,f);
 373 
 374         // Fixed within 6259434: PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit
 375         // Here we restoring Motif behaviour
 376         directoryList.select(0);
 377         if (fileList.getItemCount() != 0) {
 378             fileList.requestFocus();
 379         } else {
 380             directoryList.requestFocus();
 381         }
 382     }
 383 
 384     /**
 385      * handle the selection event
 386      */
 387     void handleSelection(String file) {
 388 
 389         int index = file.lastIndexOf(java.io.File.separatorChar);
 390 
 391         if (index == -1) {
 392             savedDir = this.dir;
 393             savedFile = file;
 394         } else {
 395             savedDir = file.substring(0, index+1);
 396             savedFile = file.substring(index+1);
 397         }
 398 
 399         String[] fileNames = fileList.getSelectedItems();
 400         int filesNumber = (fileNames != null) ? fileNames.length : 0;
 401         File[] files = new File[filesNumber];
 402         for (int i = 0; i < filesNumber; i++) {
 403             files[i] = new File(savedDir, fileNames[i]);
 404         }
 405 
 406         AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor();
 407 
 408         fileDialogAccessor.setDirectory(target, savedDir);
 409         fileDialogAccessor.setFile(target, savedFile);
 410         fileDialogAccessor.setFiles(target, files);
 411     }
 412 
 413     /**
 414      * handle the cancel event
 415      */
 416     void handleCancel() {
 417         KeyboardFocusManager.getCurrentKeyboardFocusManager()
 418             .removeKeyEventDispatcher(this);
 419 
 420         setSelectionField(null);
 421         setFilterField(null);
 422         directoryList.clear();
 423         fileList.clear();
 424 
 425         AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor();
 426 
 427         fileDialogAccessor.setDirectory(target, null);
 428         fileDialogAccessor.setFile(target, null);
 429         fileDialogAccessor.setFiles(target, null);
 430 
 431         handleQuitButton();
 432     }
 433 
 434     /**
 435      * handle the quit event
 436      */
 437     void handleQuitButton() {
 438         dir = null;
 439         file = null;
 440         target.hide();
 441     }
 442 
 443     /**
 444      * set the entry of the new dir with f
 445      */
 446     void setFilterEntry(String d, String f) {
 447         File fe = new File(d);
 448 
 449         if (fe.isDirectory() && fe.canRead()) {
 450             // Fixed 6260659: File Name set programmatically in FileDialog is overridden during navigation, XToolkit
 451             // Here we restoring Motif behaviour
 452             setSelectionField(target.getFile());
 453 
 454             if (f.equals("")) {
 455                 f = "*";
 456                 setFilterField(f);
 457             } else {
 458                 setFilterField(f);
 459             }
 460             String l[];
 461 
 462             if (f.equals("*")) {
 463                 l = fe.list();
 464             } else {
 465                 // REMIND: fileDialogFilter is not implemented yet
 466                 FileDialogFilter ff = new FileDialogFilter(f);
 467                 l = fe.list(ff);
 468             }
 469             // Fixed 6358953: handling was added in case of I/O error happens
 470             if (l == null) {
 471                 this.dir = getParentDirectory();
 472                 return;
 473             }
 474             directoryList.clear();
 475             fileList.clear();
 476             directoryList.setVisible(false);
 477             fileList.setVisible(false);
 478 
 479             directoryList.addItem("..");
 480             Arrays.sort(l);
 481             for (int i = 0 ; i < l.length ; i++) {
 482                 File file = new File(d + l[i]);
 483                 if (file.isDirectory()) {
 484                     directoryList.addItem(l[i] + "/");
 485                 } else {
 486                     if (filter != null) {
 487                         if (filter.accept(new File(l[i]),l[i]))  fileList.addItem(l[i]);
 488                     }
 489                     else fileList.addItem(l[i]);
 490                 }
 491             }
 492             this.dir = d;
 493 
 494             pathField.setText(dir);
 495 
 496             // Some code was removed
 497             // Now we do updating of the pathChoice at the time of the choice opening
 498 
 499             target.setDirectory(this.dir);
 500             directoryList.setVisible(true);
 501             fileList.setVisible(true);
 502         }
 503     }
 504 
 505 
 506     String[] getDirList(String dir) {
 507         if (!dir.endsWith("/"))
 508             dir = dir + "/";
 509         char[] charr = dir.toCharArray();
 510         int numSlashes = 0;
 511         for (int i=0;i<charr.length;i++) {
 512            if (charr[i] == '/')
 513                numSlashes++;
 514         }
 515         String[] starr =  new String[numSlashes];
 516         int j=0;
 517         for (int i=charr.length-1;i>=0;i--) {
 518             if (charr[i] == '/')
 519             {
 520                 starr[j++] = new String(charr,0,i+1);
 521             }
 522         }
 523         return starr;
 524     }
 525 
 526     /**
 527      * set the text in the selectionField
 528      */
 529     void setSelectionField(String str) {
 530         selectionField.setText(str);
 531     }
 532 
 533     /**
 534      * set the text in the filterField
 535      */
 536     void setFilterField(String str) {
 537         filterField.setText(str);
 538     }
 539 
 540     /**
 541      *
 542      * @see java.awt.event.ItemEvent
 543      * ItemEvent.ITEM_STATE_CHANGED
 544      */
 545     public void itemStateChanged(ItemEvent itemEvent){
 546         if (itemEvent.getID() != ItemEvent.ITEM_STATE_CHANGED ||
 547             itemEvent.getStateChange() == ItemEvent.DESELECTED) {
 548             return;
 549         }
 550 
 551         Object source = itemEvent.getSource();
 552 
 553         if (source == pathChoice) {
 554             /*
 555              * Update the selection ('folder name' text field) after
 556              * the current item changing in the unfurled choice by the arrow keys.
 557              * See 6259434, 6240074 for more information
 558              */
 559             String dir = pathChoice.getSelectedItem();
 560             pathField.setText(dir);
 561         } else if (directoryList == source) {
 562             setFilterField(getFileName(filterField.getText()));
 563         } else if (fileList == source) {
 564             String file = fileList.getItem((Integer)itemEvent.getItem());
 565             setSelectionField(file);
 566         }
 567     }
 568 
 569     /*
 570      * Updates the current directory only if directoryList-specific
 571      * action occurred. Returns false if the forward directory is inaccessible
 572      */
 573     boolean updateDirectoryByUserAction(String str) {
 574 
 575         String dir;
 576         if (str.equals("..")) {
 577             dir = getParentDirectory();
 578         }
 579         else {
 580             dir = this.dir + str;
 581         }
 582 
 583         File fe = new File(dir);
 584         if (fe.canRead()) {
 585             this.dir = dir;
 586             return true;
 587         }else {
 588             return false;
 589         }
 590     }
 591 
 592     String getParentDirectory(){
 593         String parent = this.dir;
 594         if (!this.dir.equals("/"))   // If the current directory is "/" leave it alone.
 595         {
 596             if (dir.endsWith("/"))
 597                 parent = parent.substring(0,parent.lastIndexOf("/"));
 598 
 599             parent = parent.substring(0,parent.lastIndexOf("/")+1);
 600         }
 601         return parent;
 602     }
 603 
 604     public void actionPerformed( ActionEvent actionEvent ) {
 605         String actionCommand = actionEvent.getActionCommand();
 606         Object source = actionEvent.getSource();
 607 
 608         if (actionCommand.equals(actionButtonText)) {
 609             handleSelection( selectionField.getText() );
 610             handleQuitButton();
 611         } else if (actionCommand.equals(filterLabelText)) {
 612             handleFilter( filterField.getText() );
 613         } else if (actionCommand.equals(cancelButtonText)) {
 614             handleCancel();
 615         } else if ( source instanceof TextField ) {
 616             if ( selectionField == ((TextField)source) ) {
 617                 // Fixed within 6259434: PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit
 618                 // We should handle the action based on the selection field
 619                 // Looks like mistake
 620                 handleSelection(selectionField.getText());
 621                 handleQuitButton();
 622             } else if (filterField == ((TextField)source)) {
 623                 handleFilter(filterField.getText());
 624             } else if (pathField == ((TextField)source)) {
 625                 target.setDirectory(pathField.getText());
 626             }
 627         } else if (source instanceof List) {
 628             if (directoryList == ((List)source)) {
 629                 //handleFilter( actionCommand + getFileName( filterField.getText() ) );
 630                 if (updateDirectoryByUserAction(actionCommand)){
 631                     handleFilter( getFileName( filterField.getText() ) );
 632                 }
 633             } else if (fileList == ((List)source)) {
 634                 handleSelection( actionCommand );
 635                 handleQuitButton();
 636             }
 637         }
 638     }
 639 
 640     public boolean dispatchKeyEvent(KeyEvent keyEvent) {
 641         int id = keyEvent.getID();
 642         int keyCode = keyEvent.getKeyCode();
 643 
 644         if (id == KeyEvent.KEY_PRESSED && keyCode == KeyEvent.VK_ESCAPE) {
 645             synchronized (target.getTreeLock()) {
 646                 Component comp = (Component) keyEvent.getSource();
 647                 while (comp != null) {
 648                     // Fix for 6240084 Disposing a file dialog when the drop-down is active does not dispose the dropdown menu, on Xtoolkit
 649                     // See also 6259493
 650                     if (comp == pathChoice) {
 651                         XChoicePeer choicePeer = (XChoicePeer)pathChoice.getPeer();
 652                         if (choicePeer.isUnfurled()){
 653                             return false;
 654                         }
 655                     }
 656                     if (comp.getPeer() == this) {
 657                         handleCancel();
 658                         return true;
 659                     }
 660                     comp = comp.getParent();
 661                 }
 662             }
 663         }
 664 
 665         return false;
 666     }
 667 
 668 
 669     /**
 670      * set the file
 671      */
 672     public void setFile(String file) {
 673 
 674         if (file == null) {
 675             this.file = null;
 676             return;
 677         }
 678 
 679         if (this.dir == null) {
 680             String d = "./";
 681             File f = new File(d, file);
 682 
 683             if (f.isFile()) {
 684                 this.file = file;
 685                 setDirectory(d);
 686             }
 687         } else {
 688             File f = new File(this.dir, file);
 689             if (f.isFile()) {
 690                 this.file = file;
 691             }
 692         }
 693 
 694         setSelectionField(file);
 695     }
 696 
 697     /**
 698      * set the directory
 699      * FIXME: we should update 'savedDir' after programmatically 'setDirectory'
 700      * Otherwise, SavedDir will be not null before second showing
 701      * So the current directory of the file dialog will be incorrect after second showing
 702      * since 'setDirectory' will be ignored
 703      * We cann't update savedDir here now since it used very often
 704      */
 705     public void setDirectory(String dir) {
 706 
 707         if (dir == null) {
 708             this.dir = null;
 709             return;
 710         }
 711 
 712         if (dir.equals(this.dir)) {
 713             return;
 714         }
 715 
 716         int i;
 717         if ((i=dir.indexOf("~")) != -1) {
 718 
 719             dir = dir.substring(0,i) + System.getProperty("user.home") + dir.substring(i+1,dir.length());
 720         }
 721 
 722         File fe = new File(dir).getAbsoluteFile();
 723         if (log.isLoggable(PlatformLogger.Level.FINE)) {
 724             log.fine("Current directory : " + fe);
 725         }
 726 
 727         if (!fe.isDirectory()) {
 728             dir = "./";
 729             fe = new File(dir).getAbsoluteFile();
 730 
 731             if (!fe.isDirectory()) {
 732                 return;
 733             }
 734         }
 735         try {
 736             dir = this.dir = fe.getCanonicalPath();
 737         } catch (java.io.IOException ie) {
 738             dir = this.dir = fe.getAbsolutePath();
 739         }
 740         pathField.setText(this.dir);
 741 
 742 
 743         if (dir.endsWith("/")) {
 744             this.dir = dir;
 745             handleFilter("");
 746         } else {
 747             this.dir = dir + "/";
 748             handleFilter("");
 749         }
 750 
 751         // Some code was removed
 752         // Now we do updating of the pathChoice at the time of the choice opening
 753         // Fixed problem:
 754         // The exception java.awt.IllegalComponentStateException will be thrown
 755         // if the user invoke setDirectory after the closing of the file dialog
 756     }
 757 
 758     /**
 759      * set filenameFilter
 760      *
 761      */
 762     public void setFilenameFilter(FilenameFilter filter) {
 763         this.filter = filter;
 764     }
 765 
 766 
 767     public void dispose() {
 768         FileDialog fd = (FileDialog)fileDialog;
 769         if (fd != null) {
 770             fd.removeAll();
 771         }
 772         super.dispose();
 773     }
 774 
 775     // 03/02/2005 b5097243 Pressing 'ESC' on a file dlg does not dispose the dlg on Xtoolkit
 776     public void setVisible(boolean b){
 777         if (fileDialog == null) {
 778             init(target);
 779         }
 780 
 781         if (savedDir != null || userDir != null) {
 782             setDirectory(savedDir != null ? savedDir : userDir);
 783         }
 784 
 785         if (savedFile != null) {
 786             // Actually in Motif implementation lost file value which was saved after prevously showing
 787             // Seems we shouldn't restore Motif behaviour in this case
 788             setFile(savedFile);
 789         }
 790 
 791         super.setVisible(b);
 792         if (b == true){
 793             // See 6240074 for more information
 794             XChoicePeer choicePeer = (XChoicePeer)pathChoice.getPeer();
 795             choicePeer.addXChoicePeerListener(this);
 796             KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
 797         }else{
 798             // See 6240074 for more information
 799             XChoicePeer choicePeer = (XChoicePeer)pathChoice.getPeer();
 800             choicePeer.removeXChoicePeerListener();
 801             KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this);
 802         }
 803 
 804         selectionField.requestFocusInWindow();
 805     }
 806 
 807     /*
 808      * Adding items to the path choice based on the text string
 809      * See 6240074 for more information
 810      */
 811     public void addItemsToPathChoice(String text){
 812         String dirList[] = getDirList(text);
 813         for (int i = 0; i < dirList.length; i++) pathChoice.addItem(dirList[i]);
 814     }
 815 
 816     /*
 817      * Refresh the unfurled choice at the time of the opening choice according to the text of the path field
 818      * See 6240074 for more information
 819      */
 820     public void unfurledChoiceOpening(ListHelper choiceHelper){
 821 
 822         // When the unfurled choice is opening the first time, we need only to add elements, otherwise we've got exception
 823         if (choiceHelper.getItemCount() == 0){
 824             addItemsToPathChoice(pathField.getText());
 825             return;
 826         }
 827 
 828         // If the set of the directories the exactly same as the used to be then dummy
 829         if (pathChoice.getItem(0).equals(pathField.getText()))
 830             return;
 831 
 832         pathChoice.removeAll();
 833         addItemsToPathChoice(pathField.getText());
 834     }
 835 
 836     /*
 837      * Refresh the file dialog at the time of the closing choice according to the selected item of the choice
 838      * See 6240074 for more information
 839      */
 840     public void unfurledChoiceClosing(){
 841           // This is the exactly same code as invoking later at the time of the itemStateChanged
 842           // Here is we restore Windows behaviour: change current directory if user press 'ESC'
 843           String dir = pathChoice.getSelectedItem();
 844           target.setDirectory(dir);
 845     }
 846 }
 847 
 848 class Separator extends Canvas {
 849     public final static int HORIZONTAL = 0;
 850     public final static int VERTICAL = 1;
 851     int orientation;
 852 
 853     public Separator(int length, int thickness, int orient) {
 854         super();
 855         orientation = orient;
 856         if (orient == HORIZONTAL) {
 857             resize(length, thickness);
 858         } else {
 859             // VERTICAL
 860             resize(thickness, length);
 861         }
 862     }
 863 
 864     public void paint(Graphics g) {
 865         int x1, y1, x2, y2;
 866         Rectangle bbox = bounds();
 867         Color c = getBackground();
 868         Color brighter = c.brighter();
 869         Color darker = c.darker();
 870 
 871         if (orientation == HORIZONTAL) {
 872             x1 = 0;
 873             x2 = bbox.width - 1;
 874             y1 = y2 = bbox.height/2 - 1;
 875 
 876         } else {
 877             // VERTICAL
 878             x1 = x2 = bbox.width/2 - 1;
 879             y1 = 0;
 880             y2 = bbox.height - 1;
 881         }
 882         g.setColor(darker);
 883         g.drawLine(x1, y2, x2, y2);
 884         g.setColor(brighter);
 885         if (orientation == HORIZONTAL)
 886             g.drawLine(x1, y2+1, x2, y2+1);
 887         else
 888             g.drawLine(x1+1, y2, x2+1, y2);
 889     }
 890 }
 891 
 892 /*
 893  * Motif file dialogs let the user specify a filter that controls the files that
 894  * are displayed in the dialog. This filter is generally specified as a regular
 895  * expression. The class is used to implement Motif-like filtering.
 896  */
 897 class FileDialogFilter implements FilenameFilter {
 898 
 899     String filter;
 900 
 901     public FileDialogFilter(String f) {
 902         filter = f;
 903     }
 904 
 905     /*
 906      * Tells whether or not the specified file should be included in a file list
 907      */
 908     public boolean accept(File dir, String fileName) {
 909 
 910         File f = new File(dir, fileName);
 911 
 912         if (f.isDirectory()) {
 913             return true;
 914         } else {
 915             return matches(fileName, filter);
 916         }
 917     }
 918 
 919     /*
 920      * Tells whether or not the input string matches the given filter
 921      */
 922     private boolean matches(String input, String filter) {
 923         String regex = convert(filter);
 924         return input.matches(regex);
 925     }
 926 
 927     /*
 928      * Converts the filter into the form which is acceptable by Java's regexps
 929      */
 930     private String convert(String filter) {
 931         String regex = "^" + filter + "$";
 932         regex = regex.replaceAll("\\.", "\\\\.");
 933         regex = regex.replaceAll("\\?", ".");
 934         regex = regex.replaceAll("\\*", ".*");
 935         return regex;
 936     }
 937 }