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
 908:         filechooser.setSelectedFile(null);
 909:     }
 910:   }
 911: 
 912:   /**
 913:    * A mouse listener for the {@link JFileChooser}.
 914:    * This listener is used for editing filenames.
 915:    */
 916:   protected class SingleClickListener
 917:     extends MouseAdapter
 918:   {
 919: 
 920:     /** Stores instance of the list */
 921:     JList list;
 922: 
 923:     /**
 924:      * Stores the current file that is being edited.
 925:      * It is null if nothing is currently being edited.
 926:      */
 927:     File editFile;
 928: 
 929:     /** The current file chooser. */
 930:     JFileChooser fc;
 931: 
 932:     /** The last file selected. */
 933:     Object lastSelected;
 934: 
 935:     /** The textfield used for editing. */
 936:     JTextField editField;
 937: 
 938:     /**
 939:      * Creates a new listener.
 940:      *
 941:      * @param list  the directory/file list.
 942:      */
 943:     public SingleClickListener(JList list)
 944:     {
 945:       this.list = list;
 946:       editFile = null;
 947:       fc = getFileChooser();
 948:       lastSelected = null;
 949:       startEditing = false;
 950:     }
 951: 
 952:     /**
 953:      * Receives notification of a mouse click event.
 954:      *
 955:      * @param e  the event.
 956:      */
 957:     public void mouseClicked(MouseEvent e)
 958:     {
 959:       if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1)
 960:         {
 961:           int index = list.locationToIndex(e.getPoint());
 962:           File[] sf = fc.getSelectedFiles();
 963:           if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1))
 964:               && index >= 0 && !startEditing && list.isSelectedIndex(index))
 965:             {
 966:               Object tmp = list.getModel().getElementAt(index);
 967:               if (lastSelected != null && lastSelected.equals(tmp))
 968:                 editFile(index);
 969:               lastSelected = tmp;
 970:             }
 971:           else
 972:               completeEditing();
 973:         }
 974:       else
 975:         completeEditing();
 976:     }
 977: 
 978:     /**
 979:      * Sets up the text editor for the current file.
 980:      *
 981:      * @param index -
 982:      *          the current index of the item in the list to be edited.
 983:      */
 984:     void editFile(int index)
 985:     {
 986:       Rectangle bounds = list.getCellBounds(index, index);
 987:       list.scrollRectToVisible(bounds);
 988:       editFile = (File) list.getModel().getElementAt(index);
 989:       if (editFile.canWrite())
 990:         {
 991:           startEditing = true;
 992:           editField = new JTextField(editFile.getName());
 993:           editField.addActionListener(new EditingActionListener());
 994: 
 995:           Icon icon = getFileView(fc).getIcon(editFile);
 996:           if (icon != null)
 997:             {
 998:               int padding = icon.getIconWidth() + 4;
 999:               bounds.x += padding;
1000:               bounds.width -= padding;
1001:             }
1002:           editField.setBounds(bounds);
1003: 
1004:           list.add(editField);
1005: 
1006:           editField.requestFocus();
1007:           editField.selectAll();
1008:         }
1009:       else
1010:         completeEditing();
1011:       list.repaint();
1012:     }
1013: 
1014:     /**
1015:      * Completes the editing.
1016:      */
1017:     void completeEditing()
1018:     {
1019:       if (editField != null && editFile != null)
1020:         {
1021:           String text = editField.getText();
1022:           if (text != null && text != "" && !text.equals(fc.getName(editFile)))
1023:             {
1024:               File f = fc.getFileSystemView().
1025:                 createFileObject(fc.getCurrentDirectory(), text);
1026:               if ( editFile.renameTo(f) )
1027:                 rescanCurrentDirectory(fc);
1028:             }
1029:           list.remove(editField);
1030:         }
1031:       startEditing = false;
1032:       editFile = null;
1033:       lastSelected = null;
1034:       editField = null;
1035:       list.repaint();
1036:     }
1037: 
1038:     /**
1039:      * ActionListener for the editing text field.
1040:      */
1041:     class EditingActionListener implements ActionListener
1042:     {
1043: 
1044:       /**
1045:        * This method is invoked when an action occurs.
1046:        *
1047:        * @param e -
1048:        *          the <code>ActionEvent</code> that occurred
1049:        */
1050:       public void actionPerformed(ActionEvent e)
1051:       {
1052:         if (editField != null)
1053:           completeEditing();
1054:       }
1055:     }
1056:   }
1057: 
1058:   /**
1059:    * A mouse listener for the {@link JFileChooser}.
1060:    * This listener is used for the table
1061:    */
1062:   private class TableClickListener extends MouseAdapter
1063:   {
1064: 
1065:     /** Stores instance of the table */
1066:     JTable table;
1067: 
1068:     /** Stores instance of the file chooser */
1069:     JFileChooser fc;
1070: 
1071:     /** The last selected file. */
1072:     Object lastSelected;
1073: 
1074:     /**
1075:      * Stores the current file that is being edited.
1076:      * It is null if nothing is currently being edited.
1077:      */
1078:     File editFile;
1079: 
1080:     /** The textfield used for editing. */
1081:     JTextField editField;
1082: 
1083:     /**
1084:      * Creates a new listener.
1085:      *
1086:      * @param table the directory/file table
1087:      * @param fc the JFileChooser
1088:      */
1089:     public TableClickListener(JTable table, JFileChooser fc)
1090:     {
1091:       this.table = table;
1092:       this.fc = fc;
1093:       lastSelected = fileList.getSelectedValue();
1094:       setDirectorySelected(false);
1095:       startEditing = false;
1096:       editFile = null;
1097:       editField = null;
1098:     }
1099: 
1100:     /**
1101:      * Receives notification of a mouse click event.
1102:      *
1103:      * @param e the event.
1104:      */
1105:     public void mouseClicked(MouseEvent e)
1106:     {
1107:       int row = table.getSelectedRow();
1108:       Object selVal = fileList.getModel().getElementAt(row);
1109:       if (selVal == null)
1110:         return;
1111:       FileSystemView fsv = fc.getFileSystemView();
1112:       if (e.getClickCount() == 1 &&
1113:           selVal.equals(lastSelected) &&
1114:           e.getButton() == MouseEvent.BUTTON1)
1115:         {
1116:           File[] sf = fc.getSelectedFiles();
1117:           if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1))
1118:               && !startEditing)
1119:             {
1120:               editFile = (File) selVal;
1121:               editFile(row);
1122:             }
1123:         }
1124:       else if (e.getClickCount() >= 2 &&
1125:           selVal.equals(lastSelected))
1126:         {
1127:           if (startEditing)
1128:             completeEditing();
1129:           File f = fsv.createFileObject(lastSelected.toString());
1130:           if (fc.isTraversable(f))
1131:             {
1132:               fc.setCurrentDirectory(f);
1133:               fc.rescanCurrentDirectory();
1134:             }
1135:           else
1136:             {
1137:               fc.setSelectedFile(f);
1138:               fc.approveSelection();
1139:               closeDialog();
1140:             }
1141:         }
1142:       else
1143:         {
1144:           if (startEditing)
1145:             completeEditing();
1146:           String path = selVal.toString();
1147:           File f = fsv.createFileObject(path);
1148:           fc.setSelectedFile(f);
1149:           if (fc.isTraversable(f))
1150:             {
1151:               setDirectorySelected(true);
1152:               setDirectory(f);
1153:             }
1154:           else
1155:             {
1156:               setDirectorySelected(false);
1157:               setDirectory(null);
1158:             }
1159:           lastSelected = selVal;
1160:           if (f.isFile())
1161:             setFileName(path.substring(path.lastIndexOf("/") + 1));
1162:           else if (fc.getFileSelectionMode() != JFileChooser.FILES_ONLY)
1163:             setFileName(path);
1164:         }
1165:       fileTable.repaint();
1166:     }
1167: 
1168:     /**
1169:      * Sets up the text editor for the current file.
1170:      *
1171:      * @param row -
1172:      *          the current row of the item in the list to be edited.
1173:      */
1174:     void editFile(int row)
1175:     {
1176:       Rectangle bounds = table.getCellRect(row, 0, true);
1177:       table.scrollRectToVisible(bounds);
1178:       if (editFile.canWrite())
1179:         {
1180:           startEditing = true;
1181:           editField = new JTextField(editFile.getName());
1182:           editField.addActionListener(new EditingActionListener());
1183: 
1184:           // Need to adjust y pos
1185:           bounds.y = row * table.getRowHeight();
1186:           editField.setBounds(bounds);
1187: 
1188:           table.add(editField);
1189: 
1190:           editField.requestFocus();
1191:           editField.selectAll();
1192:         }
1193:       else
1194:         completeEditing();
1195:       table.repaint();
1196:     }
1197: 
1198:     /**
1199:      * Completes the editing.
1200:      */
1201:     void completeEditing()
1202:     {
1203:       if (editField != null && editFile != null)
1204:         {
1205:           String text = editField.getText();
1206:           if (text != null && text != "" && !text.equals(fc.getName(editFile)))
1207:               if (editFile.renameTo(fc.getFileSystemView().createFileObject(
1208:                   fc.getCurrentDirectory(), text)))
1209:                 rescanCurrentDirectory(fc);
1210:           table.remove(editField);
1211:         }
1212:       startEditing = false;
1213:       editFile = null;
1214:       editField = null;
1215:       table.repaint();
1216:     }
1217: 
1218:     /**
1219:      * ActionListener for the editing text field.
1220:      */
1221:     class EditingActionListener implements ActionListener
1222:     {
1223: 
1224:       /**
1225:        * This method is invoked when an action occurs.
1226:        *
1227:        * @param e -
1228:        *          the <code>ActionEvent</code> that occurred
1229:        */
1230:       public void actionPerformed(ActionEvent e)
1231:       {
1232:         if (editField != null)
1233:           completeEditing();
1234:       }
1235:     }
1236: 
1237:     /**
1238:      * Closes the dialog.
1239:      */
1240:     public void closeDialog()
1241:     {
1242:       Window owner = SwingUtilities.windowForComponent(fc);
1243:       if (owner instanceof JDialog)
1244:         ((JDialog) owner).dispose();
1245:     }
1246:   }
1247: 
1248:   /** The text for a label describing the directory combo box. */
1249:   private String directoryLabel;
1250: 
1251:   private JComboBox directoryComboBox;
1252: 
1253:   /** The model for the directory combo box. */
1254:   DirectoryComboBoxModel directoryModel;
1255: 
1256:   /** The text for a label describing the file text field. */
1257:   private String fileLabel;
1258: 
1259:   /** The file name text field. */
1260:   private JTextField fileTextField;
1261: 
1262:   /** The text for a label describing the filter combo box. */
1263:   private String filterLabel;
1264: 
1265:   /**
1266:    * The top panel (contains the directory combo box and the control buttons).
1267:    */
1268:   private JPanel topPanel;
1269: 
1270:   /** A panel containing the control buttons ('up', 'home' etc.). */
1271:   private JPanel controls;
1272: 
1273:   /**
1274:    * The panel that contains the filename field and the filter combobox.
1275:    */
1276:   private JPanel bottomPanel;
1277: 
1278:   /**
1279:    * The panel that contains the 'Open' (or 'Save') and 'Cancel' buttons.
1280:    */
1281:   private JPanel buttonPanel;
1282: 
1283:   private JButton approveButton;
1284: 
1285:   /** The file list. */
1286:   JList fileList;
1287: 
1288:   /** The file table. */
1289:   JTable fileTable;
1290: 
1291:   /** The panel containing the file list. */
1292:   JPanel fileListPanel;
1293: 
1294:   /** The panel containing the file table. */
1295:   JPanel fileTablePanel;
1296: 
1297:   /** The filter combo box model. */
1298:   private FilterComboBoxModel filterModel;
1299: 
1300:   /** The action map. */
1301:   private ActionMap actionMap;
1302: 
1303:   /** True if currently in list view. */
1304:   boolean listView;
1305: 
1306:   /** True if we can or have started editing a cell. */
1307:   boolean startEditing;
1308: 
1309:   /** The scrollpane used for the table and list. */
1310:   JScrollPane scrollPane;
1311: 
1312:   /** The text for the label when saving. */
1313:   String save;
1314: 
1315:   /** The text for the label when opening a directory. */
1316:   String look;
1317: 
1318:   /** The label for the file combo box. */
1319:   JLabel dirLabel;
1320: 
1321:   /** Listeners. */
1322:   ListSelectionListener listSelList;
1323:   MouseListener doubleClickList;
1324:   SingleClickListener singleClickList;
1325:   TableClickListener tableClickList;
1326: 
1327:   /**
1328:    * A factory method that returns a UI delegate for the specified
1329:    * component.
1330:    *
1331:    * @param c  the component (which should be a {@link JFileChooser}).
1332:    */
1333:   public static ComponentUI createUI(JComponent c)
1334:   {
1335:     JFileChooser chooser = (JFileChooser) c;
1336:     return new MetalFileChooserUI(chooser);
1337:   }
1338: 
1339:   /**
1340:    * Creates a new instance of this UI delegate.
1341:    *
1342:    * @param filechooser  the file chooser component.
1343:    */
1344:   public MetalFileChooserUI(JFileChooser filechooser)
1345:   {
1346:     super(filechooser);
1347:     bottomPanel = new JPanel(new GridLayout(3, 2));
1348:     buttonPanel = new JPanel();
1349:   }
1350: 
1351:   public void installUI(JComponent c)
1352:   {
1353:     super.installUI(c);
1354:     actionMap = createActionMap();
1355:   }
1356: 
1357:   public void uninstallUI(JComponent c)
1358:   {
1359:     super.uninstallUI(c);
1360:     actionMap = null;
1361:   }
1362: 
1363:   /**
1364:    * Installs the sub-components of the file chooser.
1365:    *
1366:    * @param fc  the file chooser component.
1367:    */
1368:   public void installComponents(JFileChooser fc)
1369:   {
1370:     fc.setLayout(new BorderLayout());
1371:     topPanel = new JPanel(new BorderLayout());
1372:     dirLabel = new JLabel(directoryLabel);
1373:     topPanel.add(dirLabel, BorderLayout.WEST);
1374:     this.controls = new JPanel();
1375:     addControlButtons();
1376: 
1377:     JPanel dirPanel = new JPanel(new VerticalMidLayout());
1378:     directoryModel = createDirectoryComboBoxModel(fc);
1379:     directoryComboBox = new JComboBox(directoryModel);
1380:     directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
1381:     dirPanel.add(directoryComboBox);
1382:     topPanel.add(dirPanel);
1383:     topPanel.add(controls, BorderLayout.EAST);
1384:     topPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 0, 8));
1385:     fc.add(topPanel, BorderLayout.NORTH);
1386: 
1387:     JPanel list = createList(fc);
1388:     list.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
1389:     fc.add(list, BorderLayout.CENTER);
1390: 
1391:     JPanel bottomPanel = getBottomPanel();
1392:     filterModel = createFilterComboBoxModel();
1393:     JComboBox fileFilterCombo = new JComboBox(filterModel);
1394:     fileFilterCombo.setRenderer(createFilterComboBoxRenderer());
1395: 
1396:     fileTextField = new JTextField();
1397:     JPanel fileNamePanel = new JPanel(new VerticalMidLayout());
1398:     fileNamePanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 5));
1399:     fileNamePanel.add(fileTextField);
1400:     JPanel row1 = new JPanel(new BorderLayout());
1401:     row1.add(new JLabel(this.fileLabel), BorderLayout.WEST);
1402:     row1.add(fileNamePanel);
1403:     bottomPanel.add(row1);
1404: 
1405:     JPanel row2 = new JPanel(new BorderLayout());
1406:     row2.add(new JLabel(this.filterLabel), BorderLayout.WEST);
1407:     row2.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
1408:     row2.add(fileFilterCombo);
1409:     bottomPanel.add(row2);
1410:     JPanel buttonPanel = new JPanel(new ButtonLayout());
1411: 
1412:     approveButton = new JButton(getApproveSelectionAction());
1413:     approveButton.setText(getApproveButtonText(fc));
1414:     approveButton.setToolTipText(getApproveButtonToolTipText(fc));
1415:     approveButton.setMnemonic(getApproveButtonMnemonic(fc));
1416:     buttonPanel.add(approveButton);
1417:     buttonPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0));
1418: 
1419:     JButton cancelButton = new JButton(getCancelSelectionAction());
1420:     cancelButton.setText(cancelButtonText);
1421:     cancelButton.setToolTipText(cancelButtonToolTipText);
1422:     cancelButton.setMnemonic(cancelButtonMnemonic);
1423:     buttonPanel.add(cancelButton);
1424:     bottomPanel.add(buttonPanel, BorderLayout.SOUTH);
1425:     bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
1426:     fc.add(bottomPanel, BorderLayout.SOUTH);
1427: 
1428:     fc.add(getAccessoryPanel(), BorderLayout.EAST);
1429:   }
1430: 
1431:   /**
1432:    * Uninstalls the components added by
1433:    * {@link #installComponents(JFileChooser)}.
1434:    *
1435:    * @param fc  the file chooser.
1436:    */
1437:   public void uninstallComponents(JFileChooser fc)
1438:   {
1439:     fc.remove(bottomPanel);
1440:     bottomPanel = null;
1441:     fc.remove(fileListPanel);
1442:     fc.remove(fileTablePanel);
1443:     fileTablePanel = null;
1444:     fileListPanel = null;
1445:     fc.remove(topPanel);
1446:     topPanel = null;
1447: 
1448:     directoryModel = null;
1449:     fileTextField = null;
1450:     directoryComboBox = null;
1451:   }
1452: 
1453:   /**
1454:    * Returns the panel that contains the 'Open' (or 'Save') and 'Cancel'
1455:    * buttons.
1456:    *
1457:    * @return The panel.
1458:    */
1459:   protected JPanel getButtonPanel()
1460:   {
1461:     return buttonPanel;
1462:   }
1463: 
1464:   /**
1465:    * Creates and returns a new panel that will be used for the controls at
1466:    * the bottom of the file chooser.
1467:    *
1468:    * @return A new panel.
1469:    */
1470:   protected JPanel getBottomPanel()
1471:   {
1472:     if (bottomPanel == null)
1473:       bottomPanel = new JPanel(new GridLayout(3, 2));
1474:     return bottomPanel;
1475:   }
1476: 
1477:   /**
1478:    * Fetches localised strings for use by the labels and buttons on the
1479:    * file chooser.
1480:    *
1481:    * @param fc  the file chooser.
1482:    */
1483:   protected void installStrings(JFileChooser fc)
1484:   {
1485:      super.installStrings(fc);
1486:      look = "Look In: ";
1487:      save = "Save In: ";
1488:      if (fc.getDialogType() == JFileChooser.SAVE_DIALOG)
1489:        directoryLabel = save;
1490:      else
1491:        directoryLabel = look;
1492: 
1493:      fileLabel = "File Name: ";
1494:      filterLabel = "Files of Type: ";
1495: 
1496:      this.cancelButtonMnemonic = 0;
1497:      this.cancelButtonText = "Cancel";
1498:      this.cancelButtonToolTipText = "Abort file chooser dialog";
1499: 
1500:      this.directoryOpenButtonMnemonic = 0;
1501:      this.directoryOpenButtonText = "Open";
1502:      this.directoryOpenButtonToolTipText = "Open selected directory";
1503: 
1504:      this.helpButtonMnemonic = 0;
1505:      this.helpButtonText = "Help";
1506:      this.helpButtonToolTipText = "Filechooser help";
1507: 
1508:      this.openButtonMnemonic = 0;
1509:      this.openButtonText = "Open";
1510:      this.openButtonToolTipText = "Open selected file";
1511: 
1512:      this.saveButtonMnemonic = 0;
1513:      this.saveButtonText = "Save";
1514:      this.saveButtonToolTipText = "Save selected file";
1515: 
1516:      this.updateButtonMnemonic = 0;
1517:      this.updateButtonText = "Update";
1518:      this.updateButtonToolTipText = "Update directory listing";
1519:   }
1520: 
1521:   /**
1522:    * Installs the listeners required.
1523:    *
1524:    * @param fc  the file chooser.
1525:    */
1526:   protected void installListeners(JFileChooser fc)
1527:   {
1528:     directoryComboBox.setAction(new DirectoryComboBoxAction());
1529:     fc.addPropertyChangeListener(filterModel);
1530:     listSelList = createListSelectionListener(fc);
1531:     doubleClickList = this.createDoubleClickListener(fc, fileList);
1532:     singleClickList = new SingleClickListener(fileList);
1533:     fileList.addListSelectionListener(listSelList);
1534:     fileList.addMouseListener(doubleClickList);
1535:     fileList.addMouseListener(singleClickList);
1536:     super.installListeners(fc);
1537:   }
1538: 
1539:   protected void uninstallListeners(JFileChooser fc)
1540:   {
1541:     super.uninstallListeners(fc);
1542:     fc.removePropertyChangeListener(filterModel);
1543:     directoryComboBox.setAction(null);
1544:     fileList.removeListSelectionListener(listSelList);
1545:     fileList.removeMouseListener(doubleClickList);
1546:     fileList.removeMouseListener(singleClickList);
1547: 
1548:     if (fileTable != null)
1549:       fileTable.removeMouseListener(tableClickList);
1550:   }
1551: 
1552:   protected ActionMap getActionMap()
1553:   {
1554:     if (actionMap == null)
1555:       actionMap = createActionMap();
1556:     return actionMap;
1557:   }
1558: 
1559:   /**
1560:    * Creates and returns an action map.
1561:    *
1562:    * @return The action map.
1563:    */
1564:   protected ActionMap createActionMap()
1565:   {
1566:     ActionMap map = new ActionMap();
1567:     map.put("approveSelection", getApproveSelectionAction());
1568:     map.put("cancelSelection", getCancelSelectionAction());
1569:     map.put("Go Up", getChangeToParentDirectoryAction());
1570:     return map;
1571:   }
1572: 
1573:   /**
1574:    * Creates a panel containing a list of files.
1575:    *
1576:    * @param fc  the file chooser.
1577:    *
1578:    * @return A panel.
1579:    */
1580:   protected JPanel createList(JFileChooser fc)
1581:   {
1582:     if (fileList == null)
1583:       {
1584:         fileListPanel = new JPanel(new BorderLayout());
1585:         fileList = new JList(getModel());
1586:         scrollPane = new JScrollPane(fileList);
1587:         fileList.setLayoutOrientation(JList.VERTICAL_WRAP);
1588:         fileList.setCellRenderer(new FileRenderer());
1589:       }
1590:     else
1591:       {
1592:         fileList.setModel(getModel());
1593:         fileListPanel.removeAll();
1594:         scrollPane.getViewport().setView(fileList);
1595:       }
1596:     fileListPanel.add(scrollPane);
1597:     // This size was determined using BeanShell and dumping the JFileChooser
1598:     // component hierarchy. Sun has an internal FilePane class in there, but
1599:     // that probably doesn't matter atm.
1600:     fileListPanel.setPreferredSize(new Dimension(405, 135));
1601:     return fileListPanel;
1602:   }
1603: 
1604:   /**
1605:    * Creates a panel containing a table within a scroll pane.
1606:    *
1607:    * @param fc  the file chooser.
1608:    *
1609:    * @return The details view.
1610:    */
1611:   protected JPanel createDetailsView(JFileChooser fc)
1612:   {
1613:     fileTablePanel = new JPanel(new BorderLayout());
1614: 
1615:     Object[] cols = new Object[] {"Name", "Size", "Modified"};
1616:     Object[][] rows = new Object[fileList.getModel().getSize()][3];
1617: 
1618:     fileTable = new JTable(new DefaultTableModel(rows, cols));
1619: 
1620:     if (fc.isMultiSelectionEnabled())
1621:       fileTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
1622:     else
1623:       fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1624: 
1625:     fileTable.setShowGrid(false);
1626:     fileTable.setColumnSelectionAllowed(false);
1627:     fileTable.setDefaultRenderer(Object.class, new TableFileRenderer());
1628: 
1629:     tableClickList = new TableClickListener(fileTable, fc);
1630:     fileTable.addMouseListener(tableClickList);
1631: 
1632:     return updateTable();
1633:   }
1634: 
1635:   /**
1636:    * Sets the values in the table, and puts it in the panel.
1637:    *
1638:    * @return the panel containing the table.
1639:    */
1640:   JPanel updateTable()
1641:   {
1642:     DefaultTableModel mod = (DefaultTableModel) fileTable.getModel();
1643:     ListModel lm = fileList.getModel();
1644:     DateFormat dt = DateFormat.getDateTimeInstance(DateFormat.SHORT,
1645:                                                    DateFormat.SHORT);
1646:     File curr = null;
1647:     int size = lm.getSize();
1648:     int rc = mod.getRowCount();
1649: 
1650:     // If there are not enough rows
1651:     for (int x = rc; x < size; x++)
1652:       mod.addRow(new Object[3]);
1653: 
1654:     for (int i = 0; i < size; i++)
1655:       {
1656:         curr = (File) lm.getElementAt(i);
1657:         fileTable.setValueAt(curr.getName(), i, 0);
1658:         fileTable.setValueAt(formatSize(curr.length()), i, 1);
1659:         fileTable.setValueAt(dt.format(new Date(curr.lastModified())), i, 2);
1660:       }
1661: 
1662:     // If there are too many rows
1663:     while (rc > size)
1664:       mod.removeRow(--rc);
1665: 
1666:     scrollPane.getViewport().setView(fileTable);
1667:     scrollPane.setColumnHeaderView(fileTable.getTableHeader());
1668: 
1669:     fileTablePanel.removeAll();
1670:     fileTablePanel.add(scrollPane);
1671: 
1672:     return fileTablePanel;
1673:   }
1674: 
1675:   /**
1676:    * Formats bytes into the appropriate size.
1677:    *
1678:    * @param bytes the number of bytes to convert
1679:    * @return a string representation of the size
1680:    */
1681:   private String formatSize(long bytes)
1682:   {
1683:     NumberFormat nf = NumberFormat.getNumberInstance();
1684:     long mb = (long) Math.pow(2, 20);
1685:     long kb = (long) Math.pow(2, 10);
1686:     long gb = (long) Math.pow(2, 30);
1687:     double size = 0;
1688:     String id = "";
1689: 
1690:     if ((bytes / gb) >= 1)
1691:       {
1692:         size = (double) bytes / (double) gb;
1693:         id = "GB";
1694:       }
1695:     else if ((bytes / mb) >= 1)
1696:       {
1697:         size = (double) bytes / (double) mb;
1698:         id = "MB";
1699:       }
1700:     else if ((bytes / kb) >= 1)
1701:       {
1702:         size = (double) bytes / (double) kb;
1703:         id = "KB";
1704:       }
1705:     else
1706:       {
1707:         size = bytes;
1708:         id = "Bytes";
1709:       }
1710: 
1711:     return nf.format(size) + " " + id;
1712:   }
1713:   /**
1714:    * Creates a listener that monitors selections in the directory/file list
1715:    * and keeps the {@link JFileChooser} component up to date.
1716:    *
1717:    * @param fc  the file chooser.
1718:    *
1719:    * @return The listener.
1720:    *
1721:    * @see #installListeners(JFileChooser)
1722:    */
1723:   public ListSelectionListener createListSelectionListener(JFileChooser fc)
1724:   {
1725:     return new MetalFileChooserSelectionListener();
1726:   }
1727: 
1728:   /**
1729:    * Returns the preferred size for the file chooser component.
1730:    *
1731:    * @return The preferred size.
1732:    */
1733:   public Dimension getPreferredSize(JComponent c)
1734:   {
1735:     Dimension tp = topPanel.getPreferredSize();
1736:     Dimension bp = bottomPanel.getPreferredSize();
1737:     Dimension fl = fileListPanel.getPreferredSize();
1738:     return new Dimension(fl.width, tp.height + bp.height + fl.height);
1739:   }
1740: 
1741:   /**
1742:    * Returns the minimum size for the file chooser component.
1743:    *
1744:    * @return The minimum size.
1745:    */
1746:   public Dimension getMinimumSize(JComponent c)
1747:   {
1748:     Dimension tp = topPanel.getMinimumSize();
1749:     Dimension bp = bottomPanel.getMinimumSize();
1750:     Dimension fl = fileListPanel.getMinimumSize();
1751:     return new Dimension(fl.width, tp.height + bp.height + fl.height);
1752:   }
1753: 
1754:   /**
1755:    * Returns the maximum size for the file chooser component.
1756:    *
1757:    * @return The maximum size.
1758:    */
1759:   public Dimension getMaximumSize(JComponent c)
1760:   {
1761:     return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1762:   }
1763: 
1764:   /**
1765:    * Creates a property change listener that monitors the {@link JFileChooser}
1766:    * for property change events and updates the component display accordingly.
1767:    *
1768:    * @param fc  the file chooser.
1769:    *
1770:    * @return The property change listener.
1771:    *
1772:    * @see #installListeners(JFileChooser)
1773:    */
1774:   public PropertyChangeListener createPropertyChangeListener(JFileChooser fc)
1775:   {
1776:     return new MetalFileChooserPropertyChangeListener();
1777:   }
1778: 
1779:   /**
1780:    * Creates and returns a new instance of {@link DirectoryComboBoxModel}.
1781:    *
1782:    * @return A new instance of {@link DirectoryComboBoxModel}.
1783:    */
1784:   protected MetalFileChooserUI.DirectoryComboBoxModel
1785:       createDirectoryComboBoxModel(JFileChooser fc)
1786:   {
1787:     return new DirectoryComboBoxModel();
1788:   }
1789: 
1790:   /**
1791:    * Creates a new instance of the renderer used in the directory
1792:    * combo box.
1793:    *
1794:    * @param fc  the file chooser.
1795:    *
1796:    * @return The renderer.
1797:    */
1798:   protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(
1799:           JFileChooser fc)
1800:   {
1801:     return new DirectoryComboBoxRenderer(fc);
1802:   }
1803: 
1804:   /**
1805:    * Creates and returns a new instance of {@link FilterComboBoxModel}.
1806:    *
1807:    * @return A new instance of {@link FilterComboBoxModel}.
1808:    */
1809:   protected FilterComboBoxModel createFilterComboBoxModel()
1810:   {
1811:     return new FilterComboBoxModel();
1812:   }
1813: 
1814:   /**
1815:    * Creates and returns a new instance of {@link FilterComboBoxRenderer}.
1816:    *
1817:    * @return A new instance of {@link FilterComboBoxRenderer}.
1818:    */
1819:   protected MetalFileChooserUI.FilterComboBoxRenderer
1820:       createFilterComboBoxRenderer()
1821:   {
1822:     return new FilterComboBoxRenderer();
1823:   }
1824: 
1825:   /**
1826:    * Adds the control buttons ('up', 'home' etc.) to the panel.
1827:    */
1828:   protected void addControlButtons()
1829:   {
1830:     JButton upButton = new JButton(getChangeToParentDirectoryAction());
1831:     upButton.setText(null);
1832:     upButton.setIcon(this.upFolderIcon);
1833:     upButton.setMargin(new Insets(0, 0, 0, 0));
1834:     controls.add(upButton);
1835: 
1836:     JButton homeButton = new JButton(getGoHomeAction());
1837:     homeButton.setText(null);
1838:     homeButton.setIcon(this.homeFolderIcon);
1839:     homeButton.setMargin(new Insets(0, 0, 0, 0));
1840:     controls.add(homeButton);
1841: 
1842:     JButton newFolderButton = new JButton(getNewFolderAction());
1843:     newFolderButton.setText(null);
1844:     newFolderButton.setIcon(this.newFolderIcon);
1845:     newFolderButton.setMargin(new Insets(0, 0, 0, 0));
1846:     controls.add(newFolderButton);
1847: 
1848:     JToggleButton listButton = new JToggleButton(this.listViewIcon);
1849:     listButton.setMargin(new Insets(0, 0, 0, 0));
1850:     listButton.addActionListener(new ListViewActionListener());
1851:     listButton.setSelected(true);
1852:     listView = true;
1853:     controls.add(listButton);
1854: 
1855:     JToggleButton detailButton = new JToggleButton(this.detailsViewIcon);
1856:     detailButton.setMargin(new Insets(0, 0, 0, 0));
1857:     detailButton.addActionListener(new DetailViewActionListener());
1858:     detailButton.setSelected(false);
1859:     controls.add(detailButton);
1860: 
1861:     ButtonGroup buttonGroup = new ButtonGroup();
1862:     buttonGroup.add(listButton);
1863:     buttonGroup.add(detailButton);
1864:   }
1865: 
1866:   /**
1867:    * Removes all the buttons from the control panel.
1868:    */
1869:   protected void removeControlButtons()
1870:   {
1871:     controls.removeAll();
1872:     controls.revalidate();
1873:     controls.repaint();
1874:   }
1875: 
1876:   /**
1877:    * Updates the current directory.
1878:    *
1879:    * @param fc  the file chooser to update.
1880:    */
1881:   public void rescanCurrentDirectory(JFileChooser fc)
1882:   {
1883:     directoryModel.setSelectedItem(fc.getCurrentDirectory());
1884:     getModel().validateFileCache();
1885:     if (!listView)
1886:         updateTable();
1887:     else
1888:       createList(fc);
1889:   }
1890: 
1891:   /**
1892:    * Returns the file name in the text field.
1893:    *
1894:    * @return The file name.
1895:    */
1896:   public String getFileName()
1897:   {
1898:     String result = null;
1899:     if (fileTextField != null)
1900:       result = fileTextField.getText();
1901:     return result;
1902:   }
1903: 
1904:   /**
1905:    * Sets the file name in the text field.
1906:    *
1907:    * @param filename  the file name.
1908:    */
1909:   public void setFileName(String filename)
1910:   {
1911:     fileTextField.setText(filename);
1912:   }
1913: 
1914:   /**
1915:    * DOCUMENT ME!!
1916:    *
1917:    * @param e - DOCUMENT ME!
1918:    */
1919:   public void valueChanged(ListSelectionEvent e)
1920:   {
1921:     // FIXME: Not sure what we should be doing here, if anything.
1922:   }
1923: 
1924:   /**
1925:    * Returns the approve button.
1926:    *
1927:    * @return The approve button.
1928:    */
1929:   protected JButton getApproveButton(JFileChooser fc)
1930:   {
1931:     return approveButton;
1932:   }
1933: 
1934:   /**
1935:    * A layout manager that is used to arrange the subcomponents of the
1936:    * {@link JFileChooser}.
1937:    */
1938:   class VerticalMidLayout implements LayoutManager
1939:   {
1940:     /**
1941:      * Performs the layout.
1942:      *
1943:      * @param parent  the container.
1944:      */
1945:     public void layoutContainer(Container parent)
1946:     {
1947:       int count = parent.getComponentCount();
1948:       if (count > 0)
1949:         {
1950:           Insets insets = parent.getInsets();
1951:           Component c = parent.getComponent(0);
1952:           Dimension prefSize = c.getPreferredSize();
1953:           int h = parent.getHeight() - insets.top - insets.bottom;
1954:           int adj = Math.max(0, (h - prefSize.height) / 2);
1955:           c.setBounds(insets.left, insets.top + adj, parent.getWidth()
1956:               - insets.left - insets.right,
1957:               (int) Math.min(prefSize.getHeight(), h));
1958:         }
1959:     }
1960: 
1961:     /**
1962:      * Returns the minimum layout size.
1963:      *
1964:      * @param parent  the container.
1965:      *
1966:      * @return The minimum layout size.
1967:      */
1968:     public Dimension minimumLayoutSize(Container parent)
1969:     {
1970:       return preferredLayoutSize(parent);
1971:     }
1972: 
1973:     /**
1974:      * Returns the preferred layout size.
1975:      *
1976:      * @param parent  the container.
1977:      *
1978:      * @return The preferred layout size.
1979:      */
1980:     public Dimension preferredLayoutSize(Container parent)
1981:     {
1982:       if (parent.getComponentCount() > 0)
1983:         {
1984:           return parent.getComponent(0).getPreferredSize();
1985:         }
1986:       else return null;
1987:     }
1988: 
1989:     /**
1990:      * This layout manager does not need to track components, so this
1991:      * method does nothing.
1992:      *
1993:      * @param name  the name the component is associated with.
1994:      * @param component  the component.
1995:      */
1996:     public void addLayoutComponent(String name, Component component)
1997:     {
1998:       // do nothing
1999:     }
2000: 
2001:     /**
2002:      * This layout manager does not need to track components, so this
2003:      * method does nothing.
2004:      *
2005:      * @param component  the component.
2006:      */
2007:     public void removeLayoutComponent(Component component)
2008:     {
2009:       // do nothing
2010:     }
2011:   }
2012: 
2013:   /**
2014:    * A layout manager that is used to arrange buttons for the
2015:    * {@link JFileChooser}.
2016:    */
2017:   class ButtonLayout implements LayoutManager
2018:   {
2019:     static final int GAP = 4;
2020: 
2021:     /**
2022:      * Performs the layout.
2023:      *
2024:      * @param parent  the container.
2025:      */
2026:     public void layoutContainer(Container parent)
2027:     {
2028:       int count = parent.getComponentCount();
2029:       if (count > 0)
2030:         {
2031:           // first find the widest button
2032:           int maxW = 0;
2033:           for (int i = 0; i < count; i++)
2034:             {
2035:               Component c = parent.getComponent(i);
2036:               Dimension prefSize = c.getPreferredSize();
2037:               maxW = Math.max(prefSize.width, maxW);
2038:             }
2039: 
2040:           // then position the buttons
2041:           Insets insets = parent.getInsets();
2042:           int availableH = parent.getHeight() - insets.top - insets.bottom;
2043:           int currentX = parent.getWidth() - insets.right;
2044:           for (int i = count - 1; i >= 0; i--)
2045:             {
2046:               Component c = parent.getComponent(i);
2047:               Dimension prefSize = c.getPreferredSize();
2048:               int adj = Math.max(0, (availableH - prefSize.height) / 2);
2049:               currentX = currentX - prefSize.width;
2050:               c.setBounds(currentX, insets.top + adj, prefSize.width,
2051:                   (int) Math.min(prefSize.getHeight(), availableH));
2052:               currentX = currentX - GAP;
2053:             }
2054:         }
2055:     }
2056: 
2057:     /**
2058:      * Returns the minimum layout size.
2059:      *
2060:      * @param parent  the container.
2061:      *
2062:      * @return The minimum layout size.
2063:      */
2064:     public Dimension minimumLayoutSize(Container parent)
2065:     {
2066:       return preferredLayoutSize(parent);
2067:     }
2068: 
2069:     /**
2070:      * Returns the preferred layout size.
2071:      *
2072:      * @param parent  the container.
2073:      *
2074:      * @return The preferred layout size.
2075:      */
2076:     public Dimension preferredLayoutSize(Container parent)
2077:     {
2078:       Insets insets = parent.getInsets();
2079:       int maxW = 0;
2080:       int maxH = 0;
2081:       int count = parent.getComponentCount();
2082:       if (count > 0)
2083:         {
2084:           for (int i = 0; i < count; i++)
2085:             {
2086:               Component c = parent.getComponent(i);
2087:               Dimension d = c.getPreferredSize();
2088:               maxW = Math.max(d.width, maxW);
2089:               maxH = Math.max(d.height, maxH);
2090:             }
2091:         }
2092:       return new Dimension(maxW * count + GAP * (count - 1) + insets.left
2093:               + insets.right, maxH + insets.top + insets.bottom);
2094:     }
2095: 
2096:     /**
2097:      * This layout manager does not need to track components, so this
2098:      * method does nothing.
2099:      *
2100:      * @param name  the name the component is associated with.
2101:      * @param component  the component.
2102:      */
2103:     public void addLayoutComponent(String name, Component component)
2104:     {
2105:       // do nothing
2106:     }
2107: 
2108:     /**
2109:      * This layout manager does not need to track components, so this
2110:      * method does nothing.
2111:      *
2112:      * @param component  the component.
2113:      */
2114:     public void removeLayoutComponent(Component component)
2115:     {
2116:       // do nothing
2117:     }
2118:   }
2119: 
2120: }