/*
 * @(#)GTKPainter.java	1.67 04/06/09
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.sun.java.swing.plaf.gtk;

import sun.swing.plaf.synth.SynthUI;

import javax.swing.plaf.synth.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;

/**
 * @version 1.67, 06/09/04
 * @author Joshua Outwater
 * @author Scott Violet
 */
// Need to support:
// default_outside_border: Insets when default.
// interior_focus: Indicates if focus should appear inside border, or
//                       outside border.
// focus-line-width: Integer giving size of focus border
// focus-padding: Integer giving padding between border and focus
//        indicator.
// focus-line-pattern: 
//
class GTKPainter extends SynthPainter {
    static final GTKPainter INSTANCE = new GTKPainter();
    static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
    private static final Insets TEMP_INSETS = new Insets(0, 0, 0, 0);

    /**
     * Space for the default border.
     */
    private static final Insets BUTTON_DEFAULT_BORDER_INSETS =
                                       new Insets(1, 1, 1, 1);


    private GTKEngine getEngine(SynthContext context) {
        return ((GTKStyle)context.getStyle()).getEngine(context);
    }

    private String getName(SynthContext context) {
        return (context.getRegion().isSubregion()) ? null :
               context.getComponent().getName();
    }

    Insets getInsets(SynthContext state, Insets insets) {
        Region id = state.getRegion();
        String name = (id.isSubregion()) ? null :
                                           state.getComponent().getName();

        if (insets == null) {
            insets = new Insets(0, 0, 0, 0);
        }
        else {
            insets.top = insets.bottom = insets.left = insets.right = 0;
        }
        if (id == Region.BUTTON || id == Region.TOGGLE_BUTTON
                || id == Region.CHECK_BOX || id == Region.RADIO_BUTTON
                || id == Region.ARROW_BUTTON || id == Region.TOGGLE_BUTTON
                || id == Region.RADIO_BUTTON_MENU_ITEM
                || id == Region.CHECK_BOX_MENU_ITEM) {
            if ("Spinner.previousButton" == name ||
                    "Spinner.nextButton" == name) {
                return getSpinnerButtonInsets(state, insets);
            } else {
                return getButtonInsets(state, insets);
            }
        }
        else if (id == Region.MENU 
              || id == Region.MENU_ITEM) {
            return getMenuItemInsets(state, insets);
        }
        else if (id == Region.FORMATTED_TEXT_FIELD) {
            return getTextFieldInsets(state, insets);
        }
        else if (id == Region.INTERNAL_FRAME) {
            insets = Metacity.INSTANCE.getBorderInsets(state, insets);
        }
        else if (id == Region.DESKTOP_ICON) {
	    // Use default 0, 0, 0, 0
        }
        else if (id == Region.LABEL) {
            if ("TableHeader.renderer" == name) {
                return getButtonInsets(state, insets);
            }
            else if ("ComboBox.renderer" == name ||
                     "ComboBox.listRenderer" == name) {
                return getTextFieldInsets(state, insets);
            }
            else if ("Tree.cellRenderer" == name) {
                return getTreeCellRendererInsets(state, insets);
            }
        }
        else if (id == Region.MENU_BAR) {
            return getMenuBarInsets(state, insets);
        }
        else if (id == Region.OPTION_PANE) {
            return getOptionPaneInsets(state, insets);
        }
        else if (id == Region.POPUP_MENU) {
            return getPopupMenuInsets(state, insets);
        }
        else if (id == Region.PROGRESS_BAR) {
            return getProgressBarInsets(state, insets);
        }
        else if (id == Region.SCROLL_BAR) {
            return getScrollBarInsets(state, insets);
        }
        else if (id == Region.SEPARATOR) {
            return getSeparatorInsets(state, insets);
        }
        else if (id == Region.SLIDER) {
            return getSliderInsets(state, insets);
        }
        else if (id == Region.SLIDER_TRACK) {
            return getSliderTrackInsets(state, insets);
        }
        else if (id == Region.SPINNER) {
            return getSpinnerInsets(state, insets);
        }
        else if (id == Region.TABBED_PANE) {
            return getTabbedPaneInsets(state, insets);
        }
        else if (id == Region.TABBED_PANE_CONTENT) {
            return getTabbedPaneContentInsets(state, insets);
        }
        else if (id == Region.TABBED_PANE_TAB) {
            return getTabbedPaneTabInsets(state, insets);
        }
        else if (id == Region.TEXT_FIELD || 
                 id == Region.PASSWORD_FIELD) {
            if (name == "Tree.cellEditor") {
                return getTreeCellEditorInsets(state, insets);
            }
            return getTextFieldInsets(state, insets);
        }
        else if (id == Region.TOOL_BAR) {
            return getToolBarInsets(state, insets);
        }
        else if (id == Region.TOOL_TIP) {
            return getToolTipInsets(state, insets);
        }
        return insets;
    }

    public void paintCheckBoxBackground(SynthContext context,
                                        Graphics g, int x, int y,
                                        int w, int h) {
        paintRadioButtonCheckBoxBackground(context, getEngine(context),
                                           g, x, y, w, h);
    }

    // FORMATTED_TEXT_FIELD
    public void paintFormattedTextFieldBackground(SynthContext context,
                                          Graphics g, int x, int y,
                                          int w, int h) {
        paintTextFieldBackground(context, getEngine(context), g, x, y, w, h);
    }

    //
    // TOOL_DRAG_WINDOW
    //
    public void paintToolBarDragWindowBackground(SynthContext context,
                                     Graphics g, int x, int y,
                                     int w, int h) {
        paintToolBarBackground(context, getEngine(context), g, x, y, w, h);
    }


    //
    // TOOL_BAR
    //
    public void paintToolBarBackground(SynthContext context,
                                     Graphics g, int x, int y,
                                     int w, int h) {
        paintToolBarBackground(context, getEngine(context), g, x, y, w, h);
    }

    //
    // PASSWORD_FIELD
    //
    public void paintPasswordFieldBackground(SynthContext context,
                                     Graphics g, int x, int y,
                                     int w, int h) {
        paintTextFieldBackground(context, g, x, y, w, h);  
    }
    
    //
    // TEXT_FIELD
    //
    public void paintTextFieldBackground(SynthContext context,
                                          Graphics g, int x, int y,
                                          int w, int h) {
        GTKEngine engine = getEngine(context);
        if (getName(context) == "Tree.cellEditor") {
            paintTreeCellEditorBackground(context, engine, g, x, y, w, h);
        }
        else {
            paintTextFieldBackground(context, engine, g, x, y, w, h);
        }
    }

    //
    // RADIO_BUTTON
    //
    public void paintRadioButtonBackground(SynthContext context,
                                     Graphics g, int x, int y,
                                     int w, int h) {
        paintRadioButtonCheckBoxBackground(context, getEngine(context),
                                           g, x, y, w, h);
    }

    //
    // RADIO_BUTTON_MENU_ITEM
    //
    public void paintRadioButtonMenuItemBackground(SynthContext context,
                                     Graphics g, int x, int y,
                                     int w, int h) {
        paintRadioButtonCheckBoxMenuItemBackground(context, getEngine(context),
                                                   g, x, y, w, h);
    }

    // 
    // LABEL
    //
    public void paintLabelBackground(SynthContext context,
                                     Graphics g, int x, int y,
                                     int w, int h) {
        GTKEngine engine = getEngine(context);
        String name = getName(context);
        if ("TableHeader.renderer" == name ||
              name == "GTKFileChooser.directoryListLabel" ||
              name == "GTKFileChooser.fileListLabel") {
            paintButtonBackground(context, engine, g, x, y, w, h, "button");
        }
        else if (name == "ComboBox.renderer") {
            paintTextFieldBackground(context, engine, g, x, y, w, h);
        }
    }

    //
    // INTERNAL_FRAME
    // 
    public void paintInternalFrameBorder(SynthContext context,
                                      Graphics g, int x, int y,
                                      int w, int h) {
        Metacity.INSTANCE.paintFrameBorder(context, g, x, y, w, h);
    }

    //
    // DESKTOP_PANE
    //
    public void paintDesktopPaneBackground(SynthContext context,
                                           Graphics g, int x, int y,
                                           int w, int h) {
        GTKEngine engine = getEngine(context);
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                          context.getRegion(), context.getComponentState());
        engine.paintFlatBoxNormal(context, g, gtkState, "base", x, y, w, h);
    }

    //
    // DESKTOP_ICON
    //
    public void paintDesktopIconBorder(SynthContext context,
                                           Graphics g, int x, int y,
                                           int w, int h) {
        Metacity.INSTANCE.paintFrameBorder(context, g, x, y, w, h);
    }

    // 
    // CHECK_BOX_MENU_ITEM
    //
    public void paintCheckBoxMenuItemBackground(SynthContext context,
                                                Graphics g, int x, int y,
                                                int w, int h) {
        paintRadioButtonCheckBoxMenuItemBackground(context, getEngine(context),
                                                   g, x, y, w, h);
    }

    //
    // BUTTON
    //
    private Insets getButtonInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int focusSize = style.getClassSpecificIntValue(context,
                                                       "focus-line-width",1);
        int focusPad = style.getClassSpecificIntValue(context,
                                                      "focus-padding", 1);
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        int w = focusSize + focusPad + xThickness;
        int h = focusSize + focusPad + yThickness;
        Component component = context.getComponent();

        insets.left = insets.right = w;
        insets.top = insets.bottom = h;
        if ((component instanceof JButton) &&
                       ((JButton)component).isDefaultCapable()) {
            Insets defaultInsets = style.getClassSpecificInsetsValue(context,
                          "default-border", BUTTON_DEFAULT_BORDER_INSETS);
            insets.left += defaultInsets.left;
            insets.right += defaultInsets.right;
            insets.top += defaultInsets.top;
            insets.bottom += defaultInsets.bottom;
        }
        return insets;
    }

    public void paintButtonBackground(SynthContext context,
                                      Graphics g, int x, int y,
                                      int w, int h) {
        String name = getName(context);
        if (name != null && name.startsWith("InternalFrameTitlePane.")) {
            Metacity.INSTANCE.paintButtonBackground(context, g, x, y, w, h);
        } else {
            paintButtonBackground(context, getEngine(context), g, x, y, w, h,
                                  "button");
        }
    }

    private void paintButtonBackground(SynthContext context, GTKEngine engine,
                                       Graphics g, int x, int y, int w, int h,
                                       String detail) {
        // paint the default shadow
        int state = context.getComponentState();
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                                      context.getRegion(), state);
        GTKStyle style = (GTKStyle)context.getStyle();
        boolean interiorFocus = style.getClassSpecificBoolValue(context,
                                                 "interior-focus", true);
        int focusSize = style.getClassSpecificIntValue(context,
                                                       "focus-line-width",1);
        int focusPad = style.getClassSpecificIntValue(context, "focus-padding",
                                                      1);
        int totalFocusSize = focusSize + focusPad;
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        Component component = context.getComponent();
        AbstractButton button = (component instanceof AbstractButton) ?
                                (AbstractButton)component : null;
        boolean paintBG = (button == null || (button.isContentAreaFilled() &&
                                              button.isBorderPainted()));

        // Paint the default indicator
        if ((component instanceof JButton) &&
                       ((JButton)component).isDefaultCapable()) {
            Insets defaultInsets = (Insets)style.getClassSpecificInsetsValue(
                      context, "default-border", BUTTON_DEFAULT_BORDER_INSETS);

            if (paintBG && (state & SynthConstants.DEFAULT) ==
                           SynthConstants.DEFAULT) {
                engine.paintBox(context, g, SynthConstants.ENABLED,
                                GTKConstants.SHADOW_IN, "buttondefault",
                                x, y, w, h);
            }
            x += defaultInsets.left;
            y += defaultInsets.top;
            w -= (defaultInsets.left + defaultInsets.right);
            h -= (defaultInsets.top + defaultInsets.bottom);
        }

        // Render the box.
        if (!interiorFocus && (state & SynthConstants.FOCUSED) ==
                              SynthConstants.FOCUSED) {
            x += totalFocusSize;
            y += totalFocusSize;
            w -= 2 * totalFocusSize;
            h -= 2 * totalFocusSize;
        }
        if (paintBG && (!(component.getParent() instanceof JToolBar) ||
                        gtkState != SynthConstants.ENABLED)) {
            int shadowType;
            if ((state & SynthConstants.PRESSED) == SynthConstants.PRESSED) {
                shadowType = GTKConstants.SHADOW_IN;
            }
            else {
                shadowType = GTKConstants.SHADOW_OUT;
            }
            engine.paintBox(context, g, gtkState,  shadowType, detail,
                            x, y, w, h);
        }

        // focus
        if ((state & SynthConstants.FOCUSED) == SynthConstants.FOCUSED &&
                     (button == null || button.isFocusPainted())) {
            if (interiorFocus) {
                x += xThickness + focusPad;
                y += yThickness + focusPad;
                w -= 2 * (xThickness + focusPad);
                h -= 2 * (yThickness + focusPad);
            }
            else {
                x -= totalFocusSize;
                y -= totalFocusSize;
                w += 2 * totalFocusSize;
                h += 2 * totalFocusSize;
            }
            engine.paintFocus(context, g, gtkState, detail, x, y, w, h);
        }
    }


    //
    // ARROW_BUTTON
    //
    public void paintArrowButtonForeground(SynthContext context, Graphics g,
                                           int x, int y, int w, int h,
                                           int direction) {
        GTKEngine engine = getEngine(context);
        int state = GTKLookAndFeel.synthStateToGTKState(
                           Region.ARROW_BUTTON, context.getComponentState());
        int shadowType;

        if (state == SynthConstants.PRESSED) {
            shadowType = GTKConstants.SHADOW_IN;
        }
        else {
            shadowType = GTKConstants.SHADOW_OUT;
        }
        switch (direction) {
        case SwingConstants.NORTH:
            direction = GTKConstants.ARROW_UP;
            break;
        case SwingConstants.SOUTH:
            direction = GTKConstants.ARROW_DOWN;
            break;
        case SwingConstants.EAST:
            direction = GTKConstants.ARROW_RIGHT;
            break;
        case SwingConstants.WEST:
            direction = GTKConstants.ARROW_LEFT;
            break;
        }
        Component c = context.getComponent();
        String name = c.getName();
        String detail = "arrow";

        if (name == "ScrollBar.button") {
            if (c.getParent() instanceof JScrollBar) {
                if (((JScrollBar)c.getParent()).getOrientation() ==
                                   SwingConstants.HORIZONTAL) {
                    detail = "hscrollbar";
                }
                else {
                    detail = "vscrollbar";
                }
            }
        }
        else if (name == "Spinner.nextButton") {
            detail = "spinbutton";
        }
        else if (name == "Spinner.previousButton") {
            detail = "spinbutton";
        }

        engine.paintArrow(context, g, state, shadowType, direction,
                          detail, x, y, w, h);
    }

    public void paintArrowButtonBackground(SynthContext context,
                                            Graphics g,
                                            int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        Component c = context.getComponent();
        String name = c.getName();
        String detail = "button";

        if (name == "ScrollBar.button") {
            if (c.getParent() instanceof JScrollBar) {
                if (((JScrollBar)c.getParent()).getOrientation() ==
                                   SwingConstants.HORIZONTAL) {
                    detail = "hscrollbar";
                }
                else {
                    detail = "vscrollbar";
                }
            }
        } else if ("Spinner.previousButton" == name) {
            detail = "spinbutton_down";
        } else if ("Spinner.nextButton" == name) {
            detail = "spinbutton_up";
        }
        paintButtonBackground(context, engine, g, x, y, w, h, detail);
    }

    //
    // RADIO_BUTTON, CHECK_BUTTON 
    //
    private void paintRadioButtonCheckBoxBackground(SynthContext context,
                                            GTKEngine engine, Graphics g,
                                            int x, int y, int w, int h) {
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                context.getRegion(), context.getComponentState());

        if ((gtkState & SynthConstants.MOUSE_OVER) != 0) {
            engine.paintFlatBoxNormal(context, g, SynthConstants.MOUSE_OVER,
                                "checkbutton", x, y, w, h);
        }
    }


    //
    // CHECK_BUTTON_MENU_ITEM
    //
    private void paintRadioButtonCheckBoxMenuItemBackground(
                                            SynthContext context,
                                            GTKEngine engine, Graphics g,
                                            int x, int y, int w, int h) {
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                context.getRegion(), context.getComponentState());
        if ((context.getComponentState() & SynthConstants.MOUSE_OVER) != 0) {
            if (GTKLookAndFeel.is2_2()) {
                engine.paintBox(context, g, gtkState, GTKConstants.SHADOW_NONE,
                                "menuitem", x, y, w, h);
            }
            else {
                engine.paintBox(context, g, gtkState, GTKConstants.SHADOW_OUT,
                                "menuitem", x, y, w, h);
            }
        }
    }


    //
    // LIST
    //
    public void paintListBackground(SynthContext context, Graphics g,
                                    int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        int state = GTKLookAndFeel.synthStateToGTKState(
                Region.LIST, context.getComponentState());

        if (context.getComponent().getName() == "ComboBox.list") {
            // Paint it's shadow.
        }
        engine.paintFlatBoxText(context, g, state, "base", x, y, w, h);

    }

    //
    // MENU_BAR
    //
    private Insets getMenuBarInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int internalPadding = style.getClassSpecificIntValue(context,
                                                       "internal-padding", 0);
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        insets.top = insets.bottom = internalPadding + yThickness;
        insets.left = insets.right = internalPadding + xThickness;
        return insets;
    }

    public void paintMenuBarBackground(SynthContext context,
                                        Graphics g,
                                        int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        GTKStyle style = (GTKStyle)context.getStyle();
        int shadowType = style.getClassSpecificIntValue(context,
                "shadow-type", GTKConstants.SHADOW_OUT);
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                context.getRegion(), context.getComponentState());

        engine.paintBox(context, g, gtkState, shadowType, "menubar",
                x, y, w, h);
    }

    //
    // MENU
    //
    public void paintMenuBackground(SynthContext context,
                                     Graphics g,
                                     int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                context.getRegion(), context.getComponentState());
        if (gtkState == SynthConstants.MOUSE_OVER) {
            if (GTKLookAndFeel.is2_2()) {
                engine.paintBox(context, g, gtkState, GTKConstants.SHADOW_NONE,
                                "menuitem", x, y, w, h);
            }
            else {
                engine.paintBox(context, g, gtkState, GTKConstants.SHADOW_OUT,
                                "menuitem", x, y, w, h);
            }
        }
    }

    //
    // MENU_ITEM
    //
    private Insets getMenuItemInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int internalPadding = style.getClassSpecificIntValue(context,
                                                       "internal-padding", 0);
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        insets.top = insets.bottom = internalPadding + yThickness;
        insets.left = insets.right = internalPadding + xThickness;
        return insets;
    }

    public void paintMenuItemBackground(SynthContext context,
                                     Graphics g,
                                     int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                context.getRegion(), context.getComponentState());
        if ((context.getComponentState() & SynthConstants.MOUSE_OVER) != 0) {
            if (GTKLookAndFeel.is2_2()) {
                engine.paintBox(context, g, gtkState, GTKConstants.SHADOW_NONE,
                                "menuitem", x, y, w, h);
            }
            else {
                engine.paintBox(context, g, gtkState, GTKConstants.SHADOW_OUT,
                                "menuitem", x, y, w, h);
            }
        }
    }

    private Insets getPopupMenuInsets(SynthContext context, Insets insets) {
        insets.left = insets.right = insets.top = insets.bottom = 2;
        return insets;
    }
    
    public void paintPopupMenuBackground(SynthContext context, Graphics g,
                                        int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                context.getRegion(), context.getComponentState());
        engine.paintBox(context, g, gtkState, GTKConstants.SHADOW_OUT,
                "menu", x, y, w, h);

        GTKStyle style = (GTKStyle)context.getStyle();
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();

        engine.paintBackground(context, g, gtkState, style.getGTKColor(
               context.getComponent(), context.getRegion(), gtkState,
               GTKColorType.BACKGROUND), x + xThickness, y + yThickness,
               w - xThickness - xThickness, h - yThickness - yThickness);
    }

    //
    // PROGRESS_BAR
    //
    private Insets getProgressBarInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        insets.left = insets.right = style.getXThickness();
        insets.top = insets.bottom = style.getYThickness();
        return insets;
    }

    public void paintProgressBarBackground(SynthContext context,
                                            Graphics g,
                                            int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        GTKStyle style = (GTKStyle)context.getStyle();
        // Draw the trough.
        engine.paintBox(context, g, SynthConstants.ENABLED,
                            GTKConstants.SHADOW_IN, "trough", x, y, w, h);

    }

    public void paintProgressBarForeground(SynthContext context, Graphics g,
                                            int x, int y, int w, int h,
                                            int orientation) {
        // Draw the actual progress of the progress bar.
        if (w != 0 || h != 0) {
            getEngine(context).paintBox(context, g, SynthConstants.MOUSE_OVER,
                GTKConstants.SHADOW_OUT, "bar", x, y, w, h);
        }
    }

    public void paintViewportBorder(SynthContext context, Graphics g,
                                           int x, int y, int w, int h) {
        getEngine(context).paintShadow(context, g, SynthConstants.ENABLED,
                           GTKConstants.SHADOW_IN, "scrolled_window",
                           x, y, w, h);
    }

    public void paintScrollPaneBorder(SynthContext context, Graphics g,
                                           int x, int y, int w, int h) {
        getEngine(context).paintShadow(context, g, SynthConstants.ENABLED,
                           GTKConstants.SHADOW_IN, "scrolled_window",
                           x, y, w, h);
    }

    Insets getScrollPaneInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        insets.right = insets.left = style.getXThickness();
        insets.top = insets.bottom = style.getYThickness();
        return insets;
    }

    //
    // SEPARATOR
    //
    private Insets getSeparatorInsets(SynthContext context, Insets insets) {
        return insets;
    }

    public void paintSeparatorBackground(SynthContext context,
                                          Graphics g,
                                          int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        if (((JSeparator)context.getComponent()).getOrientation() ==
                JSeparator.HORIZONTAL) {
            engine.paintHline(context, g, SynthConstants.ENABLED,
                "hseparator", x, y, w, h);
        } else {
            engine.paintVline(context, g, SynthConstants.ENABLED,
                "vseparator", x, y, w, h);
        }
    }

    //
    // SLIDER
    //
    private Insets getSliderInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        insets.top = insets.bottom = yThickness;
        insets.right = insets.left = xThickness;
        return insets;
    }

    private Insets getSliderTrackInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int focusSize = style.getClassSpecificIntValue(context,
                                                        "focus-line-width", 1);
        int focusPad = style.getClassSpecificIntValue(context,
                                                      "focus-padding", 1);
        insets.top = insets.bottom = insets.left = insets.right =
            focusSize + focusPad;
        return insets;
    }

    public void paintSliderTrackBackground(SynthContext context,
                                       Graphics g,
                                       int x, int y, int w,int h) {
        GTKEngine engine = getEngine(context);
        GTKStyle style = (GTKStyle)context.getStyle();

        int state = context.getComponentState();
        int gtkState = GTKLookAndFeel.synthStateToGTKState(context.getRegion(),
                                                           state);
        engine.paintBox(context, g, gtkState,
                            GTKConstants.SHADOW_IN, "trough", x, y, w, h);

        if ((state & SynthConstants.FOCUSED) == SynthConstants.FOCUSED) {
            int focusSize = style.getClassSpecificIntValue(context,
                "focus-line-width", 1);
            focusSize += style.getClassSpecificIntValue(context,
                "focus-padding", 1);
            x -= focusSize;
            y -= focusSize;
            w += 2 * focusSize;
            h += 2 * focusSize;
            engine.paintFocus(context, g, SynthConstants.ENABLED,
                              "trough", x, y, w, h);
        }
    }

    public void paintSliderThumbBackground(SynthContext context,
                 Graphics g, int x, int y, int w, int h, int orientation) {
        GTKEngine engine = getEngine(context);
        if (orientation == JSlider.HORIZONTAL) {
            orientation = GTKConstants.HORIZONTAL;
        }
        else {
            orientation = GTKConstants.VERTICAL;
        }
        engine.paintSlider(context, g, GTKLookAndFeel.synthStateToGTKState(
               context.getRegion(), context.getComponentState()),
               GTKConstants.SHADOW_OUT, "slider", x, y, w, h, orientation);
    }

    //
    // SPINNER
    //
    public void paintSpinnerBackground(SynthContext context,
                                        Graphics g,
                                        int x, int y, int w, int h) {
        // This is handled in paintTextFieldBackground
    }

    private Insets getSpinnerInsets(SynthContext context, Insets insets) {
        // Spinner's have no insets, the text field gets the insets.
        return insets;
    }

    private Insets getSpinnerButtonInsets(SynthContext context, Insets insets) {
        insets.top = insets.bottom = 1;
        insets.right = insets.left = 1;
        return insets;
    }

    //
    // SPLIT_PANE_DIVIDER
    //
    public void paintSplitPaneDividerBackground(SynthContext context,
                                       Graphics g,
                                       int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        int orientation;
        if (((JSplitPane)context.getComponent()).getOrientation() ==
                                 JSplitPane.VERTICAL_SPLIT) {
            orientation = GTKConstants.HORIZONTAL;
        }
        else {
            orientation = GTKConstants.VERTICAL;
        }

        engine.paintHandle(context, g, GTKLookAndFeel.synthStateToGTKState(
                context.getRegion(), context.getComponentState()),
                GTKConstants.UNDEFINED, "paned", x, y, w, h, orientation);
    }

    public void paintSplitPaneDragDivider(SynthContext context,
                                       Graphics g,int x, int y, int w, int h,
                                       int orientation) {
        paintSplitPaneDividerForeground(context, g, x, y, w, h, orientation);
    }

    public void paintSplitPaneDividerForeground(SynthContext context,
                                       Graphics g,int x, int y, int w, int h,
                                       int orientation) {
        g.setColor(context.getStyle().getColor(context,
                                               GTKColorType.BACKGROUND));
        g.fillRect(x, y, w, h);
        paintSplitPaneDividerBackground(context, g, x, y, w, h);
    }

    //
    // TABBED_PANE
    //
    private Insets getTabbedPaneInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        insets.top = insets.bottom = yThickness;
        insets.right = insets.left = xThickness;
        return insets;
    }

    //
    // TABBED_PANE_CONTENT
    //
    private Insets getTabbedPaneContentInsets(SynthContext context,
            Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        insets.top = insets.bottom = yThickness;
        insets.right = insets.left = xThickness;
        return insets;
    }

    public void paintTabbedPaneContentBackground(SynthContext context,
                                            Graphics g,
                                            int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        JTabbedPane tabPane = (JTabbedPane)context.getComponent();
        GTKStyle style = (GTKStyle)context.getStyle();
        Region region = context.getRegion();
        int placement = GTKLookAndFeel.SwingOrientationConstantToGTK(
                                     tabPane.getTabPlacement());
        int start = 0;
        int size = 0;

        int selectedIndex = tabPane.getSelectedIndex();
        if (selectedIndex != -1) {
            Rectangle tabBounds = tabPane.getBoundsAt(selectedIndex);

            if (placement == GTKConstants.TOP ||
                             placement == GTKConstants.BOTTOM) {
                start = tabBounds.x - 1;
                size = tabBounds.width;
            }
            else {
                start = tabBounds.y - 1;
                size = tabBounds.height;
            }
        }
        engine.paintBoxGap(context, g, GTKLookAndFeel.synthStateToGTKState(
                    context.getRegion(), context.getComponentState()),
                    GTKConstants.SHADOW_OUT, "notebook", x, y, w, h,
                    placement, start, size);
    }

    //
    // TABBED_PANE_TAB
    //
    private Insets getTabbedPaneTabInsets(SynthContext context,
            Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        int focusSize = 0;
        int pad = 2;

        focusSize = style.getClassSpecificIntValue(context,
                "focus-line-width",1);
        insets.left = insets.right = focusSize + pad + xThickness;
        insets.top = insets.bottom = focusSize + pad + yThickness;
        return insets;
    }

    public void paintTabbedPaneTabBackground(SynthContext context,
                                           Graphics g,
                                           int x, int y, int w, int h,
                                           int tabIndex) {
        GTKEngine engine = getEngine(context);
        GTKStyle style = (GTKStyle)context.getStyle();
        int state = context.getComponentState();
        int selectedIndex = ((JTabbedPane)context.getComponent()).
                            getSelectedIndex();
        int offset = (selectedIndex == tabIndex) ? 0 : 2;
        int side;
        state = GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state);
        switch (((JTabbedPane)context.getComponent()).getTabPlacement()) {
        case SwingConstants.TOP:
            side = GTKConstants.BOTTOM;
            y += offset;
            h -= offset;
            break;
        case SwingConstants.BOTTOM:
            h -= offset;
            side = GTKConstants.TOP;
            break;
        case SwingConstants.LEFT:
            x += offset;
            w -= offset;
            side = GTKConstants.RIGHT;
            break;
        default:
            w -= offset;
            side = GTKConstants.LEFT;
            break;
        }
        engine.paintExtension(context, g, state, GTKConstants.SHADOW_OUT,
                "tab", x, y, w, h, side, tabIndex);
    }

    //
    // TEXT_AREA
    //
    public void paintTextAreaBackground(SynthContext context,
                                         Graphics g,
                                         int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        GTKStyle style = (GTKStyle)context.getStyle();
        int state = context.getComponentState();
        engine.paintFlatBoxText(context, g, state, "base", x, y, w, h);
    }

    //
    // TEXT_FIELD
    //
    // NOTE: comobobox and FormattedTextField calls this too.
    private void paintTextFieldBackground(SynthContext context,
                                          GTKEngine engine, Graphics g,
                                          int x, int y, int w, int h) {
        GTKStyle style = (GTKStyle)context.getStyle();
        boolean interiorFocus = style.getClassSpecificBoolValue(context,
                                                 "interior-focus", true);
        int state = context.getComponentState();
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                                        context.getRegion(), state);
        int focusSize;
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();

        paintTextBackground(context, engine, g, x, y, w, h);
        state = GTKLookAndFeel.synthStateToGTKState(context.getRegion(),state);

        if (!interiorFocus && (state & SynthConstants.FOCUSED) ==
                              SynthConstants.FOCUSED) {
            focusSize = style.getClassSpecificIntValue(context,
                                                       "focus-line-width",1);
            x += focusSize;
            y += focusSize;
            w -= 2 * focusSize;
            h -= 2 * focusSize;
        }
        else {
            focusSize = 0;
        }

        engine.paintShadow(context, g, SynthConstants.ENABLED,
                           GTKConstants.SHADOW_IN, "entry", x, y, w, h);

        g.setColor(style.getGTKColor(context.getComponent(),
                   context.getRegion(), SynthConstants.ENABLED,
                   GTKColorType.TEXT_BACKGROUND));
        g.fillRect(x + xThickness, y + yThickness, w - (2 * xThickness),
               h - (2 * yThickness));
                               

        engine.paintFlatBoxText(context, g, SynthConstants.ENABLED, "entry_bg",
               x + xThickness, y + yThickness, w - (2 * xThickness),
               h - (2 * yThickness));

        if (focusSize > 0) {
            x -= focusSize;
            y -= focusSize;
            w += 2 * focusSize;
            h += 2 * focusSize;
            engine.paintFocus(context, g, gtkState, "entry", x, y, w, h);
        }
    }

    // NOTE: this is called for ComboBox, and FormattedTextField. too.
    private Insets getTextFieldInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        boolean interiorFocus = style.getClassSpecificBoolValue(context,
                                                 "interior-focus", true);
        int focusSize = 0;

        if (!interiorFocus) {
            focusSize = style.getClassSpecificIntValue(context,
                                                       "focus-line-width",1);
        }
        
        insets.left = insets.right = focusSize + xThickness + 2;
        insets.top = insets.bottom = focusSize + yThickness + 2;
        return insets;
    }


    private void paintTextBackground(SynthContext context,
                                     GTKEngine engine, Graphics g,
                                     int x, int y, int w,int h) {
        // Text is odd, the background is filled in with
        // TEXT_BACKGROUND
        JComponent c = context.getComponent();
        // Text is odd in that it uses the TEXT_BACKGROUND vs BACKGROUND.
        if (c.isOpaque() && c.getBackground() instanceof ColorUIResource) {
            g.setColor(((GTKStyle)context.getStyle()).getGTKColor(
                       context.getComponent(), context.getRegion(),
                       SynthConstants.ENABLED, GTKColorType.TEXT_BACKGROUND));
            g.fillRect(x, y, w, h);
        }
    }


    //
    // OPTION_Pane
    //

    private Insets getOptionPaneInsets(SynthContext context, Insets insets) {
        insets.left = insets.right = insets.top = insets.bottom = 6;
        return insets;
    }


    //
    // TOGGLE_BUTTON
    //
    public void paintToggleButtonBackground(SynthContext context,
                                            Graphics g,
                                            int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        int state = context.getComponentState();
        int gtkState = GTKLookAndFeel.synthStateToGTKState(context.getRegion(),
                                                           state);
        GTKStyle style = (GTKStyle)context.getStyle();
        boolean interiorFocus = style.getClassSpecificBoolValue(context,
                                                 "interior-focus", true);
        int focusSize = style.getClassSpecificIntValue(context,
                                                       "focus-line-width",1);
        int focusPad = style.getClassSpecificIntValue(context, "focus-padding",
                                                      1);
        int totalFocusSize = focusSize + focusPad;
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        JToggleButton toggleButton = (JToggleButton)context.getComponent();

        if (!interiorFocus && (state & SynthConstants.FOCUSED) ==
                              SynthConstants.FOCUSED) {
            x += totalFocusSize;
            y += totalFocusSize;
            w -= 2 * totalFocusSize;
            h -= 2 * totalFocusSize;
        }

        int shadow = GTKConstants.SHADOW_OUT;
        if (toggleButton.isSelected() ||
                ((state & SynthConstants.PRESSED) != 0)) {
            shadow = GTKConstants.SHADOW_IN;
        }
        if (toggleButton.isContentAreaFilled() &&
                         toggleButton.isBorderPainted()) {
            engine.paintBox(context, g, gtkState, shadow, "button", x, y, w,h);
        }

        // focus
        if ((state & SynthConstants.FOCUSED) == SynthConstants.FOCUSED &&
                     toggleButton.isFocusPainted()) {
            if (interiorFocus) {
                x += xThickness + focusPad;
                y += yThickness + focusPad;
                w -= 2 * (xThickness + focusPad);
                h -= 2 * (yThickness + focusPad);
            }
            else {
                x -= totalFocusSize;
                y -= totalFocusSize;
                w += 2 * totalFocusSize;
                h += 2 * totalFocusSize;
            }
            engine.paintFocus(context, g, gtkState, "button", x, y, w, h);
        }
    }

    //
    // ROOT_PANE
    //
    public void paintRootPaneBackground(SynthContext context,
                                        Graphics g,
                                        int x, int y, int w, int h) {
        GTKEngine engine = getEngine(context);
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                          context.getRegion(), context.getComponentState());

        engine.paintFlatBoxNormal(context, g, gtkState, "base", x, y, w, h);
    }

    //
    // SCROLL_BAR
    //
    public void paintScrollBarBackground(SynthContext context,
                                          Graphics g,
                                          int x, int y, int w,int h) {
        GTKEngine engine = getEngine(context);
        GTKStyle style = (GTKStyle)context.getStyle();
        int state = context.getComponentState();
        int gtkState = GTKLookAndFeel.synthStateToGTKState(context.getRegion(),
                                                           state);
        int focusSize = style.getClassSpecificIntValue(context,
                                                        "focus-line-width",1);
        int focusPad = style.getClassSpecificIntValue(context,
                                                      "focus-padding", 1);
        int totalFocus = focusSize + focusPad;

        engine.paintBox(context, g, SynthConstants.PRESSED,
                            GTKConstants.SHADOW_IN, "trough", x + totalFocus,
                            y + totalFocus, w - 2 * totalFocus,
                            h - 2 * totalFocus);
        if ((state & SynthConstants.FOCUSED) == SynthConstants.FOCUSED) {
            engine.paintFocus(context, g, SynthConstants.ENABLED,
                              "trough", x, y, w, h);
        }
    }

    private Insets getScrollBarInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        if (context.getComponent().isFocusable()) {
            int focusSize = style.getClassSpecificIntValue(context,
                                                        "focus-line-width",1);
            int focusPad = style.getClassSpecificIntValue(context,
                                                      "focus-padding", 1);
            int w = focusSize + focusPad;
            int h = focusSize + focusPad;

            insets.left = insets.right = w;
            insets.top = insets.bottom = h;
        }
        int troughBorder = style.getClassSpecificIntValue(context,
                                                          "trough-border", 1);
        insets.left += troughBorder;
        insets.right += troughBorder;
        insets.top += troughBorder;
        insets.bottom += troughBorder;
        return insets;
    }


    //
    // SCROLL_BAR_THUMB
    //
    public void paintScrollBarThumbBackground(SynthContext context,
                    Graphics g, int x, int y, int w, int h, int orientation) {
        GTKEngine engine = getEngine(context);
        if (orientation == JScrollBar.VERTICAL) {
            orientation = GTKConstants.VERTICAL;
        }
        else {
            orientation = GTKConstants.HORIZONTAL;
        }
        engine.paintSlider(context, g, GTKLookAndFeel.synthStateToGTKState(
               context.getRegion(), context.getComponentState()),
               GTKConstants.SHADOW_OUT, "slider", x, y, w, h, orientation);
    }

    //
    // TOOL_BAR
    //
    private void paintToolBarBackground(SynthContext context, GTKEngine engine,
                                        Graphics g, int x, int y,
                                        int w, int h) {
        int state = context.getComponentState();
        int gtkState = GTKLookAndFeel.synthStateToGTKState(context.getRegion(),
                state);
        GTKStyle style = (GTKStyle)context.getStyle();

        g.setColor(style.getGTKColor(context.getComponent(),
                context.getRegion(), state, GTKColorType.BACKGROUND));
        engine.paintFlatBox(context, g, gtkState, "handlebox_bin", x, y, w, h);
    }

    private Insets getToolBarInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();
        int xThickness = style.getXThickness();
        int yThickness = style.getYThickness();
        insets.top = insets.bottom = yThickness;
        insets.right = insets.left = xThickness;
        return insets;
    }

    public void paintToolBarContentBackground(SynthContext context,
            Graphics g, int x, int y, int w, int h) {
        int state = context.getComponentState();
        int gtkState = GTKLookAndFeel.synthStateToGTKState(context.getRegion(),
                                                           state);
        getEngine(context).paintShadow(context, g, SynthConstants.ENABLED,
            GTKConstants.SHADOW_OUT, "handlebox_bin", x, y, w, h);
    }

    //
    // TOOL_TIP
    //
    public void paintToolTipBackground(SynthContext context, Graphics g,
                                        int x, int y, int w,int h) {
        getEngine(context).paintFlatBoxNormal(context, g,
                                              SynthConstants.ENABLED,"tooltip",
                                              x, y, w, h);
    }

    private Insets getToolTipInsets(SynthContext context, Insets insets) {
        GTKStyle style = (GTKStyle)context.getStyle();

        insets.left = insets.right = style.getXThickness();
        insets.top = insets.bottom = style.getYThickness();
        return insets;
    }


    //
    // TREE_CELL
    //
    public void paintTreeCellBackground(SynthContext context, Graphics g,
                                        int x, int y, int w,int h) {
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                          context.getRegion(), context.getComponentState());
        // the string arg should alternate based on row being painted, but
        // we currently don't pass that in.
        getEngine(context).paintFlatBoxText(context, g, gtkState, "cell_odd",
                                            x, y, w, h);
    }

    public void paintTreeCellFocus(SynthContext context, Graphics g,
                                    int x, int y, int w,int h) {
        GTKEngine engine = getEngine(context);
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                          context.getRegion(), context.getComponentState());
        engine.paintFocus(context, g, gtkState, "treeview", x, y, w, h);
    }


    //
    // TREE_CELL_RENDERER
    //
    private Insets getTreeCellRendererInsets(SynthContext context,
                                             Insets insets) {
        insets.left = insets.right = insets.top = insets.bottom = 1;
        return insets;
    }


    //
    // TREE_CELL_EDITOR
    //
    private Insets getTreeCellEditorInsets(SynthContext context,
                                             Insets insets) {
        insets.left = insets.right = insets.top = insets.bottom = 1;
        return insets;
    }

    private void paintTreeCellEditorBackground(SynthContext context,
                                     GTKEngine engine, Graphics g,
                                     int x, int y, int w,int h) {
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                          context.getRegion(), context.getComponentState());
        engine.paintFlatBoxText(context, g, gtkState, "entry_bg", x, y, w, h);
    }


    //
    // TREE
    //
    public void paintTreeBackground(SynthContext context, Graphics g,
                                    int x, int y, int w,int h) {
        // As far as I can tell, these don't call into the engine.
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                          context.getRegion(), context.getComponentState());
        g.setColor(((GTKStyle)context.getStyle()).getGTKColor(
                context.getComponent(), context.getRegion(), gtkState,
                GTKColorType.TEXT_BACKGROUND));
        g.fillRect(x, y, w, h);
    }


    //
    // VIEWPORT
    //
    public void paintViewportBackground(SynthContext context, Graphics g,
                                        int x, int y, int w,int h) {
        // As far as I can tell, these don't call into the engine.
        // Also note that you don't want this to call into the engine
        // as if it where to paint a background JViewport wouldn't scroll
        // correctly.
        int gtkState = GTKLookAndFeel.synthStateToGTKState(
                          context.getRegion(), context.getComponentState());
        g.setColor(((GTKStyle)context.getStyle()).getGTKColor(
                context.getComponent(), context.getRegion(), gtkState,
                GTKColorType.TEXT_BACKGROUND));
        g.fillRect(x, y, w, h);
    }



    // Refer to GTKLookAndFeel for details on this.
    static class ListTableFocusBorder extends AbstractBorder implements
                          UIResource {
 
        private SynthContext getContext(Component c) {
            SynthContext context = null;
            
            Component parent = c;
            while(parent != null && 
                    !(parent instanceof JTable) && 
                    !(parent instanceof JList)) {
                parent = parent.getParent();
            }
             
            ComponentUI ui = null;
            if (parent instanceof JTable) {
                ui = ((JTable)parent).getUI();
            } else if (parent instanceof JList) {
                ui = ((JList)parent).getUI();
            }
            
            if (ui instanceof SynthUI) {
                context = ((SynthUI)ui).getContext((JComponent)parent);
            }
            
            return context;
        }
        
        public void paintBorder(Component c, Graphics g, int x, int y,
                                int w, int h) {
            SynthContext context = getContext(c);
            if (context != null) {
                ((GTKStyle)context.getStyle()).getEngine(context).
                    paintFocus(context, g, SynthConstants.SELECTED, 
                            "", x, y, w, h);
            } else {
                g.setColor(Color.BLACK);
                GTKEngine.INSTANCE._paintFocus(
                        g, x, y, w, h, GTKEngine.DEFAULT_FOCUS_PATTERN, 1);
            }
        }
        public Insets getBorderInsets(Component c) {
            int size = 1;
            SynthContext context = getContext(c);
            if (context != null) {
                size = ((GTKStyle)context.getStyle()).getClassSpecificIntValue(
                    context, "focus-line-width", 1);   
            }
            return new Insets(size, size, size, size);
        }
        public Insets getBorderInsets(Component c, Insets i) {
            Insets ins = getBorderInsets(c);
            if (i == null) {
                return ins; 
            }
            i.left = ins.left;
            i.right = ins.right;
            i.top = ins.top;
            i.bottom = ins.bottom;
            return i;
        }
        public boolean isBorderOpaque() {
            return true;
        }
    }
}
