1 /*
2  * Copyright (c) 2003, 2013, 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 
installStrings()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 
XFileDialogPeer(FileDialog target)133     XFileDialogPeer(FileDialog target) {
134         super((Dialog)target);
135         this.target = target;
136     }
137 
init(FileDialog target)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 
updateMinimumSize()317     public void updateMinimumSize() {
318     }
319 
updateIconImages()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      */
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)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      */
getFileName(String str)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      */
handleFilter(String f)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      */
handleSelection(String file)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         String[] fileNames = fileList.getSelectedItems();
400         int filesNumber = (fileNames != null) ? fileNames.length : 0;
401         File[] files = new File[filesNumber];
402         for (int i = 0; i < filesNumber; i++) {
403             files[i] = new File(savedDir, fileNames[i]);
404         }
405 
406         AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor();
407 
408         fileDialogAccessor.setDirectory(target, savedDir);
409         fileDialogAccessor.setFile(target, savedFile);
410         fileDialogAccessor.setFiles(target, files);
411     }
412 
413     /**
414      * handle the cancel event
415      */
handleCancel()416     void handleCancel() {
417         KeyboardFocusManager.getCurrentKeyboardFocusManager()
418             .removeKeyEventDispatcher(this);
419 
420         setSelectionField(null);
421         setFilterField(null);
422         directoryList.clear();
423         fileList.clear();
424 
425         AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor();
426 
427         fileDialogAccessor.setDirectory(target, null);
428         fileDialogAccessor.setFile(target, null);
429         fileDialogAccessor.setFiles(target, null);
430 
431         handleQuitButton();
432     }
433 
434     /**
435      * handle the quit event
436      */
handleQuitButton()437     void handleQuitButton() {
438         dir = null;
439         file = null;
440         target.hide();
441     }
442 
443     /**
444      * set the entry of the new dir with f
445      */
setFilterEntry(String d, String f)446     void setFilterEntry(String d, String f) {
447         File fe = new File(d);
448 
449         if (fe.isDirectory() && fe.canRead()) {
450             // Fixed 6260659: File Name set programmatically in FileDialog is overridden during navigation, XToolkit
451             // Here we restoring Motif behaviour
452             setSelectionField(target.getFile());
453 
454             if (f.equals("")) {
455                 f = "*";
456                 setFilterField(f);
457             } else {
458                 setFilterField(f);
459             }
460             String l[];
461 
462             if (f.equals("*")) {
463                 l = fe.list();
464             } else {
465                 // REMIND: fileDialogFilter is not implemented yet
466                 FileDialogFilter ff = new FileDialogFilter(f);
467                 l = fe.list(ff);
468             }
469             // Fixed 6358953: handling was added in case of I/O error happens
470             if (l == null) {
471                 this.dir = getParentDirectory();
472                 return;
473             }
474             directoryList.clear();
475             fileList.clear();
476             directoryList.setVisible(false);
477             fileList.setVisible(false);
478 
479             directoryList.addItem("..");
480             Arrays.sort(l);
481             for (int i = 0 ; i < l.length ; i++) {
482                 File file = new File(d + l[i]);
483                 if (file.isDirectory()) {
484                     directoryList.addItem(l[i] + "/");
485                 } else {
486                     if (filter != null) {
487                         if (filter.accept(new File(l[i]),l[i]))  fileList.addItem(l[i]);
488                     }
489                     else fileList.addItem(l[i]);
490                 }
491             }
492             this.dir = d;
493 
494             pathField.setText(dir);
495 
496             // Some code was removed
497             // Now we do updating of the pathChoice at the time of the choice opening
498 
499             target.setDirectory(this.dir);
500             directoryList.setVisible(true);
501             fileList.setVisible(true);
502         }
503     }
504 
505 
getDirList(String dir)506     String[] getDirList(String dir) {
507         if (!dir.endsWith("/"))
508             dir = dir + "/";
509         char[] charr = dir.toCharArray();
510         int numSlashes = 0;
511         for (int i=0;i<charr.length;i++) {
512            if (charr[i] == '/')
513                numSlashes++;
514         }
515         String[] starr =  new String[numSlashes];
516         int j=0;
517         for (int i=charr.length-1;i>=0;i--) {
518             if (charr[i] == '/')
519             {
520                 starr[j++] = new String(charr,0,i+1);
521             }
522         }
523         return starr;
524     }
525 
526     /**
527      * set the text in the selectionField
528      */
setSelectionField(String str)529     void setSelectionField(String str) {
530         selectionField.setText(str);
531     }
532 
533     /**
534      * set the text in the filterField
535      */
setFilterField(String str)536     void setFilterField(String str) {
537         filterField.setText(str);
538     }
539 
540     /**
541      *
542      * @see java.awt.event.ItemEvent
543      * ItemEvent.ITEM_STATE_CHANGED
544      */
itemStateChanged(ItemEvent itemEvent)545     public void itemStateChanged(ItemEvent itemEvent){
546         if (itemEvent.getID() != ItemEvent.ITEM_STATE_CHANGED ||
547             itemEvent.getStateChange() == ItemEvent.DESELECTED) {
548             return;
549         }
550 
551         Object source = itemEvent.getSource();
552 
553         if (source == pathChoice) {
554             /*
555              * Update the selection ('folder name' text field) after
556              * the current item changing in the unfurled choice by the arrow keys.
557              * See 6259434, 6240074 for more information
558              */
559             String dir = pathChoice.getSelectedItem();
560             pathField.setText(dir);
561         } else if (directoryList == source) {
562             setFilterField(getFileName(filterField.getText()));
563         } else if (fileList == source) {
564             String file = fileList.getItem((Integer)itemEvent.getItem());
565             setSelectionField(file);
566         }
567     }
568 
569     /*
570      * Updates the current directory only if directoryList-specific
571      * action occurred. Returns false if the forward directory is inaccessible
572      */
updateDirectoryByUserAction(String str)573     boolean updateDirectoryByUserAction(String str) {
574 
575         String dir;
576         if (str.equals("..")) {
577             dir = getParentDirectory();
578         }
579         else {
580             dir = this.dir + str;
581         }
582 
583         File fe = new File(dir);
584         if (fe.canRead()) {
585             this.dir = dir;
586             return true;
587         }else {
588             return false;
589         }
590     }
591 
getParentDirectory()592     String getParentDirectory(){
593         String parent = this.dir;
594         if (!this.dir.equals("/"))   // If the current directory is "/" leave it alone.
595         {
596             if (dir.endsWith("/"))
597                 parent = parent.substring(0,parent.lastIndexOf("/"));
598 
599             parent = parent.substring(0,parent.lastIndexOf("/")+1);
600         }
601         return parent;
602     }
603 
actionPerformed( ActionEvent actionEvent )604     public void actionPerformed( ActionEvent actionEvent ) {
605         String actionCommand = actionEvent.getActionCommand();
606         Object source = actionEvent.getSource();
607 
608         if (actionCommand.equals(actionButtonText)) {
609             handleSelection( selectionField.getText() );
610             handleQuitButton();
611         } else if (actionCommand.equals(filterLabelText)) {
612             handleFilter( filterField.getText() );
613         } else if (actionCommand.equals(cancelButtonText)) {
614             handleCancel();
615         } else if ( source instanceof TextField ) {
616             if ( selectionField == ((TextField)source) ) {
617                 // Fixed within 6259434: PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit
618                 // We should handle the action based on the selection field
619                 // Looks like mistake
620                 handleSelection(selectionField.getText());
621                 handleQuitButton();
622             } else if (filterField == ((TextField)source)) {
623                 handleFilter(filterField.getText());
624             } else if (pathField == ((TextField)source)) {
625                 target.setDirectory(pathField.getText());
626             }
627         } else if (source instanceof List) {
628             if (directoryList == ((List)source)) {
629                 //handleFilter( actionCommand + getFileName( filterField.getText() ) );
630                 if (updateDirectoryByUserAction(actionCommand)){
631                     handleFilter( getFileName( filterField.getText() ) );
632                 }
633             } else if (fileList == ((List)source)) {
634                 handleSelection( actionCommand );
635                 handleQuitButton();
636             }
637         }
638     }
639 
dispatchKeyEvent(KeyEvent keyEvent)640     public boolean dispatchKeyEvent(KeyEvent keyEvent) {
641         int id = keyEvent.getID();
642         int keyCode = keyEvent.getKeyCode();
643 
644         if (id == KeyEvent.KEY_PRESSED && keyCode == KeyEvent.VK_ESCAPE) {
645             synchronized (target.getTreeLock()) {
646                 Component comp = (Component) keyEvent.getSource();
647                 while (comp != null) {
648                     // Fix for 6240084 Disposing a file dialog when the drop-down is active does not dispose the dropdown menu, on Xtoolkit
649                     // See also 6259493
650                     if (comp == pathChoice) {
651                         XChoicePeer choicePeer = (XChoicePeer)pathChoice.getPeer();
652                         if (choicePeer.isUnfurled()){
653                             return false;
654                         }
655                     }
656                     if (comp.getPeer() == this) {
657                         handleCancel();
658                         return true;
659                     }
660                     comp = comp.getParent();
661                 }
662             }
663         }
664 
665         return false;
666     }
667 
668 
669     /**
670      * set the file
671      */
setFile(String file)672     public void setFile(String file) {
673 
674         if (file == null) {
675             this.file = null;
676             return;
677         }
678 
679         if (this.dir == null) {
680             String d = "./";
681             File f = new File(d, file);
682 
683             if (f.isFile()) {
684                 this.file = file;
685                 setDirectory(d);
686             }
687         } else {
688             File f = new File(this.dir, file);
689             if (f.isFile()) {
690                 this.file = file;
691             }
692         }
693 
694         setSelectionField(file);
695     }
696 
697     /**
698      * set the directory
699      * FIXME: we should update 'savedDir' after programmatically 'setDirectory'
700      * Otherwise, SavedDir will be not null before second showing
701      * So the current directory of the file dialog will be incorrect after second showing
702      * since 'setDirectory' will be ignored
703      * We cann't update savedDir here now since it used very often
704      */
setDirectory(String dir)705     public void setDirectory(String dir) {
706 
707         if (dir == null) {
708             this.dir = null;
709             return;
710         }
711 
712         if (dir.equals(this.dir)) {
713             return;
714         }
715 
716         int i;
717         if ((i=dir.indexOf("~")) != -1) {
718 
719             dir = dir.substring(0,i) + System.getProperty("user.home") + dir.substring(i+1,dir.length());
720         }
721 
722         File fe = new File(dir).getAbsoluteFile();
723         if (log.isLoggable(PlatformLogger.Level.FINE)) {
724             log.fine("Current directory : " + fe);
725         }
726 
727         if (!fe.isDirectory()) {
728             dir = "./";
729             fe = new File(dir).getAbsoluteFile();
730 
731             if (!fe.isDirectory()) {
732                 return;
733             }
734         }
735         try {
736             dir = this.dir = fe.getCanonicalPath();
737         } catch (java.io.IOException ie) {
738             dir = this.dir = fe.getAbsolutePath();
739         }
740         pathField.setText(this.dir);
741 
742 
743         if (dir.endsWith("/")) {
744             this.dir = dir;
745             handleFilter("");
746         } else {
747             this.dir = dir + "/";
748             handleFilter("");
749         }
750 
751         // Some code was removed
752         // Now we do updating of the pathChoice at the time of the choice opening
753         // Fixed problem:
754         // The exception java.awt.IllegalComponentStateException will be thrown
755         // if the user invoke setDirectory after the closing of the file dialog
756     }
757 
758     /**
759      * set filenameFilter
760      *
761      */
setFilenameFilter(FilenameFilter filter)762     public void setFilenameFilter(FilenameFilter filter) {
763         this.filter = filter;
764     }
765 
766 
dispose()767     public void dispose() {
768         FileDialog fd = (FileDialog)fileDialog;
769         if (fd != null) {
770             fd.removeAll();
771         }
772         super.dispose();
773     }
774 
775     // 03/02/2005 b5097243 Pressing 'ESC' on a file dlg does not dispose the dlg on Xtoolkit
setVisible(boolean b)776     public void setVisible(boolean b){
777         if (fileDialog == null) {
778             init((FileDialog)target);
779         }
780 
781         if (savedDir != null || userDir != null) {
782             setDirectory(savedDir != null ? savedDir : userDir);
783         }
784 
785         if (savedFile != null) {
786             // Actually in Motif implementation lost file value which was saved after prevously showing
787             // Seems we shouldn't restore Motif behaviour in this case
788             setFile(savedFile);
789         }
790 
791         super.setVisible(b);
792         if (b == true){
793             // See 6240074 for more information
794             XChoicePeer choicePeer = (XChoicePeer)pathChoice.getPeer();
795             choicePeer.addXChoicePeerListener(this);
796             KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
797         }else{
798             // See 6240074 for more information
799             XChoicePeer choicePeer = (XChoicePeer)pathChoice.getPeer();
800             choicePeer.removeXChoicePeerListener();
801             KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this);
802         }
803 
804         selectionField.requestFocusInWindow();
805     }
806 
807     /*
808      * Adding items to the path choice based on the text string
809      * See 6240074 for more information
810      */
addItemsToPathChoice(String text)811     public void addItemsToPathChoice(String text){
812         String dirList[] = getDirList(text);
813         for (int i = 0; i < dirList.length; i++) pathChoice.addItem(dirList[i]);
814     }
815 
816     /*
817      * Refresh the unfurled choice at the time of the opening choice according to the text of the path field
818      * See 6240074 for more information
819      */
unfurledChoiceOpening(ListHelper choiceHelper)820     public void unfurledChoiceOpening(ListHelper choiceHelper){
821 
822         // When the unfurled choice is opening the first time, we need only to add elements, otherwise we've got exception
823         if (choiceHelper.getItemCount() == 0){
824             addItemsToPathChoice(pathField.getText());
825             return;
826         }
827 
828         // If the set of the directories the exactly same as the used to be then dummy
829         if (pathChoice.getItem(0).equals(pathField.getText()))
830             return;
831 
832         pathChoice.removeAll();
833         addItemsToPathChoice(pathField.getText());
834     }
835 
836     /*
837      * Refresh the file dialog at the time of the closing choice according to the selected item of the choice
838      * See 6240074 for more information
839      */
unfurledChoiceClosing()840     public void unfurledChoiceClosing(){
841           // This is the exactly same code as invoking later at the time of the itemStateChanged
842           // Here is we restore Windows behaviour: change current directory if user press 'ESC'
843           String dir = pathChoice.getSelectedItem();
844           target.setDirectory(dir);
845     }
846 }
847 
848 class Separator extends Canvas {
849     public final static int HORIZONTAL = 0;
850     public final static int VERTICAL = 1;
851     int orientation;
852 
Separator(int length, int thickness, int orient)853     public Separator(int length, int thickness, int orient) {
854         super();
855         orientation = orient;
856         if (orient == HORIZONTAL) {
857             resize(length, thickness);
858         } else {
859             // VERTICAL
860             resize(thickness, length);
861         }
862     }
863 
paint(Graphics g)864     public void paint(Graphics g) {
865         int x1, y1, x2, y2;
866         Rectangle bbox = bounds();
867         Color c = getBackground();
868         Color brighter = c.brighter();
869         Color darker = c.darker();
870 
871         if (orientation == HORIZONTAL) {
872             x1 = 0;
873             x2 = bbox.width - 1;
874             y1 = y2 = bbox.height/2 - 1;
875 
876         } else {
877             // VERTICAL
878             x1 = x2 = bbox.width/2 - 1;
879             y1 = 0;
880             y2 = bbox.height - 1;
881         }
882         g.setColor(darker);
883         g.drawLine(x1, y2, x2, y2);
884         g.setColor(brighter);
885         if (orientation == HORIZONTAL)
886             g.drawLine(x1, y2+1, x2, y2+1);
887         else
888             g.drawLine(x1+1, y2, x2+1, y2);
889     }
890 }
891 
892 /*
893  * Motif file dialogs let the user specify a filter that controls the files that
894  * are displayed in the dialog. This filter is generally specified as a regular
895  * expression. The class is used to implement Motif-like filtering.
896  */
897 class FileDialogFilter implements FilenameFilter {
898 
899     String filter;
900 
FileDialogFilter(String f)901     public FileDialogFilter(String f) {
902         filter = f;
903     }
904 
905     /*
906      * Tells whether or not the specified file should be included in a file list
907      */
accept(File dir, String fileName)908     public boolean accept(File dir, String fileName) {
909 
910         File f = new File(dir, fileName);
911 
912         if (f.isDirectory()) {
913             return true;
914         } else {
915             return matches(fileName, filter);
916         }
917     }
918 
919     /*
920      * Tells whether or not the input string matches the given filter
921      */
matches(String input, String filter)922     private boolean matches(String input, String filter) {
923         String regex = convert(filter);
924         return input.matches(regex);
925     }
926 
927     /*
928      * Converts the filter into the form which is acceptable by Java's regexps
929      */
convert(String filter)930     private String convert(String filter) {
931         String regex = "^" + filter + "$";
932         regex = regex.replaceAll("\\.", "\\\\.");
933         regex = regex.replaceAll("\\?", ".");
934         regex = regex.replaceAll("\\*", ".*");
935         return regex;
936     }
937 }
938