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