1 /*
   2  * Copyright (c) 2005, 2018, 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 
  26 package com.sun.java.swing.plaf.gtk;
  27 
  28 import java.awt.*;
  29 import java.awt.image.*;
  30 import java.util.HashMap;
  31 import javax.swing.*;
  32 import javax.swing.plaf.synth.*;
  33 
  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 com.sun.java.swing.plaf.gtk.GTKConstants.TextDirection;
  40 
  41 import sun.awt.image.SunWritableRaster;
  42 import sun.swing.ImageCache;
  43 
  44 /**
  45  * GTKEngine delegates all painting job to native GTK libraries.
  46  *
  47  * Painting with GTKEngine looks like this:
  48  * First, startPainting() is called. It prepares an offscreen buffer of the
  49  *   required size.
  50  * Then, any number of paintXXX() methods can be called. They effectively ignore
  51  *   the Graphics parameter and draw to the offscreen buffer.
  52  * Finally, finishPainting() should be called. It fills the data buffer passed
  53  *   in with the image data.
  54  *
  55  * @author Josh Outwater
  56  */
  57 class GTKEngine {
  58 
  59     final static GTKEngine INSTANCE = new GTKEngine();
  60 
  61     /** Size of the image cache */
  62     private static final int CACHE_SIZE = 50;
  63 
  64     /** This enum mirrors that in gtk2_interface.h */
  65     static enum WidgetType {
  66         BUTTON, CHECK_BOX, CHECK_BOX_MENU_ITEM, COLOR_CHOOSER,
  67         COMBO_BOX, COMBO_BOX_ARROW_BUTTON, COMBO_BOX_TEXT_FIELD,
  68         DESKTOP_ICON, DESKTOP_PANE, EDITOR_PANE, FORMATTED_TEXT_FIELD,
  69         HANDLE_BOX, HPROGRESS_BAR,
  70         HSCROLL_BAR, HSCROLL_BAR_BUTTON_LEFT, HSCROLL_BAR_BUTTON_RIGHT,
  71         HSCROLL_BAR_TRACK, HSCROLL_BAR_THUMB,
  72         HSEPARATOR, HSLIDER, HSLIDER_TRACK, HSLIDER_THUMB, HSPLIT_PANE_DIVIDER,
  73         INTERNAL_FRAME, INTERNAL_FRAME_TITLE_PANE, IMAGE, LABEL, LIST, MENU,
  74         MENU_BAR, MENU_ITEM, MENU_ITEM_ACCELERATOR, OPTION_PANE, PANEL,
  75         PASSWORD_FIELD, POPUP_MENU, POPUP_MENU_SEPARATOR,
  76         RADIO_BUTTON, RADIO_BUTTON_MENU_ITEM, ROOT_PANE, SCROLL_PANE,
  77         SPINNER, SPINNER_ARROW_BUTTON, SPINNER_TEXT_FIELD,
  78         SPLIT_PANE, TABBED_PANE, TABBED_PANE_TAB_AREA, TABBED_PANE_CONTENT,
  79         TABBED_PANE_TAB, TABLE, TABLE_HEADER, TEXT_AREA, TEXT_FIELD, TEXT_PANE,
  80         TITLED_BORDER,
  81         TOGGLE_BUTTON, TOOL_BAR, TOOL_BAR_DRAG_WINDOW, TOOL_BAR_SEPARATOR,
  82         TOOL_TIP, TREE, TREE_CELL, VIEWPORT, VPROGRESS_BAR,
  83         VSCROLL_BAR, VSCROLL_BAR_BUTTON_UP, VSCROLL_BAR_BUTTON_DOWN,
  84         VSCROLL_BAR_TRACK, VSCROLL_BAR_THUMB,
  85         VSEPARATOR, VSLIDER, VSLIDER_TRACK, VSLIDER_THUMB,
  86         VSPLIT_PANE_DIVIDER
  87     }
  88 
  89     /**
  90      * Representation of GtkSettings properties.
  91      * When we need more settings we can add them here and
  92      * to all implementations of getGTKSetting().
  93      */
  94     static enum Settings {
  95         GTK_FONT_NAME,
  96         GTK_ICON_SIZES,
  97         GTK_CURSOR_BLINK,
  98         GTK_CURSOR_BLINK_TIME
  99     }
 100 
 101     /* Custom regions are needed for representing regions that don't exist
 102      * in the original Region class.
 103      */
 104     static class CustomRegion extends Region {
 105         /*
 106          * TITLED_BORDER Region is mapped to GtkFrame class which can draw
 107          * titled borders around components.
 108          */
 109         static Region TITLED_BORDER = new CustomRegion("TitledBorder");
 110 
 111         private CustomRegion(String name) {
 112             super(name, null, false);
 113         }
 114     }
 115 
 116 
 117     private static HashMap<Region, Object> regionToWidgetTypeMap;
 118     private ImageCache cache = new ImageCache(CACHE_SIZE);
 119     private int x0, y0, w0, h0;
 120     private Graphics graphics;
 121     private Object[] cacheArgs;
 122 
 123     private native void native_paint_arrow(
 124             int widgetType, int state, int shadowType, String detail,
 125             int x, int y, int width, int height, int arrowType);
 126     private native void native_paint_box(
 127             int widgetType, int state, int shadowType, String detail,
 128             int x, int y, int width, int height, int synthState, int dir);
 129     private native void native_paint_box_gap(
 130             int widgetType, int state, int shadowType, String detail,
 131             int x, int y, int width, int height,
 132             int gapSide, int gapX, int gapWidth);
 133     private native void native_paint_check(
 134             int widgetType, int synthState, String detail,
 135             int x, int y, int width, int height);
 136     private native void native_paint_expander(
 137             int widgetType, int state, String detail,
 138             int x, int y, int width, int height, int expanderStyle);
 139     private native void native_paint_extension(
 140             int widgetType, int state, int shadowType, String detail,
 141             int x, int y, int width, int height, int placement);
 142     private native void native_paint_flat_box(
 143             int widgetType, int state, int shadowType, String detail,
 144             int x, int y, int width, int height, boolean hasFocus);
 145     private native void native_paint_focus(
 146             int widgetType, int state, String detail,
 147             int x, int y, int width, int height);
 148     private native void native_paint_handle(
 149             int widgetType, int state, int shadowType, String detail,
 150             int x, int y, int width, int height, int orientation);
 151     private native void native_paint_hline(
 152             int widgetType, int state, String detail,
 153             int x, int y, int width, int height);
 154     private native void native_paint_option(
 155             int widgetType, int synthState, String detail,
 156             int x, int y, int width, int height);
 157     private native void native_paint_shadow(
 158             int widgetType, int state, int shadowType, String detail,
 159             int x, int y, int width, int height, int synthState, int dir);
 160     private native void native_paint_slider(
 161             int widgetType, int state, int shadowType, String detail, int x,
 162             int y, int width, int height, int orientation, boolean hasFocus);
 163     private native void native_paint_vline(
 164             int widgetType, int state, String detail,
 165             int x, int y, int width, int height);
 166     private native void native_paint_background(
 167             int widgetType, int state, int x, int y, int width, int height);
 168     private native Object native_get_gtk_setting(int property);
 169     private native void nativeSetRangeValue(int widgetType, double value,
 170                                             double min, double max,
 171                                             double visible);
 172 
 173     private native void nativeStartPainting(int w, int h);
 174     private native int nativeFinishPainting(int[] buffer, int width, int height);
 175     private native void native_switch_theme();
 176 
 177     static {
 178         // Make sure the awt toolkit is loaded so we have access to native
 179         // methods.
 180         Toolkit.getDefaultToolkit();
 181 
 182         // Initialize regionToWidgetTypeMap
 183         regionToWidgetTypeMap = new HashMap<Region, Object>(50);
 184         regionToWidgetTypeMap.put(Region.ARROW_BUTTON, new WidgetType[] {
 185             WidgetType.SPINNER_ARROW_BUTTON,
 186             WidgetType.COMBO_BOX_ARROW_BUTTON,
 187             WidgetType.HSCROLL_BAR_BUTTON_LEFT,
 188             WidgetType.HSCROLL_BAR_BUTTON_RIGHT,
 189             WidgetType.VSCROLL_BAR_BUTTON_UP,
 190             WidgetType.VSCROLL_BAR_BUTTON_DOWN});
 191         regionToWidgetTypeMap.put(Region.BUTTON, WidgetType.BUTTON);
 192         regionToWidgetTypeMap.put(Region.CHECK_BOX, WidgetType.CHECK_BOX);
 193         regionToWidgetTypeMap.put(Region.CHECK_BOX_MENU_ITEM,
 194                                   WidgetType.CHECK_BOX_MENU_ITEM);
 195         regionToWidgetTypeMap.put(Region.COLOR_CHOOSER, WidgetType.COLOR_CHOOSER);
 196         regionToWidgetTypeMap.put(Region.FILE_CHOOSER, WidgetType.OPTION_PANE);
 197         regionToWidgetTypeMap.put(Region.COMBO_BOX, WidgetType.COMBO_BOX);
 198         regionToWidgetTypeMap.put(Region.DESKTOP_ICON, WidgetType.DESKTOP_ICON);
 199         regionToWidgetTypeMap.put(Region.DESKTOP_PANE, WidgetType.DESKTOP_PANE);
 200         regionToWidgetTypeMap.put(Region.EDITOR_PANE, WidgetType.EDITOR_PANE);
 201         regionToWidgetTypeMap.put(Region.FORMATTED_TEXT_FIELD, new WidgetType[] {
 202             WidgetType.FORMATTED_TEXT_FIELD, WidgetType.SPINNER_TEXT_FIELD});
 203         regionToWidgetTypeMap.put(GTKRegion.HANDLE_BOX, WidgetType.HANDLE_BOX);
 204         regionToWidgetTypeMap.put(Region.INTERNAL_FRAME,
 205                                   WidgetType.INTERNAL_FRAME);
 206         regionToWidgetTypeMap.put(Region.INTERNAL_FRAME_TITLE_PANE,
 207                                   WidgetType.INTERNAL_FRAME_TITLE_PANE);
 208         regionToWidgetTypeMap.put(Region.LABEL, new WidgetType[] {
 209             WidgetType.LABEL, WidgetType.COMBO_BOX_TEXT_FIELD});
 210         regionToWidgetTypeMap.put(Region.LIST, WidgetType.LIST);
 211         regionToWidgetTypeMap.put(Region.MENU, WidgetType.MENU);
 212         regionToWidgetTypeMap.put(Region.MENU_BAR, WidgetType.MENU_BAR);
 213         regionToWidgetTypeMap.put(Region.MENU_ITEM, WidgetType.MENU_ITEM);
 214         regionToWidgetTypeMap.put(Region.MENU_ITEM_ACCELERATOR,
 215                                   WidgetType.MENU_ITEM_ACCELERATOR);
 216         regionToWidgetTypeMap.put(Region.OPTION_PANE, WidgetType.OPTION_PANE);
 217         regionToWidgetTypeMap.put(Region.PANEL, WidgetType.PANEL);
 218         regionToWidgetTypeMap.put(Region.PASSWORD_FIELD,
 219                                   WidgetType.PASSWORD_FIELD);
 220         regionToWidgetTypeMap.put(Region.POPUP_MENU, WidgetType.POPUP_MENU);
 221         regionToWidgetTypeMap.put(Region.POPUP_MENU_SEPARATOR,
 222                                   WidgetType.POPUP_MENU_SEPARATOR);
 223         regionToWidgetTypeMap.put(Region.PROGRESS_BAR, new WidgetType[] {
 224             WidgetType.HPROGRESS_BAR, WidgetType.VPROGRESS_BAR});
 225         regionToWidgetTypeMap.put(Region.RADIO_BUTTON, WidgetType.RADIO_BUTTON);
 226         regionToWidgetTypeMap.put(Region.RADIO_BUTTON_MENU_ITEM,
 227                                   WidgetType.RADIO_BUTTON_MENU_ITEM);
 228         regionToWidgetTypeMap.put(Region.ROOT_PANE, WidgetType.ROOT_PANE);
 229         regionToWidgetTypeMap.put(Region.SCROLL_BAR, new WidgetType[] {
 230             WidgetType.HSCROLL_BAR, WidgetType.VSCROLL_BAR});
 231         regionToWidgetTypeMap.put(Region.SCROLL_BAR_THUMB, new WidgetType[] {
 232             WidgetType.HSCROLL_BAR_THUMB, WidgetType.VSCROLL_BAR_THUMB});
 233         regionToWidgetTypeMap.put(Region.SCROLL_BAR_TRACK, new WidgetType[] {
 234             WidgetType.HSCROLL_BAR_TRACK, WidgetType.VSCROLL_BAR_TRACK});
 235         regionToWidgetTypeMap.put(Region.SCROLL_PANE, WidgetType.SCROLL_PANE);
 236         regionToWidgetTypeMap.put(Region.SEPARATOR, new WidgetType[] {
 237             WidgetType.HSEPARATOR, WidgetType.VSEPARATOR});
 238         regionToWidgetTypeMap.put(Region.SLIDER, new WidgetType[] {
 239             WidgetType.HSLIDER, WidgetType.VSLIDER});
 240         regionToWidgetTypeMap.put(Region.SLIDER_THUMB, new WidgetType[] {
 241             WidgetType.HSLIDER_THUMB, WidgetType.VSLIDER_THUMB});
 242         regionToWidgetTypeMap.put(Region.SLIDER_TRACK, new WidgetType[] {
 243             WidgetType.HSLIDER_TRACK, WidgetType.VSLIDER_TRACK});
 244         regionToWidgetTypeMap.put(Region.SPINNER, WidgetType.SPINNER);
 245         regionToWidgetTypeMap.put(Region.SPLIT_PANE, WidgetType.SPLIT_PANE);
 246         regionToWidgetTypeMap.put(Region.SPLIT_PANE_DIVIDER, new WidgetType[] {
 247             WidgetType.HSPLIT_PANE_DIVIDER, WidgetType.VSPLIT_PANE_DIVIDER});
 248         regionToWidgetTypeMap.put(Region.TABBED_PANE, WidgetType.TABBED_PANE);
 249         regionToWidgetTypeMap.put(Region.TABBED_PANE_CONTENT,
 250                                   WidgetType.TABBED_PANE_CONTENT);
 251         regionToWidgetTypeMap.put(Region.TABBED_PANE_TAB,
 252                                   WidgetType.TABBED_PANE_TAB);
 253         regionToWidgetTypeMap.put(Region.TABBED_PANE_TAB_AREA,
 254                                   WidgetType.TABBED_PANE_TAB_AREA);
 255         regionToWidgetTypeMap.put(Region.TABLE, WidgetType.TABLE);
 256         regionToWidgetTypeMap.put(Region.TABLE_HEADER, WidgetType.TABLE_HEADER);
 257         regionToWidgetTypeMap.put(Region.TEXT_AREA, WidgetType.TEXT_AREA);
 258         regionToWidgetTypeMap.put(Region.TEXT_FIELD, new WidgetType[] {
 259             WidgetType.TEXT_FIELD, WidgetType.COMBO_BOX_TEXT_FIELD});
 260         regionToWidgetTypeMap.put(Region.TEXT_PANE, WidgetType.TEXT_PANE);
 261         regionToWidgetTypeMap.put(CustomRegion.TITLED_BORDER, WidgetType.TITLED_BORDER);
 262         regionToWidgetTypeMap.put(Region.TOGGLE_BUTTON, WidgetType.TOGGLE_BUTTON);
 263         regionToWidgetTypeMap.put(Region.TOOL_BAR, WidgetType.TOOL_BAR);
 264         regionToWidgetTypeMap.put(Region.TOOL_BAR_CONTENT, WidgetType.TOOL_BAR);
 265         regionToWidgetTypeMap.put(Region.TOOL_BAR_DRAG_WINDOW,
 266                                   WidgetType.TOOL_BAR_DRAG_WINDOW);
 267         regionToWidgetTypeMap.put(Region.TOOL_BAR_SEPARATOR,
 268                                   WidgetType.TOOL_BAR_SEPARATOR);
 269         regionToWidgetTypeMap.put(Region.TOOL_TIP, WidgetType.TOOL_TIP);
 270         regionToWidgetTypeMap.put(Region.TREE, WidgetType.TREE);
 271         regionToWidgetTypeMap.put(Region.TREE_CELL, WidgetType.TREE_CELL);
 272         regionToWidgetTypeMap.put(Region.VIEWPORT, WidgetType.VIEWPORT);
 273     }
 274 
 275     /** Translate Region and JComponent into WidgetType ordinals */
 276     static WidgetType getWidgetType(JComponent c, Region id) {
 277         Object value = regionToWidgetTypeMap.get(id);
 278 
 279         if (value instanceof WidgetType) {
 280             return (WidgetType)value;
 281         }
 282 
 283         WidgetType[] widgets = (WidgetType[])value;
 284         if (c == null ) {
 285             return widgets[0];
 286         }
 287 
 288         if (c instanceof JScrollBar) {
 289             return (((JScrollBar)c).getOrientation() == JScrollBar.HORIZONTAL) ?
 290                 widgets[0] : widgets[1];
 291         } else if (c instanceof JSeparator) {
 292             JSeparator separator = (JSeparator)c;
 293 
 294             /* We should return correrct WidgetType if the seperator is inserted
 295              * in Menu/PopupMenu/ToolBar. BugID: 6465603
 296              */
 297             if (separator.getParent() instanceof JPopupMenu) {
 298                 return WidgetType.POPUP_MENU_SEPARATOR;
 299             } else if (separator.getParent() instanceof JToolBar) {
 300                 return WidgetType.TOOL_BAR_SEPARATOR;
 301             }
 302 
 303             return (separator.getOrientation() == JSeparator.HORIZONTAL) ?
 304                 widgets[0] : widgets[1];
 305         } else if (c instanceof JSlider) {
 306             return (((JSlider)c).getOrientation() == JSlider.HORIZONTAL) ?
 307                 widgets[0] : widgets[1];
 308         } else if (c instanceof JProgressBar) {
 309             return (((JProgressBar)c).getOrientation() == JProgressBar.HORIZONTAL) ?
 310                 widgets[0] : widgets[1];
 311         } else if (c instanceof JSplitPane) {
 312             return (((JSplitPane)c).getOrientation() == JSplitPane.HORIZONTAL_SPLIT) ?
 313                 widgets[1] : widgets[0];
 314         } else if (id == Region.LABEL) {
 315             /*
 316              * For all ListCellRenderers we will use COMBO_BOX_TEXT_FIELD widget
 317              * type because we can get correct insets. List items however won't be
 318              * drawn as a text entry (see GTKPainter.paintLabelBackground).
 319              */
 320             if (c instanceof ListCellRenderer) {
 321                 return widgets[1];
 322             } else {
 323                 return widgets[0];
 324             }
 325         } else if (id == Region.TEXT_FIELD) {
 326             String name = c.getName();
 327             if (name != null && name.startsWith("ComboBox")) {
 328                 return widgets[1];
 329             } else {
 330                 return widgets[0];
 331             }
 332         } else if (id == Region.FORMATTED_TEXT_FIELD) {
 333             String name = c.getName();
 334             if (name != null && name.startsWith("Spinner")) {
 335                 return widgets[1];
 336             } else {
 337                 return widgets[0];
 338             }
 339         } else if (id == Region.ARROW_BUTTON) {
 340             if (c.getParent() instanceof JScrollBar) {
 341                 Integer prop = (Integer)
 342                     c.getClientProperty("__arrow_direction__");
 343                 int dir = (prop != null) ?
 344                     prop.intValue() : SwingConstants.WEST;
 345                 switch (dir) {
 346                 case SwingConstants.WEST:
 347                     return WidgetType.HSCROLL_BAR_BUTTON_LEFT;
 348                 case SwingConstants.EAST:
 349                     return WidgetType.HSCROLL_BAR_BUTTON_RIGHT;
 350                 case SwingConstants.NORTH:
 351                     return WidgetType.VSCROLL_BAR_BUTTON_UP;
 352                 case SwingConstants.SOUTH:
 353                     return WidgetType.VSCROLL_BAR_BUTTON_DOWN;
 354                 default:
 355                     return null;
 356                 }
 357             } else if (c.getParent() instanceof JComboBox) {
 358                 return WidgetType.COMBO_BOX_ARROW_BUTTON;
 359             } else {
 360                 return WidgetType.SPINNER_ARROW_BUTTON;
 361             }
 362         }
 363 
 364         return null;
 365     }
 366 
 367     private static int getTextDirection(SynthContext context) {
 368         TextDirection dir = TextDirection.NONE;
 369         JComponent comp = context.getComponent();
 370         if (comp != null) {
 371             ComponentOrientation co = comp.getComponentOrientation();
 372             if (co != null) {
 373                 dir = co.isLeftToRight() ?
 374                     TextDirection.LTR : TextDirection.RTL;
 375             }
 376         }
 377         return dir.ordinal();
 378     }
 379 
 380     public void paintArrow(Graphics g, SynthContext context,
 381             Region id, int state, ShadowType shadowType, ArrowType direction,
 382             String detail, int x, int y, int w, int h) {
 383 
 384         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 385         int widget = getWidgetType(context.getComponent(), id).ordinal();
 386         native_paint_arrow(widget, state, shadowType.ordinal(),
 387                 detail, x - x0, y - y0, w, h, direction.ordinal());
 388     }
 389 
 390     public void paintBox(Graphics g, SynthContext context,
 391             Region id, int state, ShadowType shadowType,
 392             String detail, int x, int y, int w, int h) {
 393 
 394         int gtkState =
 395             GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 396         int synthState = context.getComponentState();
 397         int dir = getTextDirection(context);
 398         int widget = getWidgetType(context.getComponent(), id).ordinal();
 399         native_paint_box(widget, gtkState, shadowType.ordinal(),
 400                          detail, x - x0, y - y0, w, h, synthState, dir);
 401     }
 402 
 403     public void paintBoxGap(Graphics g, SynthContext context,
 404             Region id, int state, ShadowType shadowType,
 405             String detail, int x, int y, int w, int h,
 406             PositionType boxGapType, int tabBegin, int size) {
 407 
 408         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 409         int widget = getWidgetType(context.getComponent(), id).ordinal();
 410         native_paint_box_gap(widget, state, shadowType.ordinal(), detail,
 411                 x - x0, y - y0, w, h, boxGapType.ordinal(), tabBegin, size);
 412     }
 413 
 414     public void paintCheck(Graphics g, SynthContext context,
 415             Region id, String detail, int x, int y, int w, int h) {
 416 
 417         int synthState = context.getComponentState();
 418         int widget = getWidgetType(context.getComponent(), id).ordinal();
 419         native_paint_check(widget, synthState, detail, x - x0, y - y0, w, h);
 420     }
 421 
 422     public void paintExpander(Graphics g, SynthContext context,
 423             Region id, int state, ExpanderStyle expanderStyle, String detail,
 424             int x, int y, int w, int h) {
 425 
 426         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 427         int widget = getWidgetType(context.getComponent(), id).ordinal();
 428         native_paint_expander(widget, state, detail, x - x0, y - y0, w, h,
 429                               expanderStyle.ordinal());
 430     }
 431 
 432     public void paintExtension(Graphics g, SynthContext context,
 433             Region id, int state, ShadowType shadowType, String detail,
 434             int x, int y, int w, int h, PositionType placement, int tabIndex) {
 435 
 436         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 437         int widget = getWidgetType(context.getComponent(), id).ordinal();
 438         native_paint_extension(widget, state, shadowType.ordinal(), detail,
 439                                x - x0, y - y0, w, h, placement.ordinal());
 440     }
 441 
 442     public void paintFlatBox(Graphics g, SynthContext context,
 443             Region id, int state, ShadowType shadowType, String detail,
 444             int x, int y, int w, int h, ColorType colorType) {
 445 
 446         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 447         int widget = getWidgetType(context.getComponent(), id).ordinal();
 448         native_paint_flat_box(widget, state, shadowType.ordinal(), detail,
 449                               x - x0, y - y0, w, h,
 450                               context.getComponent().hasFocus());
 451     }
 452 
 453     public void paintFocus(Graphics g, SynthContext context,
 454             Region id, int state, String detail, int x, int y, int w, int h) {
 455 
 456         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 457         int widget = getWidgetType(context.getComponent(), id).ordinal();
 458         native_paint_focus(widget, state, detail, x - x0, y - y0, w, h);
 459     }
 460 
 461     public void paintHandle(Graphics g, SynthContext context,
 462             Region id, int state, ShadowType shadowType, String detail,
 463             int x, int y, int w, int h, Orientation orientation) {
 464 
 465         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 466         int widget = getWidgetType(context.getComponent(), id).ordinal();
 467         native_paint_handle(widget, state, shadowType.ordinal(), detail,
 468                             x - x0, y - y0, w, h, orientation.ordinal());
 469     }
 470 
 471     public void paintHline(Graphics g, SynthContext context,
 472             Region id, int state, String detail, int x, int y, int w, int h) {
 473 
 474         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 475         int widget = getWidgetType(context.getComponent(), id).ordinal();
 476         native_paint_hline(widget, state, detail, x - x0, y - y0, w, h);
 477     }
 478 
 479     public void paintOption(Graphics g, SynthContext context,
 480             Region id, String detail, int x, int y, int w, int h) {
 481 
 482         int synthState = context.getComponentState();
 483         int widget = getWidgetType(context.getComponent(), id).ordinal();
 484         native_paint_option(widget, synthState, detail, x - x0, y - y0, w, h);
 485     }
 486 
 487     public void paintShadow(Graphics g, SynthContext context,
 488             Region id, int state, ShadowType shadowType, String detail,
 489             int x, int y, int w, int h) {
 490 
 491         int gtkState =
 492             GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 493         int synthState = context.getComponentState();
 494         Container parent = context.getComponent().getParent();
 495         if(GTKLookAndFeel.is3()) {
 496             if (parent != null && parent.getParent() instanceof JComboBox) {
 497                 if (parent.getParent().hasFocus()) {
 498                     synthState |= SynthConstants.FOCUSED;
 499                 }
 500             }
 501         }
 502         int dir = getTextDirection(context);
 503         int widget = getWidgetType(context.getComponent(), id).ordinal();
 504         native_paint_shadow(widget, gtkState, shadowType.ordinal(), detail,
 505                             x - x0, y - y0, w, h, synthState, dir);
 506     }
 507 
 508     public void paintSlider(Graphics g, SynthContext context,
 509             Region id, int state, ShadowType shadowType, String detail, int x,
 510             int y, int w, int h, Orientation orientation, boolean hasFocus) {
 511 
 512         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 513         int widget = getWidgetType(context.getComponent(), id).ordinal();
 514         native_paint_slider(widget, state, shadowType.ordinal(), detail,
 515                     x - x0, y - y0, w, h, orientation.ordinal(), hasFocus);
 516     }
 517 
 518     public void paintVline(Graphics g, SynthContext context,
 519             Region id, int state, String detail, int x, int y, int w, int h) {
 520 
 521         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 522         int widget = getWidgetType(context.getComponent(), id).ordinal();
 523         native_paint_vline(widget, state, detail, x - x0, y - y0, w, h);
 524     }
 525 
 526     public void paintBackground(Graphics g, SynthContext context,
 527             Region id, int state, Color color, int x, int y, int w, int h) {
 528 
 529         state = GTKLookAndFeel.synthStateToGTKStateType(state).ordinal();
 530         int widget = getWidgetType(context.getComponent(), id).ordinal();
 531         native_paint_background(widget, state, x - x0, y - y0, w, h);
 532     }
 533 
 534     private final static ColorModel[] COLOR_MODELS = {
 535         // Transparency.OPAQUE
 536         new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000),
 537         // Transparency.BITMASK
 538         new DirectColorModel(25, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x01000000),
 539         // Transparency.TRANSLUCENT
 540         ColorModel.getRGBdefault(),
 541     };
 542 
 543     private final static int[][] BAND_OFFSETS = {
 544         { 0x00ff0000, 0x0000ff00, 0x000000ff },             // OPAQUE
 545         { 0x00ff0000, 0x0000ff00, 0x000000ff, 0x01000000 }, // BITMASK
 546         { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }  // TRANSLUCENT
 547     };
 548 
 549 
 550     /**
 551      * Paint a cached image identified by its size and a set of additional
 552      * arguments, if there's one.
 553      *
 554      * @return true if a cached image has been painted, false otherwise
 555      */
 556     public boolean paintCachedImage(Graphics g,
 557             int x, int y, int w, int h, Object... args) {
 558         if (w <= 0 || h <= 0) {
 559             return true;
 560         }
 561 
 562         // look for cached image
 563         Image img = cache.getImage(getClass(), null, w, h, args);
 564         if (img != null) {
 565             g.drawImage(img, x, y, null);
 566             return true;
 567         }
 568         return false;
 569     }
 570 
 571     /*
 572      * Allocate a native offscreen buffer of the specified size.
 573      */
 574     public void startPainting(Graphics g,
 575             int x, int y, int w, int h, Object... args) {
 576         nativeStartPainting(w, h);
 577         x0 = x;
 578         y0 = y;
 579         w0 = w;
 580         h0 = h;
 581         graphics = g;
 582         cacheArgs = args;
 583     }
 584 
 585     /**
 586      * Convenience method that delegates to finishPainting() with
 587      * caching enabled.
 588      */
 589     public BufferedImage finishPainting() {
 590         return finishPainting(true);
 591     }
 592 
 593     /**
 594      * Called to indicate that painting is finished. We create a new
 595      * BufferedImage from the offscreen buffer, (optionally) cache it,
 596      * and paint it.
 597      */
 598     public BufferedImage finishPainting(boolean useCache) {
 599         DataBufferInt dataBuffer = new DataBufferInt(w0 * h0);
 600         // Note that stealData() requires a markDirty() afterwards
 601         // since we modify the data in it.
 602         int transparency =
 603             nativeFinishPainting(SunWritableRaster.stealData(dataBuffer, 0),
 604                                  w0, h0);
 605         SunWritableRaster.markDirty(dataBuffer);
 606 
 607         int[] bands = BAND_OFFSETS[transparency - 1];
 608         WritableRaster raster = Raster.createPackedRaster(
 609                 dataBuffer, w0, h0, w0, bands, null);
 610 
 611         ColorModel cm = COLOR_MODELS[transparency - 1];
 612         BufferedImage img = new BufferedImage(cm, raster, false, null);
 613         if (useCache) {
 614             cache.setImage(getClass(), null, w0, h0, cacheArgs, img);
 615         }
 616         graphics.drawImage(img, x0, y0, null);
 617         return img;
 618     }
 619 
 620     /**
 621      * Notify native layer of theme change, and flush cache
 622      */
 623     public void themeChanged() {
 624         synchronized(sun.awt.UNIXToolkit.GTK_LOCK) {
 625             native_switch_theme();
 626         }
 627         cache.flush();
 628     }
 629 
 630     /* GtkSettings enum mirrors that in gtk2_interface.h */
 631     public Object getSetting(Settings property) {
 632         synchronized(sun.awt.UNIXToolkit.GTK_LOCK) {
 633             return native_get_gtk_setting(property.ordinal());
 634         }
 635     }
 636 
 637     /**
 638      * Sets up the GtkAdjustment values for the native GtkRange widget
 639      * associated with the given region (e.g. SLIDER, SCROLL_BAR).
 640      */
 641     void setRangeValue(SynthContext context, Region id,
 642                        double value, double min, double max, double visible) {
 643         int widget = getWidgetType(context.getComponent(), id).ordinal();
 644         nativeSetRangeValue(widget, value, min, max, visible);
 645     }
 646 }