1 /*
   2  * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.java.swing.plaf.gtk;
  26 
  27 import sun.awt.UNIXToolkit;
  28 
  29 import javax.swing.plaf.synth.*;
  30 import java.awt.*;
  31 import javax.swing.*;
  32 import javax.swing.border.*;
  33 import javax.swing.plaf.*;
  34 import com.sun.java.swing.plaf.gtk.GTKConstants.ArrowType;
  35 import com.sun.java.swing.plaf.gtk.GTKConstants.ExpanderStyle;
  36 import com.sun.java.swing.plaf.gtk.GTKConstants.Orientation;
  37 import com.sun.java.swing.plaf.gtk.GTKConstants.PositionType;
  38 import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType;
  39 import java.lang.reflect.InvocationTargetException;
  40 import java.lang.reflect.Method;
  41 
  42 /**
  43  * @author Joshua Outwater
  44  * @author Scott Violet
  45  */
  46 // Need to support:
  47 // default_outside_border: Insets when default.
  48 // interior_focus: Indicates if focus should appear inside border, or
  49 //                       outside border.
  50 // focus-line-width: Integer giving size of focus border
  51 // focus-padding: Integer giving padding between border and focus
  52 //        indicator.
  53 // focus-line-pattern:
  54 //
  55 class GTKPainter extends SynthPainter {
  56     private static final PositionType[] POSITIONS = {
  57         PositionType.BOTTOM, PositionType.RIGHT,
  58         PositionType.TOP, PositionType.LEFT
  59     };
  60 
  61     private static final ShadowType SHADOWS[] = {
  62         ShadowType.NONE, ShadowType.IN, ShadowType.OUT,
  63         ShadowType.ETCHED_IN, ShadowType.OUT
  64     };
  65 
  66     private final static GTKEngine ENGINE = GTKEngine.INSTANCE;
  67     final static GTKPainter INSTANCE = new GTKPainter();
  68 
  69     private GTKPainter() {
  70     }
  71 
  72     private String getName(SynthContext context) {
  73         return (context.getRegion().isSubregion()) ? null :
  74                context.getComponent().getName();
  75     }
  76 
  77     public void paintCheckBoxBackground(SynthContext context,
  78             Graphics g, int x, int y, int w, int h) {
  79         paintRadioButtonBackground(context, g, x, y, w, h);
  80     }
  81 
  82     public void paintCheckBoxMenuItemBackground(SynthContext context,
  83             Graphics g, int x, int y, int w, int h) {
  84         paintRadioButtonMenuItemBackground(context, g, x, y, w, h);
  85     }
  86 
  87     // FORMATTED_TEXT_FIELD
  88     public void paintFormattedTextFieldBackground(SynthContext context,
  89                                           Graphics g, int x, int y,
  90                                           int w, int h) {
  91         paintTextBackground(context, g, x, y, w, h);
  92     }
  93 
  94     //
  95     // TOOL_BAR_DRAG_WINDOW
  96     //
  97     public void paintToolBarDragWindowBackground(SynthContext context,
  98                                      Graphics g, int x, int y,
  99                                      int w, int h) {
 100         paintToolBarBackground(context, g, x, y, w, h);
 101     }
 102 
 103 
 104     //
 105     // TOOL_BAR
 106     //
 107     public void paintToolBarBackground(SynthContext context,
 108                                      Graphics g, int x, int y,
 109                                      int w, int h) {
 110         Region id = context.getRegion();
 111         int state = context.getComponentState();
 112         int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
 113         int orientation = ((JToolBar)context.getComponent()).getOrientation();
 114         synchronized (UNIXToolkit.GTK_LOCK) {
 115             if (! ENGINE.paintCachedImage(g, x, y, w, h, id,
 116                                           state, orientation))
 117             {
 118                 ENGINE.startPainting(g, x, y, w, h, id, state, orientation);
 119                 ENGINE.paintBox(g, context, id, gtkState, ShadowType.OUT,
 120                                 "handlebox_bin", x, y, w, h);
 121                 ENGINE.finishPainting();
 122             }
 123         }
 124     }
 125 
 126     public void paintToolBarContentBackground(SynthContext context,
 127                                               Graphics g,
 128                                               int x, int y, int w, int h) {
 129         Region id = context.getRegion();
 130         int orientation = ((JToolBar)context.getComponent()).getOrientation();
 131         synchronized (UNIXToolkit.GTK_LOCK) {
 132             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, orientation)) {
 133                 ENGINE.startPainting(g, x, y, w, h, id, orientation);
 134                 ENGINE.paintBox(g, context, id, SynthConstants.ENABLED,
 135                                 ShadowType.OUT, "toolbar", x, y, w, h);
 136                 ENGINE.finishPainting();
 137             }
 138         }
 139     }
 140 
 141     //
 142     // PASSWORD_FIELD
 143     //
 144     public void paintPasswordFieldBackground(SynthContext context,
 145                                      Graphics g, int x, int y,
 146                                      int w, int h) {
 147         paintTextBackground(context, g, x, y, w, h);
 148     }
 149 
 150     //
 151     // TEXT_FIELD
 152     //
 153     public void paintTextFieldBackground(SynthContext context, Graphics g,
 154                                          int x, int y, int w, int h) {
 155         if (getName(context) == "Tree.cellEditor") {
 156             paintTreeCellEditorBackground(context, g, x, y, w, h);
 157         } else {
 158             paintTextBackground(context, g, x, y, w, h);
 159         }
 160     }
 161 
 162     //
 163     // RADIO_BUTTON
 164     //
 165     // NOTE: this is called for JCheckBox too
 166     public void paintRadioButtonBackground(SynthContext context,
 167                                      Graphics g, int x, int y,
 168                                      int w, int h) {
 169         Region id = context.getRegion();
 170         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 171                 id, context.getComponentState());
 172         if (gtkState == SynthConstants.MOUSE_OVER) {
 173             synchronized (UNIXToolkit.GTK_LOCK) {
 174                 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
 175                     ENGINE.startPainting(g, x, y, w, h, id);
 176                     ENGINE.paintFlatBox(g, context, id,
 177                             SynthConstants.MOUSE_OVER, ShadowType.ETCHED_OUT,
 178                             "checkbutton", x, y, w, h, ColorType.BACKGROUND);
 179                     ENGINE.finishPainting();
 180                 }
 181             }
 182         }
 183     }
 184 
 185     //
 186     // RADIO_BUTTON_MENU_ITEM
 187     //
 188     // NOTE: this is called for JCheckBoxMenuItem too
 189     public void paintRadioButtonMenuItemBackground(SynthContext context,
 190                                      Graphics g, int x, int y,
 191                                      int w, int h) {
 192         Region id = context.getRegion();
 193         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 194                 id, context.getComponentState());
 195         if (gtkState == SynthConstants.MOUSE_OVER) {
 196             synchronized (UNIXToolkit.GTK_LOCK) {
 197                 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
 198                     ShadowType shadow = (GTKLookAndFeel.is2_2() ?
 199                         ShadowType.NONE : ShadowType.OUT);
 200                     ENGINE.startPainting(g, x, y, w, h, id);
 201                     ENGINE.paintBox(g, context, id, gtkState,
 202                             shadow, "menuitem", x, y, w, h);
 203                     ENGINE.finishPainting();
 204                 }
 205             }
 206         }
 207     }
 208 
 209     //
 210     // LABEL
 211     //
 212     public void paintLabelBackground(SynthContext context,
 213                                      Graphics g, int x, int y,
 214                                      int w, int h) {
 215         String name = getName(context);
 216         JComponent c = context.getComponent();
 217         Container  container = c.getParent();
 218 
 219         if (name == "TableHeader.renderer" ||
 220             name == "GTKFileChooser.directoryListLabel" ||
 221             name == "GTKFileChooser.fileListLabel") {
 222 
 223             paintButtonBackgroundImpl(context, g, Region.BUTTON, "button",
 224                     x, y, w, h, true, false, false, false);
 225         }
 226         /*
 227          * If the label is a ListCellRenderer and it's in a container
 228          * (CellRendererPane) which is in a JComboBox then we paint the label
 229          * as a TextField like a gtk_entry for a combobox.
 230          */
 231         else if (c instanceof ListCellRenderer &&
 232                  container != null &&
 233                  container.getParent() instanceof JComboBox ) {
 234             paintTextBackground(context, g, x, y, w, h);
 235         }
 236     }
 237 
 238     //
 239     // INTERNAL_FRAME
 240     //
 241     public void paintInternalFrameBorder(SynthContext context,
 242                                       Graphics g, int x, int y,
 243                                       int w, int h) {
 244         Metacity.INSTANCE.paintFrameBorder(context, g, x, y, w, h);
 245     }
 246 
 247     //
 248     // DESKTOP_PANE
 249     //
 250     public void paintDesktopPaneBackground(SynthContext context,
 251                                            Graphics g, int x, int y,
 252                                            int w, int h) {
 253         // Does not call into ENGINE for better performance
 254         fillArea(context, g, x, y, w, h, ColorType.BACKGROUND);
 255     }
 256 
 257     //
 258     // DESKTOP_ICON
 259     //
 260     public void paintDesktopIconBorder(SynthContext context,
 261                                            Graphics g, int x, int y,
 262                                            int w, int h) {
 263         Metacity.INSTANCE.paintFrameBorder(context, g, x, y, w, h);
 264     }
 265 
 266     public void paintButtonBackground(SynthContext context, Graphics g,
 267                                       int x, int y, int w, int h) {
 268         String name = getName(context);
 269         if (name != null && name.startsWith("InternalFrameTitlePane.")) {
 270             Metacity.INSTANCE.paintButtonBackground(context, g, x, y, w, h);
 271 
 272         } else {
 273             AbstractButton button = (AbstractButton)context.getComponent();
 274             boolean paintBG = button.isContentAreaFilled() &&
 275                               button.isBorderPainted();
 276             boolean paintFocus = button.isFocusPainted();
 277             boolean defaultCapable = (button instanceof JButton) &&
 278                     ((JButton)button).isDefaultCapable();
 279             boolean toolButton = (button.getParent() instanceof JToolBar);
 280             paintButtonBackgroundImpl(context, g, Region.BUTTON, "button",
 281                     x, y, w, h, paintBG, paintFocus, defaultCapable, toolButton);
 282         }
 283     }
 284 
 285     private void paintButtonBackgroundImpl(SynthContext context, Graphics g,
 286             Region id, String detail, int x, int y, int w, int h,
 287             boolean paintBackground, boolean paintFocus,
 288             boolean defaultCapable, boolean toolButton) {
 289         int state = context.getComponentState();
 290         synchronized (UNIXToolkit.GTK_LOCK) {
 291             if (ENGINE.paintCachedImage(g, x, y, w, h, id, state, detail,
 292                     paintBackground, paintFocus, defaultCapable, toolButton)) {
 293                 return;
 294             }
 295             ENGINE.startPainting(g, x, y, w, h, id, state, detail,
 296                 paintBackground, paintFocus, defaultCapable, toolButton);
 297 
 298             // Paint the default indicator
 299             GTKStyle style = (GTKStyle)context.getStyle();
 300             if (defaultCapable && !toolButton) {
 301                 Insets defaultInsets = style.getClassSpecificInsetsValue(
 302                         context, "default-border",
 303                         GTKStyle.BUTTON_DEFAULT_BORDER_INSETS);
 304 
 305                 if (paintBackground && (state & SynthConstants.DEFAULT) != 0) {
 306                     ENGINE.paintBox(g, context, id, SynthConstants.ENABLED,
 307                             ShadowType.IN, "buttondefault", x, y, w, h);
 308                 }
 309                 x += defaultInsets.left;
 310                 y += defaultInsets.top;
 311                 w -= (defaultInsets.left + defaultInsets.right);
 312                 h -= (defaultInsets.top + defaultInsets.bottom);
 313             }
 314 
 315             boolean interiorFocus = style.getClassSpecificBoolValue(
 316                     context, "interior-focus", true);
 317             int focusSize = style.getClassSpecificIntValue(
 318                     context, "focus-line-width",1);
 319             int focusPad = style.getClassSpecificIntValue(
 320                     context, "focus-padding", 1);
 321 
 322             int totalFocusSize = focusSize + focusPad;
 323             int xThickness = style.getXThickness();
 324             int yThickness = style.getYThickness();
 325 
 326             // Render the box.
 327             if (!interiorFocus &&
 328                     (state & SynthConstants.FOCUSED) == SynthConstants.FOCUSED) {
 329                 x += totalFocusSize;
 330                 y += totalFocusSize;
 331                 w -= 2 * totalFocusSize;
 332                 h -= 2 * totalFocusSize;
 333             }
 334 
 335             int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
 336             boolean paintBg;
 337             if (toolButton) {
 338                 // Toolbar buttons should only have their background painted
 339                 // in the PRESSED, SELECTED, or MOUSE_OVER states.
 340                 paintBg =
 341                     (gtkState != SynthConstants.ENABLED) &&
 342                     (gtkState != SynthConstants.DISABLED);
 343             } else {
 344                 // Otherwise, always paint the button's background, unless
 345                 // the user has overridden it and we're in the ENABLED state.
 346                 paintBg =
 347                     paintBackground ||
 348                     (gtkState != SynthConstants.ENABLED);
 349             }
 350             if (paintBg) {
 351                 ShadowType shadowType = ShadowType.OUT;
 352                 if ((state & (SynthConstants.PRESSED |
 353                               SynthConstants.SELECTED)) != 0) {
 354                     shadowType = ShadowType.IN;
 355                 }
 356                 ENGINE.paintBox(g, context, id, gtkState,
 357                         shadowType, detail, x, y, w, h);
 358             }
 359 
 360             // focus
 361             if (paintFocus && (state & SynthConstants.FOCUSED) != 0) {
 362                 if (interiorFocus) {
 363                     x += xThickness + focusPad;
 364                     y += yThickness + focusPad;
 365                     w -= 2 * (xThickness + focusPad);
 366                     h -= 2 * (yThickness + focusPad);
 367                 } else {
 368                     x -= totalFocusSize;
 369                     y -= totalFocusSize;
 370                     w += 2 * totalFocusSize;
 371                     h += 2 * totalFocusSize;
 372                 }
 373                 ENGINE.paintFocus(g, context, id, gtkState, detail, x, y, w, h);
 374             }
 375             ENGINE.finishPainting();
 376         }
 377     }
 378 
 379     //
 380     // ARROW_BUTTON
 381     //
 382     public void paintArrowButtonForeground(SynthContext context, Graphics g,
 383                                            int x, int y, int w, int h,
 384                                            int direction) {
 385         Region id = context.getRegion();
 386         Component c = context.getComponent();
 387         String name = c.getName();
 388 
 389         ArrowType arrowType = null;
 390         switch (direction) {
 391             case SwingConstants.NORTH:
 392                 arrowType = ArrowType.UP; break;
 393             case SwingConstants.SOUTH:
 394                 arrowType = ArrowType.DOWN; break;
 395             case SwingConstants.EAST:
 396                 arrowType = ArrowType.RIGHT; break;
 397             case SwingConstants.WEST:
 398                 arrowType = ArrowType.LEFT; break;
 399         }
 400 
 401         String detail = "arrow";
 402         if ((name == "ScrollBar.button") || (name == "TabbedPane.button")) {
 403             if (arrowType == ArrowType.UP || arrowType == ArrowType.DOWN) {
 404                 detail = "vscrollbar";
 405             } else {
 406                 detail = "hscrollbar";
 407             }
 408         } else if (name == "Spinner.nextButton" ||
 409                    name == "Spinner.previousButton") {
 410             detail = "spinbutton";
 411         } else if (name != "ComboBox.arrowButton") {
 412             assert false : "unexpected name: " + name;
 413         }
 414 
 415         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 416                 id, context.getComponentState());
 417         ShadowType shadowType = (gtkState == SynthConstants.PRESSED ?
 418             ShadowType.IN : ShadowType.OUT);
 419         synchronized (UNIXToolkit.GTK_LOCK) {
 420             if (ENGINE.paintCachedImage(g, x, y, w, h,
 421                     gtkState, name, direction)) {
 422                 return;
 423             }
 424             ENGINE.startPainting(g, x, y, w, h, gtkState, name, direction);
 425             ENGINE.paintArrow(g, context, id, gtkState,
 426                     shadowType, arrowType, detail, x, y, w, h);
 427             ENGINE.finishPainting();
 428         }
 429     }
 430 
 431     public void paintArrowButtonBackground(SynthContext context,
 432             Graphics g, int x, int y, int w, int h) {
 433         Region id = context.getRegion();
 434         AbstractButton button = (AbstractButton)context.getComponent();
 435 
 436         String name = button.getName();
 437         String detail = "button";
 438         int direction = SwingConstants.CENTER;
 439         if ((name == "ScrollBar.button") || (name == "TabbedPane.button")) {
 440             Integer prop = (Integer)
 441                 button.getClientProperty("__arrow_direction__");
 442             direction = (prop != null) ?
 443                 prop.intValue() : SwingConstants.WEST;
 444             switch (direction) {
 445             default:
 446             case SwingConstants.EAST:
 447             case SwingConstants.WEST:
 448                 detail = "hscrollbar";
 449                 break;
 450             case SwingConstants.NORTH:
 451             case SwingConstants.SOUTH:
 452                 detail = "vscrollbar";
 453                 break;
 454             }
 455         } else if (name == "Spinner.previousButton") {
 456             detail = "spinbutton_down";
 457         } else if (name == "Spinner.nextButton") {
 458             detail = "spinbutton_up";
 459         } else if (name != "ComboBox.arrowButton") {
 460             assert false : "unexpected name: " + name;
 461         }
 462 
 463         int state = context.getComponentState();
 464         synchronized (UNIXToolkit.GTK_LOCK) {
 465             if (ENGINE.paintCachedImage(g, x, y, w, h, id,
 466                                         state, detail, direction))
 467             {
 468                 return;
 469             }
 470             ENGINE.startPainting(g, x, y, w, h, id,
 471                                  state, detail, direction);
 472 
 473             if (detail.startsWith("spin")) {
 474                 /*
 475                  * The ubuntulooks engine (and presumably others) expect us to
 476                  * first draw the full "spinbutton" background, and then draw
 477                  * the individual "spinbutton_up/down" buttons on top of that.
 478                  * Note that it is the state of the JSpinner (not its arrow
 479                  * button) that determines how we draw this background.
 480                  */
 481                 int spinState = button.getParent().isEnabled() ?
 482                     SynthConstants.ENABLED : SynthConstants.DISABLED;
 483                 int mody = (detail == "spinbutton_up") ? y : y-h;
 484                 int modh = h*2;
 485                 ENGINE.paintBox(g, context, id, spinState,
 486                                 ShadowType.IN, "spinbutton",
 487                                 x, mody, w, modh);
 488             }
 489 
 490             int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
 491             ShadowType shadowType = ShadowType.OUT;
 492             if ((gtkState & (SynthConstants.PRESSED |
 493                              SynthConstants.SELECTED)) != 0)
 494             {
 495                 shadowType = ShadowType.IN;
 496             }
 497             ENGINE.paintBox(g, context, id, gtkState,
 498                             shadowType, detail,
 499                             x, y, w, h);
 500 
 501             ENGINE.finishPainting();
 502         }
 503     }
 504 
 505 
 506     //
 507     // LIST
 508     //
 509     public void paintListBackground(SynthContext context, Graphics g,
 510                                     int x, int y, int w, int h) {
 511         // Does not call into ENGINE for better performance
 512         fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
 513     }
 514 
 515     public void paintMenuBarBackground(SynthContext context, Graphics g,
 516                                        int x, int y, int w, int h) {
 517         Region id = context.getRegion();
 518         synchronized (UNIXToolkit.GTK_LOCK) {
 519             if (ENGINE.paintCachedImage(g, x, y, w, h, id)) {
 520                 return;
 521             }
 522             GTKStyle style = (GTKStyle)context.getStyle();
 523             int shadow = style.getClassSpecificIntValue(
 524                     context, "shadow-type", 2);
 525             ShadowType shadowType = SHADOWS[shadow];
 526             int gtkState = GTKLookAndFeel.synthStateToGTKState(
 527                     id, context.getComponentState());
 528             ENGINE.startPainting(g, x, y, w, h, id);
 529             ENGINE.paintBox(g, context, id, gtkState,
 530                 shadowType, "menubar", x, y, w, h);
 531             ENGINE.finishPainting();
 532         }
 533     }
 534 
 535     //
 536     // MENU
 537     //
 538     public void paintMenuBackground(SynthContext context,
 539                                      Graphics g,
 540                                      int x, int y, int w, int h) {
 541         paintMenuItemBackground(context, g, x, y, w, h);
 542     }
 543 
 544     // This is called for both MENU and MENU_ITEM
 545     public void paintMenuItemBackground(SynthContext context,
 546                                      Graphics g,
 547                                      int x, int y, int w, int h) {
 548         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 549                 context.getRegion(), context.getComponentState());
 550         if (gtkState == SynthConstants.MOUSE_OVER) {
 551             Region id = Region.MENU_ITEM;
 552             synchronized (UNIXToolkit.GTK_LOCK) {
 553                 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
 554                     ShadowType shadow = (GTKLookAndFeel.is2_2() ?
 555                         ShadowType.NONE : ShadowType.OUT);
 556                     ENGINE.startPainting(g, x, y, w, h, id);
 557                     ENGINE.paintBox(g, context, id, gtkState, shadow,
 558                             "menuitem", x, y, w, h);
 559                     ENGINE.finishPainting();
 560                 }
 561             }
 562         }
 563     }
 564 
 565     public void paintPopupMenuBackground(SynthContext context, Graphics g,
 566                                         int x, int y, int w, int h) {
 567         Region id = context.getRegion();
 568         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 569                 id, context.getComponentState());
 570         synchronized (UNIXToolkit.GTK_LOCK) {
 571             if (ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState)) {
 572                 return;
 573             }
 574             ENGINE.startPainting(g, x, y, w, h, id, gtkState);
 575             ENGINE.paintBox(g, context, id, gtkState,
 576                     ShadowType.OUT, "menu", x, y, w, h);
 577 
 578             GTKStyle style = (GTKStyle)context.getStyle();
 579             int xThickness = style.getXThickness();
 580             int yThickness = style.getYThickness();
 581             ENGINE.paintBackground(g, context, id, gtkState,
 582                     style.getGTKColor(context, gtkState, GTKColorType.BACKGROUND),
 583                     x + xThickness, y + yThickness,
 584                     w - xThickness - xThickness, h - yThickness - yThickness);
 585             ENGINE.finishPainting();
 586         }
 587     }
 588 
 589     public void paintProgressBarBackground(SynthContext context,
 590                                             Graphics g,
 591                                             int x, int y, int w, int h) {
 592         Region id = context.getRegion();
 593         synchronized (UNIXToolkit.GTK_LOCK) {
 594             if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
 595                 ENGINE.startPainting(g, x, y, w, h, id);
 596                 ENGINE.paintBox(g, context, id, SynthConstants.ENABLED,
 597                         ShadowType.IN, "trough", x, y, w, h);
 598                 ENGINE.finishPainting();
 599             }
 600         }
 601     }
 602 
 603     public void paintProgressBarForeground(SynthContext context, Graphics g,
 604                                             int x, int y, int w, int h,
 605                                             int orientation) {
 606         Region id = context.getRegion();
 607         synchronized (UNIXToolkit.GTK_LOCK) {
 608             // Note that we don't call paintCachedImage() here.  Since the
 609             // progress bar foreground is painted differently for each value
 610             // it would be wasteful to try to cache an image for each state,
 611             // so instead we simply avoid caching in this case.
 612             if (w <= 0 || h <= 0) {
 613                 return;
 614             }
 615             ENGINE.startPainting(g, x, y, w, h, id, "fg");
 616             ENGINE.paintBox(g, context, id, SynthConstants.MOUSE_OVER,
 617                             ShadowType.OUT, "bar", x, y, w, h);
 618             ENGINE.finishPainting(false); // don't bother caching the image
 619         }
 620     }
 621 
 622     public void paintViewportBorder(SynthContext context, Graphics g,
 623                                            int x, int y, int w, int h) {
 624         Region id = context.getRegion();
 625         synchronized (UNIXToolkit.GTK_LOCK) {
 626             if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
 627                 ENGINE.startPainting(g, x, y, w, h, id);
 628                 ENGINE.paintShadow(g, context, id, SynthConstants.ENABLED,
 629                         ShadowType.IN, "scrolled_window", x, y, w, h);
 630                 ENGINE.finishPainting();
 631             }
 632         }
 633     }
 634 
 635     public void paintSeparatorBackground(SynthContext context,
 636                                           Graphics g,
 637                                           int x, int y, int w, int h,
 638                                          int orientation) {
 639         Region id = context.getRegion();
 640         int state = context.getComponentState();
 641         JComponent c = context.getComponent();
 642 
 643         /*
 644          * Note: In theory, the style's x/y thickness values would determine
 645          * the width of the separator content.  In practice, however, some
 646          * engines will render a line that is wider than the corresponding
 647          * thickness value.  For example, ubuntulooks reports x/y thickness
 648          * values of 1 for separators, but always renders a 2-pixel wide line.
 649          * As a result of all this, we need to be careful not to restrict
 650          * the w/h values below too much, so that the full thickness of the
 651          * rendered line will be captured by our image caching code.
 652          */
 653         String detail;
 654         if (c instanceof JToolBar.Separator) {
 655             /*
 656              * GTK renders toolbar separators differently in that an
 657              * artificial padding is added to each end of the separator.
 658              * The value of 0.2f below is derived from the source code of
 659              * gtktoolbar.c in the current version of GTK+ (2.8.20 at the
 660              * time of this writing).  Specifically, the relevant values are:
 661              *     SPACE_LINE_DIVISION 10.0
 662              *     SPACE_LINE_START     2.0
 663              *     SPACE_LINE_END       8.0
 664              * These are used to determine the distance from the top (or left)
 665              * edge of the toolbar to the other edge.  So for example, the
 666              * starting/top point of a vertical separator is 2/10 of the
 667              * height of a horizontal toolbar away from the top edge, which
 668              * is how we arrive at 0.2f below.  Likewise, the ending/bottom
 669              * point is 8/10 of the height away from the top edge, or in other
 670              * words, it is 2/10 away from the bottom edge, which is again
 671              * how we arrive at the 0.2f value below.
 672              *
 673              * The separator is also centered horizontally or vertically,
 674              * depending on its orientation.  This was determined empirically
 675              * and by examining the code referenced above.
 676              */
 677             detail = "toolbar";
 678             float pct = 0.2f;
 679             JToolBar.Separator sep = (JToolBar.Separator)c;
 680             Dimension size = sep.getSeparatorSize();
 681             GTKStyle style = (GTKStyle)context.getStyle();
 682             if (orientation == JSeparator.HORIZONTAL) {
 683                 x += (int)(w * pct);
 684                 w -= (int)(w * pct * 2);
 685                 y += (size.height - style.getYThickness()) / 2;
 686             } else {
 687                 y += (int)(h * pct);
 688                 h -= (int)(h * pct * 2);
 689                 x += (size.width - style.getXThickness()) / 2;
 690             }
 691         } else {
 692             // For regular/menu separators, we simply subtract out the insets.
 693             detail = "separator";
 694             Insets insets = c.getInsets();
 695             x += insets.left;
 696             y += insets.top;
 697             if (orientation == JSeparator.HORIZONTAL) {
 698                 w -= (insets.left + insets.right);
 699             } else {
 700                 h -= (insets.top + insets.bottom);
 701             }
 702         }
 703 
 704         synchronized (UNIXToolkit.GTK_LOCK) {
 705             if (! ENGINE.paintCachedImage(g, x, y, w, h, id,
 706                                           state, detail, orientation)) {
 707                 ENGINE.startPainting(g, x, y, w, h, id,
 708                                      state, detail, orientation);
 709                 if (orientation == JSeparator.HORIZONTAL) {
 710                     ENGINE.paintHline(g, context, id, state,
 711                                       detail, x, y, w, h);
 712                 } else {
 713                     ENGINE.paintVline(g, context, id, state,
 714                                       detail, x, y, w, h);
 715                 }
 716                 ENGINE.finishPainting();
 717             }
 718         }
 719     }
 720 
 721     public void paintSliderTrackBackground(SynthContext context,
 722                                        Graphics g,
 723                                        int x, int y, int w,int h) {
 724         Region id = context.getRegion();
 725         int state = context.getComponentState();
 726 
 727         // For focused sliders, we paint focus rect outside the bounds passed.
 728         // Need to adjust for that.
 729         boolean focused = ((state & SynthConstants.FOCUSED) != 0);
 730         int focusSize = 0;
 731         if (focused) {
 732             GTKStyle style = (GTKStyle)context.getStyle();
 733             focusSize = style.getClassSpecificIntValue(
 734                                 context, "focus-line-width", 1) +
 735                         style.getClassSpecificIntValue(
 736                                 context, "focus-padding", 1);
 737             x -= focusSize;
 738             y -= focusSize;
 739             w += focusSize * 2;
 740             h += focusSize * 2;
 741         }
 742 
 743         // The ubuntulooks engine paints slider troughs differently depending
 744         // on the current slider value and its component orientation.
 745         JSlider slider = (JSlider)context.getComponent();
 746         double value = slider.getValue();
 747         double min = slider.getMinimum();
 748         double max = slider.getMaximum();
 749         double visible = 20; // not used for sliders; any value will work
 750 
 751         synchronized (UNIXToolkit.GTK_LOCK) {
 752             // Note that we don't call paintCachedImage() here.  Since some
 753             // engines (e.g. ubuntulooks) paint the slider background
 754             // differently for any given slider value, it would be wasteful
 755             // to try to cache an image for each state, so instead we simply
 756             // avoid caching in this case.
 757             if (w <= 0 || h <= 0) {
 758                 return;
 759             }
 760             ENGINE.startPainting(g, x, y, w, h, id, state, value);
 761             int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
 762             ENGINE.setRangeValue(context, id, value, min, max, visible);
 763             ENGINE.paintBox(g, context, id, gtkState, ShadowType.IN,
 764                             "trough", x + focusSize, y + focusSize,
 765                             w - 2 * focusSize, h - 2 * focusSize);
 766             if (focused) {
 767                 ENGINE.paintFocus(g, context, id, SynthConstants.ENABLED,
 768                                   "trough", x, y, w, h);
 769             }
 770             ENGINE.finishPainting(false); // don't bother caching the image
 771         }
 772     }
 773 
 774     public void paintSliderThumbBackground(SynthContext context,
 775             Graphics g, int x, int y, int w, int h, int dir) {
 776         Region id = context.getRegion();
 777         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 778                 id, context.getComponentState());
 779         synchronized (UNIXToolkit.GTK_LOCK) {
 780             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, dir)) {
 781                 Orientation orientation = (dir == JSlider.HORIZONTAL ?
 782                     Orientation.HORIZONTAL : Orientation.VERTICAL);
 783                 String detail = (dir == JSlider.HORIZONTAL ?
 784                     "hscale" : "vscale");
 785                 ENGINE.startPainting(g, x, y, w, h, id, gtkState, dir);
 786                 ENGINE.paintSlider(g, context, id, gtkState,
 787                         ShadowType.OUT, detail, x, y, w, h, orientation);
 788                 ENGINE.finishPainting();
 789             }
 790         }
 791     }
 792 
 793     //
 794     // SPINNER
 795     //
 796     public void paintSpinnerBackground(SynthContext context,
 797                                         Graphics g,
 798                                         int x, int y, int w, int h) {
 799         // This is handled in paintTextFieldBackground
 800     }
 801 
 802     //
 803     // SPLIT_PANE_DIVIDER
 804     //
 805     public void paintSplitPaneDividerBackground(SynthContext context,
 806                                        Graphics g,
 807                                        int x, int y, int w, int h) {
 808         Region id = context.getRegion();
 809         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 810                 id, context.getComponentState());
 811         JSplitPane splitPane = (JSplitPane)context.getComponent();
 812         Orientation orientation =
 813                 (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ?
 814                     Orientation.VERTICAL : Orientation.HORIZONTAL);
 815         synchronized (UNIXToolkit.GTK_LOCK) {
 816             if (! ENGINE.paintCachedImage(g, x, y, w, h,
 817                     id, gtkState, orientation)) {
 818                 ENGINE.startPainting(g, x, y, w, h, id, gtkState, orientation);
 819                 ENGINE.paintHandle(g, context, id, gtkState,
 820                         ShadowType.OUT, "paned", x, y, w, h, orientation);
 821                 ENGINE.finishPainting();
 822             }
 823         }
 824     }
 825 
 826     public void paintSplitPaneDragDivider(SynthContext context,
 827                                        Graphics g,int x, int y, int w, int h,
 828                                        int orientation) {
 829         paintSplitPaneDividerForeground(context, g, x, y, w, h, orientation);
 830     }
 831 
 832     public void paintTabbedPaneContentBackground(SynthContext context,
 833                                       Graphics g, int x, int y, int w, int h) {
 834         JTabbedPane pane = (JTabbedPane)context.getComponent();
 835         int selectedIndex = pane.getSelectedIndex();
 836         PositionType placement = GTKLookAndFeel.SwingOrientationConstantToGTK(
 837                                                         pane.getTabPlacement());
 838 
 839         int gapStart = 0;
 840         int gapSize = 0;
 841         if (selectedIndex != -1) {
 842             Rectangle tabBounds = pane.getBoundsAt(selectedIndex);
 843 
 844             if (placement == PositionType.TOP ||
 845                 placement == PositionType.BOTTOM) {
 846 
 847                 gapStart = tabBounds.x - x;
 848                 gapSize = tabBounds.width;
 849             }
 850             else {
 851                 gapStart = tabBounds.y - y;
 852                 gapSize = tabBounds.height;
 853             }
 854         }
 855 
 856         Region id = context.getRegion();
 857         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 858                 id, context.getComponentState());
 859         synchronized (UNIXToolkit.GTK_LOCK) {
 860             if (! ENGINE.paintCachedImage(g, x, y, w, h,
 861                     id, gtkState, placement, gapStart, gapSize)) {
 862                 ENGINE.startPainting(g, x, y, w, h,
 863                         id, gtkState, placement, gapStart, gapSize);
 864                 ENGINE.paintBoxGap(g, context, id, gtkState, ShadowType.OUT,
 865                         "notebook", x, y, w, h, placement, gapStart, gapSize);
 866                 ENGINE.finishPainting();
 867             }
 868         }
 869     }
 870 
 871     public void paintTabbedPaneTabBackground(SynthContext context,
 872                                            Graphics g,
 873                                            int x, int y, int w, int h,
 874                                            int tabIndex) {
 875         Region id = context.getRegion();
 876         int state = context.getComponentState();
 877         int gtkState = ((state & SynthConstants.SELECTED) != 0 ?
 878             SynthConstants.ENABLED : SynthConstants.PRESSED);
 879         JTabbedPane pane = (JTabbedPane)context.getComponent();
 880         int placement = pane.getTabPlacement();
 881 
 882         synchronized (UNIXToolkit.GTK_LOCK) {
 883             if (! ENGINE.paintCachedImage(g, x, y, w, h,
 884                     id, gtkState, placement, tabIndex)) {
 885                 PositionType side = POSITIONS[placement - 1];
 886                 ENGINE.startPainting(g, x, y, w, h,
 887                         id, gtkState, placement, tabIndex);
 888                 ENGINE.paintExtension(g, context, id, gtkState,
 889                         ShadowType.OUT, "tab", x, y, w, h, side, tabIndex);
 890                 ENGINE.finishPainting();
 891             }
 892         }
 893     }
 894 
 895     //
 896     // TEXT_PANE
 897     //
 898     public void paintTextPaneBackground(SynthContext context, Graphics g,
 899                                         int x, int y, int w, int h) {
 900         paintTextAreaBackground(context, g, x, y, w, h);
 901     }
 902 
 903     //
 904     // EDITOR_PANE
 905     //
 906     public void paintEditorPaneBackground(SynthContext context, Graphics g,
 907                                           int x, int y, int w, int h) {
 908         paintTextAreaBackground(context, g, x, y, w, h);
 909     }
 910 
 911     //
 912     // TEXT_AREA
 913     //
 914     public void paintTextAreaBackground(SynthContext context, Graphics g,
 915                                         int x, int y, int w, int h) {
 916         // Does not call into ENGINE for better performance
 917         fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
 918     }
 919 
 920     //
 921     // TEXT_FIELD
 922     //
 923     // NOTE: Combobox and Label, Password and FormattedTextField calls this
 924     // too.
 925     private void paintTextBackground(SynthContext context, Graphics g,
 926                                      int x, int y, int w, int h) {
 927         // Text is odd in that it uses the TEXT_BACKGROUND vs BACKGROUND.
 928         JComponent c = context.getComponent();
 929         Container container = c.getParent();
 930         Container containerParent = null;
 931         GTKStyle style = (GTKStyle)context.getStyle();
 932         Region id = context.getRegion();
 933         int state = context.getComponentState();
 934 
 935         if (c instanceof ListCellRenderer && container != null) {
 936             containerParent = container.getParent();
 937             if (containerParent instanceof JComboBox
 938                     && containerParent.hasFocus()) {
 939                 state |= SynthConstants.FOCUSED;
 940             }
 941         }
 942 
 943         synchronized (UNIXToolkit.GTK_LOCK) {
 944             if (ENGINE.paintCachedImage(g, x, y, w, h, id, state)) {
 945                 return;
 946             }
 947 
 948             int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
 949             int focusSize = 0;
 950             boolean interiorFocus = style.getClassSpecificBoolValue(
 951                     context, "interior-focus", true);
 952 
 953             focusSize = style.getClassSpecificIntValue(context,
 954                     "focus-line-width",1);
 955             if (!interiorFocus && (state & SynthConstants.FOCUSED) != 0) {
 956                 x += focusSize;
 957                 y += focusSize;
 958                 w -= 2 * focusSize;
 959                 h -= 2 * focusSize;
 960             }
 961 
 962             int xThickness = style.getXThickness();
 963             int yThickness = style.getYThickness();
 964 
 965             ENGINE.startPainting(g, x, y, w, h, id, state);
 966             ENGINE.paintShadow(g, context, id, gtkState,
 967                                ShadowType.IN, "entry", x, y, w, h);
 968             ENGINE.paintFlatBox(g, context, id,
 969                                 gtkState, ShadowType.NONE, "entry_bg",
 970                                 x + xThickness,
 971                                 y + yThickness,
 972                                 w - (2 * xThickness),
 973                                 h - (2 * yThickness),
 974                                 ColorType.TEXT_BACKGROUND);
 975 
 976             if (focusSize > 0 && (state & SynthConstants.FOCUSED) != 0) {
 977                 if (!interiorFocus) {
 978                     x -=  focusSize;
 979                     y -=  focusSize;
 980                     w +=  2 * focusSize;
 981                     h +=  2 * focusSize;
 982                 } else {
 983                     if (containerParent instanceof JComboBox) {
 984                         x += (focusSize + 2);
 985                         y += (focusSize + 1);
 986                         w -= (2 * focusSize + 1);
 987                         h -= (2 * focusSize + 2);
 988                     } else {
 989                         x += focusSize;
 990                         y += focusSize;
 991                         w -= 2 * focusSize;
 992                         h -= 2 * focusSize;
 993                     }
 994                 }
 995                 ENGINE.paintFocus(g, context, id, gtkState,
 996                         "entry", x, y, w, h);
 997             }
 998             ENGINE.finishPainting();
 999         }
1000     }
1001 
1002     private void paintTreeCellEditorBackground(SynthContext context, Graphics g,
1003                                                int x, int y, int w, int h) {
1004         Region id = context.getRegion();
1005         int gtkState = GTKLookAndFeel.synthStateToGTKState(
1006                 id, context.getComponentState());
1007         synchronized (UNIXToolkit.GTK_LOCK) {
1008             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState)) {
1009                 ENGINE.startPainting(g, x, y, w, h, id, gtkState);
1010                 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE,
1011                         "entry_bg", x, y, w, h, ColorType.TEXT_BACKGROUND);
1012                 ENGINE.finishPainting();
1013             }
1014         }
1015     }
1016 
1017 
1018     //
1019     // ROOT_PANE
1020     //
1021     public void paintRootPaneBackground(SynthContext context, Graphics g,
1022                                         int x, int y, int w, int h) {
1023         // Does not call into ENGINE for better performance
1024         fillArea(context, g, x, y, w, h, GTKColorType.BACKGROUND);
1025     }
1026 
1027     //
1028     // TOGGLE_BUTTON
1029     //
1030     public void paintToggleButtonBackground(SynthContext context,
1031                                             Graphics g,
1032                                             int x, int y, int w, int h) {
1033         Region id = context.getRegion();
1034         JToggleButton toggleButton = (JToggleButton)context.getComponent();
1035         boolean paintBG = toggleButton.isContentAreaFilled() &&
1036                           toggleButton.isBorderPainted();
1037         boolean paintFocus = toggleButton.isFocusPainted();
1038         boolean toolButton = (toggleButton.getParent() instanceof JToolBar);
1039         paintButtonBackgroundImpl(context, g, id, "button",
1040                                   x, y, w, h,
1041                                   paintBG, paintFocus, false, toolButton);
1042     }
1043 
1044 
1045     //
1046     // SCROLL_BAR
1047     //
1048     public void paintScrollBarBackground(SynthContext context,
1049                                           Graphics g,
1050                                           int x, int y, int w,int h) {
1051         Region id = context.getRegion();
1052         boolean focused =
1053                 (context.getComponentState() & SynthConstants.FOCUSED) != 0;
1054         synchronized (UNIXToolkit.GTK_LOCK) {
1055             if (ENGINE.paintCachedImage(g, x, y, w, h, id, focused)) {
1056                 return;
1057             }
1058             ENGINE.startPainting(g, x, y, w, h, id, focused);
1059 
1060             // Note: the scrollbar insets already include the "trough-border",
1061             // which is needed to position the scrollbar buttons properly.
1062             // But when we render, we need to take the trough border out
1063             // of the equation so that we paint the entire area covered by
1064             // the trough border and the scrollbar content itself.
1065             Insets insets = context.getComponent().getInsets();
1066             GTKStyle style = (GTKStyle)context.getStyle();
1067             int troughBorder =
1068                 style.getClassSpecificIntValue(context, "trough-border", 1);
1069             insets.left   -= troughBorder;
1070             insets.right  -= troughBorder;
1071             insets.top    -= troughBorder;
1072             insets.bottom -= troughBorder;
1073 
1074             ENGINE.paintBox(g, context, id, SynthConstants.PRESSED,
1075                             ShadowType.IN, "trough",
1076                             x + insets.left,
1077                             y + insets.top,
1078                             w - insets.left - insets.right,
1079                             h - insets.top - insets.bottom);
1080 
1081             if (focused) {
1082                 ENGINE.paintFocus(g, context, id,
1083                         SynthConstants.ENABLED, "trough", x, y, w, h);
1084             }
1085             ENGINE.finishPainting();
1086         }
1087     }
1088 
1089 
1090     //
1091     // SCROLL_BAR_THUMB
1092     //
1093     public void paintScrollBarThumbBackground(SynthContext context,
1094             Graphics g, int x, int y, int w, int h, int dir) {
1095         Region id = context.getRegion();
1096         int gtkState = GTKLookAndFeel.synthStateToGTKState(
1097                 id, context.getComponentState());
1098 
1099         // The clearlooks engine paints scrollbar thumbs differently
1100         // depending on the current scroll value (specifically, it will avoid
1101         // rendering a certain line when the thumb is at the starting or
1102         // ending position).  Therefore, we normalize the current value to
1103         // the range [0,100] here and then pass it down to setRangeValue()
1104         // so that the native widget is configured appropriately.  Note that
1105         // there are really only four values that matter (min, middle, max,
1106         // or fill) so we restrict to one of those four values to avoid
1107         // blowing out the image cache.
1108         JScrollBar sb = (JScrollBar)context.getComponent();
1109         boolean rtl =
1110             sb.getOrientation() == JScrollBar.HORIZONTAL &&
1111             !sb.getComponentOrientation().isLeftToRight();
1112         double min = 0;
1113         double max = 100;
1114         double visible = 20;
1115         double value;
1116         if (sb.getMaximum() - sb.getMinimum() == sb.getVisibleAmount()) {
1117             // In this case, the thumb fills the entire track, so it is
1118             // touching both ends at the same time
1119             value = 0;
1120             visible = 100;
1121         } else if (sb.getValue() == sb.getMinimum()) {
1122             // At minimum
1123             value = rtl ? 100 : 0;
1124         } else if (sb.getValue() >= sb.getMaximum() - sb.getVisibleAmount()) {
1125             // At maximum
1126             value = rtl ? 0 : 100;
1127         } else {
1128             // Somewhere in between
1129             value = 50;
1130         }
1131 
1132         synchronized (UNIXToolkit.GTK_LOCK) {
1133             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState,
1134                                           dir, value, visible, rtl))
1135             {
1136                 ENGINE.startPainting(g, x, y, w, h, id, gtkState,
1137                                      dir, value, visible, rtl);
1138                 Orientation orientation = (dir == JScrollBar.HORIZONTAL ?
1139                     Orientation.HORIZONTAL : Orientation.VERTICAL);
1140                 ENGINE.setRangeValue(context, id, value, min, max, visible);
1141                 ENGINE.paintSlider(g, context, id, gtkState,
1142                         ShadowType.OUT, "slider", x, y, w, h, orientation);
1143                 ENGINE.finishPainting();
1144             }
1145         }
1146     }
1147 
1148     //
1149     // TOOL_TIP
1150     //
1151     public void paintToolTipBackground(SynthContext context, Graphics g,
1152                                         int x, int y, int w,int h) {
1153         Region id = context.getRegion();
1154         synchronized (UNIXToolkit.GTK_LOCK) {
1155             if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
1156                 ENGINE.startPainting(g, x, y, w, h, id);
1157                 ENGINE.paintFlatBox(g, context, id, SynthConstants.ENABLED,
1158                         ShadowType.OUT, "tooltip", x, y, w, h,
1159                         ColorType.BACKGROUND);
1160                 ENGINE.finishPainting();
1161             }
1162         }
1163     }
1164 
1165 
1166     //
1167     // TREE_CELL
1168     //
1169     public void paintTreeCellBackground(SynthContext context, Graphics g,
1170                                         int x, int y, int w, int h) {
1171         Region id = context.getRegion();
1172         int state = context.getComponentState();
1173         int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
1174         synchronized (UNIXToolkit.GTK_LOCK) {
1175             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, state)) {
1176                 ENGINE.startPainting(g, x, y, w, h, id, state);
1177                 // the string arg should alternate based on row being painted,
1178                 // but we currently don't pass that in.
1179                 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE,
1180                         "cell_odd", x, y, w, h, ColorType.TEXT_BACKGROUND);
1181                 ENGINE.finishPainting();
1182             }
1183         }
1184     }
1185 
1186     public void paintTreeCellFocus(SynthContext context, Graphics g,
1187                                     int x, int y, int w, int h) {
1188         Region id = Region.TREE_CELL;
1189         int state = context.getComponentState();
1190         paintFocus(context, g, id, state, "treeview", x, y, w, h);
1191     }
1192 
1193 
1194     //
1195     // TREE
1196     //
1197     public void paintTreeBackground(SynthContext context, Graphics g,
1198                                     int x, int y, int w, int h) {
1199         // As far as I can tell, these don't call into the ENGINE.
1200         fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
1201     }
1202 
1203 
1204     //
1205     // VIEWPORT
1206     //
1207     public void paintViewportBackground(SynthContext context, Graphics g,
1208                                         int x, int y, int w, int h) {
1209         // As far as I can tell, these don't call into the ENGINE.
1210         // Also note that you don't want this to call into the ENGINE
1211         // as if it where to paint a background JViewport wouldn't scroll
1212         // correctly.
1213         fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
1214     }
1215 
1216     void paintFocus(SynthContext context, Graphics g, Region id,
1217             int state, String detail, int x, int y, int w, int h) {
1218         int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
1219         synchronized (UNIXToolkit.GTK_LOCK) {
1220             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, "focus")) {
1221                 ENGINE.startPainting(g, x, y, w, h, id, gtkState, "focus");
1222                 ENGINE.paintFocus(g, context, id, gtkState, detail, x, y, w, h);
1223                 ENGINE.finishPainting();
1224             }
1225         }
1226     }
1227 
1228     void paintMetacityElement(SynthContext context, Graphics g,
1229             int gtkState, String detail, int x, int y, int w, int h,
1230             ShadowType shadow, ArrowType direction) {
1231         synchronized (UNIXToolkit.GTK_LOCK) {
1232             if (! ENGINE.paintCachedImage(
1233                     g, x, y, w, h, gtkState, detail, shadow, direction)) {
1234                 ENGINE.startPainting(
1235                         g, x, y, w, h, gtkState, detail, shadow, direction);
1236                 if (detail == "metacity-arrow") {
1237                     ENGINE.paintArrow(g, context, Region.INTERNAL_FRAME_TITLE_PANE,
1238                             gtkState, shadow, direction, "", x, y, w, h);
1239 
1240                 } else if (detail == "metacity-box") {
1241                     ENGINE.paintBox(g, context, Region.INTERNAL_FRAME_TITLE_PANE,
1242                             gtkState, shadow, "", x, y, w, h);
1243 
1244                 } else if (detail == "metacity-vline") {
1245                     ENGINE.paintVline(g, context, Region.INTERNAL_FRAME_TITLE_PANE,
1246                             gtkState, "", x, y, w, h);
1247                 }
1248                 ENGINE.finishPainting();
1249             }
1250         }
1251     }
1252 
1253     void paintIcon(SynthContext context, Graphics g,
1254             Method paintMethod, int x, int y, int w, int h) {
1255         int state = context.getComponentState();
1256         synchronized (UNIXToolkit.GTK_LOCK) {
1257             if (! ENGINE.paintCachedImage(g, x, y, w, h, state, paintMethod)) {
1258                 ENGINE.startPainting(g, x, y, w, h, state, paintMethod);
1259                 try {
1260                     paintMethod.invoke(this, context, g, state, x, y, w, h);
1261                 } catch (IllegalAccessException iae) {
1262                     assert false;
1263                 } catch (InvocationTargetException ite) {
1264                     assert false;
1265                 }
1266                 ENGINE.finishPainting();
1267             }
1268         }
1269     }
1270 
1271     void paintIcon(SynthContext context, Graphics g,
1272             Method paintMethod, int x, int y, int w, int h, Object direction) {
1273         int state = context.getComponentState();
1274         synchronized (UNIXToolkit.GTK_LOCK) {
1275             if (! ENGINE.paintCachedImage(g,
1276                     x, y, w, h, state, paintMethod, direction)) {
1277                 ENGINE.startPainting(g,
1278                         x, y, w, h, state, paintMethod, direction);
1279                 try {
1280                     paintMethod.invoke(this, context,
1281                             g, state, x, y, w, h, direction);
1282                 } catch (IllegalAccessException iae) {
1283                     assert false;
1284                 } catch (InvocationTargetException ite) {
1285                     assert false;
1286                 }
1287                 ENGINE.finishPainting();
1288             }
1289         }
1290     }
1291 
1292     // All icon painting methods are called from under GTK_LOCK
1293 
1294     public void paintTreeExpandedIcon(SynthContext context,
1295             Graphics g, int state, int x, int y, int w, int h) {
1296         ENGINE.paintExpander(g, context, Region.TREE,
1297                 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state),
1298                 ExpanderStyle.EXPANDED, "treeview", x, y, w, h);
1299     }
1300 
1301     public void paintTreeCollapsedIcon(SynthContext context,
1302             Graphics g, int state, int x, int y, int w, int h) {
1303         ENGINE.paintExpander(g, context, Region.TREE,
1304                 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state),
1305                 ExpanderStyle.COLLAPSED, "treeview", x, y, w, h);
1306     }
1307 
1308     public void paintCheckBoxIcon(SynthContext context,
1309             Graphics g, int state, int x, int y, int w, int h) {
1310         GTKStyle style = (GTKStyle)context.getStyle();
1311         int size = style.getClassSpecificIntValue(context,
1312                         "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE);
1313         int offset = style.getClassSpecificIntValue(context,
1314                         "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING);
1315 
1316         ENGINE.paintCheck(g, context, Region.CHECK_BOX, "checkbutton",
1317                 x+offset, y+offset, size, size);
1318     }
1319 
1320     public void paintRadioButtonIcon(SynthContext context,
1321             Graphics g, int state, int x, int y, int w, int h) {
1322         GTKStyle style = (GTKStyle)context.getStyle();
1323         int size = style.getClassSpecificIntValue(context,
1324                         "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE);
1325         int offset = style.getClassSpecificIntValue(context,
1326                         "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING);
1327 
1328         ENGINE.paintOption(g, context, Region.RADIO_BUTTON, "radiobutton",
1329                 x+offset, y+offset, size, size);
1330     }
1331 
1332     public void paintMenuArrowIcon(SynthContext context, Graphics g,
1333             int state, int x, int y, int w, int h, ArrowType dir) {
1334         int gtkState = GTKLookAndFeel.synthStateToGTKState(
1335                 context.getRegion(), state);
1336         ShadowType shadow = ShadowType.OUT;
1337         if (gtkState == SynthConstants.MOUSE_OVER) {
1338             shadow = ShadowType.IN;
1339         }
1340         ENGINE.paintArrow(g, context, Region.MENU_ITEM, gtkState, shadow,
1341                 dir, "menuitem", x + 3, y + 3, 7, 7);
1342     }
1343 
1344     public void paintCheckBoxMenuItemCheckIcon(SynthContext context,
1345             Graphics g, int state, int x, int y, int w, int h) {
1346 
1347         GTKStyle style = (GTKStyle)context.getStyle();
1348         int size = style.getClassSpecificIntValue(context,"indicator-size",
1349                 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE);
1350 
1351         ENGINE.paintCheck(g, context, Region.CHECK_BOX_MENU_ITEM, "check",
1352                 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
1353                 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
1354                 size, size);
1355     }
1356 
1357     public void paintRadioButtonMenuItemCheckIcon(SynthContext context,
1358             Graphics g, int state, int x, int y, int w, int h) {
1359 
1360         GTKStyle style = (GTKStyle)context.getStyle();
1361         int size = style.getClassSpecificIntValue(context,"indicator-size",
1362                 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE);
1363 
1364         ENGINE.paintOption(g, context, Region.RADIO_BUTTON_MENU_ITEM, "option",
1365                 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
1366                 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
1367                 size, size);
1368     }
1369 
1370     public void paintToolBarHandleIcon(SynthContext context, Graphics g,
1371             int state, int x, int y, int w, int h, Orientation orientation) {
1372         int gtkState = GTKLookAndFeel.synthStateToGTKState(
1373                 context.getRegion(), state);
1374 
1375         // The orientation parameter passed down by Synth refers to the
1376         // orientation of the toolbar, but the one we pass to GTK refers
1377         // to the orientation of the handle.  Therefore, we need to swap
1378         // the value here: horizontal toolbars have vertical handles, and
1379         // vice versa.
1380         orientation = (orientation == Orientation.HORIZONTAL) ?
1381             Orientation.VERTICAL : Orientation.HORIZONTAL;
1382 
1383         ENGINE.paintHandle(g, context, Region.TOOL_BAR, gtkState,
1384                 ShadowType.OUT, "handlebox", x, y, w, h, orientation);
1385     }
1386 
1387     public void paintAscendingSortIcon(SynthContext context,
1388             Graphics g, int state, int x, int y, int w, int h) {
1389         ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED,
1390                 ShadowType.IN, ArrowType.UP, "arrow", x, y, w, h);
1391     }
1392 
1393     public void paintDescendingSortIcon(SynthContext context,
1394             Graphics g, int state, int x, int y, int w, int h) {
1395         ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED,
1396                 ShadowType.IN, ArrowType.DOWN, "arrow", x, y, w, h);
1397     }
1398 
1399     /*
1400      * Fill an area with color determined from this context's Style using the
1401      * specified GTKColorType
1402      */
1403     private void fillArea(SynthContext context, Graphics g,
1404                           int x, int y, int w, int h, ColorType colorType) {
1405         if (context.getComponent().isOpaque()) {
1406             Region id = context.getRegion();
1407             int gtkState = GTKLookAndFeel.synthStateToGTKState(id,
1408                     context.getComponentState());
1409             GTKStyle style = (GTKStyle)context.getStyle();
1410 
1411             g.setColor(style.getGTKColor(context, gtkState, colorType));
1412             g.fillRect(x, y, w, h);
1413         }
1414     }
1415 
1416     // Refer to GTKLookAndFeel for details on this.
1417     static class ListTableFocusBorder extends AbstractBorder implements
1418                           UIResource {
1419 
1420         private boolean selectedCell;
1421         private boolean focusedCell;
1422 
1423         public static ListTableFocusBorder getSelectedCellBorder() {
1424             return new ListTableFocusBorder(true, true);
1425         }
1426 
1427         public static ListTableFocusBorder getUnselectedCellBorder() {
1428             return new ListTableFocusBorder(false, true);
1429         }
1430 
1431         public static ListTableFocusBorder getNoFocusCellBorder() {
1432             return new ListTableFocusBorder(false, false);
1433         }
1434 
1435         public ListTableFocusBorder(boolean selectedCell, boolean focusedCell) {
1436             this.selectedCell = selectedCell;
1437             this.focusedCell = focusedCell;
1438         }
1439 
1440         private SynthContext getContext(Component c) {
1441             SynthContext context = null;
1442 
1443             ComponentUI ui = null;
1444             if (c instanceof JLabel) {
1445                 ui = ((JLabel)c).getUI();
1446             }
1447 
1448             if (ui instanceof SynthUI) {
1449                 context = ((SynthUI)ui).getContext((JComponent)c);
1450             }
1451 
1452             return context;
1453         }
1454 
1455         public void paintBorder(Component c, Graphics g, int x, int y,
1456                                 int w, int h) {
1457             if (focusedCell) {
1458                 SynthContext context = getContext(c);
1459                 int state = (selectedCell? SynthConstants.SELECTED:
1460                              SynthConstants.FOCUSED | SynthConstants.ENABLED);
1461 
1462                 if (context != null) {
1463                     GTKPainter.INSTANCE.paintFocus(context, g,
1464                             Region.TABLE, state, "", x, y, w, h);
1465                 }
1466             }
1467         }
1468 
1469         public Insets getBorderInsets(Component c, Insets i) {
1470             SynthContext context = getContext(c);
1471 
1472             if (context != null) {
1473                 i = context.getStyle().getInsets(context, i);
1474             }
1475 
1476             return i;
1477         }
1478 
1479         public boolean isBorderOpaque() {
1480             return true;
1481         }
1482     }
1483 
1484     // TitledBorder implementation for GTK L&F
1485     static class TitledBorder extends AbstractBorder implements UIResource {
1486 
1487         public void paintBorder(Component c, Graphics g, int x, int y,
1488                                 int w, int h) {
1489             SynthContext context = getContext((JComponent)c);
1490             Region id = context.getRegion();
1491             int state = context.getComponentState();
1492             int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
1493 
1494             synchronized (UNIXToolkit.GTK_LOCK) {
1495                 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
1496                     ENGINE.startPainting(g, x, y, w, h, id);
1497                     ENGINE.paintShadow(g, context, id, gtkState, ShadowType.ETCHED_IN,
1498                                       "frame", x, y, w, h);
1499                     ENGINE.finishPainting();
1500                 }
1501             }
1502         }
1503 
1504         public Insets getBorderInsets(Component c, Insets i) {
1505             SynthContext context = getContext((JComponent)c);
1506             return context.getStyle().getInsets(context, i);
1507         }
1508 
1509         public boolean isBorderOpaque() {
1510             return true;
1511         }
1512 
1513         private SynthStyle getStyle(JComponent c) {
1514             return SynthLookAndFeel.getStyle(c, GTKEngine.CustomRegion.TITLED_BORDER);
1515         }
1516 
1517         private SynthContext getContext(JComponent c) {
1518             int state = SynthConstants.DEFAULT;
1519             return new SynthContext(c, GTKEngine.CustomRegion.TITLED_BORDER,
1520                                     getStyle(c), state);
1521         }
1522     }
1523 }