1/*
2 * Copyright (c) 2003, 2015, 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
26package sun.awt.X11;
27
28import java.awt.*;
29import javax.swing.*;
30import java.awt.event.*;
31import java.awt.peer.*;
32import java.io.*;
33import java.util.Locale;
34import java.util.Arrays;
35import java.security.AccessController;
36import java.security.PrivilegedAction;
37
38import sun.awt.AWTAccessor.ComponentAccessor;
39import sun.util.logging.PlatformLogger;
40import sun.awt.AWTAccessor;
41
42class 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
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
137    XFileDialogPeer(FileDialog target) {
138        super((Dialog)target);
139        this.target = target;
140    }
141
142    @SuppressWarnings("deprecation")
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
324    public void updateMinimumSize() {
325    }
326
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     */
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     */
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     */
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     */
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")
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")
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")
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.equals("")) {
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
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     */
539    void setSelectionField(String str) {
540        selectionField.setText(str);
541    }
542
543    /**
544     * set the text in the filterField
545     */
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     */
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     */
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
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
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
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     */
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     */
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     */
774    public void setFilenameFilter(FilenameFilter filter) {
775        this.filter = filter;
776    }
777
778
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")
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     */
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     */
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     */
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
864class Separator extends Canvas {
865    public static final int HORIZONTAL = 0;
866    public static final int VERTICAL = 1;
867    int orientation;
868
869    @SuppressWarnings("deprecation")
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")
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 */
915class FileDialogFilter implements FilenameFilter {
916
917    String filter;
918
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     */
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     */
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     */
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