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.GridLayout;
  46: import java.awt.Insets;
  47: import java.awt.LayoutManager;
  48: import java.awt.Rectangle;
  49: import java.awt.Window;
  50: import java.awt.event.ActionEvent;
  51: import java.awt.event.ActionListener;
  52: import java.awt.event.MouseAdapter;
  53: import java.awt.event.MouseEvent;
  54: import java.awt.event.MouseListener;
  55: import java.beans.PropertyChangeEvent;
  56: import java.beans.PropertyChangeListener;
  57: import java.io.File;
  58: import java.sql.Date;
  59: import java.text.DateFormat;
  60: import java.text.NumberFormat;
  61: import java.util.List;
  62: 
  63: import javax.swing.AbstractAction;
  64: import javax.swing.AbstractListModel;
  65: import javax.swing.ActionMap;
  66: import javax.swing.BorderFactory;
  67: import javax.swing.ButtonGroup;
  68: import javax.swing.ComboBoxModel;
  69: import javax.swing.DefaultListCellRenderer;
  70: import javax.swing.Icon;
  71: import javax.swing.JButton;
  72: import javax.swing.JComboBox;
  73: import javax.swing.JComponent;
  74: import javax.swing.JDialog;
  75: import javax.swing.JFileChooser;
  76: import javax.swing.JLabel;
  77: import javax.swing.JList;
  78: import javax.swing.JPanel;
  79: import javax.swing.JScrollPane;
  80: import javax.swing.JTable;
  81: import javax.swing.JTextField;
  82: import javax.swing.JToggleButton;
  83: import javax.swing.ListModel;
  84: import javax.swing.ListSelectionModel;
  85: import javax.swing.SwingUtilities;
  86: import javax.swing.UIManager;
  87: import javax.swing.event.ListSelectionEvent;
  88: import javax.swing.event.ListSelectionListener;
  89: import javax.swing.filechooser.FileFilter;
  90: import javax.swing.filechooser.FileSystemView;
  91: import javax.swing.filechooser.FileView;
  92: import javax.swing.plaf.ComponentUI;
  93: import javax.swing.plaf.basic.BasicFileChooserUI;
  94: import javax.swing.table.DefaultTableCellRenderer;
  95: import javax.swing.table.DefaultTableModel;
  96: 
  97: 
  98: /**
  99:  * A UI delegate for the {@link JFileChooser} component.  This class is only
 100:  * partially implemented and is not usable yet.
 101:  */
 102: public class MetalFileChooserUI 
 103:   extends BasicFileChooserUI
 104: {
 105:   
 106:   /**
 107:    * A renderer for the files and directories in the file chooser table.
 108:    */
 109:   class TableFileRenderer
 110:     extends DefaultTableCellRenderer
 111:   {
 112:     
 113:     /**
 114:      * Creates a new renderer.
 115:      */
 116:     public TableFileRenderer()
 117:     {
 118:       super();
 119:     }
 120:     
 121:     /**
 122:      * Returns a component that can render the specified value.
 123:      * 
 124:      * @param table  the table
 125:      * @param value  the string value of the cell
 126:      * @param isSelected  is the item selected?
 127:      * @param hasFocus  does the item have the focus?
 128:      * @param row  the row
 129:      * @param column  the column
 130:      * 
 131:      * @return The renderer.
 132:      */
 133:     public Component getTableCellRendererComponent(JTable table, Object value,
 134:         boolean isSelected, boolean hasFocus, int row, int column)
 135:     {
 136:       if (column == 0)
 137:         {
 138:           FileView v = getFileView(getFileChooser());
 139:           ListModel lm = fileList.getModel();
 140:           if (row < lm.getSize())
 141:             setIcon(v.getIcon((File) lm.getElementAt(row)));
 142:         }
 143:       else
 144:         setIcon(null);
 145:       
 146:       setText(value.toString());
 147:       setOpaque(true);
 148:       setEnabled(table.isEnabled());
 149:       setFont(fileList.getFont());
 150:       
 151:       if (startEditing && column == 0 || !isSelected)
 152:         {
 153:           setBackground(table.getBackground());
 154:           setForeground(table.getForeground());
 155:         }
 156:       else
 157:         {
 158:           setBackground(table.getSelectionBackground());
 159:           setForeground(table.getSelectionForeground());
 160:         }
 161: 
 162:       if (hasFocus)
 163:         setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
 164:       else
 165:         setBorder(noFocusBorder);
 166:       
 167:       return this;
 168:     }
 169:   }
 170:   
 171:   /**
 172:    * ActionListener for the list view.
 173:    */
 174:   class ListViewActionListener implements ActionListener
 175:   {
 176:     
 177:     /**
 178:      * This method is invoked when an action occurs.
 179:      * 
 180:      * @param e -
 181:      *          the <code>ActionEvent</code> that occurred
 182:      */
 183:     public void actionPerformed(ActionEvent e)
 184:     {
 185:       if (!listView)
 186:         {
 187:           int[] index = fileTable.getSelectedRows();
 188:           listView = true;
 189:           JFileChooser fc = getFileChooser();
 190:           fc.remove(fileTablePanel);
 191:           createList(fc);
 192: 
 193:           fileList.getSelectionModel().clearSelection();
 194:           if (index.length > 0)
 195:               for (int i = 0; i < index.length; i++)
 196:                 fileList.getSelectionModel().addSelectionInterval(index[i], index[i]);
 197:           
 198:           fc.add(fileListPanel, BorderLayout.CENTER);
 199:           fc.revalidate();
 200:           fc.repaint();
 201:         }
 202:     }
 203:   }
 204:   
 205:   /**
 206:    * ActionListener for the details view.
 207:    */
 208:   class DetailViewActionListener implements ActionListener
 209:   {
 210:     
 211:     /**
 212:      * This method is invoked when an action occurs.
 213:      * 
 214:      * @param e -
 215:      *          the <code>ActionEvent</code> that occurred
 216:      */
 217:     public void actionPerformed(ActionEvent e)
 218:     {
 219:       if (listView)
 220:         {
 221:           int[] index = fileList.getSelectedIndices();
 222:           JFileChooser fc = getFileChooser();
 223:           listView = false;
 224:           fc.remove(fileListPanel);
 225:           
 226:           if (fileTable == null)
 227:             createDetailsView(fc);
 228:           else
 229:             updateTable();
 230: 
 231:           fileTable.getSelectionModel().clearSelection();
 232:           if (index.length > 0)
 233:             {
 234:               for (int i = 0; i < index.length; i++)
 235:                 fileTable.getSelectionModel().addSelectionInterval(index[i], index[i]);
 236:             }
 237:           
 238:           fc.add(fileTablePanel, BorderLayout.CENTER);
 239:           fc.revalidate();
 240:           fc.repaint();
 241:         }
 242:     }
 243:   }
 244:   
 245:   /**
 246:    * A property change listener.
 247:    */
 248:   class MetalFileChooserPropertyChangeListener 
 249:     implements PropertyChangeListener
 250:   {
 251:     /**
 252:      * Default constructor.
 253:      */
 254:     public MetalFileChooserPropertyChangeListener()
 255:     {
 256:     }
 257:     
 258:     /**
 259:      * Handles a property change event.
 260:      * 
 261:      * @param e  the event.
 262:      */
 263:     public void propertyChange(PropertyChangeEvent e)
 264:     {
 265:       JFileChooser filechooser = getFileChooser();
 266:       
 267:       String n = e.getPropertyName();
 268:       if (n.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY))
 269:         {
 270:           int mode = -1; 
 271:           if (filechooser.isMultiSelectionEnabled())
 272:             mode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
 273:           else
 274:             mode = ListSelectionModel.SINGLE_SELECTION;
 275:           
 276:           if (listView)
 277:             fileList.setSelectionMode(mode);
 278:           else
 279:             fileTable.setSelectionMode(mode);
 280:         }
 281:       else if (n.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY))
 282:         {
 283:           File file = filechooser.getSelectedFile();
 284:           
 285:           if (file != null
 286:               && filechooser.getDialogType() == JFileChooser.SAVE_DIALOG)
 287:             {
 288:               if (file.isDirectory() && filechooser.isTraversable(file))
 289:                 {
 290:                   directoryLabel = look;
 291:                   dirLabel.setText(directoryLabel);
 292:                   filechooser.setApproveButtonText(openButtonText);
 293:                   filechooser.setApproveButtonToolTipText(openButtonToolTipText);
 294:                 }
 295:               else if (file.isFile())
 296:                 {
 297:                   directoryLabel = save;
 298:                   dirLabel.setText(directoryLabel);
 299:                   filechooser.setApproveButtonText(saveButtonText);
 300:                   filechooser.setApproveButtonToolTipText(saveButtonToolTipText);
 301:                 }
 302:             }
 303:             
 304:           if (file == null)
 305:             setFileName(null);
 306:           else
 307:             setFileName(file.getName());
 308:           int index = -1;
 309:           index = getModel().indexOf(file);
 310:           if (index >= 0)
 311:             {
 312:               if (listView)
 313:                 {
 314:                   fileList.setSelectedIndex(index);
 315:                   fileList.ensureIndexIsVisible(index);
 316:                   fileList.revalidate();
 317:                   fileList.repaint();
 318:                 }
 319:               else
 320:                 {
 321:                   fileTable.getSelectionModel().addSelectionInterval(index, index);
 322:                   fileTable.scrollRectToVisible(fileTable.getCellRect(index, 0, true));
 323:                   fileTable.revalidate();
 324:                   fileTable.repaint();
 325:                 }
 326:             }
 327:         }
 328:       
 329:       else if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY))
 330:         {
 331:           if (listView)
 332:             {
 333:               fileList.clearSelection();
 334:               fileList.revalidate();
 335:               fileList.repaint();
 336:             }
 337:           else
 338:             {
 339:               fileTable.clearSelection();
 340:               fileTable.revalidate();
 341:               fileTable.repaint();
 342:             }
 343: 
 344:           setDirectorySelected(false);
 345:           File currentDirectory = filechooser.getCurrentDirectory();
 346:           setDirectory(currentDirectory);
 347:           boolean hasParent = currentDirectory.getParentFile() != null;
 348:           getChangeToParentDirectoryAction().setEnabled(hasParent);
 349:         }
 350:       
 351:       else if (n.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY))
 352:         {
 353:           filterModel.propertyChange(e);
 354:         }
 355:       else if (n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY))
 356:         {
 357:           filterModel.propertyChange(e);
 358:         }
 359:       else if (n.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY)
 360:                  || n.equals(JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY))
 361:         {
 362:           Window owner = SwingUtilities.windowForComponent(filechooser);
 363:           if (owner instanceof JDialog)
 364:             ((JDialog) owner).setTitle(getDialogTitle(filechooser));
 365:           approveButton.setText(getApproveButtonText(filechooser));
 366:           approveButton.setToolTipText(
 367:                   getApproveButtonToolTipText(filechooser));
 368:           approveButton.setMnemonic(getApproveButtonMnemonic(filechooser));
 369:         }
 370:       
 371:       else if (n.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY))
 372:         approveButton.setText(getApproveButtonText(filechooser));
 373:       
 374:       else if (n.equals(
 375:               JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY))
 376:         approveButton.setToolTipText(getApproveButtonToolTipText(filechooser));
 377:       
 378:       else if (n.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY))
 379:         approveButton.setMnemonic(getApproveButtonMnemonic(filechooser));
 380: 
 381:       else if (n.equals(
 382:               JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY))
 383:         {
 384:           if (filechooser.getControlButtonsAreShown())
 385:             {
 386:               topPanel.add(controls, BorderLayout.EAST);
 387:             }
 388:           else
 389:             topPanel.remove(controls);
 390:           topPanel.revalidate();
 391:           topPanel.repaint();
 392:           topPanel.doLayout();
 393:         }
 394:       
 395:       else if (n.equals(
 396:               JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY))
 397:         {
 398:           if (filechooser.isAcceptAllFileFilterUsed())
 399:             filechooser.addChoosableFileFilter(
 400:                     getAcceptAllFileFilter(filechooser));
 401:           else
 402:             filechooser.removeChoosableFileFilter(
 403:                     getAcceptAllFileFilter(filechooser));
 404:         }
 405:       
 406:       else if (n.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY))
 407:         {
 408:           JComponent old = (JComponent) e.getOldValue();
 409:           if (old != null)
 410:             getAccessoryPanel().remove(old);
 411:           JComponent newval = (JComponent) e.getNewValue();
 412:           if (newval != null)
 413:             getAccessoryPanel().add(newval);
 414:         }
 415:       
 416:       if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)
 417:           || n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)
 418:           || n.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY))
 419:         {
 420:           // Remove editing component
 421:           if (fileTable != null)
 422:             fileTable.removeAll();
 423:           if (fileList != null)
 424:             fileList.removeAll();
 425:           startEditing = false;
 426:           
 427:           // Set text on button back to original.
 428:           if (filechooser.getDialogType() == JFileChooser.SAVE_DIALOG)
 429:             {
 430:               directoryLabel = save;
 431:               dirLabel.setText(directoryLabel);
 432:               filechooser.setApproveButtonText(saveButtonText);
 433:               filechooser.setApproveButtonToolTipText(saveButtonToolTipText);
 434:             }
 435:           
 436:           rescanCurrentDirectory(filechooser);
 437:         }
 438:       
 439:       filechooser.revalidate();
 440:       filechooser.repaint();
 441:     }
 442:   };
 443:   
 444:   /** 
 445:    * A combo box model containing the selected directory and all its parent
 446:    * directories.
 447:    */
 448:   protected class DirectoryComboBoxModel
 449:     extends AbstractListModel
 450:     implements ComboBoxModel
 451:   {
 452:     /** Storage for the items in the model. */
 453:     private List items;
 454:     
 455:     /** The index of the selected item. */
 456:     private int selectedIndex;
 457:     
 458:     /**
 459:      * Creates a new model.
 460:      */
 461:     public DirectoryComboBoxModel() 
 462:     {
 463:       items = new java.util.ArrayList();
 464:       selectedIndex = -1;
 465:     }
 466:     
 467:     /**
 468:      * Returns the number of items in the model.
 469:      * 
 470:      * @return The number of items in the model.
 471:      */
 472:     public int getSize()
 473:     {
 474:       return items.size();
 475:     }
 476:     
 477:     /**
 478:      * Returns the item at the specified index.
 479:      * 
 480:      * @param index  the item index.
 481:      * 
 482:      * @return The item.
 483:      */
 484:     public Object getElementAt(int index)
 485:     {
 486:       return items.get(index);
 487:     }
 488:     
 489:     /**
 490:      * Returns the depth of the item at the given <code>index</code>.
 491:      * 
 492:      * @param index  the item index.
 493:      * 
 494:      * @return The depth.
 495:      */
 496:     public int getDepth(int index)
 497:     {
 498:       return Math.max(index, 0);
 499:     }
 500: 
 501:     /**
 502:      * Returns the selected item, or <code>null</code> if no item is selected.
 503:      * 
 504:      * @return The selected item, or <code>null</code>.
 505:      */
 506:     public Object getSelectedItem()
 507:     {
 508:       if (selectedIndex >= 0) 
 509:         return items.get(selectedIndex);
 510:       else
 511:         return null;
 512:     }
 513:     
 514:     /**
 515:      * Sets the selected item.  This clears all the directories from the
 516:      * existing list, and repopulates it with the new selected directory
 517:      * and all its parent directories.
 518:      * 
 519:      * @param selectedDirectory  the selected directory.
 520:      */
 521:     public void setSelectedItem(Object selectedDirectory)
 522:     {
 523:       items.clear();
 524:       FileSystemView fsv = getFileChooser().getFileSystemView();
 525:       File parent = (File) selectedDirectory;
 526:       while (parent != null)
 527:         {
 528:           items.add(0, parent);
 529:           parent = fsv.getParentDirectory(parent);
 530:         }
 531:       selectedIndex = items.indexOf(selectedDirectory);
 532:       fireContentsChanged(this, 0, items.size() - 1);
 533:     }
 534:     
 535:   }
 536: 
 537:   /**
 538:    * Handles changes to the selection in the directory combo box.
 539:    */
 540:   protected class DirectoryComboBoxAction
 541:     extends AbstractAction
 542:   {
 543:     /**
 544:      * Creates a new action.
 545:      */
 546:     protected DirectoryComboBoxAction()
 547:     {
 548:       // Nothing to do here.
 549:     }
 550:     
 551:     /**
 552:      * Handles the action event.
 553:      * 
 554:      * @param e  the event.
 555:      */
 556:     public void actionPerformed(ActionEvent e)
 557:     {
 558:       JFileChooser fc = getFileChooser();
 559:       fc.setCurrentDirectory((File) directoryModel.getSelectedItem());
 560:     }
 561:   }
 562: 
 563:   /**
 564:    * A renderer for the items in the directory combo box.
 565:    */
 566:   class DirectoryComboBoxRenderer
 567:     extends DefaultListCellRenderer
 568:   {
 569:     /**
 570:      * Creates a new renderer.
 571:      */
 572:     public DirectoryComboBoxRenderer(JFileChooser fc)
 573:     { 
 574:     }
 575:     
 576:     /**
 577:      * Returns a component that can be used to paint the given value within 
 578:      * the list.
 579:      * 
 580:      * @param list  the list.
 581:      * @param value  the value (a {@link File}).
 582:      * @param index  the item index.
 583:      * @param isSelected  is the item selected?
 584:      * @param cellHasFocus  does the list cell have focus?
 585:      * 
 586:      * @return The list cell renderer.
 587:      */
 588:     public Component getListCellRendererComponent(JList list, Object value,
 589:         int index, boolean isSelected, boolean cellHasFocus)
 590:     {
 591:       FileView fileView = getFileView(getFileChooser());
 592:       File file = (File) value;
 593:       setIcon(fileView.getIcon(file));
 594:       setText(fileView.getName(file));
 595:       
 596:       if (isSelected)
 597:         {
 598:           setBackground(list.getSelectionBackground());
 599:           setForeground(list.getSelectionForeground());
 600:         }
 601:       else
 602:         {
 603:           setBackground(list.getBackground());
 604:           setForeground(list.getForeground());
 605:         }
 606: 
 607:       setEnabled(list.isEnabled());
 608:       setFont(list.getFont());
 609:       return this;
 610:     }
 611:   }
 612: 
 613:   /**
 614:    * A renderer for the files and directories in the file chooser.
 615:    */
 616:   protected class FileRenderer
 617:     extends DefaultListCellRenderer
 618:   {
 619:     
 620:     /**
 621:      * Creates a new renderer.
 622:      */
 623:     protected FileRenderer()
 624:     {
 625:       // Nothing to do here.
 626:     }
 627:     
 628:     /**
 629:      * Returns a component that can render the specified value.
 630:      * 
 631:      * @param list  the list.
 632:      * @param value  the value (a {@link File}).
 633:      * @param index  the index.
 634:      * @param isSelected  is the item selected?
 635:      * @param cellHasFocus  does the item have the focus?
 636:      * 
 637:      * @return The renderer.
 638:      */
 639:     public Component getListCellRendererComponent(JList list, Object value,
 640:         int index, boolean isSelected, boolean cellHasFocus)
 641:     {
 642:       FileView v = getFileView(getFileChooser());
 643:       File f = (File) value;
 644:       if (f != null)
 645:         {
 646:           setText(v.getName(f));
 647:           setIcon(v.getIcon(f));
 648:         }
 649:       else
 650:         {
 651:           setText("");
 652:           setIcon(null);
 653:         }
 654:       setOpaque(true);
 655:       if (isSelected)
 656:         {
 657:           setBackground(list.getSelectionBackground());
 658:           setForeground(list.getSelectionForeground());
 659:         }
 660:       else
 661:         {
 662:           setBackground(list.getBackground());
 663:           setForeground(list.getForeground());
 664:         }
 665: 
 666:       setEnabled(list.isEnabled());
 667:       setFont(list.getFont());
 668: 
 669:       if (cellHasFocus)
 670:         setBorder(UIManager.getBorder("List.focusCellHighlightBorder"));
 671:       else
 672:         setBorder(noFocusBorder);
 673:       return this;
 674:     }
 675:   }
 676: 
 677:   /**
 678:    * A combo box model for the file selection filters.
 679:    */
 680:   protected class FilterComboBoxModel
 681:     extends AbstractListModel
 682:     implements ComboBoxModel, PropertyChangeListener
 683:   {
 684: 
 685:     /** Storage for the filters in the model. */
 686:     protected FileFilter[] filters;
 687: 
 688:     /** The index of the selected file filter. */
 689:     private Object selected;
 690:     
 691:     /**
 692:      * Creates a new model.
 693:      */
 694:     protected FilterComboBoxModel()
 695:     {
 696:       filters = new FileFilter[1];
 697:       filters[0] = getAcceptAllFileFilter(getFileChooser());
 698:       selected = filters[0];
 699:     }
 700:     
 701:     /**
 702:      * Handles property changes.
 703:      * 
 704:      * @param e  the property change event.
 705:      */
 706:     public void propertyChange(PropertyChangeEvent e)
 707:     {
 708:       if (e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY))
 709:         {
 710:           JFileChooser fc = getFileChooser();
 711:           FileFilter[] choosableFilters = fc.getChoosableFileFilters();
 712:           filters = choosableFilters;
 713:           fireContentsChanged(this, 0, filters.length);
 714:           selected = e.getNewValue();
 715:           fireContentsChanged(this, -1, -1);
 716:         }
 717:       else if (e.getPropertyName().equals(
 718:               JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY))
 719:         {
 720:           // repopulate list
 721:           JFileChooser fc = getFileChooser();
 722:           FileFilter[] choosableFilters = fc.getChoosableFileFilters();
 723:           filters = choosableFilters;
 724:           fireContentsChanged(this, 0, filters.length);
 725:         }
 726:     }
 727:     
 728:     /**
 729:      * Sets the selected filter.
 730:      * 
 731:      * @param filter  the filter (<code>null</code> ignored).
 732:      */
 733:     public void setSelectedItem(Object filter)
 734:     {
 735:       if (filter != null)
 736:       {
 737:           selected = filter;
 738:           fireContentsChanged(this, -1, -1);
 739:       }
 740:     }
 741:     
 742:     /**
 743:      * Returns the selected file filter.
 744:      * 
 745:      * @return The selected file filter.
 746:      */
 747:     public Object getSelectedItem()
 748:     {
 749:       return selected;
 750:     }
 751:     
 752:     /**
 753:      * Returns the number of items in the model.
 754:      * 
 755:      * @return The number of items in the model.
 756:      */
 757:     public int getSize()
 758:     {
 759:       return filters.length;
 760:     }
 761:     
 762:     /**
 763:      * Returns the item at the specified index.
 764:      * 
 765:      * @param index  the item index.
 766:      * 
 767:      * @return The item at the specified index.
 768:      */
 769:     public Object getElementAt(int index)
 770:     {
 771:       return filters[index];
 772:     }
 773:     
 774:   }
 775: 
 776:   /**
 777:    * A renderer for the items in the file filter combo box.
 778:    */
 779:   public class FilterComboBoxRenderer
 780:     extends DefaultListCellRenderer
 781:   {
 782:     /**
 783:      * Creates a new renderer.
 784:      */
 785:     public FilterComboBoxRenderer()
 786:     {
 787:       // Nothing to do here.
 788:     }
 789:     
 790:     /**
 791:      * Returns a component that can be used to paint the given value within 
 792:      * the list.
 793:      * 
 794:      * @param list  the list.
 795:      * @param value  the value (a {@link FileFilter}).
 796:      * @param index  the item index.
 797:      * @param isSelected  is the item selected?
 798:      * @param cellHasFocus  does the list cell have focus?
 799:      * 
 800:      * @return This component as the renderer.
 801:      */
 802:     public Component getListCellRendererComponent(JList list, Object value,
 803:         int index, boolean isSelected, boolean cellHasFocus)
 804:     {
 805:       super.getListCellRendererComponent(list, value, index, isSelected, 
 806:                                          cellHasFocus);
 807:       FileFilter filter = (FileFilter) value;
 808:       setText(filter.getDescription());
 809:       return this;
 810:     }
 811:   }
 812: 
 813:   /**
 814:    * A listener for selection events in the file list.
 815:    * 
 816:    * @see #createListSelectionListener(JFileChooser)
 817:    */
 818:   class MetalFileChooserSelectionListener 
 819:     implements ListSelectionListener
 820:   {
 821:     /**
 822:      * Creates a new <code>SelectionListener</code> object.
 823:      */
 824:     protected MetalFileChooserSelectionListener()
 825:     {
 826:       // Do nothing here.
 827:     }
 828: 
 829:     /**
 830:      * Makes changes to different properties when
 831:      * a value has changed in the filechooser's selection.
 832:      *
 833:      * @param e - the list selection event that occured.
 834:      */
 835:     public void valueChanged(ListSelectionEvent e)
 836:     {
 837:       File f = (File) fileList.getSelectedValue();
 838:       if (f == null)
 839:         return;
 840:       JFileChooser filechooser = getFileChooser();
 841:       if (! filechooser.isTraversable(f))
 842:         filechooser.setSelectedFile(f);
 843:       else
 844:         filechooser.setSelectedFile(null);
 845:     }
 846:   }
 847: 
 848:   /**
 849:    * A mouse listener for the {@link JFileChooser}.
 850:    * This listener is used for editing filenames.
 851:    */
 852:   protected class SingleClickListener
 853:     extends MouseAdapter
 854:   {
 855:     
 856:     /** Stores instance of the list */
 857:     JList list;
 858:     
 859:     /** 
 860:      * Stores the current file that is being edited.
 861:      * It is null if nothing is currently being edited.
 862:      */
 863:     File editFile;
 864:     
 865:     /** The current file chooser. */
 866:     JFileChooser fc;
 867:     
 868:     /** The last file selected. */
 869:     Object lastSelected;
 870:     
 871:     /** The textfield used for editing. */
 872:     JTextField editField;
 873:     
 874:     /**
 875:      * Creates a new listener.
 876:      * 
 877:      * @param list  the directory/file list.
 878:      */
 879:     public SingleClickListener(JList list)
 880:     {
 881:       this.list = list;
 882:       editFile = null;
 883:       fc = getFileChooser();
 884:       lastSelected = null;
 885:       startEditing = false;
 886:     }
 887:     
 888:     /**
 889:      * Receives notification of a mouse click event.
 890:      * 
 891:      * @param e  the event.
 892:      */
 893:     public void mouseClicked(MouseEvent e)
 894:     {
 895:       if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1)
 896:         {
 897:           int index = list.locationToIndex(e.getPoint());
 898:           File[] sf = fc.getSelectedFiles();
 899:           if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1))
 900:               && index >= 0 && !startEditing && list.isSelectedIndex(index))
 901:             {
 902:               Object tmp = list.getModel().getElementAt(index);
 903:               if (lastSelected != null && lastSelected.equals(tmp))
 904:                 editFile(index);
 905:               lastSelected = tmp;
 906:             }
 907:           else
 908:               completeEditing();
 909:         }
 910:       else
 911:         completeEditing();
 912:     }
 913:     
 914:     /**
 915:      * Sets up the text editor for the current file.
 916:      * 
 917:      * @param index -
 918:      *          the current index of the item in the list to be edited.
 919:      */
 920:     void editFile(int index)
 921:     {
 922:       Rectangle bounds = list.getCellBounds(index, index);
 923:       list.scrollRectToVisible(bounds);
 924:       editFile = (File) list.getModel().getElementAt(index);
 925:       if (editFile.canWrite())
 926:         {
 927:           startEditing = true;
 928:           editField = new JTextField(editFile.getName());
 929:           editField.addActionListener(new EditingActionListener());
 930:           
 931:           Icon icon = getFileView(fc).getIcon(editFile);
 932:           if (icon != null)
 933:             {
 934:               int padding = icon.getIconWidth() + 4;
 935:               bounds.x += padding;
 936:               bounds.width -= padding;
 937:             }
 938:           editField.setBounds(bounds);
 939:           
 940:           list.add(editField);
 941:           
 942:           editField.requestFocus();
 943:           editField.selectAll();
 944:         }
 945:       else
 946:         completeEditing();
 947:       list.repaint();
 948:     }
 949:     
 950:     /** 
 951:      * Completes the editing.
 952:      */
 953:     void completeEditing()
 954:     {
 955:       if (editField != null && editFile != null)
 956:         {
 957:           String text = editField.getText();
 958:           if (text != null && text != "" && !text.equals(fc.getName(editFile)))
 959:               if (editFile.renameTo(fc.getFileSystemView().createFileObject(
 960:                   fc.getCurrentDirectory(), text)))
 961:                 rescanCurrentDirectory(fc);
 962:           list.remove(editField);
 963:         }
 964:       startEditing = false;
 965:       editFile = null;
 966:       lastSelected = null;
 967:       editField = null;
 968:       list.repaint();
 969:     }
 970:     
 971:     /**
 972:      * ActionListener for the editing text field.
 973:      */
 974:     class EditingActionListener implements ActionListener
 975:     {
 976:       
 977:       /**
 978:        * This method is invoked when an action occurs.
 979:        * 
 980:        * @param e -
 981:        *          the <code>ActionEvent</code> that occurred
 982:        */
 983:       public void actionPerformed(ActionEvent e)
 984:       {
 985:         if (e.getActionCommand().equals("notify-field-accept"))
 986:           completeEditing();
 987:         else if (editField != null)
 988:           {
 989:             list.remove(editField);
 990:             startEditing = false;
 991:             editFile = null;
 992:             lastSelected = null;
 993:             editField = null;
 994:             list.repaint();
 995:           }
 996:       }
 997:     }
 998:   }
 999: 
1000:   /**
1001:    * A mouse listener for the {@link JFileChooser}.
1002:    * This listener is used for the table
1003:    */
1004:   private class TableClickListener extends MouseAdapter
1005:   {
1006: 
1007:     /** Stores instance of the table */
1008:     JTable table;
1009: 
1010:     /** Stores instance of the file chooser */
1011:     JFileChooser fc;
1012: 
1013:     /** The last selected file. */
1014:     Object lastSelected;
1015:     
1016:     /** 
1017:      * Stores the current file that is being edited.
1018:      * It is null if nothing is currently being edited.
1019:      */
1020:     File editFile;
1021:     
1022:     /** The textfield used for editing. */
1023:     JTextField editField;
1024: 
1025:     /**
1026:      * Creates a new listener.
1027:      * 
1028:      * @param table the directory/file table
1029:      * @param fc the JFileChooser
1030:      */
1031:     public TableClickListener(JTable table, JFileChooser fc)
1032:     {
1033:       this.table = table;
1034:       this.fc = fc;
1035:       lastSelected = fileList.getSelectedValue();
1036:       setDirectorySelected(false);
1037:       startEditing = false;
1038:       editFile = null;
1039:       editField = null;
1040:     }
1041: 
1042:     /**
1043:      * Receives notification of a mouse click event.
1044:      * 
1045:      * @param e the event.
1046:      */
1047:     public void mouseClicked(MouseEvent e)
1048:     {
1049:       int row = table.getSelectedRow();
1050:       Object selVal = fileList.getModel().getElementAt(row);
1051:       if (selVal == null)
1052:         return;
1053:       FileSystemView fsv = fc.getFileSystemView();
1054:       if (e.getClickCount() == 1 &&
1055:           selVal.equals(lastSelected) &&
1056:           e.getButton() == MouseEvent.BUTTON1)
1057:         {
1058:           File[] sf = fc.getSelectedFiles();
1059:           if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1))
1060:               && !startEditing)
1061:             {
1062:               editFile = (File) selVal;
1063:               editFile(row);
1064:             }
1065:         }
1066:       else if (e.getClickCount() >= 2 &&
1067:           selVal.equals(lastSelected))
1068:         {
1069:           if (startEditing)
1070:             completeEditing();
1071:           File f = fsv.createFileObject(lastSelected.toString());
1072:           if (fc.isTraversable(f))
1073:             {
1074:               fc.setCurrentDirectory(f);
1075:               fc.rescanCurrentDirectory();
1076:             }
1077:           else
1078:             {
1079:               fc.setSelectedFile(f);
1080:               fc.approveSelection();
1081:               closeDialog();
1082:             }
1083:         }
1084:       else
1085:         {
1086:           if (startEditing)
1087:             completeEditing();
1088:           String path = selVal.toString();
1089:           File f = fsv.createFileObject(path);
1090:           fc.setSelectedFile(f);
1091:           if (fc.isTraversable(f))
1092:             {
1093:               setDirectorySelected(true);
1094:               setDirectory(f);
1095:             }
1096:           else
1097:             {
1098:               setDirectorySelected(false);
1099:               setDirectory(null);
1100:             }
1101:           lastSelected = selVal;
1102:           if (f.isFile())
1103:             setFileName(path.substring(path.lastIndexOf("/") + 1));
1104:           else if (fc.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY)
1105:             setFileName(path);
1106:         }
1107:       fileTable.repaint();
1108:     }
1109: 
1110:     /**
1111:      * Sets up the text editor for the current file.
1112:      * 
1113:      * @param row -
1114:      *          the current row of the item in the list to be edited.
1115:      */
1116:     void editFile(int row)
1117:     {
1118:       Rectangle bounds = table.getCellRect(row, 0, true);
1119:       table.scrollRectToVisible(bounds);
1120:       if (editFile.canWrite())
1121:         {
1122:           startEditing = true;
1123:           editField = new JTextField(editFile.getName());
1124:           editField.addActionListener(new EditingActionListener());
1125: 
1126:           // Need to adjust y pos
1127:           bounds.y = row * table.getRowHeight();
1128:           editField.setBounds(bounds);
1129:           
1130:           table.add(editField);
1131:           
1132:           editField.requestFocus();
1133:           editField.selectAll();
1134:         }
1135:       else
1136:         completeEditing();
1137:       table.repaint();
1138:     }
1139:     
1140:     /** 
1141:      * Completes the editing.
1142:      */
1143:     void completeEditing()
1144:     {
1145:       if (editField != null && editFile != null)
1146:         {
1147:           String text = editField.getText();
1148:           if (text != null && text != "" && !text.equals(fc.getName(editFile)))
1149:               if (editFile.renameTo(fc.getFileSystemView().createFileObject(
1150:                   fc.getCurrentDirectory(), text)))
1151:                 rescanCurrentDirectory(fc);
1152:           table.remove(editField);
1153:         }
1154:       startEditing = false;
1155:       editFile = null;
1156:       editField = null;
1157:       table.repaint();
1158:     }
1159:     
1160:     /**
1161:      * ActionListener for the editing text field.
1162:      */
1163:     class EditingActionListener implements ActionListener
1164:     {
1165:       
1166:       /**
1167:        * This method is invoked when an action occurs.
1168:        * 
1169:        * @param e -
1170:        *          the <code>ActionEvent</code> that occurred
1171:        */
1172:       public void actionPerformed(ActionEvent e)
1173:       {
1174:         if (e.getActionCommand().equals("notify-field-accept"))
1175:           completeEditing();
1176:         else if (editField != null)
1177:           {
1178:             table.remove(editField);
1179:             startEditing = false;
1180:             editFile = null;
1181:             editField = null;
1182:             table.repaint();
1183:           }
1184:       }
1185:     }
1186:     
1187:     /**
1188:      * Closes the dialog.
1189:      */
1190:     public void closeDialog()
1191:     {
1192:       Window owner = SwingUtilities.windowForComponent(fc);
1193:       if (owner instanceof JDialog)
1194:         ((JDialog) owner).dispose();
1195:     }
1196:   } 
1197:   
1198:   /** The text for a label describing the directory combo box. */
1199:   private String directoryLabel;
1200:   
1201:   private JComboBox directoryComboBox;
1202:   
1203:   /** The model for the directory combo box. */
1204:   DirectoryComboBoxModel directoryModel;
1205:   
1206:   /** The text for a label describing the file text field. */
1207:   private String fileLabel;
1208:   
1209:   /** The file name text field. */
1210:   private JTextField fileTextField;
1211:   
1212:   /** The text for a label describing the filter combo box. */
1213:   private String filterLabel;
1214: 
1215:   /** 
1216:    * The top panel (contains the directory combo box and the control buttons). 
1217:    */
1218:   private JPanel topPanel;
1219:   
1220:   /** A panel containing the control buttons ('up', 'home' etc.). */
1221:   private JPanel controls;
1222: 
1223:   /** 
1224:    * The panel that contains the filename field and the filter combobox. 
1225:    */
1226:   private JPanel bottomPanel;
1227: 
1228:   /** 
1229:    * The panel that contains the 'Open' (or 'Save') and 'Cancel' buttons. 
1230:    */
1231:   private JPanel buttonPanel;
1232:   
1233:   private JButton approveButton;
1234:   
1235:   /** The file list. */
1236:   JList fileList;
1237:   
1238:   /** The file table. */
1239:   JTable fileTable;
1240:   
1241:   /** The panel containing the file list. */
1242:   JPanel fileListPanel;
1243:   
1244:   /** The panel containing the file table. */
1245:   JPanel fileTablePanel;
1246:   
1247:   /** The filter combo box model. */
1248:   private FilterComboBoxModel filterModel;
1249: 
1250:   /** The action map. */
1251:   private ActionMap actionMap;
1252:   
1253:   /** True if currently in list view. */
1254:   boolean listView;
1255:   
1256:   /** True if we can or have started editing a cell. */
1257:   boolean startEditing;
1258:   
1259:   /** The scrollpane used for the table and list. */
1260:   JScrollPane scrollPane;
1261:   
1262:   /** The text for the label when saving. */
1263:   String save;
1264:   
1265:   /** The text for the label when opening a directory. */
1266:   String look;
1267:   
1268:   /** The label for the file combo box. */
1269:   JLabel dirLabel;
1270:   
1271:   /** Listeners. */
1272:   ListSelectionListener listSelList;
1273:   MouseListener doubleClickList;
1274:   SingleClickListener singleClickList;
1275:   TableClickListener tableClickList;
1276:   
1277:   /**
1278:    * A factory method that returns a UI delegate for the specified
1279:    * component.
1280:    * 
1281:    * @param c  the component (which should be a {@link JFileChooser}).
1282:    */
1283:   public static ComponentUI createUI(JComponent c)
1284:   {
1285:     JFileChooser chooser = (JFileChooser) c;
1286:     return new MetalFileChooserUI(chooser);
1287:   }
1288: 
1289:   /**
1290:    * Creates a new instance of this UI delegate.
1291:    * 
1292:    * @param filechooser  the file chooser component.
1293:    */
1294:   public MetalFileChooserUI(JFileChooser filechooser)
1295:   {
1296:     super(filechooser);
1297:     bottomPanel = new JPanel(new GridLayout(3, 2));
1298:     buttonPanel = new JPanel();
1299:   }
1300: 
1301:   public void installUI(JComponent c)
1302:   {
1303:     super.installUI(c);
1304:     actionMap = createActionMap();
1305:   }
1306:   
1307:   public void uninstallUI(JComponent c)
1308:   {
1309:     super.uninstallUI(c);
1310:     actionMap = null;
1311:   }
1312:   
1313:   /**
1314:    * Installs the sub-components of the file chooser.
1315:    * 
1316:    * @param fc  the file chooser component.
1317:    */
1318:   public void installComponents(JFileChooser fc)
1319:   {
1320:     fc.setLayout(new BorderLayout());
1321:     topPanel = new JPanel(new BorderLayout());
1322:     dirLabel = new JLabel(directoryLabel);
1323:     topPanel.add(dirLabel, BorderLayout.WEST);
1324:     this.controls = new JPanel();
1325:     addControlButtons();
1326:     
1327:     JPanel dirPanel = new JPanel(new VerticalMidLayout());
1328:     directoryModel = createDirectoryComboBoxModel(fc);
1329:     directoryComboBox = new JComboBox(directoryModel);
1330:     directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
1331:     dirPanel.add(directoryComboBox);
1332:     topPanel.add(dirPanel);
1333:     topPanel.add(controls, BorderLayout.EAST);
1334:     topPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 0, 8));
1335:     fc.add(topPanel, BorderLayout.NORTH);
1336:     
1337:     JPanel list = createList(fc);
1338:     list.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
1339:     fc.add(list, BorderLayout.CENTER);
1340:     
1341:     JPanel bottomPanel = getBottomPanel();
1342:     filterModel = createFilterComboBoxModel();
1343:     JComboBox fileFilterCombo = new JComboBox(filterModel);
1344:     fileFilterCombo.setRenderer(createFilterComboBoxRenderer());
1345:     
1346:     fileTextField = new JTextField();
1347:     JPanel fileNamePanel = new JPanel(new VerticalMidLayout());
1348:     fileNamePanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 5));
1349:     fileNamePanel.add(fileTextField);
1350:     JPanel row1 = new JPanel(new BorderLayout());
1351:     row1.add(new JLabel(this.fileLabel), BorderLayout.WEST);
1352:     row1.add(fileNamePanel);
1353:     bottomPanel.add(row1);
1354:     
1355:     JPanel row2 = new JPanel(new BorderLayout());
1356:     row2.add(new JLabel(this.filterLabel), BorderLayout.WEST);
1357:     row2.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
1358:     row2.add(fileFilterCombo);
1359:     bottomPanel.add(row2);
1360:     JPanel buttonPanel = new JPanel(new ButtonLayout());
1361:     
1362:     approveButton = new JButton(getApproveSelectionAction());
1363:     approveButton.setText(getApproveButtonText(fc));
1364:     approveButton.setToolTipText(getApproveButtonToolTipText(fc));
1365:     approveButton.setMnemonic(getApproveButtonMnemonic(fc));
1366:     buttonPanel.add(approveButton);
1367:     buttonPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0));
1368:     
1369:     JButton cancelButton = new JButton(getCancelSelectionAction());
1370:     cancelButton.setText(cancelButtonText);
1371:     cancelButton.setToolTipText(cancelButtonToolTipText);
1372:     cancelButton.setMnemonic(cancelButtonMnemonic);
1373:     buttonPanel.add(cancelButton);
1374:     bottomPanel.add(buttonPanel, BorderLayout.SOUTH);
1375:     bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
1376:     fc.add(bottomPanel, BorderLayout.SOUTH);
1377:     
1378:     fc.add(getAccessoryPanel(), BorderLayout.EAST);
1379:   }
1380:   
1381:   /**
1382:    * Uninstalls the components added by 
1383:    * {@link #installComponents(JFileChooser)}.
1384:    * 
1385:    * @param fc  the file chooser.
1386:    */
1387:   public void uninstallComponents(JFileChooser fc)
1388:   {
1389:     fc.remove(bottomPanel);
1390:     bottomPanel = null;
1391:     fc.remove(fileListPanel);
1392:     fc.remove(fileTablePanel);
1393:     fileTablePanel = null;
1394:     fileListPanel = null;
1395:     fc.remove(topPanel);
1396:     topPanel = null;
1397:     
1398:     directoryModel = null;
1399:     fileTextField = null;
1400:     directoryComboBox = null;
1401:   }
1402:   
1403:   /**
1404:    * Returns the panel that contains the 'Open' (or 'Save') and 'Cancel' 
1405:    * buttons.
1406:    * 
1407:    * @return The panel.
1408:    */
1409:   protected JPanel getButtonPanel()
1410:   {
1411:     return buttonPanel;    
1412:   }
1413:   
1414:   /**
1415:    * Creates and returns a new panel that will be used for the controls at
1416:    * the bottom of the file chooser.
1417:    * 
1418:    * @return A new panel.
1419:    */
1420:   protected JPanel getBottomPanel()
1421:   {
1422:     if (bottomPanel == null)
1423:       bottomPanel = new JPanel(new GridLayout(3, 2));
1424:     return bottomPanel;
1425:   }
1426:   
1427:   /**
1428:    * Fetches localised strings for use by the labels and buttons on the
1429:    * file chooser.
1430:    * 
1431:    * @param fc  the file chooser.
1432:    */
1433:   protected void installStrings(JFileChooser fc)
1434:   { 
1435:      super.installStrings(fc);
1436:      look = "Look In: ";
1437:      save = "Save In: ";
1438:      if (fc.getDialogType() == JFileChooser.SAVE_DIALOG)
1439:        directoryLabel = save;
1440:      else
1441:        directoryLabel = look;
1442:      
1443:      fileLabel = "File Name: ";
1444:      filterLabel = "Files of Type: ";
1445:      
1446:      this.cancelButtonMnemonic = 0;
1447:      this.cancelButtonText = "Cancel";
1448:      this.cancelButtonToolTipText = "Abort file chooser dialog";
1449:      
1450:      this.directoryOpenButtonMnemonic = 0;
1451:      this.directoryOpenButtonText = "Open";
1452:      this.directoryOpenButtonToolTipText = "Open selected directory";
1453:      
1454:      this.helpButtonMnemonic = 0;
1455:      this.helpButtonText = "Help";
1456:      this.helpButtonToolTipText = "Filechooser help";
1457:      
1458:      this.openButtonMnemonic = 0;
1459:      this.openButtonText = "Open";
1460:      this.openButtonToolTipText = "Open selected file";
1461:      
1462:      this.saveButtonMnemonic = 0;
1463:      this.saveButtonText = "Save";
1464:      this.saveButtonToolTipText = "Save selected file";
1465:      
1466:      this.updateButtonMnemonic = 0;
1467:      this.updateButtonText = "Update";
1468:      this.updateButtonToolTipText = "Update directory listing";   
1469:   }
1470:   
1471:   /**
1472:    * Installs the listeners required.
1473:    * 
1474:    * @param fc  the file chooser.
1475:    */
1476:   protected void installListeners(JFileChooser fc)
1477:   {
1478:     directoryComboBox.setAction(new DirectoryComboBoxAction());
1479:     fc.addPropertyChangeListener(filterModel);
1480:     listSelList = createListSelectionListener(fc);
1481:     doubleClickList = this.createDoubleClickListener(fc, fileList);
1482:     singleClickList = new SingleClickListener(fileList);
1483:     fileList.addListSelectionListener(listSelList);
1484:     fileList.addMouseListener(doubleClickList);
1485:     fileList.addMouseListener(singleClickList);
1486:     super.installListeners(fc);
1487:   }
1488:   
1489:   protected void uninstallListeners(JFileChooser fc) 
1490:   {
1491:     super.uninstallListeners(fc);
1492:     fc.removePropertyChangeListener(filterModel);
1493:     directoryComboBox.setAction(null);
1494:     fileList.removeListSelectionListener(listSelList);
1495:     fileList.removeMouseListener(doubleClickList);
1496:     fileList.removeMouseListener(singleClickList);
1497:     
1498:     if (fileTable != null)
1499:       fileTable.removeMouseListener(tableClickList);
1500:   }
1501:   
1502:   protected ActionMap getActionMap()
1503:   {
1504:     if (actionMap == null)
1505:       actionMap = createActionMap();
1506:     return actionMap;
1507:   }
1508:   
1509:   /**
1510:    * Creates and returns an action map.
1511:    * 
1512:    * @return The action map.
1513:    */
1514:   protected ActionMap createActionMap()
1515:   {
1516:     ActionMap map = new ActionMap();
1517:     map.put("approveSelection", getApproveSelectionAction());
1518:     map.put("cancelSelection", getCancelSelectionAction());
1519:     map.put("Go Up", getChangeToParentDirectoryAction());
1520:     return map;
1521:   }
1522: 
1523:   /**
1524:    * Creates a panel containing a list of files.
1525:    * 
1526:    * @param fc  the file chooser.
1527:    * 
1528:    * @return A panel.
1529:    */
1530:   protected JPanel createList(JFileChooser fc)
1531:   {
1532:     if (fileList == null)
1533:       {
1534:         fileListPanel = new JPanel(new BorderLayout());
1535:         fileList = new JList(getModel());
1536:         scrollPane = new JScrollPane(fileList);
1537:         fileList.setLayoutOrientation(JList.VERTICAL_WRAP);
1538:         fileList.setCellRenderer(new FileRenderer());
1539:       }
1540:     else
1541:       {
1542:         fileList.setModel(getModel());
1543:         fileListPanel.removeAll();
1544:         scrollPane.getViewport().setView(fileList);
1545:       }
1546:     fileListPanel.add(scrollPane);
1547:     // This size was determined using BeanShell and dumping the JFileChooser
1548:     // component hierarchy. Sun has an internal FilePane class in there, but
1549:     // that probably doesn't matter atm.
1550:     fileListPanel.setPreferredSize(new Dimension(405, 135));
1551:     return fileListPanel;
1552:   }
1553:   
1554:   /**
1555:    * Creates a panel containing a table within a scroll pane.
1556:    * 
1557:    * @param fc  the file chooser.
1558:    * 
1559:    * @return The details view.
1560:    */
1561:   protected JPanel createDetailsView(JFileChooser fc)
1562:   {
1563:     fileTablePanel = new JPanel(new BorderLayout());
1564:     
1565:     Object[] cols = new Object[] {"Name", "Size", "Modified"};
1566:     Object[][] rows = new Object[fileList.getModel().getSize()][3];
1567:     
1568:     fileTable = new JTable(new DefaultTableModel(rows, cols));
1569:     
1570:     if (fc.isMultiSelectionEnabled())
1571:       fileTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
1572:     else
1573:       fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1574:     
1575:     fileTable.setShowGrid(false);
1576:     fileTable.setColumnSelectionAllowed(false);
1577:     fileTable.setDefaultRenderer(Object.class, new TableFileRenderer());
1578: 
1579:     tableClickList = new TableClickListener(fileTable, fc);
1580:     fileTable.addMouseListener(tableClickList);
1581:     
1582:     return updateTable();  
1583:   }
1584:   
1585:   /**
1586:    * Sets the values in the table, and puts it in the panel.
1587:    * 
1588:    * @return the panel containing the table.
1589:    */
1590:   JPanel updateTable()
1591:   {
1592:     DefaultTableModel mod = (DefaultTableModel) fileTable.getModel();
1593:     ListModel lm = fileList.getModel();
1594:     DateFormat dt = DateFormat.getDateTimeInstance(DateFormat.SHORT,
1595:                                                    DateFormat.SHORT);
1596:     File curr = null;
1597:     int size = lm.getSize();
1598:     int rc = mod.getRowCount();
1599: 
1600:     // If there are not enough rows
1601:     for (int x = rc; x < size; x++)
1602:       mod.addRow(new Object[3]);
1603: 
1604:     for (int i = 0; i < size; i++)
1605:       {
1606:         curr = (File) lm.getElementAt(i);
1607:         fileTable.setValueAt(curr.getName(), i, 0);
1608:         fileTable.setValueAt(formatSize(curr.length()), i, 1);
1609:         fileTable.setValueAt(dt.format(new Date(curr.lastModified())), i, 2);
1610:       }
1611: 
1612:     // If there are too many rows
1613:     while (rc > size)
1614:       mod.removeRow(--rc);
1615: 
1616:     scrollPane.getViewport().setView(fileTable);
1617:     scrollPane.setColumnHeaderView(fileTable.getTableHeader());
1618: 
1619:     fileTablePanel.removeAll();
1620:     fileTablePanel.add(scrollPane);
1621: 
1622:     return fileTablePanel;
1623:   }
1624:   
1625:   /**
1626:    * Formats bytes into the appropriate size.
1627:    * 
1628:    * @param bytes the number of bytes to convert
1629:    * @return a string representation of the size
1630:    */
1631:   private String formatSize(long bytes)
1632:   {
1633:     NumberFormat nf = NumberFormat.getNumberInstance();
1634:     long mb = (long) Math.pow(2, 20);
1635:     long kb = (long) Math.pow(2, 10);
1636:     long gb = (long) Math.pow(2, 30);
1637:     double size = 0;
1638:     String id = "";
1639:     
1640:     if ((bytes / gb) >= 1)
1641:       {
1642:         size = (double) bytes / (double) gb;
1643:         id = "GB";
1644:       }
1645:     else if ((bytes / mb) >= 1)
1646:       {
1647:         size = (double) bytes / (double) mb;
1648:         id = "MB";
1649:       }
1650:     else if ((bytes / kb) >= 1)
1651:       {
1652:         size = (double) bytes / (double) kb;
1653:         id = "KB";
1654:       }
1655:     else
1656:       {
1657:         size = bytes;
1658:         id = "Bytes";
1659:       }
1660:     
1661:     return nf.format(size) + " " + id;
1662:   }
1663:   /**
1664:    * Creates a listener that monitors selections in the directory/file list
1665:    * and keeps the {@link JFileChooser} component up to date.
1666:    * 
1667:    * @param fc  the file chooser.
1668:    * 
1669:    * @return The listener.
1670:    * 
1671:    * @see #installListeners(JFileChooser)
1672:    */
1673:   public ListSelectionListener createListSelectionListener(JFileChooser fc)
1674:   {
1675:     return new MetalFileChooserSelectionListener();
1676:   }
1677:   
1678:   /**
1679:    * Returns the preferred size for the file chooser component.
1680:    * 
1681:    * @return The preferred size.
1682:    */
1683:   public Dimension getPreferredSize(JComponent c)
1684:   {
1685:     Dimension tp = topPanel.getPreferredSize();
1686:     Dimension bp = bottomPanel.getPreferredSize();
1687:     Dimension fl = fileListPanel.getPreferredSize();
1688:     return new Dimension(fl.width, tp.height + bp.height + fl.height);
1689:   }
1690: 
1691:   /**
1692:    * Returns the minimum size for the file chooser component.
1693:    * 
1694:    * @return The minimum size.
1695:    */
1696:   public Dimension getMinimumSize(JComponent c)
1697:   {
1698:     Dimension tp = topPanel.getMinimumSize();
1699:     Dimension bp = bottomPanel.getMinimumSize();
1700:     Dimension fl = fileListPanel.getMinimumSize();
1701:     return new Dimension(fl.width, tp.height + bp.height + fl.height);
1702:   }
1703:   
1704:   /**
1705:    * Returns the maximum size for the file chooser component.
1706:    * 
1707:    * @return The maximum size.
1708:    */
1709:   public Dimension getMaximumSize(JComponent c)
1710:   {
1711:     return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1712:   }
1713:   
1714:   /**
1715:    * Creates a property change listener that monitors the {@link JFileChooser}
1716:    * for property change events and updates the component display accordingly.
1717:    * 
1718:    * @param fc  the file chooser.
1719:    * 
1720:    * @return The property change listener.
1721:    * 
1722:    * @see #installListeners(JFileChooser)
1723:    */
1724:   public PropertyChangeListener createPropertyChangeListener(JFileChooser fc)
1725:   {
1726:     return new MetalFileChooserPropertyChangeListener();
1727:   }
1728: 
1729:   /**
1730:    * Creates and returns a new instance of {@link DirectoryComboBoxModel}.
1731:    * 
1732:    * @return A new instance of {@link DirectoryComboBoxModel}.
1733:    */
1734:   protected MetalFileChooserUI.DirectoryComboBoxModel 
1735:       createDirectoryComboBoxModel(JFileChooser fc)
1736:   {
1737:     return new DirectoryComboBoxModel();
1738:   }
1739: 
1740:   /**
1741:    * Creates a new instance of the renderer used in the directory
1742:    * combo box.
1743:    * 
1744:    * @param fc  the file chooser.
1745:    * 
1746:    * @return The renderer.
1747:    */
1748:   protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(
1749:           JFileChooser fc)
1750:   {
1751:     return new DirectoryComboBoxRenderer(fc);
1752:   }
1753: 
1754:   /**
1755:    * Creates and returns a new instance of {@link FilterComboBoxModel}.
1756:    * 
1757:    * @return A new instance of {@link FilterComboBoxModel}.
1758:    */
1759:   protected FilterComboBoxModel createFilterComboBoxModel()
1760:   {
1761:     return new FilterComboBoxModel();  
1762:   }
1763: 
1764:   /**
1765:    * Creates and returns a new instance of {@link FilterComboBoxRenderer}.
1766:    * 
1767:    * @return A new instance of {@link FilterComboBoxRenderer}.
1768:    */
1769:   protected MetalFileChooserUI.FilterComboBoxRenderer 
1770:       createFilterComboBoxRenderer()
1771:   {
1772:     return new FilterComboBoxRenderer(); 
1773:   }
1774: 
1775:   /**
1776:    * Adds the control buttons ('up', 'home' etc.) to the panel.
1777:    */
1778:   protected void addControlButtons()
1779:   {
1780:     JButton upButton = new JButton(getChangeToParentDirectoryAction());
1781:     upButton.setText(null);
1782:     upButton.setIcon(this.upFolderIcon);
1783:     upButton.setMargin(new Insets(0, 0, 0, 0));
1784:     controls.add(upButton);
1785:     
1786:     JButton homeButton = new JButton(getGoHomeAction());
1787:     homeButton.setText(null);
1788:     homeButton.setIcon(this.homeFolderIcon);
1789:     homeButton.setMargin(new Insets(0, 0, 0, 0));
1790:     controls.add(homeButton);
1791:     
1792:     JButton newFolderButton = new JButton(getNewFolderAction());
1793:     newFolderButton.setText(null);
1794:     newFolderButton.setIcon(this.newFolderIcon);
1795:     newFolderButton.setMargin(new Insets(0, 0, 0, 0));
1796:     controls.add(newFolderButton);
1797:     
1798:     JToggleButton listButton = new JToggleButton(this.listViewIcon);
1799:     listButton.setMargin(new Insets(0, 0, 0, 0));
1800:     listButton.addActionListener(new ListViewActionListener());
1801:     listButton.setSelected(true);
1802:     listView = true; 
1803:     controls.add(listButton);
1804:     
1805:     JToggleButton detailButton = new JToggleButton(this.detailsViewIcon);
1806:     detailButton.setMargin(new Insets(0, 0, 0, 0));
1807:     detailButton.addActionListener(new DetailViewActionListener());
1808:     detailButton.setSelected(false);
1809:     controls.add(detailButton);
1810: 
1811:     ButtonGroup buttonGroup = new ButtonGroup();
1812:     buttonGroup.add(listButton);
1813:     buttonGroup.add(detailButton);
1814:   }
1815:   
1816:   /**
1817:    * Removes all the buttons from the control panel.
1818:    */
1819:   protected void removeControlButtons()
1820:   {
1821:     controls.removeAll();
1822:     controls.revalidate();
1823:     controls.repaint();
1824:   }
1825:   
1826:   /**
1827:    * Updates the current directory.
1828:    * 
1829:    * @param fc  the file chooser to update.
1830:    */
1831:   public void rescanCurrentDirectory(JFileChooser fc)
1832:   {
1833:     directoryModel.setSelectedItem(fc.getCurrentDirectory());
1834:     getModel().validateFileCache();
1835:     if (!listView)
1836:         updateTable();
1837:     else
1838:       createList(fc);
1839:   }
1840:   
1841:   /**
1842:    * Returns the file name in the text field.
1843:    * 
1844:    * @return The file name.
1845:    */
1846:   public String getFileName()
1847:   {
1848:     String result = null;
1849:     if (fileTextField != null) 
1850:       result = fileTextField.getText();
1851:     return result;
1852:   }
1853:   
1854:   /**
1855:    * Sets the file name in the text field.
1856:    * 
1857:    * @param filename  the file name.
1858:    */
1859:   public void setFileName(String filename)
1860:   {
1861:     fileTextField.setText(filename);
1862:   }
1863:   
1864:   /**
1865:    * DOCUMENT ME!!
1866:    * 
1867:    * @param e - DOCUMENT ME!
1868:    */
1869:   public void valueChanged(ListSelectionEvent e)
1870:   {
1871:     // FIXME: Not sure what we should be doing here, if anything.
1872:   }
1873:   
1874:   /**
1875:    * Returns the approve button.
1876:    * 
1877:    * @return The approve button.
1878:    */
1879:   protected JButton getApproveButton(JFileChooser fc)
1880:   {
1881:     return approveButton;
1882:   }
1883: 
1884:   /**
1885:    * A layout manager that is used to arrange the subcomponents of the
1886:    * {@link JFileChooser}.
1887:    */
1888:   class VerticalMidLayout implements LayoutManager
1889:   {
1890:     /**
1891:      * Performs the layout.
1892:      * 
1893:      * @param parent  the container.
1894:      */
1895:     public void layoutContainer(Container parent) 
1896:     {
1897:       int count = parent.getComponentCount();
1898:       if (count > 0)
1899:         {
1900:           Insets insets = parent.getInsets();
1901:           Component c = parent.getComponent(0);
1902:           Dimension prefSize = c.getPreferredSize();
1903:           int h = parent.getHeight() - insets.top - insets.bottom;
1904:           int adj = Math.max(0, (h - prefSize.height) / 2);
1905:           c.setBounds(insets.left, insets.top + adj, parent.getWidth() 
1906:               - insets.left - insets.right, 
1907:               (int) Math.min(prefSize.getHeight(), h));
1908:         }
1909:     }
1910:     
1911:     /**
1912:      * Returns the minimum layout size.
1913:      * 
1914:      * @param parent  the container.
1915:      * 
1916:      * @return The minimum layout size.
1917:      */
1918:     public Dimension minimumLayoutSize(Container parent) 
1919:     {
1920:       return preferredLayoutSize(parent);
1921:     }
1922:     
1923:     /**
1924:      * Returns the preferred layout size.
1925:      * 
1926:      * @param parent  the container.
1927:      * 
1928:      * @return The preferred layout size.
1929:      */
1930:     public Dimension preferredLayoutSize(Container parent) 
1931:     {
1932:       if (parent.getComponentCount() > 0)
1933:         {
1934:           return parent.getComponent(0).getPreferredSize();
1935:         }
1936:       else return null;
1937:     }
1938:     
1939:     /**
1940:      * This layout manager does not need to track components, so this 
1941:      * method does nothing.
1942:      * 
1943:      * @param name  the name the component is associated with.
1944:      * @param component  the component.
1945:      */
1946:     public void addLayoutComponent(String name, Component component) 
1947:     {
1948:       // do nothing
1949:     }
1950:     
1951:     /**
1952:      * This layout manager does not need to track components, so this 
1953:      * method does nothing.
1954:      * 
1955:      * @param component  the component.
1956:      */
1957:     public void removeLayoutComponent(Component component) 
1958:     {
1959:       // do nothing
1960:     }
1961:   }
1962: 
1963:   /**
1964:    * A layout manager that is used to arrange buttons for the
1965:    * {@link JFileChooser}.
1966:    */
1967:   class ButtonLayout implements LayoutManager
1968:   {
1969:     static final int GAP = 4;
1970:       
1971:     /**
1972:      * Performs the layout.
1973:      * 
1974:      * @param parent  the container.
1975:      */
1976:     public void layoutContainer(Container parent) 
1977:     {
1978:       int count = parent.getComponentCount();
1979:       if (count > 0)
1980:         {
1981:           // first find the widest button
1982:           int maxW = 0;
1983:           for (int i = 0; i < count; i++)
1984:             {
1985:               Component c = parent.getComponent(i);
1986:               Dimension prefSize = c.getPreferredSize();
1987:               maxW = Math.max(prefSize.width, maxW);
1988:             }
1989:   
1990:           // then position the buttons
1991:           Insets insets = parent.getInsets();
1992:           int availableH = parent.getHeight() - insets.top - insets.bottom;
1993:           int currentX = parent.getWidth() - insets.right;
1994:           for (int i = count - 1; i >= 0; i--)
1995:             {
1996:               Component c = parent.getComponent(i);
1997:               Dimension prefSize = c.getPreferredSize();      
1998:               int adj = Math.max(0, (availableH - prefSize.height) / 2);
1999:               currentX = currentX - prefSize.width;
2000:               c.setBounds(currentX, insets.top + adj, prefSize.width, 
2001:                   (int) Math.min(prefSize.getHeight(), availableH));
2002:               currentX = currentX - GAP;
2003:             }
2004:         }
2005:     }
2006:     
2007:     /**
2008:      * Returns the minimum layout size.
2009:      * 
2010:      * @param parent  the container.
2011:      * 
2012:      * @return The minimum layout size.
2013:      */
2014:     public Dimension minimumLayoutSize(Container parent) 
2015:     {
2016:       return preferredLayoutSize(parent);
2017:     }
2018:     
2019:     /**
2020:      * Returns the preferred layout size.
2021:      * 
2022:      * @param parent  the container.
2023:      * 
2024:      * @return The preferred layout size.
2025:      */
2026:     public Dimension preferredLayoutSize(Container parent) 
2027:     {
2028:       Insets insets = parent.getInsets();
2029:       int maxW = 0;
2030:       int maxH = 0;
2031:       int count = parent.getComponentCount();
2032:       if (count > 0) 
2033:         {
2034:           for (int i = 0; i < count; i++)
2035:             {
2036:               Component c = parent.getComponent(i);
2037:               Dimension d = c.getPreferredSize();
2038:               maxW = Math.max(d.width, maxW);
2039:               maxH = Math.max(d.height, maxH);
2040:             }
2041:         }
2042:       return new Dimension(maxW * count + GAP * (count - 1) + insets.left 
2043:               + insets.right, maxH + insets.top + insets.bottom);
2044:     }
2045:     
2046:     /**
2047:      * This layout manager does not need to track components, so this 
2048:      * method does nothing.
2049:      * 
2050:      * @param name  the name the component is associated with.
2051:      * @param component  the component.
2052:      */
2053:     public void addLayoutComponent(String name, Component component) 
2054:     {
2055:       // do nothing
2056:     }
2057:     
2058:     /**
2059:      * This layout manager does not need to track components, so this 
2060:      * method does nothing.
2061:      * 
2062:      * @param component  the component.
2063:      */
2064:     public void removeLayoutComponent(Component component) 
2065:     {
2066:       // do nothing
2067:     }
2068:   }
2069: 
2070: }