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