diff options
author | Guilhem Lavaux <guilhem@kaffe.org> | 2006-06-06 20:30:07 +0000 |
---|---|---|
committer | Guilhem Lavaux <guilhem@kaffe.org> | 2006-06-06 20:30:07 +0000 |
commit | 56c5b96a2d3754736d13eebb73270fb8f925301d (patch) | |
tree | 8f13d21d9dadf9810d11284977ed98a0a5c41868 /javax/swing/plaf | |
parent | 68d82e8b935385c52f40123538e6e5b0dad7dbc9 (diff) | |
download | classpath-56c5b96a2d3754736d13eebb73270fb8f925301d.tar.gz |
2006-06-06 Guilhem Lavaux <guilhem@kaffe.org>
* Merged HEAD as of 2006-05-08.
Diffstat (limited to 'javax/swing/plaf')
51 files changed, 4007 insertions, 2388 deletions
diff --git a/javax/swing/plaf/basic/BasicButtonListener.java b/javax/swing/plaf/basic/BasicButtonListener.java index 1fca69451..89e99a29a 100644 --- a/javax/swing/plaf/basic/BasicButtonListener.java +++ b/javax/swing/plaf/basic/BasicButtonListener.java @@ -52,6 +52,7 @@ import javax.swing.AbstractAction; import javax.swing.AbstractButton; import javax.swing.ButtonModel; import javax.swing.JComponent; +import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -204,14 +205,12 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, { AbstractButton button = (AbstractButton) e.getSource(); ButtonModel model = button.getModel(); - if (button.isRolloverEnabled()) + if (button.isRolloverEnabled() + && ! SwingUtilities.isLeftMouseButton(e)) model.setRollover(true); - - if (model.isPressed() - && (e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) + + if (model.isPressed()) model.setArmed(true); - else - model.setArmed(false); } } diff --git a/javax/swing/plaf/basic/BasicButtonUI.java b/javax/swing/plaf/basic/BasicButtonUI.java index ab079fc5e..7dbcb9146 100644 --- a/javax/swing/plaf/basic/BasicButtonUI.java +++ b/javax/swing/plaf/basic/BasicButtonUI.java @@ -172,8 +172,10 @@ public class BasicButtonUI extends ButtonUI { if (b.getFont() instanceof UIResource) b.setFont(null); - b.setForeground(null); - b.setBackground(null); + if (b.getForeground() instanceof UIResource) + b.setForeground(null); + if (b.getBackground() instanceof UIResource) + b.setBackground(null); if (b.getBorder() instanceof UIResource) b.setBorder(null); b.setIconTextGap(defaultTextIconGap); diff --git a/javax/swing/plaf/basic/BasicComboBoxEditor.java b/javax/swing/plaf/basic/BasicComboBoxEditor.java index 4cbc4ae68..d87926196 100644 --- a/javax/swing/plaf/basic/BasicComboBoxEditor.java +++ b/javax/swing/plaf/basic/BasicComboBoxEditor.java @@ -39,14 +39,10 @@ exception statement from your version. */ package javax.swing.plaf.basic; import java.awt.Component; -import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.util.Iterator; -import java.util.LinkedList; - import javax.swing.ComboBoxEditor; import javax.swing.JTextField; @@ -62,8 +58,6 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, /** The editor component. */ protected JTextField editor; - private ComboBoxEditorListener listener; - /** * Creates a new <code>BasicComboBoxEditor</code> instance. */ @@ -72,7 +66,6 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, editor = new JTextField(); editor.setBorder(null); editor.setColumns(9); - listener = new ComboBoxEditorListener(); } /** @@ -156,7 +149,7 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, */ public void addActionListener(ActionListener l) { - listener.addListener(l); + editor.addActionListener(l); } /** @@ -166,7 +159,7 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, */ public void removeActionListener(ActionListener l) { - listener.removeListener(l); + editor.removeActionListener(l); } /** @@ -185,37 +178,4 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, } } - /** - * Helper class that forwards action events between the jtextfield - * editor and the basic combo box editor for use by the combo box. - */ - class ComboBoxEditorListener implements ActionListener - { - private final LinkedList listeners = new LinkedList(); - - public void actionPerformed(ActionEvent ae) - { - ActionEvent nae; - nae = new ActionEvent(BasicComboBoxEditor.this, - ae.getID(), ae.getActionCommand(), - ae.getWhen(), ae.getModifiers()); - Iterator it = listeners.iterator(); - while (it.hasNext()) - ((ActionListener) it.next()).actionPerformed(nae); - } - - void addListener(ActionListener al) - { - if (listeners.size() == 0) - editor.addActionListener(this); - listeners.add(al); - } - - void removeListener(ActionListener al) - { - listeners.remove(al); - if (listeners.size() == 0) - editor.removeActionListener(this); - } - } } diff --git a/javax/swing/plaf/basic/BasicComboBoxUI.java b/javax/swing/plaf/basic/BasicComboBoxUI.java index 5b183786f..557eea93f 100644 --- a/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -70,7 +72,6 @@ import javax.swing.JComponent; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.LookAndFeel; -import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; @@ -672,9 +673,7 @@ public class BasicComboBoxUI extends ComboBoxUI */ public Dimension getPreferredSize(JComponent c) { - Dimension size = getMinimumSize(c); - size.width += 4; - return size; + return getMinimumSize(c); } /** @@ -691,10 +690,8 @@ public class BasicComboBoxUI extends ComboBoxUI { Insets i = getInsets(); Dimension d = getDisplaySize(); - d.height += i.top + i.bottom; - int arrowButtonWidth = d.height - (i.top + i.bottom); - cachedMinimumSize = new Dimension(d.width + arrowButtonWidth + i.left - + i.right, d.height); + d.width += i.left + i.right + d.height; + cachedMinimumSize = new Dimension(d.width, d.height + i.top + i.bottom); isMinimumSizeDirty = false; } return new Dimension(cachedMinimumSize); @@ -710,9 +707,7 @@ public class BasicComboBoxUI extends ComboBoxUI */ public Dimension getMaximumSize(JComponent c) { - Dimension size = getPreferredSize(c); - size.width = 32767; - return size; + return new Dimension(32767, 32767); } public int getAccessibleChildrenCount(JComponent c) @@ -780,11 +775,16 @@ public class BasicComboBoxUI extends ComboBoxUI */ protected Rectangle rectangleForCurrentValue() { - Rectangle cbBounds = SwingUtilities.getLocalBounds(comboBox); - Rectangle abBounds = arrowButton.getBounds(); - Rectangle rectForCurrentValue = new Rectangle(cbBounds.x, cbBounds.y, - cbBounds.width - abBounds.width, cbBounds.height); - return rectForCurrentValue; + int w = comboBox.getWidth(); + int h = comboBox.getHeight(); + Insets i = comboBox.getInsets(); + int arrowSize = h - (i.top + i.bottom); + if (arrowButton != null) + { + arrowSize = arrowButton.getWidth(); + } + return new Rectangle(i.left, i.top, w - (i.left + i.right + arrowSize), + h - (i.top + i.left)); } /** @@ -902,9 +902,9 @@ public class BasicComboBoxUI extends ComboBoxUI false); currentValuePane.add(comp); comp.setFont(comboBox.getFont()); - int h = comp.getPreferredSize().height; + Dimension d = comp.getPreferredSize(); currentValuePane.remove(comp); - return new Dimension(100, h); + return d; } /** @@ -915,37 +915,58 @@ public class BasicComboBoxUI extends ComboBoxUI */ protected Dimension getDisplaySize() { - if (comboBox.isEditable() && comboBox.getModel().getSize() == 0) - return new Dimension(100, editor.getPreferredSize().height); - Dimension dim = new Dimension(); ListCellRenderer renderer = comboBox.getRenderer(); - ComboBoxModel model = comboBox.getModel(); - if (renderer != null && model.getSize() > 0) + if (renderer == null) { - // TODO: Optimize using prototype here. + renderer = DEFAULT_RENDERER; + } + + Object prototype = comboBox.getPrototypeDisplayValue(); + if (prototype != null) + { + Component comp = renderer.getListCellRendererComponent + (listBox, prototype, -1, false, false); + currentValuePane.add(comp); + comp.setFont(comboBox.getFont()); + Dimension renderSize = comp.getPreferredSize(); + currentValuePane.remove(comp); + dim.height = renderSize.height; + dim.width = renderSize.width; + } + else + { + ComboBoxModel model = comboBox.getModel(); int size = model.getSize(); - for (int i = 0; i < size; ++i) + if (size > 0) { - Component comp = renderer.getListCellRendererComponent - (listBox, model.getElementAt(i), -1, false, false); - currentValuePane.add(comp); - comp.setFont(comboBox.getFont()); - Dimension renderSize = comp.getPreferredSize(); - currentValuePane.remove(comp); - dim.width = Math.max(dim.width, renderSize.width); - dim.height = Math.max(dim.height, renderSize.height); + for (int i = 0; i < size; ++i) + { + Component comp = renderer.getListCellRendererComponent + (listBox, model.getElementAt(i), -1, false, false); + currentValuePane.add(comp); + comp.setFont(comboBox.getFont()); + Dimension renderSize = comp.getPreferredSize(); + currentValuePane.remove(comp); + dim.width = Math.max(dim.width, renderSize.width); + dim.height = Math.max(dim.height, renderSize.height); + } } - if (comboBox.isEditable()) + else { - Dimension editSize = editor.getPreferredSize(); - dim.width = Math.max(dim.width, editSize.width); - dim.height = Math.max(dim.height, editSize.height); + dim = getDefaultSize(); + if (comboBox.isEditable()) + dim.width = 100; } - displaySize.setSize(dim.width, dim.height); - return displaySize; } - return getDefaultSize(); + if (comboBox.isEditable()) + { + Dimension editSize = editor.getPreferredSize(); + dim.width = Math.max(dim.width, editSize.width); + dim.height = Math.max(dim.height, editSize.height); + } + displaySize.setSize(dim.width, dim.height); + return dim; } /** @@ -953,6 +974,7 @@ public class BasicComboBoxUI extends ComboBoxUI * by the look and feel. */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: Need to implement. } @@ -962,6 +984,7 @@ public class BasicComboBoxUI extends ComboBoxUI * installed by in {@link #installListeners}. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: Need to implement. } @@ -1046,12 +1069,11 @@ public class BasicComboBoxUI extends ComboBoxUI int arrowSize = comboBox.getHeight() - (i.top + i.bottom); int editorWidth = comboBox.getBounds().width - arrowSize; - if (editor != null) - editor.setBounds(rectangleForCurrentValue()); - if (arrowButton != null) arrowButton.setBounds(comboBox.getWidth() - (i.right + arrowSize), i.top, arrowSize, arrowSize); + if (editor != null) + editor.setBounds(rectangleForCurrentValue()); } } diff --git a/javax/swing/plaf/basic/BasicComboPopup.java b/javax/swing/plaf/basic/BasicComboPopup.java index aa058b87d..d4eabc602 100644 --- a/javax/swing/plaf/basic/BasicComboPopup.java +++ b/javax/swing/plaf/basic/BasicComboPopup.java @@ -38,9 +38,12 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Color; import java.awt.Component; import java.awt.Dimension; +import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ItemEvent; @@ -185,6 +188,8 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { Dimension size = comboBox.getSize(); size.height = getPopupHeightForRowCount(comboBox.getMaximumRowCount()); + Insets i = getInsets(); + size.width -= i.left + i.right; Rectangle bounds = computePopupBounds(0, comboBox.getBounds().height, size.width, size.height); @@ -197,7 +202,8 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup list.ensureIndexIsVisible(list.getSelectedIndex()); setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled()); - show(comboBox, bounds.x, bounds.y); } + show(comboBox, bounds.x, bounds.y); + } /** * This method hides drop down list of items @@ -288,6 +294,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup * This method uninstalls keyboard actions installed by the UI. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: Need to implement } @@ -555,6 +562,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup * DOCUMENT ME! */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: Need to implement } @@ -710,7 +718,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup totalHeight += dim.height; } - return totalHeight; + return totalHeight == 0 ? 100 : totalHeight; } /** diff --git a/javax/swing/plaf/basic/BasicFileChooserUI.java b/javax/swing/plaf/basic/BasicFileChooserUI.java index 30e3156b4..daa977083 100644 --- a/javax/swing/plaf/basic/BasicFileChooserUI.java +++ b/javax/swing/plaf/basic/BasicFileChooserUI.java @@ -157,6 +157,21 @@ public class BasicFileChooserUI extends FileChooserUI closeDialog(); } } + else + { + File f = new File(filechooser.getCurrentDirectory(), getFileName()); + if (filechooser.isTraversable(f)) + { + filechooser.setCurrentDirectory(f); + filechooser.rescanCurrentDirectory(); + } + else + { + filechooser.setSelectedFile(f); + filechooser.approveSelection(); + closeDialog(); + } + } } } @@ -1046,9 +1061,7 @@ public class BasicFileChooserUI extends FileChooserUI */ public String getFileName() { - // FIXME: I'm thinking that this method just provides access to the - // text value in the JTextField component...but not sure yet - return null; //filename; + return entry.getText(); } /** diff --git a/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java b/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java index d7e26669f..11980f6ca 100644 --- a/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java +++ b/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java @@ -520,22 +520,22 @@ public class BasicInternalFrameTitlePane extends JComponent } /** The action command for the Close action. */ - protected static final String CLOSE_CMD = "Close"; + protected static final String CLOSE_CMD; /** The action command for the Minimize action. */ - protected static final String ICONIFY_CMD = "Minimize"; + protected static final String ICONIFY_CMD; /** The action command for the Maximize action. */ - protected static final String MAXIMIZE_CMD = "Maximize"; + protected static final String MAXIMIZE_CMD; /** The action command for the Move action. */ - protected static final String MOVE_CMD = "Move"; + protected static final String MOVE_CMD; /** The action command for the Restore action. */ - protected static final String RESTORE_CMD = "Restore"; + protected static final String RESTORE_CMD; /** The action command for the Size action. */ - protected static final String SIZE_CMD = "Size"; + protected static final String SIZE_CMD; /** The action associated with closing the JInternalFrame. */ protected Action closeAction; @@ -614,6 +614,17 @@ public class BasicInternalFrameTitlePane extends JComponent * This is package-private to avoid an accessor method. */ transient JLabel title; + + static + { + // not constants in JDK + CLOSE_CMD = "Close"; + ICONIFY_CMD = "Minimize"; + MAXIMIZE_CMD = "Maximize"; + MOVE_CMD = "Move"; + RESTORE_CMD = "Restore"; + SIZE_CMD = "Size"; + } /** * Creates a new BasicInternalFrameTitlePane object that is used in the diff --git a/javax/swing/plaf/basic/BasicInternalFrameUI.java b/javax/swing/plaf/basic/BasicInternalFrameUI.java index c6f3dd99e..0a330e776 100644 --- a/javax/swing/plaf/basic/BasicInternalFrameUI.java +++ b/javax/swing/plaf/basic/BasicInternalFrameUI.java @@ -38,10 +38,13 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.AWTEvent; import java.awt.Color; import java.awt.Component; import java.awt.Container; +import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Insets; @@ -164,6 +167,12 @@ public class BasicInternalFrameUI extends InternalFrameUI protected class BorderListener extends MouseInputAdapter implements SwingConstants { + /** + * If true, the cursor is being already shown in the alternative "resize" + * shape. + */ + transient boolean showingResizeCursor; + /** FIXME: Use for something. */ protected final int RESIZE_NONE = 0; @@ -263,25 +272,69 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse exits the JInternalFrame. - * + * * @param e The MouseEvent. */ public void mouseExited(MouseEvent e) { - // There is nothing to do when the mouse exits - // the border area. + // Reset the cursor shape. + if (showingResizeCursor) + { + frame.setCursor(Cursor.getDefaultCursor()); + showingResizeCursor = false; + } } /** - * This method is called when the mouse is moved inside the - * JInternalFrame. - * + * This method is called when the mouse is moved inside the JInternalFrame. + * * @param e The MouseEvent. */ public void mouseMoved(MouseEvent e) { - // There is nothing to do when the mouse moves - // over the border area. + // Turn off the resize cursor if we are in the frame header. + if (showingResizeCursor && e.getSource() != frame) + { + frame.setCursor(Cursor.getDefaultCursor()); + showingResizeCursor = false; + } + else if (e.getSource()==frame && frame.isResizable()) + { + int cursor; + switch (sectionOfClick(e.getX(), e.getY())) + { + case NORTH: + cursor = Cursor.N_RESIZE_CURSOR; + break; + case NORTH_EAST: + cursor = Cursor.NE_RESIZE_CURSOR; + break; + case EAST: + cursor = Cursor.E_RESIZE_CURSOR; + break; + case SOUTH_EAST: + cursor = Cursor.SE_RESIZE_CURSOR; + break; + case SOUTH: + cursor = Cursor.S_RESIZE_CURSOR; + break; + case SOUTH_WEST: + cursor = Cursor.SW_RESIZE_CURSOR; + break; + case WEST: + cursor = Cursor.W_RESIZE_CURSOR; + break; + case NORTH_WEST: + cursor = Cursor.NW_RESIZE_CURSOR; + break; + default: + cursor = Cursor.DEFAULT_CURSOR; + } + + Cursor resize = Cursor.getPredefinedCursor(cursor); + frame.setCursor(resize); + showingResizeCursor = true; + } } /** @@ -1158,6 +1211,7 @@ public class BasicInternalFrameUI extends InternalFrameUI * This method installs the keyboard actions for the JInternalFrame. */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: Implement. } @@ -1240,6 +1294,7 @@ public class BasicInternalFrameUI extends InternalFrameUI * This method uninstalls the keyboard actions for the JInternalFrame. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: Implement. } diff --git a/javax/swing/plaf/basic/BasicLabelUI.java b/javax/swing/plaf/basic/BasicLabelUI.java index d0964f473..60e3a9868 100644 --- a/javax/swing/plaf/basic/BasicLabelUI.java +++ b/javax/swing/plaf/basic/BasicLabelUI.java @@ -37,6 +37,8 @@ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Color; import java.awt.Dimension; import java.awt.FontMetrics; @@ -372,6 +374,7 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener * @param l The {@link JLabel} to install keyboard actions for. */ protected void installKeyboardActions(JLabel l) + throws NotImplementedException { //FIXME: implement. } @@ -382,6 +385,7 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener * @param l The {@link JLabel} to uninstall keyboard actions for. */ protected void uninstallKeyboardActions(JLabel l) + throws NotImplementedException { //FIXME: implement. } diff --git a/javax/swing/plaf/basic/BasicListUI.java b/javax/swing/plaf/basic/BasicListUI.java index 5cde9630c..d9bc0676d 100644 --- a/javax/swing/plaf/basic/BasicListUI.java +++ b/javax/swing/plaf/basic/BasicListUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; @@ -1025,6 +1027,7 @@ public class BasicListUI extends ListUI * Uninstalls keyboard actions for this UI in the {@link JList}. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // TODO: Implement this properly. } diff --git a/javax/swing/plaf/basic/BasicLookAndFeel.java b/javax/swing/plaf/basic/BasicLookAndFeel.java index 3451224be..78c16ef08 100644 --- a/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -44,6 +44,7 @@ import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; +import java.awt.SystemColor; import java.awt.Toolkit; import java.awt.event.AWTEventListener; import java.awt.event.ActionEvent; @@ -52,10 +53,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.Enumeration; -import java.util.Iterator; import java.util.ResourceBundle; -import java.util.Set; -import java.util.WeakHashMap; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; @@ -66,11 +64,9 @@ import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.BorderFactory; -import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.MenuSelectionManager; -import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.BevelBorder; @@ -104,11 +100,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel { /** - * Registered popups for autoclose. - */ - private WeakHashMap autoClosePopups = new WeakHashMap(); - - /** * Receives an event from the event queue. * * @param event @@ -137,46 +128,8 @@ public abstract class BasicLookAndFeel extends LookAndFeel target = ((Container) target).findComponentAt(ev.getPoint()); if (! m.isComponentPartOfCurrentMenu(target)) m.clearSelectedPath(); - - // Handle other registered popup instances, like ComboBox popups. - autoClosePopups(ev, target); - } - - /** - * Registers Popup and its content to be autoclosed when a mouseclick - * occurs outside of the popup. - * - * @param popup the popup to be autoclosed when clicked outside - */ - void registerForAutoClose(JPopupMenu popup) - { - autoClosePopups.put(popup, null); } - /** - * Automatically closes all popups that are not 'hit' by the mouse event. - * - * @param ev the mouse event - * @param target the target of the mouse event - */ - private void autoClosePopups(MouseEvent ev, Component target) - { - if (autoClosePopups.size() != 0) - { - Set popups = autoClosePopups.keySet(); - Iterator i = popups.iterator(); - while (i.hasNext()) - { - JPopupMenu popup = (JPopupMenu) i.next(); - if (!(target == popup - || SwingUtilities.isDescendingFrom(target, popup))) - { - popup.setVisible(false); - i.remove(); - } - } - } - } } /** @@ -251,7 +204,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel */ public BasicLookAndFeel() { - // TODO + // Nothing to do here. } /** @@ -337,59 +290,138 @@ public abstract class BasicLookAndFeel extends LookAndFeel /** * Populates the <code>defaults</code> table with system color defaults. + * + * This sets up a couple of default values and passes them to + * {@link #loadSystemColors(UIDefaults, String[], boolean)}. If the + * look and feel is a native look and feel, these defaults may be overridden + * by the corresponding SystemColor constants. * * @param defaults the defaults table (<code>null</code> not permitted). */ protected void initSystemColorDefaults(UIDefaults defaults) { - Color highLight = new Color(249, 247, 246); - Color light = new Color(239, 235, 231); - Color shadow = new Color(139, 136, 134); - Color darkShadow = new Color(16, 16, 16); - - Object[] uiDefaults; - uiDefaults = new Object[] { - "activeCaption", new ColorUIResource(0, 0, 128), - "activeCaptionBorder", new ColorUIResource(Color.lightGray), - "activeCaptionText", new ColorUIResource(Color.white), - "control", new ColorUIResource(light), - "controlDkShadow", new ColorUIResource(shadow), - "controlHighlight", new ColorUIResource(highLight), - "controlLtHighlight", new ColorUIResource(highLight), - "controlShadow", new ColorUIResource(shadow), - "controlText", new ColorUIResource(darkShadow), - "desktop", new ColorUIResource(0, 92, 92), - "inactiveCaption", new ColorUIResource(Color.gray), - "inactiveCaptionBorder", new ColorUIResource(Color.lightGray), - "inactiveCaptionText", new ColorUIResource(Color.lightGray), - "info", new ColorUIResource(light), - "infoText", new ColorUIResource(darkShadow), - "menu", new ColorUIResource(light), - "menuText", new ColorUIResource(darkShadow), - "scrollbar", new ColorUIResource(light), - "text", new ColorUIResource(Color.white), - "textHighlight", new ColorUIResource(Color.black), - "textHighlightText", new ColorUIResource(Color.white), - "textInactiveText", new ColorUIResource(Color.gray), - "textText", new ColorUIResource(Color.black), - "window", new ColorUIResource(light), - "windowBorder", new ColorUIResource(Color.black), - "windowText", new ColorUIResource(darkShadow) + String[] defaultColors = new String[] { + "activeCaption", "#000080", + "activeCaptionBorder", "#C0C0C0", + "activeCaptionText", "#FFFFFF", + "control", "#C0C0C0", + "controlDkShadow", "#000000", + "controlHighlight", "#C0C0C0", + "controlLtHighlight", "#FFFFFF", + "controlShadow", "#808080", + "controlText", "#000000", + "desktop", "#005C5C", + "inactiveCaption", "#808080", + "inactiveCaptionBorder", "#C0C0C0", + "inactiveCaptionText", "#C0C0C0", + "info", "#FFFFE1", + "infoText", "#000000", + "menu", "#C0C0C0", + "menuText", "#000000", + "scrollbar", "#E0E0E0", + "text", "#C0C0C0", + "textHighlight", "#000080", + "textHighlightText", "#FFFFFF", + "textInactiveText", "#808080", + "textText", "#000000", + "window", "#FFFFFF", + "windowBorder", "#000000", + "windowText", "#000000" }; - defaults.putDefaults(uiDefaults); + loadSystemColors(defaults, defaultColors, isNativeLookAndFeel()); } /** - * Loads the system colors. This method is not implemented yet. - * + * Populates the <code>defaults</code> table with the system colors. If + * <code>useNative</code> is <code>true</code>, the table is populated + * with the constants in {@link SystemColor}, otherwise the + * <code>systemColors</code> parameter is decoded into the defaults table. + * The system colors array is made up of pairs, where the first entry is the + * name of the system color, and the second entry is a string denoting + * an RGB color value like "#C0C0C0", which is decoded using + * {@link Color#decode(String)}. + * * @param defaults the defaults table (<code>null</code> not permitted). - * @param systemColors TODO - * @param useNative TODO + * @param systemColors defaults to use when <code>useNative</code> is + * <code>false</code> + * @param useNative when <code>true</code>, installs the values of the + * SystemColor constants, when <code>false</code>, install the values + * from <code>systemColors</code> */ protected void loadSystemColors(UIDefaults defaults, String[] systemColors, boolean useNative) { - // TODO + if (useNative) + { + defaults.put("activeCaption", + new ColorUIResource(SystemColor.ACTIVE_CAPTION)); + defaults.put("activeCaptionBorder", + new ColorUIResource(SystemColor.ACTIVE_CAPTION_BORDER)); + defaults.put("activeCaptionText", + new ColorUIResource(SystemColor.ACTIVE_CAPTION_TEXT)); + defaults.put("control", + new ColorUIResource(SystemColor.CONTROL)); + defaults.put("controlDkShadow", + new ColorUIResource(SystemColor.CONTROL_DK_SHADOW)); + defaults.put("controlHighlight", + new ColorUIResource(SystemColor.CONTROL_HIGHLIGHT)); + defaults.put("controlLtHighlight", + new ColorUIResource(SystemColor.CONTROL_LT_HIGHLIGHT)); + defaults.put("controlShadow", + new ColorUIResource(SystemColor.CONTROL_SHADOW)); + defaults.put("controlText", + new ColorUIResource(SystemColor.CONTROL_TEXT)); + defaults.put("desktop", + new ColorUIResource(SystemColor.DESKTOP)); + defaults.put("inactiveCaption", + new ColorUIResource(SystemColor.INACTIVE_CAPTION)); + defaults.put("inactiveCaptionBorder", + new ColorUIResource(SystemColor.INACTIVE_CAPTION_BORDER)); + defaults.put("inactiveCaptionText", + new ColorUIResource(SystemColor.INACTIVE_CAPTION_TEXT)); + defaults.put("info", + new ColorUIResource(SystemColor.INFO)); + defaults.put("infoText", + new ColorUIResource(SystemColor.INFO_TEXT)); + defaults.put("menu", + new ColorUIResource(SystemColor.MENU)); + defaults.put("menuText", + new ColorUIResource(SystemColor.MENU_TEXT)); + defaults.put("scrollbar", + new ColorUIResource(SystemColor.SCROLLBAR)); + defaults.put("text", + new ColorUIResource(SystemColor.TEXT)); + defaults.put("textHighlight", + new ColorUIResource(SystemColor.TEXT_HIGHLIGHT)); + defaults.put("textHighlightText", + new ColorUIResource(SystemColor.TEXT_HIGHLIGHT_TEXT)); + defaults.put("textInactiveText", + new ColorUIResource(SystemColor.TEXT_INACTIVE_TEXT)); + defaults.put("textText", + new ColorUIResource(SystemColor.TEXT_TEXT)); + defaults.put("window", + new ColorUIResource(SystemColor.WINDOW)); + defaults.put("windowBorder", + new ColorUIResource(SystemColor.WINDOW_BORDER)); + defaults.put("windowText", + new ColorUIResource(SystemColor.WINDOW_TEXT)); + } + else + { + for (int i = 0; i < systemColors.length; i += 2) + { + Color color = Color.BLACK; + try + { + color = Color.decode(systemColors[i + 1]); + } + catch (NumberFormatException e) + { + e.printStackTrace(); + } + defaults.put(systemColors[i], new ColorUIResource(color)); + } + } } /** @@ -1162,6 +1194,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TabbedPane.shadow", new ColorUIResource(shadow), "TabbedPane.tabbedPaneContentBorderInsets", new InsetsUIResource(3, 2, 1, 2), "TabbedPane.tabbedPaneTabPadInsets", new InsetsUIResource(1, 1, 1, 1), + "TabbedPane.tabsOpaque", Boolean.TRUE, "TabbedPane.tabAreaInsets", new InsetsUIResource(3, 2, 0, 2), "TabbedPane.tabInsets", new InsetsUIResource(0, 4, 1, 4), "TabbedPane.tabRunOverlay", new Integer(2), @@ -1648,17 +1681,4 @@ public abstract class BasicLookAndFeel extends LookAndFeel toolkit.removeAWTEventListener(popupHelper); popupHelper = null; } - - /** - * Registers a JPopupMenu for autoclosing when a mouseclick occurs outside - * of the JPopupMenu. This must be called when the popup gets opened. The - * popup is unregistered from autoclosing as soon as it either got closed - * by this helper, or when it has been garbage collected. - * - * @param popup the popup menu to autoclose - */ - void registerForAutoClose(JPopupMenu popup) - { - popupHelper.registerForAutoClose(popup); - } } diff --git a/javax/swing/plaf/basic/BasicMenuBarUI.java b/javax/swing/plaf/basic/BasicMenuBarUI.java index a21514467..f258ebe30 100644 --- a/javax/swing/plaf/basic/BasicMenuBarUI.java +++ b/javax/swing/plaf/basic/BasicMenuBarUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Dimension; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; @@ -176,6 +178,7 @@ public class BasicMenuBarUI extends MenuBarUI * This method installs the keyboard actions for the JMenuBar. */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: implement } @@ -223,6 +226,7 @@ public class BasicMenuBarUI extends MenuBarUI * This method reverses the work done in installKeyboardActions. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: implement. } diff --git a/javax/swing/plaf/basic/BasicMenuItemUI.java b/javax/swing/plaf/basic/BasicMenuItemUI.java index 9166c49ee..69c9c4507 100644 --- a/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -40,6 +40,7 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Component; +import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; @@ -82,6 +83,7 @@ import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentInputMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.MenuItemUI; +import javax.swing.text.View; /** * UI Delegate for JMenuItem. @@ -183,7 +185,43 @@ public class BasicMenuItemUI extends MenuItemUI /** A PropertyChangeListener to make UI updates after property changes **/ PropertyChangeHandler propertyChangeListener; - + + /** + * The view rectangle used for layout of the menu item. + */ + private Rectangle viewRect; + + /** + * The rectangle that holds the area of the label. + */ + private Rectangle textRect; + + /** + * The rectangle that holds the area of the accelerator. + */ + private Rectangle accelRect; + + /** + * The rectangle that holds the area of the icon. + */ + private Rectangle iconRect; + + /** + * The rectangle that holds the area of the icon. + */ + private Rectangle arrowIconRect; + + /** + * The rectangle that holds the area of the check icon. + */ + private Rectangle checkIconRect; + + /** + * A rectangle used for temporary storage to avoid creation of new + * rectangles. + */ + private Rectangle cachedRect; + /** * A class to handle PropertChangeEvents for the JMenuItem * @author Anthony Balkissoon abalkiss at redhat dot com. @@ -242,6 +280,15 @@ public class BasicMenuItemUI extends MenuItemUI menuKeyListener = createMenuKeyListener(menuItem); itemListener = new ItemHandler(); propertyChangeListener = new PropertyChangeHandler(); + + // Initialize rectangles for layout. + viewRect = new Rectangle(); + textRect = new Rectangle(); + iconRect = new Rectangle(); + arrowIconRect = new Rectangle(); + checkIconRect = new Rectangle(); + accelRect = new Rectangle(); + cachedRect = new Rectangle(); } /** @@ -378,50 +425,69 @@ public class BasicMenuItemUI extends MenuItemUI int defaultTextIconGap) { JMenuItem m = (JMenuItem) c; - Dimension d = BasicGraphicsUtils.getPreferredButtonSize(m, - defaultTextIconGap); - - // if menu item has accelerator then take accelerator's size into account - // when calculating preferred size. - KeyStroke accelerator = m.getAccelerator(); - Rectangle rect; - - if (accelerator != null) + String accelText = getAcceleratorString(m); + + // Layout the menu item. The result gets stored in the rectangle + // fields of this class. + layoutMenuItem(m, accelText); + + // The union of the text and icon areas is the label area. + cachedRect.setBounds(textRect); + Rectangle pref = SwingUtilities.computeUnion(iconRect.x, iconRect.y, + iconRect.width, + iconRect.height, + cachedRect); + + // Find the widest menu item text and accelerator and store it in + // client properties of the parent, so that we can align the accelerators + // properly. Of course, we only need can do this, if the parent is + // a JComponent and this menu item is not a toplevel menu. + Container parent = m.getParent(); + if (parent != null && parent instanceof JComponent + && !(m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) { - rect = getAcceleratorRect( - accelerator, - m.getToolkit().getFontMetrics(acceleratorFont)); + JComponent p = (JComponent) parent; - // add width of accelerator's text - d.width += rect.width + defaultAcceleratorLabelGap; + // The widest text so far. + Integer maxTextWidth = (Integer) p.getClientProperty("maxTextWidth"); + int maxTextValue = maxTextWidth == null ? 0 : maxTextWidth.intValue(); + if (pref.width < maxTextValue) + pref.width = maxTextValue; + else + p.putClientProperty("maxTextWidth", new Integer(pref.width)); - // adjust the heigth of the preferred size if necessary - if (d.height < rect.height) - d.height = rect.height; + // The widest accelerator so far. + Integer maxAccelWidth = (Integer) p.getClientProperty("maxAccelWidth"); + int maxAccelValue = maxAccelWidth == null ? 0 + : maxAccelWidth.intValue(); + if (accelRect.width > maxAccelValue) + { + maxAccelValue = accelRect.width; + p.putClientProperty("maxAccelWidth", new Integer(accelRect.width)); + } + pref.width += maxAccelValue; + pref.width += defaultTextIconGap; } - if (checkIcon != null) + // Add arrow and check size if appropriate. + if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) { - d.width += checkIcon.getIconWidth() + defaultTextIconGap; - - if (checkIcon.getIconHeight() > d.height) - d.height = checkIcon.getIconHeight(); + pref.width += checkIconRect.width; + pref.width += defaultTextIconGap; + pref.width += arrowIconRect.width; + pref.width += defaultTextIconGap; } - if (arrowIcon != null && (c instanceof JMenu)) - { - int pWidth = m.getParent().getWidth(); - if (!((JMenu)c).isTopLevelMenu() && d.width < pWidth) - d.width = pWidth - - m.getInsets().left - m.getInsets().right; - else - d.width += arrowIcon.getIconWidth() + MenuGap; - - if (arrowIcon.getIconHeight() > d.height) - d.height = arrowIcon.getIconHeight(); - } - - return d; + // Add a gap ~2 times as wide as the defaultTextIconGap. + pref.width += 2 * defaultTextIconGap; + + // Respect the insets of the menu item. + Insets i = m.getInsets(); + pref.width += i.left + i.right; + pref.height += i.top + i.bottom; + + // Return a copy, so that nobody messes with our textRect. + return pref.getSize(); } /** @@ -541,7 +607,7 @@ public class BasicMenuItemUI extends MenuItemUI */ public void paint(Graphics g, JComponent c) { - paintMenuItem(g, c, checkIcon, arrowIcon, c.getBackground(), + paintMenuItem(g, c, checkIcon, arrowIcon, selectionBackground, c.getForeground(), defaultTextIconGap); } @@ -560,16 +626,18 @@ public class BasicMenuItemUI extends MenuItemUI // Menu item is considered to be highlighted when it is selected. // But we don't want to paint the background of JCheckBoxMenuItems ButtonModel mod = menuItem.getModel(); - if (menuItem.isContentAreaFilled()) + Color saved = g.getColor(); + if (mod.isArmed() || ((menuItem instanceof JMenu) && mod.isSelected())) { - if ((menuItem.isSelected() && checkIcon == null) || (mod != null && - mod.isArmed()) - && (menuItem.getParent() instanceof MenuElement)) - g.setColor(selectionBackground); - else - g.setColor(bgColor); + g.setColor(bgColor); + g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight()); + } + else if (menuItem.isOpaque()) + { + g.setColor(menuItem.getBackground()); g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight()); - } + } + g.setColor(saved); } /** @@ -595,87 +663,123 @@ public class BasicMenuItemUI extends MenuItemUI Color foreground, int defaultTextIconGap) { JMenuItem m = (JMenuItem) c; - Rectangle tr = new Rectangle(); // text rectangle - Rectangle ir = new Rectangle(); // icon rectangle - Rectangle vr = new Rectangle(); // view rectangle - Rectangle br = new Rectangle(); // border rectangle - Rectangle ar = new Rectangle(); // accelerator rectangle - Rectangle cr = new Rectangle(); // checkIcon rectangle - - int vertAlign = m.getVerticalAlignment(); - int horAlign = m.getHorizontalAlignment(); - int vertTextPos = m.getVerticalTextPosition(); - int horTextPos = m.getHorizontalTextPosition(); - - Font f = m.getFont(); - g.setFont(f); - FontMetrics fm = g.getFontMetrics(f); - SwingUtilities.calculateInnerArea(m, vr); + + // Fetch fonts. + Font oldFont = g.getFont(); + Font font = c.getFont(); + g.setFont(font); + FontMetrics accelFm = m.getFontMetrics(acceleratorFont); + + // Create accelerator string. + String accelText = getAcceleratorString(m); + + // Layout menu item. The result gets stored in the rectangle fields + // of this class. + layoutMenuItem(m, accelText); + + // Paint the background. paintBackground(g, m, background); - /* - * MenuItems insets are equal to menuItems margin, space between text and - * menuItems border. We need to paint insets region as well. - */ - Insets insets = m.getInsets(); - br.x -= insets.left; - br.y -= insets.top; - br.width += insets.right + insets.left; - br.height += insets.top + insets.bottom; + Color oldColor = g.getColor(); - // If this menu item is a JCheckBoxMenuItem then paint check icon + // Paint the check icon. if (checkIcon != null) { - SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign, - horAlign, vertTextPos, horTextPos, - vr, cr, tr, defaultTextIconGap); - checkIcon.paintIcon(m, g, cr.x, cr.y); - // We need to calculate position of the menu text and position of - // user menu icon if there exists one relative to the check icon. - // So we need to adjust view rectangle s.t. its starting point is at - // checkIcon.width + defaultTextIconGap. - vr.x = cr.x + cr.width + defaultTextIconGap; + checkIcon.paintIcon(m, g, checkIconRect.x, checkIconRect.y); + } + + // Paint the icon. + ButtonModel model = m.getModel(); + if (m.getIcon() != null) + { + // Determine icon depending on the menu item + // state (normal/disabled/pressed). + Icon icon; + if (! m.isEnabled()) + { + icon = m.getDisabledIcon(); + } + else if (model.isPressed() && model.isArmed()) + { + icon = m.getPressedIcon(); + if (icon == null) + { + icon = m.getIcon(); + } + } + else + { + icon = m.getIcon(); + } + + if (icon != null) + { + icon.paintIcon(m, g, iconRect.x, iconRect.y); + } } - // if this is a submenu, then paint arrow icon to indicate it. - if (arrowIcon != null && (c instanceof JMenu)) + // Paint the text. + String text = m.getText(); + if (text != null) { - if (!((JMenu) c).isTopLevelMenu()) + // Handle HTML. + View html = (View) m.getClientProperty(BasicHTML.propertyKey); + if (html != null) + { + html.paint(g, textRect); + } + else { - int width = arrowIcon.getIconWidth(); - int height = arrowIcon.getIconHeight(); - int offset = (vr.height - height) / 2; - arrowIcon.paintIcon(m, g, vr.width - width, vr.y + offset); + paintText(g, m, textRect, text); } } - // paint text and user menu icon if it exists - Icon i = m.getIcon(); - SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i, vertAlign, - horAlign, vertTextPos, horTextPos, vr, - ir, tr, defaultTextIconGap); - if (i != null) - i.paintIcon(c, g, ir.x, ir.y); - paintText(g, m, tr, m.getText()); + // Paint accelerator text. + if (! accelText.equals("")) + { + // Align the accelerator text. In getPreferredMenuItemSize() we + // store a client property 'maxAccelWidth' in the parent which holds + // the maximum accelerator width for the children of this parent. + // We use this here to align the accelerators properly. + int accelOffset = 0; + Container parent = m.getParent(); + if (parent != null && parent instanceof JComponent) + { + JComponent p = (JComponent) parent; + Integer maxAccelWidth = + (Integer) p.getClientProperty("maxAccelWidth"); + int maxAccelValue = maxAccelWidth == null ? 0 + : maxAccelWidth.intValue(); + accelOffset = maxAccelValue - accelRect.width; + } - // paint accelerator - String acceleratorText = ""; + g.setFont(acceleratorFont); + if (! m.isEnabled()) + { + // Paint accelerator disabled. + g.setColor(disabledForeground); + } + else + { + if (m.isArmed() || (m instanceof JMenu && m.isSelected())) + g.setColor(acceleratorSelectionForeground); + else + g.setColor(acceleratorForeground); + } + g.drawString(accelText, accelRect.x - accelOffset, + accelRect.y + accelFm.getAscent()); + } - if (m.getAccelerator() != null) + // Paint arrow. + if (arrowIcon != null + && ! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) { - acceleratorText = getAcceleratorText(m.getAccelerator()); - fm = g.getFontMetrics(acceleratorFont); - ar.width = fm.stringWidth(acceleratorText); - ar.x = br.width - ar.width; - vr.x = br.width - ar.width - defaultTextIconGap; - - SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null, - vertAlign, horAlign, vertTextPos, - horTextPos, vr, ir, ar, - defaultTextIconGap); - - paintAccelerator(g, m, ar, acceleratorText); + arrowIcon.paintIcon(m, g, arrowIconRect.x, arrowIconRect.y); } + + g.setFont(oldFont); + g.setColor(oldColor); + } /** @@ -860,37 +964,6 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * Paints accelerator inside menu item - * - * @param g - * The graphics context used to paint the border - * @param menuItem - * Menu item for which to draw accelerator - * @param acceleratorRect - * rectangle representing position of the accelerator relative to the - * menu item - * @param acceleratorText - * accelerator's text - */ - private void paintAccelerator(Graphics g, JMenuItem menuItem, - Rectangle acceleratorRect, - String acceleratorText) - { - g.setFont(acceleratorFont); - FontMetrics fm = g.getFontMetrics(acceleratorFont); - - if (menuItem.isEnabled()) - g.setColor(acceleratorForeground); - else - // FIXME: should fix this to use 'disabledForeground', but its - // default value in BasicLookAndFeel is null. - g.setColor(Color.gray); - - BasicGraphicsUtils.drawString(g, acceleratorText, 0, acceleratorRect.x, - acceleratorRect.y + fm.getAscent()); - } - - /** * This class handles mouse events occuring inside the menu item. Most of the * events are forwarded for processing to MenuSelectionManager of the current * menu hierarchy. @@ -1139,4 +1212,134 @@ public class BasicMenuItemUI extends MenuItemUI menuItem.repaint(); } } + + /** + * A helper method to create the accelerator string from the menu item's + * accelerator property. The returned string is empty if there is + * no accelerator defined. + * + * @param m the menu item + * + * @return the accelerator string, not null + */ + private String getAcceleratorString(JMenuItem m) + { + // Create accelerator string. + KeyStroke accel = m.getAccelerator(); + String accelText = ""; + if (accel != null) + { + int mods = accel.getModifiers(); + if (mods > 0) + { + accelText = KeyEvent.getKeyModifiersText(mods); + accelText += acceleratorDelimiter; + } + int keycode = accel.getKeyCode(); + if (keycode != 0) + accelText += KeyEvent.getKeyText(keycode); + else + accelText += accel.getKeyChar(); + } + return accelText; + } + + /** + * A helper method that lays out the menu item. The layout is stored + * in the fields of this class. + * + * @param m the menu item to layout + * @param accelText the accelerator text + */ + private void layoutMenuItem(JMenuItem m, String accelText) + { + int width = m.getWidth(); + int height = m.getHeight(); + + // Reset rectangles. + iconRect.setBounds(0, 0, 0, 0); + textRect.setBounds(0, 0, 0, 0); + accelRect.setBounds(0, 0, 0, 0); + checkIconRect.setBounds(0, 0, 0, 0); + arrowIconRect.setBounds(0, 0, 0, 0); + viewRect.setBounds(0, 0, width, height); + + // Substract insets to the view rect. + Insets insets = m.getInsets(); + viewRect.x += insets.left; + viewRect.y += insets.top; + viewRect.width -= (insets.left + insets.right); + viewRect.height -= (insets.top + insets.bottom); + + // Fetch the fonts. + Font font = m.getFont(); + FontMetrics fm = m.getFontMetrics(font); + FontMetrics accelFm = m.getFontMetrics(acceleratorFont); + + String text = m.getText(); + SwingUtilities.layoutCompoundLabel(m, fm, text, m.getIcon(), + m.getVerticalAlignment(), + m.getHorizontalAlignment(), + m.getVerticalTextPosition(), + m.getHorizontalTextPosition(), + viewRect, iconRect, textRect, + defaultTextIconGap); + + // Initialize accelerator width and height. + if (! accelText.equals("")) + { + accelRect.width = accelFm.stringWidth(accelText); + accelRect.height = accelFm.getHeight(); + } + + // Initialize check and arrow icon width and height. + if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) + { + if (checkIcon != null) + { + checkIconRect.width = checkIcon.getIconWidth(); + checkIconRect.height = checkIcon.getIconHeight(); + } + if (arrowIcon != null) + { + arrowIconRect.width = arrowIcon.getIconWidth(); + arrowIconRect.height = arrowIcon.getIconHeight(); + } + } + + // The union of the icon and text of the menu item is the 'label area'. + cachedRect.setBounds(textRect); + Rectangle labelRect = SwingUtilities.computeUnion(iconRect.x, + iconRect.y, + iconRect.width, + iconRect.height, + cachedRect); + textRect.x += defaultTextIconGap; + iconRect.x += defaultTextIconGap; + + // Layout accelerator rect. + accelRect.x = viewRect.x + viewRect.width - arrowIconRect.width + - defaultTextIconGap - accelRect.width; + // Layout check and arrow icons only when not in toplevel menu. + if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) + { + checkIconRect.x = viewRect.x + defaultTextIconGap; + textRect.x += defaultTextIconGap + checkIconRect.width; + iconRect.x += defaultTextIconGap + checkIconRect.width; + arrowIconRect.x = viewRect.x + viewRect.width - defaultTextIconGap + - arrowIconRect.width; + } + + // Align the accelerator text and all the icons vertically centered to + // the menu text. + accelRect.y = labelRect.y + (labelRect.height / 2) + - (accelRect.height / 2); + if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) + { + arrowIconRect.y = labelRect.y + (labelRect.height / 2) + - (arrowIconRect.height / 2); + checkIconRect.y = labelRect.y + (labelRect.height / 2) + - (checkIconRect.height / 2); + } + } } diff --git a/javax/swing/plaf/basic/BasicMenuUI.java b/javax/swing/plaf/basic/BasicMenuUI.java index 4fb250d24..f8936be5b 100644 --- a/javax/swing/plaf/basic/BasicMenuUI.java +++ b/javax/swing/plaf/basic/BasicMenuUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Component; import java.awt.Dimension; import java.awt.event.MouseEvent; @@ -218,6 +220,7 @@ public class BasicMenuUI extends BasicMenuItemUI * */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: Need to implement } @@ -262,6 +265,7 @@ public class BasicMenuUI extends BasicMenuItemUI * Basic look and feel's defaults. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: Need to implement } diff --git a/javax/swing/plaf/basic/BasicOptionPaneUI.java b/javax/swing/plaf/basic/BasicOptionPaneUI.java index 005a3b394..88bca3b53 100644 --- a/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -1204,6 +1206,7 @@ public class BasicOptionPaneUI extends OptionPaneUI * This method installs keyboard actions for the JOptionpane. */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: implement. } @@ -1336,6 +1339,7 @@ public class BasicOptionPaneUI extends OptionPaneUI * This method uninstalls keyboard actions for the JOptionPane. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: implement. } diff --git a/javax/swing/plaf/basic/BasicPopupMenuUI.java b/javax/swing/plaf/basic/BasicPopupMenuUI.java index 6ecd06b39..a26a5c7c4 100644 --- a/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -37,6 +37,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Component; import java.awt.Dimension; import java.awt.event.ComponentEvent; @@ -137,6 +139,7 @@ public class BasicPopupMenuUI extends PopupMenuUI * This method installs the keyboard actions for this {@link JPopupMenu}. */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: Need to implement } @@ -179,6 +182,7 @@ public class BasicPopupMenuUI extends PopupMenuUI * Uninstalls any keyboard actions. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: Need to implement } diff --git a/javax/swing/plaf/basic/BasicRadioButtonUI.java b/javax/swing/plaf/basic/BasicRadioButtonUI.java index 66e538037..a66fa28e6 100644 --- a/javax/swing/plaf/basic/BasicRadioButtonUI.java +++ b/javax/swing/plaf/basic/BasicRadioButtonUI.java @@ -45,6 +45,7 @@ import java.awt.Graphics; import java.awt.Rectangle; import javax.swing.AbstractButton; +import javax.swing.ButtonModel; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.SwingUtilities; @@ -142,14 +143,15 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI g.setFont(f); + ButtonModel m = b.getModel(); Icon currentIcon = null; - if (b.isSelected() && b.isEnabled()) + if (m.isSelected() && m.isEnabled()) currentIcon = b.getSelectedIcon(); - else if (!b.isSelected() && b.isEnabled()) + else if (! m.isSelected() && m.isEnabled()) currentIcon = b.getIcon(); - else if (b.isSelected() && !b.isEnabled()) + else if (m.isSelected() && ! m.isEnabled()) currentIcon = b.getDisabledSelectedIcon(); - else // (!b.isSelected() && !b.isEnabled()) + else // (!m.isSelected() && ! m.isEnabled()) currentIcon = b.getDisabledIcon(); SwingUtilities.calculateInnerArea(b, vr); @@ -166,7 +168,7 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI if (text != null) paintText(g, b, tr, text); // TODO: Figure out what is the size parameter? - if (b.hasFocus() && b.isFocusPainted() && b.isEnabled()) + if (b.hasFocus() && b.isFocusPainted() && m.isEnabled()) paintFocus(g, tr, null); } diff --git a/javax/swing/plaf/basic/BasicRootPaneUI.java b/javax/swing/plaf/basic/BasicRootPaneUI.java index 28e3b67c1..933db4c6b 100644 --- a/javax/swing/plaf/basic/BasicRootPaneUI.java +++ b/javax/swing/plaf/basic/BasicRootPaneUI.java @@ -38,17 +38,98 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.ButtonModel; +import javax.swing.InputMap; +import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JRootPane; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.ComponentInputMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.RootPaneUI; public class BasicRootPaneUI extends RootPaneUI implements PropertyChangeListener { + + /** + * Performed when the user activates the default button inside the JRootPane, + * usually by pressing 'ENTER'. + */ + private class DefaultPressAction + extends AbstractAction + { + /** + * The JRootPane for which this action should be installed. + */ + private JRootPane rootPane; + + /** + * Creates a new DefaultPressAction for the specified JRootPane. + */ + DefaultPressAction(JRootPane rp) + { + rootPane = rp; + } + + /** + * Performes the action. + */ + public void actionPerformed(ActionEvent ev) + { + JButton b = rootPane.getDefaultButton(); + if (b != null) + { + ButtonModel m = b.getModel(); + m.setArmed(true); + m.setPressed(true); + } + } + } + + /** + * Performed when the user activates the default button inside the JRootPane, + * usually by releasing 'ENTER'. + */ + private class DefaultReleaseAction + extends AbstractAction + { + /** + * The JRootPane for which this action should be installed. + */ + private JRootPane rootPane; + + /** + * Creates a new DefaultReleaseAction for the specified JRootPane. + */ + DefaultReleaseAction(JRootPane rp) + { + rootPane = rp; + } + + /** + * Performes the action. + */ + public void actionPerformed(ActionEvent ev) + { + JButton b = rootPane.getDefaultButton(); + if (b != null) + { + ButtonModel m = b.getModel(); + m.setPressed(false); + m.setArmed(false); + } + } + } + public static ComponentUI createUI(JComponent x) { return new BasicRootPaneUI(); @@ -107,14 +188,43 @@ public class BasicRootPaneUI extends RootPaneUI */ protected void installKeyboardActions(JRootPane rp) { - // We currently do not install any keyboard actions here. - // This method is here anyway for compatibility and to provide - // the necessary hooks to subclasses. + // Install the keyboard actions. + ActionMapUIResource am = new ActionMapUIResource(); + am.put("press", new DefaultPressAction(rp)); + am.put("release", new DefaultReleaseAction(rp)); + SwingUtilities.replaceUIActionMap(rp, am); + + // Install the input map from the UIManager. It seems like the actual + // bindings are installed in the JRootPane only when the defaultButton + // property receives a value. So we also only install an empty + // input map here, and fill it in propertyChange. + ComponentInputMapUIResource im = new ComponentInputMapUIResource(rp); + SwingUtilities.replaceUIInputMap(rp, JComponent.WHEN_IN_FOCUSED_WINDOW, + im); } public void propertyChange(PropertyChangeEvent event) { - // TODO: Implement this properly. + JRootPane source = (JRootPane) event.getSource(); + String propertyName = event.getPropertyName(); + if (propertyName.equals("defaultButton")) + { + Object newValue = event.getNewValue(); + InputMap im = + SwingUtilities.getUIInputMap(source, + JComponent.WHEN_IN_FOCUSED_WINDOW); + if (newValue != null) + { + Object[] keybindings = + (Object[]) UIManager.get + ("RootPane.defaultButtonWindowKeyBindings"); + LookAndFeel.loadKeyBindings(im, keybindings); + } + else + { + im.clear(); + } + } } /** @@ -176,6 +286,8 @@ public class BasicRootPaneUI extends RootPaneUI */ protected void uninstallKeyboardActions(JRootPane rp) { - // We do nothing here. + SwingUtilities.replaceUIActionMap(rp, null); + SwingUtilities.replaceUIInputMap(rp, JComponent.WHEN_IN_FOCUSED_WINDOW, + null); } } diff --git a/javax/swing/plaf/basic/BasicScrollBarUI.java b/javax/swing/plaf/basic/BasicScrollBarUI.java index 0ab2914ea..f24485484 100644 --- a/javax/swing/plaf/basic/BasicScrollBarUI.java +++ b/javax/swing/plaf/basic/BasicScrollBarUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -763,6 +765,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, * This method installs the keyboard actions for the scrollbar. */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: implement. } @@ -1141,6 +1144,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, * during install. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: implement. } diff --git a/javax/swing/plaf/basic/BasicScrollPaneUI.java b/javax/swing/plaf/basic/BasicScrollPaneUI.java index 71671b799..e6a4eaf4f 100644 --- a/javax/swing/plaf/basic/BasicScrollPaneUI.java +++ b/javax/swing/plaf/basic/BasicScrollPaneUI.java @@ -38,9 +38,15 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + +import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.beans.PropertyChangeEvent; @@ -53,6 +59,8 @@ import javax.swing.JViewport; import javax.swing.LookAndFeel; import javax.swing.ScrollPaneConstants; import javax.swing.ScrollPaneLayout; +import javax.swing.Scrollable; +import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; @@ -222,18 +230,149 @@ public class BasicScrollPaneUI extends ScrollPaneUI */ protected class MouseWheelHandler implements MouseWheelListener { + /** + * Use to compute the visible rectangle. + */ + final Rectangle rect = new Rectangle(); /** - * Receives notification whenever the mouse wheel is moved. - * - * @param event the mouse wheel event + * Scroll with the mouse whell. + * + * @author Audrius Meskauskas (audriusa@Bioinformatics.org) */ - public void mouseWheelMoved(MouseWheelEvent event) + public void mouseWheelMoved(MouseWheelEvent e) { - // TODO: Implement this properly. + if (scrollpane.getViewport().getComponentCount() == 0) + return; + + Component target = scrollpane.getViewport().getComponent(0); + JScrollBar bar = scrollpane.getVerticalScrollBar(); + Scrollable scrollable = (target instanceof Scrollable) ? (Scrollable) target + : null; + + boolean tracksHeight = scrollable != null + && scrollable.getScrollableTracksViewportHeight(); + int wheel = e.getWheelRotation() * ROWS_PER_WHEEL_CLICK; + int delta; + + // If possible, scroll vertically. + if (bar != null && ! tracksHeight) + { + if (scrollable != null) + { + bounds(target); + delta = scrollable.getScrollableUnitIncrement( + rect, SwingConstants.VERTICAL, wheel); + } + else + { + // Scroll non scrollables. + delta = wheel * SCROLL_NON_SCROLLABLES; + } + scroll(bar, delta); + } + // If not, try to scroll horizontally + else + { + bar = scrollpane.getHorizontalScrollBar(); + boolean tracksWidth = scrollable != null + && scrollable.getScrollableTracksViewportWidth(); + + if (bar != null && ! tracksWidth) + { + if (scrollable != null) + { + bounds(target); + delta = scrollable.getScrollableUnitIncrement( + rect, SwingConstants.HORIZONTAL, wheel); + } + else + { + // Scroll non scrollables. + delta = wheel * SCROLL_NON_SCROLLABLES; + } + scroll(bar, delta); + } + } } + + /** + * Place the component bounds into rect. The x and y values + * need to be reversed. + * + * @param target the target being scrolled + */ + final void bounds(Component target) + { + // Viewport bounds, translated by the scroll bar positions. + target.getParent().getBounds(rect); + rect.x = getValue(scrollpane.getHorizontalScrollBar()); + rect.y = getValue(scrollpane.getVerticalScrollBar()); + } + + /** + * Get the scroll bar value or null if there is no such scroll bar. + */ + final int getValue(JScrollBar bar) + { + return bar != null ? bar.getValue() : 0; + } + + /** + * Scroll the given distance. + * + * @param bar the scrollbar to scroll + * @param delta the distance + */ + final void scroll(JScrollBar bar, int delta) + { + int y = bar.getValue() + delta; + + if (y < bar.getMinimum()) + y = bar.getMinimum(); + if (y > bar.getMaximum()) + y = bar.getMaximum(); + bar.setValue(y); + } + } + + /** + * Adds/removes the mouse wheel listener when the component is added/removed + * to/from the scroll pane view port. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ + class ViewportContainerListener implements ContainerListener + { + /** + * Add the mouse wheel listener, allowing to scroll with the mouse. + */ + public void componentAdded(ContainerEvent e) + { + e.getChild().addMouseWheelListener(mouseWheelListener); + } + + /** + * Remove the mouse wheel listener. + */ + public void componentRemoved(ContainerEvent e) + { + e.getChild().removeMouseWheelListener(mouseWheelListener); + } } + + /** + * The number of pixels by that we should scroll the content that does + * not implement Scrollable. + */ + static int SCROLL_NON_SCROLLABLES = 10; + + /** + * The number of rows to scroll per mouse wheel click. From impression, + * Sun seems using the value 3. + */ + static int ROWS_PER_WHEEL_CLICK = 3; /** The Scrollpane for which the UI is provided by this class. */ protected JScrollPane scrollpane; @@ -262,6 +401,12 @@ public class BasicScrollPaneUI extends ScrollPaneUI * The mousewheel listener for the scrollpane. */ MouseWheelListener mouseWheelListener; + + /** + * The listener to add and remove the mouse wheel listener to/from + * the component container. + */ + ContainerListener containerListener; public static ComponentUI createUI(final JComponent c) { @@ -316,11 +461,21 @@ public class BasicScrollPaneUI extends ScrollPaneUI if (viewportChangeListener == null) viewportChangeListener = createViewportChangeListener(); - sp.getViewport().addChangeListener(viewportChangeListener); - + if (mouseWheelListener == null) mouseWheelListener = createMouseWheelListener(); - sp.addMouseWheelListener(mouseWheelListener); + + if (containerListener == null) + containerListener = new ViewportContainerListener(); + + JViewport v = sp.getViewport(); + v.addChangeListener(viewportChangeListener); + v.addContainerListener(containerListener); + + // Add mouse wheel listeners to the componets that are probably already + // in the view port. + for (int i = 0; i < v.getComponentCount(); i++) + v.getComponent(i).addMouseWheelListener(mouseWheelListener); } /** @@ -331,6 +486,7 @@ public class BasicScrollPaneUI extends ScrollPaneUI * @param sp the scrollpane to install keyboard actions on */ protected void installKeyboardActions(JScrollPane sp) + throws NotImplementedException { // TODO: Is this only a hook method or should we actually do something // here? If the latter, than figure out what and implement this. @@ -408,8 +564,14 @@ public class BasicScrollPaneUI extends ScrollPaneUI .removeChangeListener(hsbChangeListener); sp.getVerticalScrollBar().getModel() .removeChangeListener(vsbChangeListener); - sp.getViewport().removeChangeListener(viewportChangeListener); - sp.removeMouseWheelListener(mouseWheelListener); + + JViewport v = sp.getViewport(); + v.removeChangeListener(viewportChangeListener); + v.removeContainerListener(containerListener); + + for (int i = 0; i < v.getComponentCount(); i++) + v.getComponent(i).removeMouseWheelListener(mouseWheelListener); + } /** @@ -420,6 +582,7 @@ public class BasicScrollPaneUI extends ScrollPaneUI * @param sp the scrollpane to uninstall keyboard actions from */ protected void uninstallKeyboardActions(JScrollPane sp) + throws NotImplementedException { // TODO: Is this only a hook method or should we actually do something // here? If the latter, than figure out what and implement this. diff --git a/javax/swing/plaf/basic/BasicSliderUI.java b/javax/swing/plaf/basic/BasicSliderUI.java index 694aa9612..137ab55a6 100644 --- a/javax/swing/plaf/basic/BasicSliderUI.java +++ b/javax/swing/plaf/basic/BasicSliderUI.java @@ -1,5 +1,5 @@ /* BasicSliderUI.java -- - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Color; import java.awt.Component; import java.awt.ComponentOrientation; @@ -61,7 +63,9 @@ import java.util.Dictionary; import java.util.Enumeration; import javax.swing.AbstractAction; +import javax.swing.ActionMap; import javax.swing.BoundedRangeModel; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JSlider; @@ -72,6 +76,7 @@ import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.MouseInputAdapter; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.SliderUI; @@ -204,6 +209,7 @@ public class BasicSliderUI extends SliderUI * @param e A {@link FocusEvent}. */ public void focusGained(FocusEvent e) + throws NotImplementedException { // FIXME: implement. } @@ -215,6 +221,7 @@ public class BasicSliderUI extends SliderUI * @param e A {@link FocusEvent}. */ public void focusLost(FocusEvent e) + throws NotImplementedException { // FIXME: implement. } @@ -236,14 +243,16 @@ public class BasicSliderUI extends SliderUI { // Check for orientation changes. if (e.getPropertyName().equals("orientation")) - recalculateIfOrientationChanged(); + recalculateIfOrientationChanged(); else if (e.getPropertyName().equals("model")) { - BoundedRangeModel oldModel = (BoundedRangeModel) e.getOldValue(); - oldModel.removeChangeListener(changeListener); - slider.getModel().addChangeListener(changeListener); - calculateThumbLocation(); + BoundedRangeModel oldModel = (BoundedRangeModel) e.getOldValue(); + oldModel.removeChangeListener(changeListener); + slider.getModel().addChangeListener(changeListener); + calculateThumbLocation(); } + else if (e.getPropertyName().equals("paintTicks")) + calculateGeometry(); // elif the componentOrientation changes (this is a bound property, // just undocumented) we change leftToRightCache. In Sun's @@ -304,14 +313,14 @@ public class BasicSliderUI extends SliderUI { if (! trackListener.shouldScroll(direction)) { - scrollTimer.stop(); - return; + scrollTimer.stop(); + return; } if (block) - scrollByBlock(direction); + scrollByBlock(direction); else - scrollByUnit(direction); + scrollByUnit(direction); } /** @@ -416,7 +425,8 @@ public class BasicSliderUI extends SliderUI if (slider.getSnapToTicks()) value = findClosestTick(value); - // If the thumb is hit, then we don't need to set the timers to move it. + // If the thumb is hit, then we don't need to set the timers to + // move it. if (! thumbRect.contains(e.getPoint())) { // The mouse has hit some other part of the slider. @@ -469,14 +479,14 @@ public class BasicSliderUI extends SliderUI { int value; if (slider.getOrientation() == JSlider.HORIZONTAL) - value = valueForXPosition(currentMouseX); + value = valueForXPosition(currentMouseX); else - value = valueForYPosition(currentMouseY); + value = valueForYPosition(currentMouseY); if (direction == POSITIVE_SCROLL) - return (value > slider.getValue()); + return (value > slider.getValue()); else - return (value < slider.getValue()); + return (value < slider.getValue()); } } @@ -652,35 +662,35 @@ public class BasicSliderUI extends SliderUI super.installUI(c); if (c instanceof JSlider) { - slider = (JSlider) c; + slider = (JSlider) c; - focusRect = new Rectangle(); - contentRect = new Rectangle(); - thumbRect = new Rectangle(); - trackRect = new Rectangle(); - tickRect = new Rectangle(); - labelRect = new Rectangle(); + focusRect = new Rectangle(); + contentRect = new Rectangle(); + thumbRect = new Rectangle(); + trackRect = new Rectangle(); + tickRect = new Rectangle(); + labelRect = new Rectangle(); - insetCache = slider.getInsets(); - leftToRightCache = ! slider.getInverted(); + insetCache = slider.getInsets(); + leftToRightCache = ! slider.getInverted(); - scrollTimer = new Timer(200, null); - scrollTimer.setRepeats(true); + scrollTimer = new Timer(200, null); + scrollTimer.setRepeats(true); - installDefaults(slider); - installListeners(slider); - installKeyboardActions(slider); + installDefaults(slider); + installListeners(slider); + installKeyboardActions(slider); - calculateFocusRect(); + calculateFocusRect(); - calculateContentRect(); - calculateThumbSize(); - calculateTrackBuffer(); - calculateTrackRect(); - calculateThumbLocation(); + calculateContentRect(); + calculateThumbSize(); + calculateTrackBuffer(); + calculateTrackRect(); + calculateThumbLocation(); - calculateTickRect(); - calculateLabelRect(); + calculateTickRect(); + calculateLabelRect(); } } @@ -865,7 +875,10 @@ public class BasicSliderUI extends SliderUI */ protected void installKeyboardActions(JSlider slider) { - // FIXME: implement. + InputMap keyMap = getInputMap(JComponent.WHEN_FOCUSED); + SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, keyMap); + ActionMap map = getActionMap(); + SwingUtilities.replaceUIActionMap(slider, map); } /** @@ -877,7 +890,8 @@ public class BasicSliderUI extends SliderUI */ protected void uninstallKeyboardActions(JSlider slider) { - // FIXME: implement. + SwingUtilities.replaceUIActionMap(slider, null); + SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, null); } /* XXX: This is all after experimentation with SUN's implementation. @@ -908,8 +922,7 @@ public class BasicSliderUI extends SliderUI // The width should cover all the labels (which are usually the // deciding factor of the width) int width = getWidthOfWidestLabel() * (slider.getLabelTable() == null ? 0 - : slider.getLabelTable() - .size()); + : slider.getLabelTable().size()); // If there are not enough labels. // This number is pretty much arbitrary, but it looks nice. @@ -1129,8 +1142,8 @@ public class BasicSliderUI extends SliderUI } /** - * This method calculates the size but not the position of the thumbRect. It - * must take into account the orientation of the slider. + * Sets the width and height of the <code>thumbRect</code> field, using the + * dimensions returned by {@link #getThumbSize()}. */ protected void calculateThumbSize() { @@ -1144,8 +1157,9 @@ public class BasicSliderUI extends SliderUI } /** - * This method calculates the size and position of the contentRect. This - * method does not need to be called if the orientation changes. + * Updates the <code>contentRect</code> field to an area inside the + * <code>focusRect</code>. This method does not need to be called if the + * orientation changes. */ protected void calculateContentRect() { @@ -1172,36 +1186,50 @@ public class BasicSliderUI extends SliderUI if (slider.getOrientation() == JSlider.HORIZONTAL) { - thumbRect.x = xPositionForValue(value) - thumbRect.width / 2; - thumbRect.y = trackRect.y; + thumbRect.x = xPositionForValue(value) - thumbRect.width / 2; + thumbRect.y = trackRect.y; } else { - thumbRect.x = trackRect.x; - thumbRect.y = yPositionForValue(value) - thumbRect.height / 2; + thumbRect.x = trackRect.x; + thumbRect.y = yPositionForValue(value) - thumbRect.height / 2; } } /** - * Calculates the gap size between the left edge of the contentRect and the - * left edge of the trackRect. + * Calculates the gap size between the edge of the <code>contentRect</code> + * and the edge of the <code>trackRect</code>, storing the result in the + * <code>trackBuffer</code> field. Sufficient space needs to be reserved + * for the slider thumb and/or the labels at each end of the slider track. */ protected void calculateTrackBuffer() { if (slider.getOrientation() == JSlider.HORIZONTAL) - trackBuffer = thumbRect.width / 2; + { + int w = Math.max(getWidthOfLowValueLabel(), getWidthOfHighValueLabel()); + trackBuffer = Math.max(thumbRect.width / 2, w / 2); + + } else - trackBuffer = thumbRect.height / 2; + { + int h = Math.max(getHeightOfLowValueLabel(), + getHeightOfHighValueLabel()); + trackBuffer = Math.max(thumbRect.height / 2, h / 2); + } } /** - * This method returns the size of the thumbRect. + * Returns the size of the slider's thumb. The size is hard coded to + * <code>11 x 20</code> for horizontal sliders, and <code>20 x 11</code> for + * vertical sliders. Note that a new instance of {@link Dimension} is + * returned for every call to this method (this seems wasteful, but + * {@link Dimension} instances are not immutable, so this is probably + * unavoidable). * - * @return The dimensions of the thumb. + * @return The size of the slider's thumb. */ protected Dimension getThumbSize() { - // TODO: shouldn't create new objects every time if (slider.getOrientation() == JSlider.HORIZONTAL) return new Dimension(11, 20); else @@ -1216,14 +1244,16 @@ public class BasicSliderUI extends SliderUI { if (slider.getOrientation() == JSlider.HORIZONTAL) { - trackRect.x = contentRect.x + trackBuffer; + trackRect.x = contentRect.x + trackBuffer; int h = getThumbSize().height; if (slider.getPaintTicks() && (slider.getMajorTickSpacing() > 0 || slider.getMinorTickSpacing() > 0)) h += getTickLength(); - trackRect.y = contentRect.y + (contentRect.height - h) / 2 - 1; - trackRect.width = contentRect.width - 2 * trackBuffer; - trackRect.height = thumbRect.height; + if (slider.getPaintLabels()) + h += getHeightOfTallestLabel(); + trackRect.y = contentRect.y + (contentRect.height - h) / 2 - 1; + trackRect.width = contentRect.width - 2 * trackBuffer; + trackRect.height = thumbRect.height; } else { @@ -1231,10 +1261,12 @@ public class BasicSliderUI extends SliderUI if (slider.getPaintTicks() && (slider.getMajorTickSpacing() > 0 || slider.getMinorTickSpacing() > 0)) w += getTickLength(); - trackRect.x = contentRect.x + (contentRect.width - w) / 2 - 1; - trackRect.y = contentRect.y + trackBuffer; - trackRect.width = thumbRect.width; - trackRect.height = contentRect.height - 2 * trackBuffer; + if (slider.getPaintLabels()) + w += getWidthOfWidestLabel(); + trackRect.x = contentRect.x + (contentRect.width - w) / 2 - 1; + trackRect.y = contentRect.y + trackBuffer; + trackRect.width = thumbRect.width; + trackRect.height = contentRect.height - 2 * trackBuffer; } } @@ -1261,23 +1293,23 @@ public class BasicSliderUI extends SliderUI { if (slider.getOrientation() == JSlider.HORIZONTAL) { - tickRect.x = trackRect.x; - tickRect.y = trackRect.y + trackRect.height; - tickRect.width = trackRect.width; - tickRect.height = getTickLength(); + tickRect.x = trackRect.x; + tickRect.y = trackRect.y + trackRect.height; + tickRect.width = trackRect.width; + tickRect.height = (slider.getPaintTicks() ? getTickLength() : 0); - if (tickRect.y + tickRect.height > contentRect.y + contentRect.height) - tickRect.height = contentRect.y + contentRect.height - tickRect.y; + if (tickRect.y + tickRect.height > contentRect.y + contentRect.height) + tickRect.height = contentRect.y + contentRect.height - tickRect.y; } else { - tickRect.x = trackRect.x + trackRect.width; - tickRect.y = trackRect.y; - tickRect.width = getTickLength(); - tickRect.height = trackRect.height; + tickRect.x = trackRect.x + trackRect.width; + tickRect.y = trackRect.y; + tickRect.width = (slider.getPaintTicks() ? getTickLength() : 0); + tickRect.height = trackRect.height; - if (tickRect.x + tickRect.width > contentRect.x + contentRect.width) - tickRect.width = contentRect.x + contentRect.width - tickRect.x; + if (tickRect.x + tickRect.width > contentRect.x + contentRect.width) + tickRect.width = contentRect.x + contentRect.width - tickRect.x; } } @@ -1289,17 +1321,17 @@ public class BasicSliderUI extends SliderUI { if (slider.getOrientation() == JSlider.HORIZONTAL) { - labelRect.x = contentRect.x; - labelRect.y = tickRect.y + tickRect.height; - labelRect.width = contentRect.width; - labelRect.height = contentRect.height - labelRect.y; + labelRect.x = contentRect.x; + labelRect.y = tickRect.y + tickRect.height; + labelRect.width = contentRect.width; + labelRect.height = getHeightOfTallestLabel(); } else { - labelRect.x = tickRect.x + tickRect.width; - labelRect.y = contentRect.y; - labelRect.width = contentRect.width - labelRect.x; - labelRect.height = contentRect.height; + labelRect.x = tickRect.x + tickRect.width; + labelRect.y = contentRect.y; + labelRect.width = getWidthOfWidestLabel(); + labelRect.height = contentRect.height; } } @@ -1321,13 +1353,13 @@ public class BasicSliderUI extends SliderUI for (Enumeration list = slider.getLabelTable().elements(); list.hasMoreElements();) { - Object comp = list.nextElement(); - if (! (comp instanceof Component)) - continue; - label = (Component) comp; - pref = label.getPreferredSize(); - if (pref != null && pref.width > widest) - widest = pref.width; + Object comp = list.nextElement(); + if (! (comp instanceof Component)) + continue; + label = (Component) comp; + pref = label.getPreferredSize(); + if (pref != null && pref.width > widest) + widest = pref.width; } return widest; } @@ -1349,50 +1381,54 @@ public class BasicSliderUI extends SliderUI for (Enumeration list = slider.getLabelTable().elements(); list.hasMoreElements();) { - Object comp = list.nextElement(); - if (! (comp instanceof Component)) - continue; - label = (Component) comp; - pref = label.getPreferredSize(); - if (pref != null && pref.height > tallest) - tallest = pref.height; + Object comp = list.nextElement(); + if (! (comp instanceof Component)) + continue; + label = (Component) comp; + pref = label.getPreferredSize(); + if (pref != null && pref.height > tallest) + tallest = pref.height; } return tallest; } /** - * This method returns the width of the label whose key has the highest - * value. + * Returns the width of the label whose key has the highest value, or 0 if + * there are no labels. * - * @return The width of the high value label or 0 if no label table exists. + * @return The width of the label whose key has the highest value. + * + * @see #getHighestValueLabel() */ protected int getWidthOfHighValueLabel() { Component highValueLabel = getHighestValueLabel(); if (highValueLabel != null) - return highValueLabel.getWidth(); + return highValueLabel.getPreferredSize().width; else return 0; } /** - * This method returns the width of the label whose key has the lowest - * value. + * Returns the width of the label whose key has the lowest value, or 0 if + * there are no labels. * - * @return The width of the low value label or 0 if no label table exists. + * @return The width of the label whose key has the lowest value. + * + * @see #getLowestValueLabel() */ protected int getWidthOfLowValueLabel() { Component lowValueLabel = getLowestValueLabel(); if (lowValueLabel != null) - return lowValueLabel.getWidth(); + return lowValueLabel.getPreferredSize().width; else return 0; } /** - * This method returns the height of the label whose key has the highest - * value. + * Returns the height of the label whose key has the highest value, or 0 if + * there are no labels. * * @return The height of the high value label or 0 if no label table exists. */ @@ -1400,14 +1436,14 @@ public class BasicSliderUI extends SliderUI { Component highValueLabel = getHighestValueLabel(); if (highValueLabel != null) - return highValueLabel.getHeight(); + return highValueLabel.getPreferredSize().height; else return 0; } /** - * This method returns the height of the label whose key has the lowest - * value. + * Returns the height of the label whose key has the lowest value, or 0 if + * there are no labels. * * @return The height of the low value label or 0 if no label table exists. */ @@ -1415,19 +1451,20 @@ public class BasicSliderUI extends SliderUI { Component lowValueLabel = getLowestValueLabel(); if (lowValueLabel != null) - return lowValueLabel.getHeight(); + return lowValueLabel.getPreferredSize().height; else return 0; } /** - * This method returns whether the slider is to be drawn inverted. + * Returns <code>true</code> if the slider scale is to be drawn inverted, + * and <code>false</code> if not. * - * @return True is the slider is to be drawn inverted. + * @return <code>true</code> if the slider is to be drawn inverted. */ protected boolean drawInverted() { - return ! (slider.getInverted() ^ leftToRightCache); + return slider.getInverted(); } /** @@ -1446,12 +1483,12 @@ public class BasicSliderUI extends SliderUI for (Enumeration list = labelTable.keys(); list.hasMoreElements();) { - Object value = list.nextElement(); - if (! (value instanceof Integer)) - continue; - tmpKey = (Integer) value; - if (tmpKey.intValue() < key.intValue()) - key = tmpKey; + Object value = list.nextElement(); + if (! (value instanceof Integer)) + continue; + tmpKey = (Integer) value; + if (tmpKey.intValue() < key.intValue()) + key = tmpKey; } Object comp = labelTable.get(key); if (! (comp instanceof Component)) @@ -1460,9 +1497,10 @@ public class BasicSliderUI extends SliderUI } /** - * This method returns the label whose key has the highest value. + * Returns the label whose key has the highest value. * - * @return The high value label or null if no label table exists. + * @return The label whose key has the highest value or <code>null</code> if + * no label table exists. */ protected Component getHighestValueLabel() { @@ -1475,12 +1513,12 @@ public class BasicSliderUI extends SliderUI for (Enumeration list = labelTable.keys(); list.hasMoreElements();) { - Object value = list.nextElement(); - if (! (value instanceof Integer)) - continue; - tmpKey = (Integer) value; - if (tmpKey.intValue() > key.intValue()) - key = tmpKey; + Object value = list.nextElement(); + if (! (value instanceof Integer)) + continue; + tmpKey = (Integer) value; + if (tmpKey.intValue() > key.intValue()) + key = tmpKey; } Object comp = labelTable.get(key); if (! (comp instanceof Component)) @@ -1499,7 +1537,8 @@ public class BasicSliderUI extends SliderUI public void paint(Graphics g, JComponent c) { // FIXME: Move this to propertyChangeEvent handler, when we get those. - leftToRightCache = slider.getComponentOrientation() != ComponentOrientation.RIGHT_TO_LEFT; + leftToRightCache = slider.getComponentOrientation() + != ComponentOrientation.RIGHT_TO_LEFT; // FIXME: This next line is only here because the above line is here. calculateGeometry(); @@ -1608,23 +1647,23 @@ public class BasicSliderUI extends SliderUI if (slider.getOrientation() == JSlider.HORIZONTAL) { - width = trackRect.width; - height = (thumbRect.height / 4 == 0) ? 1 : thumbRect.height / 4; + width = trackRect.width; + height = (thumbRect.height / 4 == 0) ? 1 : thumbRect.height / 4; - a.translate(0, (trackRect.height / 2) - (height / 2)); - b.translate(0, (trackRect.height / 2) + (height / 2)); - c.translate(trackRect.width, (trackRect.height / 2) + (height / 2)); - d.translate(trackRect.width, (trackRect.height / 2) - (height / 2)); + a.translate(0, (trackRect.height / 2) - (height / 2)); + b.translate(0, (trackRect.height / 2) + (height / 2)); + c.translate(trackRect.width, (trackRect.height / 2) + (height / 2)); + d.translate(trackRect.width, (trackRect.height / 2) - (height / 2)); } else { - width = (thumbRect.width / 4 == 0) ? 1 : thumbRect.width / 4; - height = trackRect.height; + width = (thumbRect.width / 4 == 0) ? 1 : thumbRect.width / 4; + height = trackRect.height; - a.translate((trackRect.width / 2) - (width / 2), 0); - b.translate((trackRect.width / 2) - (width / 2), trackRect.height); - c.translate((trackRect.width / 2) + (width / 2), trackRect.height); - d.translate((trackRect.width / 2) + (width / 2), 0); + a.translate((trackRect.width / 2) - (width / 2), 0); + b.translate((trackRect.width / 2) - (width / 2), trackRect.height); + c.translate((trackRect.width / 2) + (width / 2), trackRect.height); + d.translate((trackRect.width / 2) + (width / 2), 0); } g.setColor(Color.GRAY); g.fillRect(a.x, a.y, width, height); @@ -1656,86 +1695,42 @@ public class BasicSliderUI extends SliderUI if (majorSpace > 0) { - if (slider.getOrientation() == JSlider.HORIZONTAL) - { - double loc = tickRect.x + 0.5; - double increment = (max == min) ? 0 - : majorSpace * (double) (tickRect.width - 1) / (max - min); - if (drawInverted()) - { - loc += tickRect.width; - increment *= -1; - } + if (slider.getOrientation() == JSlider.HORIZONTAL) + { g.translate(0, tickRect.y); - for (int i = min; i <= max; i += majorSpace) - { - paintMajorTickForHorizSlider(g, tickRect, (int) loc); - loc += increment; - } + for (int i = min; i <= max; i += majorSpace) + paintMajorTickForHorizSlider(g, tickRect, xPositionForValue(i)); g.translate(0, -tickRect.y); - } - else - { - double loc = tickRect.height + tickRect.y + 0.5; - double increment = (max == min) ? 0 - : -majorSpace * (double) (tickRect.height - 1) / (max - min); - if (drawInverted()) - { - loc = tickRect.y + 0.5; - increment *= -1; - } + } + else // JSlider.VERTICAL + { g.translate(tickRect.x, 0); - for (int i = min; i <= max; i += majorSpace) - { - paintMajorTickForVertSlider(g, tickRect, (int) loc); - loc += increment; - } + for (int i = min; i <= max; i += majorSpace) + paintMajorTickForVertSlider(g, tickRect, yPositionForValue(i)); g.translate(-tickRect.x, 0); - } + } } if (minorSpace > 0) { - if (slider.getOrientation() == JSlider.HORIZONTAL) - { - double loc = tickRect.x + 0.5; - double increment = (max == min) ? 0 - : minorSpace * (double) (tickRect.width - 1) / (max - min); - if (drawInverted()) - { - loc += tickRect.width; - increment *= -1; - } + if (slider.getOrientation() == JSlider.HORIZONTAL) + { g.translate(0, tickRect.y); - for (int i = min; i <= max; i += minorSpace) - { - paintMinorTickForHorizSlider(g, tickRect, (int) loc); - loc += increment; - } + for (int i = min; i <= max; i += minorSpace) + paintMinorTickForHorizSlider(g, tickRect, xPositionForValue(i)); g.translate(0, -tickRect.y); - } - else - { - double loc = tickRect.height + tickRect.y + 0.5; - double increment = (max == min) ? 0 - : -minorSpace * (double) (tickRect.height - 1) / (max - min); - if (drawInverted()) - { - loc = tickRect.y + 0.5; - increment *= -1; - } + } + else + { g.translate(tickRect.x, 0); - for (int i = min; i <= max; i += minorSpace) - { - paintMinorTickForVertSlider(g, tickRect, (int) loc); - loc += increment; - } + for (int i = min; i <= max; i += minorSpace) + paintMinorTickForVertSlider(g, tickRect, yPositionForValue(i)); g.translate(-tickRect.x, 0); - } + } } } - /* Minor ticks start at 1/4 of the height (or width) of the tickRect and extend - to 1/2 of the tickRect. + /* Minor ticks start at 1/4 of the height (or width) of the tickRect and + extend to 1/2 of the tickRect. Major ticks start at 1/4 of the height and extend to 3/4. */ @@ -1828,45 +1823,45 @@ public class BasicSliderUI extends SliderUI { if (slider.getLabelTable() != null) { - Dictionary table = slider.getLabelTable(); - Integer tmpKey; - Object key; - Object element; - Component label; - if (slider.getOrientation() == JSlider.HORIZONTAL) - { - for (Enumeration list = table.keys(); list.hasMoreElements();) - { - key = list.nextElement(); - if (! (key instanceof Integer)) - continue; - tmpKey = (Integer) key; - element = table.get(tmpKey); - // We won't paint them if they're not - // JLabels so continue anyway - if (! (element instanceof JLabel)) - continue; - label = (Component) element; - paintHorizontalLabel(g, tmpKey.intValue(), label); - } - } - else - { - for (Enumeration list = table.keys(); list.hasMoreElements();) - { - key = list.nextElement(); - if (! (key instanceof Integer)) - continue; - tmpKey = (Integer) key; - element = table.get(tmpKey); - // We won't paint them if they're not - // JLabels so continue anyway - if (! (element instanceof JLabel)) - continue; - label = (Component) element; - paintVerticalLabel(g, tmpKey.intValue(), label); - } - } + Dictionary table = slider.getLabelTable(); + Integer tmpKey; + Object key; + Object element; + Component label; + if (slider.getOrientation() == JSlider.HORIZONTAL) + { + for (Enumeration list = table.keys(); list.hasMoreElements();) + { + key = list.nextElement(); + if (! (key instanceof Integer)) + continue; + tmpKey = (Integer) key; + element = table.get(tmpKey); + // We won't paint them if they're not + // JLabels so continue anyway + if (! (element instanceof JLabel)) + continue; + label = (Component) element; + paintHorizontalLabel(g, tmpKey.intValue(), label); + } + } + else + { + for (Enumeration list = table.keys(); list.hasMoreElements();) + { + key = list.nextElement(); + if (! (key instanceof Integer)) + continue; + tmpKey = (Integer) key; + element = table.get(tmpKey); + // We won't paint them if they're not + // JLabels so continue anyway + if (! (element instanceof JLabel)) + continue; + label = (Component) element; + paintVerticalLabel(g, tmpKey.intValue(), label); + } + } } } @@ -1927,7 +1922,7 @@ public class BasicSliderUI extends SliderUI h = labelRect.height; label.setBounds(xpos, ypos, w, h); - javax.swing.SwingUtilities.paintComponent(g, label, null, label.getBounds()); + SwingUtilities.paintComponent(g, label, null, label.getBounds()); } /** @@ -1966,7 +1961,7 @@ public class BasicSliderUI extends SliderUI w = labelRect.width; label.setBounds(xpos, ypos, w, h); - javax.swing.SwingUtilities.paintComponent(g, label, null, label.getBounds()); + SwingUtilities.paintComponent(g, label, null, label.getBounds()); } /** @@ -2006,49 +2001,52 @@ public class BasicSliderUI extends SliderUI Polygon dark; // dark shadow Polygon all; - // This will be in X-dimension if the slider is inverted and y if it isn't. + // This will be in X-dimension if the slider is inverted and y if it isn't. int turnPoint; if (slider.getOrientation() == JSlider.HORIZONTAL) { - turnPoint = thumbRect.height * 3 / 4; + turnPoint = thumbRect.height * 3 / 4; - b.translate(thumbRect.width - 1, 0); - c.translate(thumbRect.width - 1, turnPoint); - d.translate(thumbRect.width / 2 - 1, thumbRect.height - 1); - e.translate(0, turnPoint); + b.translate(thumbRect.width - 1, 0); + c.translate(thumbRect.width - 1, turnPoint); + d.translate(thumbRect.width / 2 - 1, thumbRect.height - 1); + e.translate(0, turnPoint); - bright = new Polygon(new int[] { b.x - 1, a.x, e.x, d.x }, - new int[] { b.y, a.y, e.y, d.y }, 4); + bright = new Polygon(new int[] { b.x - 1, a.x, e.x, d.x }, + new int[] { b.y, a.y, e.y, d.y }, 4); - dark = new Polygon(new int[] { b.x, c.x, d.x + 1 }, - new int[] { b.y, c.y - 1, d.y }, 3); + dark = new Polygon(new int[] { b.x, c.x, d.x + 1 }, + new int[] { b.y, c.y - 1, d.y }, 3); light = new Polygon(new int[] { b.x - 1, c.x - 1, d.x + 1 }, new int[] { b.y + 1, c.y - 1, d.y - 1 }, 3); - all = new Polygon(new int[] { a.x + 1, b.x - 2, c.x - 2, d.x, e.x + 1 }, - new int[] { a.y + 1, b.y + 1, c.y - 1, d.y - 1, e.y }, 5); + all = new Polygon(new int[] { a.x + 1, b.x - 2, c.x - 2, d.x, e.x + 1 }, + new int[] { a.y + 1, b.y + 1, c.y - 1, d.y - 1, e.y }, + 5); } else { - turnPoint = thumbRect.width * 3 / 4 - 1; + turnPoint = thumbRect.width * 3 / 4 - 1; - b.translate(turnPoint, 0); - c.translate(thumbRect.width - 1, thumbRect.height / 2); - d.translate(turnPoint, thumbRect.height - 1); - e.translate(0, thumbRect.height - 1); + b.translate(turnPoint, 0); + c.translate(thumbRect.width - 1, thumbRect.height / 2); + d.translate(turnPoint, thumbRect.height - 1); + e.translate(0, thumbRect.height - 1); - bright = new Polygon(new int[] { c.x - 1, b.x, a.x, e.x }, - new int[] { c.y - 1, b.y, a.y, e.y - 1 }, 4); + bright = new Polygon(new int[] { c.x - 1, b.x, a.x, e.x }, + new int[] { c.y - 1, b.y, a.y, e.y - 1 }, 4); - dark = new Polygon(new int[] { c.x, d.x, e.x }, - new int[] { c.y, d.y, e.y }, 3); + dark = new Polygon(new int[] { c.x, d.x, e.x }, + new int[] { c.y, d.y, e.y }, 3); light = new Polygon(new int[] { c.x - 1, d.x, e.x + 1}, new int[] { c.y, d.y - 1, e.y - 1}, 3); - all = new Polygon(new int[] { a.x + 1, b.x, c.x - 2, c.x - 2, d.x, e.x + 1 }, - new int[] { a.y + 1, b.y + 1, c.y - 1, c.y, d.y - 2, e.y - 2 }, 6); + all = new Polygon(new int[] { a.x + 1, b.x, c.x - 2, c.x - 2, d.x, + e.x + 1 }, + new int[] { a.y + 1, b.y + 1, c.y - 1, c.y, d.y - 2, + e.y - 2 }, 6); } g.setColor(Color.WHITE); @@ -2080,18 +2078,22 @@ public class BasicSliderUI extends SliderUI } /** - * This method is used to move the thumb one block in the direction - * specified. If the slider snaps to ticks, this method is responsible for - * snapping it to a tick after the thumb has been moved. + * Moves the thumb one block in the direction specified (a block is 1/10th + * of the slider range). If the slider snaps to ticks, this method is + * responsible for snapping it to a tick after the thumb has been moved. * - * @param direction The direction to move in. + * @param direction the direction (positive values increment the thumb + * position by one block, zero/negative values decrement the thumb position + * by one block). */ public void scrollByBlock(int direction) { - // The direction is -1 for backwards and 1 for forwards. - int unit = direction * (slider.getMaximum() - slider.getMinimum()) / 10; - - int moveTo = slider.getValue() + unit; + int unit = (slider.getMaximum() - slider.getMinimum()) / 10; + int moveTo = slider.getValue(); + if (direction > 0) + moveTo += unit; + else + moveTo -= unit; if (slider.getSnapToTicks()) moveTo = findClosestTick(moveTo); @@ -2100,16 +2102,21 @@ public class BasicSliderUI extends SliderUI } /** - * This method is used to move the thumb one unit in the direction - * specified. If the slider snaps to ticks, this method is responsible for - * snapping it to a tick after the thumb has been moved. + * Moves the thumb one unit in the specified direction. If the slider snaps + * to ticks, this method is responsible for snapping it to a tick after the + * thumb has been moved. * - * @param direction The direction to move in. + * @param direction the direction (positive values increment the thumb + * position by one, zero/negative values decrement the thumb position by + * one). */ public void scrollByUnit(int direction) { - // The direction is -1 for backwards and 1 for forwards. - int moveTo = slider.getValue() + direction; + int moveTo = slider.getValue(); + if (direction > 0) + moveTo++; + else + moveTo--; if (slider.getSnapToTicks()) moveTo = findClosestTick(moveTo); @@ -2135,53 +2142,60 @@ public class BasicSliderUI extends SliderUI } /** - * This method returns the X coordinate for the value passed in. + * Returns the x-coordinate (relative to the component) for the given slider + * value. This method assumes that the <code>trackRect</code> field is + * set up. * - * @param value The value to calculate an x coordinate for. + * @param value the slider value. * - * @return The x coordinate for the value. + * @return The x-coordinate. */ protected int xPositionForValue(int value) { - int min = slider.getMinimum(); - int max = slider.getMaximum(); - int len = trackRect.width - 1; - - int xPos = (max == min) ? 0 : (value - min) * len / (max - min); + double min = slider.getMinimum(); + if (value < min) + value = (int) min; + double max = slider.getMaximum(); + if (value > max) + value = (int) max; + double len = trackRect.width; + if ((max - min) <= 0.0) + return 0; + int xPos = (int) ((value - min) / (max - min) * len + 0.5); - if (! drawInverted()) - xPos += trackRect.x; + if (drawInverted()) + return trackRect.x + Math.max(trackRect.width - xPos - 1, 0); else - { - xPos = len - xPos; - xPos += trackRect.x; - } - return xPos; + return trackRect.x + Math.min(xPos, trackRect.width - 1); } /** - * This method returns the y coordinate for the value passed in. + * Returns the y-coordinate (relative to the component) for the given slider + * value. This method assumes that the <code>trackRect</code> field is + * set up. * - * @param value The value to calculate a y coordinate for. + * @param value the slider value. * - * @return The y coordinate for the value. + * @return The y-coordinate. */ protected int yPositionForValue(int value) { - int min = slider.getMinimum(); - int max = slider.getMaximum(); - int len = trackRect.height - 1; + double min = slider.getMinimum(); + if (value < min) + value = (int) min; + double max = slider.getMaximum(); + if (value > max) + value = (int) max; + int len = trackRect.height; + if ((max - min) <= 0.0) + return 0; - int yPos = (max == min) ? 0 : (value - min) * len / (max - min); + int yPos = (int) ((value - min) / (max - min) * len + 0.5); if (! drawInverted()) - { - yPos = len - yPos; - yPos += trackRect.y; - } + return trackRect.y + trackRect.height - Math.max(yPos, 1); else - yPos += trackRect.y; - return yPos; + return trackRect.y + Math.min(yPos, trackRect.height - 1); } /** @@ -2288,26 +2302,26 @@ public class BasicSliderUI extends SliderUI // First check the major ticks. if (majorSpace > 0) { - int lowerBound = (value - min) / majorSpace; - int majLower = majorSpace * lowerBound + min; - int majHigher = majorSpace * (lowerBound + 1) + min; - - if (majHigher <= max && majHigher - value <= value - majLower) - major = majHigher - value; - else - major = majLower - value; + int lowerBound = (value - min) / majorSpace; + int majLower = majorSpace * lowerBound + min; + int majHigher = majorSpace * (lowerBound + 1) + min; + + if (majHigher <= max && majHigher - value <= value - majLower) + major = majHigher - value; + else + major = majLower - value; } if (minorSpace > 0) { - int lowerBound = value / minorSpace; - int minLower = minorSpace * lowerBound; - int minHigher = minorSpace * (lowerBound + 1); - - if (minHigher <= max && minHigher - value <= value - minLower) - minor = minHigher - value; - else - minor = minLower - value; + int lowerBound = value / minorSpace; + int minLower = minorSpace * lowerBound; + int minHigher = minorSpace * (lowerBound + 1); + + if (minHigher <= max && minHigher - value <= value - minLower) + minor = minHigher - value; + else + minor = minLower - value; } // Give preference to minor ticks @@ -2316,4 +2330,123 @@ public class BasicSliderUI extends SliderUI else return value + minor; } + + InputMap getInputMap(int condition) + { + if (condition == JComponent.WHEN_FOCUSED) + return (InputMap) UIManager.get("Slider.focusInputMap"); + return null; + } + + /** + * Returns the action map for the {@link JSlider}. All sliders share + * a single action map which is created the first time this method is + * called, then stored in the UIDefaults table for subsequent access. + * + * @return The shared action map. + */ + ActionMap getActionMap() + { + ActionMap map = (ActionMap) UIManager.get("Slider.actionMap"); + + if (map == null) // first time here + { + map = createActionMap(); + if (map != null) + UIManager.put("Slider.actionMap", map); + } + return map; + } + + /** + * Creates the action map shared by all {@link JSlider} instances. + * This method is called once by {@link #getActionMap()} when it + * finds no action map in the UIDefaults table...after the map is + * created, it gets added to the defaults table so that subsequent + * calls to {@link #getActionMap()} will return the same shared + * instance. + * + * @return The action map. + */ + ActionMap createActionMap() + { + ActionMap map = new ActionMapUIResource(); + map.put("positiveUnitIncrement", + new AbstractAction("positiveUnitIncrement") { + public void actionPerformed(ActionEvent event) + { + JSlider slider = (JSlider) event.getSource(); + BasicSliderUI ui = (BasicSliderUI) slider.getUI(); + if (slider.getInverted()) + ui.scrollByUnit(BasicSliderUI.NEGATIVE_SCROLL); + else + ui.scrollByUnit(BasicSliderUI.POSITIVE_SCROLL); + } + } + ); + map.put("negativeUnitIncrement", + new AbstractAction("negativeUnitIncrement") { + public void actionPerformed(ActionEvent event) + { + JSlider slider = (JSlider) event.getSource(); + BasicSliderUI ui = (BasicSliderUI) slider.getUI(); + if (slider.getInverted()) + ui.scrollByUnit(BasicSliderUI.POSITIVE_SCROLL); + else + ui.scrollByUnit(BasicSliderUI.NEGATIVE_SCROLL); + } + } + ); + map.put("positiveBlockIncrement", + new AbstractAction("positiveBlockIncrement") { + public void actionPerformed(ActionEvent event) + { + JSlider slider = (JSlider) event.getSource(); + BasicSliderUI ui = (BasicSliderUI) slider.getUI(); + if (slider.getInverted()) + ui.scrollByBlock(BasicSliderUI.NEGATIVE_SCROLL); + else + ui.scrollByBlock(BasicSliderUI.POSITIVE_SCROLL); + } + } + ); + map.put("negativeBlockIncrement", + new AbstractAction("negativeBlockIncrement") { + public void actionPerformed(ActionEvent event) + { + JSlider slider = (JSlider) event.getSource(); + BasicSliderUI ui = (BasicSliderUI) slider.getUI(); + if (slider.getInverted()) + ui.scrollByBlock(BasicSliderUI.POSITIVE_SCROLL); + else + ui.scrollByBlock(BasicSliderUI.NEGATIVE_SCROLL); + } + } + ); + map.put("minScroll", + new AbstractAction("minScroll") { + public void actionPerformed(ActionEvent event) + { + JSlider slider = (JSlider) event.getSource(); + if (slider.getInverted()) + slider.setValue(slider.getMaximum()); + else + slider.setValue(slider.getMinimum()); + } + } + ); + map.put("maxScroll", + new AbstractAction("maxScroll") { + public void actionPerformed(ActionEvent event) + { + JSlider slider = (JSlider) event.getSource(); + if (slider.getInverted()) + slider.setValue(slider.getMinimum()); + else + slider.setValue(slider.getMaximum()); + } + } + ); + return map; + } } diff --git a/javax/swing/plaf/basic/BasicSpinnerUI.java b/javax/swing/plaf/basic/BasicSpinnerUI.java index 6f7a41a1d..465374bfd 100644 --- a/javax/swing/plaf/basic/BasicSpinnerUI.java +++ b/javax/swing/plaf/basic/BasicSpinnerUI.java @@ -41,7 +41,6 @@ package javax.swing.plaf.basic; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; -import java.awt.Font; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.event.ActionEvent; @@ -365,9 +364,9 @@ public class BasicSpinnerUI extends SpinnerUI private class DefaultLayoutManager implements LayoutManager { /** - * DOCUMENT ME! + * Layout the spinners inner parts. * - * @param parent DOCUMENT ME! + * @param parent The parent container */ public void layoutContainer(Container parent) { @@ -385,12 +384,12 @@ public class BasicSpinnerUI extends SpinnerUI Dimension e = prefSize(editor); Dimension n = prefSize(next); Dimension p = prefSize(previous); - Dimension s = spinner.getPreferredSize(); + Dimension s = parent.getSize(); int x = l2r ? i.left : i.right; int y = i.top; int w = Math.max(p.width, n.width); - int h = e.height / 2; + int h = (s.height - i.bottom) / 2; int e_width = s.width - w - i.left - i.right; if (l2r) diff --git a/javax/swing/plaf/basic/BasicSplitPaneUI.java b/javax/swing/plaf/basic/BasicSplitPaneUI.java index 906acfe16..694baadda 100644 --- a/javax/swing/plaf/basic/BasicSplitPaneUI.java +++ b/javax/swing/plaf/basic/BasicSplitPaneUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Canvas; import java.awt.Color; import java.awt.Component; @@ -1047,6 +1049,7 @@ public class BasicSplitPaneUI extends SplitPaneUI * This method installs the keyboard actions for the JSplitPane. */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: implement. } @@ -1055,6 +1058,7 @@ public class BasicSplitPaneUI extends SplitPaneUI * This method reverses the work done in installKeyboardActions. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: implement. } diff --git a/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 5b1e1ff0f..6d9bed331 100644 --- a/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -130,52 +132,49 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void mousePressed(MouseEvent e) { - int x = e.getX(); - int y = e.getY(); - int tabCount = tabPane.getTabCount(); - - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + if (tabPane.isEnabled()) { - if (e.getSource() == incrButton) + int index = tabForCoordinate(tabPane, e.getX(), e.getY()); + if (index >= 0 && tabPane.isEnabledAt(index)) { - if (++currentScrollLocation >= tabCount) - currentScrollLocation = tabCount - 1; - - int width = 0; - for (int i = currentScrollLocation - 1; i < tabCount; i++) - width += rects[i].width; - if (width < viewport.getWidth()) - // FIXME: Still getting mouse events after the button is disabled. - // incrButton.setEnabled(false); - currentScrollLocation--; - else if (! decrButton.isEnabled()) - decrButton.setEnabled(true); - tabPane.revalidate(); - tabPane.repaint(); - return; - } - else if (e.getSource() == decrButton) - { - if (--currentScrollLocation < 0) - currentScrollLocation = 0; - if (currentScrollLocation == 0) - decrButton.setEnabled(false); - else if (! incrButton.isEnabled()) - incrButton.setEnabled(true); - tabPane.revalidate(); - tabPane.repaint(); - return; + tabPane.setSelectedIndex(index); } } + } - int index = tabForCoordinate(tabPane, x, y); + /** + * Receives notification when the mouse pointer has entered the tabbed + * pane. + * + * @param ev the mouse event + */ + public void mouseEntered(MouseEvent ev) + { + int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY()); + setRolloverTab(tabIndex); + } - // We need to check since there are areas where tabs cannot be - // e.g. in the inset area. - if (index != -1 && tabPane.isEnabledAt(index)) - tabPane.setSelectedIndex(index); - tabPane.revalidate(); - tabPane.repaint(); + /** + * Receives notification when the mouse pointer has exited the tabbed + * pane. + * + * @param ev the mouse event + */ + public void mouseExited(MouseEvent ev) + { + setRolloverTab(-1); + } + + /** + * Receives notification when the mouse pointer has moved over the tabbed + * pane. + * + * @param ev the mouse event + */ + public void mouseMoved(MouseEvent ev) + { + int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY()); + setRolloverTab(tabIndex); } } @@ -241,21 +240,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void calculateLayoutInfo() { - assureRectsCreated(tabPane.getTabCount()); - contentRect = SwingUtilities.calculateInnerArea(tabPane, contentRect); - - calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount()); - - if (tabPane.getSelectedIndex() != -1) - { - Component visible = getVisibleComponent(); - Insets insets = getContentBorderInsets(tabPane.getTabPlacement()); - if (visible != null) - visible.setBounds(contentRect.x + insets.left, - contentRect.y + insets.top, - contentRect.width - insets.left - insets.right, - contentRect.height - insets.top - insets.bottom); - } + int count = tabPane.getTabCount(); + assureRectsCreated(count); + calculateTabRects(tabPane.getTabPlacement(), count); + tabRunsDirty = false; } /** @@ -269,45 +257,51 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected Dimension calculateSize(boolean minimum) { int tabPlacement = tabPane.getTabPlacement(); + int width = 0; int height = 0; - - int componentHeight = 0; - int componentWidth = 0; Component c; Dimension dims; + + // Find out the minimum/preferred size to display the largest child + // of the tabbed pane. for (int i = 0; i < tabPane.getTabCount(); i++) { c = tabPane.getComponentAt(i); if (c == null) continue; - calcRect = c.getBounds(); - dims = c.getPreferredSize(); + dims = minimum ? c.getMinimumSize() : c.getPreferredSize(); if (dims != null) { - componentHeight = Math.max(componentHeight, dims.height); - componentWidth = Math.max(componentWidth, dims.width); + height = Math.max(height, dims.height); + width = Math.max(width, dims.width); } } + + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { int min = calculateMaxTabWidth(tabPlacement); - width = Math.max(min, componentWidth); - - int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width); - height = tabAreaHeight + componentHeight; + width = Math.max(min, width); + int tabAreaHeight = preferredTabAreaHeight(tabPlacement, + width - tabAreaInsets.left + -tabAreaInsets.right); + height += tabAreaHeight; } else { int min = calculateMaxTabHeight(tabPlacement); - height = Math.max(min, componentHeight); - - int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height); - width = tabAreaWidth + componentWidth; + height = Math.max(min, height); + int tabAreaWidth = preferredTabAreaWidth(tabPlacement, + height - tabAreaInsets.top + - tabAreaInsets.bottom); + width += tabAreaWidth; } - return new Dimension(width, height); + Insets tabPaneInsets = tabPane.getInsets(); + return new Dimension(width + tabPaneInsets.left + tabPaneInsets.right, + height + tabPaneInsets.top + tabPaneInsets.bottom); } // if tab placement is LEFT OR RIGHT, they share width. @@ -330,192 +324,197 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void calculateTabRects(int tabPlacement, int tabCount) { + Insets insets = tabPane.getInsets(); + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); + Dimension size = tabPane.getSize(); + + // The coordinates of the upper left corner of the tab area. + int x; + int y; + // The location at which the runs must be broken. + int breakAt; + + // Calculate the bounds for the tab area. + switch (tabPlacement) + { + case LEFT: + maxTabWidth = calculateMaxTabWidth(tabPlacement); + x = insets.left + tabAreaInsets.left; + y = insets.top + tabAreaInsets.top; + breakAt = size.height - (insets.bottom + tabAreaInsets.bottom); + break; + case RIGHT: + maxTabWidth = calculateMaxTabWidth(tabPlacement); + x = size.width - (insets.right + tabAreaInsets.right) - maxTabWidth; + y = insets.top + tabAreaInsets.top; + breakAt = size.height - (insets.bottom + tabAreaInsets.bottom); + break; + case BOTTOM: + maxTabHeight = calculateMaxTabHeight(tabPlacement); + x = insets.left + tabAreaInsets.left; + y = size.height - (insets.bottom + tabAreaInsets.bottom) + - maxTabHeight; + breakAt = size.width - (insets.right + tabAreaInsets.right); + break; + case TOP: + default: + maxTabHeight = calculateMaxTabHeight(tabPlacement); + x = insets.left + tabAreaInsets.left; + y = insets.top + tabAreaInsets.top; + breakAt = size.width - (insets.right + tabAreaInsets.right); + break; + } + if (tabCount == 0) return; FontMetrics fm = getFontMetrics(); - SwingUtilities.calculateInnerArea(tabPane, calcRect); - Insets tabAreaInsets = getTabAreaInsets(tabPlacement); - Insets insets = tabPane.getInsets(); - int max = 0; - int runs = 0; - int start = getTabRunIndent(tabPlacement, 1); + runCount = 0; + selectedRun = -1; + int selectedIndex = tabPane.getSelectedIndex(); + + Rectangle rect; + + // Go through all the tabs and build the tab runs. if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int maxHeight = calculateMaxTabHeight(tabPlacement); - - calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; - max = calcRect.width + tabAreaInsets.left + insets.left; - start += tabAreaInsets.left + insets.left; - int width = 0; - int runWidth = start; - for (int i = 0; i < tabCount; i++) { - width = calculateTabWidth(tabPlacement, i, fm); - if (runWidth + width > max) + rect = rects[i]; + if (i > 0) { - runWidth = tabAreaInsets.left + insets.left - + getTabRunIndent(tabPlacement, ++runs); - rects[i] = new Rectangle(runWidth, - insets.top + tabAreaInsets.top, - width, maxHeight); - runWidth += width; - if (runs > tabRuns.length - 1) - expandTabRunsArray(); - tabRuns[runs] = i; + rect.x = rects[i - 1].x + rects[i - 1].width; } else { - rects[i] = new Rectangle(runWidth, - insets.top + tabAreaInsets.top, - width, maxHeight); - runWidth += width; + tabRuns[0] = 0; + runCount = 1; + maxTabWidth = 0; + rect.x = x; } - } - runs++; - tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; - tabAreaRect.height = runs * maxTabHeight - - (runs - 1) * tabRunOverlay - + tabAreaInsets.top + tabAreaInsets.bottom; - contentRect.width = tabAreaRect.width; - contentRect.height = tabPane.getHeight() - insets.top - - insets.bottom - tabAreaRect.height; - contentRect.x = insets.left; - tabAreaRect.x = insets.left; - if (tabPlacement == SwingConstants.BOTTOM) - { - contentRect.y = insets.top; - tabAreaRect.y = contentRect.y + contentRect.height; - } - else - { - tabAreaRect.y = insets.top; - contentRect.y = tabAreaRect.y + tabAreaRect.height; + rect.width = calculateTabWidth(tabPlacement, i, fm); + maxTabWidth = Math.max(maxTabWidth, rect.width); + + if (rect.x != 2 + insets.left && rect.x + rect.width > breakAt) + { + if (runCount > tabRuns.length - 1) + expandTabRunsArray(); + tabRuns[runCount] = i; + runCount++; + rect.x = x; + } + + rect.y = y; + rect.height = maxTabHeight; + if (i == selectedIndex) + selectedRun = runCount - 1; + } } else { - int maxWidth = calculateMaxTabWidth(tabPlacement); - calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; - max = calcRect.height + tabAreaInsets.top + insets.top; - - int height = 0; - start += tabAreaInsets.top + insets.top; - int runHeight = start; - - int fontHeight = fm.getHeight(); - for (int i = 0; i < tabCount; i++) { - height = calculateTabHeight(tabPlacement, i, fontHeight); - if (runHeight + height > max) + rect = rects[i]; + if (i > 0) { - runHeight = tabAreaInsets.top + insets.top - + getTabRunIndent(tabPlacement, ++runs); - rects[i] = new Rectangle(insets.left + tabAreaInsets.left, - runHeight, maxWidth, height); - runHeight += height; - if (runs > tabRuns.length - 1) - expandTabRunsArray(); - tabRuns[runs] = i; + rect.y = rects[i - 1].y + rects[i - 1].height; } else { - rects[i] = new Rectangle(insets.left + tabAreaInsets.left, - runHeight, maxWidth, height); - runHeight += height; + tabRuns[0] = 0; + runCount = 1; + maxTabHeight = 0; + rect.y = y; } - } - runs++; + rect.height = calculateTabHeight(tabPlacement, i, + fm.getHeight()); + maxTabHeight = Math.max(maxTabHeight, rect.height); - tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay - + tabAreaInsets.left + tabAreaInsets.right; - tabAreaRect.height = tabPane.getHeight() - insets.top - - insets.bottom; - tabAreaRect.y = insets.top; - contentRect.width = tabPane.getWidth() - insets.left - insets.right - - tabAreaRect.width; - contentRect.height = tabAreaRect.height; - contentRect.y = insets.top; - if (tabPlacement == SwingConstants.LEFT) - { - tabAreaRect.x = insets.left; - contentRect.x = tabAreaRect.x + tabAreaRect.width; - } - else - { - contentRect.x = insets.left; - tabAreaRect.x = contentRect.x + contentRect.width; + if (rect.y != 2 + insets.top && rect.y + rect.height > breakAt) + { + if (runCount > tabRuns.length - 1) + expandTabRunsArray(); + tabRuns[runCount] = i; + runCount++; + rect.y = y; + } + + rect.x = x; + rect.width = maxTabWidth; + + if (i == selectedIndex) + selectedRun = runCount - 1; } } - runCount = runs; - if (runCount > tabRuns.length) - expandTabRunsArray(); - - tabRuns[0] = 0; - normalizeTabRuns(tabPlacement, tabCount, start, max); - selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex()); - if (shouldRotateTabRuns(tabPlacement)) - rotateTabRuns(tabPlacement, selectedRun); - // Need to pad the runs and move them to the correct location. - for (int i = 0; i < runCount; i++) + if (runCount > 1) { - int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, i); - if (shouldPadTabRun(tabPlacement, i)) - padTabRun(tabPlacement, first, last, max); - - // Done padding, now need to move it. - if (tabPlacement == SwingConstants.TOP && i > 0) + int start; + if (tabPlacement == SwingConstants.TOP + || tabPlacement == SwingConstants.BOTTOM) + start = y; + else + start = x; + normalizeTabRuns(tabPlacement, tabCount, start, breakAt); + selectedRun = getRunForTab(tabCount, selectedIndex); + if (shouldRotateTabRuns(tabPlacement)) { - for (int j = first; j <= last; j++) - rects[j].y += (runCount - i) * maxTabHeight - - (runCount - i) * tabRunOverlay; + rotateTabRuns(tabPlacement, selectedRun); } + } - if (tabPlacement == SwingConstants.BOTTOM) + // Pad the runs. + int tabRunOverlay = getTabRunOverlay(tabPlacement); + for (int i = runCount - 1; i >= 0; --i) + { + int start = tabRuns[i]; + int nextIndex; + if (i == runCount - 1) + nextIndex = 0; + else + nextIndex = i + 1; + int next = tabRuns[nextIndex]; + int end = (next != 0 ? next - 1 : tabCount - 1); + if (tabPlacement == SwingConstants.TOP + || tabPlacement == SwingConstants.BOTTOM) { - int height = tabPane.getBounds().height - insets.bottom - - tabAreaInsets.bottom; - int adjustment; - if (i == 0) - adjustment = height - maxTabHeight; + for (int j = start; j <= end; ++j) + { + rect = rects[j]; + rect.y = y; + rect.x += getTabRunIndent(tabPlacement, i); + } + if (shouldPadTabRun(tabPlacement, i)) + { + padTabRun(tabPlacement, start, end, breakAt); + } + if (tabPlacement == BOTTOM) + y -= (maxTabHeight - tabRunOverlay); else - adjustment = height - (runCount - i + 1) * maxTabHeight - - (runCount - i) * tabRunOverlay; - - for (int j = first; j <= last; j++) - rects[j].y = adjustment; - } - - if (tabPlacement == SwingConstants.LEFT && i > 0) - { - for (int j = first; j <= last; j++) - rects[j].x += (runCount - i) * maxTabWidth - - (runCount - i) * tabRunOverlay; + y += (maxTabHeight - tabRunOverlay); } - - if (tabPlacement == SwingConstants.RIGHT) + else { - int width = tabPane.getBounds().width - insets.right - - tabAreaInsets.right; - int adjustment; - if (i == 0) - adjustment = width - maxTabWidth; + for (int j = start; j <= end; ++j) + { + rect = rects[j]; + rect.x = x; + rect.y += getTabRunIndent(tabPlacement, i); + } + if (shouldPadTabRun(tabPlacement, i)) + { + padTabRun(tabPlacement, start, end, breakAt); + } + if (tabPlacement == RIGHT) + x -= (maxTabWidth - tabRunOverlay); else - adjustment = width - (runCount - i + 1) * maxTabWidth - + (runCount - i) * tabRunOverlay; - - for (int j = first; j <= last; j++) - rects[j].x = adjustment; + x += (maxTabWidth - tabRunOverlay); + } } - padSelectedTab(tabPlacement, tabPane.getSelectedIndex()); + padSelectedTab(tabPlacement, selectedIndex); } /** @@ -528,6 +527,58 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants public void layoutContainer(Container parent) { calculateLayoutInfo(); + + int tabPlacement = tabPane.getTabPlacement(); + Insets insets = tabPane.getInsets(); + int childCount = tabPane.getComponentCount(); + if (childCount > 0) + { + int compX; + int compY; + int tabAreaWidth = 0; + int tabAreaHeight = 0; + switch (tabPlacement) + { + case LEFT: + tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount, + maxTabWidth); + compX = tabAreaWidth + insets.left + contentBorderInsets.left; + compY = insets.top + contentBorderInsets.top; + break; + case RIGHT: + tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount, + maxTabWidth); + compX = insets.left + contentBorderInsets.left; + compY = insets.top + contentBorderInsets.top; + break; + case BOTTOM: + tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount, + maxTabHeight); + compX = insets.left + contentBorderInsets.left; + compY = insets.top + contentBorderInsets.top; + break; + case TOP: + default: + tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount, + maxTabHeight); + compX = insets.left + contentBorderInsets.left; + compY = tabAreaHeight + insets.top + contentBorderInsets.top; + } + Rectangle bounds = tabPane.getBounds(); + int compWidth = bounds.width - tabAreaWidth - insets.left + - insets.right - contentBorderInsets.left + - contentBorderInsets.right; + int compHeight = bounds.height - tabAreaHeight - insets.top + - insets.bottom - contentBorderInsets.top + - contentBorderInsets.bottom; + + + for (int i = 0; i < childCount; ++i) + { + Component c = tabPane.getComponent(i); + c.setBounds(compX, compY, compWidth, compHeight); + } + } } /** @@ -1288,6 +1339,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int[] tabRuns; /** + * Indicates if the layout of the tab runs is ok or not. This is package + * private to avoid a synthetic accessor method. + */ + boolean tabRunsDirty; + + /** * This is the keystroke for moving down. * * @deprecated 1.3 @@ -1343,6 +1400,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants transient Rectangle contentRect; /** + * The index over which the mouse is currently moving. + */ + private int rolloverTab; + + /** + * Determines if tabs are painted opaque or not. This can be adjusted using + * the UIManager property 'TabbedPane.tabsOpaque'. + */ + private boolean tabsOpaque; + + /** * Creates a new BasicTabbedPaneUI object. */ public BasicTabbedPaneUI() @@ -1557,6 +1625,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants selectedTabPadInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabPadInsets"); tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets"); contentBorderInsets = UIManager.getInsets("TabbedPane.tabbedPaneContentBorderInsets"); + tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque"); calcRect = new Rectangle(); tabRuns = new int[10]; @@ -1585,9 +1654,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants lightHighlight = null; highlight = null; - tabPane.setBackground(null); - tabPane.setForeground(null); - tabPane.setFont(null); + // Install UI colors and fonts. + LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background", + "TabbedPane.foreground", + "TabbedPane.font"); } /** @@ -1666,6 +1736,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants * This method installs keyboard actions for the JTabbedPane. */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: Implement. } @@ -1674,6 +1745,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants * This method uninstalls keyboard actions for the JTabbedPane. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: Implement. } @@ -1710,6 +1782,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void paint(Graphics g, JComponent c) { + if (!tabPane.isValid()) + tabPane.validate(); + if (tabPane.getTabCount() == 0) return; if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) @@ -1735,42 +1810,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // Please note: the ordering of the painting is important. // we WANT to paint the outermost run first and then work our way in. int tabCount = tabPane.getTabCount(); - int currRun = 1; - - if (tabCount < 1) - return; - - if (runCount > 1) - currRun = 0; - for (int i = 0; i < runCount; i++) + for (int i = runCount - 1; i >= 0; --i) { - int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; - if (isScroll) - first = currentScrollLocation; - else if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, currRun); - if (isScroll) + int start = tabRuns[i]; + int next; + if (i == runCount - 1) + next = tabRuns[0]; + else + next = tabRuns[i + 1]; + int end = (next != 0 ? next - 1 : tabCount - 1); + for (int j = start; j <= end; ++j) { - for (int k = first; k < tabCount; k++) + if (j != selectedIndex) { - if (rects[k].x + rects[k].width - rects[first].x > viewport - .getWidth()) - { - last = k; - break; - } + paintTab(g, tabPlacement, rects, j, ir, tr); } } - - for (int j = first; j <= last; j++) - { - if (j != selectedIndex || isScroll) - paintTab(g, tabPlacement, rects, j, ir, tr); - } - currRun = getPreviousTabRun(currRun); } - if (! isScroll) + + // Paint selected tab in front of every other tab. + if (selectedIndex >= 0) paintTab(g, tabPlacement, rects, selectedIndex, ir, tr); } @@ -1788,49 +1847,34 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect) { - FontMetrics fm = getFontMetrics(); - Icon icon = getIconForTab(tabIndex); - String title = tabPane.getTitleAt(tabIndex); + Rectangle rect = rects[tabIndex]; boolean isSelected = tabIndex == tabPane.getSelectedIndex(); - calcRect = getTabBounds(tabPane, tabIndex); - - int x = calcRect.x; - int y = calcRect.y; - int w = calcRect.width; - int h = calcRect.height; - if (getRunForTab(tabPane.getTabCount(), tabIndex) == 1) + // Paint background if necessary. + if (tabsOpaque || tabPane.isOpaque()) { - Insets insets = getTabAreaInsets(tabPlacement); - switch (tabPlacement) - { - case TOP: - h += insets.bottom; - break; - case LEFT: - w += insets.right; - break; - case BOTTOM: - y -= insets.top; - h += insets.top; - break; - case RIGHT: - x -= insets.left; - w += insets.left; - break; - } + paintTabBackground(g, tabPlacement, tabIndex, rect.x, rect.y, + rect.width, rect.height, isSelected); } - layoutLabel(tabPlacement, fm, tabIndex, title, icon, calcRect, iconRect, - textRect, isSelected); - paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected); - paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected); + // Paint border. + paintTabBorder(g, tabPlacement, tabIndex, rect.x, rect.y, rect.width, + rect.height, isSelected); - // FIXME: Paint little folding corner and jagged edge clipped tab. - if (icon != null) - paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); - if (title != null && ! title.equals("")) - paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title, + + // Layout label. + FontMetrics fm = getFontMetrics(); + Icon icon = getIconForTab(tabIndex); + String title = tabPane.getTitleAt(tabIndex); + layoutLabel(tabPlacement, fm, tabIndex, title, icon, rect, iconRect, textRect, isSelected); + // Paint the text. + paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title, + textRect, isSelected); + // Paint icon if necessary. + paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); + // Paint focus indicator. + paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, textRect, + isSelected); } /** @@ -1902,6 +1946,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants FontMetrics metrics, int tabIndex, String title, Rectangle textRect, boolean isSelected) { + g.setFont(font); View textView = getTextViewForTab(tabIndex); if (textView != null) { @@ -1909,54 +1954,48 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants return; } - Color fg = tabPane.getForegroundAt(tabIndex); - if (fg == null) - fg = tabPane.getForeground(); - Color bg = tabPane.getBackgroundAt(tabIndex); - if (bg == null) - bg = tabPane.getBackground(); - - Color saved_color = g.getColor(); - Font f = g.getFont(); - g.setFont(font); + int ascent = metrics.getAscent(); - if (tabPane.isEnabledAt(tabIndex)) + int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); + if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) { + Color fg = tabPane.getForegroundAt(tabIndex); + if (isSelected && (fg instanceof UIResource)) + { + Color selectionForeground = + UIManager.getColor("TabbedPane.selectionForeground"); + if (selectionForeground != null) + fg = selectionForeground; + } g.setColor(fg); - int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); - if (mnemIndex != -1) BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, textRect.x, - textRect.y - + metrics.getAscent()); + textRect.y + ascent); else - g.drawString(title, textRect.x, textRect.y + metrics.getAscent()); + g.drawString(title, textRect.x, textRect.y + ascent); } else { + Color bg = tabPane.getBackgroundAt(tabIndex); g.setColor(bg.brighter()); - - int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); - if (mnemIndex != -1) BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, - textRect.x, textRect.y); + textRect.x, textRect.y + + ascent); else - g.drawString(title, textRect.x, textRect.y); + g.drawString(title, textRect.x, textRect.y + ascent); g.setColor(bg.darker()); if (mnemIndex != -1) BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, textRect.x + 1, - textRect.y + 1); + textRect.y + 1 + + ascent); else - g.drawString(title, textRect.x + 1, textRect.y + 1); + g.drawString(title, textRect.x + 1, textRect.y + 1 + ascent); } - - g.setColor(saved_color); - g.setFont(f); } /** @@ -2009,14 +2048,45 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Rectangle iconRect, Rectangle textRect, boolean isSelected) { - Color saved = g.getColor(); - calcRect = iconRect.union(textRect); - - g.setColor(focus); - - g.drawRect(calcRect.x, calcRect.y, calcRect.width, calcRect.height); - - g.setColor(saved); + if (tabPane.hasFocus() && isSelected) + { + Rectangle rect = rects[tabIndex]; + // The focus rectangle. + int x; + int y; + int w; + int h; + + g.setColor(focus); + switch (tabPlacement) + { + case LEFT: + x = rect.x + 3; + y = rect.y + 3; + w = rect.width - 5; + h = rect.height - 6; + break; + case RIGHT: + x = rect.x + 2; + y = rect.y + 3; + w = rect.width - 6; + h = rect.height - 5; + break; + case BOTTOM: + x = rect.x + 3; + y = rect.y + 2; + w = rect.width - 6; + h = rect.height - 5; + break; + case TOP: + default: + x = rect.x + 3; + y = rect.y + 3; + w = rect.width - 6; + h = rect.height - 5; + } + BasicGraphicsUtils.drawDashedRect(g, x, y, w, h); + } } /** @@ -2109,10 +2179,44 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { - int x = contentRect.x; - int y = contentRect.y; - int w = contentRect.width; - int h = contentRect.height; + int width = tabPane.getWidth(); + int height = tabPane.getHeight(); + Insets insets = tabPane.getInsets(); + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); + + // Calculate coordinates of content area. + int x = insets.left; + int y = insets.top; + int w = width - insets.left - insets.right; + int h = height - insets.top - insets.bottom; + + switch (tabPlacement) + { + case LEFT: + x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); + w -= (x - insets.left); + break; + case RIGHT: + w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); + break; + case BOTTOM: + h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); + break; + case TOP: + default: + y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); + h -= (y - insets.top); + } + + // Fill background if necessary. + if (tabPane.isOpaque()) + { + Color bg = UIManager.getColor("TabbedPane.contentAreaColor"); + g.setColor(bg); + g.fillRect(x, y, w, h); + } + + // Paint border. paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h); paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h); paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h); @@ -2332,23 +2436,23 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public int tabForCoordinate(JTabbedPane pane, int x, int y) { - Point p = new Point(x, y); + if (! tabPane.isValid()) + tabPane.validate(); + int tabCount = tabPane.getTabCount(); - int currRun = 1; - for (int i = 0; i < runCount; i++) + int index = -1; + for (int i = 0; i < tabCount; ++i) { - int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, currRun); - for (int j = first; j <= last; j++) + if (rects[i].contains(x, y)) { - if (getTabBounds(pane, j).contains(p)) - return j; + index = i; + break; } - currRun = getNextTabRun(currRun); } - return -1; + + // FIXME: Handle scrollable tab layout. + + return index; } /** @@ -2455,10 +2559,23 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected int lastTabInRun(int tabCount, int run) { - if (tabRuns[run] == 0) - return tabCount - 1; + int lastTab; + if (runCount == 1) + lastTab = tabCount - 1; else - return tabRuns[run] - 1; + { + int nextRun; + if (run == runCount - 1) + nextRun = 0; + else + nextRun = run + 1; + + if (tabRuns[nextRun] == 0) + lastTab = tabCount - 1; + else + lastTab = tabRuns[nextRun] - 1; + } + return lastTab; } /** @@ -2554,24 +2671,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) { - Icon icon = getIconForTab(tabIndex); - Insets insets = getTabInsets(tabPlacement, tabIndex); + // FIXME: Handle HTML somehow. - int height = 0; + int height = fontHeight; + Icon icon = getIconForTab(tabIndex); + Insets tabInsets = getTabInsets(tabPlacement, tabIndex); if (icon != null) - { - Rectangle vr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - layoutLabel(tabPlacement, getFontMetrics(), tabIndex, - tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, - tabIndex == tabPane.getSelectedIndex()); - height = tr.union(ir).height; - } - else - height = fontHeight; - - height += insets.top + insets.bottom; + height = Math.max(height, icon.getIconHeight()); + height += tabInsets.top + tabInsets.bottom + 2; return height; } @@ -2704,9 +2811,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected Insets getTabInsets(int tabPlacement, int tabIndex) { - Insets target = new Insets(0, 0, 0, 0); - rotateInsets(tabInsets, target, tabPlacement); - return target; + return tabInsets; } /** @@ -3068,4 +3173,33 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants break; } } + + /** + * Sets the tab which should be highlighted when in rollover mode. And + * <code>index</code> of <code>-1</code> means that the rollover tab + * is deselected (i.e. the mouse is outside of the tabarea). + * + * @param index the index of the tab that is under the mouse, <code>-1</code> + * for no tab + * + * @since 1.5 + */ + protected void setRolloverTab(int index) + { + rolloverTab = index; + } + + /** + * Retunrs the index of the tab over which the mouse is currently moving, + * or <code>-1</code> for no tab. + * + * @return the index of the tab over which the mouse is currently moving, + * or <code>-1</code> for no tab + * + * @since 1.5 + */ + protected int getRolloverTab() + { + return rolloverTab; + } } diff --git a/javax/swing/plaf/basic/BasicTableHeaderUI.java b/javax/swing/plaf/basic/BasicTableHeaderUI.java index c6f9e37df..cfbebda21 100644 --- a/javax/swing/plaf/basic/BasicTableHeaderUI.java +++ b/javax/swing/plaf/basic/BasicTableHeaderUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; @@ -97,6 +99,11 @@ public class BasicTableHeaderUI extends TableHeaderUI * The header cell border. */ private Border cellBorder; + + /** + * Original mouse cursor prior to resizing. + */ + private Cursor originalCursor; /** * If not null, one of the columns is currently being dragged. @@ -243,6 +250,7 @@ public class BasicTableHeaderUI extends TableHeaderUI if (onBoundary) { + originalCursor = header.getCursor(); if (p < x) header.setCursor(Cursor.getPredefinedCursor (Cursor.W_RESIZE_CURSOR)); @@ -252,7 +260,7 @@ public class BasicTableHeaderUI extends TableHeaderUI } else { - header.setCursor(Cursor.getDefaultCursor()); + header.setCursor(originalCursor); header.setResizingColumn(null); } @@ -343,7 +351,7 @@ public class BasicTableHeaderUI extends TableHeaderUI showingResizeCursor = false; if (timer != null) timer.stop(); - header.setCursor(Cursor.getDefaultCursor()); + header.setCursor(originalCursor); } /** @@ -415,6 +423,7 @@ public class BasicTableHeaderUI extends TableHeaderUI } protected void installKeyboardActions() + throws NotImplementedException { // TODO: Implement this properly. } @@ -447,6 +456,7 @@ public class BasicTableHeaderUI extends TableHeaderUI } protected void uninstallKeyboardActions() + throws NotImplementedException { // TODO: Implement this properly. } diff --git a/javax/swing/plaf/basic/BasicTableUI.java b/javax/swing/plaf/basic/BasicTableUI.java index 8360a9ec7..ef491cbf1 100644 --- a/javax/swing/plaf/basic/BasicTableUI.java +++ b/javax/swing/plaf/basic/BasicTableUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Color; import java.awt.Component; import java.awt.ComponentOrientation; @@ -88,7 +90,7 @@ public class BasicTableUI extends TableUI protected FocusListener focusListener; protected KeyListener keyListener; - protected MouseInputListener mouseInputListener; + protected MouseInputListener mouseInputListener; protected CellRendererPane rendererPane; protected JTable table; @@ -115,6 +117,8 @@ public class BasicTableUI extends TableUI /** * Receives notification that a key has been pressed and released. + * Activates the editing session for the focused cell by pressing the + * character keys. * * @param event the key event */ @@ -122,6 +126,16 @@ public class BasicTableUI extends TableUI { // Key events should be handled through the InputMap/ActionMap mechanism // since JDK1.3. This class is only there for backwards compatibility. + + // Editor activation is a specific kind of response to ''any'' + // character key. Hence it is handled here. + if (!table.isEditing() && table.isEnabled()) + { + int r = table.getSelectedRow(); + int c = table.getSelectedColumn(); + if (table.isCellEditable(r, c)) + table.editCellAt(r, c); + } } /** @@ -509,11 +523,9 @@ public class BasicTableUI extends TableUI if (command.equals("selectPreviousRowExtendSelection")) { rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0)); - colModel.setLeadSelectionIndex(colLead); } else if (command.equals("selectLastColumn")) { - rowModel.setSelectionInterval(rowLead, rowLead); colModel.setSelectionInterval(colMax, colMax); } else if (command.equals("startEditing")) @@ -524,53 +536,43 @@ public class BasicTableUI extends TableUI else if (command.equals("selectFirstRowExtendSelection")) { rowModel.setLeadSelectionIndex(0); - colModel.setLeadSelectionIndex(colLead); } else if (command.equals("selectFirstColumn")) { - rowModel.setSelectionInterval(rowLead, rowLead); colModel.setSelectionInterval(0, 0); } else if (command.equals("selectFirstColumnExtendSelection")) { colModel.setLeadSelectionIndex(0); - rowModel.setLeadSelectionIndex(rowLead); } else if (command.equals("selectLastRow")) { rowModel.setSelectionInterval(rowMax,rowMax); - colModel.setSelectionInterval(colLead, colLead); } else if (command.equals("selectNextRowExtendSelection")) { rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax)); - colModel.setLeadSelectionIndex(colLead); } else if (command.equals("selectFirstRow")) { rowModel.setSelectionInterval(0,0); - colModel.setSelectionInterval(colLead, colLead); } else if (command.equals("selectNextColumnExtendSelection")) { colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax)); - rowModel.setLeadSelectionIndex(rowLead); } else if (command.equals("selectLastColumnExtendSelection")) { colModel.setLeadSelectionIndex(colMax); - rowModel.setLeadSelectionIndex(rowLead); } else if (command.equals("selectPreviousColumnExtendSelection")) { colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0)); - rowModel.setLeadSelectionIndex(rowLead); } else if (command.equals("selectNextRow")) { rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax), Math.min(rowLead + 1, rowMax)); - colModel.setSelectionInterval(colLead,colLead); } else if (command.equals("scrollUpExtendSelection")) { @@ -589,7 +591,6 @@ public class BasicTableUI extends TableUI { rowModel.setSelectionInterval(Math.max(rowLead - 1, 0), Math.max(rowLead - 1, 0)); - colModel.setSelectionInterval(colLead,colLead); } else if (command.equals("scrollRightChangeSelection")) { @@ -606,7 +607,6 @@ public class BasicTableUI extends TableUI } else if (command.equals("selectPreviousColumn")) { - rowModel.setSelectionInterval(rowLead,rowLead); colModel.setSelectionInterval(Math.max(colLead - 1, 0), Math.max(colLead - 1, 0)); } @@ -715,7 +715,6 @@ public class BasicTableUI extends TableUI } else if (command.equals("selectNextColumn")) { - rowModel.setSelectionInterval(rowLead,rowLead); colModel.setSelectionInterval(Math.min(colLead + 1, colMax), Math.min(colLead + 1, colMax)); } @@ -903,7 +902,6 @@ public class BasicTableUI extends TableUI table.scrollRectToVisible (table.getCellRect(rowModel.getLeadSelectionIndex(), colModel.getLeadSelectionIndex(), false)); - table.repaint(); } /** @@ -1172,6 +1170,7 @@ public class BasicTableUI extends TableUI } protected void uninstallKeyboardActions() + throws NotImplementedException { // TODO: Implement this properly. } @@ -1247,16 +1246,18 @@ public class BasicTableUI extends TableUI if (rn == -1) rn = table.getRowCount() - 1; + int columnMargin = table.getColumnModel().getColumnMargin(); + int rowMargin = table.getRowMargin(); + TableColumnModel cmodel = table.getColumnModel(); int [] widths = new int[cn+1]; for (int i = c0; i <=cn ; i++) { - widths[i] = cmodel.getColumn(i).getWidth(); + widths[i] = cmodel.getColumn(i).getWidth() - columnMargin; } Rectangle bounds = table.getCellRect(r0, c0, false); - bounds.height = table.getRowHeight()+table.getRowMargin(); - + // The left boundary of the area being repainted. int left = bounds.x; @@ -1266,9 +1267,6 @@ public class BasicTableUI extends TableUI // The bottom boundary of the area being repainted. int bottom; - // The cell height. - int height = bounds.height; - // paint the cell contents Color grid = table.getGridColor(); for (int r = r0; r <= rn; ++r) @@ -1277,25 +1275,28 @@ public class BasicTableUI extends TableUI { bounds.width = widths[c]; paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c)); - bounds.x += widths[c]; + bounds.x += widths[c] + columnMargin; } - bounds.y += height; bounds.x = left; + bounds.y += table.getRowHeight(r) + rowMargin; + // Update row height for tables with custom heights. + bounds.height = table.getRowHeight(r + 1); } - bottom = bounds.y; + bottom = bounds.y - rowMargin; // paint vertical grid lines if (grid != null && table.getShowVerticalLines()) { Color save = gfx.getColor(); gfx.setColor(grid); - int x = left; - + int x = left - columnMargin; for (int c = c0; c <= cn; ++c) { + // The vertical grid is draw right from the cells, so we + // add before drawing. + x += widths[c] + columnMargin; gfx.drawLine(x, top, x, bottom); - x += widths[c]; } gfx.setColor(save); } @@ -1305,11 +1306,13 @@ public class BasicTableUI extends TableUI { Color save = gfx.getColor(); gfx.setColor(grid); - int y = top; + int y = top - rowMargin; for (int r = r0; r <= rn; ++r) { + // The horizontal grid is draw below the cells, so we + // add before drawing. + y += table.getRowHeight(r) + rowMargin; gfx.drawLine(left, y, p2.x, y); - y += height; } gfx.setColor(save); } diff --git a/javax/swing/plaf/basic/BasicTextAreaUI.java b/javax/swing/plaf/basic/BasicTextAreaUI.java index 36854e07f..93e119b31 100644 --- a/javax/swing/plaf/basic/BasicTextAreaUI.java +++ b/javax/swing/plaf/basic/BasicTextAreaUI.java @@ -1,5 +1,5 @@ /* BasicTextAreaUI.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -108,6 +108,9 @@ public class BasicTextAreaUI extends BasicTextUI JTextArea comp = (JTextArea)getComponent(); if (ev.getPropertyName() == "lineWrap" || ev.getPropertyName() == "wrapStyleWord") - modelChanged(); + { + // Changes the View (without modifying the document or it's listeners). + setView(create(textComponent.getDocument().getDefaultRootElement())); + } } } diff --git a/javax/swing/plaf/basic/BasicTextFieldUI.java b/javax/swing/plaf/basic/BasicTextFieldUI.java index 4e2ca9f93..89c4e5a75 100644 --- a/javax/swing/plaf/basic/BasicTextFieldUI.java +++ b/javax/swing/plaf/basic/BasicTextFieldUI.java @@ -1,5 +1,5 @@ /* BasicTextFieldUI.java - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,6 +42,7 @@ import java.beans.PropertyChangeEvent; import javax.swing.JComponent; import javax.swing.UIDefaults; +import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.text.Element; import javax.swing.text.FieldView; @@ -83,6 +84,9 @@ public class BasicTextFieldUI extends BasicTextUI * Receives notification whenever one of the text component's bound * properties changes. Here we check for the editable and enabled * properties and adjust the background color accordingly. + * + * <p>The colors are only changed if they are not a + * <code>ColorUIResource</code>.</p> * * @param event the property change event */ @@ -91,10 +95,11 @@ public class BasicTextFieldUI extends BasicTextUI if (event.getPropertyName().equals("editable")) { boolean editable = ((Boolean) event.getNewValue()).booleanValue(); - if (editable) - textComponent.setBackground(background); - else - textComponent.setBackground(inactiveBackground); + + // Changing the color only if the current background is an instance of + // ColorUIResource is the behavior of the RI. + if (textComponent.getBackground() instanceof ColorUIResource) + textComponent.setBackground(editable ? background : inactiveBackground); } } } diff --git a/javax/swing/plaf/basic/BasicTextUI.java b/javax/swing/plaf/basic/BasicTextUI.java index bc5917105..3b620f049 100644 --- a/javax/swing/plaf/basic/BasicTextUI.java +++ b/javax/swing/plaf/basic/BasicTextUI.java @@ -38,14 +38,20 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.HeadlessException; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.beans.PropertyChangeEvent; @@ -55,7 +61,6 @@ import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; -import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; @@ -63,7 +68,6 @@ import javax.swing.UIManager; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.plaf.ActionMapUIResource; -import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TextUI; import javax.swing.plaf.UIResource; import javax.swing.text.AbstractDocument; @@ -436,6 +440,9 @@ public abstract class BasicTextUI extends TextUI */ public void changedUpdate(DocumentEvent ev) { + // Updates are forwarded to the View even if 'getVisibleEditorRect' + // method returns null. This means the View classes have to be + // aware of that possibility. rootView.changedUpdate(ev, getVisibleEditorRect(), rootView.getViewFactory()); } @@ -447,6 +454,9 @@ public abstract class BasicTextUI extends TextUI */ public void insertUpdate(DocumentEvent ev) { + // Updates are forwarded to the View even if 'getVisibleEditorRect' + // method returns null. This means the View classes have to be + // aware of that possibility. rootView.insertUpdate(ev, getVisibleEditorRect(), rootView.getViewFactory()); } @@ -458,6 +468,9 @@ public abstract class BasicTextUI extends TextUI */ public void removeUpdate(DocumentEvent ev) { + // Updates are forwarded to the View even if 'getVisibleEditorRect' + // method returns null. This means the View classes have to be + // aware of that possibility. rootView.removeUpdate(ev, getVisibleEditorRect(), rootView.getViewFactory()); } @@ -607,6 +620,44 @@ public abstract class BasicTextUI extends TextUI public void focusLost(FocusEvent e) { textComponent.repaint(); + + // Integrates Swing text components with the system clipboard: + // The idea is that if one wants to copy text around X11-style + // (select text and middle-click in the target component) the focus + // will move to the new component which gives the old focus owner the + // possibility to paste its selection into the clipboard. + if (!e.isTemporary() + && textComponent.getSelectionStart() + != textComponent.getSelectionEnd()) + { + SecurityManager sm = System.getSecurityManager(); + try + { + if (sm != null) + sm.checkSystemClipboardAccess(); + + Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection(); + if (cb != null) + { + StringSelection selection = new StringSelection(textComponent.getSelectedText()); + cb.setContents(selection, selection); + } + } + catch (SecurityException se) + { + // Not allowed to access the clipboard: Ignore and + // do not access it. + } + catch (HeadlessException he) + { + // There is no AWT: Ignore and do not access the + // clipboard. + } + catch (IllegalStateException ise) + { + // Clipboard is currently unavaible. + } + } } }; @@ -654,33 +705,23 @@ public abstract class BasicTextUI extends TextUI */ protected Keymap createKeymap() { - // FIXME: It seems to me that this method implementation is wrong. It seems - // to fetch the focusInputMap and transform it to the KeyBinding/Keymap - // implemenation. I would think that it should be done the other way, - // fetching the keybindings (from prefix + ".bindings") and transform - // it to the newer InputMap/ActionMap implementation. - JTextComponent.KeyBinding[] bindings = null; - String prefix = getPropertyPrefix(); - InputMapUIResource m = (InputMapUIResource) UIManager.get(prefix + ".focusInputMap"); - if (m != null) + String keymapName = getKeymapName(); + Keymap keymap = JTextComponent.getKeymap(keymapName); + if (keymap == null) { - KeyStroke[] keys = m.keys(); - int len = keys.length; - bindings = new JTextComponent.KeyBinding[len]; - for (int i = 0; i < len; i++) + Keymap parentMap = + JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP); + keymap = JTextComponent.addKeymap(keymapName, parentMap); + Object val = UIManager.get(getPropertyPrefix() + ".keyBindings"); + if (val != null && val instanceof JTextComponent.KeyBinding[]) { - KeyStroke curr = keys[i]; - bindings[i] = new JTextComponent.KeyBinding(curr, - (String) m.get(curr)); + JTextComponent.KeyBinding[] bindings = + (JTextComponent.KeyBinding[]) val; + JTextComponent.loadKeymap(keymap, bindings, + getComponent().getActions()); } } - if (bindings == null) - bindings = new JTextComponent.KeyBinding[0]; - - Keymap km = JTextComponent.addKeymap(getKeymapName(), - JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP)); - JTextComponent.loadKeymap(km, bindings, textComponent.getActions()); - return km; + return keymap; } /** @@ -688,11 +729,8 @@ public abstract class BasicTextUI extends TextUI */ protected void installKeyboardActions() { - // load key bindings for the older interface - Keymap km = JTextComponent.getKeymap(getKeymapName()); - if (km == null) - km = createKeymap(); - textComponent.setKeymap(km); + // This is only there for backwards compatibility. + textComponent.setKeymap(createKeymap()); // load any bindings for the newer InputMap / ActionMap interface SwingUtilities.replaceUIInputMap(textComponent, JComponent.WHEN_FOCUSED, @@ -793,6 +831,7 @@ public abstract class BasicTextUI extends TextUI * this UI. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: Uninstall keyboard actions here. } @@ -985,6 +1024,10 @@ public abstract class BasicTextUI extends TextUI public void damageRange(JTextComponent t, int p0, int p1, Position.Bias firstBias, Position.Bias secondBias) { + // Do nothing if the component cannot be properly displayed. + if (t.getWidth() == 0 || t.getHeight() == 0) + return; + try { // Limit p0 and p1 to sane values to prevent unfriendly @@ -999,7 +1042,10 @@ public abstract class BasicTextUI extends TextUI Rectangle l1 = modelToView(t, p0, firstBias); Rectangle l2 = modelToView(t, p1, secondBias); if (l1.y == l2.y) - t.repaint(l1.union(l2)); + { + SwingUtilities.computeUnion(l2.x, l2.y, l2.width, l2.height, l1); + t.repaint(l1); + } else { // The two rectangles lie on different lines and we need a @@ -1107,7 +1153,12 @@ public abstract class BasicTextUI extends TextUI Position.Bias[] biasRet) throws BadLocationException { - return 0; // TODO: Implement me. + // A comment in the spec of NavigationFilter.getNextVisualPositionFrom() + // suggests that this method should be implemented by forwarding the call + // the root view. + return rootView.getNextVisualPositionFrom(pos, b, + getVisibleEditorRect(), + direction, biasRet); } /** @@ -1163,7 +1214,10 @@ public abstract class BasicTextUI extends TextUI public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias) throws BadLocationException { - return rootView.modelToView(pos, getVisibleEditorRect(), bias).getBounds(); + Rectangle r = getVisibleEditorRect(); + + return (r != null) ? rootView.modelToView(pos, r, bias).getBounds() + : null; } /** @@ -1240,8 +1294,9 @@ public abstract class BasicTextUI extends TextUI int width = textComponent.getWidth(); int height = textComponent.getHeight(); + // Return null if the component has no valid size. if (width <= 0 || height <= 0) - return new Rectangle(0, 0, 0, 0); + return null; Insets insets = textComponent.getInsets(); return new Rectangle(insets.left, insets.top, diff --git a/javax/swing/plaf/basic/BasicToolBarUI.java b/javax/swing/plaf/basic/BasicToolBarUI.java index 5d718876f..80fec6a77 100644 --- a/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/javax/swing/plaf/basic/BasicToolBarUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.NotImplementedException; + import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -604,6 +606,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants * by the look and feel. */ protected void installKeyboardActions() + throws NotImplementedException { // FIXME: implement. } @@ -900,6 +903,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants * This method uninstalls keyboard actions installed by the UI. */ protected void uninstallKeyboardActions() + throws NotImplementedException { // FIXME: implement. } diff --git a/javax/swing/plaf/basic/BasicTreeUI.java b/javax/swing/plaf/basic/BasicTreeUI.java index 1c6e6c5e5..a143a962b 100644 --- a/javax/swing/plaf/basic/BasicTreeUI.java +++ b/javax/swing/plaf/basic/BasicTreeUI.java @@ -1,5 +1,5 @@ /* BasicTreeUI.java -- - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,8 @@ package javax.swing.plaf.basic; +import gnu.javax.swing.tree.GnuPath; + import java.awt.Color; import java.awt.Component; import java.awt.Dimension; @@ -55,6 +57,7 @@ import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; +import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; @@ -76,7 +79,6 @@ import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JScrollBar; import javax.swing.JScrollPane; -import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; @@ -96,17 +98,17 @@ import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TreeUI; -import javax.swing.text.Caret; +import javax.swing.plaf.metal.MetalIconFactory; import javax.swing.tree.AbstractLayoutCache; import javax.swing.tree.DefaultTreeCellEditor; import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.FixedHeightLayoutCache; import javax.swing.tree.TreeCellEditor; import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; +import javax.swing.tree.VariableHeightLayoutCache; /** * A delegate providing the user interface for <code>JTree</code> according to @@ -117,16 +119,17 @@ import javax.swing.tree.TreeSelectionModel; * @author Sascha Brawer (brawer@dandelis.ch) * @author Audrius Meskauskas (audriusa@bioinformatics.org) */ -public class BasicTreeUI extends TreeUI +public class BasicTreeUI + extends TreeUI { /** * The tree cell editing may be started by the single mouse click on the * selected cell. To separate it from the double mouse click, the editing - * session starts after this time (in ms) after that single click, and only - * no other clicks were performed during that time. + * session starts after this time (in ms) after that single click, and only no + * other clicks were performed during that time. */ - static int WAIT_TILL_EDITING = 900; - + static int WAIT_TILL_EDITING = 900; + /** Collapse Icon for the tree. */ protected transient Icon collapsedIcon; @@ -251,39 +254,45 @@ public class BasicTreeUI extends TreeUI int maxHeight = 0; /** Listeners */ - private PropertyChangeListener propertyChangeListener; + PropertyChangeListener propertyChangeListener; - private FocusListener focusListener; + FocusListener focusListener; - private TreeSelectionListener treeSelectionListener; + TreeSelectionListener treeSelectionListener; - private MouseListener mouseListener; + MouseListener mouseListener; - private KeyListener keyListener; + KeyListener keyListener; - private PropertyChangeListener selectionModelPropertyChangeListener; + PropertyChangeListener selectionModelPropertyChangeListener; - private ComponentListener componentListener; + ComponentListener componentListener; CellEditorListener cellEditorListener; - private TreeExpansionListener treeExpansionListener; + TreeExpansionListener treeExpansionListener; + + TreeModelListener treeModelListener; - private TreeModelListener treeModelListener; - /** * This timer fires the editing action after about 1200 ms if not reset during - * that time. It handles the editing start with the single mouse click - * (and not the double mouse click) on the selected tree node. + * that time. It handles the editing start with the single mouse click (and + * not the double mouse click) on the selected tree node. */ Timer startEditTimer; /** + * The zero size icon, used for expand controls, if they are not visible. + */ + static Icon nullIcon; + + /** * The special value of the mouse event is sent indicating that this is not * just the mouse click, but the mouse click on the selected node. Sending * such event forces to start the cell editing session. */ - static final MouseEvent EDIT = new MouseEvent(new Label(), 7,7,7,7,7,7, false); + static final MouseEvent EDIT = new MouseEvent(new Label(), 7, 7, 7, 7, 7, 7, + false); /** * Creates a new BasicTreeUI object. @@ -306,22 +315,21 @@ public class BasicTreeUI extends TreeUI treeExpansionListener = createTreeExpansionListener(); treeModelListener = createTreeModelListener(); - editingRow = -1; - lastSelectedRow = -1; + editingRow = - 1; + lastSelectedRow = - 1; } /** * Returns an instance of the UI delegate for the specified component. * - * @param c - * the <code>JComponent</code> for which we need a UI delegate for. + * @param c the <code>JComponent</code> for which we need a UI delegate for. * @return the <code>ComponentUI</code> for c. */ public static ComponentUI createUI(JComponent c) { return new BasicTreeUI(); } - + /** * Returns the Hash color. * @@ -335,8 +343,7 @@ public class BasicTreeUI extends TreeUI /** * Sets the Hash color. * - * @param color - * the <code>Color</code> to set the Hash to. + * @param color the <code>Color</code> to set the Hash to. */ protected void setHashColor(Color color) { @@ -347,8 +354,7 @@ public class BasicTreeUI extends TreeUI /** * Sets the left child's indent value. * - * @param newAmount - * is the new indent value for the left child. + * @param newAmount is the new indent value for the left child. */ public void setLeftChildIndent(int newAmount) { @@ -368,8 +374,7 @@ public class BasicTreeUI extends TreeUI /** * Sets the right child's indent value. * - * @param newAmount - * is the new indent value for the right child. + * @param newAmount is the new indent value for the right child. */ public void setRightChildIndent(int newAmount) { @@ -389,8 +394,7 @@ public class BasicTreeUI extends TreeUI /** * Sets the expanded icon. * - * @param newG - * is the new expanded icon. + * @param newG is the new expanded icon. */ public void setExpandedIcon(Icon newG) { @@ -410,8 +414,7 @@ public class BasicTreeUI extends TreeUI /** * Sets the collapsed icon. * - * @param newG - * is the new collapsed icon. + * @param newG is the new collapsed icon. */ public void setCollapsedIcon(Icon newG) { @@ -431,8 +434,7 @@ public class BasicTreeUI extends TreeUI /** * Updates the componentListener, if necessary. * - * @param largeModel - * sets this.largeModel to it. + * @param largeModel sets this.largeModel to it. */ protected void setLargeModel(boolean largeModel) { @@ -457,13 +459,12 @@ public class BasicTreeUI extends TreeUI /** * Sets the row height. * - * @param rowHeight - * is the height to set this.rowHeight to. + * @param rowHeight is the height to set this.rowHeight to. */ protected void setRowHeight(int rowHeight) { if (rowHeight == 0) - rowHeight = Math.max(getMaxHeight(tree), 20); + rowHeight = getMaxHeight(tree); treeState.setRowHeight(rowHeight); } @@ -474,15 +475,14 @@ public class BasicTreeUI extends TreeUI */ protected int getRowHeight() { - return treeState.getRowHeight(); + return tree.getRowHeight(); } /** * Sets the TreeCellRenderer to <code>tcr</code>. This invokes * <code>updateRenderer</code>. * - * @param tcr - * is the new TreeCellRenderer. + * @param tcr is the new TreeCellRenderer. */ protected void setCellRenderer(TreeCellRenderer tcr) { @@ -507,13 +507,13 @@ public class BasicTreeUI extends TreeUI /** * Sets the tree's model. * - * @param model - * to set the treeModel to. + * @param model to set the treeModel to. */ protected void setModel(TreeModel model) { tree.setModel(model); treeModel = tree.getModel(); + treeState.setModel(treeModel); } /** @@ -529,8 +529,7 @@ public class BasicTreeUI extends TreeUI /** * Sets the root to being visible. * - * @param newValue - * sets the visibility of the root + * @param newValue sets the visibility of the root */ protected void setRootVisible(boolean newValue) { @@ -550,8 +549,7 @@ public class BasicTreeUI extends TreeUI /** * Determines whether the node handles are to be displayed. * - * @param newValue - * sets whether or not node handles should be displayed. + * @param newValue sets whether or not node handles should be displayed. */ protected void setShowsRootHandles(boolean newValue) { @@ -571,8 +569,7 @@ public class BasicTreeUI extends TreeUI /** * Sets the cell editor. * - * @param editor - * to set the cellEditor to. + * @param editor to set the cellEditor to. */ protected void setCellEditor(TreeCellEditor editor) { @@ -593,8 +590,7 @@ public class BasicTreeUI extends TreeUI /** * Configures the receiver to allow, or not allow, editing. * - * @param newValue - * sets the receiver to allow editing if true. + * @param newValue sets the receiver to allow editing if true. */ protected void setEditable(boolean newValue) { @@ -615,8 +611,7 @@ public class BasicTreeUI extends TreeUI * Resets the selection model. The appropriate listeners are installed on the * model. * - * @param newLSM - * resets the selection model. + * @param newLSM resets the selection model. */ protected void setSelectionModel(TreeSelectionModel newLSM) { @@ -642,35 +637,23 @@ public class BasicTreeUI extends TreeUI * path will be drawn to. Will return null if any component in path is * currently valid. * - * @param tree - * is the current tree the path will be drawn to. - * @param path - * is the current path the tree to draw to. + * @param tree is the current tree the path will be drawn to. + * @param path is the current path the tree to draw to. * @return the Rectangle enclosing the label portion that the last item in the * path will be drawn to. */ public Rectangle getPathBounds(JTree tree, TreePath path) { - int row = -1; - Object cell = null; - if (path != null) - { - row = getRowForPath(tree, path); - cell = path.getLastPathComponent(); - } - return nodeDimensions.getNodeDimensions(cell, row, getLevel(cell), - tree.isExpanded(path), - new Rectangle()); + return treeState.getBounds(path, new Rectangle()); } /** * Returns the max height of all the nodes in the tree. * - * @param tree - - * the current tree + * @param tree - the current tree * @return the max height. */ - private int getMaxHeight(JTree tree) + int getMaxHeight(JTree tree) { if (maxHeight != 0) return maxHeight; @@ -692,72 +675,61 @@ public class BasicTreeUI extends TreeUI maxHeight = Math.max(maxHeight, iconHeight + gap); } - + + treeState.setRowHeight(maxHeight); return maxHeight; } + + /** + * Get the tree node icon. + */ + Icon getNodeIcon(TreePath path) + { + Object node = path.getLastPathComponent(); + if (treeModel.isLeaf(node)) + return UIManager.getIcon("Tree.leafIcon"); + else if (treeState.getExpandedState(path)) + return UIManager.getIcon("Tree.openIcon"); + else + return UIManager.getIcon("Tree.closedIcon"); + } /** * Returns the path for passed in row. If row is not visible null is returned. * - * @param tree - * is the current tree to return path for. - * @param row - * is the row number of the row to return. + * @param tree is the current tree to return path for. + * @param row is the row number of the row to return. * @return the path for passed in row. If row is not visible null is returned. */ public TreePath getPathForRow(JTree tree, int row) { - if (treeModel != null && currentVisiblePath != null) - { - Object[] nodes = currentVisiblePath.getPath(); - if (row < nodes.length) - return new TreePath(getPathToRoot(nodes[row], 0)); - } - return null; + return treeState.getPathForRow(row); } /** * Returns the row that the last item identified in path is visible at. Will * return -1 if any of the elments in the path are not currently visible. * - * @param tree - * is the current tree to return the row for. - * @param path - * is the path used to find the row. + * @param tree is the current tree to return the row for. + * @param path is the path used to find the row. * @return the row that the last item identified in path is visible at. Will * return -1 if any of the elments in the path are not currently * visible. */ public int getRowForPath(JTree tree, TreePath path) { - int row = 0; - Object dest = path.getLastPathComponent(); - int rowCount = getRowCount(tree); - if (currentVisiblePath != null) - { - Object[] nodes = currentVisiblePath.getPath(); - while (row < rowCount) - { - if (dest.equals(nodes[row])) - return row; - row++; - } - } - return -1; + return treeState.getRowForPath(path); } /** * Returns the number of rows that are being displayed. * - * @param tree - * is the current tree to return the number of rows for. + * @param tree is the current tree to return the number of rows for. * @return the number of rows being displayed. */ public int getRowCount(JTree tree) { - if (currentVisiblePath != null) - return currentVisiblePath.getPathCount(); - return 0; + return treeState.getRowCount(); } /** @@ -766,35 +738,21 @@ public class BasicTreeUI extends TreeUI * valid path. If you need to test if the returned object is exactly at x,y * you should get the bounds for the returned path and test x,y against that. * - * @param tree - * the tree to search for the closest path - * @param x - * is the x coordinate of the location to search - * @param y - * is the y coordinate of the location to search + * @param tree the tree to search for the closest path + * @param x is the x coordinate of the location to search + * @param y is the y coordinate of the location to search * @return the tree path closes to x,y. */ public TreePath getClosestPathForLocation(JTree tree, int x, int y) { - int row = Math.round(y / getMaxHeight(tree)); - TreePath path = getPathForRow(tree, row); - - // no row is visible at this node - while (row > 0 && path == null) - { - --row; - path = getPathForRow(tree, row); - } - - return path; + return treeState.getPathClosestTo(x, y); } /** * Returns true if the tree is being edited. The item that is being edited can * be returned by getEditingPath(). * - * @param tree - * is the tree to check for editing. + * @param tree is the tree to check for editing. * @return true if the tree is being edited. */ public boolean isEditing(JTree tree) @@ -807,8 +765,7 @@ public class BasicTreeUI extends TreeUI * being edited. Returns true if the editor allows the editing session to * stop. * - * @param tree - * is the tree to stop the editing on + * @param tree is the tree to stop the editing on * @return true if the editor allows the editing session to stop. */ public boolean stopEditing(JTree tree) @@ -818,32 +775,29 @@ public class BasicTreeUI extends TreeUI completeEditing(false, false, true); finish(); } - return !isEditing(tree); + return ! isEditing(tree); } /** * Cancels the current editing session. * - * @param tree - * is the tree to cancel the editing session on. + * @param tree is the tree to cancel the editing session on. */ public void cancelEditing(JTree tree) - { - // There is no need to send the cancel message to the editor, - // as the cancellation event itself arrives from it. This would - // only be necessary when cancelling the editing programatically. - completeEditing(false, false, false); - finish(); + { + // There is no need to send the cancel message to the editor, + // as the cancellation event itself arrives from it. This would + // only be necessary when cancelling the editing programatically. + completeEditing(false, false, false); + finish(); } /** * Selects the last item in path and tries to edit it. Editing will fail if * the CellEditor won't allow it for the selected item. * - * @param tree - * is the tree to edit on. - * @param path - * is the path in tree to edit on. + * @param tree is the tree to edit on. + * @param path is the path in tree to edit on. */ public void startEditingAtPath(JTree tree, TreePath path) { @@ -853,8 +807,7 @@ public class BasicTreeUI extends TreeUI /** * Returns the path to the element that is being editted. * - * @param tree - * is the tree to get the editing path from. + * @param tree is the tree to get the editing path from. * @return the path that is being edited. */ public TreePath getEditingPath(JTree tree) @@ -902,7 +855,8 @@ public class BasicTreeUI extends TreeUI /** * Creates an instance of NodeDimensions that is able to determine the size of - * a given node in the tree. + * a given node in the tree. The node dimensions must be created before + * configuring the layout cache. * * @return the NodeDimensions of a given node in the tree */ @@ -1018,7 +972,7 @@ public class BasicTreeUI extends TreeUI */ protected AbstractLayoutCache createLayoutCache() { - return new FixedHeightLayoutCache(); + return new VariableHeightLayoutCache(); } /** @@ -1152,8 +1106,7 @@ public class BasicTreeUI extends TreeUI * by getting the expanded descendants from the tree and forwarding to the * tree state. * - * @param path - * the path used to update the expanded states + * @param path the path used to update the expanded states */ protected void updateExpandedDescendants(TreePath path) { @@ -1165,8 +1118,7 @@ public class BasicTreeUI extends TreeUI /** * Returns a path to the last child of <code>parent</code> * - * @param parent - * is the topmost path to specified + * @param parent is the topmost path to specified * @return a path to the last child of parent */ protected TreePath getLastChildPath(TreePath parent) @@ -1200,23 +1152,21 @@ public class BasicTreeUI extends TreeUI protected void updateRenderer() { if (tree != null) - { - if (tree.getCellRenderer() == null) - { - if (currentCellRenderer == null) - currentCellRenderer = createDefaultCellRenderer(); - tree.setCellRenderer(currentCellRenderer); - } - } + currentCellRenderer = tree.getCellRenderer(); + + if (currentCellRenderer == null) + currentCellRenderer = createDefaultCellRenderer(); } /** * Resets the treeState instance based on the tree we're providing the look - * and feel for. + * and feel for. The node dimensions handler is required and must be created + * in advance. */ protected void configureLayoutCache() { treeState = createLayoutCache(); + treeState.setNodeDimensions(nodeDimensions); } /** @@ -1236,42 +1186,19 @@ public class BasicTreeUI extends TreeUI */ protected void updateCachedPreferredSize() { - int maxWidth = 0; - updateCurrentVisiblePath(); - boolean isLeaf = false; - if (currentVisiblePath != null) - { - Object[] path = currentVisiblePath.getPath(); - for (int i = 0; i < path.length; i++) - { - TreePath curr = new TreePath(getPathToRoot(path[i], 0)); - Rectangle bounds = getPathBounds(tree, curr); - if (treeModel != null) - isLeaf = treeModel.isLeaf(path[i]); - if (!isLeaf && hasControlIcons()) - bounds.width += getCurrentControlIcon(curr).getIconWidth(); - maxWidth = Math.max(maxWidth, bounds.x + bounds.width); - } - - maxHeight = 0; - maxHeight = getMaxHeight(tree); - preferredSize = new Dimension(maxWidth, (maxHeight * path.length)); - } - else - preferredSize = new Dimension(0, 0); - validCachedPreferredSize = true; + validCachedPreferredSize = false; } /** * Messaged from the VisibleTreeNode after it has been expanded. * - * @param path - * is the path that has been expanded. + * @param path is the path that has been expanded. */ protected void pathWasExpanded(TreePath path) { validCachedPreferredSize = false; - tree.repaint(); + treeState.setExpandedState(path, true); + tree.repaint(); } /** @@ -1280,6 +1207,7 @@ public class BasicTreeUI extends TreeUI protected void pathWasCollapsed(TreePath path) { validCachedPreferredSize = false; + treeState.setExpandedState(path, false); tree.repaint(); } @@ -1294,6 +1222,7 @@ public class BasicTreeUI extends TreeUI rightChildIndent = UIManager.getInt("Tree.rightChildIndent"); leftChildIndent = UIManager.getInt("Tree.leftChildIndent"); + totalChildIndent = rightChildIndent + leftChildIndent; setRowHeight(UIManager.getInt("Tree.rowHeight")); tree.setRowHeight(getRowHeight()); tree.setScrollsOnExpand(UIManager.getBoolean("Tree.scrollsOnExpand")); @@ -1345,8 +1274,7 @@ public class BasicTreeUI extends TreeUI /** * Converts the modifiers. * - * @param mod - - * modifier to convert + * @param mod - modifier to convert * @returns the new modifier */ private int convertModifiers(int mod) @@ -1354,27 +1282,27 @@ public class BasicTreeUI extends TreeUI if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0) { mod |= KeyEvent.SHIFT_MASK; - mod &= ~KeyEvent.SHIFT_DOWN_MASK; + mod &= ~ KeyEvent.SHIFT_DOWN_MASK; } if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0) { mod |= KeyEvent.CTRL_MASK; - mod &= ~KeyEvent.CTRL_DOWN_MASK; + mod &= ~ KeyEvent.CTRL_DOWN_MASK; } if ((mod & KeyEvent.META_DOWN_MASK) != 0) { mod |= KeyEvent.META_MASK; - mod &= ~KeyEvent.META_DOWN_MASK; + mod &= ~ KeyEvent.META_DOWN_MASK; } if ((mod & KeyEvent.ALT_DOWN_MASK) != 0) { mod |= KeyEvent.ALT_MASK; - mod &= ~KeyEvent.ALT_DOWN_MASK; + mod &= ~ KeyEvent.ALT_DOWN_MASK; } if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0) { mod |= KeyEvent.ALT_GRAPH_MASK; - mod &= ~KeyEvent.ALT_GRAPH_DOWN_MASK; + mod &= ~ KeyEvent.ALT_GRAPH_DOWN_MASK; } return mod; } @@ -1399,16 +1327,16 @@ public class BasicTreeUI extends TreeUI /** * Install the UI for the component * - * @param c - * the component to install UI for + * @param c the component to install UI for */ public void installUI(JComponent c) { tree = (JTree) c; + treeModel = tree.getModel(); + prepareForUIInstall(); super.installUI(c); installDefaults(); - installComponents(); installKeyboardActions(); installListeners(); @@ -1419,10 +1347,13 @@ public class BasicTreeUI extends TreeUI setModel(tree.getModel()); treeSelectionModel = tree.getSelectionModel(); + setRootVisible(tree.isRootVisible()); + treeState.setRootVisible(tree.isRootVisible()); + updateExpandedDescendants(new TreePath(new Object[] { treeModel.getRoot() })); completeUIInstall(); } - + /** * Uninstall the defaults for the tree */ @@ -1436,8 +1367,7 @@ public class BasicTreeUI extends TreeUI /** * Uninstall the UI for the component * - * @param c - * the component to uninstall UI for + * @param c the component to uninstall UI for */ public void uninstallUI(JComponent c) { @@ -1456,51 +1386,103 @@ public class BasicTreeUI extends TreeUI * component is being painted. Subclasses should override this method and use * the specified Graphics object to render the content of the component. * - * @param g - * the Graphics context in which to paint - * @param c - * the component being painted; this argument is often ignored, but + * @param g the Graphics context in which to paint + * @param c the component being painted; this argument is often ignored, but * might be used if the UI object is stateless and shared by multiple * components */ public void paint(Graphics g, JComponent c) { JTree tree = (JTree) c; - updateCurrentVisiblePath(); + + int rows = treeState.getRowCount(); + + if (rows == 0) + // There is nothing to do if the tree is empty. + return; Rectangle clip = g.getClipBounds(); + Insets insets = tree.getInsets(); - if (clip != null && treeModel != null && currentVisiblePath != null) + if (clip != null && treeModel != null) { int startIndex = tree.getClosestRowForLocation(clip.x, clip.y); int endIndex = tree.getClosestRowForLocation(clip.x + clip.width, clip.y + clip.height); - paintVerticalPartOfLeg(g, clip, insets, currentVisiblePath); - for (int i = startIndex; i <= endIndex; i++) + // Also paint dashes to the invisible nodes below. + // These should be painted first, otherwise they may cover + // the control icons. + if (endIndex < rows) + for (int i = endIndex + 1; i < rows; i++) + { + TreePath path = treeState.getPathForRow(i); + if (isLastChild(path)) + paintVerticalPartOfLeg(g, clip, insets, path); + } + + // The two loops are required to ensure that the lines are not + // painted over the other tree components. + + int n = endIndex - startIndex + 1; + Rectangle[] bounds = new Rectangle[n]; + boolean[] isLeaf = new boolean[n]; + boolean[] isExpanded = new boolean[n]; + TreePath[] path = new TreePath[n]; + int k; + + k = 0; + for (int i = startIndex; i <= endIndex; i++, k++) { - Object curr = currentVisiblePath.getPathComponent(i); - boolean isLeaf = treeModel.isLeaf(curr); - TreePath path = new TreePath(getPathToRoot(curr, 0)); - - boolean isExpanded = tree.isExpanded(path); - Rectangle bounds = getPathBounds(tree, path); - paintHorizontalPartOfLeg(g, clip, insets, bounds, path, i, - isExpanded, false, isLeaf); - paintRow(g, clip, insets, bounds, path, i, isExpanded, false, - isLeaf); + path[k] = treeState.getPathForRow(i); + isLeaf[k] = treeModel.isLeaf(path[k].getLastPathComponent()); + isExpanded[k] = tree.isExpanded(path[k]); + bounds[k] = getPathBounds(tree, path[k]); + + paintHorizontalPartOfLeg(g, clip, insets, bounds[k], path[k], i, + isExpanded[k], false, isLeaf[k]); + if (isLastChild(path[k])) + paintVerticalPartOfLeg(g, clip, insets, path[k]); + } + + k = 0; + for (int i = startIndex; i <= endIndex; i++, k++) + { + paintRow(g, clip, insets, bounds[k], path[k], i, isExpanded[k], + false, isLeaf[k]); } } } /** + * Check if the path is referring to the last child of some parent. + */ + private boolean isLastChild(TreePath path) + { + if (path instanceof GnuPath) + { + // Except the seldom case when the layout cache is changed, this + // optimized code will be executed. + return ((GnuPath) path).isLastChild; + } + else + { + // Non optimized general case. + TreePath parent = path.getParentPath(); + if (parent == null) + return false; + int childCount = treeState.getVisibleChildCount(parent); + int p = treeModel.getIndexOfChild(parent, path.getLastPathComponent()); + return p == childCount - 1; + } + } + + /** * Ensures that the rows identified by beginRow through endRow are visible. * - * @param beginRow - * is the first row - * @param endRow - * is the last row + * @param beginRow is the first row + * @param endRow is the last row */ protected void ensureRowsAreVisible(int beginRow, int endRow) { @@ -1514,7 +1496,7 @@ public class BasicTreeUI extends TreeUI for (int i = beginRow; i < endRow; i++) { TreePath path = getPathForRow(tree, i); - if (!tree.isVisible(path)) + if (! tree.isVisible(path)) tree.makeVisible(path); } } @@ -1522,8 +1504,7 @@ public class BasicTreeUI extends TreeUI /** * Sets the preferred minimum size. * - * @param newSize - * is the new preferred minimum size. + * @param newSize is the new preferred minimum size. */ public void setPreferredMinSize(Dimension newSize) { @@ -1537,15 +1518,17 @@ public class BasicTreeUI extends TreeUI */ public Dimension getPreferredMinSize() { - return preferredMinSize; + if (preferredMinSize == null) + return getPreferredSize(tree); + else + return preferredMinSize; } /** * Returns the preferred size to properly display the tree, this is a cover * method for getPreferredSize(c, false). * - * @param c - * the component whose preferred size is being queried; this argument + * @param c the component whose preferred size is being queried; this argument * is often ignored but might be used if the UI object is stateless * and shared by multiple components * @return the preferred size @@ -1559,17 +1542,20 @@ public class BasicTreeUI extends TreeUI * Returns the preferred size to represent the tree in c. If checkConsistancy * is true, checkConsistancy is messaged first. * - * @param c - * the component whose preferred size is being queried. - * @param checkConsistancy - * if true must check consistancy + * @param c the component whose preferred size is being queried. + * @param checkConsistancy if true must check consistancy * @return the preferred size */ public Dimension getPreferredSize(JComponent c, boolean checkConsistancy) { - // FIXME: checkConsistancy not implemented, c not used - if (!validCachedPreferredSize) - updateCachedPreferredSize(); + if (! validCachedPreferredSize) + { + Rectangle size = tree.getBounds(); + // Add the scrollbar dimensions to the preferred size. + preferredSize = new Dimension(treeState.getPreferredWidth(size), + treeState.getPreferredHeight()); + validCachedPreferredSize = true; + } return preferredSize; } @@ -1577,31 +1563,24 @@ public class BasicTreeUI extends TreeUI * Returns the minimum size for this component. Which will be the min * preferred size or (0,0). * - * @param c - * the component whose min size is being queried. + * @param c the component whose min size is being queried. * @returns the preferred size or null */ public Dimension getMinimumSize(JComponent c) { - Dimension min = getPreferredMinSize(); - if (min == null) - return new Dimension(); - return min; + return preferredMinSize = getPreferredSize(c); } /** * Returns the maximum size for the component, which will be the preferred * size if the instance is currently in JTree or (0,0). * - * @param c - * the component whose preferred size is being queried + * @param c the component whose preferred size is being queried * @return the max size or null */ public Dimension getMaximumSize(JComponent c) { - if (c instanceof JTree) - return ((JTree) c).getPreferredSize(); - return new Dimension(); + return getPreferredSize(c); } /** @@ -1622,12 +1601,9 @@ public class BasicTreeUI extends TreeUI * cancelEditing. If messageTree is true, the treeModel is messaged with * valueForPathChanged. * - * @param messageStop - * message to stop editing - * @param messageCancel - * message to cancel editing - * @param messageTree - * message to treeModel + * @param messageStop message to stop editing + * @param messageCancel message to cancel editing + * @param messageTree message to treeModel */ protected void completeEditing(boolean messageStop, boolean messageCancel, boolean messageTree) @@ -1659,25 +1635,16 @@ public class BasicTreeUI extends TreeUI * Will start editing for node if there is a cellEditor and shouldSelectCall * returns true. This assumes that path is valid and visible. * - * @param path - * is the path to start editing - * @param event - * is the MouseEvent performed on the path + * @param path is the path to start editing + * @param event is the MouseEvent performed on the path * @return true if successful */ protected boolean startEditing(TreePath path, MouseEvent event) { - // Force to recalculate the maximal row height. - maxHeight = 0; - - // Force to recalculate the cached preferred size. - validCachedPreferredSize = false; - updateCellEditor(); TreeCellEditor ed = getCellEditor(); - if (ed != null - && (event == EDIT || ed.shouldSelectCell(event)) + if (ed != null && (event == EDIT || ed.shouldSelectCell(event)) && ed.isCellEditable(event)) { Rectangle bounds = getPathBounds(tree, path); @@ -1718,12 +1685,9 @@ public class BasicTreeUI extends TreeUI * If the <code>mouseX</code> and <code>mouseY</code> are in the expand or * collapse region of the row, this will toggle the row. * - * @param path - * the path we are concerned with - * @param mouseX - * is the cursor's x position - * @param mouseY - * is the cursor's y position + * @param path the path we are concerned with + * @param mouseX is the cursor's x position + * @param mouseY is the cursor's y position */ protected void checkForClickInExpandControl(TreePath path, int mouseX, int mouseY) @@ -1737,12 +1701,9 @@ public class BasicTreeUI extends TreeUI * the area of row that is used to expand/collpse the node and the node at row * does not represent a leaf. * - * @param path - * the path we are concerned with - * @param mouseX - * is the cursor's x position - * @param mouseY - * is the cursor's y position + * @param path the path we are concerned with + * @param mouseX is the cursor's x position + * @param mouseY is the cursor's y position * @return true if the <code>mouseX</code> and <code>mouseY</code> fall in * the area of row that is used to expand/collpse the node and the * node at row does not represent a leaf. @@ -1753,7 +1714,7 @@ public class BasicTreeUI extends TreeUI boolean cntlClick = false; int row = getRowForPath(tree, path); - if (!isLeaf(row)) + if (! isLeaf(row)) { Rectangle bounds = getPathBounds(tree, path); @@ -1769,12 +1730,9 @@ public class BasicTreeUI extends TreeUI * Messaged when the user clicks the particular row, this invokes * toggleExpandState. * - * @param path - * the path we are concerned with - * @param mouseX - * is the cursor's x position - * @param mouseY - * is the cursor's y position + * @param path the path we are concerned with + * @param mouseX is the cursor's x position + * @param mouseY is the cursor's y position */ protected void handleExpandControlClick(TreePath path, int mouseX, int mouseY) { @@ -1787,8 +1745,7 @@ public class BasicTreeUI extends TreeUI * invoked to scroll as many of the children to visible as possible (tries to * scroll to last visible descendant of path). * - * @param path - * the path we are concerned with + * @param path the path we are concerned with */ protected void toggleExpandState(TreePath path) { @@ -1800,30 +1757,40 @@ public class BasicTreeUI extends TreeUI /** * Returning true signifies a mouse event on the node should toggle the - * selection of only the row under the mouse. + * selection of only the row under the mouse. The BasisTreeUI treats the + * event as "toggle selection event" if the CTRL button was pressed while + * clicking. The event is not counted as toggle event if the associated + * tree does not support the multiple selection. * - * @param event - * is the MouseEvent performed on the row. + * @param event is the MouseEvent performed on the row. * @return true signifies a mouse event on the node should toggle the * selection of only the row under the mouse. */ protected boolean isToggleSelectionEvent(MouseEvent event) { - return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION); + return + (tree.getSelectionModel().getSelectionMode() != + TreeSelectionModel.SINGLE_TREE_SELECTION) && + ((event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0); } /** * Returning true signifies a mouse event on the node should select from the - * anchor point. + * anchor point. The BasisTreeUI treats the event as "multiple selection + * event" if the SHIFT button was pressed while clicking. The event is not + * counted as multiple selection event if the associated tree does not support + * the multiple selection. * - * @param event - * is the MouseEvent performed on the node. + * @param event is the MouseEvent performed on the node. * @return true signifies a mouse event on the node should select from the * anchor point. */ protected boolean isMultiSelectEvent(MouseEvent event) { - return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION); + return + (tree.getSelectionModel().getSelectionMode() != + TreeSelectionModel.SINGLE_TREE_SELECTION) && + ((event.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0); } /** @@ -1831,8 +1798,7 @@ public class BasicTreeUI extends TreeUI * the event. This is invoked after checkForClickInExpandControl, implying the * location is not in the expand (toggle) control. * - * @param event - * is the MouseEvent performed on the row. + * @param event is the MouseEvent performed on the row. * @return true indicates the row under the mouse should be toggled based on * the event. */ @@ -1846,17 +1812,19 @@ public class BasicTreeUI extends TreeUI * row. If the even is a toggle selection event, the row is either selected, * or deselected. If the event identifies a multi selection event, the * selection is updated from the anchor point. Otherwise, the row is selected, - * and if the even specified a toggle event the row is expanded/collapsed. + * and the previous selection is cleared.</p> * - * @param path - * is the path selected for an event - * @param event - * is the MouseEvent performed on the path. + * @param path is the path selected for an event + * @param event is the MouseEvent performed on the path. + * + * @see #isToggleSelectionEvent(MouseEvent) + * @see #isMultiSelectEvent(MouseEvent) */ protected void selectPathForEvent(TreePath path, MouseEvent event) { if (isToggleSelectionEvent(event)) { + // The event selects or unselects the clicked row. if (tree.isPathSelected(path)) tree.removeSelectionPath(path); else @@ -1867,6 +1835,7 @@ public class BasicTreeUI extends TreeUI } else if (isMultiSelectEvent(event)) { + // The event extends selection form anchor till the clicked row. TreePath anchor = tree.getAnchorSelectionPath(); if (anchor != null) { @@ -1877,14 +1846,17 @@ public class BasicTreeUI extends TreeUI tree.addSelectionPath(path); } else - tree.addSelectionPath(path); + { + // This is an ordinary event that just selects the clicked row. + tree.setSelectionPath(path); + tree.setAnchorSelectionPath(path); + } } /** * Returns true if the node at <code>row</code> is a leaf. * - * @param row - * is the row we are concerned with. + * @param row is the row we are concerned with. * @return true if the node at <code>row</code> is a leaf. */ protected boolean isLeaf(int row) @@ -1902,46 +1874,38 @@ public class BasicTreeUI extends TreeUI * are pressed for the JTree. The actionPerformed method is called when a key * that has been registered for the JTree is received. */ - class TreeAction extends AbstractAction + class TreeAction + extends AbstractAction { /** * What to do when this action is called. * - * @param e - * the ActionEvent that caused this action. + * @param e the ActionEvent that caused this action. */ public void actionPerformed(ActionEvent e) { + String command = e.getActionCommand(); TreePath lead = tree.getLeadSelectionPath(); - if (e.getActionCommand().equals("selectPreviousChangeLead") - || e.getActionCommand().equals("selectPreviousExtendSelection") - || e.getActionCommand().equals("selectPrevious") - || e.getActionCommand().equals("selectNext") - || e.getActionCommand().equals("selectNextExtendSelection") - || e.getActionCommand().equals("selectNextChangeLead")) + if (command.equals("selectPreviousChangeLead") + || command.equals("selectPreviousExtendSelection") + || command.equals("selectPrevious") || command.equals("selectNext") + || command.equals("selectNextExtendSelection") + || command.equals("selectNextChangeLead")) (new TreeIncrementAction(0, "")).actionPerformed(e); - else if (e.getActionCommand().equals("selectParent") - || e.getActionCommand().equals("selectChild")) + else if (command.equals("selectParent") || command.equals("selectChild")) (new TreeTraverseAction(0, "")).actionPerformed(e); - else if (e.getActionCommand().equals("selectAll")) + else if (command.equals("selectAll")) { - TreePath[] paths = new TreePath[tree.getVisibleRowCount()]; - - Object curr = getNextVisibleNode(treeModel.getRoot()); - int i = 0; - while (curr != null && i < paths.length) - { - paths[i] = new TreePath(getPathToRoot(curr, 0)); - i++; - } - + TreePath[] paths = new TreePath[treeState.getRowCount()]; + for (int i = 0; i < paths.length; i++) + paths[i] = treeState.getPathForRow(i); tree.addSelectionPaths(paths); } - else if (e.getActionCommand().equals("startEditing")) + else if (command.equals("startEditing")) tree.startEditingAtPath(lead); - else if (e.getActionCommand().equals("toggle")) + else if (command.equals("toggle")) { if (tree.isEditing()) tree.stopEditing(); @@ -1949,17 +1913,17 @@ public class BasicTreeUI extends TreeUI { Object last = lead.getLastPathComponent(); TreePath path = new TreePath(getPathToRoot(last, 0)); - if (!treeModel.isLeaf(last)) + if (! treeModel.isLeaf(last)) toggleExpandState(path); } } - else if (e.getActionCommand().equals("clearSelection")) + else if (command.equals("clearSelection")) tree.clearSelection(); - if (tree.isEditing() && !e.getActionCommand().equals("startEditing")) + if (tree.isEditing() && ! command.equals("startEditing")) tree.stopEditing(); - tree.scrollPathToVisible(lead); + tree.scrollPathToVisible(tree.getLeadSelectionPath()); } } @@ -1970,7 +1934,8 @@ public class BasicTreeUI extends TreeUI * to the true receiver after altering the actionCommand property of the * event. */ - private static class ActionListenerProxy extends AbstractAction + private static class ActionListenerProxy + extends AbstractAction { ActionListener target; @@ -1992,11 +1957,12 @@ public class BasicTreeUI extends TreeUI } } - /** + /** * Updates the preferred size when scrolling, if necessary. */ - public class ComponentHandler extends ComponentAdapter implements - ActionListener + public class ComponentHandler + extends ComponentAdapter + implements ActionListener { /** * Timer used when inside a scrollpane and the scrollbar is adjusting @@ -2017,8 +1983,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when the component's position changes. * - * @param e - * the event that occurs when moving the component + * @param e the event that occurs when moving the component */ public void componentMoved(ComponentEvent e) { @@ -2048,8 +2013,7 @@ public class BasicTreeUI extends TreeUI * Public as a result of Timer. If the scrollBar is null, or not adjusting, * this stops the timer and updates the sizing. * - * @param ae - * is the action performed + * @param ae is the action performed */ public void actionPerformed(ActionEvent ae) { @@ -2061,7 +2025,8 @@ public class BasicTreeUI extends TreeUI * Listener responsible for getting cell editing events and updating the tree * accordingly. */ - public class CellEditorHandler implements CellEditorListener + public class CellEditorHandler + implements CellEditorListener { /** * Constructor @@ -2075,8 +2040,7 @@ public class BasicTreeUI extends TreeUI * Messaged when editing has stopped in the tree. Tells the listeners * editing has stopped. * - * @param e - * is the notification event + * @param e is the notification event */ public void editingStopped(ChangeEvent e) { @@ -2087,8 +2051,7 @@ public class BasicTreeUI extends TreeUI * Messaged when editing has been canceled in the tree. This tells the * listeners the editor has canceled editing. * - * @param e - * is the notification event + * @param e is the notification event */ public void editingCanceled(ChangeEvent e) { @@ -2099,7 +2062,8 @@ public class BasicTreeUI extends TreeUI /** * Repaints the lead selection row when focus is lost/grained. */ - public class FocusHandler implements FocusListener + public class FocusHandler + implements FocusListener { /** * Constructor @@ -2111,26 +2075,38 @@ public class BasicTreeUI extends TreeUI /** * Invoked when focus is activated on the tree we're in, redraws the lead - * row. Invoked when a component gains the keyboard focus. + * row. Invoked when a component gains the keyboard focus. The method + * repaints the lead row that is shown differently when the tree is in + * focus. * - * @param e - * is the focus event that is activated + * @param e is the focus event that is activated */ public void focusGained(FocusEvent e) { - // TODO: Implement this properly. + repaintLeadRow(); } /** * Invoked when focus is deactivated on the tree we're in, redraws the lead - * row. Invoked when a component loses the keyboard focus. + * row. Invoked when a component loses the keyboard focus. The method + * repaints the lead row that is shown differently when the tree is in + * focus. * - * @param e - * is the focus event that is deactivated + * @param e is the focus event that is deactivated */ public void focusLost(FocusEvent e) { - // TODO: Implement this properly. + repaintLeadRow(); + } + + /** + * Repaint the lead row. + */ + void repaintLeadRow() + { + TreePath lead = tree.getLeadSelectionPath(); + if (lead!=null) + tree.repaint(tree.getPathBounds(lead)); } } @@ -2138,7 +2114,8 @@ public class BasicTreeUI extends TreeUI * This is used to get multiple key down events to appropriately genereate * events. */ - public class KeyHandler extends KeyAdapter + public class KeyHandler + extends KeyAdapter { /** Key code that is being generated for. */ protected Action repeatKeyAction; @@ -2160,8 +2137,7 @@ public class BasicTreeUI extends TreeUI * user. Subsequent same key presses move the keyboard focus to the next * object that starts with the same letter. * - * @param e - * the key typed + * @param e the key typed */ public void keyTyped(KeyEvent e) { @@ -2171,8 +2147,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when a key has been pressed. * - * @param e - * the key pressed + * @param e the key pressed */ public void keyPressed(KeyEvent e) { @@ -2182,8 +2157,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when a key has been released * - * @param e - * the key released + * @param e the key released */ public void keyReleased(KeyEvent e) { @@ -2195,7 +2169,9 @@ public class BasicTreeUI extends TreeUI * MouseListener is responsible for updating the selection based on mouse * events. */ - public class MouseHandler extends MouseAdapter implements MouseMotionListener + public class MouseHandler + extends MouseAdapter + implements MouseMotionListener { /** * Constructor @@ -2208,11 +2184,10 @@ public class BasicTreeUI extends TreeUI /** * Invoked when a mouse button has been pressed on a component. * - * @param e - * is the mouse event that occured + * @param e is the mouse event that occured */ public void mousePressed(MouseEvent e) - { + { // Any mouse click cancels the previous waiting edit action, initiated // by the single click on the selected node. if (startEditTimer != null) @@ -2220,7 +2195,7 @@ public class BasicTreeUI extends TreeUI startEditTimer.stop(); startEditTimer = null; } - + Point click = e.getPoint(); TreePath path = getClosestPathForLocation(tree, click.x, click.y); @@ -2228,6 +2203,11 @@ public class BasicTreeUI extends TreeUI { Rectangle bounds = getPathBounds(tree, path); int row = getRowForPath(tree, path); + + // Cancel the editing session if clicked on the different row. + if (tree.isEditing() && row != editingRow) + cancelEditing(tree); + boolean cntlClick = isLocationInExpandControl(path, click.x, click.y); boolean isLeaf = isLeaf(row); @@ -2258,35 +2238,32 @@ public class BasicTreeUI extends TreeUI if (inBounds) { TreePath currentLead = tree.getLeadSelectionPath(); - if ( - currentLead != null && - currentLead.equals(path) && - e.getClickCount() == 1 && - tree.isEditable() - ) + if (currentLead != null && currentLead.equals(path) + && e.getClickCount() == 1 && tree.isEditable()) { // Schedule the editing session. final TreePath editPath = path; - + if (startEditTimer != null) startEditTimer.stop(); - - startEditTimer = new Timer(WAIT_TILL_EDITING, - new ActionListener() - { + + startEditTimer = new Timer(WAIT_TILL_EDITING, + new ActionListener() + { public void actionPerformed(ActionEvent e) - { - startEditing(editPath, EDIT); - } + { + startEditing(editPath, EDIT); + } }); - startEditTimer.setRepeats(false); - startEditTimer.start(); + startEditTimer.setRepeats(false); + startEditTimer.start(); } else { - selectPath(tree, path); - if (e.getClickCount() == 2 && !isLeaf(row)) + if (e.getClickCount() == 2 && ! isLeaf(row)) toggleExpandState(path); + else + selectPathForEvent(path, e); } } @@ -2309,8 +2286,7 @@ public class BasicTreeUI extends TreeUI * the drag originated until the mouse button is released (regardless of * whether the mouse position is within the bounds of the component). * - * @param e - * is the mouse event that occured + * @param e is the mouse event that occured */ public void mouseDragged(MouseEvent e) { @@ -2321,8 +2297,7 @@ public class BasicTreeUI extends TreeUI * Invoked when the mouse button has been moved on a component (with no * buttons no down). * - * @param e - * the mouse event that occured + * @param e the mouse event that occured */ public void mouseMoved(MouseEvent e) { @@ -2332,8 +2307,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when a mouse button has been released on a component. * - * @param e - * is the mouse event that occured + * @param e is the mouse event that occured */ public void mouseReleased(MouseEvent e) { @@ -2346,7 +2320,8 @@ public class BasicTreeUI extends TreeUI * events, until the mouse is released to the destination it is constructed * with. */ - public class MouseInputHandler implements MouseInputListener + public class MouseInputHandler + implements MouseInputListener { /** Source that events are coming from */ protected Component source; @@ -2357,12 +2332,9 @@ public class BasicTreeUI extends TreeUI /** * Constructor * - * @param source - * that events are coming from - * @param destination - * that receives all events - * @param e - * is the event received + * @param source that events are coming from + * @param destination that receives all events + * @param e is the event received */ public MouseInputHandler(Component source, Component destination, MouseEvent e) @@ -2375,8 +2347,7 @@ public class BasicTreeUI extends TreeUI * Invoked when the mouse button has been clicked (pressed and released) on * a component. * - * @param e - * mouse event that occured + * @param e mouse event that occured */ public void mouseClicked(MouseEvent e) { @@ -2386,8 +2357,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when a mouse button has been pressed on a component. * - * @param e - * mouse event that occured + * @param e mouse event that occured */ public void mousePressed(MouseEvent e) { @@ -2397,8 +2367,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when a mouse button has been released on a component. * - * @param e - * mouse event that occured + * @param e mouse event that occured */ public void mouseReleased(MouseEvent e) { @@ -2408,8 +2377,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when the mouse enters a component. * - * @param e - * mouse event that occured + * @param e mouse event that occured */ public void mouseEntered(MouseEvent e) { @@ -2419,8 +2387,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when the mouse exits a component. * - * @param e - * mouse event that occured + * @param e mouse event that occured */ public void mouseExited(MouseEvent e) { @@ -2433,8 +2400,7 @@ public class BasicTreeUI extends TreeUI * the drag originated until the mouse button is released (regardless of * whether the mouse position is within the bounds of the component). * - * @param e - * mouse event that occured + * @param e mouse event that occured */ public void mouseDragged(MouseEvent e) { @@ -2445,8 +2411,7 @@ public class BasicTreeUI extends TreeUI * Invoked when the mouse cursor has been moved onto a component but no * buttons have been pushed. * - * @param e - * mouse event that occured + * @param e mouse event that occured */ public void mouseMoved(MouseEvent e) { @@ -2467,7 +2432,8 @@ public class BasicTreeUI extends TreeUI * BasicTreeUI method. X location does not include insets, that is handled in * getPathBounds. */ - public class NodeDimensionsHandler extends AbstractLayoutCache.NodeDimensions + public class NodeDimensionsHandler + extends AbstractLayoutCache.NodeDimensions { /** * Constructor @@ -2483,16 +2449,11 @@ public class BasicTreeUI extends TreeUI * bounds is null, a newly created Rectangle should be returned, otherwise * the value should be placed in bounds and returned. * - * @param cell - * the value to be represented - * @param row - * row being queried - * @param depth - * the depth of the row - * @param expanded - * true if row is expanded - * @param size - * a Rectangle containing the size needed to represent value + * @param cell the value to be represented + * @param row row being queried + * @param depth the depth of the row + * @param expanded true if row is expanded + * @param size a Rectangle containing the size needed to represent value * @return containing the node dimensions, or null if node has no dimension */ public Rectangle getNodeDimensions(Object cell, int row, int depth, @@ -2507,8 +2468,11 @@ public class BasicTreeUI extends TreeUI if (s != null) { + TreePath path = treeState.getPathForRow(row); size.x = getRowX(row, depth); size.width = SwingUtilities.computeStringWidth(fm, s); + size.width = size.width + getCurrentControlIcon(path).getIconWidth() + + gap + getNodeIcon(path).getIconWidth(); size.height = getMaxHeight(tree); size.y = size.height * row; } @@ -2523,17 +2487,16 @@ public class BasicTreeUI extends TreeUI */ protected int getRowX(int row, int depth) { - if (row == 0) - return 0; - return depth * rightChildIndent; + return depth * totalChildIndent; } }// NodeDimensionsHandler /** - * PropertyChangeListener for the tree. Updates the appropriate varaible, or + * PropertyChangeListener for the tree. Updates the appropriate variable, or * TreeState, based on what changes. */ - public class PropertyChangeHandler implements PropertyChangeListener + public class PropertyChangeHandler + implements PropertyChangeListener { /** @@ -2547,17 +2510,28 @@ public class BasicTreeUI extends TreeUI /** * This method gets called when a bound property is changed. * - * @param event - * A PropertyChangeEvent object describing the event source and the - * property that has changed. + * @param event A PropertyChangeEvent object describing the event source and + * the property that has changed. */ public void propertyChange(PropertyChangeEvent event) { - if ((event.getPropertyName()).equals("rootVisible")) + String property = event.getPropertyName(); + if (property.equals(JTree.ROOT_VISIBLE_PROPERTY)) { validCachedPreferredSize = false; + treeState.setRootVisible(tree.isRootVisible()); tree.repaint(); } + else if (property.equals(JTree.SELECTION_MODEL_PROPERTY)) + { + treeSelectionModel = tree.getSelectionModel(); + treeSelectionModel.setRowMapper(treeState); + } + else if (property.equals(JTree.TREE_MODEL_PROPERTY)) + { + treeModel = tree.getModel(); + treeModel.addTreeModelListener(treeModelListener); + } } } @@ -2565,8 +2539,8 @@ public class BasicTreeUI extends TreeUI * Listener on the TreeSelectionModel, resets the row selection if any of the * properties of the model change. */ - public class SelectionModelPropertyChangeHandler implements - PropertyChangeListener + public class SelectionModelPropertyChangeHandler + implements PropertyChangeListener { /** @@ -2580,9 +2554,8 @@ public class BasicTreeUI extends TreeUI /** * This method gets called when a bound property is changed. * - * @param event - * A PropertyChangeEvent object describing the event source and the - * property that has changed. + * @param event A PropertyChangeEvent object describing the event source and + * the property that has changed. */ public void propertyChange(PropertyChangeEvent event) { @@ -2593,7 +2566,8 @@ public class BasicTreeUI extends TreeUI /** * ActionListener that invokes cancelEditing when action performed. */ - public class TreeCancelEditingAction extends AbstractAction + public class TreeCancelEditingAction + extends AbstractAction { /** @@ -2607,8 +2581,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when an action occurs. * - * @param e - * event that occured + * @param e event that occured */ public void actionPerformed(ActionEvent e) { @@ -2630,7 +2603,8 @@ public class BasicTreeUI extends TreeUI /** * Updates the TreeState in response to nodes expanding/collapsing. */ - public class TreeExpansionHandler implements TreeExpansionListener + public class TreeExpansionHandler + implements TreeExpansionListener { /** @@ -2644,24 +2618,30 @@ public class BasicTreeUI extends TreeUI /** * Called whenever an item in the tree has been expanded. * - * @param event - * is the event that occured + * @param event is the event that occured */ public void treeExpanded(TreeExpansionEvent event) { validCachedPreferredSize = false; + treeState.setExpandedState(event.getPath(), true); + // The maximal cell height may change + maxHeight = 0; + tree.revalidate(); tree.repaint(); } /** * Called whenever an item in the tree has been collapsed. * - * @param event - * is the event that occured + * @param event is the event that occured */ public void treeCollapsed(TreeExpansionEvent event) { validCachedPreferredSize = false; + treeState.setExpandedState(event.getPath(), false); + // The maximal cell height may change + maxHeight = 0; + tree.revalidate(); tree.repaint(); } }// TreeExpansionHandler @@ -2670,7 +2650,8 @@ public class BasicTreeUI extends TreeUI * TreeHomeAction is used to handle end/home actions. Scrolls either the first * or last cell to be visible based on direction. */ - public class TreeHomeAction extends AbstractAction + public class TreeHomeAction + extends AbstractAction { /** The direction, either home or end */ @@ -2679,10 +2660,8 @@ public class BasicTreeUI extends TreeUI /** * Constructor * - * @param direction - - * it is home or end - * @param name - * is the name of the direction + * @param direction - it is home or end + * @param name is the name of the direction */ public TreeHomeAction(int direction, String name) { @@ -2692,8 +2671,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when an action occurs. * - * @param e - * is the event that occured + * @param e is the event that occured */ public void actionPerformed(ActionEvent e) { @@ -2716,7 +2694,8 @@ public class BasicTreeUI extends TreeUI * TreeIncrementAction is used to handle up/down actions. Selection is moved * up or down based on direction. */ - public class TreeIncrementAction extends AbstractAction + public class TreeIncrementAction + extends AbstractAction { /** Specifies the direction to adjust the selection by. */ @@ -2725,10 +2704,8 @@ public class BasicTreeUI extends TreeUI /** * Constructor * - * @param direction - * up or down - * @param name - * is the name of the direction + * @param direction up or down + * @param name is the name of the direction */ public TreeIncrementAction(int direction, String name) { @@ -2738,73 +2715,79 @@ public class BasicTreeUI extends TreeUI /** * Invoked when an action occurs. * - * @param e - * is the event that occured + * @param e is the event that occured */ public void actionPerformed(ActionEvent e) { - Object last = tree.getLeadSelectionPath().getLastPathComponent(); + TreePath currentPath = tree.getLeadSelectionPath(); + int currentRow; - if (e.getActionCommand().equals("selectPreviousChangeLead")) - { - Object prev = getPreviousVisibleNode(last); + if (currentPath != null) + currentRow = treeState.getRowForPath(currentPath); + else + currentRow = 0; - if (prev != null) - { - TreePath newPath = new TreePath(getPathToRoot(prev, 0)); - selectPath(tree, newPath); - tree.setLeadSelectionPath(newPath); - } - } - else if (e.getActionCommand().equals("selectPreviousExtendSelection")) + int rows = treeState.getRowCount(); + + int nextRow = currentRow + 1; + int prevRow = currentRow - 1; + boolean hasNext = nextRow < rows; + boolean hasPrev = prevRow >= 0 && rows > 0; + TreePath newPath; + String command = e.getActionCommand(); + + if (command.equals("selectPreviousChangeLead") && hasPrev) { - Object prev = getPreviousVisibleNode(last); - if (prev != null) - { - TreePath newPath = new TreePath(getPathToRoot(prev, 0)); - tree.addSelectionPath(newPath); - tree.setLeadSelectionPath(newPath); - } + newPath = treeState.getPathForRow(prevRow); + tree.setSelectionPath(newPath); + tree.setAnchorSelectionPath(newPath); + tree.setLeadSelectionPath(newPath); } - else if (e.getActionCommand().equals("selectPrevious")) + else if (command.equals("selectPreviousExtendSelection") && hasPrev) { - Object prev = getPreviousVisibleNode(last); + newPath = treeState.getPathForRow(prevRow); - if (prev != null) - { - TreePath newPath = new TreePath(getPathToRoot(prev, 0)); - selectPath(tree, newPath); - } + // If the new path is already selected, the selection shrinks, + // unselecting the previously current path. + if (tree.isPathSelected(newPath)) + tree.getSelectionModel().removeSelectionPath(currentPath); + + // This must be called in any case because it updates the model + // lead selection index. + tree.addSelectionPath(newPath); + tree.setLeadSelectionPath(newPath); } - else if (e.getActionCommand().equals("selectNext")) + else if (command.equals("selectPrevious") && hasPrev) { - Object next = getNextVisibleNode(last); - - if (next != null) - { - TreePath newPath = new TreePath(getPathToRoot(next, 0)); - selectPath(tree, newPath); - } + newPath = treeState.getPathForRow(prevRow); + tree.setSelectionPath(newPath); } - else if (e.getActionCommand().equals("selectNextExtendSelection")) + else if (command.equals("selectNext") && hasNext) { - Object next = getNextVisibleNode(last); - if (next != null) - { - TreePath newPath = new TreePath(getPathToRoot(next, 0)); - tree.addSelectionPath(newPath); - tree.setLeadSelectionPath(newPath); - } + newPath = treeState.getPathForRow(nextRow); + tree.setSelectionPath(newPath); } - else if (e.getActionCommand().equals("selectNextChangeLead")) + else if (command.equals("selectNextExtendSelection") && hasNext) { - Object next = getNextVisibleNode(last); - if (next != null) - { - TreePath newPath = new TreePath(getPathToRoot(next, 0)); - selectPath(tree, newPath); - tree.setLeadSelectionPath(newPath); - } + newPath = treeState.getPathForRow(nextRow); + + // If the new path is already selected, the selection shrinks, + // unselecting the previously current path. + if (tree.isPathSelected(newPath)) + tree.getSelectionModel().removeSelectionPath(currentPath); + + // This must be called in any case because it updates the model + // lead selection index. + tree.addSelectionPath(newPath); + + tree.setLeadSelectionPath(newPath); + } + else if (command.equals("selectNextChangeLead") && hasNext) + { + newPath = treeState.getPathForRow(nextRow); + tree.setSelectionPath(newPath); + tree.setAnchorSelectionPath(newPath); + tree.setLeadSelectionPath(newPath); } } @@ -2823,7 +2806,8 @@ public class BasicTreeUI extends TreeUI /** * Forwards all TreeModel events to the TreeState. */ - public class TreeModelHandler implements TreeModelListener + public class TreeModelHandler + implements TreeModelListener { /** * Constructor @@ -2843,12 +2827,12 @@ public class BasicTreeUI extends TreeUI * node(s). e.getChildIndices() returns the index(es) of the changed * node(s). * - * @param e - * is the event that occured + * @param e is the event that occured */ public void treeNodesChanged(TreeModelEvent e) { validCachedPreferredSize = false; + treeState.treeNodesChanged(e); tree.repaint(); } @@ -2857,12 +2841,12 @@ public class BasicTreeUI extends TreeUI * get the parent of the new node(s). e.getChildIndices() returns the * index(es) of the new node(s) in ascending order. * - * @param e - * is the event that occured + * @param e is the event that occured */ public void treeNodesInserted(TreeModelEvent e) { validCachedPreferredSize = false; + treeState.treeNodesInserted(e); tree.repaint(); } @@ -2874,12 +2858,12 @@ public class BasicTreeUI extends TreeUI * node(s). e.getChildIndices() returns, in ascending order, the index(es) * the node(s) had before being deleted. * - * @param e - * is the event that occured + * @param e is the event that occured */ public void treeNodesRemoved(TreeModelEvent e) { validCachedPreferredSize = false; + treeState.treeNodesRemoved(e); tree.repaint(); } @@ -2890,15 +2874,15 @@ public class BasicTreeUI extends TreeUI * should become the new root of the tree. Use e.getPath() to get the path * to the node. e.getChildIndices() returns null. * - * @param e - * is the event that occured + * @param e is the event that occured */ public void treeStructureChanged(TreeModelEvent e) { if (e.getPath().length == 1 - && !e.getPath()[0].equals(treeModel.getRoot())) + && ! e.getPath()[0].equals(treeModel.getRoot())) tree.expandPath(new TreePath(treeModel.getRoot())); validCachedPreferredSize = false; + treeState.treeStructureChanged(e); tree.repaint(); } }// TreeModelHandler @@ -2906,7 +2890,8 @@ public class BasicTreeUI extends TreeUI /** * TreePageAction handles page up and page down events. */ - public class TreePageAction extends AbstractAction + public class TreePageAction + extends AbstractAction { /** Specifies the direction to adjust the selection by. */ protected int direction; @@ -2914,10 +2899,8 @@ public class BasicTreeUI extends TreeUI /** * Constructor * - * @param direction - * up or down - * @param name - * is the name of the direction + * @param direction up or down + * @param name is the name of the direction */ public TreePageAction(int direction, String name) { @@ -2927,8 +2910,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when an action occurs. * - * @param e - * is the event that occured + * @param e is the event that occured */ public void actionPerformed(ActionEvent e) { @@ -2950,7 +2932,8 @@ public class BasicTreeUI extends TreeUI * Listens for changes in the selection model and updates the display * accordingly. */ - public class TreeSelectionHandler implements TreeSelectionListener + public class TreeSelectionHandler + implements TreeSelectionListener { /** * Constructor @@ -2964,26 +2947,42 @@ public class BasicTreeUI extends TreeUI * Messaged when the selection changes in the tree we're displaying for. * Stops editing, messages super and displays the changed paths. * - * @param event - * the event that characterizes the change. + * @param event the event that characterizes the change. */ public void valueChanged(TreeSelectionEvent event) { if (tree.isEditing()) - tree.stopEditing(); + tree.cancelEditing(); + + TreePath op = event.getOldLeadSelectionPath(); + TreePath np = event.getNewLeadSelectionPath(); + + // Repaint of the changed lead selection path. + if (op != np) + { + Rectangle o = treeState.getBounds(event.getOldLeadSelectionPath(), + new Rectangle()); + Rectangle n = treeState.getBounds(event.getNewLeadSelectionPath(), + new Rectangle()); + + if (o!=null) + tree.repaint(o); + if (n!=null) + tree.repaint(n); + } } }// TreeSelectionHandler /** * For the first selected row expandedness will be toggled. */ - public class TreeToggleAction extends AbstractAction + public class TreeToggleAction + extends AbstractAction { /** * Constructor * - * @param name - * is the name of <code>Action</code> field + * @param name is the name of <code>Action</code> field */ public TreeToggleAction(String name) { @@ -2993,8 +2992,7 @@ public class BasicTreeUI extends TreeUI /** * Invoked when an action occurs. * - * @param e - * the event that occured + * @param e the event that occured */ public void actionPerformed(ActionEvent e) { @@ -3016,7 +3014,8 @@ public class BasicTreeUI extends TreeUI * TreeTraverseAction is the action used for left/right keys. Will toggle the * expandedness of a node, as well as potentially incrementing the selection. */ - public class TreeTraverseAction extends AbstractAction + public class TreeTraverseAction + extends AbstractAction { /** * Determines direction to traverse, 1 means expand, -1 means collapse. @@ -3026,10 +3025,8 @@ public class BasicTreeUI extends TreeUI /** * Constructor * - * @param direction - * to traverse - * @param name - * is the name of the direction + * @param direction to traverse + * @param name is the name of the direction */ public TreeTraverseAction(int direction, String name) { @@ -3039,35 +3036,49 @@ public class BasicTreeUI extends TreeUI /** * Invoked when an action occurs. * - * @param e - * the event that occured + * @param e the event that occured */ public void actionPerformed(ActionEvent e) { - Object last = tree.getLeadSelectionPath().getLastPathComponent(); + TreePath current = tree.getLeadSelectionPath(); + if (current == null) + return; if (e.getActionCommand().equals("selectParent")) { - TreePath path = new TreePath(getPathToRoot(last, 0)); - Object p = getParent(treeModel.getRoot(), last); + if (current == null) + return; - if (!treeModel.isLeaf(last)) - toggleExpandState(path); - else if (p != null) - selectPath(tree, new TreePath(getPathToRoot(p, 0))); + if (tree.isExpanded(current)) + { + tree.collapsePath(current); + } + else + { + // If the node is not expanded (also, if it is a leaf node), + // we just select the parent. We do not select the root if it + // is not visible. + TreePath parent = current.getParentPath(); + if (parent != null && + !(parent.getPathCount()==1 && !tree.isRootVisible()) ) + tree.setSelectionPath(parent); + } } else if (e.getActionCommand().equals("selectChild")) { - TreePath path = new TreePath(getPathToRoot(last, 0)); - - if (!treeModel.isLeaf(last)) - toggleExpandState(path); + Object node = current.getLastPathComponent(); + int nc = treeModel.getChildCount(node); + if (nc == 0 || treeState.isExpanded(current)) + { + // If the node is leaf or it is already expanded, + // we just select the next row. + int nextRow = tree.getLeadSelectionRow() + 1; + if (nextRow <= tree.getRowCount()) + tree.setSelectionRow(nextRow); + } else { - Object next = getNextVisibleNode(last); - - if (next != null) - selectPath(tree, new TreePath(getPathToRoot(next, 0))); + tree.expandPath(current); } } } @@ -3105,18 +3116,42 @@ public class BasicTreeUI extends TreeUI */ Icon getCurrentControlIcon(TreePath path) { - if (tree.isExpanded(path)) - return expandedIcon; - return collapsedIcon; + if (hasControlIcons()) + { + if (tree.isExpanded(path)) + return expandedIcon; + else + return collapsedIcon; + } + else + { + if (nullIcon == null) + nullIcon = new Icon() + { + public int getIconHeight() + { + return 0; + } + + public int getIconWidth() + { + return 0; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + // No action here. + } + }; + return nullIcon; + } } /** * Returns the parent of the current node * - * @param root - * is the root of the tree - * @param node - * is the current node + * @param root is the root of the tree + * @param node is the current node * @return is the parent of the current node */ Object getParent(Object root, Object node) @@ -3132,15 +3167,13 @@ public class BasicTreeUI extends TreeUI /** * Recursively checks the tree for the specified node, starting at the root. * - * @param root - * is starting node to start searching at. - * @param node - * is the node to search for + * @param root is starting node to start searching at. + * @param node is the node to search for * @return the parent node of node */ private Object findNode(Object root, Object node) { - if (!treeModel.isLeaf(root) && !root.equals(node)) + if (! treeModel.isLeaf(root) && ! root.equals(node)) { int size = treeModel.getChildCount(root); for (int j = 0; j < size; j++) @@ -3158,167 +3191,20 @@ public class BasicTreeUI extends TreeUI } /** - * Get previous visible node in the tree. Package private for use in inner - * classes. - * - * @param node - - * current node - * @return the next visible node in the JTree. Return null if there are no - * more. - */ - Object getPreviousVisibleNode(Object node) - { - if (currentVisiblePath != null) - { - Object[] nodes = currentVisiblePath.getPath(); - int i = 0; - while (i < nodes.length && !node.equals(nodes[i])) - i++; - // return the next node - if (i - 1 >= 0) - return nodes[i - 1]; - } - return null; - } - - /** - * Returns the next node in the tree Package private for use in inner classes. - * - * @param curr - - * current node - * @return the next node in the tree - */ - Object getNextNode(Object curr) - { - if (!treeModel.isLeaf(curr) && treeModel.getChildCount(curr) > 0) - return treeModel.getChild(curr, 0); - - Object node = curr; - Object sibling = null; - do - { - sibling = getNextSibling(node); - node = getParent(treeModel.getRoot(), node); - } - while (sibling == null && node != null); - - return sibling; - } - - /** - * Returns the previous node in the tree Package private for use in inner - * classes. - * - * @param node - * current node - * @return the previous node in the tree - */ - Object getPreviousNode(Object node) - { - Object parent = getParent(treeModel.getRoot(), node); - if (parent == null) - return null; - - Object sibling = getPreviousSibling(node); - - if (sibling == null) - return parent; - - int size = 0; - if (!treeModel.isLeaf(sibling)) - size = treeModel.getChildCount(sibling); - while (size > 0) - { - sibling = treeModel.getChild(sibling, size - 1); - if (!treeModel.isLeaf(sibling)) - size = treeModel.getChildCount(sibling); - else - size = 0; - } - - return sibling; - } - - /** - * Returns the next sibling in the tree Package private for use in inner - * classes. - * - * @param node - - * current node - * @return the next sibling in the tree - */ - Object getNextSibling(Object node) - { - Object parent = getParent(treeModel.getRoot(), node); - if (parent == null) - return null; - - int index = treeModel.getIndexOfChild(parent, node) + 1; - - int size = 0; - if (!treeModel.isLeaf(parent)) - size = treeModel.getChildCount(parent); - if (index == 0 || index >= size) - return null; - - return treeModel.getChild(parent, index); - } - - /** - * Returns the previous sibling in the tree Package private for use in inner - * classes. - * - * @param node - - * current node - * @return the previous sibling in the tree - */ - Object getPreviousSibling(Object node) - { - Object parent = getParent(treeModel.getRoot(), node); - if (parent == null) - return null; - - int index = treeModel.getIndexOfChild(parent, node) - 1; - - int size = 0; - if (!treeModel.isLeaf(parent)) - size = treeModel.getChildCount(parent); - if (index < 0 || index >= size) - return null; - - return treeModel.getChild(parent, index); - } - - /** * Selects the specified path in the tree depending on modes. Package private * for use in inner classes. * - * @param tree - * is the tree we are selecting the path in - * @param path - * is the path we are selecting + * @param tree is the tree we are selecting the path in + * @param path is the path we are selecting */ void selectPath(JTree tree, TreePath path) { if (path != null) { - if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION) - { - tree.getSelectionModel().clearSelection(); - tree.addSelectionPath(path); - tree.setLeadSelectionPath(path); - } - else if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) - { - // TODO - } - else - { - tree.addSelectionPath(path); - tree.setLeadSelectionPath(path); - tree.getSelectionModel().setSelectionMode( - TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); - } + tree.setSelectionPath(path); + tree.setLeadSelectionPath(path); + tree.makeVisible(path); + tree.scrollPathToVisible(path); } } @@ -3326,10 +3212,8 @@ public class BasicTreeUI extends TreeUI * Returns the path from node to the root. Package private for use in inner * classes. * - * @param node - * the node to get the path to - * @param depth - * the depth of the tree to return a path for + * @param node the node to get the path to + * @param depth the depth of the tree to return a path for * @return an array of tree nodes that represent the path to node. */ Object[] getPathToRoot(Object node, int depth) @@ -3349,47 +3233,13 @@ public class BasicTreeUI extends TreeUI } /** - * Returns the level of the node in the tree. - * - * @param node - - * current node - * @return the number of the level - */ - int getLevel(Object node) - { - int count = -1; - - Object current = node; - - if (treeModel != null) - { - Object root = treeModel.getRoot(); - if (!tree.isRootVisible() && tree.isExpanded(new TreePath(root))) - count--; - - do - { - current = getParent(root, current); - count++; - } - while (current != null); - } - return count; - } - - /** * Draws a vertical line using the given graphic context * - * @param g - * is the graphic context - * @param c - * is the component the new line will belong to - * @param x - * is the horizonal position - * @param top - * specifies the top of the line - * @param bottom - * specifies the bottom of the line + * @param g is the graphic context + * @param c is the component the new line will belong to + * @param x is the horizonal position + * @param top specifies the top of the line + * @param bottom specifies the bottom of the line */ protected void paintVerticalLine(Graphics g, JComponent c, int x, int top, int bottom) @@ -3402,16 +3252,11 @@ public class BasicTreeUI extends TreeUI /** * Draws a horizontal line using the given graphic context * - * @param g - * is the graphic context - * @param c - * is the component the new line will belong to - * @param y - * is the vertical position - * @param left - * specifies the left point of the line - * @param right - * specifies the right point of the line + * @param g is the graphic context + * @param c is the component the new line will belong to + * @param y is the vertical position + * @param left specifies the left point of the line + * @param right specifies the right point of the line */ protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left, int right) @@ -3424,16 +3269,11 @@ public class BasicTreeUI extends TreeUI /** * Draws an icon at around a specific position * - * @param c - * is the component the new line will belong to - * @param g - * is the graphic context - * @param icon - * is the icon which will be drawn - * @param x - * is the center position in x-direction - * @param y - * is the center position in y-direction + * @param c is the component the new line will belong to + * @param g is the graphic context + * @param icon is the icon which will be drawn + * @param x is the center position in x-direction + * @param y is the center position in y-direction */ protected void drawCentered(Component c, Graphics g, Icon icon, int x, int y) { @@ -3451,14 +3291,10 @@ public class BasicTreeUI extends TreeUI /** * Draws a dashed horizontal line. * - * @param g - - * the graphics configuration. - * @param y - - * the y location to start drawing at - * @param x1 - - * the x location to start drawing at - * @param x2 - - * the x location to finish drawing at + * @param g - the graphics configuration. + * @param y - the y location to start drawing at + * @param x1 - the x location to start drawing at + * @param x2 - the x location to finish drawing at */ protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2) { @@ -3470,14 +3306,10 @@ public class BasicTreeUI extends TreeUI /** * Draws a dashed vertical line. * - * @param g - - * the graphics configuration. - * @param x - - * the x location to start drawing at - * @param y1 - - * the y location to start drawing at - * @param y2 - - * the y location to finish drawing at + * @param g - the graphics configuration. + * @param x - the x location to start drawing at + * @param y1 - the y location to start drawing at + * @param y2 - the y location to finish drawing at */ protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2) { @@ -3490,22 +3322,15 @@ public class BasicTreeUI extends TreeUI * Paints the expand (toggle) part of a row. The receiver should NOT modify * clipBounds, or insets. * - * @param g - - * the graphics configuration + * @param g - the graphics configuration * @param clipBounds - * @param insets - - * @param bounds - - * bounds of expand control - * @param path - - * path to draw control for - * @param row - - * row to draw control for - * @param isExpanded - - * is the row expanded - * @param hasBeenExpanded - - * has the row already been expanded - * @param isLeaf - - * is the path a leaf + * @param bounds - bounds of expand control + * @param path - path to draw control for + * @param row - row to draw control for + * @param isExpanded - is the row expanded + * @param hasBeenExpanded - has the row already been expanded + * @param isLeaf - is the path a leaf */ protected void paintExpandControl(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, @@ -3516,9 +3341,7 @@ public class BasicTreeUI extends TreeUI { Icon icon = getCurrentControlIcon(path); int iconW = icon.getIconWidth(); - int x = bounds.x - rightChildIndent + iconW / 2; - if (x + iconW > bounds.x) - x = bounds.x - rightChildIndent - gap; + int x = bounds.x - iconW - gap; icon.paintIcon(tree, g, x, bounds.y + bounds.height / 2 - icon.getIconHeight() / 2); } @@ -3529,22 +3352,15 @@ public class BasicTreeUI extends TreeUI * clipBounds, or insets. NOTE: parentRow can be -1 if the root is not * visible. * - * @param g - - * the graphics configuration + * @param g - the graphics configuration * @param clipBounds - * @param insets - - * @param bounds - - * bounds of the cell - * @param path - - * path to draw leg for - * @param row - - * row to start drawing at - * @param isExpanded - - * is the row expanded - * @param hasBeenExpanded - - * has the row already been expanded - * @param isLeaf - - * is the path a leaf + * @param bounds - bounds of the cell + * @param path - path to draw leg for + * @param row - row to start drawing at + * @param isExpanded - is the row expanded + * @param hasBeenExpanded - has the row already been expanded + * @param isLeaf - is the path a leaf */ protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, @@ -3554,45 +3370,32 @@ public class BasicTreeUI extends TreeUI boolean isLeaf) { if (row != 0) - paintHorizontalLine(g, tree, bounds.y + bounds.height / 2, bounds.x - gap - - 2, bounds.x); + { + paintHorizontalLine(g, tree, bounds.y + bounds.height / 2, + bounds.x - leftChildIndent - gap, bounds.x - gap); + } } /** * Paints the vertical part of the leg. The receiver should NOT modify * clipBounds, insets. * - * @param g - - * the graphics configuration. + * @param g - the graphics configuration. * @param clipBounds - * @param insets - - * @param path - - * the path to draw the vertical part for. + * @param path - the path to draw the vertical part for. */ protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) { - int max = tree.getVisibleRowCount(); - for (int i = 0; i < max; i++) + Rectangle bounds = getPathBounds(tree, path); + TreePath parent = path.getParentPath(); + if (parent != null) { - Object curr = path.getPathComponent(i); - TreePath currPath = new TreePath(getPathToRoot(curr, 0)); - int numChild = treeModel.getChildCount(curr); - if (numChild > 0 && tree.isExpanded(currPath)) - { - Rectangle bounds = getPathBounds(tree, currPath); - Rectangle lastChildBounds = getPathBounds( - tree, - new TreePath( - getPathToRoot( - treeModel.getChild( - curr, - numChild - 1), - 0))); - paintVerticalLine(g, tree, bounds.x + gap + 2, bounds.y - + bounds.height - 2, - lastChildBounds.y + lastChildBounds.height / 2); - } + Rectangle parentBounds = getPathBounds(tree, parent); + paintVerticalLine(g, tree, parentBounds.x + 2* gap, + parentBounds.y + parentBounds.height / 2, + bounds.y + bounds.height / 2); } } @@ -3600,22 +3403,15 @@ public class BasicTreeUI extends TreeUI * Paints the renderer part of a row. The receiver should NOT modify * clipBounds, or insets. * - * @param g - - * the graphics configuration + * @param g - the graphics configuration * @param clipBounds - * @param insets - - * @param bounds - - * bounds of expand control - * @param path - - * path to draw control for - * @param row - - * row to draw control for - * @param isExpanded - - * is the row expanded - * @param hasBeenExpanded - - * has the row already been expanded - * @param isLeaf - - * is the path a leaf + * @param bounds - bounds of expand control + * @param path - path to draw control for + * @param row - row to draw control for + * @param isExpanded - is the row expanded + * @param hasBeenExpanded - has the row already been expanded + * @param isLeaf - is the path a leaf */ protected void paintRow(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, TreePath path, int row, @@ -3626,26 +3422,21 @@ public class BasicTreeUI extends TreeUI boolean hasIcons = false; Object node = path.getLastPathComponent(); - if (tree.isVisible(path)) - { - if (!validCachedPreferredSize) - updateCachedPreferredSize(); - - paintExpandControl(g, clipBounds, insets, bounds, path, row, - isExpanded, hasBeenExpanded, isLeaf); - - if (row != 0) - bounds.x += gap; - bounds.width = preferredSize.width + bounds.x; - TreeCellRenderer dtcr = tree.getCellRenderer(); - if (dtcr == null) - dtcr = createDefaultCellRenderer(); - - Component c = dtcr.getTreeCellRendererComponent(tree, node, selected, - isExpanded, isLeaf, - row, tree.hasFocus()); - rendererPane.paintComponent(g, c, c.getParent(), bounds); - } + paintExpandControl(g, clipBounds, insets, bounds, path, row, isExpanded, + hasBeenExpanded, isLeaf); + + TreeCellRenderer dtcr = currentCellRenderer; + + boolean focused = false; + if (treeSelectionModel != null) + focused = treeSelectionModel.getLeadSelectionRow() == row + && tree.isFocusOwner(); + + Component c = dtcr.getTreeCellRendererComponent(tree, node, selected, + isExpanded, isLeaf, row, + focused); + + rendererPane.paintComponent(g, c, c.getParent(), bounds); } /** @@ -3660,16 +3451,11 @@ public class BasicTreeUI extends TreeUI * Returns true if the expand (toggle) control should be drawn for the * specified row. * - * @param path - - * current path to check for. - * @param row - - * current row to check for. - * @param isExpanded - - * true if the path is expanded - * @param hasBeenExpanded - - * true if the path has been expanded already - * @param isLeaf - - * true if the row is a lead + * @param path - current path to check for. + * @param row - current row to check for. + * @param isExpanded - true if the path is expanded + * @param hasBeenExpanded - true if the path has been expanded already + * @param isLeaf - true if the row is a lead */ protected boolean shouldPaintExpandControl(TreePath path, int row, boolean isExpanded, @@ -3677,121 +3463,25 @@ public class BasicTreeUI extends TreeUI boolean isLeaf) { Object node = path.getLastPathComponent(); - return (!isLeaf && getLevel(node) != 0 && hasControlIcons()); + return (! isLeaf && hasControlIcons()); } /** - * Updates the cached current TreePath of all visible nodes in the tree. + * Finish the editing session. */ - void updateCurrentVisiblePath() - { - if (treeModel == null) - return; - - Object next = treeModel.getRoot(); - if (next == null) - return; - - TreePath rootPath = new TreePath(next); - Rectangle bounds = getPathBounds(tree, rootPath); - - // If root is not a valid size to be visible, or is - // not visible and the tree is expanded, then the next node acts - // as the root - if ((bounds.width == 0 && bounds.height == 0) - || (!isRootVisible() && tree.isExpanded(new TreePath(next)))) - { - next = getNextNode(next); - rootPath = new TreePath(next); - } - - Object root = next; - TreePath current = null; - while (next != null) - { - if (current == null) - current = rootPath; - else - current = current.pathByAddingChild(next); - - do - { - TreePath path = new TreePath(getPathToRoot(next, 0)); - if ((tree.isVisible(path) && tree.isExpanded(path)) - || treeModel.isLeaf(next)) - next = getNextNode(next); - else - { - Object pNext = next; - next = getNextSibling(pNext); - // if no next sibling, check parent's next sibling. - if (next == null) - { - Object parent = getParent(root, pNext); - while (next == null && parent != null) - { - next = getNextSibling(parent); - if (next == null) - parent = getParent(root, parent); - } - } - } - } - while (next != null - && !tree.isVisible(new TreePath(getPathToRoot(next, 0)))); - } - - currentVisiblePath = current; - tree.setVisibleRowCount(getRowCount(tree)); - - if (tree.getSelectionModel() != null && tree.getSelectionCount() == 0 - && currentVisiblePath != null) - selectPath( - tree, - new TreePath( - getPathToRoot( - currentVisiblePath.getPathComponent(0), - 0))); - } - - /** - * Get next visible node in the currentVisiblePath. Package private for use in - * inner classes. - * - * @param node - * current node - * @return the next visible node in the JTree. Return null if there are no - * more. - */ - Object getNextVisibleNode(Object node) - { - if (currentVisiblePath != null) - { - Object[] nodes = currentVisiblePath.getPath(); - int i = 0; - while (i < nodes.length && !node.equals(nodes[i])) - i++; - // return the next node - if (i + 1 < nodes.length) - return nodes[i + 1]; - } - return null; - } - - /** - * Finish the editing session. - */ void finish() { + treeState.invalidatePathBounds(treeState.getPathForRow(editingRow)); editingPath = null; - editingRow = -1; + editingRow = - 1; stopEditingInCompleteEditing = false; isEditing = false; + Rectangle bounds = editingComponent.getParent().getBounds(); tree.removeAll(); validCachedPreferredSize = false; - // Repaint the region, where was the editing component. - tree.repaint(editingComponent.getParent().getBounds()); + tree.repaint(bounds); editingComponent = null; + tree.requestFocus(); } } // BasicTreeUI diff --git a/javax/swing/plaf/metal/MetalBorders.java b/javax/swing/plaf/metal/MetalBorders.java index 77e684127..98a00ee0a 100644 --- a/javax/swing/plaf/metal/MetalBorders.java +++ b/javax/swing/plaf/metal/MetalBorders.java @@ -139,24 +139,59 @@ public class MetalBorders if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme) paintOceanButtonBorder(c, g, x, y, w, h); else - { - ButtonModel bmodel = null; - - if (c instanceof AbstractButton) - bmodel = ((AbstractButton) c).getModel(); + paintDefaultButtonBorder(c, g, x, y, w, h); + } - Color darkShadow = MetalLookAndFeel.getControlDarkShadow(); - Color shadow = MetalLookAndFeel.getControlShadow(); - Color light = MetalLookAndFeel.getControlHighlight(); - Color middle = MetalLookAndFeel.getControl(); + /** + * Paints the button border for the DefaultMetalTheme. + * + * @param c the component (button) + * @param g the graphics object to use + * @param x the upper left corner of the component, X coordinate + * @param y the upper left corner of the component, Y coordinate + * @param w the width of the component + * @param h the height of the component + */ + private void paintDefaultButtonBorder(Component c, Graphics g, int x, + int y, int w, int h) + { + ButtonModel bmodel = null; - if (c.isEnabled()) - { - // draw dark border - g.setColor(darkShadow); - g.drawRect(x, y, w - 2, h - 2); + if (c instanceof AbstractButton) + bmodel = ((AbstractButton) c).getModel(); + + Color darkShadow = MetalLookAndFeel.getControlDarkShadow(); + Color shadow = MetalLookAndFeel.getControlShadow(); + Color light = MetalLookAndFeel.getControlHighlight(); + Color middle = MetalLookAndFeel.getControl(); - if (!bmodel.isPressed()) + if (c.isEnabled()) + { + // draw dark border + g.setColor(darkShadow); + g.drawRect(x, y, w - 2, h - 2); + + // If the button is the default button, we paint a special border, + // regardless of the pressed state. + if (c instanceof JButton && ((JButton) c).isDefaultButton()) + { + g.drawRect(x + 1, y + 1, w - 4, h - 4); + // Draw white highlight. + g.setColor(light); + g.drawLine(x + 2, y + 2, x + w - 4, y + 2); + g.drawLine(x + 2, y + 2, x + 2, y + h - 4); + g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1); + g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 1); + // Draw crossing pixels. + g.setColor(middle); + g.fillRect(x + w - 2, y + 2, 1, 1); + g.fillRect(x + 2, y + h - 2, 1, 1); + } + else + { + // The normal border. This is used when the button is not + // pressed or the button is not armed. + if (! (bmodel.isPressed() && bmodel.isArmed()) ) { // draw light border g.setColor(light); @@ -167,6 +202,8 @@ public class MetalBorders g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); } + // The pressed border. This border is painted only when + // the button is both pressed and armed. else { // draw light border @@ -185,12 +222,12 @@ public class MetalBorders g.drawRect(x + w - 2, y + 1, 0, 0); } } - else - { - // draw disabled border - g.setColor(MetalLookAndFeel.getInactiveControlTextColor()); - g.drawRect(x, y, w - 2, h - 2); - } + } + else + { + // draw disabled border + g.setColor(MetalLookAndFeel.getInactiveControlTextColor()); + g.drawRect(x, y, w - 2, h - 2); } } @@ -219,11 +256,16 @@ public class MetalBorders if (c.isEnabled()) { - if (bmodel.isPressed()) + // Paint the pressed border if the button is pressed, or if + // the button is the default button. In the OceanTheme, the default + // button has the same border as a pressed button. + if (bmodel.isPressed() || ((c instanceof JButton) + && ((JButton) c).isDefaultButton())) { - // draw fat border - g.drawLine(x + 1, y + 1, x + w - 2, y + 1); - g.drawLine(x + 1, y + 1, x + 1, y + h - 2); + // Draw fat border. + g.setColor(darkShadow); + g.drawRect(x, y, w - 1, h - 1); + g.drawRect(x + 1, y + 1, w - 3, h - 3); } else if (bmodel.isRollover()) { diff --git a/javax/swing/plaf/metal/MetalButtonUI.java b/javax/swing/plaf/metal/MetalButtonUI.java index 485424cda..83cd33662 100644 --- a/javax/swing/plaf/metal/MetalButtonUI.java +++ b/javax/swing/plaf/metal/MetalButtonUI.java @@ -45,11 +45,13 @@ import java.awt.Graphics; import java.awt.Rectangle; import javax.swing.AbstractButton; +import javax.swing.ButtonModel; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonUI; @@ -159,8 +161,8 @@ public class MetalButtonUI } /** - * Paints the background of the button to indicate that it is in the "pressed" - * state. + * Paints the background of the button to indicate that it is in the + * "pressed" state. * * @param g the graphics context. * @param b the button. @@ -233,9 +235,12 @@ public class MetalButtonUI public void update(Graphics g, JComponent c) { AbstractButton b = (AbstractButton) c; + ButtonModel m = b.getModel(); if (b.isContentAreaFilled() - && UIManager.get(getPropertyPrefix() + "gradient") != null - && !b.getModel().isPressed() && b.isEnabled()) + && (UIManager.get(getPropertyPrefix() + "gradient") != null) + && ! m.isPressed() && ! m.isArmed() + && b.isEnabled() + && (b.getBackground() instanceof UIResource)) { MetalUtils.paintGradient(g, 0, 0, c.getWidth(), c.getHeight(), SwingConstants.VERTICAL, diff --git a/javax/swing/plaf/metal/MetalComboBoxButton.java b/javax/swing/plaf/metal/MetalComboBoxButton.java index b8dd95098..3787a98c3 100644 --- a/javax/swing/plaf/metal/MetalComboBoxButton.java +++ b/javax/swing/plaf/metal/MetalComboBoxButton.java @@ -112,6 +112,7 @@ public class MetalComboBoxButton iconOnly = onlyIcon; listBox = list; rendererPane = pane; + setRolloverEnabled(false); setEnabled(comboBox.isEnabled()); setFocusable(comboBox.isEnabled()); } @@ -187,7 +188,7 @@ public class MetalComboBoxButton */ public boolean isFocusTraversable() { - return !comboBox.isEditable() && comboBox.isEnabled(); + return false; } /** @@ -198,8 +199,16 @@ public class MetalComboBoxButton public void setEnabled(boolean enabled) { super.setEnabled(enabled); - // TODO: figure out what this might need to be used for - // perhaps it has something to do with the button's icon and/or border? + if (enabled) + { + setBackground(comboBox.getBackground()); + setForeground(comboBox.getForeground()); + } + else + { + setBackground(UIManager.getColor("ComboBox.disabledBackground")); + setForeground(UIManager.getColor("ComboBox.disabledForeground")); + } } /** diff --git a/javax/swing/plaf/metal/MetalComboBoxEditor.java b/javax/swing/plaf/metal/MetalComboBoxEditor.java index fb25644fa..11e415103 100644 --- a/javax/swing/plaf/metal/MetalComboBoxEditor.java +++ b/javax/swing/plaf/metal/MetalComboBoxEditor.java @@ -39,6 +39,7 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Component; +import java.awt.Dimension; import java.awt.Graphics; import java.awt.Insets; @@ -79,16 +80,31 @@ public class MetalComboBoxEditor extends BasicComboBoxEditor int h) { g.translate(x, y); - g.setColor(MetalLookAndFeel.getControlDarkShadow()); - g.drawLine(0, 0, w - 1, 0); - g.drawLine(0, 0, 0, h - 2); - g.drawLine(0, h - 2, w - 1, h - 2); - g.setColor(MetalLookAndFeel.getControlHighlight()); - g.drawLine(1, 1, w - 1, 1); - g.drawLine(1, 1, 1, h - 1); - g.drawLine(1, h - 1, w - 1, h - 1); - g.setColor(MetalLookAndFeel.getControl()); - g.drawLine(1, h - 2, 1, h - 2); + if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(0, 0, 0, h - 1); + g.drawLine(0, h - 1, w - 1, h - 1); + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(1, 1, w - 2, 1); + g.drawLine(1, 1, 1, h - 2); + g.drawLine(1, h - 2, w - 1, h - 2); + g.drawLine(w - 1, 1, w - 1, h - 2); + } + else + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(0, 0, 0, h - 2); + g.drawLine(0, h - 2, w - 1, h - 2); + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 1, w - 1, 1); + g.drawLine(1, 1, 1, h - 1); + g.drawLine(1, h - 1, w - 1, h - 1); + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(1, h - 2, 1, h - 2); + } g.translate(-x, -y); } @@ -125,7 +141,40 @@ public class MetalComboBoxEditor extends BasicComboBoxEditor // Nothing to do here. } } - + + /** + * A special textfield implementation for the MetalComboBoxEditor. + */ + private class EditorTextField extends JTextField + { + EditorTextField(String s, int columns) + { + super(s, columns); + } + + /** + * Tests seem to show that the textfield in MetalComboBoxEditors have + * a height + 4. + */ + public Dimension getPreferredSize() + { + Dimension size = super.getPreferredSize(); + size.height += 4; + return size; + } + + /** + * Tests seem to show that the textfield in MetalComboBoxEditors have + * a height + 4. + */ + public Dimension getMinimumSize() + { + Dimension size = super.getMinimumSize(); + size.height += 4; + return size; + } + } + /** The editor's border insets. */ protected static Insets editorBorderInsets = new Insets(2, 2, 2, 0); @@ -134,7 +183,7 @@ public class MetalComboBoxEditor extends BasicComboBoxEditor */ public MetalComboBoxEditor() { - super(); + editor = new EditorTextField("", 9); editor.setBorder(new MetalComboBoxEditorBorder()); } diff --git a/javax/swing/plaf/metal/MetalComboBoxUI.java b/javax/swing/plaf/metal/MetalComboBoxUI.java index 0266d34f8..c24c08505 100644 --- a/javax/swing/plaf/metal/MetalComboBoxUI.java +++ b/javax/swing/plaf/metal/MetalComboBoxUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; @@ -130,7 +131,7 @@ public class MetalComboBoxUI extends BasicComboBoxUI String name = e.getPropertyName(); if (name.equals("editable")) editablePropertyChanged(e); - if (name.equals("enabled")) + else if (name.equals("enabled")) { if (arrowButton instanceof MetalComboBoxButton) { @@ -139,6 +140,18 @@ public class MetalComboBoxUI extends BasicComboBoxUI comboBox.repaint(); } } + else if (name.equals("background")) + { + Color c = (Color) e.getNewValue(); + arrowButton.setBackground(c); + listBox.setBackground(c); + } + else if (name.equals("foreground")) + { + Color c = (Color) e.getNewValue(); + arrowButton.setForeground(c); + listBox.setForeground(c); + } } } @@ -306,14 +319,8 @@ public class MetalComboBoxUI extends BasicComboBoxUI { d = super.getMinimumSize(c); Insets arrowMargin = arrowButton.getMargin(); - Insets comboInsets = comboBox.getInsets(); - if (editor instanceof JComponent) - { - Insets editorInsets = ((JComponent) editor).getInsets(); - d.height += editorInsets.top + editorInsets.bottom; - } d.height += arrowMargin.top + arrowMargin.bottom; - d.height += comboInsets.top + comboInsets.bottom; + d.width += arrowMargin.left + arrowMargin.right; } else { diff --git a/javax/swing/plaf/metal/MetalDesktopIconUI.java b/javax/swing/plaf/metal/MetalDesktopIconUI.java index ecbb76e6e..0c1163a8e 100644 --- a/javax/swing/plaf/metal/MetalDesktopIconUI.java +++ b/javax/swing/plaf/metal/MetalDesktopIconUI.java @@ -1,5 +1,5 @@ /* MetalDesktopIconUI.java - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -50,10 +50,6 @@ public class MetalDesktopIconUI extends BasicDesktopIconUI { - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for MetalDesktopIcons */ - private static MetalDesktopIconUI instance = null; - /** * Constructs a new instance of <code>MetalDesktopIconUI</code>. */ @@ -63,16 +59,14 @@ public class MetalDesktopIconUI } /** - * Returns a shared instance of <code>MetalDesktopIconUI</code>. + * Returns a new <code>MetalDesktopIconUI</code> instance. * - * @param component the component for which we return an UI instance + * @param component the component (ignored). * - * @return A shared instance of <code>MetalDesktopIconUI</code>. + * @return A new <code>MetalDesktopIconUI</code> instance. */ public static ComponentUI createUI(JComponent component) { - if (instance == null) - instance = new MetalDesktopIconUI(); - return instance; + return new MetalDesktopIconUI(); } } diff --git a/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java b/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java index 534f0ca34..f74828e56 100644 --- a/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java +++ b/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java @@ -93,7 +93,11 @@ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane public void propertyChange(PropertyChangeEvent e) { String propName = e.getPropertyName(); - if (propName.equals("JInternalFrame.isPalette")) + if (e.getPropertyName().equals(JInternalFrame.FRAME_ICON_PROPERTY)) + { + title.setIcon( frame.getFrameIcon() ); + } + else if (propName.equals("JInternalFrame.isPalette")) { if (e.getNewValue().equals(Boolean.TRUE)) setPalette(true); @@ -262,7 +266,7 @@ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane paletteTitleHeight = UIManager.getInt("InternalFrame.paletteTitleHeight"); paletteCloseIcon = UIManager.getIcon("InternalFrame.paletteCloseIcon"); minIcon = MetalIconFactory.getInternalFrameAltMaximizeIcon(16); - + title = new JLabel(frame.getTitle(), MetalIconFactory.getInternalFrameDefaultMenuIcon(), SwingConstants.LEFT); @@ -383,8 +387,8 @@ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane paintPalette(g); else { - paintTitleBackground(g); - paintChildren(g); + paintTitleBackground(g); + paintChildren(g); Dimension d = getSize(); if (frame.isSelected()) g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); diff --git a/javax/swing/plaf/metal/MetalLookAndFeel.java b/javax/swing/plaf/metal/MetalLookAndFeel.java index 38b357219..7a973d46e 100644 --- a/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -1204,7 +1204,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Table.focusCellForeground", getControlTextColor(), "Table.foreground", getControlTextColor(), "Table.focusCellHighlightBorder", - new BorderUIResource.LineBorderUIResource(getControlShadow()), + new BorderUIResource.LineBorderUIResource(getFocusColor()), "Table.focusCellBackground", getWindowBackground(), "Table.gridColor", getControlDarkShadow(), "Table.selectionBackground", new ColorUIResource(204, 204, 255), diff --git a/javax/swing/plaf/metal/MetalRootPaneUI.java b/javax/swing/plaf/metal/MetalRootPaneUI.java index 23051e9bc..6cabc7e86 100644 --- a/javax/swing/plaf/metal/MetalRootPaneUI.java +++ b/javax/swing/plaf/metal/MetalRootPaneUI.java @@ -47,11 +47,11 @@ import java.awt.Graphics; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.LayoutManager2; +import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; import java.awt.event.ActionEvent; -import java.awt.event.WindowEvent; -import java.awt.event.WindowFocusListener; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import javax.swing.AbstractAction; @@ -70,6 +70,7 @@ import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.AbstractBorder; +import javax.swing.event.MouseInputAdapter; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRootPaneUI; @@ -191,6 +192,50 @@ public class MetalRootPaneUI */ private static class MetalTitlePane extends JComponent { + + /** + * Handles dragging of the title pane and moves the window accordingly. + */ + private class MouseHandler + extends MouseInputAdapter + { + /** + * The point where the dragging started. + */ + Point lastDragLocation; + + /** + * Receives notification when the mouse gets pressed on the title pane. + * This updates the lastDragLocation. + * + * @param ev the mouse event + */ + public void mousePressed(MouseEvent ev) + { + lastDragLocation = ev.getPoint(); + } + + /** + * Receives notification when the mouse is dragged on the title pane. + * This will move the nearest window accordingly. + * + * @param ev the mouse event + */ + public void mouseDragged(MouseEvent ev) + { + Point dragLocation = ev.getPoint(); + int deltaX = dragLocation.x - lastDragLocation.x; + int deltaY = dragLocation.y - lastDragLocation.y; + Window window = SwingUtilities.getWindowAncestor(rootPane); + Point loc = window.getLocation(); + window.setLocation(loc.x + deltaX, loc.y + deltaY); + // Note that we do not update the lastDragLocation. This is because + // we move the underlying window while dragging the component, which + // results in having the same lastDragLocation under the mouse while + // dragging. + } + } + /** * The Action responsible for closing the JInternalFrame. */ @@ -255,6 +300,45 @@ public class MetalRootPaneUI } /** + * This action is performed when the iconify button is pressed. + */ + private class IconifyAction + extends AbstractAction + { + + public void actionPerformed(ActionEvent event) + { + Window w = SwingUtilities.getWindowAncestor(rootPane); + if (w instanceof Frame) + { + Frame f = (Frame) w; + int state = f.getExtendedState(); + f.setExtendedState(Frame.ICONIFIED); + } + } + + } + + /** + * This action is performed when the maximize button is pressed. + */ + private class MaximizeAction + extends AbstractAction + { + + public void actionPerformed(ActionEvent event) + { + Window w = SwingUtilities.getWindowAncestor(rootPane); + if (w instanceof Frame) + { + Frame f = (Frame) w; + int state = f.getExtendedState(); + f.setExtendedState(Frame.MAXIMIZED_BOTH); + } + } + } + + /** * This helper class is used to create the minimize, maximize and close * buttons in the top right corner of the Title Pane. These buttons are * special since they cannot be given focus and have no border. @@ -499,23 +583,16 @@ public class MetalRootPaneUI private void installListeners() { - Window window = SwingUtilities.getWindowAncestor(rootPane); - window.addWindowFocusListener(new WindowFocusListener() - { - public void windowGainedFocus(WindowEvent ev) - { - repaint(); - } - public void windowLostFocus(WindowEvent ev) - { - repaint(); - } - }); + MouseInputAdapter mouseHandler = new MouseHandler(); + addMouseListener(mouseHandler); + addMouseMotionListener(mouseHandler); } private void createActions() { closeAction = new CloseAction(); + iconifyAction = new IconifyAction(); + maximizeAction = new MaximizeAction(); } private void assembleSystemMenu() @@ -699,6 +776,21 @@ public class MetalRootPaneUI */ private Dimension prefSize; + /** + * The title pane for l&f decorated frames. + */ + private MetalTitlePane titlePane; + + /** + * Creates a new MetalRootLayout. + * + * @param tp the title pane + */ + MetalRootLayout(MetalTitlePane tp) + { + titlePane = tp; + } + public void addLayoutComponent(Component component, Object constraints) { // Nothing to do here. @@ -747,12 +839,8 @@ public class MetalRootPaneUI { JRootPane rp = (JRootPane) parent; JLayeredPane layeredPane = rp.getLayeredPane(); - Component contentPane = layeredPane.getComponent(0); - Component titlePane = layeredPane.getComponent(1); - Component menuBar = null; - if (layeredPane.getComponentCount() > 2 - && layeredPane.getComponent(2) instanceof JMenuBar) - menuBar = layeredPane.getComponent(2); + Component contentPane = rp.getContentPane(); + Component menuBar = rp.getJMenuBar(); // We must synchronize here, otherwise we cannot guarantee that the // prefSize is still non-null when returning. @@ -789,12 +877,8 @@ public class MetalRootPaneUI { JRootPane rp = (JRootPane) parent; JLayeredPane layeredPane = rp.getLayeredPane(); - Component contentPane = layeredPane.getComponent(0); - Component titlePane = layeredPane.getComponent(1); - Component menuBar = null; - if (layeredPane.getComponentCount() > 2 - && layeredPane.getComponent(2) instanceof JMenuBar) - menuBar = layeredPane.getComponent(2); + Component contentPane = rp.getContentPane(); + Component menuBar = rp.getJMenuBar(); Component glassPane = rp.getGlassPane(); if (glassPaneBounds == null || layeredPaneBounds == null @@ -937,6 +1021,7 @@ public class MetalRootPaneUI */ public void propertyChange(PropertyChangeEvent ev) { + super.propertyChange(ev); String propertyName = ev.getPropertyName(); if (propertyName.equals("windowDecorationStyle")) { @@ -958,12 +1043,14 @@ public class MetalRootPaneUI private void installWindowDecorations(JRootPane rp) { rp.setBorder(new MetalFrameBorder()); - rp.setLayout(new MetalRootLayout()); + MetalTitlePane titlePane = new MetalTitlePane(rp); + rp.setLayout(new MetalRootLayout(titlePane)); // We should have a contentPane already. - assert rp.getLayeredPane().getComponentCount() == 1 + assert rp.getLayeredPane().getComponentCount() > 0 : "We should have a contentPane already"; - rp.getLayeredPane().add(new MetalTitlePane(rp), - JLayeredPane.FRAME_CONTENT_LAYER); + + rp.getLayeredPane().add(titlePane, + JLayeredPane.FRAME_CONTENT_LAYER, 1); } /** @@ -975,6 +1062,14 @@ public class MetalRootPaneUI private void uninstallWindowDecorations(JRootPane rp) { rp.setBorder(null); - rp.getLayeredPane().remove(1); + JLayeredPane lp = rp.getLayeredPane(); + for (int i = lp.getComponentCount() - 1; i >= 0; --i) + { + if (lp.getComponent(i) instanceof MetalTitlePane) + { + lp.remove(i); + break; + } + } } } diff --git a/javax/swing/plaf/metal/MetalSliderUI.java b/javax/swing/plaf/metal/MetalSliderUI.java index 9b2f45cc7..f97717f31 100644 --- a/javax/swing/plaf/metal/MetalSliderUI.java +++ b/javax/swing/plaf/metal/MetalSliderUI.java @@ -1,5 +1,5 @@ /* MetalSliderUI.java - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -210,7 +210,7 @@ public class MetalSliderUI extends BasicSliderUI { int trackX = trackRect.x; int trackY = trackRect.y + (trackRect.height - getTrackWidth()) / 2; - int trackW = trackRect.width - 1; + int trackW = trackRect.width; int trackH = getTrackWidth(); // draw border @@ -264,7 +264,7 @@ public class MetalSliderUI extends BasicSliderUI int trackX = trackRect.x + (trackRect.width - getTrackWidth()) / 2; int trackY = trackRect.y; int trackW = getTrackWidth(); - int trackH = trackRect.height - 1; + int trackH = trackRect.height; if (slider.isEnabled()) BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, darkShadowColor, shadowColor, darkShadowColor, highlightColor); diff --git a/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/javax/swing/plaf/metal/MetalTabbedPaneUI.java index c6c46ffe6..39dec3d66 100644 --- a/javax/swing/plaf/metal/MetalTabbedPaneUI.java +++ b/javax/swing/plaf/metal/MetalTabbedPaneUI.java @@ -47,6 +47,7 @@ import javax.swing.JComponent; import javax.swing.JTabbedPane; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicTabbedPaneUI; /** @@ -101,6 +102,17 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI // do nothing, because the selected tab does not have extra padding in // the MetalLookAndFeel } + + /** + * Overridden because tab runs are only normalized for TOP and BOTTOM + * tab placement in the Metal L&F. + */ + protected void normalizeTabRuns(int tabPlacement, int tabCount, int start, + int max) + { + if (tabPlacement == TOP || tabPlacement == BOTTOM) + super.normalizeTabRuns(tabPlacement, tabCount, start, max); + } } /** @@ -125,7 +137,12 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI /** The graphics to draw the highlight below the tab. */ private Graphics hg; - + + /** + * Indicates if the tabs are having their background filled. + */ + private boolean tabsOpaque; + /** * Constructs a new instance of MetalTabbedPaneUI. */ @@ -153,7 +170,7 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI */ protected LayoutManager createLayoutManager() { - return super.createLayoutManager(); + return new TabbedPaneLayout(); } /** @@ -172,16 +189,24 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { - if (tabPlacement == TOP) - paintTopTabBorder(tabIndex, g, x, y, w, h, 0, 0, isSelected); - else if (tabPlacement == LEFT) - paintLeftTabBorder(tabIndex, g, x, y, w, h, 0, 0, isSelected); - else if (tabPlacement == BOTTOM) - paintBottomTabBorder(tabIndex, g, x, y, w, h, 0, 0, isSelected); - else if (tabPlacement == RIGHT) - paintRightTabBorder(tabIndex, g, x, y, w, h, 0, 0, isSelected); - else - throw new AssertionError("Unrecognised 'tabPlacement' argument."); + int bottom = y + h - 1; + int right = x + w - 1; + + switch (tabPlacement) + { + case LEFT: + paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); + break; + case BOTTOM: + paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); + break; + case RIGHT: + paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); + break; + case TOP: + default: + paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); + } } /** @@ -194,35 +219,90 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI * @param y the y-coordinate for the tab's bounding rectangle. * @param w the width for the tab's bounding rectangle. * @param h the height for the tab's bounding rectangle. - * @param btm ??? - * @param rght ??? + * @param btm the y coordinate of the bottom border + * @param rght the x coordinate of the right border * @param isSelected indicates whether the tab is selected. */ 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); + int tabCount = tabPane.getTabCount(); + int currentRun = getRunForTab(tabCount, tabIndex); + int right = w - 1; + int bottom = h - 1; + + // Paint gap. if (shouldFillGap(currentRun, tabIndex, x, y)) { g.translate(x, y); - g.setColor(getColorForGap(currentRun, x, y)); + g.setColor(getColorForGap(currentRun, x, y + 1)); g.fillRect(1, 0, 5, 3); g.fillRect(1, 3, 2, 2); g.translate(-x, -y); } - - if (isSelected) - { - g.setColor(MetalLookAndFeel.getControlHighlight()); - g.drawLine(x + 1, y + h, x + 1, y + 6); - g.drawLine(x + 1, y + 6, x + 6, y + 1); - g.drawLine(x + 6, y + 1, x + w - 1, y + 1); - } - g.setColor(MetalLookAndFeel.getControlDarkShadow()); - g.drawLine(x, y + h - 1, x, y + 6); - g.drawLine(x, y + 6, x + 6, y); - g.drawLine(x + 6, y, x + w, y); - g.drawLine(x + w, y, x + w, y + h - 1); + + g.translate(x, y); + + boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; + Color oceanSelectedBorder = + UIManager.getColor("TabbedPane.borderHightlightColor"); + if (isOcean && isSelected) + g.setColor(oceanSelectedBorder); + else + g.setColor(darkShadow); + + // Slant + g.drawLine(1, 5, 6, 0); + // Top. + g.drawLine(6, 0, right, 0); + // Right. + int lastIndex = lastTabInRun(tabCount, currentRun); + if (tabIndex == lastIndex) + g.drawLine(right, 1, right, bottom); + // Left. + int selectedIndex = tabPane.getSelectedIndex(); + if (isOcean && tabIndex - 1 == selectedIndex + && currentRun == getRunForTab(tabCount, selectedIndex)) + { + g.setColor(oceanSelectedBorder); + } + if (tabIndex != tabRuns[runCount - 1]) + { + if (isOcean && isSelected) + { + g.drawLine(0, 6, 0, bottom); + g.setColor(darkShadow); + g.drawLine(0, 0, 0, 5); + } + else + { + g.drawLine(0, 0, 0, bottom); + } + } + else + { + g.drawLine(0, 6, 0, bottom); + } + + // Paint the highlight. + g.setColor(isSelected ? selectHighlight : highlight); + // Slant. + g.drawLine(1, 6, 6, 1); + // Top. + g.drawLine(6, 1, right, 1); + // Left. + g.drawLine(1, 6, 1, bottom); + int firstIndex = tabRuns[currentRun]; + if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1]) + { + if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1]) + g.setColor(selectHighlight); + else + g.setColor(highlight); + g.drawLine(1, 0, 1, 4); + } + + g.translate(-x, -y); } /** @@ -242,18 +322,115 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { - if (isSelected) - { - g.setColor(MetalLookAndFeel.getControlHighlight()); - g.drawLine(x + 1, y + h, x + 1, y + 6); - g.drawLine(x + 1, y + 6, x + 6, y + 1); - g.drawLine(x + 6, y + 1, x + w - 1, y + 1); - } - g.setColor(MetalLookAndFeel.getControlDarkShadow()); - g.drawLine(x, y + h, x, y + 6); - g.drawLine(x, y + 6, x + 6, y); - g.drawLine(x + 6, y, x + w - 1, y); - g.drawLine(x, y + h, x + w - 1, y + h); + g.translate(x, y); + int bottom = h - 1; + int right = w - 1; + + + int tabCount = tabPane.getTabCount(); + int currentRun = getRunForTab(tabCount, tabIndex); + int firstIndex = tabRuns[currentRun]; + + // Paint the part of the above tab. + if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque) + { + Color c; + if (tabPane.getSelectedIndex() == tabIndex - 1) + c = selectColor; + else + c = getUnselectedBackground(tabIndex - 1); + g.setColor(c); + g.fillRect(2, 0, 4, 3); + g.drawLine(2, 3, 2, 3); + } + + // Paint the highlight. + boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; + if (isOcean) + { + g.setColor(isSelected ? selectHighlight : MetalLookAndFeel.getWhite()); + } + else + { + g.setColor(isSelected ? selectHighlight : highlight); + } + // Slant. + g.drawLine(1, 6, 6, 1); + // Left. + g.drawLine(1, 6, 1, bottom); + // Top. + g.drawLine(6, 1, right, 1); + if (tabIndex != firstIndex) + { + if (isOcean) + { + g.setColor(MetalLookAndFeel.getWhite()); + } + g.drawLine(1, 0, 1, 4); + } + + // Paint border. + Color oceanSelectedBorder = + UIManager.getColor("TabbedPane.borderHightlightColor"); + if (isOcean && isSelected) + { + g.setColor(oceanSelectedBorder); + } + else + { + g.setColor(darkShadow); + } + + // Slant. + g.drawLine(1, 5, 6, 0); + // Top. + g.drawLine(6, 0, right, 0); + // Bottom. + int lastIndex = lastTabInRun(tabCount, currentRun); + if (tabIndex == lastIndex) + { + g.drawLine(0, bottom, right, bottom); + } + // Left. + if (isOcean) + { + if (tabPane.getSelectedIndex() == tabIndex - 1) + { + g.drawLine(0, 5, 0, bottom); + g.setColor(oceanSelectedBorder); + g.drawLine(0, 0, 0, 5); + } + else if (isSelected) + { + g.drawLine(0, 5, 0, bottom); + if (tabIndex != 0) + { + g.setColor(darkShadow); + g.drawLine(0, 0, 0, 5); + } + } + else if (tabIndex != firstIndex) + { + g.drawLine(0, 0, 0, bottom); + } + else + { + g.drawLine(0, 6, 0, bottom); + } + } + else + { + if (tabIndex != firstIndex) + { + g.drawLine(0, 0, 0, bottom); + } + else + { + g.drawLine(0, 6, 0, bottom); + } + } + + g.translate(-x, -y); } /** @@ -273,17 +450,92 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { - if (isSelected) - { - g.setColor(MetalLookAndFeel.getControlHighlight()); - g.drawLine(x, y + 1, x + w - 7, y + 1); - g.drawLine(x + w - 7, y + 1, x + w - 1, y + 7); - } - g.setColor(MetalLookAndFeel.getControlDarkShadow()); - g.drawLine(x, y, x + w - 7, y); - g.drawLine(x + w - 7, y, x + w - 1, y + 6); - g.drawLine(x + w - 1, y + 6, x + w - 1, y + h - 1); - g.drawLine(x + w - 1, y + h, x, y + h); + g.translate(x, y); + int bottom = h - 1; + int right = w - 1; + + int tabCount = tabPane.getTabCount(); + int currentRun = getRunForTab(tabCount, tabIndex); + int firstIndex = tabRuns[currentRun]; + + // Paint part of the above tab. + if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque) + { + Color c; + if (tabPane.getSelectedIndex() == tabIndex - 1) + c = UIManager.getColor("TabbedPane.tabAreaBackground"); + else + c = getUnselectedBackground(tabIndex - 1); + g.fillRect(right - 5, 0, 5, 3); + g.fillRect(right - 2, 3, 2, 2); + } + + // Paint highlight. + g.setColor(isSelected ? selectHighlight : highlight); + + // Slant. + g.drawLine(right - 6, 1, right - 1, 6); + // Top. + g.drawLine(0, 1, right - 6, 1); + // Left. + if (! isSelected) + { + g.drawLine(0, 1, 0, bottom); + } + + // Paint border. + boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; + Color oceanSelectedBorder = + UIManager.getColor("TabbedPane.borderHightlightColor"); + if (isOcean && isSelected) + { + g.setColor(oceanSelectedBorder); + } + else + { + g.setColor(darkShadow); + } + + // Bottom. + int lastIndex = lastTabInRun(tabCount, currentRun); + if (tabIndex == lastIndex) + { + g.drawLine(0, bottom, right, bottom); + } + // Slant. + if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1) + { + g.setColor(oceanSelectedBorder); + } + g.drawLine(right - 6, 0, right, 6); + // Top. + g.drawLine(0, 0, right - 6, 0); + // Right. + if (isOcean && isSelected) + { + g.drawLine(right, 6, right, bottom); + if (tabIndex != firstIndex) + { + g.setColor(darkShadow); + g.drawLine(right, 0, right, 5); + } + } + else if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1) + { + g.setColor(oceanSelectedBorder); + g.drawLine(right, 0, right, 6); + g.setColor(darkShadow); + g.drawLine(right, 6, right, bottom); + } + else if (tabIndex != firstIndex) + { + g.drawLine(right, 0, right, bottom); + } + else + { + g.drawLine(right, 6, right, bottom); + } + g.translate(-x, -y); } /** @@ -303,27 +555,94 @@ 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); + int bottom = h - 1; + int right = w - 1; + + int tabCount = tabPane.getTabCount(); + int currentRun = getRunForTab(tabCount, tabIndex); + // Paint gap if necessary. 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.fillRect(1, bottom - 4, 3, 5); + g.fillRect(4, bottom - 1, 2, 2); g.translate(-x, -y); } - - if (isSelected) - { - g.setColor(MetalLookAndFeel.getControlHighlight()); - g.drawLine(x + 1, y, x + 1, y + h - 7); - g.drawLine(x + 1, y + h - 7, x + 7, y + h - 1); - } - g.setColor(MetalLookAndFeel.getControlDarkShadow()); - g.drawLine(x, y, x, y + h - 7); - g.drawLine(x, y + h - 7, x + 6, y + h - 1); - g.drawLine(x + 6, y + h - 1, x + w, y + h - 1); - g.drawLine(x + w, y + h - 1, x + w, y); + + g.translate(x, y); + + // Paint border. + boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; + Color oceanSelectedBorder = + UIManager.getColor("TabbedPane.borderHightlightColor"); + if (isOcean && isSelected) + { + g.setColor(oceanSelectedBorder); + } + else + { + g.setColor(darkShadow); + } + // Slant. + g.drawLine(1, bottom - 5, 6, bottom); + // Bottom. + g.drawLine(6, bottom, right, bottom); + // Right. + int lastIndex = lastTabInRun(tabCount, currentRun); + if (tabIndex == lastIndex) + { + g.drawLine(right, 0, right, bottom); + } + // Left. + if (isOcean && isSelected) + { + g.drawLine(0, 0, 0, bottom - 5); + if ((currentRun == 0 && tabIndex != 0) + || (currentRun > 0 && tabIndex != tabRuns[currentRun - 1])) + { + g.setColor(darkShadow); + g.drawLine(0, bottom - 5, 0, bottom); + } + } + else + { + if (isOcean && tabIndex == tabPane.getSelectedIndex()+ 1) + { + g.setColor(oceanSelectedBorder); + } + if (tabIndex != tabRuns[runCount- 1]) + { + g.drawLine(0, 0, 0, bottom); + } + else + { + g.drawLine(0, 0, 0, bottom - 6); + } + } + + // Paint highlight. + g.setColor(isSelected ? selectHighlight : highlight); + // Slant. + g.drawLine(1, bottom - 6, 6, bottom - 1); + // Left. + g.drawLine(1, 0, 1, bottom - 6); + + int firstIndex = tabRuns[currentRun]; + if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1]) + { + if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1]) + { + g.setColor(selectHighlight); + } + else + { + g.setColor(highlight); + } + g.drawLine(1, bottom - 4, 1, bottom); + } + + g.translate(-x, -y); } /** @@ -343,42 +662,29 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI int tabIndex, int x, int y, int w, int h, boolean isSelected) { if (isSelected) - g.setColor(UIManager.getColor("TabbedPane.selected")); + g.setColor(selectColor); else - { - // 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) - { - px = new int[] {x + 6, x + w - 1, x + w -1, x + 2, x + 2}; - py = new int[] {y + 2, y + 2, y + h - 1, y + h -1, y + 6}; - } - else if (tabPlacement == LEFT) - { - px = new int[] {x + 6, x + w - 1, x + w -1, x + 2, x + 2}; - py = new int[] {y + 2, y + 2, y + h - 1, y + h -1, y + 6}; - } - else if (tabPlacement == BOTTOM) - { - px = new int[] {x + 2, x + w - 1, x + w -1, x + 8, x + 2}; - py = new int[] {y, y, y + h - 1, y + h -1, y + h - 7}; - } - else if (tabPlacement == RIGHT) - { - px = new int[] {x + 2, x + w - 7, x + w - 1, x + w - 1, x + 2}; - py = new int[] {y + 2, y + 2, y + 7, y + h -1, y + h - 1}; - } - else - throw new AssertionError("Unrecognised 'tabPlacement' argument."); - g.fillPolygon(px, py, 5); - hg = g; - paintHighlightBelowTab(); + g.setColor(getUnselectedBackground(tabIndex)); + + switch (tabPlacement) + { + case LEFT: + g.fillRect(x + 5, y + 1, w - 5, h - 1); + g.fillRect(x + 2, y + 4, 3, h - 4); + break; + case BOTTOM: + g.fillRect(x + 2, y, w - 2, h - 3); + g.fillRect(x + 5, y + h - 4, w - 5, 3); + break; + case RIGHT: + g.fillRect(x, y + 1, w - 4, h - 1); + g.fillRect(x + w - 4, y + 5, 3, h - 5); + break; + case TOP: + default: + g.fillRect(x + 4, y + 2, w - 4, h - 2); + g.fillRect(x + 2, y + 5, 2, h - 5); + } } /** @@ -408,6 +714,7 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI selectColor = UIManager.getColor("TabbedPane.selected"); selectHighlight = UIManager.getColor("TabbedPane.selectHighlight"); tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground"); + tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque"); minTabWidth = 0; } @@ -487,4 +794,354 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI // false because tab runs are not rotated in the MetalLookAndFeel return false; } + + protected int calculateMaxTabHeight(int tabPlacement) + { + // FIXME: Why is this overridden? + return super.calculateMaxTabHeight(tabPlacement); + } + + /** + * Returns the amount of overlay among the tabs. In + * the Metal L&F the overlay for LEFT and RIGHT placement + * is half of the maxTabHeight. For TOP and BOTTOM placement + * the tabs do not overlay. + * + * @param tabPlacement the placement + * + * @return the amount of overlay among the tabs + */ + protected int getTabRunOverlay(int tabPlacement) + { + int overlay = 0; + if (tabPlacement == LEFT || tabPlacement == RIGHT) + { + int maxHeight = calculateMaxTabHeight(tabPlacement); + overlay = maxTabHeight / 2; + } + return overlay; + } + + /** + * Paints the upper edge of the content border. + * + * @param g the graphics to use for painting + * @param tabPlacement the tab placement + * @param selectedIndex the index of the selected tab + * @param x the upper left coordinate of the content area + * @param y the upper left coordinate of the content area + * @param w the width of the content area + * @param h the height of the content area + */ + protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, + int selectedIndex, int x, int y, + int w, int h) + { + Color oceanSelectedBorder = + UIManager.getColor("TabbedPane.borderHightlightColor"); + boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; + if (isOcean) + { + g.setColor(oceanSelectedBorder); + } + else + { + g.setColor(selectHighlight); + } + + Rectangle rect = selectedIndex < 0 ? null : + getTabBounds(selectedIndex, calcRect); + + // If tabs are not placed on TOP, or if the selected tab is not in the + // run directly above the content or the selected tab is not visible, + // then we draw an unbroken line. + if (tabPlacement != TOP || selectedIndex < 0 + || rect.y + rect.height + 1 < y || rect.x < x ||rect.x > x + w) + { + g.drawLine(x, y, x + w - 2, y); + if (isOcean && tabPlacement == TOP) + { + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(x, y + 1, x + w - 2, y + 1); + } + } + else + { + boolean isLast = isLastTabInRun(selectedIndex); + if (isLast) + { + g.drawLine(x, y, rect.x + 1, y); + } + else + { + g.drawLine(x, y, rect.x, y); + } + + int right = x + w - 1; + if (rect.x + rect.width < right - 1) + { + if (isLast) + { + g.drawLine(rect.x + rect.width - 1, y, right - 1, y); + } + else + { + g.drawLine(rect.x + rect.width, y, right - 1, y); + } + } + else + { + g.setColor(shadow); + g.drawLine(x + w - 2, y, x + w - 2, y); + } + + // When in OceanTheme, draw another white line. + if (isOcean) + { + g.setColor(MetalLookAndFeel.getWhite()); + if (isLast) + { + g.drawLine(x, y + 1, rect.x + 1, y + 1); + } + else + { + g.drawLine(x, y + 1, rect.x, y + 1); + } + + if (rect.x + rect.width < right - 1) + { + if (isLast) + { + g.drawLine(rect.x + rect.width - 1, y + 1, right - 1, + y + 1); + } + else + { + g.drawLine(rect.x + rect.width, y + 1, right - 1, y + 1); + } + } + else + { + g.setColor(shadow); + g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); + } + } + } + } + + /** + * Paints the lower edge of the content border. + * + * @param g the graphics to use for painting + * @param tabPlacement the tab placement + * @param selectedIndex the index of the selected tab + * @param x the upper left coordinate of the content area + * @param y the upper left coordinate of the content area + * @param w the width of the content area + * @param h the height of the content area + */ + protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, + int selectedIndex, int x, int y, + int w, int h) + { + g.setColor(darkShadow); + + // If tabs are not placed on BOTTOM, or if the selected tab is not in the + // run directly below the content or the selected tab is not visible, + // then we draw an unbroken line. + Rectangle rect = selectedIndex < 0 ? null : + getTabBounds(selectedIndex, calcRect); + boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; + Color oceanSelectedBorder = + UIManager.getColor("TabbedPane.borderHightlightColor"); + if (tabPlacement != BOTTOM || selectedIndex < 0 || rect.y - 1 > h + || rect.x < x || rect.x > x + w) + { + if (isOcean && tabPlacement == BOTTOM) + { + g.setColor(oceanSelectedBorder); + } + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + } + else + { + boolean isLast = isLastTabInRun(selectedIndex); + if (isOcean) + { + g.setColor(oceanSelectedBorder); + } + + int bottom = y + h - 1; + int right = x + w - 1; + if (isLast) + { + g.drawLine(x, bottom, rect.x, bottom); + } + else + { + g.drawLine(x, bottom, rect.x - 1, bottom); + } + + if (rect.x + rect.width < x + w - 2) + { + if (isLast) + { + g.drawLine(rect.x + rect.width - 1, bottom, right, bottom); + } + else + { + g.drawLine(rect.x + rect.width, bottom, right, bottom); + } + } + } + } + + /** + * Paints the left edge of the content border. + * + * @param g the graphics to use for painting + * @param tabPlacement the tab placement + * @param selectedIndex the index of the selected tab + * @param x the upper left coordinate of the content area + * @param y the upper left coordinate of the content area + * @param w the width of the content area + * @param h the height of the content area + */ + protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, + int selectedIndex, int x, int y, + int w, int h) + { + boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; + Color oceanSelectedBorder = + UIManager.getColor("TabbedPane.borderHightlightColor"); + Rectangle rect = selectedIndex < 0 ? null : + getTabBounds(selectedIndex, calcRect); + + if (isOcean) + { + g.setColor(oceanSelectedBorder); + } + else + { + g.setColor(selectHighlight); + } + + // If tabs are not placed on LEFT, or if the selected tab is not in the + // run directly left to the content or the selected tab is not visible, + // then we draw an unbroken line. + if (tabPlacement != LEFT || selectedIndex < 0 + || rect.x + rect.width + 1 < x || rect.y < y || rect.y > y + h) + { + g.drawLine(x, y + 1, x, y + h - 2); + if (isOcean && tabPlacement == LEFT) + { + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(x, y + 1, x, y + h - 2); + } + } + else + { + g.drawLine(x, y, x, rect.y + 1); + if (rect.y + rect.height < y + h - 2) + { + g.drawLine(x, rect.y + rect.height + 1, x, y + h + 2); + } + if (isOcean) + { + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(x + 1, y + 1, x + 1, rect.y + 1); + if (rect.y + rect.height < y + h - 2) + { + g.drawLine(x + y, rect.y + rect.height + 1, x + 1, y + h + 2); + } + } + } + + } + + /** + * Paints the right edge of the content border. + * + * @param g the graphics to use for painting + * @param tabPlacement the tab placement + * @param selectedIndex the index of the selected tab + * @param x the upper left coordinate of the content area + * @param y the upper left coordinate of the content area + * @param w the width of the content area + * @param h the height of the content area + */ + protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, + int selectedIndex, int x, int y, + int w, int h) + { + g.setColor(darkShadow); + Rectangle rect = selectedIndex < 0 ? null : + getTabBounds(selectedIndex, calcRect); + boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; + Color oceanSelectedBorder = + UIManager.getColor("TabbedPane.borderHightlightColor"); + + // If tabs are not placed on RIGHT, or if the selected tab is not in the + // run directly right to the content or the selected tab is not visible, + // then we draw an unbroken line. + if (tabPlacement != RIGHT || selectedIndex < 0 || rect.x - 1 > w + || rect.y < y || rect.y > y + h) + { + if (isOcean && tabPlacement == RIGHT) + { + g.setColor(oceanSelectedBorder); + } + g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + } + else + { + if (isOcean) + { + g.setColor(oceanSelectedBorder); + } + g.drawLine(x + w - 1, y, x + w - 1, rect.y); + + if (rect.y + rect.height < y + h - 2) + { + g.drawLine(x + w - 1, rect.y + rect.height, x + w - 1, y + h - 2); + } + } + } + + /** + * Determines if the specified tab is the last tab in its tab run. + * + * @param tabIndex the index of the tab + * + * @return if the specified tab is the last tab in its tab run + */ + private boolean isLastTabInRun(int tabIndex) + { + int count = tabPane.getTabCount(); + int run = getRunForTab(count, tabIndex); + int lastIndex = lastTabInRun(count, run); + return tabIndex == lastIndex; + } + + /** + * Returns the background for an unselected tab. This first asks the + * JTabbedPane for the background at the specified tab index, if this + * is an UIResource (that means, it is inherited from the JTabbedPane) + * and the TabbedPane.unselectedBackground UI property is not null, + * this returns the value of the TabbedPane.unselectedBackground property, + * otherwise the value returned by the JTabbedPane. + * + * @param tabIndex the index of the tab for which we query the background + * + * @return the background for an unselected tab + */ + private Color getUnselectedBackground(int tabIndex) + { + Color bg = tabPane.getBackgroundAt(tabIndex); + Color unselectedBackground = + UIManager.getColor("TabbedPane.unselectedBackground"); + if (bg instanceof UIResource && unselectedBackground != null) + bg = unselectedBackground; + return bg; + } } diff --git a/javax/swing/plaf/metal/MetalToggleButtonUI.java b/javax/swing/plaf/metal/MetalToggleButtonUI.java index b1ed33236..8c7a46e3a 100644 --- a/javax/swing/plaf/metal/MetalToggleButtonUI.java +++ b/javax/swing/plaf/metal/MetalToggleButtonUI.java @@ -45,12 +45,14 @@ import java.awt.Graphics; import java.awt.Rectangle; import javax.swing.AbstractButton; +import javax.swing.ButtonModel; import javax.swing.JComponent; import javax.swing.JToggleButton; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicToggleButtonUI; @@ -209,7 +211,12 @@ public class MetalToggleButtonUI */ public void update(Graphics g, JComponent c) { - if (c.isOpaque() && UIManager.get(getPropertyPrefix() + "gradient") != null) + AbstractButton b = (AbstractButton) c; + ButtonModel m = b.getModel(); + if (b.getBackground() instanceof UIResource + && b.isContentAreaFilled() + && b.isEnabled() && ! m.isArmed() && ! m.isPressed() + && UIManager.get(getPropertyPrefix() + "gradient") != null) { MetalUtils.paintGradient(g, 0, 0, c.getWidth(), c.getHeight(), SwingConstants.VERTICAL, diff --git a/javax/swing/plaf/metal/MetalToolBarUI.java b/javax/swing/plaf/metal/MetalToolBarUI.java index 797567d55..1848c1f16 100644 --- a/javax/swing/plaf/metal/MetalToolBarUI.java +++ b/javax/swing/plaf/metal/MetalToolBarUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Graphics; import java.awt.Point; import java.awt.event.ContainerListener; import java.awt.event.MouseEvent; @@ -46,6 +47,7 @@ import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JToolBar; +import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.MouseInputListener; @@ -259,4 +261,38 @@ public class MetalToolBarUI extends BasicToolBarUI } super.uninstallUI(c); } + + /** + * Paints the background of the component if necessary and then calls + * <code>paint(g, c)</code>. + * + * This is overridden to implement the OceanTheme gradient when an OceanTheme + * is installed. + * + * @param g the graphics to use + * @param c the component to paint. + * + * @since 1.5 + */ + public void update(Graphics g, JComponent c) + { + // TODO: Sun's implementation uses the MenuBar.gradient here. + // I would consider this a bug, but implement it like this + // for compatibility. + if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme + && UIManager.get("MenuBar.gradient") != null) + { + if (c.isOpaque()) + { + MetalUtils.paintGradient(g, 0, 0, c.getWidth(), c.getHeight(), + SwingConstants.VERTICAL, + "MenuBar.gradient"); + } + paint(g, c); + } + else + { + super.update(g, c); + } + } } diff --git a/javax/swing/plaf/metal/MetalTreeUI.java b/javax/swing/plaf/metal/MetalTreeUI.java index 24432a2b5..3ea37c82f 100644 --- a/javax/swing/plaf/metal/MetalTreeUI.java +++ b/javax/swing/plaf/metal/MetalTreeUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import gnu.classpath.NotImplementedException; + import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; @@ -133,6 +135,7 @@ public class MetalTreeUI extends BasicTreeUI * @param lineStyleFlag - String representation */ protected void decodeLineStyle(Object lineStyleFlag) + throws NotImplementedException { // FIXME: not implemented } @@ -176,6 +179,7 @@ public class MetalTreeUI extends BasicTreeUI * @param c - the current component to draw */ protected void paintHorizontalSeparators(Graphics g, JComponent c) + throws NotImplementedException { // FIXME: not implemented } diff --git a/javax/swing/plaf/metal/OceanTheme.java b/javax/swing/plaf/metal/OceanTheme.java index cfba4b5ff..9d76ff7e8 100644 --- a/javax/swing/plaf/metal/OceanTheme.java +++ b/javax/swing/plaf/metal/OceanTheme.java @@ -43,6 +43,7 @@ import java.util.Arrays; import javax.swing.UIDefaults; import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.BorderUIResource.LineBorderUIResource; /** * A modern theme for the Metal Look & Feel. @@ -264,6 +265,10 @@ public class OceanTheme extends DefaultMetalTheme defaults.put("ToolBar.borderColor", c3); defaults.put("Tree.selectionBorderColor", PRIMARY1); + // Borders. + defaults.put("Table.focusCellHighlightBorder", + new LineBorderUIResource(getPrimary1())); + // Insets. defaults.put("TabbedPane.contentBorderInsets", new Insets(4, 2, 3, 3)); defaults.put("TabbedPane.tabAreaInsets", new Insets(2, 2, 0, 6)); diff --git a/javax/swing/plaf/synth/ColorType.java b/javax/swing/plaf/synth/ColorType.java index 954e309e1..ced1efc02 100644 --- a/javax/swing/plaf/synth/ColorType.java +++ b/javax/swing/plaf/synth/ColorType.java @@ -78,7 +78,12 @@ public class ColorType /** * The maximum number of color types. */ - public static final int MAX_COUNT = 5; + public static final int MAX_COUNT; + static + { + // This is not a constant in the JDK. + MAX_COUNT = 5; + } /** * A counter used to assign an ID to the created color types. diff --git a/javax/swing/plaf/synth/SynthGraphicsUtils.java b/javax/swing/plaf/synth/SynthGraphicsUtils.java index a68b6f6ec..1907d754f 100644 --- a/javax/swing/plaf/synth/SynthGraphicsUtils.java +++ b/javax/swing/plaf/synth/SynthGraphicsUtils.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.synth; +import gnu.classpath.NotImplementedException; + import java.awt.Component; import java.awt.Dimension; import java.awt.Font; @@ -159,6 +161,7 @@ public class SynthGraphicsUtils Icon icon, int hAlign, int vAlign, int hTextPosition,int vTextPosition, int iconTextGap,int mnemonicIndex) + throws NotImplementedException { // FIXME: Implement this correctly. return new Dimension(0, 0); @@ -187,6 +190,7 @@ public class SynthGraphicsUtils Icon icon, int hAlign, int vAlign, int hTextPosition,int vTextPosition, int iconTextGap,int mnemonicIndex) + throws NotImplementedException { // FIXME: Implement this correctly. return new Dimension(0, 0); @@ -215,6 +219,7 @@ public class SynthGraphicsUtils Icon icon, int hAlign, int vAlign, int hTextPosition,int vTextPosition, int iconTextGap,int mnemonicIndex) + throws NotImplementedException { // FIXME: Implement this correctly. return new Dimension(0, 0); @@ -277,6 +282,7 @@ public class SynthGraphicsUtils int hAlign, int vAlign, int hTextPosition, int vTextPosition, int iconTextGap, int mnemonicIndex, int textOffset) + throws NotImplementedException { // FIXME: Implement this correctly. } diff --git a/javax/swing/plaf/synth/SynthLookAndFeel.java b/javax/swing/plaf/synth/SynthLookAndFeel.java index ed5a5b053..1a2489e7e 100644 --- a/javax/swing/plaf/synth/SynthLookAndFeel.java +++ b/javax/swing/plaf/synth/SynthLookAndFeel.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.synth; +import gnu.classpath.NotImplementedException; + import java.awt.Component; import java.io.InputStream; import java.text.ParseException; @@ -119,6 +121,7 @@ public class SynthLookAndFeel * @param c the componenent for which to update the style */ public static void updateStyles(Component c) + throws NotImplementedException { // FIXME: Implement this properly. } @@ -131,6 +134,7 @@ public class SynthLookAndFeel * @return the region for a given Swing component */ public static Region getRegion(JComponent c) + throws NotImplementedException { // FIXME: This can be implemented as soon as we have the component UI // classes in place, since this region will be matched via the UI classes. @@ -147,6 +151,7 @@ public class SynthLookAndFeel * component */ public static ComponentUI createUI(JComponent c) + throws NotImplementedException { // FIXME: This can be implemented as soon as we have the component UI // classes in place. @@ -157,6 +162,7 @@ public class SynthLookAndFeel * Initializes this look and feel. */ public void initialize() + throws NotImplementedException { super.initialize(); // TODO: Implement at least the following here: @@ -168,6 +174,7 @@ public class SynthLookAndFeel * Uninitializes the look and feel. */ public void uninitialize() + throws NotImplementedException { super.uninitialize(); // TODO: What to do here? @@ -179,6 +186,7 @@ public class SynthLookAndFeel * @return the UI defaults of this look and feel */ public UIDefaults getDefaults() + throws NotImplementedException { // FIXME: This is certainly wrong. The defaults should be fetched/merged // from the file from which the l&f is loaded. @@ -191,6 +199,7 @@ public class SynthLookAndFeel * @return FIXME */ public boolean shouldUpdateStyleOnAncestorChanged() + throws NotImplementedException { return false; } @@ -210,7 +219,7 @@ public class SynthLookAndFeel // FIXME: The signature in the JDK has a Class<?> here. Should be fixed as // soon as we switch to the generics branch. public void load(InputStream in, Class resourceBase) - throws ParseException, IllegalArgumentException + throws ParseException, IllegalArgumentException, NotImplementedException { // FIXME: Implement this correctly. } diff --git a/javax/swing/plaf/synth/SynthPainter.java b/javax/swing/plaf/synth/SynthPainter.java index 4d7f18ee4..fa1f6f572 100644 --- a/javax/swing/plaf/synth/SynthPainter.java +++ b/javax/swing/plaf/synth/SynthPainter.java @@ -1100,9 +1100,10 @@ public abstract class SynthPainter * @param y the Y coordinate of the area to paint * @param w the width of the area to paint * @param h the height of the area to paint + * @param orientation orientation of the scrollbar */ public void paintScrollBarThumbBackground(SynthContext ctx, Graphics g, int x, - int y, int w, int h) + int y, int w, int h, int orientation) { // Nothing to do here. } @@ -1117,9 +1118,10 @@ public abstract class SynthPainter * @param y the Y coordinate of the area to paint * @param w the width of the area to paint * @param h the height of the area to paint + * @param orientation orientation of the scrollbar */ public void paintScrollBarThumbBorder(SynthContext ctx, Graphics g, int x, - int y, int w, int h) + int y, int w, int h, int orientation) { // Nothing to do here. } @@ -1270,9 +1272,10 @@ public abstract class SynthPainter * @param y the Y coordinate of the area to paint * @param w the width of the area to paint * @param h the height of the area to paint + * @param orientation orientation of the slider */ public void paintSliderThumbBackground(SynthContext ctx, Graphics g, int x, - int y, int w, int h) + int y, int w, int h, int orientation) { // Nothing to do here. } @@ -1287,9 +1290,10 @@ public abstract class SynthPainter * @param y the Y coordinate of the area to paint * @param w the width of the area to paint * @param h the height of the area to paint + * @param orientation orientation of the slider */ public void paintSliderThumbBorder(SynthContext ctx, Graphics g, int x, - int y, int w, int h) + int y, int w, int h, int orientation) { // Nothing to do here. } @@ -1414,23 +1418,6 @@ public abstract class SynthPainter } /** - * Paints the border of a split pane's divider. - * - * @param ctx the synth context identifying the component and region for - * painting - * @param g the graphics context to use for painting - * @param x the X coordinate of the area to paint - * @param y the Y coordinate of the area to paint - * @param w the width of the area to paint - * @param h the height of the area to paint - */ - public void paintSplitPaneDividerBorder(SynthContext ctx, Graphics g, int x, - int y, int w, int h) - { - // Nothing to do here. - } - - /** * Paints the background of a tabbed pane. * * @param ctx the synth context identifying the component and region for @@ -1542,9 +1529,10 @@ public abstract class SynthPainter * @param y the Y coordinate of the area to paint * @param w the width of the area to paint * @param h the height of the area to paint + * @param index the index of the tab to paint */ public void paintTabbedPaneTabBackground(SynthContext ctx, Graphics g, int x, - int y, int w, int h) + int y, int w, int h, int index) { // Nothing to do here. } @@ -1559,9 +1547,10 @@ public abstract class SynthPainter * @param y the Y coordinate of the area to paint * @param w the width of the area to paint * @param h the height of the area to paint + * @param index the index of the tab to paint */ public void paintTabbedPaneTabBorder(SynthContext ctx, Graphics g, int x, - int y, int w, int h) + int y, int w, int h, int index) { // Nothing to do here. } diff --git a/javax/swing/plaf/synth/SynthStyle.java b/javax/swing/plaf/synth/SynthStyle.java index e0a8dbcc7..f5ab3df3b 100644 --- a/javax/swing/plaf/synth/SynthStyle.java +++ b/javax/swing/plaf/synth/SynthStyle.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.synth; +import gnu.classpath.NotImplementedException; + import java.awt.Color; import java.awt.Font; import java.awt.Insets; @@ -58,87 +60,144 @@ public abstract class SynthStyle * Creates a new <code>SynthStyle</code> object. */ public SynthStyle() + throws NotImplementedException { // FIXME: Implement this correctly. } public SynthGraphicsUtils getGraphicsUtils(SynthContext ctx) + throws NotImplementedException { // FIXME: Implement this correctly. return null; } public Color getColor(SynthContext ctx, ColorType type) + throws NotImplementedException { // FIXME: Implement this correctly. return null; } - public abstract Color getColorForState(SynthContext ctx, ColorType type); + protected abstract Color getColorForState(SynthContext ctx, ColorType type); public Font getFont(SynthContext ctx) + throws NotImplementedException { // FIXME: Implement this correctly. return null; } - public abstract Font getFontForState(SynthContext ctx); + protected abstract Font getFontForState(SynthContext ctx); - public Insets getInsets(SynthContext ctx) + public Insets getInsets(SynthContext ctx, Insets result) + throws NotImplementedException { // FIXME: Implement this correctly. return null; } - public SynthPainter getPainted(SynthContext ctx) + public SynthPainter getPainter(SynthContext ctx) + throws NotImplementedException { // FIXME: Implement this correctly. return null; } public boolean isOpaque(SynthContext ctx) + throws NotImplementedException { // FIXME: Implement this correctly. return true; } public Object get(SynthContext ctx, Object key) + throws NotImplementedException { // FIXME: Implement this correctly. return null; } public void installDefaults(SynthContext ctx) + throws NotImplementedException { // FIXME: Implement this correctly. } public void uninstallDefaults(SynthContext ctx) + throws NotImplementedException { // FIXME: Implement this correctly. } + /** + * A convenience method to fetch an integer property. + * If the property's value is a {@link Number}, then the + * integer value is returned. Otherwise, the default value + * is returned. + * @param ctx the context + * @param key the key to fetch + * @param defaultValue the default value + * @return the integer value of the property, or the default value + */ public int getInt(SynthContext ctx, Object key, int defaultValue) { - // FIXME: Implement this correctly. - return -1; + Object obj = get(ctx, key); + if (obj instanceof Number) + return ((Number) obj).intValue(); + return defaultValue; } - public boolean getBoolean(SynthContext ctx, Object key, boolean defaultValue) + /** + * A convenience method to fetch an integer property. + * If the property's value is a {@link Boolean}, then the + * value is returned. Otherwise, the default value + * is returned. + * @param ctx the context + * @param key the key to fetch + * @param defaultValue the default value + * @return the boolean value of the property, or the default value + */ + public boolean getBoolean(SynthContext ctx, Object key, + boolean defaultValue) { - // FIXME: Implement this correctly. - return false; + Object obj = get(ctx, key); + if (obj instanceof Boolean) + return ((Boolean) obj).booleanValue(); + return defaultValue; } + /** + * A convenience method to fetch an Icon-valued property. + * If the property's value is an {@link Icon}, then the + * value is returned. Otherwise, null is returned. + * @param ctx the context + * @param key the key to fetch + * @return the icon, or null + */ public Icon getIcon(SynthContext ctx, Object key) { - // FIXME: Implement this correctly. + Object obj = get(ctx, key); + if (key instanceof Icon) + return (Icon) obj; return null; } + /** + * A convenience method to fetch a String property. + * If the property's value is a {@link String}, then the + * value is returned. Otherwise, the default value + * is returned. + * @param ctx the context + * @param key the key to fetch + * @param defaultValue the default value + * @return the String value of the property, or the default value + */ public String getString(SynthContext ctx, Object key, String defaultValue) { - // FIXME: Implement this correctly. - return null; + Object obj = get(ctx, key); + if (obj instanceof String) + return (String) obj; + return defaultValue; } } |