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