diff options
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/metal')
11 files changed, 2450 insertions, 82 deletions
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java index a43ee3cb04f..0006b78fee3 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java @@ -313,4 +313,40 @@ public class MetalComboBoxUI extends BasicComboBoxUI d.height + insetsH + 1); } + /** + * Configures the editor for this combo box. + */ + public void configureEditor() + { + ComboBoxEditor cbe = comboBox.getEditor(); + if (cbe != null) + { + cbe.getEditorComponent().setFont(comboBox.getFont()); + cbe.setItem(comboBox.getSelectedItem()); + cbe.addActionListener(comboBox); + } + } + + /** + * Unconfigures the editor for this combo box. + */ + public void unconfigureEditor() + { + ComboBoxEditor cbe = comboBox.getEditor(); + if (cbe != null) + { + cbe.getEditorComponent().setFont(null); + cbe.setItem(null); + cbe.removeActionListener(comboBox); + } + } + + /** + * Lays out the ComboBox + */ + public void layoutComboBox(Container parent, + MetalComboBoxUI.MetalComboBoxLayoutManager manager) + { + manager.layoutContainer(parent); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java index 3a2e1c13508..967c40d29ad 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -38,39 +38,421 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.BorderLayout; import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.awt.Rectangle; +import java.awt.Window; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.text.NumberFormat; + import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; + import java.io.File; -import java.util.List; import javax.swing.AbstractAction; import javax.swing.AbstractListModel; +import javax.swing.ActionMap; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; import javax.swing.ComboBoxModel; import javax.swing.DefaultListCellRenderer; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComboBox; import javax.swing.JComponent; +import javax.swing.JDialog; import javax.swing.JFileChooser; +import javax.swing.JLabel; import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.JViewport; +import javax.swing.ListModel; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; import javax.swing.UIManager; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileSystemView; import javax.swing.filechooser.FileView; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicFileChooserUI; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; + +import java.sql.Date; + +import java.text.DateFormat; + +import java.util.List; /** * A UI delegate for the {@link JFileChooser} component. This class is only * partially implemented and is not usable yet. */ -public class MetalFileChooserUI extends BasicFileChooserUI +public class MetalFileChooserUI + extends BasicFileChooserUI { + + /** + * A renderer for the files and directories in the file chooser table. + */ + class TableFileRenderer + extends DefaultTableCellRenderer + { + + /** + * Creates a new renderer. + */ + public TableFileRenderer() + { + super(); + } + + /** + * Returns a component that can render the specified value. + * + * @param table the table + * @param value the string value of the cell + * @param isSelected is the item selected? + * @param hasFocus does the item have the focus? + * @param row the row + * @param column the column + * + * @return The renderer. + */ + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) + { + if (column == 0) + { + FileView v = getFileView(getFileChooser()); + ListModel lm = fileList.getModel(); + if (row < lm.getSize()) + setIcon(v.getIcon((File) lm.getElementAt(row))); + } + else + setIcon(null); + + setText(value.toString()); + setOpaque(true); + setEnabled(table.isEnabled()); + setFont(fileList.getFont()); + + if (startEditing && column == 0 || !isSelected) + { + setBackground(table.getBackground()); + setForeground(table.getForeground()); + } + else + { + setBackground(table.getSelectionBackground()); + setForeground(table.getSelectionForeground()); + } + + if (hasFocus) + setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); + else + setBorder(noFocusBorder); + + return this; + } + } + + /** + * ActionListener for the list view. + */ + class ListViewActionListener implements ActionListener + { + + /** + * This method is invoked when an action occurs. + * + * @param e - + * the <code>ActionEvent</code> that occurred + */ + public void actionPerformed(ActionEvent e) + { + if (!listView) + { + int[] index = fileTable.getSelectedRows(); + listView = true; + JFileChooser fc = getFileChooser(); + fc.remove(fileTablePanel); + createList(fc); + + fileList.getSelectionModel().clearSelection(); + if (index.length > 0) + for (int i = 0; i < index.length; i++) + fileList.getSelectionModel().addSelectionInterval(index[i], index[i]); + + fc.add(fileListPanel, BorderLayout.CENTER); + fc.revalidate(); + fc.repaint(); + } + } + } + + /** + * ActionListener for the details view. + */ + class DetailViewActionListener implements ActionListener + { + + /** + * This method is invoked when an action occurs. + * + * @param e - + * the <code>ActionEvent</code> that occurred + */ + public void actionPerformed(ActionEvent e) + { + if (listView) + { + int[] index = fileList.getSelectedIndices(); + JFileChooser fc = getFileChooser(); + listView = false; + fc.remove(fileListPanel); + + if (fileTable == null) + createDetailsView(fc); + else + updateTable(); + + fileTable.getSelectionModel().clearSelection(); + if (index.length > 0) + { + for (int i = 0; i < index.length; i++) + fileTable.getSelectionModel().addSelectionInterval(index[i], index[i]); + } + + fc.add(fileTablePanel, BorderLayout.CENTER); + fc.revalidate(); + fc.repaint(); + } + } + } + + /** + * A property change listener. + */ + class MetalFileChooserPropertyChangeListener + implements PropertyChangeListener + { + /** + * Default constructor. + */ + public MetalFileChooserPropertyChangeListener() + { + } + + /** + * Handles a property change event. + * + * @param e the event. + */ + public void propertyChange(PropertyChangeEvent e) + { + JFileChooser filechooser = getFileChooser(); + + String n = e.getPropertyName(); + if (n.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) + { + int mode = -1; + if (filechooser.isMultiSelectionEnabled()) + mode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION; + else + mode = ListSelectionModel.SINGLE_SELECTION; + + if (listView) + fileList.setSelectionMode(mode); + else + fileTable.setSelectionMode(mode); + } + else if (n.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) + { + File file = filechooser.getSelectedFile(); + + if (file != null + && filechooser.getDialogType() == JFileChooser.SAVE_DIALOG) + { + if (file.isDirectory() && filechooser.isTraversable(file)) + { + directoryLabel = look; + dirLabel.setText(directoryLabel); + filechooser.setApproveButtonText(openButtonText); + filechooser.setApproveButtonToolTipText(openButtonToolTipText); + } + else if (file.isFile()) + { + directoryLabel = save; + dirLabel.setText(directoryLabel); + filechooser.setApproveButtonText(saveButtonText); + filechooser.setApproveButtonToolTipText(saveButtonToolTipText); + } + } + + if (file == null) + setFileName(null); + else + setFileName(file.getName()); + int index = -1; + index = getModel().indexOf(file); + if (index >= 0) + { + if (listView) + { + fileList.setSelectedIndex(index); + fileList.ensureIndexIsVisible(index); + fileList.revalidate(); + fileList.repaint(); + } + else + { + fileTable.getSelectionModel().addSelectionInterval(index, index); + fileTable.scrollRectToVisible(fileTable.getCellRect(index, 0, true)); + fileTable.revalidate(); + fileTable.repaint(); + } + } + } + + else if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) + { + if (listView) + { + fileList.clearSelection(); + fileList.revalidate(); + fileList.repaint(); + } + else + { + fileTable.clearSelection(); + fileTable.revalidate(); + fileTable.repaint(); + } + + setDirectorySelected(false); + File currentDirectory = filechooser.getCurrentDirectory(); + setDirectory(currentDirectory); + boolean hasParent = (currentDirectory.getParentFile() != null); + getChangeToParentDirectoryAction().setEnabled(hasParent); + } + + else if (n.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) + { + filterModel.propertyChange(e); + } + else if (n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) + { + filterModel.propertyChange(e); + } + else if (n.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) + || n.equals(JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY)) + { + Window owner = SwingUtilities.windowForComponent(filechooser); + if (owner instanceof JDialog) + ((JDialog) owner).setTitle(getDialogTitle(filechooser)); + approveButton.setText(getApproveButtonText(filechooser)); + approveButton.setToolTipText( + getApproveButtonToolTipText(filechooser)); + approveButton.setMnemonic(getApproveButtonMnemonic(filechooser)); + } + + else if (n.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY)) + approveButton.setText(getApproveButtonText(filechooser)); + + else if (n.equals( + JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) + approveButton.setToolTipText(getApproveButtonToolTipText(filechooser)); + + else if (n.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) + approveButton.setMnemonic(getApproveButtonMnemonic(filechooser)); + + else if (n.equals( + JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) + { + if (filechooser.getControlButtonsAreShown()) + { + topPanel.add(controls, BorderLayout.EAST); + } + else + topPanel.remove(controls); + topPanel.revalidate(); + topPanel.repaint(); + topPanel.doLayout(); + } + + else if (n.equals( + JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY)) + { + if (filechooser.isAcceptAllFileFilterUsed()) + filechooser.addChoosableFileFilter( + getAcceptAllFileFilter(filechooser)); + else + filechooser.removeChoosableFileFilter( + getAcceptAllFileFilter(filechooser)); + } + + else if (n.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) + { + JComponent old = (JComponent) e.getOldValue(); + if (old != null) + getAccessoryPanel().remove(old); + JComponent newval = (JComponent) e.getNewValue(); + if (newval != null) + getAccessoryPanel().add(newval); + } + + if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) + || n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY) + || n.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY)) + { + // Remove editing component + if (fileTable != null) + fileTable.removeAll(); + if (fileList != null) + fileList.removeAll(); + startEditing = false; + + // Set text on button back to original. + if (filechooser.getDialogType() == JFileChooser.SAVE_DIALOG) + { + directoryLabel = save; + dirLabel.setText(directoryLabel); + filechooser.setApproveButtonText(saveButtonText); + filechooser.setApproveButtonToolTipText(saveButtonToolTipText); + } + + rescanCurrentDirectory(filechooser); + } + + filechooser.revalidate(); + filechooser.repaint(); + } + }; + /** * A combo box model containing the selected directory and all its parent * directories. */ - protected class DirectoryComboBoxModel extends AbstractListModel + protected class DirectoryComboBoxModel + extends AbstractListModel implements ComboBoxModel { /** Storage for the items in the model. */ @@ -161,7 +543,8 @@ public class MetalFileChooserUI extends BasicFileChooserUI /** * Handles changes to the selection in the directory combo box. */ - protected class DirectoryComboBoxAction extends AbstractAction + protected class DirectoryComboBoxAction + extends AbstractAction { /** * Creates a new action. @@ -184,9 +567,60 @@ public class MetalFileChooserUI extends BasicFileChooserUI } /** + * A renderer for the items in the directory combo box. + */ + class DirectoryComboBoxRenderer + extends DefaultListCellRenderer + { + /** + * Creates a new renderer. + */ + public DirectoryComboBoxRenderer(JFileChooser fc) + { + } + + /** + * Returns a component that can be used to paint the given value within + * the list. + * + * @param list the list. + * @param value the value (a {@link File}). + * @param index the item index. + * @param isSelected is the item selected? + * @param cellHasFocus does the list cell have focus? + * + * @return The list cell renderer. + */ + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) + { + FileView fileView = getFileView(getFileChooser()); + File file = (File) value; + setIcon(fileView.getIcon(file)); + setText(fileView.getName(file)); + + if (isSelected) + { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } + else + { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + setEnabled(list.isEnabled()); + setFont(list.getFont()); + return this; + } + } + + /** * A renderer for the files and directories in the file chooser. */ - protected class FileRenderer extends DefaultListCellRenderer + protected class FileRenderer + extends DefaultListCellRenderer { /** @@ -213,8 +647,17 @@ public class MetalFileChooserUI extends BasicFileChooserUI { FileView v = getFileView(getFileChooser()); File f = (File) value; - setText(v.getName(f)); - setIcon(v.getIcon(f)); + if (f != null) + { + setText(v.getName(f)); + setIcon(v.getIcon(f)); + } + else + { + setText(""); + setIcon(null); + } + setOpaque(true); if (isSelected) { setBackground(list.getSelectionBackground()); @@ -249,7 +692,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI protected FileFilter[] filters; /** The index of the selected file filter. */ - private int selectedIndex; + private Object selected; /** * Creates a new model. @@ -258,7 +701,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI { filters = new FileFilter[1]; filters[0] = getAcceptAllFileFilter(getFileChooser()); - selectedIndex = 0; + selected = filters[0]; } /** @@ -270,11 +713,11 @@ public class MetalFileChooserUI extends BasicFileChooserUI { if (e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) { - selectedIndex = -1; - FileFilter selected = (FileFilter) e.getNewValue(); - for (int i = 0; i < filters.length; i++) - if (filters[i].equals(selected)) - selectedIndex = i; + JFileChooser fc = getFileChooser(); + FileFilter[] choosableFilters = fc.getChoosableFileFilters(); + filters = choosableFilters; + fireContentsChanged(this, 0, filters.length); + selected = e.getNewValue(); fireContentsChanged(this, -1, -1); } else if (e.getPropertyName().equals( @@ -291,13 +734,15 @@ public class MetalFileChooserUI extends BasicFileChooserUI /** * Sets the selected filter. * - * @param filter the filter. + * @param filter the filter (<code>null</code> ignored). */ public void setSelectedItem(Object filter) { - // change the filter in the file chooser and let the property change - // event trigger the change to the selected item - getFileChooser().setFileFilter((FileFilter) filter); + if (filter != null) + { + selected = filter; + fireContentsChanged(this, -1, -1); + } } /** @@ -307,9 +752,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI */ public Object getSelectedItem() { - if (selectedIndex >= 0) - return filters[selectedIndex]; - return null; + return selected; } /** @@ -339,7 +782,8 @@ public class MetalFileChooserUI extends BasicFileChooserUI /** * A renderer for the items in the file filter combo box. */ - public class FilterComboBoxRenderer extends DefaultListCellRenderer + public class FilterComboBoxRenderer + extends DefaultListCellRenderer { /** * Creates a new renderer. @@ -359,20 +803,488 @@ public class MetalFileChooserUI extends BasicFileChooserUI * @param isSelected is the item selected? * @param cellHasFocus does the list cell have focus? * - * @return A component. + * @return This component as the renderer. */ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); FileFilter filter = (FileFilter) value; - return super.getListCellRendererComponent(list, filter.getDescription(), - index, isSelected, cellHasFocus); + setText(filter.getDescription()); + return this; + } + } + + /** + * A listener for selection events in the file list. + * + * @see #createListSelectionListener(JFileChooser) + */ + class MetalFileChooserSelectionListener + implements ListSelectionListener + { + /** + * Creates a new <code>SelectionListener</code> object. + */ + protected MetalFileChooserSelectionListener() + { + // Do nothing here. + } + + /** + * Makes changes to different properties when + * a value has changed in the filechooser's selection. + * + * @param e - the list selection event that occured. + */ + public void valueChanged(ListSelectionEvent e) + { + File f = (File) fileList.getSelectedValue(); + if (f == null) + return; + JFileChooser filechooser = getFileChooser(); + if (! filechooser.isTraversable(f)) + filechooser.setSelectedFile(f); + else + filechooser.setSelectedFile(null); + } + } + + /** + * A mouse listener for the {@link JFileChooser}. + * This listener is used for editing filenames. + */ + protected class SingleClickListener + extends MouseAdapter + { + + /** Stores instance of the list */ + JList list; + + /** + * Stores the current file that is being edited. + * It is null if nothing is currently being edited. + */ + File editFile; + + /** The current file chooser. */ + JFileChooser fc; + + /** The last file selected. */ + Object lastSelected; + + /** The textfield used for editing. */ + JTextField editField; + + /** + * Creates a new listener. + * + * @param list the directory/file list. + */ + public SingleClickListener(JList list) + { + this.list = list; + editFile = null; + fc = getFileChooser(); + lastSelected = null; + startEditing = false; + } + + /** + * Receives notification of a mouse click event. + * + * @param e the event. + */ + public void mouseClicked(MouseEvent e) + { + if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) + { + int index = list.locationToIndex(e.getPoint()); + File[] sf = fc.getSelectedFiles(); + if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1)) + && index >= 0 && !startEditing && list.isSelectedIndex(index)) + { + Object tmp = list.getModel().getElementAt(index); + if (lastSelected != null && lastSelected.equals(tmp)) + editFile(index); + lastSelected = tmp; + } + else + completeEditing(); + } + else + completeEditing(); + } + + /** + * Sets up the text editor for the current file. + * + * @param index - + * the current index of the item in the list to be edited. + */ + void editFile(int index) + { + Rectangle bounds = list.getCellBounds(index, index); + list.scrollRectToVisible(bounds); + editFile = (File) list.getModel().getElementAt(index); + if (editFile.canWrite()) + { + startEditing = true; + editField = new JTextField(editFile.getName()); + editField.addActionListener(new EditingActionListener()); + + Icon icon = getFileView(fc).getIcon(editFile); + if (icon != null) + { + int padding = icon.getIconWidth() + 4; + bounds.x += padding; + bounds.width -= padding; + } + editField.setBounds(bounds); + + list.add(editField); + + editField.requestFocus(); + editField.selectAll(); + } + else + completeEditing(); + list.repaint(); + } + + /** + * Completes the editing. + */ + void completeEditing() + { + if (editField != null && editFile != null) + { + String text = editField.getText(); + if (text != null && text != "" && !text.equals(fc.getName(editFile))) + if (editFile.renameTo + (fc.getFileSystemView().createFileObject + (fc.getCurrentDirectory(), text))) + rescanCurrentDirectory(fc); + list.remove(editField); + } + startEditing = false; + editFile = null; + lastSelected = null; + editField = null; + list.repaint(); + } + + /** + * ActionListener for the editing text field. + */ + class EditingActionListener implements ActionListener + { + + /** + * This method is invoked when an action occurs. + * + * @param e - + * the <code>ActionEvent</code> that occurred + */ + public void actionPerformed(ActionEvent e) + { + if (e.getActionCommand().equals("notify-field-accept")) + completeEditing(); + else if (editField != null) + { + list.remove(editField); + startEditing = false; + editFile = null; + lastSelected = null; + editField = null; + list.repaint(); + } + } } } + /** + * A mouse listener for the {@link JFileChooser}. + * This listener is used for the table + */ + private class TableClickListener extends MouseAdapter + { + + /** Stores instance of the table */ + JTable table; + + /** Stores instance of the file chooser */ + JFileChooser fc; + + /** The last selected file. */ + Object lastSelected = null; + + /** + * Stores the current file that is being edited. + * It is null if nothing is currently being edited. + */ + File editFile; + + /** The textfield used for editing. */ + JTextField editField; + + /** + * Creates a new listener. + * + * @param table + * the directory/file table + * @param fc + * the JFileChooser + */ + public TableClickListener(JTable table, JFileChooser fc) + { + this.table = table; + this.fc = fc; + lastSelected = fileList.getSelectedValue(); + setDirectorySelected(false); + startEditing = false; + editFile = null; + editField = null; + } + + /** + * Receives notification of a mouse click event. + * + * @param e + * the event. + */ + public void mouseClicked(MouseEvent e) + { + int row = table.getSelectedRow(); + Object selVal = fileList.getModel().getElementAt(row); + if (selVal == null) + return; + FileSystemView fsv = fc.getFileSystemView(); + if (e.getClickCount() == 1 && + selVal.equals(lastSelected) && + e.getButton() == MouseEvent.BUTTON1) + { + File[] sf = fc.getSelectedFiles(); + if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1)) + && !startEditing) + { + editFile = (File) selVal; + editFile(row); + } + } + else if (e.getClickCount() >= 2 && + selVal.equals(lastSelected)) + { + if (startEditing) + completeEditing(); + File f = fsv.createFileObject(lastSelected.toString()); + if (fc.isTraversable(f)) + { + fc.setCurrentDirectory(f); + fc.rescanCurrentDirectory(); + } + else + { + fc.setSelectedFile(f); + fc.approveSelection(); + closeDialog(); + } + } + else + { + if (startEditing) + completeEditing(); + String path = selVal.toString(); + File f = fsv.createFileObject(path); + fc.setSelectedFile(f); + if (fc.isTraversable(f)) + { + setDirectorySelected(true); + setDirectory(f); + } + else + { + setDirectorySelected(false); + setDirectory(null); + } + lastSelected = selVal; + if (f.isFile()) + setFileName(path.substring(path.lastIndexOf("/") + 1)); + else if (fc.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) + setFileName(path); + } + fileTable.repaint(); + } + + /** + * Sets up the text editor for the current file. + * + * @param row - + * the current row of the item in the list to be edited. + */ + void editFile(int row) + { + Rectangle bounds = table.getCellRect(row, 0, true); + table.scrollRectToVisible(bounds); + if (editFile.canWrite()) + { + startEditing = true; + editField = new JTextField(editFile.getName()); + editField.addActionListener(new EditingActionListener()); + + // Need to adjust y pos + bounds.y = row * table.getRowHeight(); + editField.setBounds(bounds); + + table.add(editField); + + editField.requestFocus(); + editField.selectAll(); + } + else + completeEditing(); + table.repaint(); + } + + /** + * Completes the editing. + */ + void completeEditing() + { + if (editField != null && editFile != null) + { + String text = editField.getText(); + if (text != null && text != "" && !text.equals(fc.getName(editFile))) + if (editFile.renameTo + (fc.getFileSystemView().createFileObject + (fc.getCurrentDirectory(), text))) + rescanCurrentDirectory(fc); + table.remove(editField); + } + startEditing = false; + editFile = null; + editField = null; + table.repaint(); + } + + /** + * ActionListener for the editing text field. + */ + class EditingActionListener implements ActionListener + { + + /** + * This method is invoked when an action occurs. + * + * @param e - + * the <code>ActionEvent</code> that occurred + */ + public void actionPerformed(ActionEvent e) + { + if (e.getActionCommand().equals("notify-field-accept")) + completeEditing(); + else if (editField != null) + { + table.remove(editField); + startEditing = false; + editFile = null; + editField = null; + table.repaint(); + } + } + } + + /** + * Closes the dialog. + */ + public void closeDialog() + { + Window owner = SwingUtilities.windowForComponent(fc); + if (owner instanceof JDialog) + ((JDialog) owner).dispose(); + } + } + + /** The text for a label describing the directory combo box. */ + private String directoryLabel; + + private JComboBox directoryComboBox; + /** The model for the directory combo box. */ DirectoryComboBoxModel directoryModel; + /** The text for a label describing the file text field. */ + private String fileLabel; + + /** The file name text field. */ + private JTextField fileTextField; + + /** The text for a label describing the filter combo box. */ + private String filterLabel; + + /** + * The top panel (contains the directory combo box and the control buttons). + */ + private JPanel topPanel; + + /** A panel containing the control buttons ('up', 'home' etc.). */ + private JPanel controls; + + /** + * The panel that contains the filename field and the filter combobox. + */ + private JPanel bottomPanel; + + /** + * The panel that contains the 'Open' (or 'Save') and 'Cancel' buttons. + */ + private JPanel buttonPanel; + + private JButton approveButton; + + /** The file list. */ + JList fileList; + + /** The file table. */ + JTable fileTable; + + /** The panel containing the file list. */ + JPanel fileListPanel; + + /** The panel containing the file table. */ + JPanel fileTablePanel; + + /** The filter combo box model. */ + private FilterComboBoxModel filterModel; + + /** The action map. */ + private ActionMap actionMap; + + /** True if currently in list view. */ + boolean listView; + + /** True if we can or have started editing a cell. */ + boolean startEditing; + + /** The scrollpane used for the table and list. */ + JScrollPane scrollPane; + + /** The text for the label when saving. */ + String save; + + /** The text for the label when opening a directory. */ + String look; + + /** The label for the file combo box. */ + JLabel dirLabel; + + /** Listeners. */ + ListSelectionListener listSelList; + MouseListener doubleClickList; + SingleClickListener singleClickList; + TableClickListener tableClickList; + /** * A factory method that returns a UI delegate for the specified * component. @@ -393,9 +1305,439 @@ public class MetalFileChooserUI extends BasicFileChooserUI public MetalFileChooserUI(JFileChooser filechooser) { super(filechooser); + bottomPanel = new JPanel(new GridLayout(3, 2)); + buttonPanel = new JPanel(); + } + + public void installUI(JComponent c) + { + super.installUI(c); + actionMap = createActionMap(); + } + + public void uninstallUI(JComponent c) + { + super.uninstallUI(c); + actionMap = null; + } + + /** + * Installs the sub-components of the file chooser. + * + * @param fc the file chooser component. + */ + public void installComponents(JFileChooser fc) + { + fc.setLayout(new BorderLayout()); + topPanel = new JPanel(new BorderLayout()); + dirLabel = new JLabel(directoryLabel); + topPanel.add(dirLabel, BorderLayout.WEST); + this.controls = new JPanel(); + addControlButtons(); + + JPanel dirPanel = new JPanel(new VerticalMidLayout()); + directoryModel = createDirectoryComboBoxModel(fc); + directoryComboBox = new JComboBox(directoryModel); + directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc)); + dirPanel.add(directoryComboBox); + topPanel.add(dirPanel); + topPanel.add(controls, BorderLayout.EAST); + topPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 0, 8)); + fc.add(topPanel, BorderLayout.NORTH); + + JPanel list = createList(fc); + list.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + fc.add(list, BorderLayout.CENTER); + + JPanel bottomPanel = getBottomPanel(); + filterModel = createFilterComboBoxModel(); + JComboBox fileFilterCombo = new JComboBox(filterModel); + fileFilterCombo.setRenderer(createFilterComboBoxRenderer()); + + fileTextField = new JTextField(); + JPanel fileNamePanel = new JPanel(new VerticalMidLayout()); + fileNamePanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 5)); + fileNamePanel.add(fileTextField); + JPanel row1 = new JPanel(new BorderLayout()); + row1.add(new JLabel(this.fileLabel), BorderLayout.WEST); + row1.add(fileNamePanel); + bottomPanel.add(row1); + + JPanel row2 = new JPanel(new BorderLayout()); + row2.add(new JLabel(this.filterLabel), BorderLayout.WEST); + row2.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); + row2.add(fileFilterCombo); + bottomPanel.add(row2); + JPanel buttonPanel = new JPanel(new ButtonLayout()); + + approveButton = new JButton(getApproveSelectionAction()); + approveButton.setText(getApproveButtonText(fc)); + approveButton.setToolTipText(getApproveButtonToolTipText(fc)); + approveButton.setMnemonic(getApproveButtonMnemonic(fc)); + buttonPanel.add(approveButton); + buttonPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0)); + + JButton cancelButton = new JButton(getCancelSelectionAction()); + cancelButton.setText(cancelButtonText); + cancelButton.setToolTipText(cancelButtonToolTipText); + cancelButton.setMnemonic(cancelButtonMnemonic); + buttonPanel.add(cancelButton); + bottomPanel.add(buttonPanel, BorderLayout.SOUTH); + bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8)); + fc.add(bottomPanel, BorderLayout.SOUTH); + + fc.add(getAccessoryPanel(), BorderLayout.EAST); + } + + /** + * Uninstalls the components added by + * {@link #installComponents(JFileChooser)}. + * + * @param fc the file chooser. + */ + public void uninstallComponents(JFileChooser fc) + { + fc.remove(bottomPanel); + bottomPanel = null; + fc.remove(fileListPanel); + fc.remove(fileTablePanel); + fileTablePanel = null; + fileListPanel = null; + fc.remove(topPanel); + topPanel = null; + + directoryModel = null; + fileTextField = null; + directoryComboBox = null; + } + + /** + * Returns the panel that contains the 'Open' (or 'Save') and 'Cancel' + * buttons. + * + * @return The panel. + */ + protected JPanel getButtonPanel() + { + return buttonPanel; + } + + /** + * Creates and returns a new panel that will be used for the controls at + * the bottom of the file chooser. + * + * @return A new panel. + */ + protected JPanel getBottomPanel() + { + if (bottomPanel == null) + bottomPanel = new JPanel(new GridLayout(3, 2)); + return bottomPanel; + } + + /** + * Fetches localised strings for use by the labels and buttons on the + * file chooser. + * + * @param fc the file chooser. + */ + protected void installStrings(JFileChooser fc) + { + super.installStrings(fc); + look = "Look In: "; + save = "Save In: "; + if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) + directoryLabel = save; + else + directoryLabel = look; + + fileLabel = "File Name: "; + filterLabel = "Files of Type: "; + + this.cancelButtonMnemonic = 0; + this.cancelButtonText = "Cancel"; + this.cancelButtonToolTipText = "Abort file chooser dialog"; + + this.directoryOpenButtonMnemonic = 0; + this.directoryOpenButtonText = "Open"; + this.directoryOpenButtonToolTipText = "Open selected directory"; + + this.helpButtonMnemonic = 0; + this.helpButtonText = "Help"; + this.helpButtonToolTipText = "Filechooser help"; + + this.openButtonMnemonic = 0; + this.openButtonText = "Open"; + this.openButtonToolTipText = "Open selected file"; + + this.saveButtonMnemonic = 0; + this.saveButtonText = "Save"; + this.saveButtonToolTipText = "Save selected file"; + + this.updateButtonMnemonic = 0; + this.updateButtonText = "Update"; + this.updateButtonToolTipText = "Update directory listing"; + } + + /** + * Installs the listeners required. + * + * @param fc the file chooser. + */ + protected void installListeners(JFileChooser fc) + { + directoryComboBox.setAction(new DirectoryComboBoxAction()); + fc.addPropertyChangeListener(filterModel); + listSelList = createListSelectionListener(fc); + doubleClickList = this.createDoubleClickListener(fc, fileList); + singleClickList = new SingleClickListener(fileList); + fileList.addListSelectionListener(listSelList); + fileList.addMouseListener(doubleClickList); + fileList.addMouseListener(singleClickList); + super.installListeners(fc); + } + + protected void uninstallListeners(JFileChooser fc) + { + super.uninstallListeners(fc); + fc.removePropertyChangeListener(filterModel); + directoryComboBox.setAction(null); + fileList.removeListSelectionListener(listSelList); + fileList.removeMouseListener(doubleClickList); + fileList.removeMouseListener(singleClickList); + + if (fileTable != null) + fileTable.removeMouseListener(tableClickList); + } + + protected ActionMap getActionMap() + { + if (actionMap == null) + actionMap = createActionMap(); + return actionMap; + } + + /** + * Creates and returns an action map. + * + * @return The action map. + */ + protected ActionMap createActionMap() + { + ActionMap map = new ActionMap(); + map.put("approveSelection", getApproveSelectionAction()); + map.put("cancelSelection", getCancelSelectionAction()); + map.put("Go Up", getChangeToParentDirectoryAction()); + return map; + } + + /** + * Creates a panel containing a list of files. + * + * @param fc the file chooser. + * + * @return A panel. + */ + protected JPanel createList(JFileChooser fc) + { + if (fileList == null) + { + fileListPanel = new JPanel(new BorderLayout()); + fileList = new JList(getModel()); + scrollPane = new JScrollPane(fileList); + scrollPane.setVerticalScrollBarPolicy + (JScrollPane.VERTICAL_SCROLLBAR_NEVER); + fileList.setLayoutOrientation(JList.VERTICAL_WRAP); + fileList.setCellRenderer(new FileRenderer()); + } + else + { + fileList.setModel(getModel()); + fileListPanel.removeAll(); + scrollPane.getViewport().setView(fileList); + } + fileListPanel.add(scrollPane); + + return fileListPanel; + } + + /** + * Creates a panel containing a table within a scroll pane. + * + * @param fc the file chooser. + * + * @return The details view. + */ + protected JPanel createDetailsView(JFileChooser fc) + { + fileTablePanel = new JPanel(new BorderLayout()); + + Object[] cols = new Object[] {"Name", "Size", "Modified"}; + Object[][] rows = new Object[fileList.getModel().getSize()][3]; + + fileTable = new JTable(new DefaultTableModel(rows, cols)); + + if (fc.isMultiSelectionEnabled()) + fileTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + else + fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + fileTable.setShowGrid(false); + fileTable.setColumnSelectionAllowed(false); + fileTable.setDefaultRenderer(Object.class, new TableFileRenderer()); + + tableClickList = new TableClickListener(fileTable, fc); + fileTable.addMouseListener(tableClickList); + + return updateTable(); + } + + /** + * Sets the values in the table, and puts it in the panel. + * + * @return the panel containing the table. + */ + JPanel updateTable() + { + DefaultTableModel mod = (DefaultTableModel) fileTable.getModel(); + ListModel lm = fileList.getModel(); + DateFormat dt = DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.SHORT); + File curr = null; + int size = lm.getSize(); + int rc = mod.getRowCount(); + + // If there are not enough rows + for (int x = rc; x < size; x++) + mod.addRow(new Object[3]); + + for (int i = 0; i < size; i++) + { + curr = (File) lm.getElementAt(i); + fileTable.setValueAt(curr.getName(), i, 0); + fileTable.setValueAt(formatSize(curr.length()), i, 1); + fileTable.setValueAt(dt.format(new Date(curr.lastModified())), i, 2); + } + + // If there are too many rows + while (rc > size) + mod.removeRow(--rc); + + scrollPane.getViewport().setView(fileTable); + scrollPane.setColumnHeaderView(fileTable.getTableHeader()); + + fileTablePanel.removeAll(); + fileTablePanel.add(scrollPane); + + return fileTablePanel; + } + + /** + * Formats bytes into the appropriate size. + * + * @param bytes - + * the number of bytes to convert + * @return a string representation of the size + */ + private String formatSize(long bytes) + { + NumberFormat nf = NumberFormat.getNumberInstance(); + long mb = (long) Math.pow(2, 20); + long kb = (long) Math.pow(2, 10); + long gb = (long) Math.pow(2, 30); + double size = 0; + String id = ""; + + if ((bytes / gb) >= 1) + { + size = (double) bytes / (double) gb; + id = "GB"; + } + else if ((bytes / mb) >= 1) + { + size = (double) bytes / (double) mb; + id = "MB"; + } + else if ((bytes / kb) >= 1) + { + size = (double) bytes / (double) kb; + id = "KB"; + } + else + { + size = bytes; + id = "Bytes"; + } + + return nf.format(size) + " " + id; + } + /** + * Creates a listener that monitors selections in the directory/file list + * and keeps the {@link JFileChooser} component up to date. + * + * @param fc the file chooser. + * + * @return The listener. + * + * @see #installListeners(JFileChooser) + */ + public ListSelectionListener createListSelectionListener(JFileChooser fc) + { + return new MetalFileChooserSelectionListener(); + } + + /** + * Returns the preferred size for the file chooser component. + * + * @return The preferred size. + */ + public Dimension getPreferredSize(JComponent c) + { + Dimension tp = topPanel.getPreferredSize(); + Dimension bp = bottomPanel.getPreferredSize(); + Dimension fl = fileListPanel.getPreferredSize(); + return new Dimension(fl.width, tp.height + bp.height + fl.height); + } + + /** + * Returns the minimum size for the file chooser component. + * + * @return The minimum size. + */ + public Dimension getMinimumSize(JComponent c) + { + Dimension tp = topPanel.getMinimumSize(); + Dimension bp = bottomPanel.getMinimumSize(); + Dimension fl = fileListPanel.getMinimumSize(); + return new Dimension(fl.width, tp.height + bp.height + fl.height); } /** + * Returns the maximum size for the file chooser component. + * + * @return The maximum size. + */ + public Dimension getMaximumSize(JComponent c) + { + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + /** + * Creates a property change listener that monitors the {@link JFileChooser} + * for property change events and updates the component display accordingly. + * + * @param fc the file chooser. + * + * @return The property change listener. + * + * @see #installListeners(JFileChooser) + */ + public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) + { + return new MetalFileChooserPropertyChangeListener(); + } + + /** * Creates and returns a new instance of {@link DirectoryComboBoxModel}. * * @return A new instance of {@link DirectoryComboBoxModel}. @@ -407,6 +1749,20 @@ public class MetalFileChooserUI extends BasicFileChooserUI } /** + * Creates a new instance of the renderer used in the directory + * combo box. + * + * @param fc the file chooser. + * + * @return The renderer. + */ + protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer( + JFileChooser fc) + { + return new DirectoryComboBoxRenderer(fc); + } + + /** * Creates and returns a new instance of {@link FilterComboBoxModel}. * * @return A new instance of {@link FilterComboBoxModel}. @@ -427,4 +1783,297 @@ public class MetalFileChooserUI extends BasicFileChooserUI return new FilterComboBoxRenderer(); } + /** + * Adds the control buttons ('up', 'home' etc.) to the panel. + */ + protected void addControlButtons() + { + JButton upButton = new JButton(getChangeToParentDirectoryAction()); + upButton.setText(null); + upButton.setIcon(this.upFolderIcon); + upButton.setMargin(new Insets(0, 0, 0, 0)); + controls.add(upButton); + + JButton homeButton = new JButton(getGoHomeAction()); + homeButton.setText(null); + homeButton.setIcon(this.homeFolderIcon); + homeButton.setMargin(new Insets(0, 0, 0, 0)); + controls.add(homeButton); + + JButton newFolderButton = new JButton(getNewFolderAction()); + newFolderButton.setText(null); + newFolderButton.setIcon(this.newFolderIcon); + newFolderButton.setMargin(new Insets(0, 0, 0, 0)); + controls.add(newFolderButton); + + JToggleButton listButton = new JToggleButton(this.listViewIcon); + listButton.setMargin(new Insets(0, 0, 0, 0)); + listButton.addActionListener(new ListViewActionListener()); + listButton.setSelected(true); + listView = true; + controls.add(listButton); + + JToggleButton detailButton = new JToggleButton(this.detailsViewIcon); + detailButton.setMargin(new Insets(0, 0, 0, 0)); + detailButton.addActionListener(new DetailViewActionListener()); + detailButton.setSelected(false); + controls.add(detailButton); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(listButton); + buttonGroup.add(detailButton); + } + + /** + * Removes all the buttons from the control panel. + */ + protected void removeControlButtons() + { + controls.removeAll(); + controls.revalidate(); + controls.repaint(); + } + + /** + * Updates the current directory. + * + * @param the file chooser to update. + */ + public void rescanCurrentDirectory(JFileChooser fc) + { + directoryModel.setSelectedItem(fc.getCurrentDirectory()); + getModel().validateFileCache(); + if (!listView) + updateTable(); + else + createList(fc); + } + + /** + * Returns the file name in the text field. + * + * @return The file name. + */ + public String getFileName() + { + String result = null; + if (fileTextField != null) + result = fileTextField.getText(); + return result; + } + + /** + * Sets the file name in the text field. + * + * @param filename the file name. + */ + public void setFileName(String filename) + { + fileTextField.setText(filename); + } + + /** + * DOCUMENT ME!! + * + * @param e - DOCUMENT ME! + */ + public void valueChanged(ListSelectionEvent e) + { + // FIXME: Not sure what we should be doing here, if anything. + } + + /** + * Returns the approve button. + * + * @return The approve button. + */ + protected JButton getApproveButton(JFileChooser fc) + { + return approveButton; + } + + /** + * A layout manager that is used to arrange the subcomponents of the + * {@link JFileChooser}. + */ + class VerticalMidLayout implements LayoutManager + { + /** + * Performs the layout. + * + * @param parent the container. + */ + public void layoutContainer(Container parent) + { + int count = parent.getComponentCount(); + if (count > 0) + { + Insets insets = parent.getInsets(); + Component c = parent.getComponent(0); + Dimension prefSize = c.getPreferredSize(); + int h = parent.getHeight() - insets.top - insets.bottom; + int adj = Math.max(0, (h - prefSize.height) / 2); + c.setBounds(insets.left, insets.top + adj, parent.getWidth() + - insets.left - insets.right, + (int) Math.min(prefSize.getHeight(), h)); + } + } + + /** + * Returns the minimum layout size. + * + * @param parent the container. + * + * @return The minimum layout size. + */ + public Dimension minimumLayoutSize(Container parent) + { + return preferredLayoutSize(parent); + } + + /** + * Returns the preferred layout size. + * + * @param parent the container. + * + * @return The preferred layout size. + */ + public Dimension preferredLayoutSize(Container parent) + { + if (parent.getComponentCount() > 0) + { + return parent.getComponent(0).getPreferredSize(); + } + else return null; + } + + /** + * This layout manager does not need to track components, so this + * method does nothing. + * + * @param name the name the component is associated with. + * @param component the component. + */ + public void addLayoutComponent(String name, Component component) + { + // do nothing + } + + /** + * This layout manager does not need to track components, so this + * method does nothing. + * + * @param component the component. + */ + public void removeLayoutComponent(Component component) { + // do nothing + } + } + + /** + * A layout manager that is used to arrange buttons for the + * {@link JFileChooser}. + */ + class ButtonLayout implements LayoutManager + { + static final int GAP = 4; + + /** + * Performs the layout. + * + * @param parent the container. + */ + public void layoutContainer(Container parent) + { + int count = parent.getComponentCount(); + if (count > 0) + { + // first find the widest button + int maxW = 0; + for (int i = 0; i < count; i++) + { + Component c = parent.getComponent(i); + Dimension prefSize = c.getPreferredSize(); + maxW = Math.max(prefSize.width, maxW); + } + + // then position the buttons + Insets insets = parent.getInsets(); + int availableH = parent.getHeight() - insets.top - insets.bottom; + int currentX = parent.getWidth() - insets.right; + for (int i = count - 1; i >= 0; i--) + { + Component c = parent.getComponent(i); + Dimension prefSize = c.getPreferredSize(); + int adj = Math.max(0, (availableH - prefSize.height) / 2); + currentX = currentX - prefSize.width; + c.setBounds(currentX, insets.top + adj, prefSize.width, + (int) Math.min(prefSize.getHeight(), availableH)); + currentX = currentX - GAP; + } + } + } + + /** + * Returns the minimum layout size. + * + * @param parent the container. + * + * @return The minimum layout size. + */ + public Dimension minimumLayoutSize(Container parent) + { + return preferredLayoutSize(parent); + } + + /** + * Returns the preferred layout size. + * + * @param parent the container. + * + * @return The preferred layout size. + */ + public Dimension preferredLayoutSize(Container parent) + { + Insets insets = parent.getInsets(); + int maxW = 0; + int maxH = 0; + int count = parent.getComponentCount(); + if (count > 0) + { + for (int i = 0; i < count; i++) + { + Component c = parent.getComponent(i); + Dimension d = c.getPreferredSize(); + maxW = Math.max(d.width, maxW); + maxH = Math.max(d.height, maxH); + } + } + return new Dimension(maxW * count + GAP * (count - 1) + insets.left + + insets.right, maxH + insets.top + insets.bottom); + } + + /** + * This layout manager does not need to track components, so this + * method does nothing. + * + * @param name the name the component is associated with. + * @param component the component. + */ + public void addLayoutComponent(String name, Component component) + { + // do nothing + } + + /** + * This layout manager does not need to track components, so this + * method does nothing. + * + * @param component the component. + */ + public void removeLayoutComponent(Component component) { + // do nothing + } + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java index bcb86e61047..0b644f30037 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java @@ -68,6 +68,12 @@ public class MetalIconFactory implements Serializable /** A constant representing "light". */ public static final boolean LIGHT = true; + + /** A shared instance of the MenuArrowIcon. */ + private static Icon menuArrow; + + /** A shared instance of the MenuItemArrowIcon. */ + private static Icon menuItemArrow; /** * An icon displayed for {@link JCheckBoxMenuItem} components. @@ -2467,4 +2473,102 @@ public class MetalIconFactory implements Serializable return treeHardDriveIcon; } + /** + * Returns a new instance of a 4 x 8 icon showing a small black triangle that + * points to the right. This is displayed in menu items that have a + * sub menu. + * + * @return The icon. + */ + public static Icon getMenuArrowIcon() + { + if (menuArrow == null) + menuArrow = new Icon() + { + public int getIconHeight() + { + return 8; + } + + public int getIconWidth() + { + return 4; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + g.setColor(Color.BLACK); + for (int i = 0; i < 4; i++) + g.drawLine(x + i, y + i, x + i, y + 7 - i); + g.setColor(saved); + } + }; + return menuArrow; + } + + /** + * Returns a new instance of a 4 x 8 icon showing a small black triangle that + * points to the right. This is displayed in menu items that have a sub menu. + * + * @return The icon. + */ + public static Icon getMenuItemArrowIcon() + { + if (menuItemArrow == null) + menuItemArrow = new Icon() + { + public int getIconHeight() + { + return 8; + } + + public int getIconWidth() + { + return 4; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + g.setColor(Color.BLACK); + for (int i = 0; i < 4; i++) + g.drawLine(x + i, y + i, x + i, y + 7 - i); + g.setColor(saved); + } + }; + return menuItemArrow; + } + + /** + * Returns a new instance of a 13 x 13 icon showing a small black check mark. + * + * @return The icon. + */ + public static Icon getMenuItemCheckIcon() + { + return new Icon() + { + public int getIconHeight() + { + return 13; + } + + public int getIconWidth() + { + return 13; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + g.setColor(Color.BLACK); + g.drawLine(3 + x, 5 + y, 3 + x, 9 + y); + g.drawLine(4 + x, 5 + y, 4 + x, 9 + y); + g.drawLine(5 + x, 7 + y, 9 + x, 3 + y); + g.drawLine(5 + x, 8 + y, 9 + x, 4 + y); + g.setColor(saved); + } + }; + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java index da019379bf5..c60b55c9e7b 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -40,7 +40,6 @@ package javax.swing.plaf.metal; import java.awt.Color; import java.awt.Font; -import java.awt.Insets; import javax.swing.LookAndFeel; import javax.swing.UIDefaults; @@ -55,7 +54,17 @@ import javax.swing.plaf.basic.BasicLookAndFeel; /** * A custom look and feel that is designed to look similar across different - * operating systems. + * operating systems. To install this look and feel, add the following code + * (or something similar) near the start of your application:</p> + * <pre> + * try + * { + * UIManager.setLookAndFeel(new MetalLookAndFeel()); + * } + * catch (UnsupportedLookAndFeelException e) + * { + * e.printStackTrace(); + * }</pre> */ public class MetalLookAndFeel extends BasicLookAndFeel { @@ -72,8 +81,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public MetalLookAndFeel() { - if (theme == null) - createDefaultTheme(); + createDefaultTheme(); } /** @@ -81,7 +89,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ protected void createDefaultTheme() { - setCurrentTheme(new DefaultMetalTheme()); + if (theme == null) + setCurrentTheme(new DefaultMetalTheme()); } /** @@ -115,7 +124,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public String getDescription() { - return "Metal look and feel"; + return "The Java(tm) Look and Feel"; } /** @@ -125,7 +134,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public String getID() { - return "MetalLookAndFeel"; + return "Metal"; } /** @@ -135,7 +144,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public String getName() { - return "MetalLookAndFeel"; + return "Metal"; } public UIDefaults getDefaults() @@ -145,7 +154,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel LAF_defaults = super.getDefaults(); // add custom theme entries to the table - theme.addCustomEntriesToTable(LAF_defaults); + if (theme != null) + theme.addCustomEntriesToTable(LAF_defaults); } // Returns the default values for this look and feel. @@ -159,7 +169,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getAcceleratorForeground() { - return theme.getAcceleratorForeground(); + if (theme != null) + return theme.getAcceleratorForeground(); + return null; } /** @@ -170,7 +182,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getAcceleratorSelectedForeground() { - return theme.getAcceleratorSelectedForeground(); + if (theme != null) + return theme.getAcceleratorSelectedForeground(); + return null; } /** @@ -180,7 +194,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getBlack() { - return theme.getBlack(); + if (theme != null) + return theme.getBlack(); + return null; } /** @@ -190,7 +206,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControl() { - return theme.getControl(); + if (theme != null) + return theme.getControl(); + return null; } /** @@ -201,7 +219,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlDarkShadow() { - return theme.getControlDarkShadow(); + if (theme != null) + return theme.getControlDarkShadow(); + return null; } /** @@ -211,7 +231,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlDisabled() { - return theme.getControlDisabled(); + if (theme != null) + return theme.getControlDisabled(); + return null; } /** @@ -222,7 +244,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlHighlight() { - return theme.getControlHighlight(); + if (theme != null) + return theme.getControlHighlight(); + return null; } /** @@ -233,7 +257,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlInfo() { - return theme.getControlInfo(); + if (theme != null) + return theme.getControlInfo(); + return null; } /** @@ -244,7 +270,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlShadow() { - return theme.getControlShadow(); + if (theme != null) + return theme.getControlShadow(); + return null; } /** @@ -254,7 +282,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlTextColor() { - return theme.getControlTextColor(); + if (theme != null) + return theme.getControlTextColor(); + return null; } /** @@ -264,7 +294,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getControlTextFont() { - return theme.getControlTextFont(); + if (theme != null) + return theme.getControlTextFont(); + return null; } /** @@ -275,7 +307,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getDesktopColor() { - return theme.getDesktopColor(); + if (theme != null) + return theme.getDesktopColor(); + return null; } /** @@ -286,7 +320,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getFocusColor() { - return theme.getFocusColor(); + if (theme != null) + return theme.getFocusColor(); + return null; } /** @@ -297,7 +333,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getHighlightedTextColor() { - return theme.getHighlightedTextColor(); + if (theme != null) + return theme.getHighlightedTextColor(); + return null; } /** @@ -308,7 +346,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getInactiveControlTextColor() { - return theme.getInactiveControlTextColor(); + if (theme != null) + return theme.getInactiveControlTextColor(); + return null; } /** @@ -319,7 +359,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getInactiveSystemTextColor() { - return theme.getInactiveSystemTextColor(); + if (theme != null) + return theme.getInactiveSystemTextColor(); + return null; } /** @@ -331,7 +373,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuBackground() { - return theme.getMenuBackground(); + if (theme != null) + return theme.getMenuBackground(); + return null; } /** @@ -344,7 +388,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuDisabledForeground() { - return theme.getMenuDisabledForeground(); + if (theme != null) + return theme.getMenuDisabledForeground(); + return null; } /** @@ -357,7 +403,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuForeground() { - return theme.getMenuForeground(); + if (theme != null) + return theme.getMenuForeground(); + return null; } /** @@ -370,7 +418,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuSelectedBackground() { - return theme.getMenuSelectedBackground(); + if (theme != null) + return theme.getMenuSelectedBackground(); + return null; } /** @@ -383,7 +433,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuSelectedForeground() { - return theme.getMenuSelectedForeground(); + if (theme != null) + return theme.getMenuSelectedForeground(); + return null; } /** @@ -393,7 +445,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getMenuTextFont() { - return theme.getMenuTextFont(); + if (theme != null) + return theme.getMenuTextFont(); + return null; } /** @@ -403,7 +457,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControl() { - return theme.getPrimaryControl(); + if (theme != null) + return theme.getPrimaryControl(); + return null; } /** @@ -414,7 +470,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlDarkShadow() { - return theme.getPrimaryControlDarkShadow(); + if (theme != null) + return theme.getPrimaryControlDarkShadow(); + return null; } /** @@ -425,7 +483,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlHighlight() { - return theme.getPrimaryControlHighlight(); + if (theme != null) + return theme.getPrimaryControlHighlight(); + return null; } /** @@ -436,7 +496,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlInfo() { - return theme.getPrimaryControlInfo(); + if (theme != null) + return theme.getPrimaryControlInfo(); + return null; } /** @@ -447,7 +509,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlShadow() { - return theme.getPrimaryControlShadow(); + if (theme != null) + return theme.getPrimaryControlShadow(); + return null; } /** @@ -457,7 +521,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getSeparatorBackground() { - return theme.getSeparatorBackground(); + if (theme != null) + return theme.getSeparatorBackground(); + return null; } /** @@ -467,7 +533,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getSeparatorForeground() { - return theme.getSeparatorForeground(); + if (theme != null) + return theme.getSeparatorForeground(); + return null; } /** @@ -477,7 +545,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getSubTextFont() { - return theme.getSubTextFont(); + if (theme != null) + return theme.getSubTextFont(); + return null; } /** @@ -487,7 +557,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getSystemTextColor() { - return theme.getSystemTextColor(); + if (theme != null) + return theme.getSystemTextColor(); + return null; } /** @@ -497,7 +569,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getSystemTextFont() { - return theme.getSystemTextFont(); + if (theme != null) + return theme.getSystemTextFont(); + return null; } /** @@ -507,7 +581,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getTextHighlightColor() { - return theme.getTextHighlightColor(); + if (theme != null) + return theme.getTextHighlightColor(); + return null; } /** @@ -517,7 +593,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getUserTextColor() { - return theme.getUserTextColor(); + if (theme != null) + return theme.getUserTextColor(); + return null; } /** @@ -527,7 +605,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getUserTextFont() { - return theme.getUserTextFont(); + if (theme != null) + return theme.getUserTextFont(); + return null; } /** @@ -537,7 +617,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWhite() { - return theme.getWhite(); + if (theme != null) + return theme.getWhite(); + return null; } /** @@ -547,7 +629,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowBackground() { - return theme.getWindowBackground(); + if (theme != null) + return theme.getWindowBackground(); + return null; } /** @@ -557,7 +641,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleBackground() { - return theme.getWindowTitleBackground(); + if (theme != null) + return theme.getWindowTitleBackground(); + return null; } /** @@ -569,7 +655,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getWindowTitleFont() { - return theme.getWindowTitleFont(); + if (theme != null) + return theme.getWindowTitleFont(); + return null; } /** @@ -579,7 +667,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleForeground() { - return theme.getWindowTitleForeground(); + if (theme != null) + return theme.getWindowTitleForeground(); + return null; } /** @@ -590,7 +680,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleInactiveBackground() { - return theme.getWindowTitleInactiveBackground(); + if (theme != null) + return theme.getWindowTitleInactiveBackground(); + return null; } /** @@ -601,7 +693,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleInactiveForeground() { - return theme.getWindowTitleInactiveForeground(); + if (theme != null) + return theme.getWindowTitleInactiveForeground(); + return null; } /** @@ -691,6 +785,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "CheckBoxUI", "javax.swing.plaf.metal.MetalCheckBoxUI", "ComboBoxUI", "javax.swing.plaf.metal.MetalComboBoxUI", "DesktopIconUI", "javax.swing.plaf.metal.MetalDesktopIconUI", + "FileChooserUI", "javax.swing.plaf.metal.MetalFileChooserUI", "InternalFrameUI", "javax.swing.plaf.metal.MetalInternalFrameUI", "LabelUI", "javax.swing.plaf.metal.MetalLabelUI", "MenuBarUI", "javax.swing.plaf.metal.MetalMenuBarUI", @@ -841,7 +936,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "EditorPane.background", getWindowBackground(), "EditorPane.caretForeground", getUserTextColor(), - "EditorPane.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "EditorPane.font", new FontUIResource("Dialog", Font.BOLD, 12), "EditorPane.foreground", getUserTextColor(), "EditorPane.inactiveForeground", getInactiveSystemTextColor(), "EditorPane.selectionBackground", getTextHighlightColor(), @@ -858,6 +953,19 @@ public class MetalLookAndFeel extends BasicLookAndFeel "FormattedTextField.selectionBackground", getTextHighlightColor(), "FormattedTextField.selectionForeground", getHighlightedTextColor(), + "FileChooser.upFolderIcon", + MetalIconFactory.getFileChooserUpFolderIcon(), + "FileChooser.listViewIcon", + MetalIconFactory.getFileChooserListViewIcon(), + "FileChooser.newFolderIcon", + MetalIconFactory.getFileChooserNewFolderIcon(), + "FileChooser.homeFolderIcon", + MetalIconFactory.getFileChooserHomeFolderIcon(), + "FileChooser.detailsViewIcon", + MetalIconFactory.getFileChooserDetailViewIcon(), + "FileChooser.fileNameLabelMnemonic", new Integer(78), + "FileChooser.filesOfTypeLabelMnemonic",new Integer(84), + "FileChooser.lookInLabelMnemonic", new Integer(73), "FileView.computerIcon", MetalIconFactory.getTreeComputerIcon(), "FileView.directoryIcon", MetalIconFactory.getTreeFolderIcon(), "FileView.fileIcon", MetalIconFactory.getTreeLeafIcon(), @@ -875,15 +983,20 @@ public class MetalLookAndFeel extends BasicLookAndFeel "InternalFrame.icon", MetalIconFactory.getInternalFrameDefaultMenuIcon(), "InternalFrame.closeIcon", MetalIconFactory.getInternalFrameCloseIcon(16), + "InternalFrame.closeSound", "sounds/FrameClose.wav", "InternalFrame.inactiveTitleBackground", getWindowTitleInactiveBackground(), "InternalFrame.inactiveTitleForeground", getWindowTitleInactiveForeground(), "InternalFrame.maximizeIcon", MetalIconFactory.getInternalFrameMaximizeIcon(16), + "InternalFrame.maximizeSound", "sounds/FrameMaximize.wav", "InternalFrame.iconifyIcon", MetalIconFactory.getInternalFrameMinimizeIcon(16), + "InternalFrame.minimizeSound", "sounds/FrameMinimize.wav", "InternalFrame.paletteBorder", new MetalBorders.PaletteBorder(), "InternalFrame.paletteCloseIcon", new MetalIconFactory.PaletteCloseIcon(), "InternalFrame.paletteTitleHeight", new Integer(11), + "InternalFrame.restoreDownSound", "sounds/FrameRestoreDown.wav", + "InternalFrame.restoreUpSound", "sounds/FrameRestoreUp.wav", "Label.background", getControl(), "Label.disabledForeground", getInactiveSystemTextColor(), @@ -902,14 +1015,18 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Menu.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), "Menu.acceleratorForeground", getAcceleratorForeground(), "Menu.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "Menu.arrowIcon", MetalIconFactory.getMenuArrowIcon(), "Menu.background", getMenuBackground(), "Menu.border", new MetalBorders.MenuItemBorder(), "Menu.borderPainted", Boolean.TRUE, + "MenuItem.commandSound", "sounds/MenuItemCommand.wav", "Menu.disabledForeground", getMenuDisabledForeground(), "Menu.font", getControlTextFont(), "Menu.foreground", getMenuForeground(), "Menu.selectionBackground", getMenuSelectedBackground(), "Menu.selectionForeground", getMenuSelectedForeground(), + "Menu.submenuPopupOffsetX", new Integer(-4), + "Menu.submenuPopupOffsetY", new Integer(-3), "MenuBar.background", getMenuBackground(), "MenuBar.border", new MetalBorders.MenuBarBorder(), @@ -918,11 +1035,14 @@ public class MetalLookAndFeel extends BasicLookAndFeel "MenuBar.highlight", getControlHighlight(), "MenuBar.shadow", getControlShadow(), + "MenuItem.acceleratorDelimiter", "-", "MenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), "MenuItem.acceleratorForeground", getAcceleratorForeground(), "MenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "MenuItem.arrowIcon", MetalIconFactory.getMenuItemArrowIcon(), "MenuItem.background", getMenuBackground(), "MenuItem.border", new MetalBorders.MenuItemBorder(), + "MenuItem.borderPainted", Boolean.TRUE, "MenuItem.disabledForeground", getMenuDisabledForeground(), "MenuItem.font", getControlTextFont(), "MenuItem.foreground", getMenuForeground(), @@ -930,6 +1050,10 @@ public class MetalLookAndFeel extends BasicLookAndFeel "MenuItem.selectionForeground", getMenuSelectedForeground(), "OptionPane.background", getControl(), + "OptionPane.errorSound", "sounds/OptionPaneError.wav", + "OptionPane.informationSound", "sounds/OptionPaneInformation.wav", + "OptionPane.questionSound", "sounds/OptionPaneQuestion.wav", + "OptionPane.warningSound", "sounds/OptionPaneWarning.wav", "OptionPane.errorDialog.border.background", new ColorUIResource(153, 51, 51), "OptionPane.errorDialog.titlePane.background", new ColorUIResource(255, 153, 153), "OptionPane.errorDialog.titlePane.foreground", new ColorUIResource(51, 0, 0), @@ -953,6 +1077,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel new BorderUIResource(MetalBorders.getTextFieldBorder()), "PasswordField.caretForeground", getUserTextColor(), "PasswordField.foreground", getUserTextColor(), + "PasswordField.font", new FontUIResource("Dialog", Font.PLAIN, 12), "PasswordField.inactiveBackground", getControl(), "PasswordField.inactiveForeground", getInactiveSystemTextColor(), "PasswordField.selectionBackground", getTextHighlightColor(), @@ -962,6 +1087,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "PopupMenu.border", new MetalBorders.PopupMenuBorder(), "PopupMenu.font", new FontUIResource("Dialog", Font.BOLD, 12), "PopupMenu.foreground", getMenuForeground(), + "PopupMenu.popupSound", "sounds/PopupMenuPopup.wav", "ProgressBar.background", getControl(), "ProgressBar.border", new BorderUIResource.LineBorderUIResource(getControlDarkShadow(), 1), @@ -997,6 +1123,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "RadioButtonMenuItem.borderPainted", Boolean.TRUE, "RadioButtonMenuItem.checkIcon", MetalIconFactory.getRadioButtonMenuItemIcon(), + "RadioButtonMenuItem.commandSound", "sounds/MenuItemCommand.wav", "RadioButtonMenuItem.disabledForeground", getMenuDisabledForeground(), "RadioButtonMenuItem.font", MetalLookAndFeel.getControlTextFont(), "RadioButtonMenuItem.foreground", getMenuForeground(), @@ -1006,6 +1133,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "RadioButtonMenuItem.selectionForeground", MetalLookAndFeel.getMenuSelectedForeground(), + "ScrollBar.allowsAbsolutePositioning", Boolean.TRUE, "ScrollBar.background", getControl(), "ScrollBar.darkShadow", getControlDarkShadow(), "ScrollBar.foreground", getControl(), @@ -1041,6 +1169,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Slider.verticalThumbIcon", MetalIconFactory.getVerticalSliderThumbIcon(), + "Spinner.arrowButtonInsets", new InsetsUIResource(0, 0, 0, 0), "Spinner.background", getControl(), "Spinner.font", new FontUIResource("Dialog", Font.BOLD, 12), "Spinner.foreground", getControl(), @@ -1048,6 +1177,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "SplitPane.background", getControl(), "SplitPane.darkShadow", getControlDarkShadow(), "SplitPane.dividerFocusColor", getPrimaryControl(), + "SplitPane.dividerSize", new Integer(10), "SplitPane.highlight", getControlHighlight(), "SplitPane.shadow", getControlShadow(), @@ -1150,6 +1280,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "ToolTip.font", new FontUIResource("Dialog", Font.PLAIN, 12), "ToolTip.foreground", getPrimaryControlInfo(), "ToolTip.foregroundInactive", getControlDarkShadow(), + "ToolTip.hideAccelerator", Boolean.FALSE, "Tree.background", getWindowBackground(), "Tree.closedIcon", MetalIconFactory.getTreeFolderIcon(), diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java index de71fe8e5b2..9fb960f68b9 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java @@ -176,7 +176,7 @@ public class MetalRadioButtonUI protected void paintFocus(Graphics g, Rectangle t, Dimension d) { g.setColor(focusColor); - g.drawRect(t.x - 1, t.y + 2, t.width + 2, t.height - 4); + g.drawRect(t.x - 1, t.y - 1, t.width + 2, t.height + 2); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java index d5bf175f92d..ae14af3cae9 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java @@ -38,7 +38,10 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.beans.PropertyChangeListener; + import javax.swing.JComponent; +import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollPaneUI; @@ -68,4 +71,89 @@ public class MetalScrollPaneUI { return new MetalScrollPaneUI(); } + + /** + * Configures the specified component appropriate for the look and feel. + * This method is invoked when the ComponentUI instance is being installed + * as the UI delegate on the specified component. This method should + * completely configure the component for the look and feel, + * including the following: + * 1. Install any default property values for color, fonts, borders, + * icons, opacity, etc. on the component. Whenever possible, property + * values initialized by the client program should not be overridden. + * 2. Install a LayoutManager on the component if necessary. + * 3. Create/add any required sub-components to the component. + * 4. Create/install event listeners on the component. + * 5. Create/install a PropertyChangeListener on the component in order + * to detect and respond to component property changes appropriately. + * 6. Install keyboard UI (mnemonics, traversal, etc.) on the component. + * 7. Initialize any appropriate instance data. + * + * @param c - the component to install the ui on + */ + public void installUI(JComponent c) + { + super.installUI(c); + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + hsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, Boolean.FALSE); + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + vsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, Boolean.FALSE); + } + + /** + * Reverses configuration which was done on the specified component + * during installUI. This method is invoked when this UIComponent + * instance is being removed as the UI delegate for the specified + * component. This method should undo the configuration performed in + * installUI, being careful to leave the JComponent instance in a + * clean state (no extraneous listeners, look-and-feel-specific property + * objects, etc.). This should include the following: + * 1. Remove any UI-set borders from the component. + * 2. Remove any UI-set layout managers on the component. + * 3. Remove any UI-added sub-components from the component. + * 4. Remove any UI-added event/property listeners from the component. + * 5. Remove any UI-installed keyboard UI from the component. + * 6. Nullify any allocated instance data objects to allow for GC. + * + * @param c - the component to uninstall the ui on + */ + public void uninstallUI(JComponent c) + { + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + hsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, null); + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + vsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, null); + super.uninstallUI(c); + } + + /** + * Installs listeners on scrollPane + * + * @param scrollPane - the component to install the listeners on + */ + public void installListeners(JScrollPane scrollPane) + { + super.installListeners(scrollPane); + } + + /** + * Uninstalls listeners on scrollPane + * + * @param scrollPane - the component to uninstall the listeners on + */ + public void uninstallListeners(JScrollPane scrollPane) + { + super.uninstallListeners(scrollPane); + } + + /** + * TODO + * + * @return TODO + */ + protected PropertyChangeListener createScrollBarSwapListener() + { + // FIXME: Anything else to do here? + return super.createPropertyChangeListener(); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java index 016e09557d6..34a964cb339 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java @@ -38,9 +38,16 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; +import java.awt.Component; +import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.LayoutManager; +import java.awt.Point; +import javax.swing.JSplitPane; +import javax.swing.SwingConstants; +import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicSplitPaneDivider; /** @@ -56,7 +63,13 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider /** The light color in the pattern. */ Color light; + + /** The JSplitPane the divider is on. */ + JSplitPane splitPane; + /** The split pane orientation. */ + int orientation; + /** * Creates a new instance of MetalSplitPaneDivider. * @@ -65,6 +78,9 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider public MetalSplitPaneDivider(MetalSplitPaneUI ui, Color light, Color dark) { super(ui); + setLayout(new MetalDividerLayout()); + this.splitPane = super.splitPane; + this.orientation = super.orientation; this.light = light; this.dark = dark; } @@ -76,9 +92,127 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider */ public void paint(Graphics g) { - //super.paint(g); Dimension s = getSize(); MetalUtils.fillMetalPattern(splitPane, g, 2, 2, s.width - 4, s.height - 4, light, dark); + if (splitPane.isOneTouchExpandable()) + { + ((BasicArrowButton) rightButton).paint(g); + ((BasicArrowButton) leftButton).paint(g); + } + } + + /** + * This helper class acts as the Layout Manager for the divider. + */ + public class MetalDividerLayout implements LayoutManager + { + /** The right button. */ + BasicArrowButton rb; + + /** The left button. */ + BasicArrowButton lb; + + /** + * Creates a new DividerLayout object. + */ + public MetalDividerLayout() + { + // Nothing to do here + } + + /** + * This method is called when a Component is added. + * + * @param string The constraints string. + * @param c The Component to add. + */ + public void addLayoutComponent(String string, Component c) + { + // Nothing to do here, constraints are set depending on + // orientation in layoutContainer + } + + /** + * This method is called to lay out the container. + * + * @param c The container to lay out. + */ + public void layoutContainer(Container c) + { + // The only components we care about setting up are the + // one touch buttons. + if (splitPane.isOneTouchExpandable()) + { + if (c.getComponentCount() == 2) + { + Component c1 = c.getComponent(0); + Component c2 = c.getComponent(1); + if ((c1 instanceof BasicArrowButton) + && (c2 instanceof BasicArrowButton)) + { + lb = ((BasicArrowButton) c1); + rb = ((BasicArrowButton) c2); + } + } + if (rb != null && lb != null) + { + Point p = getLocation(); + lb.setSize(lb.getPreferredSize()); + rb.setSize(rb.getPreferredSize()); + lb.setLocation(p.x, p.y); + + if (orientation == JSplitPane.HORIZONTAL_SPLIT) + { + rb.setDirection(SwingConstants.EAST); + lb.setDirection(SwingConstants.WEST); + rb.setLocation(p.x, p.y + lb.getHeight()); + } + else + { + rb.setDirection(SwingConstants.SOUTH); + lb.setDirection(SwingConstants.NORTH); + rb.setLocation(p.x + lb.getWidth(), p.y); + } + } + } + } + + /** + * This method returns the minimum layout size. + * + * @param c The container to calculate for. + * + * @return The minimum layout size. + */ + public Dimension minimumLayoutSize(Container c) + { + return preferredLayoutSize(c); + } + + /** + * This method returns the preferred layout size. + * + * @param c The container to calculate for. + * + * @return The preferred layout size. + */ + public Dimension preferredLayoutSize(Container c) + { + int dividerSize = getDividerSize(); + return new Dimension(dividerSize, dividerSize); + } + + /** + * This method is called when a component is removed. + * + * @param c The component to remove. + */ + public void removeLayoutComponent(Component c) + { + // Nothing to do here. If buttons are removed + // they will not be layed out when layoutContainer is + // called. + } } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java index b1e02c7a6b0..c6c46ffe614 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java @@ -38,11 +38,14 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; import java.awt.Graphics; import java.awt.LayoutManager; +import java.awt.Rectangle; import javax.swing.JComponent; import javax.swing.JTabbedPane; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTabbedPaneUI; @@ -101,6 +104,29 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI } /** + * The minimum tab width. + */ + protected int minTabWidth; + + /** + * The color for the selected tab. + */ + protected Color selectColor; + + /** + * The color for a highlighted selected tab. + */ + protected Color selectHighlight; + + /** + * The background color used for the tab area. + */ + protected Color tabAreaBackground; + + /** The graphics to draw the highlight below the tab. */ + private Graphics hg; + + /** * Constructs a new instance of MetalTabbedPaneUI. */ public MetalTabbedPaneUI() @@ -175,6 +201,16 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { + int currentRun = getRunForTab(tabPane.getTabCount(), tabIndex); + if (shouldFillGap(currentRun, tabIndex, x, y)) + { + g.translate(x, y); + g.setColor(getColorForGap(currentRun, x, y)); + g.fillRect(1, 0, 5, 3); + g.fillRect(1, 3, 2, 2); + g.translate(-x, -y); + } + if (isSelected) { g.setColor(MetalLookAndFeel.getControlHighlight()); @@ -267,6 +303,16 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { + int currentRun = getRunForTab(tabPane.getTabCount(), tabIndex); + if (shouldFillGap(currentRun, tabIndex, x, y)) + { + g.translate(x, y); + g.setColor(getColorForGap(currentRun, x, y)); + g.fillRect(1, h - 5, 3, 5); + g.fillRect(4, h - 2, 2, 2); + g.translate(-x, -y); + } + if (isSelected) { g.setColor(MetalLookAndFeel.getControlHighlight()); @@ -297,9 +343,16 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI int tabIndex, int x, int y, int w, int h, boolean isSelected) { if (isSelected) - g.setColor(MetalLookAndFeel.getControl()); + g.setColor(UIManager.getColor("TabbedPane.selected")); else - g.setColor(MetalLookAndFeel.getControlShadow()); + { + // This is only present in the OceanTheme, so we must check if it + // is actually there + Color background = UIManager.getColor("TabbedPane.unselectedBackground"); + if (background == null) + background = UIManager.getColor("TabbedPane.background"); + g.setColor(background); + } int[] px, py; if (tabPlacement == TOP) { @@ -324,6 +377,8 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI else throw new AssertionError("Unrecognised 'tabPlacement' argument."); g.fillPolygon(px, py, 5); + hg = g; + paintHighlightBelowTab(); } /** @@ -342,5 +397,94 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI // (which is drawn at the very top for tabPlacement == TOP) return run < this.runCount - 1; } + + /** + * Installs the defaults for this UI. This method calls super.installDefaults + * and then loads the Metal specific defaults for TabbedPane. + */ + protected void installDefaults() + { + super.installDefaults(); + selectColor = UIManager.getColor("TabbedPane.selected"); + selectHighlight = UIManager.getColor("TabbedPane.selectHighlight"); + tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground"); + minTabWidth = 0; + } + /** + * Returns the color for the gap. + * + * @param currentRun - The current run to return the color for + * @param x - The x position of the current run + * @param y - The y position of the current run + * + * @return the color for the gap in the current run. + */ + protected Color getColorForGap(int currentRun, int x, int y) + { + int index = tabForCoordinate(tabPane, x, y); + int selected = tabPane.getSelectedIndex(); + if (selected == index) + return selectColor; + return tabAreaBackground; + } + + /** + * Returns true if the gap should be filled in. + * + * @param currentRun - The current run + * @param tabIndex - The current tab + * @param x - The x position of the tab + * @param y - The y position of the tab + * + * @return true if the gap at the current run should be filled + */ + protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y) + { + // As far as I can tell, the gap is never filled in. + return false; + } + + /** + * Paints the highlight below the tab, if there is one. + */ + protected void paintHighlightBelowTab() + { + int selected = tabPane.getSelectedIndex(); + int tabPlacement = tabPane.getTabPlacement(); + Rectangle bounds = getTabBounds(tabPane, selected); + + hg.setColor(selectColor); + int x = bounds.x; + int y = bounds.y; + int w = bounds.width; + int h = bounds.height; + + if (tabPlacement == TOP) + hg.fillRect(x, y + h - 2, w, 30); + else if (tabPlacement == LEFT) + hg.fillRect(x + w - 1, y, 20, h); + else if (tabPlacement == BOTTOM) + hg.fillRect(x, y - h + 2, w, 30); + else if (tabPlacement == RIGHT) + hg.fillRect(x - 18, y, 20, h); + else + throw new AssertionError("Unrecognised 'tabPlacement' argument."); + hg = null; + } + + /** + * Returns true if we should rotate the tab runs. + * + * @param tabPlacement - The current tab placement. + * @param selectedRun - The selected run. + * + * @return true if the tab runs should be rotated. + */ + protected boolean shouldRotateTabRuns(int tabPlacement, + int selectedRun) + { + // false because tab runs are not rotated in the MetalLookAndFeel + return false; + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java index 6984daeccbe..30738b37c72 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.beans.PropertyChangeEvent; + import javax.swing.JComponent; import javax.swing.JTextField; import javax.swing.plaf.ComponentUI; @@ -67,4 +69,14 @@ public class MetalTextFieldUI extends BasicTextFieldUI { return new MetalTextFieldUI(); } + + /** + * This method gets called when a bound property is changed on the associated + * JTextComponent. This is a hook which UI implementations may change to + * reflect how the UI displays bound properties of JTextComponent subclasses. + */ + public void propertyChange(PropertyChangeEvent evt) + { + super.propertyChange(evt); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java index c5ca91399ab..16e22ac5286 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java @@ -38,12 +38,16 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Point; import java.awt.event.ContainerListener; +import java.awt.event.MouseEvent; + import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JToolBar; import javax.swing.border.Border; +import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToolBarUI; @@ -158,5 +162,67 @@ public class MetalToolBarUI extends BasicToolBarUI { return MetalBorders.getToolbarButtonBorder(); } - + + /** + * Sets the offset for the window used for dragging the toolbar. + * It is set as long as the window is not null (it has been installed). + */ + protected void setDragOffset(Point p) + { + if (dragWindow != null) + dragWindow.setOffset(p); + } + + /** + * Creates and returns an instance of MetalDockingListener. + * + * @return an instance of MetalDockingListener. + */ + protected MouseInputListener createDockingListener() + { + return new MetalDockingListener(toolBar); + } + + /** + * This is the MouseHandler class that allows the user to drag the JToolBar + * in and out of the parent and dock it if it can. + */ + protected class MetalDockingListener extends BasicToolBarUI.DockingListener + { + /** + * Creates a new DockingListener object. + * + * @param t The JToolBar this DockingListener is being used for. + */ + public MetalDockingListener(JToolBar t) + { + super(t); + } + + /** + * This method is called when the mouse is pressed in the JToolBar. If the + * press doesn't occur in a place where it causes the JToolBar to be + * dragged, it returns. Otherwise, it starts a drag session. + * + * @param e The MouseEvent. + */ + public void mousePressed(MouseEvent e) + { + super.mousePressed(e); + setDragOffset(new Point(e.getX(), e.getY())); + } + + /** + * This method is called when the mouse is dragged. It delegates the drag + * painting to the dragTo method. + * + * @param e The MouseEvent. + */ + public void mouseDragged(MouseEvent e) + { + // Does not do anything differently than dragging + // BasicToolBarUI.DockingListener + super.mouseDragged(e); + } + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java index f1886b167cf..d1fc4cfecde 100644 --- a/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java +++ b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java @@ -238,6 +238,10 @@ public class OceanTheme extends DefaultMetalTheme {new Double(0.3), new Double(0.0), new ColorUIResource(221, 232, 243), new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)})); + defaults.put("Button.rollover", Boolean.TRUE); + + defaults.put("TabbedPane.selected", new ColorUIResource(200, 221, 242)); + defaults.put("TabbedPane.unselectedBackground", SECONDARY3); } } |