-
Bug
-
Resolution: Fixed
-
P4
-
1.4.0
-
beta
-
x86
-
windows_nt
Name: jk109818 Date: 04/24/2002
FULL PRODUCT VERSION :
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)
FULL OPERATING SYSTEM VERSION :
Microsoft Windows NT
4.00.1381
ServicePack: 5
A DESCRIPTION OF THE PROBLEM :
23-apr-2002
Painting of MotifRadioButton (and other buttons) is faulty.
Due to several bugs, a RadioButton is drawn incorrectly.
One bug, 4126679, reported already that the insets were
not taken into account when drawing in BasicRadioButtonUI.
Other problems are:
o in MotifIconFactory.RadioButtonIcon the start position
for drawing the icon is shifted right by 2.
This seems to be just a hack to fix the problem
really caused by not taking the insets into account
when painting, see above.
(( Same in CheckBoxIcon; others? ))
o MotifIconFactory.RadioButtonIcon says it is 13 wide and 13
high.
In its paint method lines are drawn from coordinates x+0
through x+13, and y+0 through y+13. Thus the icon is really
14 wide and 14 high.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
o split the enclosed source into files:
MyMetalLAF.java
MyMetalRadioButtonUI.java
MyMotifIconFactory.java
MyMotifLAF.java
MyMotifRadioButtonUI.java
MyRadioButton.java
MyWindowsLAF.java
MyWindowsRadioButtonUI.java
T01.java
o compile the files into a directory
o start T01 using one of:
<java-invocation> -Dlaf=windows T01
<java-invocation> -Dlaf=metal T01
<java-invocation> -Dlaf=motif T01
to have the program use one of its mini-lafs: MyWindowsLAF,
MyMetalLAF
and MyMotifLAF respectively
The program displays three labels and three RadioButtons.
The left RadioButton is an original swing JRadioButton.
The middle RadioButton is a MyRadioButton, behaving just
like the
standard JRadioButton, except that it paints its areas computed
by the paint method in its UI:
light blue : background of widgets
yellow : viewR: union of iconR and textR
light green: iconR: the rectangle into which the icon should
be drawn
light red : textR: the rectangle into which text should be drawn
The right RadioButton is a MyRadioButton, using fixes for
the problems,
except the one that the Motif icon reports a wrong size. It also
paints its areas like the middle button.
Methods were copied from jdk 1.4.0 sources as mentioned in
the sources.
Changes are marked:
//x : line is commented out
//{ : start change
//} : end change
To localize fixes, search for "FIXED".
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
===== MyMetalLAF.java
package myMetalLAF;
import javax.swing.*;
import javax.swing.plaf.metal.*;
public class MyMetalLAF
extends MetalLookAndFeel
{
public String getName() {
return "MyMetalLAF";
}
public String getID() {
return "MyMetalLAF";
}
public String getDescription() {
return "MY METAL LOOK AND FEEL";
}
public boolean isSupportedLookAndFeel() {
return true;
}
protected void initClassDefaults(UIDefaults table) {
super.initClassDefaults(table);
table.putDefaults(new Object[] {
"MyRadioButtonUI", "myMetalLAF.MyMetalRadioButtonUI",
});
}
}
===== MyMetalRadioButtonUI.java
package myMetalLAF;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.View;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.metal.*;
import javax.swing.plaf.basic.BasicGraphicsUtils;
public class MyMetalRadioButtonUI
extends MetalRadioButtonUI
{
public static ComponentUI createUI(JComponent c) {
return new MyMetalRadioButtonUI();
}
public synchronized void paint(Graphics g, JComponent c)
{
System.out.println("paint: called");
_paint(g, c);
}
// from: MetalRadioButtonUI.java
public synchronized void _paint(Graphics g, JComponent c) {
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
Dimension size = c.getSize();
int w = size.width;
int h = size.height;
Font f = c.getFont();
g.setFont(f);
FontMetrics fm = g.getFontMetrics();
//x Rectangle viewRect = new Rectangle(size);
//{
Rectangle viewRect;
if (c.getClientProperty("FIXED") != null) {
viewRect = new Rectangle();
Insets prefInsets = b.getInsets();
viewRect.x = prefInsets.left;
viewRect.y = prefInsets.top;
viewRect.width = size.width-prefInsets.left-prefInsets.right;
viewRect.height = size.height-prefInsets.top-prefInsets.bottom;
} else {
viewRect = new Rectangle(size);
}
//}
Rectangle iconRect = new Rectangle();
Rectangle textRect = new Rectangle();
Icon altIcon = b.getIcon();
Icon selectedIcon = null;
Icon disabledIcon = null;
String text = SwingUtilities.layoutCompoundLabel(
c, fm, b.getText(), altIcon != null ? altIcon : getDefaultIcon(),
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewRect, iconRect, textRect, b.getIconTextGap());
// fill background
if(c.isOpaque()) {
//x g.setColor(b.getBackground());
//{
g.setColor(new Color(127, 127, 255));
//}
g.fillRect(0,0, size.width, size.height);
}
//{
g.setColor(Color.yellow);
g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
g.setColor(new Color(127,255,127));
g.fillRect(iconRect.x, iconRect.y, iconRect.width, iconRect.height);
g.setColor(new Color(255,127,127));
g.fillRect(textRect.x, textRect.y, textRect.width, textRect.height);
//}
// Paint the radio button
if(altIcon != null) {
if(!model.isEnabled()) {
if(model.isSelected()) {
altIcon = b.getDisabledSelectedIcon();
} else {
altIcon = b.getDisabledIcon();
}
} else if(model.isPressed() && model.isArmed()) {
altIcon = b.getPressedIcon();
if(altIcon == null) {
// Use selected icon
altIcon = b.getSelectedIcon();
}
} else if(model.isSelected()) {
if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverSelectedIcon();
if (altIcon == null) {
altIcon = (Icon) b.getSelectedIcon();
}
} else {
altIcon = (Icon) b.getSelectedIcon();
}
} else if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverIcon();
}
if(altIcon == null) {
altIcon = b.getIcon();
}
altIcon.paintIcon(c, g, iconRect.x, iconRect.y);
} else {
getDefaultIcon().paintIcon(c, g, iconRect.x, iconRect.y);
}
// Draw the Text
if(text != null) {
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
v.paint(g, textRect);
} else {
int mnemIndex = b.getDisplayedMnemonicIndex();
if(model.isEnabled()) {
// *** paint the text normally
g.setColor(b.getForeground());
BasicGraphicsUtils.drawStringUnderlineCharAt(g,text,
mnemIndex, textRect.x, textRect.y + fm.getAscent());
} else {
// *** paint the text disabled
g.setColor(getDisabledTextColor());
BasicGraphicsUtils.drawStringUnderlineCharAt(g,text,
mnemIndex, textRect.x, textRect.y + fm.getAscent());
}
if(b.hasFocus() && b.isFocusPainted() &&
textRect.width > 0 && textRect.height > 0 ) {
paintFocus(g,textRect,size);
}
}
}
}
protected void paintFocus(Graphics g, Rectangle t, Dimension d){
g.setColor(getFocusColor());
g.drawRect(t.x-1, t.y-1, t.width+1, t.height+1);
}
}
===== MyMotifIconFactory.java
package myMotifLAF;
import java.awt.*;
import java.io.Serializable;
import javax.swing.*;
import javax.swing.plaf.UIResource;
import com.sun.java.swing.plaf.motif.*;
public class MyMotifIconFactory
{
private static Icon radioButtonIcon;
public static Icon getRadioButtonIcon() {
if (radioButtonIcon == null) {
radioButtonIcon = new RadioButtonIcon();
}
return radioButtonIcon;
}
// from: MotifIconFactory
private static class RadioButtonIcon implements Icon, UIResource, Serializable {
private Color dot = UIManager.getColor("activeCaptionBorder");
private Color highlight = UIManager.getColor("controlHighlight");
private Color shadow = UIManager.getColor("controlShadow");
public void paintIcon(Component c, Graphics g, int x, int y) {
// fill interior
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
int w = getIconWidth();
int h = getIconHeight();
// add pad so focus isn't smudged on the x
if (b instanceof JComponent && ((JComponent)b).getClientProperty("FIXED") != null) {
} else {
//x x += (MotifGraphicsUtils.isLeftToRight(c))? 2 : -3;
x += 2;
}
boolean isPressed = model.isPressed();
boolean isArmed = model.isArmed();
boolean isEnabled = model.isEnabled();
boolean isSelected = model.isSelected();
boolean checkIn = ((isPressed &&
!isArmed &&
isSelected) ||
(isPressed &&
isArmed &&
!isSelected)
||
(!isPressed &&
isArmed &&
isSelected ||
(!isPressed &&
!isArmed &&
isSelected)));
if (checkIn){
g.setColor(shadow);
g.drawLine(x+5,y+0,x+8,y+0);
g.drawLine(x+3,y+1,x+4,y+1);
g.drawLine(x+9,y+1,x+9,y+1);
g.drawLine(x+2,y+2,x+2,y+2);
g.drawLine(x+1,y+3,x+1,y+3);
g.drawLine(x,y+4,x,y+9);
g.drawLine(x+1,y+10,x+1,y+10);
g.drawLine(x+2,y+11,x+2,y+11);
g.setColor(highlight);
g.drawLine(x+3,y+12,x+4,y+12);
g.drawLine(x+5,y+13,x+8,y+13);
g.drawLine(x+9,y+12,x+10,y+12);
g.drawLine(x+11,y+11,x+11,y+11);
g.drawLine(x+12,y+10,x+12,y+10);
g.drawLine(x+13,y+9,x+13,y+4);
g.drawLine(x+12,y+3,x+12,y+3);
g.drawLine(x+11,y+2,x+11,y+2);
g.drawLine(x+10,y+1,x+10,y+1);
g.setColor(dot);
g.fillRect(x+4,y+5,6,4);
g.drawLine(x+5,y+4,x+8,y+4);
g.drawLine(x+5,y+9,x+8,y+9);
}
else {
g.setColor(highlight);
g.drawLine(x+5,y+0,x+8,y+0);
g.drawLine(x+3,y+1,x+4,y+1);
g.drawLine(x+9,y+1,x+9,y+1);
g.drawLine(x+2,y+2,x+2,y+2);
g.drawLine(x+1,y+3,x+1,y+3);
g.drawLine(x,y+4,x,y+9);
g.drawLine(x+1,y+10,x+1,y+10);
g.drawLine(x+2,y+11,x+2,y+11);
g.setColor(shadow);
g.drawLine(x+3,y+12,x+4,y+12);
g.drawLine(x+5,y+13,x+8,y+13);
g.drawLine(x+9,y+12,x+10,y+12);
g.drawLine(x+11,y+11,x+11,y+11);
g.drawLine(x+12,y+10,x+12,y+10);
g.drawLine(x+13,y+9,x+13,y+4);
g.drawLine(x+12,y+3,x+12,y+3);
g.drawLine(x+11,y+2,x+11,y+2);
g.drawLine(x+10,y+1,x+10,y+1);
}
}
public int getIconWidth() {
return 13;
}
public int getIconHeight() {
return 13;
}
} // end class RadioButtonIcon
}
===== MyMotifLAF.java
package myMotifLAF;
import javax.swing.*;
import com.sun.java.swing.plaf.motif.*;
public class MyMotifLAF
extends MotifLookAndFeel
{
public String getName() {
return "MyMotifLAF";
}
public String getID() {
return "MyMotifLAF";
}
public String getDescription() {
return "MY MOTIF LOOK AND FEEL";
}
public boolean isSupportedLookAndFeel() {
return true;
}
protected void initClassDefaults(UIDefaults table) {
super.initClassDefaults(table);
table.putDefaults(new Object[] {
"MyRadioButtonUI", "myMotifLAF.MyMotifRadioButtonUI",
});
}
protected void initComponentDefaults(UIDefaults table) {
super.initComponentDefaults(table);
UIManager.put("RadioButton.icon", new UIDefaults.LazyValue() {
public Object createValue(UIDefaults table) {
return MyMotifIconFactory.getRadioButtonIcon();
}
});
}
}
===== MyMotifRadioButtonUI.java
package myMotifLAF;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.View;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import com.sun.java.swing.plaf.motif.*;
public class MyMotifRadioButtonUI
extends MotifRadioButtonUI
{
public static ComponentUI createUI(JComponent c) {
return new MyMotifRadioButtonUI();
}
public synchronized void paint(Graphics g, JComponent c)
{
System.out.println("paint: called");
_paint(g, c);
}
private static Dimension size = new Dimension();
private static Rectangle viewRect = new Rectangle();
private static Rectangle iconRect = new Rectangle();
private static Rectangle textRect = new Rectangle();
// from BasicRadioButtonUI.java
public synchronized void _paint(Graphics g, JComponent c) {
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
Font f = c.getFont();
g.setFont(f);
FontMetrics fm = g.getFontMetrics();
size = b.getSize(size);
//x viewRect.x = viewRect.y = 0;
//x viewRect.width = size.width;
//x viewRect.height = size.height;
//{
//
prefInsets = b.getInsets(prefInsets);
if (c.getClientProperty("FIXED") != null) {
Insets prefInsets = b.getInsets();
viewRect.x = prefInsets.left;
viewRect.y = prefInsets.top;
viewRect.width = size.width-prefInsets.left-prefInsets.right;
viewRect.height = size.height-prefInsets.top-prefInsets.bottom;
} else {
viewRect.x = viewRect.y = 0;
viewRect.width = size.width;
viewRect.height = size.height;
}
//}
iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
textRect.x = textRect.y = textRect.width = textRect.height = 0;
Icon altIcon = b.getIcon();
Icon selectedIcon = null;
Icon disabledIcon = null;
String text = SwingUtilities.layoutCompoundLabel(
c, fm, b.getText(), altIcon != null ? altIcon : getDefaultIcon(),
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewRect, iconRect, textRect,
b.getText() == null ? 0 : b.getIconTextGap());
// fill background
if(c.isOpaque()) {
//x g.setColor(b.getBackground());
//{
g.setColor(new Color(127, 127, 255));
//}
g.fillRect(0,0, size.width, size.height);
}
//{
g.setColor(Color.yellow);
g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
g.setColor(new Color(127,255,127));
g.fillRect(iconRect.x, iconRect.y, iconRect.width, iconRect.height);
g.setColor(new Color(255,127,127));
g.fillR
###@###.### 10/13/04 17:53 GMT
ect(textRect.x, textRect.y, textRect.width, textRect.height);
//}
// Paint the radio button
if(altIcon != null) {
if(!model.isEnabled()) {
if(model.isSelected()) {
altIcon = b.getDisabledSelectedIcon();
} else {
altIcon = b.getDisabledIcon();
}
} else if(model.isPressed() && model.isArmed()) {
altIcon = b.getPressedIcon();
if(altIcon == null) {
// Use selected icon
altIcon = b.getSelectedIcon();
}
} else if(model.isSelected()) {
if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverSelectedIcon();
if (altIcon == null) {
altIcon = (Icon) b.getSelectedIcon();
}
} else {
altIcon = (Icon) b.getSelectedIcon();
}
} else if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverIcon();
}
if(altIcon == null) {
altIcon = b.getIcon();
}
altIcon.paintIcon(c, g, iconRect.x, iconRect.y);
} else {
getDefaultIcon().paintIcon(c, g, iconRect.x, iconRect.y);
}
// Draw the Text
if(text != null) {
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
v.paint(g, textRect);
} else {
paintText(g, b, textRect, text);
if(b.hasFocus() && b.isFocusPainted() &&
textRect.width > 0 && textRect.height > 0 ) {
paintFocus(g, textRect, size);
}
}
}
}
// from MotifRadioButtonUI.java:
protected void paintFocus(Graphics g, Rectangle t, Dimension d){
g.setColor(getFocusColor());
g.drawRect(0,0,d.width-1,d.height-1);
}
}
===== MyRadioButton.java
import javax.swing.JRadioButton;
public class MyRadioButton
extends JRadioButton
{
public String getUIClassID() {
return "MyRadioButtonUI";
}
public MyRadioButton(String text)
{
super(text);
}
}
===== MyWindowsLAF.java
package myWindowsLAF;
import javax.swing.*;
import com.sun.java.swing.plaf.windows.*;
public class MyWindowsLAF
extends WindowsLookAndFeel
{
public String getName() {
return "MyWindowsLAF";
}
public String getID() {
return "MyWindowsLAF";
}
public String getDescription() {
return "MY WINDOWS LOOK AND FEEL";
}
public boolean isSupportedLookAndFeel() {
return true;
}
protected void initClassDefaults(UIDefaults table) {
super.initClassDefaults(table);
table.putDefaults(new Object[] {
"MyRadioButtonUI", "myWindowsLAF.MyWindowsRadioButtonUI",
});
}
}
===== MyWindowsRadioButtonUI.java
package myWindowsLAF;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.View;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import com.sun.java.swing.plaf.windows.*;
import javax.swing.plaf.basic.BasicGraphicsUtils;
public class MyWindowsRadioButtonUI
extends WindowsRadioButtonUI
{
public static ComponentUI createUI(JComponent c) {
return new MyWindowsRadioButtonUI();
}
public synchronized void paint(Graphics g, JComponent c)
{
System.out.println("paint: called");
_paint(g, c);
}
private static Dimension size = new Dimension();
private static Rectangle viewRect = new Rectangle();
private static Rectangle iconRect = new Rectangle();
private static Rectangle textRect = new Rectangle();
// von: BasicRadioButtonUI.java
public synchronized void _paint(Graphics g, JComponent c) {
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
Font f = c.getFont();
g.setFont(f);
FontMetrics fm = g.getFontMetrics();
size = b.getSize(size);
//x viewRect.x = viewRect.y = 0;
//x viewRect.width = size.width;
//x viewRect.height = size.height;
//{
//
prefInsets = b.getInsets(prefInsets);
if (c.getClientProperty("FIXED") != null) {
Insets prefInsets = b.getInsets();
viewRect.x = prefInsets.left;
viewRect.y = prefInsets.top;
viewRect.width = size.width-prefInsets.left-prefInsets.right;
viewRect.height = size.height-prefInsets.top-prefInsets.bottom;
} else {
viewRect.x = viewRect.y = 0;
viewRect.width = size.width;
viewRect.height = size.height;
}
//}
iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
textRect.x = textRect.y = textRect.width = textRect.height = 0;
Icon altIcon = b.getIcon();
Icon selectedIcon = null;
Icon disabledIcon = null;
String text = SwingUtilities.layoutCompoundLabel(
c, fm, b.getText(), altIcon != null ? altIcon : getDefaultIcon(),
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewRect, iconRect, textRect,
b.getText() == null ? 0 : b.getIconTextGap());
// fill background
if(c.isOpaque()) {
//x g.setColor(b.getBackground());
//{
g.setColor(new Color(127, 127, 255));
//}
g.fillRect(0,0, size.width, size.height);
}
//{
g.setColor(Color.yellow);
g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
g.setColor(new Color(127,255,127));
g.fillRect(iconRect.x, iconRect.y, iconRect.width, iconRect.height);
g.setColor(new Color(255,127,127));
g.fillRect(textRect.x, textRect.y, textRect.width, textRect.height);
//}
// Paint the radio button
if(altIcon != null) {
if(!model.isEnabled()) {
if(model.isSelected()) {
altIcon = b.getDisabledSelectedIcon();
} else {
altIcon = b.getDisabledIcon();
}
} else if(model.isPressed() && model.isArmed()) {
altIcon = b.getPressedIcon();
if(altIcon == null) {
// Use selected icon
altIcon = b.getSelectedIcon();
}
} else if(model.isSelected()) {
if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverSelectedIcon();
if (altIcon == null) {
altIcon = (Icon) b.getSelectedIcon();
}
} else {
altIcon = (Icon) b.getSelectedIcon();
}
} else if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverIcon();
}
if(altIcon == null) {
altIcon = b.getIcon();
}
altIcon.paintIcon(c, g, iconRect.x, iconRect.y);
} else {
getDefaultIcon().paintIcon(c, g, iconRect.x, iconRect.y);
}
// Draw the Text
if(text != null) {
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
v.paint(g, textRect);
} else {
paintText(g, b, textRect, text);
if(b.hasFocus() && b.isFocusPainted() &&
textRect.width > 0 && textRect.height > 0 ) {
paintFocus(g, textRect, size);
}
}
}
}
// from: WindowsRadioButtonUI
protected void paintFocus(Graphics g, Rectangle textRect, Dimension d){
g.setColor(getFocusColor());
BasicGraphicsUtils.drawDashedRect(g, textRect.x, textRect.y, textRect.width,
textRect.height);
}
}
===== T01.java
import java.awt.*;
import javax.swing.*;
public class T01
extends JFrame
{
public static void main(String[] av)
{
// usage:
// -Dlaf=windows|metal|motif
String lafID = System.getProperties().getProperty("laf", "metal");
System.out.println("lafID="+lafID);
try {
if ("metal".equals(lafID))
javax.swing.UIManager.setLookAndFeel("myMetalLAF.MyMetalLAF");
else if ("motif".equals(lafID))
javax.swing.UIManager.setLookAndFeel("myMotifLAF.MyMotifLAF");
else if ("windows".equals(lafID))
javax.swing.UIManager.setLookAndFeel("myWindowsLAF.MyWindowsLAF");
} catch(Exception ex) {
System.err.println("Could not set L&F: "+ex);
System.exit(1);
}
System.out.println("using "+UIManager.getLookAndFeel());
T01 t = new T01();
t.init();
t.pack();
t.setVisible(true);
}
void init()
{
Container cont = getContentPane();
cont.setLayout(new FlowLayout());
cont.add(new JLabel("a RadioButton:"));
cont.add(new JRadioButton("standard"));
cont.add(new JLabel("a MyRadioButton:"));
MyRadioButton rb = new MyRadioButton("like standard");
cont.add(rb);
cont.add(new JLabel("a MyRadioButton:"));
rb = new MyRadioButton("fixed");
rb.putClientProperty("FIXED", "OK");
cont.add(rb);
}
}
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
Just do as in the example program: override some
classes and some methods, create derived L&Fs, etc.
(Review ID: 145775)
======================================================================
FULL PRODUCT VERSION :
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)
FULL OPERATING SYSTEM VERSION :
Microsoft Windows NT
4.00.1381
ServicePack: 5
A DESCRIPTION OF THE PROBLEM :
23-apr-2002
Painting of MotifRadioButton (and other buttons) is faulty.
Due to several bugs, a RadioButton is drawn incorrectly.
One bug, 4126679, reported already that the insets were
not taken into account when drawing in BasicRadioButtonUI.
Other problems are:
o in MotifIconFactory.RadioButtonIcon the start position
for drawing the icon is shifted right by 2.
This seems to be just a hack to fix the problem
really caused by not taking the insets into account
when painting, see above.
(( Same in CheckBoxIcon; others? ))
o MotifIconFactory.RadioButtonIcon says it is 13 wide and 13
high.
In its paint method lines are drawn from coordinates x+0
through x+13, and y+0 through y+13. Thus the icon is really
14 wide and 14 high.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
o split the enclosed source into files:
MyMetalLAF.java
MyMetalRadioButtonUI.java
MyMotifIconFactory.java
MyMotifLAF.java
MyMotifRadioButtonUI.java
MyRadioButton.java
MyWindowsLAF.java
MyWindowsRadioButtonUI.java
T01.java
o compile the files into a directory
o start T01 using one of:
<java-invocation> -Dlaf=windows T01
<java-invocation> -Dlaf=metal T01
<java-invocation> -Dlaf=motif T01
to have the program use one of its mini-lafs: MyWindowsLAF,
MyMetalLAF
and MyMotifLAF respectively
The program displays three labels and three RadioButtons.
The left RadioButton is an original swing JRadioButton.
The middle RadioButton is a MyRadioButton, behaving just
like the
standard JRadioButton, except that it paints its areas computed
by the paint method in its UI:
light blue : background of widgets
yellow : viewR: union of iconR and textR
light green: iconR: the rectangle into which the icon should
be drawn
light red : textR: the rectangle into which text should be drawn
The right RadioButton is a MyRadioButton, using fixes for
the problems,
except the one that the Motif icon reports a wrong size. It also
paints its areas like the middle button.
Methods were copied from jdk 1.4.0 sources as mentioned in
the sources.
Changes are marked:
//x : line is commented out
//{ : start change
//} : end change
To localize fixes, search for "FIXED".
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
===== MyMetalLAF.java
package myMetalLAF;
import javax.swing.*;
import javax.swing.plaf.metal.*;
public class MyMetalLAF
extends MetalLookAndFeel
{
public String getName() {
return "MyMetalLAF";
}
public String getID() {
return "MyMetalLAF";
}
public String getDescription() {
return "MY METAL LOOK AND FEEL";
}
public boolean isSupportedLookAndFeel() {
return true;
}
protected void initClassDefaults(UIDefaults table) {
super.initClassDefaults(table);
table.putDefaults(new Object[] {
"MyRadioButtonUI", "myMetalLAF.MyMetalRadioButtonUI",
});
}
}
===== MyMetalRadioButtonUI.java
package myMetalLAF;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.View;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.metal.*;
import javax.swing.plaf.basic.BasicGraphicsUtils;
public class MyMetalRadioButtonUI
extends MetalRadioButtonUI
{
public static ComponentUI createUI(JComponent c) {
return new MyMetalRadioButtonUI();
}
public synchronized void paint(Graphics g, JComponent c)
{
System.out.println("paint: called");
_paint(g, c);
}
// from: MetalRadioButtonUI.java
public synchronized void _paint(Graphics g, JComponent c) {
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
Dimension size = c.getSize();
int w = size.width;
int h = size.height;
Font f = c.getFont();
g.setFont(f);
FontMetrics fm = g.getFontMetrics();
//x Rectangle viewRect = new Rectangle(size);
//{
Rectangle viewRect;
if (c.getClientProperty("FIXED") != null) {
viewRect = new Rectangle();
Insets prefInsets = b.getInsets();
viewRect.x = prefInsets.left;
viewRect.y = prefInsets.top;
viewRect.width = size.width-prefInsets.left-prefInsets.right;
viewRect.height = size.height-prefInsets.top-prefInsets.bottom;
} else {
viewRect = new Rectangle(size);
}
//}
Rectangle iconRect = new Rectangle();
Rectangle textRect = new Rectangle();
Icon altIcon = b.getIcon();
Icon selectedIcon = null;
Icon disabledIcon = null;
String text = SwingUtilities.layoutCompoundLabel(
c, fm, b.getText(), altIcon != null ? altIcon : getDefaultIcon(),
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewRect, iconRect, textRect, b.getIconTextGap());
// fill background
if(c.isOpaque()) {
//x g.setColor(b.getBackground());
//{
g.setColor(new Color(127, 127, 255));
//}
g.fillRect(0,0, size.width, size.height);
}
//{
g.setColor(Color.yellow);
g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
g.setColor(new Color(127,255,127));
g.fillRect(iconRect.x, iconRect.y, iconRect.width, iconRect.height);
g.setColor(new Color(255,127,127));
g.fillRect(textRect.x, textRect.y, textRect.width, textRect.height);
//}
// Paint the radio button
if(altIcon != null) {
if(!model.isEnabled()) {
if(model.isSelected()) {
altIcon = b.getDisabledSelectedIcon();
} else {
altIcon = b.getDisabledIcon();
}
} else if(model.isPressed() && model.isArmed()) {
altIcon = b.getPressedIcon();
if(altIcon == null) {
// Use selected icon
altIcon = b.getSelectedIcon();
}
} else if(model.isSelected()) {
if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverSelectedIcon();
if (altIcon == null) {
altIcon = (Icon) b.getSelectedIcon();
}
} else {
altIcon = (Icon) b.getSelectedIcon();
}
} else if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverIcon();
}
if(altIcon == null) {
altIcon = b.getIcon();
}
altIcon.paintIcon(c, g, iconRect.x, iconRect.y);
} else {
getDefaultIcon().paintIcon(c, g, iconRect.x, iconRect.y);
}
// Draw the Text
if(text != null) {
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
v.paint(g, textRect);
} else {
int mnemIndex = b.getDisplayedMnemonicIndex();
if(model.isEnabled()) {
// *** paint the text normally
g.setColor(b.getForeground());
BasicGraphicsUtils.drawStringUnderlineCharAt(g,text,
mnemIndex, textRect.x, textRect.y + fm.getAscent());
} else {
// *** paint the text disabled
g.setColor(getDisabledTextColor());
BasicGraphicsUtils.drawStringUnderlineCharAt(g,text,
mnemIndex, textRect.x, textRect.y + fm.getAscent());
}
if(b.hasFocus() && b.isFocusPainted() &&
textRect.width > 0 && textRect.height > 0 ) {
paintFocus(g,textRect,size);
}
}
}
}
protected void paintFocus(Graphics g, Rectangle t, Dimension d){
g.setColor(getFocusColor());
g.drawRect(t.x-1, t.y-1, t.width+1, t.height+1);
}
}
===== MyMotifIconFactory.java
package myMotifLAF;
import java.awt.*;
import java.io.Serializable;
import javax.swing.*;
import javax.swing.plaf.UIResource;
import com.sun.java.swing.plaf.motif.*;
public class MyMotifIconFactory
{
private static Icon radioButtonIcon;
public static Icon getRadioButtonIcon() {
if (radioButtonIcon == null) {
radioButtonIcon = new RadioButtonIcon();
}
return radioButtonIcon;
}
// from: MotifIconFactory
private static class RadioButtonIcon implements Icon, UIResource, Serializable {
private Color dot = UIManager.getColor("activeCaptionBorder");
private Color highlight = UIManager.getColor("controlHighlight");
private Color shadow = UIManager.getColor("controlShadow");
public void paintIcon(Component c, Graphics g, int x, int y) {
// fill interior
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
int w = getIconWidth();
int h = getIconHeight();
// add pad so focus isn't smudged on the x
if (b instanceof JComponent && ((JComponent)b).getClientProperty("FIXED") != null) {
} else {
//x x += (MotifGraphicsUtils.isLeftToRight(c))? 2 : -3;
x += 2;
}
boolean isPressed = model.isPressed();
boolean isArmed = model.isArmed();
boolean isEnabled = model.isEnabled();
boolean isSelected = model.isSelected();
boolean checkIn = ((isPressed &&
!isArmed &&
isSelected) ||
(isPressed &&
isArmed &&
!isSelected)
||
(!isPressed &&
isArmed &&
isSelected ||
(!isPressed &&
!isArmed &&
isSelected)));
if (checkIn){
g.setColor(shadow);
g.drawLine(x+5,y+0,x+8,y+0);
g.drawLine(x+3,y+1,x+4,y+1);
g.drawLine(x+9,y+1,x+9,y+1);
g.drawLine(x+2,y+2,x+2,y+2);
g.drawLine(x+1,y+3,x+1,y+3);
g.drawLine(x,y+4,x,y+9);
g.drawLine(x+1,y+10,x+1,y+10);
g.drawLine(x+2,y+11,x+2,y+11);
g.setColor(highlight);
g.drawLine(x+3,y+12,x+4,y+12);
g.drawLine(x+5,y+13,x+8,y+13);
g.drawLine(x+9,y+12,x+10,y+12);
g.drawLine(x+11,y+11,x+11,y+11);
g.drawLine(x+12,y+10,x+12,y+10);
g.drawLine(x+13,y+9,x+13,y+4);
g.drawLine(x+12,y+3,x+12,y+3);
g.drawLine(x+11,y+2,x+11,y+2);
g.drawLine(x+10,y+1,x+10,y+1);
g.setColor(dot);
g.fillRect(x+4,y+5,6,4);
g.drawLine(x+5,y+4,x+8,y+4);
g.drawLine(x+5,y+9,x+8,y+9);
}
else {
g.setColor(highlight);
g.drawLine(x+5,y+0,x+8,y+0);
g.drawLine(x+3,y+1,x+4,y+1);
g.drawLine(x+9,y+1,x+9,y+1);
g.drawLine(x+2,y+2,x+2,y+2);
g.drawLine(x+1,y+3,x+1,y+3);
g.drawLine(x,y+4,x,y+9);
g.drawLine(x+1,y+10,x+1,y+10);
g.drawLine(x+2,y+11,x+2,y+11);
g.setColor(shadow);
g.drawLine(x+3,y+12,x+4,y+12);
g.drawLine(x+5,y+13,x+8,y+13);
g.drawLine(x+9,y+12,x+10,y+12);
g.drawLine(x+11,y+11,x+11,y+11);
g.drawLine(x+12,y+10,x+12,y+10);
g.drawLine(x+13,y+9,x+13,y+4);
g.drawLine(x+12,y+3,x+12,y+3);
g.drawLine(x+11,y+2,x+11,y+2);
g.drawLine(x+10,y+1,x+10,y+1);
}
}
public int getIconWidth() {
return 13;
}
public int getIconHeight() {
return 13;
}
} // end class RadioButtonIcon
}
===== MyMotifLAF.java
package myMotifLAF;
import javax.swing.*;
import com.sun.java.swing.plaf.motif.*;
public class MyMotifLAF
extends MotifLookAndFeel
{
public String getName() {
return "MyMotifLAF";
}
public String getID() {
return "MyMotifLAF";
}
public String getDescription() {
return "MY MOTIF LOOK AND FEEL";
}
public boolean isSupportedLookAndFeel() {
return true;
}
protected void initClassDefaults(UIDefaults table) {
super.initClassDefaults(table);
table.putDefaults(new Object[] {
"MyRadioButtonUI", "myMotifLAF.MyMotifRadioButtonUI",
});
}
protected void initComponentDefaults(UIDefaults table) {
super.initComponentDefaults(table);
UIManager.put("RadioButton.icon", new UIDefaults.LazyValue() {
public Object createValue(UIDefaults table) {
return MyMotifIconFactory.getRadioButtonIcon();
}
});
}
}
===== MyMotifRadioButtonUI.java
package myMotifLAF;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.View;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import com.sun.java.swing.plaf.motif.*;
public class MyMotifRadioButtonUI
extends MotifRadioButtonUI
{
public static ComponentUI createUI(JComponent c) {
return new MyMotifRadioButtonUI();
}
public synchronized void paint(Graphics g, JComponent c)
{
System.out.println("paint: called");
_paint(g, c);
}
private static Dimension size = new Dimension();
private static Rectangle viewRect = new Rectangle();
private static Rectangle iconRect = new Rectangle();
private static Rectangle textRect = new Rectangle();
// from BasicRadioButtonUI.java
public synchronized void _paint(Graphics g, JComponent c) {
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
Font f = c.getFont();
g.setFont(f);
FontMetrics fm = g.getFontMetrics();
size = b.getSize(size);
//x viewRect.x = viewRect.y = 0;
//x viewRect.width = size.width;
//x viewRect.height = size.height;
//{
//
prefInsets = b.getInsets(prefInsets);
if (c.getClientProperty("FIXED") != null) {
Insets prefInsets = b.getInsets();
viewRect.x = prefInsets.left;
viewRect.y = prefInsets.top;
viewRect.width = size.width-prefInsets.left-prefInsets.right;
viewRect.height = size.height-prefInsets.top-prefInsets.bottom;
} else {
viewRect.x = viewRect.y = 0;
viewRect.width = size.width;
viewRect.height = size.height;
}
//}
iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
textRect.x = textRect.y = textRect.width = textRect.height = 0;
Icon altIcon = b.getIcon();
Icon selectedIcon = null;
Icon disabledIcon = null;
String text = SwingUtilities.layoutCompoundLabel(
c, fm, b.getText(), altIcon != null ? altIcon : getDefaultIcon(),
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewRect, iconRect, textRect,
b.getText() == null ? 0 : b.getIconTextGap());
// fill background
if(c.isOpaque()) {
//x g.setColor(b.getBackground());
//{
g.setColor(new Color(127, 127, 255));
//}
g.fillRect(0,0, size.width, size.height);
}
//{
g.setColor(Color.yellow);
g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
g.setColor(new Color(127,255,127));
g.fillRect(iconRect.x, iconRect.y, iconRect.width, iconRect.height);
g.setColor(new Color(255,127,127));
g.fillR
###@###.### 10/13/04 17:53 GMT
ect(textRect.x, textRect.y, textRect.width, textRect.height);
//}
// Paint the radio button
if(altIcon != null) {
if(!model.isEnabled()) {
if(model.isSelected()) {
altIcon = b.getDisabledSelectedIcon();
} else {
altIcon = b.getDisabledIcon();
}
} else if(model.isPressed() && model.isArmed()) {
altIcon = b.getPressedIcon();
if(altIcon == null) {
// Use selected icon
altIcon = b.getSelectedIcon();
}
} else if(model.isSelected()) {
if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverSelectedIcon();
if (altIcon == null) {
altIcon = (Icon) b.getSelectedIcon();
}
} else {
altIcon = (Icon) b.getSelectedIcon();
}
} else if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverIcon();
}
if(altIcon == null) {
altIcon = b.getIcon();
}
altIcon.paintIcon(c, g, iconRect.x, iconRect.y);
} else {
getDefaultIcon().paintIcon(c, g, iconRect.x, iconRect.y);
}
// Draw the Text
if(text != null) {
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
v.paint(g, textRect);
} else {
paintText(g, b, textRect, text);
if(b.hasFocus() && b.isFocusPainted() &&
textRect.width > 0 && textRect.height > 0 ) {
paintFocus(g, textRect, size);
}
}
}
}
// from MotifRadioButtonUI.java:
protected void paintFocus(Graphics g, Rectangle t, Dimension d){
g.setColor(getFocusColor());
g.drawRect(0,0,d.width-1,d.height-1);
}
}
===== MyRadioButton.java
import javax.swing.JRadioButton;
public class MyRadioButton
extends JRadioButton
{
public String getUIClassID() {
return "MyRadioButtonUI";
}
public MyRadioButton(String text)
{
super(text);
}
}
===== MyWindowsLAF.java
package myWindowsLAF;
import javax.swing.*;
import com.sun.java.swing.plaf.windows.*;
public class MyWindowsLAF
extends WindowsLookAndFeel
{
public String getName() {
return "MyWindowsLAF";
}
public String getID() {
return "MyWindowsLAF";
}
public String getDescription() {
return "MY WINDOWS LOOK AND FEEL";
}
public boolean isSupportedLookAndFeel() {
return true;
}
protected void initClassDefaults(UIDefaults table) {
super.initClassDefaults(table);
table.putDefaults(new Object[] {
"MyRadioButtonUI", "myWindowsLAF.MyWindowsRadioButtonUI",
});
}
}
===== MyWindowsRadioButtonUI.java
package myWindowsLAF;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.View;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import com.sun.java.swing.plaf.windows.*;
import javax.swing.plaf.basic.BasicGraphicsUtils;
public class MyWindowsRadioButtonUI
extends WindowsRadioButtonUI
{
public static ComponentUI createUI(JComponent c) {
return new MyWindowsRadioButtonUI();
}
public synchronized void paint(Graphics g, JComponent c)
{
System.out.println("paint: called");
_paint(g, c);
}
private static Dimension size = new Dimension();
private static Rectangle viewRect = new Rectangle();
private static Rectangle iconRect = new Rectangle();
private static Rectangle textRect = new Rectangle();
// von: BasicRadioButtonUI.java
public synchronized void _paint(Graphics g, JComponent c) {
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
Font f = c.getFont();
g.setFont(f);
FontMetrics fm = g.getFontMetrics();
size = b.getSize(size);
//x viewRect.x = viewRect.y = 0;
//x viewRect.width = size.width;
//x viewRect.height = size.height;
//{
//
prefInsets = b.getInsets(prefInsets);
if (c.getClientProperty("FIXED") != null) {
Insets prefInsets = b.getInsets();
viewRect.x = prefInsets.left;
viewRect.y = prefInsets.top;
viewRect.width = size.width-prefInsets.left-prefInsets.right;
viewRect.height = size.height-prefInsets.top-prefInsets.bottom;
} else {
viewRect.x = viewRect.y = 0;
viewRect.width = size.width;
viewRect.height = size.height;
}
//}
iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
textRect.x = textRect.y = textRect.width = textRect.height = 0;
Icon altIcon = b.getIcon();
Icon selectedIcon = null;
Icon disabledIcon = null;
String text = SwingUtilities.layoutCompoundLabel(
c, fm, b.getText(), altIcon != null ? altIcon : getDefaultIcon(),
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewRect, iconRect, textRect,
b.getText() == null ? 0 : b.getIconTextGap());
// fill background
if(c.isOpaque()) {
//x g.setColor(b.getBackground());
//{
g.setColor(new Color(127, 127, 255));
//}
g.fillRect(0,0, size.width, size.height);
}
//{
g.setColor(Color.yellow);
g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
g.setColor(new Color(127,255,127));
g.fillRect(iconRect.x, iconRect.y, iconRect.width, iconRect.height);
g.setColor(new Color(255,127,127));
g.fillRect(textRect.x, textRect.y, textRect.width, textRect.height);
//}
// Paint the radio button
if(altIcon != null) {
if(!model.isEnabled()) {
if(model.isSelected()) {
altIcon = b.getDisabledSelectedIcon();
} else {
altIcon = b.getDisabledIcon();
}
} else if(model.isPressed() && model.isArmed()) {
altIcon = b.getPressedIcon();
if(altIcon == null) {
// Use selected icon
altIcon = b.getSelectedIcon();
}
} else if(model.isSelected()) {
if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverSelectedIcon();
if (altIcon == null) {
altIcon = (Icon) b.getSelectedIcon();
}
} else {
altIcon = (Icon) b.getSelectedIcon();
}
} else if(b.isRolloverEnabled() && model.isRollover()) {
altIcon = (Icon) b.getRolloverIcon();
}
if(altIcon == null) {
altIcon = b.getIcon();
}
altIcon.paintIcon(c, g, iconRect.x, iconRect.y);
} else {
getDefaultIcon().paintIcon(c, g, iconRect.x, iconRect.y);
}
// Draw the Text
if(text != null) {
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
v.paint(g, textRect);
} else {
paintText(g, b, textRect, text);
if(b.hasFocus() && b.isFocusPainted() &&
textRect.width > 0 && textRect.height > 0 ) {
paintFocus(g, textRect, size);
}
}
}
}
// from: WindowsRadioButtonUI
protected void paintFocus(Graphics g, Rectangle textRect, Dimension d){
g.setColor(getFocusColor());
BasicGraphicsUtils.drawDashedRect(g, textRect.x, textRect.y, textRect.width,
textRect.height);
}
}
===== T01.java
import java.awt.*;
import javax.swing.*;
public class T01
extends JFrame
{
public static void main(String[] av)
{
// usage:
// -Dlaf=windows|metal|motif
String lafID = System.getProperties().getProperty("laf", "metal");
System.out.println("lafID="+lafID);
try {
if ("metal".equals(lafID))
javax.swing.UIManager.setLookAndFeel("myMetalLAF.MyMetalLAF");
else if ("motif".equals(lafID))
javax.swing.UIManager.setLookAndFeel("myMotifLAF.MyMotifLAF");
else if ("windows".equals(lafID))
javax.swing.UIManager.setLookAndFeel("myWindowsLAF.MyWindowsLAF");
} catch(Exception ex) {
System.err.println("Could not set L&F: "+ex);
System.exit(1);
}
System.out.println("using "+UIManager.getLookAndFeel());
T01 t = new T01();
t.init();
t.pack();
t.setVisible(true);
}
void init()
{
Container cont = getContentPane();
cont.setLayout(new FlowLayout());
cont.add(new JLabel("a RadioButton:"));
cont.add(new JRadioButton("standard"));
cont.add(new JLabel("a MyRadioButton:"));
MyRadioButton rb = new MyRadioButton("like standard");
cont.add(rb);
cont.add(new JLabel("a MyRadioButton:"));
rb = new MyRadioButton("fixed");
rb.putClientProperty("FIXED", "OK");
cont.add(rb);
}
}
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
Just do as in the example program: override some
classes and some methods, create derived L&Fs, etc.
(Review ID: 145775)
======================================================================