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