1 /*
   2  * Copyright (c) 2002, 2016, 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 static final GTKEngine ENGINE = GTKEngine.INSTANCE;
  67     static final 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         if (GTKLookAndFeel.is3()) {
 747             if (slider.getOrientation() == JSlider.VERTICAL) {
 748                 y += 1;
 749                 h -= 2;
 750             } else {
 751                 x += 1;
 752                 w -= 2;
 753             }
 754         }
 755         double value = slider.getValue();
 756         double min = slider.getMinimum();
 757         double max = slider.getMaximum();
 758         double visible = 20; // not used for sliders; any value will work
 759 
 760         synchronized (UNIXToolkit.GTK_LOCK) {
 761             // Note that we don't call paintCachedImage() here.  Since some
 762             // engines (e.g. ubuntulooks) paint the slider background
 763             // differently for any given slider value, it would be wasteful
 764             // to try to cache an image for each state, so instead we simply
 765             // avoid caching in this case.
 766             if (w <= 0 || h <= 0) {
 767                 return;
 768             }
 769             ENGINE.startPainting(g, x, y, w, h, id, state, value);
 770             int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
 771             ENGINE.setRangeValue(context, id, value, min, max, visible);
 772             ENGINE.paintBox(g, context, id, gtkState, ShadowType.IN,
 773                             "trough", x + focusSize, y + focusSize,
 774                             w - 2 * focusSize, h - 2 * focusSize);
 775             if (focused) {
 776                 ENGINE.paintFocus(g, context, id, SynthConstants.ENABLED,
 777                                   "trough", x, y, w, h);
 778             }
 779             ENGINE.finishPainting(false); // don't bother caching the image
 780         }
 781     }
 782 
 783     public void paintSliderThumbBackground(SynthContext context,
 784             Graphics g, int x, int y, int w, int h, int dir) {
 785         Region id = context.getRegion();
 786         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 787                 id, context.getComponentState());
 788         boolean hasFocus = GTKLookAndFeel.is3() &&
 789                 ((context.getComponentState() & SynthConstants.FOCUSED) != 0);
 790         synchronized (UNIXToolkit.GTK_LOCK) {
 791             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, dir,
 792                                                                     hasFocus)) {
 793                 Orientation orientation = (dir == JSlider.HORIZONTAL ?
 794                     Orientation.HORIZONTAL : Orientation.VERTICAL);
 795                 String detail = (dir == JSlider.HORIZONTAL ?
 796                     "hscale" : "vscale");
 797                 ENGINE.startPainting(g, x, y, w, h, id, gtkState, dir);
 798                 ENGINE.paintSlider(g, context, id, gtkState,
 799                         ShadowType.OUT, detail, x, y, w, h, orientation,
 800                                                                      hasFocus);
 801                 ENGINE.finishPainting();
 802             }
 803         }
 804     }
 805 
 806     //
 807     // SPINNER
 808     //
 809     public void paintSpinnerBackground(SynthContext context,
 810                                         Graphics g,
 811                                         int x, int y, int w, int h) {
 812         // This is handled in paintTextFieldBackground
 813     }
 814 
 815     //
 816     // SPLIT_PANE_DIVIDER
 817     //
 818     public void paintSplitPaneDividerBackground(SynthContext context,
 819                                        Graphics g,
 820                                        int x, int y, int w, int h) {
 821         Region id = context.getRegion();
 822         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 823                 id, context.getComponentState());
 824         JSplitPane splitPane = (JSplitPane)context.getComponent();
 825         Orientation orientation =
 826                 (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ?
 827                     Orientation.VERTICAL : Orientation.HORIZONTAL);
 828         synchronized (UNIXToolkit.GTK_LOCK) {
 829             if (! ENGINE.paintCachedImage(g, x, y, w, h,
 830                     id, gtkState, orientation)) {
 831                 ENGINE.startPainting(g, x, y, w, h, id, gtkState, orientation);
 832                 ENGINE.paintHandle(g, context, id, gtkState,
 833                         ShadowType.OUT, "paned", x, y, w, h, orientation);
 834                 ENGINE.finishPainting();
 835             }
 836         }
 837     }
 838 
 839     public void paintSplitPaneDragDivider(SynthContext context,
 840                                        Graphics g,int x, int y, int w, int h,
 841                                        int orientation) {
 842         paintSplitPaneDividerForeground(context, g, x, y, w, h, orientation);
 843     }
 844 
 845     public void paintTabbedPaneContentBackground(SynthContext context,
 846                                       Graphics g, int x, int y, int w, int h) {
 847         JTabbedPane pane = (JTabbedPane)context.getComponent();
 848         int selectedIndex = pane.getSelectedIndex();
 849         PositionType placement = GTKLookAndFeel.SwingOrientationConstantToGTK(
 850                                                         pane.getTabPlacement());
 851 
 852         int gapStart = 0;
 853         int gapSize = 0;
 854         if (selectedIndex != -1) {
 855             Rectangle tabBounds = pane.getBoundsAt(selectedIndex);
 856 
 857             if (placement == PositionType.TOP ||
 858                 placement == PositionType.BOTTOM) {
 859 
 860                 gapStart = tabBounds.x - x;
 861                 gapSize = tabBounds.width;
 862             }
 863             else {
 864                 gapStart = tabBounds.y - y;
 865                 gapSize = tabBounds.height;
 866             }
 867         }
 868 
 869         Region id = context.getRegion();
 870         int gtkState = GTKLookAndFeel.synthStateToGTKState(
 871                 id, context.getComponentState());
 872         synchronized (UNIXToolkit.GTK_LOCK) {
 873             if (! ENGINE.paintCachedImage(g, x, y, w, h,
 874                     id, gtkState, placement, gapStart, gapSize)) {
 875                 ENGINE.startPainting(g, x, y, w, h,
 876                         id, gtkState, placement, gapStart, gapSize);
 877                 ENGINE.paintBoxGap(g, context, id, gtkState, ShadowType.OUT,
 878                         "notebook", x, y, w, h, placement, gapStart, gapSize);
 879                 ENGINE.finishPainting();
 880             }
 881         }
 882     }
 883 
 884     public void paintTabbedPaneTabBackground(SynthContext context,
 885                                            Graphics g,
 886                                            int x, int y, int w, int h,
 887                                            int tabIndex) {
 888         Region id = context.getRegion();
 889         int state = context.getComponentState();
 890         int gtkState = ((state & SynthConstants.SELECTED) != 0 ?
 891             SynthConstants.ENABLED : SynthConstants.PRESSED);
 892         JTabbedPane pane = (JTabbedPane)context.getComponent();
 893         int placement = pane.getTabPlacement();
 894 
 895         synchronized (UNIXToolkit.GTK_LOCK) {
 896             if (! ENGINE.paintCachedImage(g, x, y, w, h,
 897                     id, gtkState, placement, tabIndex)) {
 898                 PositionType side = POSITIONS[placement - 1];
 899                 ENGINE.startPainting(g, x, y, w, h,
 900                         id, gtkState, placement, tabIndex);
 901                 ENGINE.paintExtension(g, context, id, gtkState,
 902                         ShadowType.OUT, "tab", x, y, w, h, side, tabIndex);
 903                 ENGINE.finishPainting();
 904             }
 905         }
 906     }
 907 
 908     //
 909     // TEXT_PANE
 910     //
 911     public void paintTextPaneBackground(SynthContext context, Graphics g,
 912                                         int x, int y, int w, int h) {
 913         paintTextAreaBackground(context, g, x, y, w, h);
 914     }
 915 
 916     //
 917     // EDITOR_PANE
 918     //
 919     public void paintEditorPaneBackground(SynthContext context, Graphics g,
 920                                           int x, int y, int w, int h) {
 921         paintTextAreaBackground(context, g, x, y, w, h);
 922     }
 923 
 924     //
 925     // TEXT_AREA
 926     //
 927     public void paintTextAreaBackground(SynthContext context, Graphics g,
 928                                         int x, int y, int w, int h) {
 929         // Does not call into ENGINE for better performance
 930         fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
 931     }
 932 
 933     //
 934     // TEXT_FIELD
 935     //
 936     // NOTE: Combobox and Label, Password and FormattedTextField calls this
 937     // too.
 938     private void paintTextBackground(SynthContext context, Graphics g,
 939                                      int x, int y, int w, int h) {
 940         // Text is odd in that it uses the TEXT_BACKGROUND vs BACKGROUND.
 941         JComponent c = context.getComponent();
 942         Container container = c.getParent();
 943         Container containerParent = null;
 944         GTKStyle style = (GTKStyle)context.getStyle();
 945         Region id = context.getRegion();
 946         int state = context.getComponentState();
 947 
 948         if (c instanceof ListCellRenderer && container != null) {
 949             containerParent = container.getParent();
 950             if (containerParent instanceof JComboBox
 951                     && containerParent.hasFocus()) {
 952                 state |= SynthConstants.FOCUSED;
 953             }
 954         }
 955 
 956         synchronized (UNIXToolkit.GTK_LOCK) {
 957             if (ENGINE.paintCachedImage(g, x, y, w, h, id, state)) {
 958                 return;
 959             }
 960 
 961             int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
 962             int focusSize = 0;
 963             boolean interiorFocus = style.getClassSpecificBoolValue(
 964                     context, "interior-focus", true);
 965 
 966             focusSize = style.getClassSpecificIntValue(context,
 967                     "focus-line-width",1);
 968             if (!interiorFocus && (state & SynthConstants.FOCUSED) != 0) {
 969                 x += focusSize;
 970                 y += focusSize;
 971                 w -= 2 * focusSize;
 972                 h -= 2 * focusSize;
 973             }
 974 
 975             int xThickness = style.getXThickness();
 976             int yThickness = style.getYThickness();
 977 
 978             ENGINE.startPainting(g, x, y, w, h, id, state);
 979             if (GTKLookAndFeel.is3()) {
 980                 ENGINE.paintBackground(g, context, id, gtkState, null,
 981                                                                     x, y, w, h);
 982             }
 983             ENGINE.paintShadow(g, context, id, gtkState,
 984                                ShadowType.IN, "entry", x, y, w, h);
 985             if (!GTKLookAndFeel.is3()) {
 986                 ENGINE.paintFlatBox(g, context, id,
 987                         gtkState, ShadowType.NONE, "entry_bg",
 988                         x + xThickness,
 989                         y + yThickness,
 990                         w - (2 * xThickness),
 991                         h - (2 * yThickness),
 992                         ColorType.TEXT_BACKGROUND);
 993             }
 994 
 995             if (focusSize > 0 && (state & SynthConstants.FOCUSED) != 0) {
 996                 if (!interiorFocus) {
 997                     x -=  focusSize;
 998                     y -=  focusSize;
 999                     w +=  2 * focusSize;
1000                     h +=  2 * focusSize;
1001                 } else {
1002                     if (containerParent instanceof JComboBox) {
1003                         x += (focusSize + 2);
1004                         y += focusSize + (GTKLookAndFeel.is3() ? 3 : 1);
1005                         w -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 1);
1006                         h -= 2 * focusSize + (GTKLookAndFeel.is3() ? 6 : 2);
1007                     } else {
1008                         x += focusSize + (GTKLookAndFeel.is3() ? 2 : 0);
1009                         y += focusSize + (GTKLookAndFeel.is3() ? 2 :0 );
1010                         w -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 0);
1011                         h -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 0);
1012                     }
1013                 }
1014                 ENGINE.paintFocus(g, context, id, gtkState,
1015                         "entry", x, y, w, h);
1016             }
1017             ENGINE.finishPainting();
1018         }
1019     }
1020 
1021     private void paintTreeCellEditorBackground(SynthContext context, Graphics g,
1022                                                int x, int y, int w, int h) {
1023         Region id = context.getRegion();
1024         int gtkState = GTKLookAndFeel.synthStateToGTKState(
1025                 id, context.getComponentState());
1026         synchronized (UNIXToolkit.GTK_LOCK) {
1027             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState)) {
1028                 ENGINE.startPainting(g, x, y, w, h, id, gtkState);
1029                 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE,
1030                         "entry_bg", x, y, w, h, ColorType.TEXT_BACKGROUND);
1031                 ENGINE.finishPainting();
1032             }
1033         }
1034     }
1035 
1036 
1037     //
1038     // ROOT_PANE
1039     //
1040     public void paintRootPaneBackground(SynthContext context, Graphics g,
1041                                         int x, int y, int w, int h) {
1042         // Does not call into ENGINE for better performance
1043         fillArea(context, g, x, y, w, h, GTKColorType.BACKGROUND);
1044     }
1045 
1046     //
1047     // TOGGLE_BUTTON
1048     //
1049     public void paintToggleButtonBackground(SynthContext context,
1050                                             Graphics g,
1051                                             int x, int y, int w, int h) {
1052         Region id = context.getRegion();
1053         JToggleButton toggleButton = (JToggleButton)context.getComponent();
1054         boolean paintBG = toggleButton.isContentAreaFilled() &&
1055                           toggleButton.isBorderPainted();
1056         boolean paintFocus = toggleButton.isFocusPainted();
1057         boolean toolButton = (toggleButton.getParent() instanceof JToolBar);
1058         paintButtonBackgroundImpl(context, g, id, "button",
1059                                   x, y, w, h,
1060                                   paintBG, paintFocus, false, toolButton);
1061     }
1062 
1063 
1064     //
1065     // SCROLL_BAR
1066     //
1067     public void paintScrollBarBackground(SynthContext context,
1068                                           Graphics g,
1069                                           int x, int y, int w,int h) {
1070         Region id = context.getRegion();
1071         boolean focused =
1072                 (context.getComponentState() & SynthConstants.FOCUSED) != 0;
1073         synchronized (UNIXToolkit.GTK_LOCK) {
1074             if (ENGINE.paintCachedImage(g, x, y, w, h, id, focused)) {
1075                 return;
1076             }
1077             ENGINE.startPainting(g, x, y, w, h, id, focused);
1078 
1079             // Note: the scrollbar insets already include the "trough-border",
1080             // which is needed to position the scrollbar buttons properly.
1081             // But when we render, we need to take the trough border out
1082             // of the equation so that we paint the entire area covered by
1083             // the trough border and the scrollbar content itself.
1084             Insets insets = context.getComponent().getInsets();
1085             GTKStyle style = (GTKStyle)context.getStyle();
1086             int troughBorder =
1087                 style.getClassSpecificIntValue(context, "trough-border", 1);
1088             insets.left   -= troughBorder;
1089             insets.right  -= troughBorder;
1090             insets.top    -= troughBorder;
1091             insets.bottom -= troughBorder;
1092 
1093             ENGINE.paintBox(g, context, id, SynthConstants.PRESSED,
1094                             ShadowType.IN, "trough",
1095                             x + insets.left,
1096                             y + insets.top,
1097                             w - insets.left - insets.right,
1098                             h - insets.top - insets.bottom);
1099 
1100             if (focused) {
1101                 ENGINE.paintFocus(g, context, id,
1102                         SynthConstants.ENABLED, "trough", x, y, w, h);
1103             }
1104             ENGINE.finishPainting();
1105         }
1106     }
1107 
1108 
1109     //
1110     // SCROLL_BAR_THUMB
1111     //
1112     public void paintScrollBarThumbBackground(SynthContext context,
1113             Graphics g, int x, int y, int w, int h, int dir) {
1114         Region id = context.getRegion();
1115         int gtkState = GTKLookAndFeel.synthStateToGTKState(
1116                 id, context.getComponentState());
1117 
1118         // The clearlooks engine paints scrollbar thumbs differently
1119         // depending on the current scroll value (specifically, it will avoid
1120         // rendering a certain line when the thumb is at the starting or
1121         // ending position).  Therefore, we normalize the current value to
1122         // the range [0,100] here and then pass it down to setRangeValue()
1123         // so that the native widget is configured appropriately.  Note that
1124         // there are really only four values that matter (min, middle, max,
1125         // or fill) so we restrict to one of those four values to avoid
1126         // blowing out the image cache.
1127         JScrollBar sb = (JScrollBar)context.getComponent();
1128         boolean rtl =
1129             sb.getOrientation() == JScrollBar.HORIZONTAL &&
1130             !sb.getComponentOrientation().isLeftToRight();
1131         double min = 0;
1132         double max = 100;
1133         double visible = 20;
1134         double value;
1135         if (sb.getMaximum() - sb.getMinimum() == sb.getVisibleAmount()) {
1136             // In this case, the thumb fills the entire track, so it is
1137             // touching both ends at the same time
1138             value = 0;
1139             visible = 100;
1140         } else if (sb.getValue() == sb.getMinimum()) {
1141             // At minimum
1142             value = rtl ? 100 : 0;
1143         } else if (sb.getValue() >= sb.getMaximum() - sb.getVisibleAmount()) {
1144             // At maximum
1145             value = rtl ? 0 : 100;
1146         } else {
1147             // Somewhere in between
1148             value = 50;
1149         }
1150 
1151         synchronized (UNIXToolkit.GTK_LOCK) {
1152             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState,
1153                                           dir, value, visible, rtl))
1154             {
1155                 ENGINE.startPainting(g, x, y, w, h, id, gtkState,
1156                                      dir, value, visible, rtl);
1157                 Orientation orientation = (dir == JScrollBar.HORIZONTAL ?
1158                     Orientation.HORIZONTAL : Orientation.VERTICAL);
1159                 ENGINE.setRangeValue(context, id, value, min, max, visible);
1160                 ENGINE.paintSlider(g, context, id, gtkState, ShadowType.OUT,
1161                                       "slider", x, y, w, h, orientation, false);
1162                 ENGINE.finishPainting();
1163             }
1164         }
1165     }
1166 
1167     //
1168     // TOOL_TIP
1169     //
1170     public void paintToolTipBackground(SynthContext context, Graphics g,
1171                                         int x, int y, int w,int h) {
1172         Region id = context.getRegion();
1173         synchronized (UNIXToolkit.GTK_LOCK) {
1174             if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
1175                 ENGINE.startPainting(g, x, y, w, h, id);
1176                 ENGINE.paintFlatBox(g, context, id, SynthConstants.ENABLED,
1177                         ShadowType.OUT, "tooltip", x, y, w, h,
1178                         ColorType.BACKGROUND);
1179                 ENGINE.finishPainting();
1180             }
1181         }
1182     }
1183 
1184 
1185     //
1186     // TREE_CELL
1187     //
1188     public void paintTreeCellBackground(SynthContext context, Graphics g,
1189                                         int x, int y, int w, int h) {
1190         Region id = context.getRegion();
1191         int state = context.getComponentState();
1192         int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
1193         synchronized (UNIXToolkit.GTK_LOCK) {
1194             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, state)) {
1195                 ENGINE.startPainting(g, x, y, w, h, id, state);
1196                 // the string arg should alternate based on row being painted,
1197                 // but we currently don't pass that in.
1198                 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE,
1199                         "cell_odd", x, y, w, h, ColorType.TEXT_BACKGROUND);
1200                 ENGINE.finishPainting();
1201             }
1202         }
1203     }
1204 
1205     public void paintTreeCellFocus(SynthContext context, Graphics g,
1206                                     int x, int y, int w, int h) {
1207         Region id = Region.TREE_CELL;
1208         int state = context.getComponentState();
1209         paintFocus(context, g, id, state, "treeview", x, y, w, h);
1210     }
1211 
1212 
1213     //
1214     // TREE
1215     //
1216     public void paintTreeBackground(SynthContext context, Graphics g,
1217                                     int x, int y, int w, int h) {
1218         // As far as I can tell, these don't call into the ENGINE.
1219         fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
1220     }
1221 
1222 
1223     //
1224     // VIEWPORT
1225     //
1226     public void paintViewportBackground(SynthContext context, Graphics g,
1227                                         int x, int y, int w, int h) {
1228         // As far as I can tell, these don't call into the ENGINE.
1229         // Also note that you don't want this to call into the ENGINE
1230         // as if it where to paint a background JViewport wouldn't scroll
1231         // correctly.
1232         fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND);
1233     }
1234 
1235     void paintFocus(SynthContext context, Graphics g, Region id,
1236             int state, String detail, int x, int y, int w, int h) {
1237         int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
1238         synchronized (UNIXToolkit.GTK_LOCK) {
1239             if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, "focus")) {
1240                 ENGINE.startPainting(g, x, y, w, h, id, gtkState, "focus");
1241                 ENGINE.paintFocus(g, context, id, gtkState, detail, x, y, w, h);
1242                 ENGINE.finishPainting();
1243             }
1244         }
1245     }
1246 
1247     void paintMetacityElement(SynthContext context, Graphics g,
1248             int gtkState, String detail, int x, int y, int w, int h,
1249             ShadowType shadow, ArrowType direction) {
1250         synchronized (UNIXToolkit.GTK_LOCK) {
1251             if (! ENGINE.paintCachedImage(
1252                     g, x, y, w, h, gtkState, detail, shadow, direction)) {
1253                 ENGINE.startPainting(
1254                         g, x, y, w, h, gtkState, detail, shadow, direction);
1255                 if (detail == "metacity-arrow") {
1256                     ENGINE.paintArrow(g, context, Region.INTERNAL_FRAME_TITLE_PANE,
1257                             gtkState, shadow, direction, "", x, y, w, h);
1258 
1259                 } else if (detail == "metacity-box") {
1260                     ENGINE.paintBox(g, context, Region.INTERNAL_FRAME_TITLE_PANE,
1261                             gtkState, shadow, "", x, y, w, h);
1262 
1263                 } else if (detail == "metacity-vline") {
1264                     ENGINE.paintVline(g, context, Region.INTERNAL_FRAME_TITLE_PANE,
1265                             gtkState, "", x, y, w, h);
1266                 }
1267                 ENGINE.finishPainting();
1268             }
1269         }
1270     }
1271 
1272     void paintIcon(SynthContext context, Graphics g,
1273             Method paintMethod, int x, int y, int w, int h) {
1274         int state = context.getComponentState();
1275         synchronized (UNIXToolkit.GTK_LOCK) {
1276             if (! ENGINE.paintCachedImage(g, x, y, w, h, state, paintMethod)) {
1277                 ENGINE.startPainting(g, x, y, w, h, state, paintMethod);
1278                 try {
1279                     paintMethod.invoke(this, context, g, state, x, y, w, h);
1280                 } catch (IllegalAccessException iae) {
1281                     assert false;
1282                 } catch (InvocationTargetException ite) {
1283                     assert false;
1284                 }
1285                 ENGINE.finishPainting();
1286             }
1287         }
1288     }
1289 
1290     void paintIcon(SynthContext context, Graphics g,
1291             Method paintMethod, int x, int y, int w, int h, Object direction) {
1292         int state = context.getComponentState();
1293         synchronized (UNIXToolkit.GTK_LOCK) {
1294             if (! ENGINE.paintCachedImage(g,
1295                     x, y, w, h, state, paintMethod, direction)) {
1296                 ENGINE.startPainting(g,
1297                         x, y, w, h, state, paintMethod, direction);
1298                 try {
1299                     paintMethod.invoke(this, context,
1300                             g, state, x, y, w, h, direction);
1301                 } catch (IllegalAccessException iae) {
1302                     assert false;
1303                 } catch (InvocationTargetException ite) {
1304                     assert false;
1305                 }
1306                 ENGINE.finishPainting();
1307             }
1308         }
1309     }
1310 
1311     // All icon painting methods are called from under GTK_LOCK
1312 
1313     public void paintTreeExpandedIcon(SynthContext context,
1314             Graphics g, int state, int x, int y, int w, int h) {
1315         ENGINE.paintExpander(g, context, Region.TREE,
1316                 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state),
1317                 ExpanderStyle.EXPANDED, "treeview", x, y, w, h);
1318     }
1319 
1320     public void paintTreeCollapsedIcon(SynthContext context,
1321             Graphics g, int state, int x, int y, int w, int h) {
1322         ENGINE.paintExpander(g, context, Region.TREE,
1323                 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state),
1324                 ExpanderStyle.COLLAPSED, "treeview", x, y, w, h);
1325     }
1326 
1327     public void paintCheckBoxIcon(SynthContext context,
1328             Graphics g, int state, int x, int y, int w, int h) {
1329         GTKStyle style = (GTKStyle)context.getStyle();
1330         int size = style.getClassSpecificIntValue(context,
1331                         "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE);
1332         int offset = style.getClassSpecificIntValue(context,
1333                         "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING);
1334 
1335         ENGINE.paintCheck(g, context, Region.CHECK_BOX, "checkbutton",
1336                 x+offset, y+offset, size, size);
1337     }
1338 
1339     public void paintRadioButtonIcon(SynthContext context,
1340             Graphics g, int state, int x, int y, int w, int h) {
1341         GTKStyle style = (GTKStyle)context.getStyle();
1342         int size = style.getClassSpecificIntValue(context,
1343                         "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE);
1344         int offset = style.getClassSpecificIntValue(context,
1345                         "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING);
1346 
1347         ENGINE.paintOption(g, context, Region.RADIO_BUTTON, "radiobutton",
1348                 x+offset, y+offset, size, size);
1349     }
1350 
1351     public void paintMenuArrowIcon(SynthContext context, Graphics g,
1352             int state, int x, int y, int w, int h, ArrowType dir) {
1353         int gtkState = GTKLookAndFeel.synthStateToGTKState(
1354                 context.getRegion(), state);
1355         ShadowType shadow = ShadowType.OUT;
1356         if (gtkState == SynthConstants.MOUSE_OVER) {
1357             shadow = ShadowType.IN;
1358         }
1359         ENGINE.paintArrow(g, context, Region.MENU_ITEM, gtkState, shadow,
1360                 dir, "menuitem", x + 3, y + 3, 7, 7);
1361     }
1362 
1363     public void paintCheckBoxMenuItemCheckIcon(SynthContext context,
1364             Graphics g, int state, int x, int y, int w, int h) {
1365 
1366         GTKStyle style = (GTKStyle)context.getStyle();
1367         int size = style.getClassSpecificIntValue(context,"indicator-size",
1368                 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE);
1369 
1370         ENGINE.paintCheck(g, context, Region.CHECK_BOX_MENU_ITEM, "check",
1371                 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
1372                 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
1373                 size, size);
1374     }
1375 
1376     public void paintRadioButtonMenuItemCheckIcon(SynthContext context,
1377             Graphics g, int state, int x, int y, int w, int h) {
1378 
1379         GTKStyle style = (GTKStyle)context.getStyle();
1380         int size = style.getClassSpecificIntValue(context,"indicator-size",
1381                 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE);
1382 
1383         ENGINE.paintOption(g, context, Region.RADIO_BUTTON_MENU_ITEM, "option",
1384                 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
1385                 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET,
1386                 size, size);
1387     }
1388 
1389     public void paintToolBarHandleIcon(SynthContext context, Graphics g,
1390             int state, int x, int y, int w, int h, Orientation orientation) {
1391         int gtkState = GTKLookAndFeel.synthStateToGTKState(
1392                 context.getRegion(), state);
1393 
1394         // The orientation parameter passed down by Synth refers to the
1395         // orientation of the toolbar, but the one we pass to GTK refers
1396         // to the orientation of the handle.  Therefore, we need to swap
1397         // the value here: horizontal toolbars have vertical handles, and
1398         // vice versa.
1399         orientation = (orientation == Orientation.HORIZONTAL) ?
1400             Orientation.VERTICAL : Orientation.HORIZONTAL;
1401 
1402         ENGINE.paintHandle(g, context, Region.TOOL_BAR, gtkState,
1403                 ShadowType.OUT, "handlebox", x, y, w, h, orientation);
1404     }
1405 
1406     public void paintAscendingSortIcon(SynthContext context,
1407             Graphics g, int state, int x, int y, int w, int h) {
1408         ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED,
1409                 ShadowType.IN, ArrowType.UP, "arrow", x, y, w, h);
1410     }
1411 
1412     public void paintDescendingSortIcon(SynthContext context,
1413             Graphics g, int state, int x, int y, int w, int h) {
1414         ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED,
1415                 ShadowType.IN, ArrowType.DOWN, "arrow", x, y, w, h);
1416     }
1417 
1418     /*
1419      * Fill an area with color determined from this context's Style using the
1420      * specified GTKColorType
1421      */
1422     private void fillArea(SynthContext context, Graphics g,
1423                           int x, int y, int w, int h, ColorType colorType) {
1424         if (context.getComponent().isOpaque()) {
1425             Region id = context.getRegion();
1426             int gtkState = GTKLookAndFeel.synthStateToGTKState(id,
1427                     context.getComponentState());
1428             GTKStyle style = (GTKStyle)context.getStyle();
1429 
1430             g.setColor(style.getGTKColor(context, gtkState, colorType));
1431             g.fillRect(x, y, w, h);
1432         }
1433     }
1434 
1435     // Refer to GTKLookAndFeel for details on this.
1436     @SuppressWarnings("serial") // Superclass is not serializable across versions
1437     static class ListTableFocusBorder extends AbstractBorder implements
1438                           UIResource {
1439 
1440         private boolean selectedCell;
1441         private boolean focusedCell;
1442 
1443         public static ListTableFocusBorder getSelectedCellBorder() {
1444             return new ListTableFocusBorder(true, true);
1445         }
1446 
1447         public static ListTableFocusBorder getUnselectedCellBorder() {
1448             return new ListTableFocusBorder(false, true);
1449         }
1450 
1451         public static ListTableFocusBorder getNoFocusCellBorder() {
1452             return new ListTableFocusBorder(false, false);
1453         }
1454 
1455         public ListTableFocusBorder(boolean selectedCell, boolean focusedCell) {
1456             this.selectedCell = selectedCell;
1457             this.focusedCell = focusedCell;
1458         }
1459 
1460         private SynthContext getContext(Component c) {
1461             SynthContext context = null;
1462 
1463             ComponentUI ui = null;
1464             if (c instanceof JLabel) {
1465                 ui = ((JLabel)c).getUI();
1466             }
1467 
1468             if (ui instanceof SynthUI) {
1469                 context = ((SynthUI)ui).getContext((JComponent)c);
1470             }
1471 
1472             return context;
1473         }
1474 
1475         public void paintBorder(Component c, Graphics g, int x, int y,
1476                                 int w, int h) {
1477             if (focusedCell) {
1478                 SynthContext context = getContext(c);
1479                 int state = (selectedCell? SynthConstants.SELECTED:
1480                              SynthConstants.FOCUSED | SynthConstants.ENABLED);
1481 
1482                 if (context != null) {
1483                     GTKPainter.INSTANCE.paintFocus(context, g,
1484                             Region.TABLE, state, "", x, y, w, h);
1485                 }
1486             }
1487         }
1488 
1489         public Insets getBorderInsets(Component c, Insets i) {
1490             SynthContext context = getContext(c);
1491 
1492             if (context != null) {
1493                 i = context.getStyle().getInsets(context, i);
1494             }
1495 
1496             return i;
1497         }
1498 
1499         public boolean isBorderOpaque() {
1500             return true;
1501         }
1502     }
1503 
1504     // TitledBorder implementation for GTK L&F
1505     @SuppressWarnings("serial") // Superclass is not serializable across versions
1506     static class TitledBorder extends AbstractBorder implements UIResource {
1507 
1508         public void paintBorder(Component c, Graphics g, int x, int y,
1509                                 int w, int h) {
1510             SynthContext context = getContext((JComponent)c);
1511             Region id = context.getRegion();
1512             int state = context.getComponentState();
1513             int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state);
1514 
1515             synchronized (UNIXToolkit.GTK_LOCK) {
1516                 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) {
1517                     ENGINE.startPainting(g, x, y, w, h, id);
1518                     ENGINE.paintShadow(g, context, id, gtkState, ShadowType.ETCHED_IN,
1519                                       "frame", x, y, w, h);
1520                     ENGINE.finishPainting();
1521                 }
1522             }
1523         }
1524 
1525         public Insets getBorderInsets(Component c, Insets i) {
1526             SynthContext context = getContext((JComponent)c);
1527             return context.getStyle().getInsets(context, i);
1528         }
1529 
1530         public boolean isBorderOpaque() {
1531             return true;
1532         }
1533 
1534         private SynthStyle getStyle(JComponent c) {
1535             return SynthLookAndFeel.getStyle(c, GTKEngine.CustomRegion.TITLED_BORDER);
1536         }
1537 
1538         private SynthContext getContext(JComponent c) {
1539             int state = SynthConstants.DEFAULT;
1540             return new SynthContext(c, GTKEngine.CustomRegion.TITLED_BORDER,
1541                                     getStyle(c), state);
1542         }
1543     }
1544 }