/*
* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.java.swing.plaf.gtk;
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;
/**
* @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);
JComponent c = context.getComponent();
Container container = c.getParent();
if (name == "TableHeader.renderer" ||
name == "GTKFileChooser.directoryListLabel" ||
name == "GTKFileChooser.fileListLabel") {
paintButtonBackgroundImpl(context, g, Region.BUTTON, "button",
x, y, w, h, true, false, false, false);
}
/*
* If the label is a ListCellRenderer and it's in a container
* (CellRendererPane) which is in a JComboBox then we paint the label
* as a TextField like a gtk_entry for a combobox.
*/
else if (c instanceof ListCellRenderer &&
container != null &&
container.getParent() instanceof JComboBox ) {
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 = 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") || (name == "TabbedPane.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 : "unexpected name: " + name;
}
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") || (name == "TabbedPane.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 : "unexpected name: " + name;
}
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) {
// Note that we don't call paintCachedImage() here. Since the
// progress bar foreground is painted differently for each value
// it would be wasteful to try to cache an image for each state,
// so instead we simply avoid caching in this case.
if (w <= 0 || h <= 0) {
return;
}
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(false); // don't bother caching the image
}
}
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 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;
}
// The ubuntulooks engine paints slider troughs differently depending
// on the current slider value and its component orientation.
JSlider slider = (JSlider)context.getComponent();
double value = slider.getValue();
double min = slider.getMinimum();
double max = slider.getMaximum();
double visible = 20; // not used for sliders; any value will work
synchronized (UNIXToolkit.GTK_LOCK) {
// Note that we don't call paintCachedImage() here. Since some
// engines (e.g. ubuntulooks) paint the slider background
// differently for any given slider value, it would be wasteful
// to try to cache an image for each state, so instead we simply
// avoid caching in this case.
if (w <= 0 || h <= 0) {
return;
}
ENGINE.startPainting(g, x, y, w, h, id, state, value);
int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
ENGINE.setRangeValue(context, id, value, min, max, visible);
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(false); // don't bother caching the image
}
}
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 - x;
gapSize = tabBounds.width;
}
else {
gapStart = tabBounds.y - y;
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_PANE
//
public void paintTextPaneBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
paintTextAreaBackground(context, g, x, y, w, h);
}
//
// EDITOR_PANE
//
public void paintEditorPaneBackground(SynthContext context, Graphics g,
int x, int y, int w, int h) {
paintTextAreaBackground(context, g, x, y, w, h);
}
//
// 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();
Container container = c.getParent();
Container containerParent = null;
GTKStyle style = (GTKStyle)context.getStyle();
Region id = context.getRegion();
int state = context.getComponentState();
if (c instanceof ListCellRenderer && container != null) {
containerParent = container.getParent();
if (containerParent instanceof JComboBox
&& containerParent.hasFocus()) {
state |= SynthConstants.FOCUSED;
}
}
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);
focusSize = style.getClassSpecificIntValue(context,
"focus-line-width",1);
if (!interiorFocus && (state & SynthConstants.FOCUSED) != 0) {
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 && (state & SynthConstants.FOCUSED) != 0) {
if (!interiorFocus) {
x -= focusSize;
y -= focusSize;
w += 2 * focusSize;
h += 2 * focusSize;
} else {
if (containerParent instanceof JComboBox) {
x += (focusSize + 2);
y += (focusSize + 1);
w -= (2 * focusSize + 1);
h -= (2 * focusSize + 2);
} else {
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());
// The clearlooks engine paints scrollbar thumbs differently
// depending on the current scroll value (specifically, it will avoid
// rendering a certain line when the thumb is at the starting or
// ending position). Therefore, we normalize the current value to
// the range [0,100] here and then pass it down to setRangeValue()
// so that the native widget is configured appropriately. Note that
// there are really only four values that matter (min, middle, max,
// or fill) so we restrict to one of those four values to avoid
// blowing out the image cache.
JScrollBar sb = (JScrollBar)context.getComponent();
boolean rtl =
sb.getOrientation() == JScrollBar.HORIZONTAL &&
!sb.getComponentOrientation().isLeftToRight();
double min = 0;
double max = 100;
double visible = 20;
double value;
if (sb.getMaximum() - sb.getMinimum() == sb.getVisibleAmount()) {
// In this case, the thumb fills the entire track, so it is
// touching both ends at the same time
value = 0;
visible = 100;
} else if (sb.getValue() == sb.getMinimum()) {
// At minimum
value = rtl ? 100 : 0;
} else if (sb.getValue() >= sb.getMaximum() - sb.getVisibleAmount()) {
// At maximum
value = rtl ? 0 : 100;
} else {
// Somewhere in between
value = 50;
}
synchronized (UNIXToolkit.GTK_LOCK) {
if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState,
dir, value, visible, rtl))
{
ENGINE.startPainting(g, x, y, w, h, id, gtkState,
dir, value, visible, rtl);
Orientation orientation = (dir == JScrollBar.HORIZONTAL ?
Orientation.HORIZONTAL : Orientation.VERTICAL);
ENGINE.setRangeValue(context, id, value, min, max, visible);
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) {
GTKStyle style = (GTKStyle)context.getStyle();
int size = style.getClassSpecificIntValue(context,
"indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE);
int offset = style.getClassSpecificIntValue(context,
"indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING);
ENGINE.paintCheck(g, context, Region.CHECK_BOX, "checkbutton",
x+offset, y+offset, size, size);
}
public void paintRadioButtonIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
GTKStyle style = (GTKStyle)context.getStyle();
int size = style.getClassSpecificIntValue(context,
"indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE);
int offset = style.getClassSpecificIntValue(context,
"indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING);
ENGINE.paintOption(g, context, Region.RADIO_BUTTON, "radiobutton",
x+offset, y+offset, size, size);
}
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 paintCheckBoxMenuItemCheckIcon(SynthContext context,
Graphics g, int state, int x, int y, int w, int h) {
GTKStyle style = (GTKStyle)context.getStyle();
int size = style.getClassSpecificIntValue(context,"indicator-size",
GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE);
ENGINE.paintCheck(g, context, Region.CHECK_BOX_MENU_ITEM, "check",
x + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
y + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
/**代码未完, 请加载全部代码(NowJava.com).**/