001 /* MetalFileChooserUI.java --
002 Copyright (C) 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing.plaf.metal;
040
041 import java.awt.BorderLayout;
042 import java.awt.Component;
043 import java.awt.Container;
044 import java.awt.Dimension;
045 import java.awt.Graphics;
046 import java.awt.GridLayout;
047 import java.awt.Insets;
048 import java.awt.LayoutManager;
049 import java.awt.Rectangle;
050 import java.awt.Window;
051 import java.awt.event.ActionEvent;
052 import java.awt.event.ActionListener;
053 import java.awt.event.MouseAdapter;
054 import java.awt.event.MouseEvent;
055 import java.awt.event.MouseListener;
056 import java.beans.PropertyChangeEvent;
057 import java.beans.PropertyChangeListener;
058 import java.io.File;
059 import java.text.DateFormat;
060 import java.text.NumberFormat;
061 import java.util.Date;
062 import java.util.List;
063
064 import javax.swing.AbstractAction;
065 import javax.swing.AbstractListModel;
066 import javax.swing.ActionMap;
067 import javax.swing.BorderFactory;
068 import javax.swing.ButtonGroup;
069 import javax.swing.ComboBoxModel;
070 import javax.swing.DefaultListCellRenderer;
071 import javax.swing.Icon;
072 import javax.swing.JButton;
073 import javax.swing.JComboBox;
074 import javax.swing.JComponent;
075 import javax.swing.JDialog;
076 import javax.swing.JFileChooser;
077 import javax.swing.JLabel;
078 import javax.swing.JList;
079 import javax.swing.JPanel;
080 import javax.swing.JScrollPane;
081 import javax.swing.JTable;
082 import javax.swing.JTextField;
083 import javax.swing.JToggleButton;
084 import javax.swing.ListModel;
085 import javax.swing.ListSelectionModel;
086 import javax.swing.SwingUtilities;
087 import javax.swing.UIManager;
088 import javax.swing.event.ListSelectionEvent;
089 import javax.swing.event.ListSelectionListener;
090 import javax.swing.filechooser.FileFilter;
091 import javax.swing.filechooser.FileSystemView;
092 import javax.swing.filechooser.FileView;
093 import javax.swing.plaf.ComponentUI;
094 import javax.swing.plaf.basic.BasicFileChooserUI;
095 import javax.swing.table.DefaultTableCellRenderer;
096 import javax.swing.table.DefaultTableModel;
097
098
099 /**
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 }