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