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