Source for javax.swing.plaf.metal.MetalFileChooserUI

   1: /* MetalFileChooserUI.java --
   2:    Copyright (C) 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.metal;
  40: 
  41: import java.awt.BorderLayout;
  42: import java.awt.Component;
  43: import java.awt.Container;
  44: import java.awt.Dimension;
  45: import java.awt.Graphics;
  46: import java.awt.GridLayout;
  47: import java.awt.Insets;
  48: import java.awt.LayoutManager;
  49: import java.awt.Rectangle;
  50: import java.awt.Window;
  51: import java.awt.event.ActionEvent;
  52: import java.awt.event.ActionListener;
  53: import java.awt.event.MouseAdapter;
  54: import java.awt.event.MouseEvent;
  55: import java.awt.event.MouseListener;
  56: import java.beans.PropertyChangeEvent;
  57: import java.beans.PropertyChangeListener;
  58: import java.io.File;
  59: import java.text.DateFormat;
  60: import java.text.NumberFormat;
  61: import java.util.Date;
  62: import java.util.List;
  63: 
  64: import javax.swing.AbstractAction;
  65: import javax.swing.AbstractListModel;
  66: import javax.swing.ActionMap;
  67: import javax.swing.BorderFactory;
  68: import javax.swing.ButtonGroup;
  69: import javax.swing.ComboBoxModel;
  70: import javax.swing.DefaultListCellRenderer;
  71: import javax.swing.Icon;
  72: import javax.swing.JButton;
  73: import javax.swing.JComboBox;
  74: import javax.swing.JComponent;
  75: import javax.swing.JDialog;
  76: import javax.swing.JFileChooser;
  77: import javax.swing.JLabel;
  78: import javax.swing.JList;
  79: import javax.swing.JPanel;
  80: import javax.swing.JScrollPane;
  81: import javax.swing.JTable;
  82: import javax.swing.JTextField;
  83: import javax.swing.JToggleButton;
  84: import javax.swing.ListModel;
  85: import javax.swing.ListSelectionModel;
  86: import javax.swing.SwingUtilities;
  87: import javax.swing.UIManager;
  88: import javax.swing.event.ListSelectionEvent;
  89: import javax.swing.event.ListSelectionListener;
  90: import javax.swing.filechooser.FileFilter;
  91: import javax.swing.filechooser.FileSystemView;
  92: import javax.swing.filechooser.FileView;
  93: import javax.swing.plaf.ComponentUI;
  94: import javax.swing.plaf.basic.BasicFileChooserUI;
  95: import javax.swing.table.DefaultTableCellRenderer;
  96: import javax.swing.table.DefaultTableModel;
  97: 
  98: 
  99: /**
 100:  * A UI delegate for the {@link JFileChooser} component.  This class is only
 101:  * partially implemented and is not usable yet.
 102:  */
 103: public class MetalFileChooserUI 
 104:   extends BasicFileChooserUI
 105: {
 106:   
 107:   /**
 108:    * A renderer for the files and directories in the file chooser table.
 109:    */
 110:   class TableFileRenderer
 111:     extends DefaultTableCellRenderer
 112:   {
 113:     
 114:     /**
 115:      * Creates a new renderer.
 116:      */
 117:     public TableFileRenderer()
 118:     {
 119:       super();
 120:     }
 121:     
 122:     /**
 123:      * Returns a component that can render the specified value.
 124:      * 
 125:      * @param table  the table
 126:      * @param value  the string value of the cell
 127:      * @param isSelected  is the item selected?
 128:      * @param hasFocus  does the item have the focus?
 129:      * @param row  the row
 130:      * @param column  the column
 131:      * 
 132:      * @return The renderer.
 133:      */
 134:     public Component getTableCellRendererComponent(JTable table, Object value,
 135:         boolean isSelected, boolean hasFocus, int row, int column)
 136:     {
 137:       if (column == 0)
 138:         {
 139:           FileView v = getFileView(getFileChooser());
 140:           ListModel lm = fileList.getModel();
 141:           if (row < lm.getSize())
 142:             setIcon(v.getIcon((File) lm.getElementAt(row)));
 143:         }
 144:       else
 145:         setIcon(null);
 146:       
 147:       setText(value.toString());
 148:       setOpaque(true);
 149:       setEnabled(table.isEnabled());
 150:       setFont(fileList.getFont());
 151:       
 152:       if (startEditing && column == 0 || !isSelected)
 153:         {
 154:           setBackground(table.getBackground());
 155:           setForeground(table.getForeground());
 156:         }
 157:       else
 158:         {
 159:           setBackground(table.getSelectionBackground());
 160:           setForeground(table.getSelectionForeground());
 161:         }
 162: 
 163:       if (hasFocus)
 164:         setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
 165:       else
 166:         setBorder(noFocusBorder);
 167:       
 168:       return this;
 169:     }
 170:   }
 171:   
 172:   /**
 173:    * ActionListener for the list view.
 174:    */
 175:   class ListViewActionListener implements ActionListener
 176:   {
 177:     
 178:     /**
 179:      * This method is invoked when an action occurs.
 180:      * 
 181:      * @param e -
 182:      *          the <code>ActionEvent</code> that occurred
 183:      */
 184:     public void actionPerformed(ActionEvent e)
 185:     {
 186:       if (!listView)
 187:         {
 188:           int[] index = fileTable.getSelectedRows();
 189:           listView = true;
 190:           JFileChooser fc = getFileChooser();
 191:           fc.remove(fileTablePanel);
 192:           createList(fc);
 193: 
 194:           fileList.getSelectionModel().clearSelection();
 195:           if (index.length > 0)
 196:               for (int i = 0; i < index.length; i++)
 197:                 fileList.getSelectionModel().addSelectionInterval(index[i], index[i]);
 198:           
 199:           fc.add(fileListPanel, BorderLayout.CENTER);
 200:           fc.revalidate();
 201:           fc.repaint();
 202:         }
 203:     }
 204:   }
 205:   
 206:   /**
 207:    * ActionListener for the details view.
 208:    */
 209:   class DetailViewActionListener implements ActionListener
 210:   {
 211:     
 212:     /**
 213:      * This method is invoked when an action occurs.
 214:      * 
 215:      * @param e -
 216:      *          the <code>ActionEvent</code> that occurred
 217:      */
 218:     public void actionPerformed(ActionEvent e)
 219:     {
 220:       if (listView)
 221:         {
 222:           int[] index = fileList.getSelectedIndices();
 223:           JFileChooser fc = getFileChooser();
 224:           listView = false;
 225:           fc.remove(fileListPanel);
 226:           
 227:           if (fileTable == null)
 228:             createDetailsView(fc);
 229:           else
 230:             updateTable();
 231: 
 232:           fileTable.getSelectionModel().clearSelection();
 233:           if (index.length > 0)
 234:             {
 235:               for (int i = 0; i < index.length; i++)
 236:                 fileTable.getSelectionModel().addSelectionInterval(index[i], index[i]);
 237:             }
 238:           
 239:           fc.add(fileTablePanel, BorderLayout.CENTER);
 240:           fc.revalidate();
 241:           fc.repaint();
 242:         }
 243:     }
 244:   }
 245:   
 246:   /**
 247:    * A property change listener.
 248:    */
 249:   class MetalFileChooserPropertyChangeListener 
 250:     implements PropertyChangeListener
 251:   {
 252:     /**
 253:      * Default constructor.
 254:      */
 255:     public MetalFileChooserPropertyChangeListener()
 256:     {
 257:     }
 258:     
 259:     /**
 260:      * Handles a property change event.
 261:      * 
 262:      * @param e  the event.
 263:      */
 264:     public void propertyChange(PropertyChangeEvent e)
 265:     {
 266:       JFileChooser filechooser = getFileChooser();
 267:       
 268:       String n = e.getPropertyName();
 269:       if (n.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY))
 270:         {
 271:           int mode = -1; 
 272:           if (filechooser.isMultiSelectionEnabled())
 273:             mode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
 274:           else
 275:             mode = ListSelectionModel.SINGLE_SELECTION;
 276:           
 277:           if (listView)
 278:             fileList.setSelectionMode(mode);
 279:           else
 280:             fileTable.setSelectionMode(mode);
 281:         }
 282:       else if (n.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY))
 283:         {
 284:           File file = filechooser.getSelectedFile();
 285:           
 286:           if (file != null
 287:               && filechooser.getDialogType() == JFileChooser.SAVE_DIALOG)
 288:             {
 289:               if (file.isDirectory() && filechooser.isTraversable(file))
 290:                 {
 291:                   directoryLabel = look;
 292:                   dirLabel.setText(directoryLabel);
 293:                   filechooser.setApproveButtonText(openButtonText);
 294:                   filechooser.setApproveButtonToolTipText(openButtonToolTipText);
 295:                 }
 296:               else if (file.isFile())
 297:                 {
 298:                   directoryLabel = save;
 299:                   dirLabel.setText(directoryLabel);
 300:                   filechooser.setApproveButtonText(saveButtonText);
 301:                   filechooser.setApproveButtonToolTipText(saveButtonToolTipText);
 302:                 }
 303:             }
 304:             
 305:           if (file == null)
 306:             setFileName(null);
 307:           else if (file.isFile() || filechooser.getFileSelectionMode() 
 308:            != JFileChooser.FILES_ONLY)
 309:         setFileName(file.getName());
 310:           int index = -1;
 311:           index = getModel().indexOf(file);
 312:           if (index >= 0)
 313:             {
 314:               if (listView)
 315:                 {
 316:                   fileList.setSelectedIndex(index);
 317:                   fileList.ensureIndexIsVisible(index);
 318:                   fileList.revalidate();
 319:                   fileList.repaint();
 320:                 }
 321:               else
 322:                 {
 323:                   fileTable.getSelectionModel().addSelectionInterval(index, index);
 324:                   fileTable.scrollRectToVisible(fileTable.getCellRect(index, 0, true));
 325:                   fileTable.revalidate();
 326:                   fileTable.repaint();
 327:                 }
 328:             }
 329:         }
 330:       
 331:       else if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY))
 332:         {
 333:           if (listView)
 334:             {
 335:               fileList.clearSelection();
 336:               fileList.revalidate();
 337:               fileList.repaint();
 338:             }
 339:           else
 340:             {
 341:               fileTable.clearSelection();
 342:               fileTable.revalidate();
 343:               fileTable.repaint();
 344:             }
 345: 
 346:           setDirectorySelected(false);
 347:           File currentDirectory = filechooser.getCurrentDirectory();
 348:           setDirectory(currentDirectory);
 349:           boolean hasParent = currentDirectory.getParentFile() != null;
 350:           getChangeToParentDirectoryAction().setEnabled(hasParent);
 351:         }
 352:       
 353:       else if (n.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY))
 354:         {
 355:           filterModel.propertyChange(e);
 356:         }
 357:       else if (n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY))
 358:         {
 359:           filterModel.propertyChange(e);
 360:         }
 361:       else if (n.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY)
 362:                  || n.equals(JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY))
 363:         {
 364:           Window owner = SwingUtilities.windowForComponent(filechooser);
 365:           if (owner instanceof JDialog)
 366:             ((JDialog) owner).setTitle(getDialogTitle(filechooser));
 367:           approveButton.setText(getApproveButtonText(filechooser));
 368:           approveButton.setToolTipText(
 369:                   getApproveButtonToolTipText(filechooser));
 370:           approveButton.setMnemonic(getApproveButtonMnemonic(filechooser));
 371:         }
 372:       
 373:       else if (n.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY))
 374:         approveButton.setText(getApproveButtonText(filechooser));
 375:       
 376:       else if (n.equals(
 377:               JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY))
 378:         approveButton.setToolTipText(getApproveButtonToolTipText(filechooser));
 379:       
 380:       else if (n.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY))
 381:         approveButton.setMnemonic(getApproveButtonMnemonic(filechooser));
 382: 
 383:       else if (n.equals(
 384:               JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY))
 385:         {
 386:           if (filechooser.getControlButtonsAreShown())
 387:             {
 388:               topPanel.add(controls, BorderLayout.EAST);
 389:             }
 390:           else
 391:             topPanel.remove(controls);
 392:           topPanel.revalidate();
 393:           topPanel.repaint();
 394:           topPanel.doLayout();
 395:         }
 396:       
 397:       else if (n.equals(
 398:               JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY))
 399:         {
 400:           if (filechooser.isAcceptAllFileFilterUsed())
 401:             filechooser.addChoosableFileFilter(
 402:                     getAcceptAllFileFilter(filechooser));
 403:           else
 404:             filechooser.removeChoosableFileFilter(
 405:                     getAcceptAllFileFilter(filechooser));
 406:         }
 407:       
 408:       else if (n.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY))
 409:         {
 410:           JComponent old = (JComponent) e.getOldValue();
 411:           if (old != null)
 412:             getAccessoryPanel().remove(old);
 413:           JComponent newval = (JComponent) e.getNewValue();
 414:           if (newval != null)
 415:             getAccessoryPanel().add(newval);
 416:         }
 417:       
 418:       if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)
 419:           || n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)
 420:           || n.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY))
 421:         {
 422:           // Remove editing component
 423:           if (fileTable != null)
 424:             fileTable.removeAll();
 425:           if (fileList != null)
 426:             fileList.removeAll();
 427:           startEditing = false;
 428:           
 429:           // Set text on button back to original.
 430:           if (filechooser.getDialogType() == JFileChooser.SAVE_DIALOG)
 431:             {
 432:               directoryLabel = save;
 433:               dirLabel.setText(directoryLabel);
 434:               filechooser.setApproveButtonText(saveButtonText);
 435:               filechooser.setApproveButtonToolTipText(saveButtonToolTipText);
 436:             }
 437:           
 438:           rescanCurrentDirectory(filechooser);
 439:         }
 440:       
 441:       filechooser.revalidate();
 442:       filechooser.repaint();
 443:     }
 444:   }
 445:   
 446:   /** 
 447:    * A combo box model containing the selected directory and all its parent
 448:    * directories.
 449:    */
 450:   protected class DirectoryComboBoxModel
 451:     extends AbstractListModel
 452:     implements ComboBoxModel
 453:   {
 454:     /** Storage for the items in the model. */
 455:     private List items;
 456:     
 457:     /** The index of the selected item. */
 458:     private int selectedIndex;
 459:     
 460:     /**
 461:      * Creates a new model.
 462:      */
 463:     public DirectoryComboBoxModel() 
 464:     {
 465:       items = new java.util.ArrayList();
 466:       selectedIndex = -1;
 467:     }
 468:     
 469:     /**
 470:      * Returns the number of items in the model.
 471:      * 
 472:      * @return The number of items in the model.
 473:      */
 474:     public int getSize()
 475:     {
 476:       return items.size();
 477:     }
 478:     
 479:     /**
 480:      * Returns the item at the specified index.
 481:      * 
 482:      * @param index  the item index.
 483:      * 
 484:      * @return The item.
 485:      */
 486:     public Object getElementAt(int index)
 487:     {
 488:       return items.get(index);
 489:     }
 490:     
 491:     /**
 492:      * Returns the depth of the item at the given <code>index</code>.
 493:      * 
 494:      * @param index  the item index.
 495:      * 
 496:      * @return The depth.
 497:      */
 498:     public int getDepth(int index)
 499:     {
 500:       return Math.max(index, 0);
 501:     }
 502: 
 503:     /**
 504:      * Returns the selected item, or <code>null</code> if no item is selected.
 505:      * 
 506:      * @return The selected item, or <code>null</code>.
 507:      */
 508:     public Object getSelectedItem()
 509:     {
 510:       if (selectedIndex >= 0) 
 511:         return items.get(selectedIndex);
 512:       else
 513:         return null;
 514:     }
 515:     
 516:     /**
 517:      * Sets the selected item.  This clears all the directories from the
 518:      * existing list, and repopulates it with the new selected directory
 519:      * and all its parent directories.
 520:      * 
 521:      * @param selectedDirectory  the selected directory.
 522:      */
 523:     public void setSelectedItem(Object selectedDirectory)
 524:     {
 525:       items.clear();
 526:       FileSystemView fsv = getFileChooser().getFileSystemView();
 527:       File parent = (File) selectedDirectory;
 528:       while (parent != null)
 529:         {
 530:           items.add(0, parent);
 531:           parent = fsv.getParentDirectory(parent);
 532:         }
 533:       selectedIndex = items.indexOf(selectedDirectory);
 534:       fireContentsChanged(this, 0, items.size() - 1);
 535:     }
 536:     
 537:   }
 538: 
 539:   /**
 540:    * Handles changes to the selection in the directory combo box.
 541:    */
 542:   protected class DirectoryComboBoxAction
 543:     extends AbstractAction
 544:   {
 545:     /**
 546:      * Creates a new action.
 547:      */
 548:     protected DirectoryComboBoxAction()
 549:     {
 550:       // Nothing to do here.
 551:     }
 552:     
 553:     /**
 554:      * Handles the action event.
 555:      * 
 556:      * @param e  the event.
 557:      */
 558:     public void actionPerformed(ActionEvent e)
 559:     {
 560:       JFileChooser fc = getFileChooser();
 561:       fc.setCurrentDirectory((File) directoryModel.getSelectedItem());
 562:     }
 563:   }
 564: 
 565:   /**
 566:    * A renderer for the items in the directory combo box.
 567:    */
 568:   class DirectoryComboBoxRenderer
 569:     extends DefaultListCellRenderer
 570:   {
 571:     /**
 572:      * This is the icon that is displayed in the combobox. This wraps
 573:      * the standard icon and adds indendation.
 574:      */
 575:     private IndentIcon indentIcon;
 576: 
 577:     /**
 578:      * Creates a new renderer.
 579:      */
 580:     public DirectoryComboBoxRenderer(JFileChooser fc)
 581:     {
 582:       indentIcon = new IndentIcon();
 583:     }
 584:     
 585:     /**
 586:      * Returns a component that can be used to paint the given value within 
 587:      * the list.
 588:      * 
 589:      * @param list  the list.
 590:      * @param value  the value (a {@link File}).
 591:      * @param index  the item index.
 592:      * @param isSelected  is the item selected?
 593:      * @param cellHasFocus  does the list cell have focus?
 594:      * 
 595:      * @return The list cell renderer.
 596:      */
 597:     public Component getListCellRendererComponent(JList list, Object value,
 598:                                                   int index,
 599:                                                   boolean isSelected,
 600:                                                   boolean cellHasFocus)
 601:     {
 602:       super.getListCellRendererComponent(list, value, index, isSelected,
 603:                                          cellHasFocus);
 604:       File file = (File) value;
 605:       setText(getFileChooser().getName(file));
 606: 
 607:       // Install indented icon.
 608:       Icon icon = getFileChooser().getIcon(file);
 609:       indentIcon.setIcon(icon);
 610:       int depth = directoryModel.getDepth(index);
 611:       indentIcon.setDepth(depth);
 612:       setIcon(indentIcon);
 613: 
 614:       return this;
 615:     }
 616:   }
 617: 
 618:   /**
 619:    * An icon that wraps another icon and adds indentation.
 620:    */
 621:   class IndentIcon
 622:     implements Icon
 623:   {
 624: 
 625:     /**
 626:      * The indentation level.
 627:      */
 628:     private static final int INDENT = 10;
 629: 
 630:     /**
 631:      * The wrapped icon.
 632:      */
 633:     private Icon icon;
 634: 
 635:     /**
 636:      * The current depth.
 637:      */
 638:     private int depth;
 639: 
 640:     /**
 641:      * Sets the icon to be wrapped.
 642:      *
 643:      * @param i the icon
 644:      */
 645:     void setIcon(Icon i)
 646:     {
 647:       icon = i;
 648:     }
 649: 
 650:     /**
 651:      * Sets the indentation depth.
 652:      *
 653:      * @param d the depth to set
 654:      */
 655:     void setDepth(int d)
 656:     {
 657:       depth = d;
 658:     }
 659: 
 660:     public int getIconHeight()
 661:     {
 662:       return icon.getIconHeight();
 663:     }
 664: 
 665:     public int getIconWidth()
 666:     {
 667:       return icon.getIconWidth() + depth * INDENT;
 668:     }
 669: 
 670:     public void paintIcon(Component c, Graphics g, int x, int y)
 671:     {
 672:       icon.paintIcon(c, g, x + depth * INDENT, y);
 673:     }
 674:       
 675:   }
 676: 
 677:   /**
 678:    * A renderer for the files and directories in the file chooser.
 679:    */
 680:   protected class FileRenderer
 681:     extends DefaultListCellRenderer
 682:   {
 683:     
 684:     /**
 685:      * Creates a new renderer.
 686:      */
 687:     protected FileRenderer()
 688:     {
 689:       // Nothing to do here.
 690:     }
 691:     
 692:     /**
 693:      * Returns a component that can render the specified value.
 694:      * 
 695:      * @param list  the list.
 696:      * @param value  the value (a {@link File}).
 697:      * @param index  the index.
 698:      * @param isSelected  is the item selected?
 699:      * @param cellHasFocus  does the item have the focus?
 700:      * 
 701:      * @return The renderer.
 702:      */
 703:     public Component getListCellRendererComponent(JList list, Object value,
 704:         int index, boolean isSelected, boolean cellHasFocus)
 705:     {
 706:       FileView v = getFileView(getFileChooser());
 707:       File f = (File) value;
 708:       if (f != null)
 709:         {
 710:           setText(v.getName(f));
 711:           setIcon(v.getIcon(f));
 712:         }
 713:       else
 714:         {
 715:           setText("");
 716:           setIcon(null);
 717:         }
 718:       setOpaque(true);
 719:       if (isSelected)
 720:         {
 721:           setBackground(list.getSelectionBackground());
 722:           setForeground(list.getSelectionForeground());
 723:         }
 724:       else
 725:         {
 726:           setBackground(list.getBackground());
 727:           setForeground(list.getForeground());
 728:         }
 729: 
 730:       setEnabled(list.isEnabled());
 731:       setFont(list.getFont());
 732: 
 733:       if (cellHasFocus)
 734:         setBorder(UIManager.getBorder("List.focusCellHighlightBorder"));
 735:       else
 736:         setBorder(noFocusBorder);
 737:       return this;
 738:     }
 739:   }
 740: 
 741:   /**
 742:    * A combo box model for the file selection filters.
 743:    */
 744:   protected class FilterComboBoxModel
 745:     extends AbstractListModel
 746:     implements ComboBoxModel, PropertyChangeListener
 747:   {
 748: 
 749:     /** Storage for the filters in the model. */
 750:     protected FileFilter[] filters;
 751: 
 752:     /** The index of the selected file filter. */
 753:     private Object selected;
 754:     
 755:     /**
 756:      * Creates a new model.
 757:      */
 758:     protected FilterComboBoxModel()
 759:     {
 760:       filters = new FileFilter[1];
 761:       filters[0] = getAcceptAllFileFilter(getFileChooser());
 762:       selected = filters[0];
 763:     }
 764:     
 765:     /**
 766:      * Handles property changes.
 767:      * 
 768:      * @param e  the property change event.
 769:      */
 770:     public void propertyChange(PropertyChangeEvent e)
 771:     {
 772:       if (e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY))
 773:         {
 774:           JFileChooser fc = getFileChooser();
 775:           FileFilter[] choosableFilters = fc.getChoosableFileFilters();
 776:           filters = choosableFilters;
 777:           fireContentsChanged(this, 0, filters.length);
 778:           selected = e.getNewValue();
 779:           fireContentsChanged(this, -1, -1);
 780:         }
 781:       else if (e.getPropertyName().equals(
 782:               JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY))
 783:         {
 784:           // repopulate list
 785:           JFileChooser fc = getFileChooser();
 786:           FileFilter[] choosableFilters = fc.getChoosableFileFilters();
 787:           filters = choosableFilters;
 788:           fireContentsChanged(this, 0, filters.length);
 789:         }
 790:     }
 791:     
 792:     /**
 793:      * Sets the selected filter.
 794:      * 
 795:      * @param filter  the filter (<code>null</code> ignored).
 796:      */
 797:     public void setSelectedItem(Object filter)
 798:     {
 799:       if (filter != null)
 800:       {
 801:           selected = filter;
 802:           fireContentsChanged(this, -1, -1);
 803:       }
 804:     }
 805:     
 806:     /**
 807:      * Returns the selected file filter.
 808:      * 
 809:      * @return The selected file filter.
 810:      */
 811:     public Object getSelectedItem()
 812:     {
 813:       return selected;
 814:     }
 815:     
 816:     /**
 817:      * Returns the number of items in the model.
 818:      * 
 819:      * @return The number of items in the model.
 820:      */
 821:     public int getSize()
 822:     {
 823:       return filters.length;
 824:     }
 825:     
 826:     /**
 827:      * Returns the item at the specified index.
 828:      * 
 829:      * @param index  the item index.
 830:      * 
 831:      * @return The item at the specified index.
 832:      */
 833:     public Object getElementAt(int index)
 834:     {
 835:       return filters[index];
 836:     }
 837:     
 838:   }
 839: 
 840:   /**
 841:    * A renderer for the items in the file filter combo box.
 842:    */
 843:   public class FilterComboBoxRenderer
 844:     extends DefaultListCellRenderer
 845:   {
 846:     /**
 847:      * Creates a new renderer.
 848:      */
 849:     public FilterComboBoxRenderer()
 850:     {
 851:       // Nothing to do here.
 852:     }
 853:     
 854:     /**
 855:      * Returns a component that can be used to paint the given value within 
 856:      * the list.
 857:      * 
 858:      * @param list  the list.
 859:      * @param value  the value (a {@link FileFilter}).
 860:      * @param index  the item index.
 861:      * @param isSelected  is the item selected?
 862:      * @param cellHasFocus  does the list cell have focus?
 863:      * 
 864:      * @return This component as the renderer.
 865:      */
 866:     public Component getListCellRendererComponent(JList list, Object value,
 867:         int index, boolean isSelected, boolean cellHasFocus)
 868:     {
 869:       super.getListCellRendererComponent(list, value, index, isSelected, 
 870:                                          cellHasFocus);
 871:       FileFilter filter = (FileFilter) value;
 872:       setText(filter.getDescription());
 873:       return this;
 874:     }
 875:   }
 876: 
 877:   /**
 878:    * A listener for selection events in the file list.
 879:    * 
 880:    * @see #createListSelectionListener(JFileChooser)
 881:    */
 882:   class MetalFileChooserSelectionListener 
 883:     implements ListSelectionListener
 884:   {
 885:     /**
 886:      * Creates a new <code>SelectionListener</code> object.
 887:      */
 888:     protected MetalFileChooserSelectionListener()
 889:     {
 890:       // Do nothing here.
 891:     }
 892: 
 893:     /**
 894:      * Makes changes to different properties when
 895:      * a value has changed in the filechooser's selection.
 896:      *
 897:      * @param e - the list selection event that occured.
 898:      */
 899:     public void valueChanged(ListSelectionEvent e)
 900:     {
 901:       File f = (File) fileList.getSelectedValue();
 902:       if (f == null)
 903:         return;
 904:       JFileChooser filechooser = getFileChooser();
 905:       if (! filechooser.isTraversable(f))
 906:         filechooser.setSelectedFile(f);
 907:       else
 9