/*
* @(#)GTKPainter.java 1.84 07/03/15
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.sun.java.swing.plaf.gtk;
import sun.swing.plaf.synth.SynthUI;
import sun.awt.UNIXToolkit;
import javax.swing.plaf.synth.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import com.sun.java.swing.plaf.gtk.GTKConstants.ArrowType;
import com.sun.java.swing.plaf.gtk.GTKConstants.ExpanderStyle;
import com.sun.java.swing.plaf.gtk.GTKConstants.Orientation;
import com.sun.java.swing.plaf.gtk.GTKConstants.PositionType;
import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @version 1.84, 03/15/07
* @author Joshua Outwater
* @author Scott Violet
*/
// Need to support:
// default_outside_border: Insets when default.
// interior_focus: Indicates if focus should appear inside border, or
// outside border.
// focus-line-width: Integer giving size of focus border
// focus-padding: Integer giving padding between border and focus
// indicator.
// focus-line-pattern:
//
class GTKPainter extends SynthPainter {
private static final PositionType[] POSITIONS = {
PositionType.BOTTOM, PositionType.RIGHT,
PositionType.TOP, PositionType.LEFT
};
private static final ShadowType SHADOWS[] = {
ShadowType.NONE, ShadowType.IN, ShadowType.OUT,
ShadowType.ETCHED_IN, ShadowType.OUT
};
private final static GTKEngine ENGINE = GTKEngine.INSTANCE;
final static GTKPainter INSTANCE = new GTKPainter();
private GTKPainter() {
}
private String getName(SynthContext context) {
return (context.getRegion().isSubregion()) ? null :
context.getComponent().getName();
}
public void paintCheckBoxBackground(SynthContext context,
Graphics g, int x, int y, int w, int h) {
paintRadioButtonBackground(context, g, x, y, w, h);
}
public void paintCheckBoxMenuItemBackground(SynthContext context,
Graphics g, int x, int y, int w, int h) {
paintRadioButtonMenuItemBackground(context, g, x, y, w, h);
}
// FORMATTED_TEXT_FIELD
public void paintFormattedTextFieldBackground(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
paintTextBackground(context, g, x, y, w, h);
}
//
// TOOL_BAR_DRAG_WINDOW
//
public void paintToolBarDragWindowBackground(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
paintToolBarBackground(context, g, x, y, w, h);
}
//
// TOOL_BAR
//
public void paintToolBarBackground(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
Region id = context.getRegion();
int state = context.getComponentState();
int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
int orientation = ((JToolBar)context.getComponent()).getOrientation();
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id,
state, orientation))
{
ENGINE.startPainting(g, x, y, w, h, id, state, orientation);
ENGINE.paintBox(g, context, id, gtkState, ShadowType.OUT,
"handlebox_bin", x, y, w, h);
ENGINE.finishPainting();
}
}
}
public void paintToolBarContentBackground(SynthContext context,
Graphics g,
int x, int y, int w, int h) {
Region id = context.getRegion();
int orientation = ((JToolBar)context.getComponent()).getOrientation();
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id, orientation)) {
ENGINE.startPainting(g, x, y, w, h, id, orientation);
ENGINE.paintBox(g, context, id, SynthConstants.ENABLED,
ShadowType.OUT, "toolbar", x, y, w, h);
ENGINE.finishPainting();
}
}
}
//
// PASSWORD_FIELD
//
public void paintPasswordFieldBackground(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
paintTextBackground(context, g, x, y, w, h);
}
//
// TEXT_FIELD
//
public void paintTextFieldBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
if (getName(context) == "Tree.cellEditor") {
paintTreeCellEditorBackground(context, g, x, y, w, h);
} else {
paintTextBackground(context, g, x, y, w, h);
}
}
//
// RADIO_BUTTON
//
// NOTE: this is called for JCheckBox too
public void paintRadioButtonBackground(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
Region id = context.getRegion();
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
if (gtkState == SynthConstants.MOUSE_OVER) {
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
ENGINE.startPainting(g, x, y, w, h, id);
ENGINE.paintFlatBox(g, context, id,
SynthConstants.MOUSE_OVER, ShadowType.ETCHED_OUT,
"checkbutton", x, y, w, h, ColorType.BACKGROUND);
ENGINE.finishPainting();
}
}
}
}
//
// RADIO_BUTTON_MENU_ITEM
//
// NOTE: this is called for JCheckBoxMenuItem too
public void paintRadioButtonMenuItemBackground(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
Region id = context.getRegion();
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
if (gtkState == SynthConstants.MOUSE_OVER) {
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
ShadowType shadow = (GTKLookAndFeel.is2_2() ?
ShadowType.NONE : ShadowType.OUT);
ENGINE.startPainting(g, x, y, w, h, id);
ENGINE.paintBox(g, context, id, gtkState,
shadow, "menuitem", x, y, w, h);
ENGINE.finishPainting();
}
}
}
}
//
// LABEL
//
public void paintLabelBackground(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
String name = getName(context);
if (name == "TableHeader.renderer" ||
name == "GTKFileChooser.directoryListLabel" ||
name == "GTKFileChooser.fileListLabel") {
paintButtonBackgroundImpl(context, g, Region.BUTTON, "button",
x, y, w, h, true, false, false, false);
} else if (name == "ComboBox.renderer") {
paintTextBackground(context, g, x, y, w, h);
}
}
//
// INTERNAL_FRAME
//
public void paintInternalFrameBorder(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
Metacity.INSTANCE.paintFrameBorder(context, g, x, y, w, h);
}
//
// DESKTOP_PANE
//
public void paintDesktopPaneBackground(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
// Does not call into ENGINE for better performance
fillArea(context, g, x, y, w, h, ColorType.BACKGROUND);
}
//
// DESKTOP_ICON
//
public void paintDesktopIconBorder(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
Metacity.INSTANCE.paintFrameBorder(context, g, x, y, w, h);
}
public void paintButtonBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
String name = getName(context);
if (name != null && name.startsWith("InternalFrameTitlePane.")) {
Metacity.INSTANCE.paintButtonBackground(context, g, x, y, w, h);
} else {
AbstractButton button = (AbstractButton)context.getComponent();
boolean paintBG = button.isContentAreaFilled() &&
button.isBorderPainted();
boolean paintFocus = button.isFocusPainted();
boolean defaultCapable = (button instanceof JButton) &&
((JButton)button).isDefaultCapable();
boolean toolButton = (button.getParent() instanceof JToolBar);
paintButtonBackgroundImpl(context, g, Region.BUTTON, "button",
x, y, w, h, paintBG, paintFocus, defaultCapable, toolButton);
}
}
private void paintButtonBackgroundImpl(SynthContext context, Graphics g,
Region id, String detail, int x, int y, int w, int h,
boolean paintBackground, boolean paintFocus,
boolean defaultCapable, boolean toolButton) {
int state = context.getComponentState();
synchronized (UNIXToolkit.GTK_LOCK) {
if (ENGINE.paintCachedImage(g, x, y, w, h, id, state, detail,
paintBackground, paintFocus, defaultCapable, toolButton)) {
return;
}
ENGINE.startPainting(g, x, y, w, h, id, state, detail,
paintBackground, paintFocus, defaultCapable, toolButton);
// Paint the default indicator
GTKStyle style = (GTKStyle)context.getStyle();
if (defaultCapable && !toolButton) {
Insets defaultInsets = (Insets)style.getClassSpecificInsetsValue(
context, "default-border",
GTKStyle.BUTTON_DEFAULT_BORDER_INSETS);
if (paintBackground && (state & SynthConstants.DEFAULT) != 0) {
ENGINE.paintBox(g, context, id, SynthConstants.ENABLED,
ShadowType.IN, "buttondefault", x, y, w, h);
}
x += defaultInsets.left;
y += defaultInsets.top;
w -= (defaultInsets.left + defaultInsets.right);
h -= (defaultInsets.top + defaultInsets.bottom);
}
boolean interiorFocus = style.getClassSpecificBoolValue(
context, "interior-focus", true);
int focusSize = style.getClassSpecificIntValue(
context, "focus-line-width",1);
int focusPad = style.getClassSpecificIntValue(
context, "focus-padding", 1);
int totalFocusSize = focusSize + focusPad;
int xThickness = style.getXThickness();
int yThickness = style.getYThickness();
// Render the box.
if (!interiorFocus &&
(state & SynthConstants.FOCUSED) == SynthConstants.FOCUSED) {
x += totalFocusSize;
y += totalFocusSize;
w -= 2 * totalFocusSize;
h -= 2 * totalFocusSize;
}
int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
boolean paintBg;
if (toolButton) {
// Toolbar buttons should only have their background painted
// in the PRESSED, SELECTED, or MOUSE_OVER states.
paintBg =
(gtkState != SynthConstants.ENABLED) &&
(gtkState != SynthConstants.DISABLED);
} else {
// Otherwise, always paint the button's background, unless
// the user has overridden it and we're in the ENABLED state.
paintBg =
paintBackground ||
(gtkState != SynthConstants.ENABLED);
}
if (paintBg) {
ShadowType shadowType = ShadowType.OUT;
if ((state & (SynthConstants.PRESSED |
SynthConstants.SELECTED)) != 0) {
shadowType = ShadowType.IN;
}
ENGINE.paintBox(g, context, id, gtkState,
shadowType, detail, x, y, w, h);
}
// focus
if (paintFocus && (state & SynthConstants.FOCUSED) != 0) {
if (interiorFocus) {
x += xThickness + focusPad;
y += yThickness + focusPad;
w -= 2 * (xThickness + focusPad);
h -= 2 * (yThickness + focusPad);
} else {
x -= totalFocusSize;
y -= totalFocusSize;
w += 2 * totalFocusSize;
h += 2 * totalFocusSize;
}
ENGINE.paintFocus(g, context, id, gtkState, detail, x, y, w, h);
}
ENGINE.finishPainting();
}
}
//
// ARROW_BUTTON
//
public void paintArrowButtonForeground(SynthContext context, Graphics g,
int x, int y, int w, int h,
int direction) {
Region id = context.getRegion();
Component c = context.getComponent();
String name = c.getName();
ArrowType arrowType = null;
switch (direction) {
case SwingConstants.NORTH:
arrowType = ArrowType.UP; break;
case SwingConstants.SOUTH:
arrowType = ArrowType.DOWN; break;
case SwingConstants.EAST:
arrowType = ArrowType.RIGHT; break;
case SwingConstants.WEST:
arrowType = ArrowType.LEFT; break;
}
String detail = "arrow";
if (name == "ScrollBar.button") {
if (arrowType == ArrowType.UP || arrowType == ArrowType.DOWN) {
detail = "vscrollbar";
} else {
detail = "hscrollbar";
}
} else if (name == "Spinner.nextButton" ||
name == "Spinner.previousButton") {
detail = "spinbutton";
} else if (name != "ComboBox.arrowButton") {
assert false;
}
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
ShadowType shadowType = (gtkState == SynthConstants.PRESSED ?
ShadowType.IN : ShadowType.OUT);
synchronized (UNIXToolkit.GTK_LOCK) {
if (ENGINE.paintCachedImage(g, x, y, w, h,
gtkState, name, direction)) {
return;
}
ENGINE.startPainting(g, x, y, w, h, gtkState, name, direction);
ENGINE.paintArrow(g, context, id, gtkState,
shadowType, arrowType, detail, x, y, w, h);
ENGINE.finishPainting();
}
}
public void paintArrowButtonBackground(SynthContext context,
Graphics g, int x, int y, int w, int h) {
Region id = context.getRegion();
AbstractButton button = (AbstractButton)context.getComponent();
String name = button.getName();
String detail = "button";
int direction = SwingConstants.CENTER;
if (name == "ScrollBar.button") {
Integer prop = (Integer)
button.getClientProperty("__arrow_direction__");
direction = (prop != null) ?
prop.intValue() : SwingConstants.WEST;
switch (direction) {
default:
case SwingConstants.EAST:
case SwingConstants.WEST:
detail = "hscrollbar";
break;
case SwingConstants.NORTH:
case SwingConstants.SOUTH:
detail = "vscrollbar";
break;
}
} else if (name == "Spinner.previousButton") {
detail = "spinbutton_down";
} else if (name == "Spinner.nextButton") {
detail = "spinbutton_up";
} else if (name != "ComboBox.arrowButton") {
assert false;
}
int state = context.getComponentState();
synchronized (UNIXToolkit.GTK_LOCK) {
if (ENGINE.paintCachedImage(g, x, y, w, h, id,
state, detail, direction))
{
return;
}
ENGINE.startPainting(g, x, y, w, h, id,
state, detail, direction);
if (detail.startsWith("spin")) {
/*
* The ubuntulooks engine (and presumably others) expect us to
* first draw the full "spinbutton" background, and then draw
* the individual "spinbutton_up/down" buttons on top of that.
* Note that it is the state of the JSpinner (not its arrow
* button) that determines how we draw this background.
*/
int spinState = button.getParent().isEnabled() ?
SynthConstants.ENABLED : SynthConstants.DISABLED;
int mody = (detail == "spinbutton_up") ? y : y-h;
int modh = h*2;
ENGINE.paintBox(g, context, id, spinState,
ShadowType.IN, "spinbutton",
x, mody, w, modh);
}
int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
ShadowType shadowType = ShadowType.OUT;
if ((gtkState & (SynthConstants.PRESSED |
SynthConstants.SELECTED)) != 0)
{
shadowType = ShadowType.IN;
}
ENGINE.paintBox(g, context, id, gtkState,
shadowType, detail, x, y, w, h);
ENGINE.finishPainting();
}
}
//
// LIST
//
public void paintListBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
// Does not call into ENGINE for better performance
fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
}
public void paintMenuBarBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
Region id = context.getRegion();
synchronized (UNIXToolkit.GTK_LOCK) {
if (ENGINE.paintCachedImage(g, x, y, w, h, id)) {
return;
}
GTKStyle style = (GTKStyle)context.getStyle();
int shadow = style.getClassSpecificIntValue(
context, "shadow-type", 2);
ShadowType shadowType = SHADOWS[shadow];
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
ENGINE.startPainting(g, x, y, w, h, id);
ENGINE.paintBox(g, context, id, gtkState,
shadowType, "menubar", x, y, w, h);
ENGINE.finishPainting();
}
}
//
// MENU
//
public void paintMenuBackground(SynthContext context,
Graphics g,
int x, int y, int w, int h) {
paintMenuItemBackground(context, g, x, y, w, h);
}
// This is called for both MENU and MENU_ITEM
public void paintMenuItemBackground(SynthContext context,
Graphics g,
int x, int y, int w, int h) {
int gtkState = GTKLookAndFeel.synthStateToGTKState(
context.getRegion(), context.getComponentState());
if (gtkState == SynthConstants.MOUSE_OVER) {
Region id = Region.MENU_ITEM;
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
ShadowType shadow = (GTKLookAndFeel.is2_2() ?
ShadowType.NONE : ShadowType.OUT);
ENGINE.startPainting(g, x, y, w, h, id);
ENGINE.paintBox(g, context, id, gtkState, shadow,
"menuitem", x, y, w, h);
ENGINE.finishPainting();
}
}
}
}
public void paintPopupMenuBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
Region id = context.getRegion();
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
synchronized (UNIXToolkit.GTK_LOCK) {
if (ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState)) {
return;
}
ENGINE.startPainting(g, x, y, w, h, id, gtkState);
ENGINE.paintBox(g, context, id, gtkState,
ShadowType.OUT, "menu", x, y, w, h);
GTKStyle style = (GTKStyle)context.getStyle();
int xThickness = style.getXThickness();
int yThickness = style.getYThickness();
ENGINE.paintBackground(g, context, id, gtkState,
style.getGTKColor(context, gtkState, GTKColorType.BACKGROUND),
x + xThickness, y + yThickness,
w - xThickness - xThickness, h - yThickness - yThickness);
ENGINE.finishPainting();
}
}
public void paintProgressBarBackground(SynthContext context,
Graphics g,
int x, int y, int w, int h) {
Region id = context.getRegion();
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
ENGINE.startPainting(g, x, y, w, h, id);
ENGINE.paintBox(g, context, id, SynthConstants.ENABLED,
ShadowType.IN, "trough", x, y, w, h);
ENGINE.finishPainting();
}
}
}
public void paintProgressBarForeground(SynthContext context, Graphics g,
int x, int y, int w, int h,
int orientation) {
Region id = context.getRegion();
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id, "fg")) {
ENGINE.startPainting(g, x, y, w, h, id, "fg");
ENGINE.paintBox(g, context, id, SynthConstants.MOUSE_OVER,
ShadowType.OUT, "bar", x, y, w, h);
ENGINE.finishPainting();
}
}
}
public void paintViewportBorder(SynthContext context, Graphics g,
int x, int y, int w, int h) {
Region id = context.getRegion();
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
ENGINE.startPainting(g, x, y, w, h, id);
ENGINE.paintShadow(g, context, id, SynthConstants.ENABLED,
ShadowType.IN, "scrolled_window", x, y, w, h);
ENGINE.finishPainting();
}
}
}
public void paintScrollPaneBorder(SynthContext context, Graphics g,
int x, int y, int w, int h) {
paintViewportBorder(context, g, x, y, w, h);
}
public void paintSeparatorBackground(SynthContext context,
Graphics g,
int x, int y, int w, int h,
int orientation) {
Region id = context.getRegion();
int state = context.getComponentState();
JComponent c = context.getComponent();
/*
* Note: In theory, the style's x/y thickness values would determine
* the width of the separator content. In practice, however, some
* engines will render a line that is wider than the corresponding
* thickness value. For example, ubuntulooks reports x/y thickness
* values of 1 for separators, but always renders a 2-pixel wide line.
* As a result of all this, we need to be careful not to restrict
* the w/h values below too much, so that the full thickness of the
* rendered line will be captured by our image caching code.
*/
String detail;
if (c instanceof JToolBar.Separator) {
/*
* GTK renders toolbar separators differently in that an
* artificial padding is added to each end of the separator.
* The value of 0.2f below is derived from the source code of
* gtktoolbar.c in the current version of GTK+ (2.8.20 at the
* time of this writing). Specifically, the relevant values are:
* SPACE_LINE_DIVISION 10.0
* SPACE_LINE_START 2.0
* SPACE_LINE_END 8.0
* These are used to determine the distance from the top (or left)
* edge of the toolbar to the other edge. So for example, the
* starting/top point of a vertical separator is 2/10 of the
* height of a horizontal toolbar away from the top edge, which
* is how we arrive at 0.2f below. Likewise, the ending/bottom
* point is 8/10 of the height away from the top edge, or in other
* words, it is 2/10 away from the bottom edge, which is again
* how we arrive at the 0.2f value below.
*
* The separator is also centered horizontally or vertically,
* depending on its orientation. This was determined empirically
* and by examining the code referenced above.
*/
detail = "toolbar";
float pct = 0.2f;
JToolBar.Separator sep = (JToolBar.Separator)c;
Dimension size = sep.getSeparatorSize();
GTKStyle style = (GTKStyle)context.getStyle();
if (orientation == JSeparator.HORIZONTAL) {
x += (int)(w * pct);
w -= (int)(w * pct * 2);
y += (size.height - style.getYThickness()) / 2;
} else {
y += (int)(h * pct);
h -= (int)(h * pct * 2);
x += (size.width - style.getXThickness()) / 2;
}
} else {
// For regular/menu separators, we simply subtract out the insets.
detail = "separator";
Insets insets = c.getInsets();
x += insets.left;
y += insets.top;
if (orientation == JSeparator.HORIZONTAL) {
w -= (insets.left + insets.right);
} else {
h -= (insets.top + insets.bottom);
}
}
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id,
state, detail, orientation)) {
ENGINE.startPainting(g, x, y, w, h, id,
state, detail, orientation);
if (orientation == JSeparator.HORIZONTAL) {
ENGINE.paintHline(g, context, id, state,
detail, x, y, w, h);
} else {
ENGINE.paintVline(g, context, id, state,
detail, x, y, w, h);
}
ENGINE.finishPainting();
}
}
}
public void paintSliderTrackBackground(SynthContext context,
Graphics g,
int x, int y, int w,int h) {
Region id = context.getRegion();
int state = context.getComponentState();
// For focused sliders, we paint focus rect outside the bounds passed.
// Need to adjust for that.
boolean focused = ((state & SynthConstants.FOCUSED) != 0);
int focusSize = 0;
if (focused) {
GTKStyle style = (GTKStyle)context.getStyle();
focusSize = style.getClassSpecificIntValue(
context, "focus-line-width", 1) +
style.getClassSpecificIntValue(
context, "focus-padding", 1);
x -= focusSize;
y -= focusSize;
w += focusSize * 2;
h += focusSize * 2;
}
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id, state)) {
ENGINE.startPainting(g, x, y, w, h, id, state);
int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
ENGINE.paintBox(g, context, id, gtkState, ShadowType.IN,
"trough", x + focusSize, y + focusSize,
w - 2 * focusSize, h - 2 * focusSize);
if (focused) {
ENGINE.paintFocus(g, context, id, SynthConstants.ENABLED,
"trough", x, y, w, h);
}
ENGINE.finishPainting();
}
}
}
public void paintSliderThumbBackground(SynthContext context,
Graphics g, int x, int y, int w, int h, int dir) {
Region id = context.getRegion();
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, dir)) {
Orientation orientation = (dir == JSlider.HORIZONTAL ?
Orientation.HORIZONTAL : Orientation.VERTICAL);
String detail = (dir == JSlider.HORIZONTAL ?
"hscale" : "vscale");
ENGINE.startPainting(g, x, y, w, h, id, gtkState, dir);
ENGINE.paintSlider(g, context, id, gtkState,
ShadowType.OUT, detail, x, y, w, h, orientation);
ENGINE.finishPainting();
}
}
}
//
// SPINNER
//
public void paintSpinnerBackground(SynthContext context,
Graphics g,
int x, int y, int w, int h) {
// This is handled in paintTextFieldBackground
}
//
// SPLIT_PANE_DIVIDER
//
public void paintSplitPaneDividerBackground(SynthContext context,
Graphics g,
int x, int y, int w, int h) {
Region id = context.getRegion();
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
JSplitPane splitPane = (JSplitPane)context.getComponent();
Orientation orientation =
(splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ?
Orientation.VERTICAL : Orientation.HORIZONTAL);
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h,
id, gtkState, orientation)) {
ENGINE.startPainting(g, x, y, w, h, id, gtkState, orientation);
ENGINE.paintHandle(g, context, id, gtkState,
ShadowType.OUT, "paned", x, y, w, h, orientation);
ENGINE.finishPainting();
}
}
}
public void paintSplitPaneDragDivider(SynthContext context,
Graphics g,int x, int y, int w, int h,
int orientation) {
paintSplitPaneDividerForeground(context, g, x, y, w, h, orientation);
}
public void paintTabbedPaneContentBackground(SynthContext context,
Graphics g, int x, int y, int w, int h) {
JTabbedPane pane = (JTabbedPane)context.getComponent();
int selectedIndex = pane.getSelectedIndex();
PositionType placement = GTKLookAndFeel.SwingOrientationConstantToGTK(
pane.getTabPlacement());
int gapStart = 0;
int gapSize = 0;
if (selectedIndex != -1) {
Rectangle tabBounds = pane.getBoundsAt(selectedIndex);
if (placement == PositionType.TOP ||
placement == PositionType.BOTTOM) {
gapStart = tabBounds.x - 1;
gapSize = tabBounds.width;
}
else {
gapStart = tabBounds.y - 1;
gapSize = tabBounds.height;
}
}
Region id = context.getRegion();
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h,
id, gtkState, placement, gapStart, gapSize)) {
ENGINE.startPainting(g, x, y, w, h,
id, gtkState, placement, gapStart, gapSize);
ENGINE.paintBoxGap(g, context, id, gtkState, ShadowType.OUT,
"notebook", x, y, w, h, placement, gapStart, gapSize);
ENGINE.finishPainting();
}
}
}
public void paintTabbedPaneTabBackground(SynthContext context,
Graphics g,
int x, int y, int w, int h,
int tabIndex) {
Region id = context.getRegion();
int state = context.getComponentState();
int gtkState = ((state & SynthConstants.SELECTED) != 0 ?
SynthConstants.ENABLED : SynthConstants.PRESSED);
JTabbedPane pane = (JTabbedPane)context.getComponent();
int placement = pane.getTabPlacement();
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h,
id, gtkState, placement, tabIndex)) {
PositionType side = POSITIONS[placement - 1];
ENGINE.startPainting(g, x, y, w, h,
id, gtkState, placement, tabIndex);
ENGINE.paintExtension(g, context, id, gtkState,
ShadowType.OUT, "tab", x, y, w, h, side, tabIndex);
ENGINE.finishPainting();
}
}
}
//
// TEXT_AREA
//
public void paintTextAreaBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
// Does not call into ENGINE for better performance
fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
}
//
// TEXT_FIELD
//
// NOTE: Combobox and Label, Password and FormattedTextField calls this
// too.
private void paintTextBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
// Text is odd in that it uses the TEXT_BACKGROUND vs BACKGROUND.
JComponent c = context.getComponent();
GTKStyle style = (GTKStyle)context.getStyle();
Region id = context.getRegion();
int state = context.getComponentState();
synchronized (UNIXToolkit.GTK_LOCK) {
if (ENGINE.paintCachedImage(g, x, y, w, h, id, state)) {
return;
}
int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
int focusSize = 0;
boolean interiorFocus = style.getClassSpecificBoolValue(
context, "interior-focus", true);
if (!interiorFocus && (state & SynthConstants.FOCUSED) != 0) {
focusSize = style.getClassSpecificIntValue(context,
"focus-line-width",1);
x += focusSize;
y += focusSize;
w -= 2 * focusSize;
h -= 2 * focusSize;
}
int xThickness = style.getXThickness();
int yThickness = style.getYThickness();
ENGINE.startPainting(g, x, y, w, h, id, state);
ENGINE.paintShadow(g, context, id, gtkState,
ShadowType.IN, "entry", x, y, w, h);
ENGINE.paintFlatBox(g, context, id,
gtkState, ShadowType.NONE, "entry_bg",
x + xThickness,
y + yThickness,
w - (2 * xThickness),
h - (2 * yThickness),
ColorType.TEXT_BACKGROUND);
if (focusSize > 0) {
x -= focusSize;
y -= focusSize;
w += 2 * focusSize;
h += 2 * focusSize;
ENGINE.paintFocus(g, context, id, gtkState,
"entry", x, y, w, h);
}
ENGINE.finishPainting();
}
}
private void paintTreeCellEditorBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
Region id = context.getRegion();
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState)) {
ENGINE.startPainting(g, x, y, w, h, id, gtkState);
ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE,
"entry_bg", x, y, w, h, ColorType.TEXT_BACKGROUND);
ENGINE.finishPainting();
}
}
}
//
// ROOT_PANE
//
public void paintRootPaneBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
// Does not call into ENGINE for better performance
fillArea(context, g, x, y, w, h, GTKColorType.BACKGROUND);
}
//
// TOGGLE_BUTTON
//
public void paintToggleButtonBackground(SynthContext context,
Graphics g,
int x, int y, int w, int h) {
Region id = context.getRegion();
JToggleButton toggleButton = (JToggleButton)context.getComponent();
boolean paintBG = toggleButton.isContentAreaFilled() &&
toggleButton.isBorderPainted();
boolean paintFocus = toggleButton.isFocusPainted();
boolean toolButton = (toggleButton.getParent() instanceof JToolBar);
paintButtonBackgroundImpl(context, g, id, "button",
x, y, w, h,
paintBG, paintFocus, false, toolButton);
}
//
// SCROLL_BAR
//
public void paintScrollBarBackground(SynthContext context,
Graphics g,
int x, int y, int w,int h) {
Region id = context.getRegion();
boolean focused =
(context.getComponentState() & SynthConstants.FOCUSED) != 0;
synchronized (UNIXToolkit.GTK_LOCK) {
if (ENGINE.paintCachedImage(g, x, y, w, h, id, focused)) {
return;
}
ENGINE.startPainting(g, x, y, w, h, id, focused);
// Note: the scrollbar insets already include the "trough-border",
// which is needed to position the scrollbar buttons properly.
// But when we render, we need to take the trough border out
// of the equation so that we paint the entire area covered by
// the trough border and the scrollbar content itself.
Insets insets = context.getComponent().getInsets();
GTKStyle style = (GTKStyle)context.getStyle();
int troughBorder =
style.getClassSpecificIntValue(context, "trough-border", 1);
insets.left -= troughBorder;
insets.right -= troughBorder;
insets.top -= troughBorder;
insets.bottom -= troughBorder;
ENGINE.paintBox(g, context, id, SynthConstants.PRESSED,
ShadowType.IN, "trough",
x + insets.left,
y + insets.top,
w - insets.left - insets.right,
h - insets.top - insets.bottom);
if (focused) {
ENGINE.paintFocus(g, context, id,
SynthConstants.ENABLED, "trough", x, y, w, h);
}
ENGINE.finishPainting();
}
}
//
// SCROLL_BAR_THUMB
//
public void paintScrollBarThumbBackground(SynthContext context,
Graphics g, int x, int y, int w, int h, int dir) {
Region id = context.getRegion();
int gtkState = GTKLookAndFeel.synthStateToGTKState(
id, context.getComponentState());
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, dir)) {
ENGINE.startPainting(g, x, y, w, h, id, gtkState, dir);
Orientation orientation = (dir == JScrollBar.HORIZONTAL ?
Orientation.HORIZONTAL : Orientation.VERTICAL);
ENGINE.paintSlider(g, context, id, gtkState,
ShadowType.OUT, "slider", x, y, w, h, orientation);
ENGINE.finishPainting();
}
}
}
//
// TOOL_TIP
//
public void paintToolTipBackground(SynthContext context, Graphics g,
int x, int y, int w,int h) {
Region id = context.getRegion();
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
ENGINE.startPainting(g, x, y, w, h, id);
ENGINE.paintFlatBox(g, context, id, SynthConstants.ENABLED,
ShadowType.OUT, "tooltip", x, y, w, h,
ColorType.BACKGROUND);
ENGINE.finishPainting();
}
}
}
//
// TREE_CELL
//
public void paintTreeCellBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
Region id = context.getRegion();
int state = context.getComponentState();
int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id, state)) {
ENGINE.startPainting(g, x, y, w, h, id, state);
// the string arg should alternate based on row being painted,
// but we currently don't pass that in.
ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE,
"cell_odd", x, y, w, h, ColorType.TEXT_BACKGROUND);
ENGINE.finishPainting();
}
}
}
public void paintTreeCellFocus(SynthContext context, Graphics g,
int x, int y, int w, int h) {
Region id = Region.TREE_CELL;
int state = context.getComponentState();
paintFocus(context, g, id, state, "treeview", x, y, w, h);
}
//
// TREE
//
public void paintTreeBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
// As far as I can tell, these don't call into the ENGINE.
fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
}
//
// VIEWPORT
//
public void paintViewportBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
// As far as I can tell, these don't call into the ENGINE.
// Also note that you don't want this to call into the ENGINE
// as if it where to paint a background JViewport wouldn't scroll
// correctly.
fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
}
void paintFocus(SynthContext context, Graphics g, Region id,
int state, String detail, int x, int y, int w, int h) {
int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, "focus")) {
ENGINE.startPainting(g, x, y, w, h, id, gtkState, "focus");
ENGINE.paintFocus(g, context, id, gtkState, detail, x, y, w, h);
ENGINE.finishPainting();
}
}
}
void paintMetacityElement(SynthContext context, Graphics g,
int gtkState, String detail, int x, int y, int w, int h,
ShadowType shadow, ArrowType direction) {
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(
g, x, y, w, h, gtkState, detail, shadow, direction)) {
ENGINE.startPainting(
g, x, y, w, h, gtkState, detail, shadow, direction);
if (detail == "metacity-arrow") {
ENGINE.paintArrow(g, context, Region.INTERNAL_FRAME_TITLE_PANE,
gtkState, shadow, direction, "", x, y, w, h);
} else if (detail == "metacity-box") {
ENGINE.paintBox(g, context, Region.INTERNAL_FRAME_TITLE_PANE,
gtkState, shadow, "", x, y, w, h);
} else if (detail == "metacity-vline") {
ENGINE.paintVline(g, context, Region.INTERNAL_FRAME_TITLE_PANE,
gtkState, "", x, y, w, h);
}
ENGINE.finishPainting();
}
}
}
void paintIcon(SynthContext context, Graphics g,
Method paintMethod, int x, int y, int w, int h) {
int state = context.getComponentState();
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, state, paintMethod)) {
ENGINE.startPainting(g, x, y, w, h, state, paintMethod);
try {
paintMethod.invoke(this, context, g, state, x, y, w, h);
} catch (IllegalAccessException iae) {
assert false;
} catch (InvocationTargetException ite) {
assert false;
}
ENGINE.finishPainting();
}
}
}
void paintIcon(SynthContext context, Graphics g,
Method paintMethod, int x, int y, int w, int h, Object direction) {
int state = context.getComponentState();
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g,
x, y, w, h, state, paintMethod, direction)) {
ENGINE.startPainting(g,
x, y, w, h, state, paintMethod, direction);
try {
paintMethod.invoke(this, context,
g, state, x, y, w, h, direction);
} catch (IllegalAccessException iae) {
assert false;
} catch (InvocationTargetException ite) {
assert false;
}
ENGINE.finishPainting();
}
}
}
// All icon painting methods are called from under GTK_LOCK
public void paintTreeExpandedIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
ENGINE.paintExpander(g, context, Region.TREE,
GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state),
ExpanderStyle.EXPANDED, "treeview", x, y, w, h);
}
public void paintTreeCollapsedIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
ENGINE.paintExpander(g, context, Region.TREE,
GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state),
ExpanderStyle.COLLAPSED, "treeview", x, y, w, h);
}
public void paintCheckBoxIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
ShadowType shadowType = ShadowType.OUT;
if (((JCheckBox)context.getComponent()).isSelected()) {
shadowType = ShadowType.IN;
}
ENGINE.paintCheck(g, context, Region.CHECK_BOX,
GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state),
shadowType, "checkbutton", x, y, w, h);
}
public void paintRadioButtonIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
int gtkState = GTKLookAndFeel.synthStateToGTKState(
context.getRegion(), state);
ShadowType shadowType = ShadowType.OUT;
// RadioButton painting appears to be special cased to pass
// SELECTED into the ENGINE even though text colors are PRESSED.
if ((state & SynthConstants.SELECTED) != 0) {
gtkState = SynthConstants.SELECTED;
}
if (gtkState == SynthConstants.SELECTED) {
shadowType = ShadowType.IN;
}
ENGINE.paintOption(g, context, Region.RADIO_BUTTON, gtkState,
shadowType, "radiobutton", x, y, w, h);
}
public void paintMenuArrowIcon(SynthContext context, Graphics g,
int state, int x, int y, int w, int h, ArrowType dir) {
int gtkState = GTKLookAndFeel.synthStateToGTKState(
context.getRegion(), state);
ShadowType shadow = ShadowType.OUT;
if (gtkState == SynthConstants.MOUSE_OVER) {
shadow = ShadowType.IN;
}
ENGINE.paintArrow(g, context, Region.MENU_ITEM, gtkState, shadow,
dir, "menuitem", x + 3, y + 3, 7, 7);
}
public void paintMenuItemArrowIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
// Don't paint anything. We are just reserving space so we align the
// menu items correctly.
}
public void paintCheckBoxMenuItemArrowIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
// Don't paint anything. We are just reserving space so we align the
// menu items correctly.
}
public void paintRadioButtonMenuItemArrowIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
// Don't paint anything. We are just reserving space so we align the
// menu items correctly.
}
public void paintCheckBoxMenuItemCheckIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
ShadowType shadowType = ShadowType.OUT;
int gtkState = GTKLookAndFeel.synthStateToGTKState(
context.getRegion(), state);
if ((state & SynthConstants.MOUSE_OVER) != 0) {
gtkState = SynthConstants.MOUSE_OVER;
}
if ((state & SynthConstants.SELECTED) != 0) {
shadowType = ShadowType.IN;
}
ENGINE.paintCheck(g, context, Region.CHECK_BOX_MENU_ITEM,
gtkState, shadowType, "check", x, y, w, h);
}
public void paintRadioButtonMenuItemCheckIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
int gtkState = GTKLookAndFeel.synthStateToGTKState(
context.getRegion(), state);
if ((state & SynthConstants.MOUSE_OVER) != 0) {
gtkState = SynthConstants.MOUSE_OVER;
}
ShadowType shadowType = ShadowType.OUT;
if ((state & SynthConstants.SELECTED) != 0) {
shadowType = ShadowType.IN;
}
ENGINE.paintOption(g, context, Region.RADIO_BUTTON_MENU_ITEM,
gtkState, shadowType, "option", x, y, w, h);
}
public void paintToolBarHandleIcon(SynthContext context, Graphics g,
int state, int x, int y, int w, int h, Orientation orientation) {
int gtkState = GTKLookAndFeel.synthStateToGTKState(
context.getRegion(), state);
// The orientation parameter passed down by Synth refers to the
// orientation of the toolbar, but the one we pass to GTK refers
// to the orientation of the handle. Therefore, we need to swap
// the value here: horizontal toolbars have vertical handles, and
// vice versa.
orientation = (orientation == Orientation.HORIZONTAL) ?
Orientation.VERTICAL : Orientation.HORIZONTAL;
ENGINE.paintHandle(g, context, Region.TOOL_BAR, gtkState,
ShadowType.OUT, "handlebox", x, y, w, h, orientation);
}
public void paintAscendingSortIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED,
ShadowType.IN, ArrowType.UP, "arrow", x, y, w, h);
}
public void paintDescendingSortIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED,
ShadowType.IN, ArrowType.DOWN, "arrow", x, y, w, h);
}
/*
* Fill an area with color determined from this context's Style using the
* specified GTKColorType
*/
private void fillArea(SynthContext context, Graphics g,
int x, int y, int w, int h, ColorType colorType) {
if (context.getComponent().isOpaque()) {
Region id = context.getRegion();
int gtkState = GTKLookAndFeel.synthStateToGTKState(id,
context.getComponentState());
GTKStyle style = (GTKStyle)context.getStyle();
g.setColor(style.getGTKColor(context, gtkState, colorType));
g.fillRect(x, y, w, h);
}
}
// Refer to GTKLookAndFeel for details on this.
static class ListTableFocusBorder extends AbstractBorder implements
UIResource {
private boolean selectedCell;
public static ListTableFocusBorder getSelectedCellBorder() {
return new ListTableFocusBorder(true);
}
public static ListTableFocusBorder getUnselectedCellBorder() {
return new ListTableFocusBorder(false);
}
public ListTableFocusBorder(boolean selectedCell) {
this.selectedCell = selectedCell;
}
private SynthContext getContext(Component c) {
SynthContext context = null;
Component parent = c;
while(parent != null &&
!(parent instanceof JTable) &&
!(parent instanceof JList)) {
parent = parent.getParent();
}
ComponentUI ui = null;
if (parent instanceof JTable) {
ui = ((JTable)parent).getUI();
} else if (parent instanceof JList) {
ui = ((JList)parent).getUI();
}
if (ui instanceof SynthUI) {
context = ((SynthUI)ui).getContext((JComponent)parent);
}
return context;
}
public void paintBorder(Component c, Graphics g, int x, int y,
int w, int h) {
SynthContext context = getContext(c);
int state = (selectedCell? SynthConstants.SELECTED:
SynthConstants.FOCUSED | SynthConstants.ENABLED);
if (context != null) {
GTKPainter.INSTANCE.paintFocus(context, g,
Region.TABLE, state, "", x, y, w, h);
} else {
if (ENGINE instanceof GTKDefaultEngine) {
g.setColor(Color.BLACK);
((GTKDefaultEngine)ENGINE)._paintFocus(
g, x, y, w, h,
GTKDefaultEngine.DEFAULT_FOCUS_PATTERN, 1);
}
}
}
public Insets getBorderInsets(Component c) {
int size = 1;
SynthContext context = getContext(c);
if (context != null) {
size = ((GTKStyle)context.getStyle()).getClassSpecificIntValue(
context, "focus-line-width", 1);
}
return new Insets(size, size, size, size);
}
public Insets getBorderInsets(Component c, Insets i) {
Insets ins = getBorderInsets(c);
if (i == null) {
return ins;
}
i.left = ins.left;
i.right = ins.right;
i.top = ins.top;
i.bottom = ins.bottom;
return i;
}
public boolean isBorderOpaque() {
return true;
}
}
}