1/*
2 * Copyright (c) 1997, 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 com.sun.java.swing.plaf.motif;
27
28import javax.swing.*;
29import javax.swing.filechooser.*;
30import javax.swing.event.*;
31import javax.swing.plaf.*;
32import javax.swing.plaf.basic.*;
33import java.awt.*;
34import java.awt.event.MouseAdapter;
35import java.awt.event.MouseEvent;
36import java.beans.*;
37import java.io.File;
38import java.io.IOException;
39import java.util.*;
40import sun.awt.shell.ShellFolder;
41import sun.swing.SwingUtilities2;
42
43/**
44 * Motif FileChooserUI.
45 *
46 * @author Jeff Dinkins
47 */
48public class MotifFileChooserUI extends BasicFileChooserUI {
49
50    private FilterComboBoxModel filterComboBoxModel;
51
52    protected JList<File> directoryList = null;
53    protected JList<File> fileList = null;
54
55    protected JTextField pathField = null;
56    protected JComboBox<FileFilter> filterComboBox = null;
57    protected JTextField filenameTextField = null;
58
59    private static final Dimension hstrut10 = new Dimension(10, 1);
60    private static final Dimension vstrut10 = new Dimension(1, 10);
61
62    private static final Insets insets = new Insets(10, 10, 10, 10);
63
64    private static Dimension prefListSize = new Dimension(75, 150);
65
66    private static Dimension WITH_ACCELERATOR_PREF_SIZE = new Dimension(650, 450);
67    private static Dimension PREF_SIZE = new Dimension(350, 450);
68    private static final int MIN_WIDTH = 200;
69    private static final int MIN_HEIGHT = 300;
70    private static Dimension PREF_ACC_SIZE = new Dimension(10, 10);
71    private static Dimension ZERO_ACC_SIZE = new Dimension(1, 1);
72
73    private static Dimension MAX_SIZE = new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
74
75    private static final Insets buttonMargin = new Insets(3, 3, 3, 3);
76
77    private JPanel bottomPanel;
78
79    protected JButton approveButton;
80
81    private String enterFolderNameLabelText = null;
82    private int enterFolderNameLabelMnemonic = 0;
83    private String enterFileNameLabelText = null;
84    private int enterFileNameLabelMnemonic = 0;
85
86    private String filesLabelText = null;
87    private int filesLabelMnemonic = 0;
88
89    private String foldersLabelText = null;
90    private int foldersLabelMnemonic = 0;
91
92    private String pathLabelText = null;
93    private int pathLabelMnemonic = 0;
94
95    private String filterLabelText = null;
96    private int filterLabelMnemonic = 0;
97
98    private JLabel fileNameLabel;
99
100    private void populateFileNameLabel() {
101        if (getFileChooser().getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) {
102            fileNameLabel.setText(enterFolderNameLabelText);
103            fileNameLabel.setDisplayedMnemonic(enterFolderNameLabelMnemonic);
104        } else {
105            fileNameLabel.setText(enterFileNameLabelText);
106            fileNameLabel.setDisplayedMnemonic(enterFileNameLabelMnemonic);
107        }
108    }
109
110    private String fileNameString(File file) {
111        if (file == null) {
112            return null;
113        } else {
114            JFileChooser fc = getFileChooser();
115            if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
116                return file.getPath();
117            } else {
118                return file.getName();
119            }
120        }
121    }
122
123    private String fileNameString(File[] files) {
124        StringBuilder sb = new StringBuilder();
125        for (int i = 0; files != null && i < files.length; i++) {
126            if (i > 0) {
127                sb.append(" ");
128            }
129            if (files.length > 1) {
130                sb.append("\"");
131            }
132            sb.append(fileNameString(files[i]));
133            if (files.length > 1) {
134                sb.append("\"");
135            }
136        }
137        return sb.toString();
138    }
139
140    public MotifFileChooserUI(JFileChooser filechooser) {
141        super(filechooser);
142    }
143
144    public String getFileName() {
145        if(filenameTextField != null) {
146            return filenameTextField.getText();
147        } else {
148            return null;
149        }
150    }
151
152    public void setFileName(String filename) {
153        if(filenameTextField != null) {
154            filenameTextField.setText(filename);
155        }
156    }
157
158    public String getDirectoryName() {
159        return pathField.getText();
160    }
161
162    public void setDirectoryName(String dirname) {
163        pathField.setText(dirname);
164    }
165
166    public void ensureFileIsVisible(JFileChooser fc, File f) {
167        // PENDING(jeff)
168    }
169
170    public void rescanCurrentDirectory(JFileChooser fc) {
171        getModel().validateFileCache();
172    }
173
174    public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) {
175        return new PropertyChangeListener() {
176            public void propertyChange(PropertyChangeEvent e) {
177                String prop = e.getPropertyName();
178                if(prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
179                    File f = (File) e.getNewValue();
180                    if(f != null) {
181                        setFileName(getFileChooser().getName(f));
182                    }
183                } else if (prop.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {
184                    File[] files = (File[]) e.getNewValue();
185                    JFileChooser fc = getFileChooser();
186                    if (files != null && files.length > 0 && (files.length > 1 || fc.isDirectorySelectionEnabled()
187                            || !files[0].isDirectory())) {
188                        setFileName(fileNameString(files));
189                    }
190                } else if (prop.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {
191                    fileList.clearSelection();
192                } else if(prop.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
193                    directoryList.clearSelection();
194                    ListSelectionModel sm = directoryList.getSelectionModel();
195                    if (sm instanceof DefaultListSelectionModel) {
196                        ((DefaultListSelectionModel)sm).moveLeadSelectionIndex(0);
197                        sm.setAnchorSelectionIndex(0);
198                    }
199                    fileList.clearSelection();
200                    sm = fileList.getSelectionModel();
201                    if (sm instanceof DefaultListSelectionModel) {
202                        ((DefaultListSelectionModel)sm).moveLeadSelectionIndex(0);
203                        sm.setAnchorSelectionIndex(0);
204                    }
205                    File currentDirectory = getFileChooser().getCurrentDirectory();
206                    if(currentDirectory != null) {
207                        try {
208                            setDirectoryName(ShellFolder.getNormalizedFile((File)e.getNewValue()).getPath());
209                        } catch (IOException ioe) {
210                            setDirectoryName(((File)e.getNewValue()).getAbsolutePath());
211                        }
212                        if ((getFileChooser().getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) && !getFileChooser().isMultiSelectionEnabled()) {
213                            setFileName(getDirectoryName());
214                        }
215                    }
216                } else if(prop.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
217                    if (fileNameLabel != null) {
218                        populateFileNameLabel();
219                    }
220                    directoryList.clearSelection();
221                } else if (prop.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
222                    if(getFileChooser().isMultiSelectionEnabled()) {
223                        fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
224                    } else {
225                        fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
226                        fileList.clearSelection();
227                        getFileChooser().setSelectedFiles(null);
228                    }
229                } else if (prop.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) {
230                    if(getAccessoryPanel() != null) {
231                        if(e.getOldValue() != null) {
232                            getAccessoryPanel().remove((JComponent) e.getOldValue());
233                        }
234                        JComponent accessory = (JComponent) e.getNewValue();
235                        if(accessory != null) {
236                            getAccessoryPanel().add(accessory, BorderLayout.CENTER);
237                            getAccessoryPanel().setPreferredSize(PREF_ACC_SIZE);
238                            getAccessoryPanel().setMaximumSize(MAX_SIZE);
239                        } else {
240                            getAccessoryPanel().setPreferredSize(ZERO_ACC_SIZE);
241                            getAccessoryPanel().setMaximumSize(ZERO_ACC_SIZE);
242                        }
243                    }
244                } else if (prop.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY) ||
245                        prop.equals(JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY) ||
246                        prop.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY)) {
247                    approveButton.setText(getApproveButtonText(getFileChooser()));
248                    approveButton.setToolTipText(getApproveButtonToolTipText(getFileChooser()));
249                } else if (prop.equals(JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) {
250                    doControlButtonsChanged(e);
251                } else if (prop.equals("componentOrientation")) {
252                    ComponentOrientation o = (ComponentOrientation)e.getNewValue();
253                    JFileChooser cc = (JFileChooser)e.getSource();
254                    if (o != (ComponentOrientation)e.getOldValue()) {
255                        cc.applyComponentOrientation(o);
256                    }
257                }
258            }
259        };
260    }
261
262    //
263    // ComponentUI Interface Implementation methods
264    //
265    public static ComponentUI createUI(JComponent c) {
266        return new MotifFileChooserUI((JFileChooser)c);
267    }
268
269    public void installUI(JComponent c) {
270        super.installUI(c);
271    }
272
273    public void uninstallUI(JComponent c) {
274        c.removePropertyChangeListener(filterComboBoxModel);
275        approveButton.removeActionListener(getApproveSelectionAction());
276        filenameTextField.removeActionListener(getApproveSelectionAction());
277        super.uninstallUI(c);
278    }
279
280    public void installComponents(JFileChooser fc) {
281        fc.setLayout(new BorderLayout(10, 10));
282        fc.setAlignmentX(JComponent.CENTER_ALIGNMENT);
283
284        @SuppressWarnings("serial") // anonymous class
285        JPanel interior = new JPanel() {
286            public Insets getInsets() {
287                return insets;
288            }
289        };
290        interior.setInheritsPopupMenu(true);
291        align(interior);
292        interior.setLayout(new BoxLayout(interior, BoxLayout.PAGE_AXIS));
293
294        fc.add(interior, BorderLayout.CENTER);
295
296        // PENDING(jeff) - I18N
297        JLabel l = new JLabel(pathLabelText);
298        l.setDisplayedMnemonic(pathLabelMnemonic);
299        align(l);
300        interior.add(l);
301
302        File currentDirectory = fc.getCurrentDirectory();
303        String curDirName = null;
304        if(currentDirectory != null) {
305            curDirName = currentDirectory.getPath();
306        }
307
308        @SuppressWarnings("serial") // anonymous class
309        JTextField tmp1 = new JTextField(curDirName, 35) {
310            public Dimension getMaximumSize() {
311                Dimension d = super.getMaximumSize();
312                d.height = getPreferredSize().height;
313                return d;
314            }
315        };
316        pathField = tmp1;
317        pathField.setInheritsPopupMenu(true);
318        l.setLabelFor(pathField);
319        align(pathField);
320
321        // Change to folder on return
322        pathField.addActionListener(getUpdateAction());
323        interior.add(pathField);
324
325        interior.add(Box.createRigidArea(vstrut10));
326
327
328        // CENTER: left, right accessory
329        JPanel centerPanel = new JPanel();
330        centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.LINE_AXIS));
331        align(centerPanel);
332
333        // left panel - Filter & folderList
334        JPanel leftPanel = new JPanel();
335        leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.PAGE_AXIS));
336        align(leftPanel);
337
338        // add the filter PENDING(jeff) - I18N
339        l = new JLabel(filterLabelText);
340        l.setDisplayedMnemonic(filterLabelMnemonic);
341        align(l);
342        leftPanel.add(l);
343
344        @SuppressWarnings("serial") // anonymous class
345        JComboBox<FileFilter> tmp2 = new JComboBox<FileFilter>() {
346            public Dimension getMaximumSize() {
347                Dimension d = super.getMaximumSize();
348                d.height = getPreferredSize().height;
349                return d;
350            }
351        };
352        filterComboBox = tmp2;
353        filterComboBox.setInheritsPopupMenu(true);
354        l.setLabelFor(filterComboBox);
355        filterComboBoxModel = createFilterComboBoxModel();
356        filterComboBox.setModel(filterComboBoxModel);
357        filterComboBox.setRenderer(createFilterComboBoxRenderer());
358        fc.addPropertyChangeListener(filterComboBoxModel);
359        align(filterComboBox);
360        leftPanel.add(filterComboBox);
361
362        // leftPanel.add(Box.createRigidArea(vstrut10));
363
364        // Add the Folder List PENDING(jeff) - I18N
365        l = new JLabel(foldersLabelText);
366        l.setDisplayedMnemonic(foldersLabelMnemonic);
367        align(l);
368        leftPanel.add(l);
369        JScrollPane sp = createDirectoryList();
370        sp.getVerticalScrollBar().setFocusable(false);
371        sp.getHorizontalScrollBar().setFocusable(false);
372        sp.setInheritsPopupMenu(true);
373        l.setLabelFor(sp.getViewport().getView());
374        leftPanel.add(sp);
375        leftPanel.setInheritsPopupMenu(true);
376
377
378        // create files list
379        JPanel rightPanel = new JPanel();
380        align(rightPanel);
381        rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.PAGE_AXIS));
382        rightPanel.setInheritsPopupMenu(true);
383
384        l = new JLabel(filesLabelText);
385        l.setDisplayedMnemonic(filesLabelMnemonic);
386        align(l);
387        rightPanel.add(l);
388        sp = createFilesList();
389        l.setLabelFor(sp.getViewport().getView());
390        rightPanel.add(sp);
391        sp.setInheritsPopupMenu(true);
392
393        centerPanel.add(leftPanel);
394        centerPanel.add(Box.createRigidArea(hstrut10));
395        centerPanel.add(rightPanel);
396        centerPanel.setInheritsPopupMenu(true);
397
398        JComponent accessoryPanel = getAccessoryPanel();
399        JComponent accessory = fc.getAccessory();
400        if(accessoryPanel != null) {
401            if(accessory == null) {
402                accessoryPanel.setPreferredSize(ZERO_ACC_SIZE);
403                accessoryPanel.setMaximumSize(ZERO_ACC_SIZE);
404            } else {
405                getAccessoryPanel().add(accessory, BorderLayout.CENTER);
406                accessoryPanel.setPreferredSize(PREF_ACC_SIZE);
407                accessoryPanel.setMaximumSize(MAX_SIZE);
408            }
409            align(accessoryPanel);
410            centerPanel.add(accessoryPanel);
411            accessoryPanel.setInheritsPopupMenu(true);
412        }
413        interior.add(centerPanel);
414        interior.add(Box.createRigidArea(vstrut10));
415
416        // add the filename field PENDING(jeff) - I18N
417        fileNameLabel = new JLabel();
418        populateFileNameLabel();
419        align(fileNameLabel);
420        interior.add(fileNameLabel);
421
422        @SuppressWarnings("serial") // anonymous class
423        JTextField tmp3 = new JTextField(35) {
424            public Dimension getMaximumSize() {
425                Dimension d = super.getMaximumSize();
426                d.height = getPreferredSize().height;
427                return d;
428            }
429        };
430        filenameTextField = tmp3;
431        filenameTextField.setInheritsPopupMenu(true);
432        fileNameLabel.setLabelFor(filenameTextField);
433        filenameTextField.addActionListener(getApproveSelectionAction());
434        align(filenameTextField);
435        filenameTextField.setAlignmentX(JComponent.LEFT_ALIGNMENT);
436        interior.add(filenameTextField);
437
438        bottomPanel = getBottomPanel();
439        bottomPanel.add(new JSeparator(), BorderLayout.NORTH);
440
441        // Add buttons
442        JPanel buttonPanel = new JPanel();
443        align(buttonPanel);
444        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
445        buttonPanel.add(Box.createGlue());
446
447        @SuppressWarnings("serial") // anonymous class
448        JButton tmp4 = new JButton(getApproveButtonText(fc)) {
449            public Dimension getMaximumSize() {
450                return new Dimension(MAX_SIZE.width, this.getPreferredSize().height);
451            }
452        };
453        approveButton = tmp4;
454        approveButton.setMnemonic(getApproveButtonMnemonic(fc));
455        approveButton.setToolTipText(getApproveButtonToolTipText(fc));
456        approveButton.setInheritsPopupMenu(true);
457        align(approveButton);
458        approveButton.setMargin(buttonMargin);
459        approveButton.addActionListener(getApproveSelectionAction());
460        buttonPanel.add(approveButton);
461        buttonPanel.add(Box.createGlue());
462
463        @SuppressWarnings("serial") // anonymous class
464        JButton updateButton = new JButton(updateButtonText) {
465            public Dimension getMaximumSize() {
466                return new Dimension(MAX_SIZE.width, this.getPreferredSize().height);
467            }
468        };
469        updateButton.setMnemonic(updateButtonMnemonic);
470        updateButton.setToolTipText(updateButtonToolTipText);
471        updateButton.setInheritsPopupMenu(true);
472        align(updateButton);
473        updateButton.setMargin(buttonMargin);
474        updateButton.addActionListener(getUpdateAction());
475        buttonPanel.add(updateButton);
476        buttonPanel.add(Box.createGlue());
477
478        @SuppressWarnings("serial") // anonymous class
479        JButton cancelButton = new JButton(cancelButtonText) {
480            public Dimension getMaximumSize() {
481                return new Dimension(MAX_SIZE.width, this.getPreferredSize().height);
482            }
483        };
484        cancelButton.setMnemonic(cancelButtonMnemonic);
485        cancelButton.setToolTipText(cancelButtonToolTipText);
486        cancelButton.setInheritsPopupMenu(true);
487        align(cancelButton);
488        cancelButton.setMargin(buttonMargin);
489        cancelButton.addActionListener(getCancelSelectionAction());
490        buttonPanel.add(cancelButton);
491        buttonPanel.add(Box.createGlue());
492
493        @SuppressWarnings("serial") // anonymous class
494        JButton helpButton = new JButton(helpButtonText) {
495            public Dimension getMaximumSize() {
496                return new Dimension(MAX_SIZE.width, this.getPreferredSize().height);
497            }
498        };
499        helpButton.setMnemonic(helpButtonMnemonic);
500        helpButton.setToolTipText(helpButtonToolTipText);
501        align(helpButton);
502        helpButton.setMargin(buttonMargin);
503        helpButton.setEnabled(false);
504        helpButton.setInheritsPopupMenu(true);
505        buttonPanel.add(helpButton);
506        buttonPanel.add(Box.createGlue());
507        buttonPanel.setInheritsPopupMenu(true);
508
509        bottomPanel.add(buttonPanel, BorderLayout.SOUTH);
510        bottomPanel.setInheritsPopupMenu(true);
511        if (fc.getControlButtonsAreShown()) {
512           fc.add(bottomPanel, BorderLayout.SOUTH);
513        }
514    }
515
516    protected JPanel getBottomPanel() {
517        if (bottomPanel == null) {
518            bottomPanel = new JPanel(new BorderLayout(0, 4));
519        }
520        return bottomPanel;
521    }
522
523    private void doControlButtonsChanged(PropertyChangeEvent e) {
524        if (getFileChooser().getControlButtonsAreShown()) {
525            getFileChooser().add(bottomPanel,BorderLayout.SOUTH);
526        } else {
527            getFileChooser().remove(getBottomPanel());
528        }
529    }
530
531    public void uninstallComponents(JFileChooser fc) {
532        fc.removeAll();
533        bottomPanel = null;
534        if (filterComboBoxModel != null) {
535            fc.removePropertyChangeListener(filterComboBoxModel);
536        }
537    }
538
539    protected void installStrings(JFileChooser fc) {
540        super.installStrings(fc);
541
542        Locale l = fc.getLocale();
543
544        enterFolderNameLabelText = UIManager.getString("FileChooser.enterFolderNameLabelText",l);
545        enterFolderNameLabelMnemonic = getMnemonic("FileChooser.enterFolderNameLabelMnemonic", l);
546        enterFileNameLabelText = UIManager.getString("FileChooser.enterFileNameLabelText",l);
547        enterFileNameLabelMnemonic = getMnemonic("FileChooser.enterFileNameLabelMnemonic", l);
548
549        filesLabelText = UIManager.getString("FileChooser.filesLabelText",l);
550        filesLabelMnemonic = getMnemonic("FileChooser.filesLabelMnemonic", l);
551
552        foldersLabelText = UIManager.getString("FileChooser.foldersLabelText",l);
553        foldersLabelMnemonic = getMnemonic("FileChooser.foldersLabelMnemonic", l);
554
555        pathLabelText = UIManager.getString("FileChooser.pathLabelText",l);
556        pathLabelMnemonic = getMnemonic("FileChooser.pathLabelMnemonic", l);
557
558        filterLabelText = UIManager.getString("FileChooser.filterLabelText",l);
559        filterLabelMnemonic = getMnemonic("FileChooser.filterLabelMnemonic", l);
560    }
561
562    private Integer getMnemonic(String key, Locale l) {
563        return SwingUtilities2.getUIDefaultsInt(key, l);
564    }
565
566    protected void installIcons(JFileChooser fc) {
567        // Since motif doesn't have button icons, leave this empty
568        // which overrides the supertype icon loading
569    }
570
571    protected void uninstallIcons(JFileChooser fc) {
572        // Since motif doesn't have button icons, leave this empty
573        // which overrides the supertype icon loading
574    }
575
576    protected JScrollPane createFilesList() {
577        fileList = new JList<File>();
578
579        if(getFileChooser().isMultiSelectionEnabled()) {
580            fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
581        } else {
582            fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
583        }
584
585        fileList.setModel(new MotifFileListModel());
586        fileList.getSelectionModel().removeSelectionInterval(0, 0);
587        fileList.setCellRenderer(new FileCellRenderer());
588        fileList.addListSelectionListener(createListSelectionListener(getFileChooser()));
589        fileList.addMouseListener(createDoubleClickListener(getFileChooser(), fileList));
590        fileList.addMouseListener(new MouseAdapter() {
591            public void mouseClicked(MouseEvent e) {
592                JFileChooser chooser = getFileChooser();
593                if (SwingUtilities.isLeftMouseButton(e) && !chooser.isMultiSelectionEnabled()) {
594                    int index = SwingUtilities2.loc2IndexFileList(fileList, e.getPoint());
595                    if (index >= 0) {
596                        File file = fileList.getModel().getElementAt(index);
597                        setFileName(chooser.getName(file));
598                    }
599                }
600            }
601        });
602        align(fileList);
603        JScrollPane scrollpane = new JScrollPane(fileList);
604        scrollpane.setPreferredSize(prefListSize);
605        scrollpane.setMaximumSize(MAX_SIZE);
606        align(scrollpane);
607        fileList.setInheritsPopupMenu(true);
608        scrollpane.setInheritsPopupMenu(true);
609        return scrollpane;
610    }
611
612    protected JScrollPane createDirectoryList() {
613        directoryList = new JList<File>();
614        align(directoryList);
615
616        directoryList.setCellRenderer(new DirectoryCellRenderer());
617        directoryList.setModel(new MotifDirectoryListModel());
618        directoryList.getSelectionModel().removeSelectionInterval(0, 0);
619        directoryList.addMouseListener(createDoubleClickListener(getFileChooser(), directoryList));
620        directoryList.addListSelectionListener(createListSelectionListener(getFileChooser()));
621        directoryList.setInheritsPopupMenu(true);
622
623        JScrollPane scrollpane = new JScrollPane(directoryList);
624        scrollpane.setMaximumSize(MAX_SIZE);
625        scrollpane.setPreferredSize(prefListSize);
626        scrollpane.setInheritsPopupMenu(true);
627        align(scrollpane);
628        return scrollpane;
629    }
630
631    @Override
632    public Dimension getPreferredSize(JComponent c) {
633        Dimension prefSize =
634            (getFileChooser().getAccessory() != null) ? WITH_ACCELERATOR_PREF_SIZE : PREF_SIZE;
635        Dimension d = c.getLayout().preferredLayoutSize(c);
636        if (d != null) {
637            return new Dimension(d.width < prefSize.width ? prefSize.width : d.width,
638                                 d.height < prefSize.height ? prefSize.height : d.height);
639        } else {
640            return prefSize;
641        }
642    }
643
644    @Override
645    public Dimension getMinimumSize(JComponent x) {
646        return new Dimension(MIN_WIDTH, MIN_HEIGHT);
647    }
648
649    @Override
650    public Dimension getMaximumSize(JComponent x) {
651        return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
652    }
653
654    protected void align(JComponent c) {
655        c.setAlignmentX(JComponent.LEFT_ALIGNMENT);
656        c.setAlignmentY(JComponent.TOP_ALIGNMENT);
657    }
658
659    @SuppressWarnings("serial") // Superclass is not serializable across versions
660    protected class FileCellRenderer extends DefaultListCellRenderer  {
661        public Component getListCellRendererComponent(JList<?> list, Object value, int index,
662                                                      boolean isSelected, boolean cellHasFocus) {
663
664            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
665            setText(getFileChooser().getName((File) value));
666            setInheritsPopupMenu(true);
667            return this;
668        }
669    }
670
671    @SuppressWarnings("serial") // Superclass is not serializable across versions
672    protected class DirectoryCellRenderer extends DefaultListCellRenderer  {
673        public Component getListCellRendererComponent(JList<?> list, Object value, int index,
674                                                      boolean isSelected, boolean cellHasFocus) {
675
676            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
677            setText(getFileChooser().getName((File) value));
678            setInheritsPopupMenu(true);
679            return this;
680        }
681    }
682
683    @SuppressWarnings("serial") // Superclass is not serializable across versions
684    protected class MotifDirectoryListModel extends AbstractListModel<File> implements ListDataListener {
685        public MotifDirectoryListModel() {
686            getModel().addListDataListener(this);
687        }
688
689        public int getSize() {
690            return getModel().getDirectories().size();
691        }
692
693        public File getElementAt(int index) {
694            return getModel().getDirectories().elementAt(index);
695        }
696
697        public void intervalAdded(ListDataEvent e) {
698            fireIntervalAdded(this, e.getIndex0(), e.getIndex1());
699        }
700
701        public void intervalRemoved(ListDataEvent e) {
702            fireIntervalRemoved(this, e.getIndex0(), e.getIndex1());
703        }
704
705        // PENDING(jeff) - this is inefficient - should sent out
706        // incremental adjustment values instead of saying that the
707        // whole list has changed.
708        public void fireContentsChanged() {
709            fireContentsChanged(this, 0, getModel().getDirectories().size()-1);
710        }
711
712        // PENDING(jeff) - fire the correct interval changed - currently sending
713        // out that everything has changed
714        public void contentsChanged(ListDataEvent e) {
715            fireContentsChanged();
716        }
717
718    }
719
720    @SuppressWarnings("serial") // Superclass is not serializable across versions
721    protected class MotifFileListModel extends AbstractListModel<File> implements ListDataListener {
722        public MotifFileListModel() {
723            getModel().addListDataListener(this);
724        }
725
726        public int getSize() {
727            return getModel().getFiles().size();
728        }
729
730        public boolean contains(Object o) {
731            return getModel().getFiles().contains(o);
732        }
733
734        public int indexOf(Object o) {
735            return getModel().getFiles().indexOf(o);
736        }
737
738        public File getElementAt(int index) {
739            return getModel().getFiles().elementAt(index);
740        }
741
742        public void intervalAdded(ListDataEvent e) {
743            fireIntervalAdded(this, e.getIndex0(), e.getIndex1());
744        }
745
746        public void intervalRemoved(ListDataEvent e) {
747            fireIntervalRemoved(this, e.getIndex0(), e.getIndex1());
748        }
749
750        // PENDING(jeff) - this is inefficient - should sent out
751        // incremental adjustment values instead of saying that the
752        // whole list has changed.
753        public void fireContentsChanged() {
754            fireContentsChanged(this, 0, getModel().getFiles().size()-1);
755        }
756
757        // PENDING(jeff) - fire the interval changed
758        public void contentsChanged(ListDataEvent e) {
759            fireContentsChanged();
760        }
761
762    }
763
764    //
765    // DataModel for Types Comboxbox
766    //
767    protected FilterComboBoxModel createFilterComboBoxModel() {
768        return new FilterComboBoxModel();
769    }
770
771    //
772    // Renderer for Types ComboBox
773    //
774    protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
775        return new FilterComboBoxRenderer();
776    }
777
778
779    /**
780     * Render different type sizes and styles.
781     */
782    @SuppressWarnings("serial") // Superclass is not serializable across versions
783    public class FilterComboBoxRenderer extends DefaultListCellRenderer {
784        public Component getListCellRendererComponent(JList<?> list,
785            Object value, int index, boolean isSelected,
786            boolean cellHasFocus) {
787
788            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
789
790            if (value != null && value instanceof FileFilter) {
791                setText(((FileFilter)value).getDescription());
792            }
793
794            return this;
795        }
796    }
797
798    /**
799     * Data model for a type-face selection combo-box.
800     */
801    @SuppressWarnings("serial") // Superclass is not serializable across versions
802    protected class FilterComboBoxModel extends AbstractListModel<FileFilter> implements ComboBoxModel<FileFilter>,
803            PropertyChangeListener {
804        protected FileFilter[] filters;
805        protected FilterComboBoxModel() {
806            super();
807            filters = getFileChooser().getChoosableFileFilters();
808        }
809
810        public void propertyChange(PropertyChangeEvent e) {
811            String prop = e.getPropertyName();
812            if(prop.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) {
813                filters = (FileFilter[]) e.getNewValue();
814                fireContentsChanged(this, -1, -1);
815            } else if (prop.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {
816                fireContentsChanged(this, -1, -1);
817            }
818        }
819
820        public void setSelectedItem(Object filter) {
821            if(filter != null) {
822                getFileChooser().setFileFilter((FileFilter) filter);
823                fireContentsChanged(this, -1, -1);
824            }
825        }
826
827        public Object getSelectedItem() {
828            // Ensure that the current filter is in the list.
829            // NOTE: we shouldnt' have to do this, since JFileChooser adds
830            // the filter to the choosable filters list when the filter
831            // is set. Lets be paranoid just in case someone overrides
832            // setFileFilter in JFileChooser.
833            FileFilter currentFilter = getFileChooser().getFileFilter();
834            boolean found = false;
835            if(currentFilter != null) {
836                for (FileFilter filter : filters) {
837                    if (filter == currentFilter) {
838                        found = true;
839                    }
840                }
841                if (!found) {
842                    getFileChooser().addChoosableFileFilter(currentFilter);
843                }
844            }
845            return getFileChooser().getFileFilter();
846        }
847
848        public int getSize() {
849            if(filters != null) {
850                return filters.length;
851            } else {
852                return 0;
853            }
854        }
855
856        public FileFilter getElementAt(int index) {
857            if(index > getSize() - 1) {
858                // This shouldn't happen. Try to recover gracefully.
859                return getFileChooser().getFileFilter();
860            }
861            if(filters != null) {
862                return filters[index];
863            } else {
864                return null;
865            }
866        }
867    }
868
869    protected JButton getApproveButton(JFileChooser fc) {
870        return approveButton;
871    }
872
873}
874