This should fix some remaining issues with Swing's TEXT_LAYOUT_CACHE for
buttons and menu items. Basically we need to initially trigger an update
for the cache, because the font gets installed _before_ the listeners,
and we should cleanup the cache on UI uninstall. Note that this patch
contains some unrelated cleanup, because that's been lying on my HD for
some time.
2006-09-27 Roman Kennke <[EMAIL PROTECTED]>
PR 29036
PR 29161
* javax/swing/plaf/basic/BasicButtonUI.java
(cachedInsets): New field.
(installListeners): Fire synthetic property change to initialize
TEXT_LAYOUT_CACHE for the button because the font has been
installed before.
(uninstallUI): Clear the TEXT_LAYOUT_CACHE for the button.
(paint): Use cached insets.
(paintText): Let new method forward to old one, not vice versa.
* javax/swing/plaf/basic/BasicMenuItemUI.java
(defaultAcceleratorLabelGap): Removed unused field.
(MenuGap): Removed unused field.
(propertyChangeListener): Made private.
(getAcceleratorRect): Removed unused method.
(getAcceleratorText): Removed unused method.
(getPath): Removed unnecessary cast.
(installListeners): Fire synthetic property change to initialize
TEXT_LAYOUT_CACHE for the button because the font has been
installed before.
(uninstallUI): Clear the TEXT_LAYOUT_CACHE for the button.
(layoutMenuItem): Removed unused statements.
/Roman
Index: javax/swing/plaf/basic/BasicButtonUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicButtonUI.java,v
retrieving revision 1.42
diff -u -1 -5 -r1.42 BasicButtonUI.java
--- javax/swing/plaf/basic/BasicButtonUI.java 22 Aug 2006 11:23:58 -0000 1.42
+++ javax/swing/plaf/basic/BasicButtonUI.java 27 Sep 2006 19:11:41 -0000
@@ -32,30 +32,31 @@
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package javax.swing.plaf.basic;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ButtonUI;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.text.View;
@@ -71,30 +72,35 @@
static Rectangle viewR = new Rectangle();
/**
* Cached rectangle for layouting the label. Used in paint() and
* BasicGraphicsUtils.getPreferredButtonSize().
*/
static Rectangle iconR = new Rectangle();
/**
* Cached rectangle for layouting the label. Used in paint() and
* BasicGraphicsUtils.getPreferredButtonSize().
*/
static Rectangle textR = new Rectangle();
/**
+ * Cached Insets instance, used in paint().
+ */
+ static Insets cachedInsets;
+
+ /**
* The shared button UI.
*/
private static BasicButtonUI sharedUI;
/**
* The shared BasicButtonListener.
*/
private static BasicButtonListener sharedListener;
/**
* A constant used to pad out elements in the button's layout and
* preferred size calculations.
*/
protected int defaultTextIconGap = 4;
@@ -245,30 +251,34 @@
* Installs listeners for the button.
*
* @param b the button (<code>null</code> not permitted).
*/
protected void installListeners(AbstractButton b)
{
BasicButtonListener listener = createButtonListener(b);
if (listener != null)
{
b.addChangeListener(listener);
b.addPropertyChangeListener(listener);
b.addFocusListener(listener);
b.addMouseListener(listener);
b.addMouseMotionListener(listener);
}
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ listener.propertyChange(new PropertyChangeEvent(b, "font", null,
+ b.getFont()));
}
/**
* Uninstalls listeners for the button.
*
* @param b the button (<code>null</code> not permitted).
*/
protected void uninstallListeners(AbstractButton b)
{
BasicButtonListener listener = getButtonListener(b);
if (listener != null)
{
b.removeChangeListener(listener);
b.removePropertyChangeListener(listener);
b.removeFocusListener(listener);
@@ -317,30 +327,31 @@
/**
* Uninstalls the UI from the component.
*
* @param c the component from which to uninstall the UI
*/
public void uninstallUI(JComponent c)
{
if (c instanceof AbstractButton)
{
AbstractButton b = (AbstractButton) c;
uninstallKeyboardActions(b);
uninstallListeners(b);
uninstallDefaults(b);
BasicHTML.updateRenderer(b, "");
+ b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
}
}
/**
* Calculates the minimum size for the specified component.
*
* @param c the component for which to compute the minimum size
*
* @return the minimum size for the specified component
*/
public Dimension getMinimumSize(JComponent c)
{
Dimension size = getPreferredSize(c);
// When the HTML view has a minimum width different from the preferred
// width, then substract this here accordingly. The height is not
@@ -421,31 +432,31 @@
return i;
}
/**
* Paint the component, which is an [EMAIL PROTECTED] AbstractButton}, according to
* its current state.
*
* @param g The graphics context to paint with
* @param c The component to paint the state of
*/
public void paint(Graphics g, JComponent c)
{
AbstractButton b = (AbstractButton) c;
- Insets i = c.getInsets();
+ Insets i = c.getInsets(cachedInsets);
viewR.x = i.left;
viewR.y = i.top;
viewR.width = c.getWidth() - i.left - i.right;
viewR.height = c.getHeight() - i.top - i.bottom;
textR.x = 0;
textR.y = 0;
textR.width = 0;
textR.height = 0;
iconR.x = 0;
iconR.y = 0;
iconR.width = 0;
iconR.height = 0;
Font f = c.getFont();
g.setFont(f);
@@ -546,65 +557,66 @@
g.fillRect(area.x, area.y, area.width, area.height);
}
}
/**
* Paints the "text" property of an [EMAIL PROTECTED] AbstractButton}.
*
* @param g The graphics context to paint with
* @param c The component to paint the state of
* @param textRect The area in which to paint the text
* @param text The text to paint
*/
protected void paintText(Graphics g, JComponent c, Rectangle textRect,
String text)
{
- paintText(g, (AbstractButton) c, textRect, text);
- }
-
- /**
- * Paints the "text" property of an [EMAIL PROTECTED] AbstractButton}.
- *
- * @param g The graphics context to paint with
- * @param b The button to paint the state of
- * @param textRect The area in which to paint the text
- * @param text The text to paint
- *
- * @since 1.4
- */
- protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
- String text)
- {
+ AbstractButton b = (AbstractButton) c;
Font f = b.getFont();
g.setFont(f);
FontMetrics fm = g.getFontMetrics(f);
if (b.isEnabled())
{
g.setColor(b.getForeground());
// FIXME: Underline mnemonic.
BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x,
textRect.y + fm.getAscent());
}
else
{
String prefix = getPropertyPrefix();
g.setColor(UIManager.getColor(prefix + "disabledText"));
// FIXME: Underline mnemonic.
BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x,
textRect.y + fm.getAscent());
}
+ }
+
+ /**
+ * Paints the "text" property of an [EMAIL PROTECTED] AbstractButton}.
+ *
+ * @param g The graphics context to paint with
+ * @param b The button to paint the state of
+ * @param textRect The area in which to paint the text
+ * @param text The text to paint
+ *
+ * @since 1.4
+ */
+ protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
+ String text)
+ {
+ paintText(g, (JComponent) b, textRect, text);
}
/**
* A helper method that finds the BasicButtonListener for the specified
* button. This is there because this UI class is stateless and
* shared for all buttons, and thus can't store the listener
* as instance field. (We store our shared instance in sharedListener,
* however, subclasses may override createButtonListener() and we would
* be lost in this case).
*
* @param b the button
*
* @return the UI event listener
*/
private BasicButtonListener getButtonListener(AbstractButton b)
Index: javax/swing/plaf/basic/BasicMenuItemUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java,v
retrieving revision 1.51
diff -u -1 -5 -r1.51 BasicMenuItemUI.java
--- javax/swing/plaf/basic/BasicMenuItemUI.java 17 Aug 2006 14:45:46 -0000 1.51
+++ javax/swing/plaf/basic/BasicMenuItemUI.java 27 Sep 2006 19:11:41 -0000
@@ -168,41 +168,33 @@
* Color of the text that is used when menu item is selected.
*/
protected Color selectionForeground;
/**
* String that separates description of the modifiers and the key
*/
private String acceleratorDelimiter;
/**
* ItemListener to listen for item changes in the menu item
*/
private ItemListener itemListener;
/**
- * Number of spaces between accelerator and menu item's label.
+ * A PropertyChangeListener to make UI updates after property changes.
*/
- private int defaultAcceleratorLabelGap = 10;
-
- /**
- * The gap between different menus on the MenuBar.
- */
- private int MenuGap = 10;
-
- /** A PropertyChangeListener to make UI updates after property changes **/
- PropertyChangeHandler propertyChangeListener;
+ private 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;
@@ -250,31 +242,30 @@
JComponent.WHEN_IN_FOCUSED_WINDOW);
if (map != null)
map.remove((KeyStroke) e.getOldValue());
else
map = new ComponentInputMapUIResource(menuItem);
KeyStroke accelerator = (KeyStroke) e.getNewValue();
if (accelerator != null)
map.put(accelerator, "doClick");
}
// TextLayout caching for speed-up drawing of text.
else if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)
|| property.equals("font"))
&& SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
== null)
-
{
AbstractButton b = (AbstractButton) e.getSource();
String text = b.getText();
if (text == null)
text = "";
FontRenderContext frc = new FontRenderContext(new AffineTransform(),
false, false);
TextLayout layout = new TextLayout(text, b.getFont(), frc);
b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout);
}
}
}
/**
* A class to handle accelerator keys. This is the Action we will
@@ -402,31 +393,31 @@
}
/**
* Returns path to this menu item.
*
* @return $MenuElement[]$ Returns array of menu elements that constitute a
* path to this menu item.
*/
public MenuElement[] getPath()
{
ArrayList path = new ArrayList();
Component c = menuItem;
while (c instanceof MenuElement)
{
- path.add(0, (MenuElement) c);
+ path.add(0, c);
if (c instanceof JPopupMenu)
c = ((JPopupMenu) c).getInvoker();
else
c = c.getParent();
}
MenuElement[] pathArray = new MenuElement[path.size()];
path.toArray(pathArray);
return pathArray;
}
/**
* Returns preferred size for the given menu item.
*
@@ -591,30 +582,35 @@
UIActionMap.put("doClick", new ClickAction());
SwingUtilities.replaceUIActionMap(menuItem, UIActionMap);
}
/**
* This method installs the listeners for the [EMAIL PROTECTED] JMenuItem}.
*/
protected void installListeners()
{
menuItem.addMouseListener(mouseInputListener);
menuItem.addMouseMotionListener(mouseInputListener);
menuItem.addMenuDragMouseListener(menuDragMouseListener);
menuItem.addMenuKeyListener(menuKeyListener);
menuItem.addItemListener(itemListener);
menuItem.addPropertyChangeListener(propertyChangeListener);
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ propertyChangeListener.propertyChange(new PropertyChangeEvent(menuItem,
+ "font", null,
+ menuItem.getFont()));
}
/**
* Installs and initializes all fields for this UI delegate. Any properties of
* the UI that need to be initialized and/or set to defaults will be done now.
* It will also install any listeners necessary.
*
* @param c
* The [EMAIL PROTECTED] JComponent} that is having this UI installed.
*/
public void installUI(JComponent c)
{
super.installUI(c);
menuItem = (JMenuItem) c;
installDefaults();
@@ -923,88 +919,48 @@
}
/**
* Performs the opposite of installUI. Any properties or resources that need
* to be cleaned up will be done now. It will also uninstall any listeners it
* has. In addition, any properties of this UI will be nulled.
*
* @param c
* The [EMAIL PROTECTED] JComponent} that is having this UI uninstalled.
*/
public void uninstallUI(JComponent c)
{
uninstallListeners();
uninstallDefaults();
uninstallComponents(menuItem);
+ c.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
menuItem = null;
}
/**
* This method calls paint.
*
* @param g
* The graphics context used to paint this menu item
* @param c
* The menu item to paint
*/
public void update(Graphics g, JComponent c)
{
paint(g, c);
}
/**
- * Return text representation of the specified accelerator
- *
- * @param accelerator
- * Accelerator for which to return string representation
- * @return $String$ Text representation of the given accelerator
- */
- private String getAcceleratorText(KeyStroke accelerator)
- {
- // convert keystroke into string format
- String modifiersText = "";
- int modifiers = accelerator.getModifiers();
- char keyChar = accelerator.getKeyChar();
- int keyCode = accelerator.getKeyCode();
-
- if (modifiers != 0)
- modifiersText = KeyEvent.getKeyModifiersText(modifiers)
- + acceleratorDelimiter;
-
- if (keyCode == KeyEvent.VK_UNDEFINED)
- return modifiersText + keyChar;
- else
- return modifiersText + KeyEvent.getKeyText(keyCode);
- }
-
- /**
- * Calculates and return rectange in which accelerator should be displayed
- *
- * @param accelerator
- * accelerator for which to return the display rectangle
- * @param fm
- * The font metrics used to measure the text
- * @return $Rectangle$ reactangle which will be used to display accelerator
- */
- private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm)
- {
- int width = fm.stringWidth(getAcceleratorText(accelerator));
- int height = fm.getHeight();
- return new Rectangle(0, 0, width, height);
- }
-
- /**
* 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.
*/
protected class MouseInputHandler implements MouseInputListener
{
/**
* Creates a new MouseInputHandler object.
*/
protected MouseInputHandler()
{
// Nothing to do here.
}
/**
@@ -1297,33 +1253,30 @@
viewRect.setBounds(insets.left, insets.top,
i.getWidth() - insets.left - insets.right,
i.getHeight() - insets.top - insets.bottom);
}
}
/**
* 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();
-
// 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.