/*
 * @(#)BlueprintEngine.java	1.23 03/12/19
 *
 * 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 java.awt.*;
import java.awt.image.*;
import java.security.AccessController;
import java.util.*;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.synth.*;
import sun.security.action.GetPropertyAction;
import sun.swing.plaf.synth.SynthUI;
/**
 * GTKEngine implementation that renders using images. The images to render
 * are dictated by the BlueprintStyle.Info.
 *
 * @version 1.23 12/19/03
 * @author Joshua Outwater
 */
class BlueprintEngine extends GTKEngine implements GTKConstants {
    /**
     * By default we don't use smooth scaling as it is currently not optimized.
     */
    private static final Object RENDERING_HINT;
    private int COMPONENT_NORTH_WEST = 1;
    private int COMPONENT_NORTH = 2;
    private int COMPONENT_NORTH_EAST = 4;
    private int COMPONENT_WEST = 8;
    private int COMPONENT_CENTER = 16;
    private int COMPONENT_EAST = 32;
    private int COMPONENT_SOUTH_EAST = 64;
    private int COMPONENT_SOUTH = 128;
    private int COMPONENT_SOUTH_WEST = 256;
    private int COMPONENT_ALL = 512;
    private int _clipX1;
    private int _clipX2;
    private int _clipY1;
    private int _clipY2;
    static {
        if ("true".equals((String)AccessController.doPrivileged(
                   new GetPropertyAction("swing.pixmap.smoothScaling")))) {
            RENDERING_HINT = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
        }
        else {
            RENDERING_HINT = null;
        }
    }
    public void paintSlider(SynthContext context, Graphics g, int state,
                           int shadowType, String info,
                           int x, int y, int w, int h, int orientation) {
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)context.getStyle()).
                         getInfo("SLIDER", info, state, shadowType, orientation,
                                 UNDEFINED, UNDEFINED, null))) {
            super.paintSlider(context, g, state, shadowType, info,
                              x, y, w, h, orientation);
        }
    }
    public void paintHline(SynthContext context, Graphics g, int state,
                           String info, int x, int y, int w, int h) {
        SynthStyle style = context.getStyle();
        Component c = context.getComponent();
        // We have a different style to use if we are the child of
        // a popup menu.
        c = c.getParent();
        if (c instanceof JPopupMenu) {
            SynthStyle newStyle = getStyle((JPopupMenu)c,
                    ((JPopupMenu)c).getUI());
            if (newStyle != null) {
                style = newStyle;
            }
        }
        BlueprintStyle.Info blueprintInfo =
                ((BlueprintStyle)style).getInfo("HLINE", info,
                        state, UNDEFINED, GTKConstants.HORIZONTAL,
                        UNDEFINED, UNDEFINED, null);
        if (blueprintInfo != null && blueprintInfo.getImage() != null) {
            themeBlueprintRender(context, g, x, y, w, h,
                    blueprintInfo.getImage(), blueprintInfo.getImageInsets(),
                    COMPONENT_ALL, blueprintInfo.getStretch(), false,
                    blueprintInfo.isBkgMask(), blueprintInfo.isRecolorable(),
                    blueprintInfo.getColorizeColor());
        } else {
            super.paintHline(context, g, state, info, x, y, w, h);
        }
    }
    public void paintVline(SynthContext context, Graphics g, int state,
                           String info, int x, int y, int w, int h) {
        BlueprintStyle.Info blueprintInfo =
                ((BlueprintStyle)context.getStyle()).getInfo("VLINE", info,
                        state, UNDEFINED, GTKConstants.VERTICAL,
                        UNDEFINED, UNDEFINED, null);
        if (blueprintInfo != null && blueprintInfo.getImage() != null) {
            themeBlueprintRender(context, g, x, y, w, h, blueprintInfo.getImage(),
                    blueprintInfo.getImageInsets(), COMPONENT_ALL,
                    blueprintInfo.getStretch(), false,
                    blueprintInfo.isBkgMask(), blueprintInfo.isRecolorable(),
                    blueprintInfo.getColorizeColor());
        } else {
            super.paintVline(context, g, state, info, x, y, w, h);
        }
    }
    public void paintArrow(SynthContext context, Graphics g, int state,
                           int shadowType, int direction, String info,
                           int x, int y, int w, int h) {
        Component c = context.getComponent();
        // Don't paint the arrow if we're in a spinner or combo box.
        // We get that from the image.
        if (c.getName() == "Spinner.nextButton" ||
                c.getName() == "Spinner.previousButton" ||
                c.getName() == "ComboBox.arrowButton") {
            return;
        }
        String parentType = null;
        c = c.getParent();
        if (c != null && c instanceof JComponent) {
            c = c.getParent();
            if (c != null && c instanceof JComponent) {
                parentType = getComponentType((JComponent)c);
            }
        }
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)context.getStyle()).
                        getInfo("ARROW", info, state, shadowType,
                            UNDEFINED, UNDEFINED, direction, parentType))) {
            super.paintArrow(context, g, state, shadowType, direction,
                    info, x, y, w, h);
        }
    }
    public void paintBox(SynthContext context, Graphics g, int state,
                         int shadowType, String info, int x, int y,
                         int w, int h) {
        int orientation;
        Region id = context.getRegion();
        Component c = context.getComponent();
        SynthStyle style = context.getStyle();
        // Blueprint checks to make sure that we aren't calling
        // paintBox on a slider/scrollbar with detail hscrollbar or
        // vscrollbar, because they do the work in paintArrow instead.
        // We do it here because we have the correct bounds for the whole
        // button.
        Integer arrowDirection =
            (Integer)((JComponent)c).getClientProperty("__arrow_direction__");
        if (info == "vscrollbar" || info == "hscrollbar" &&
                arrowDirection != null) {
            int direction = arrowDirection.intValue();
            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;
            }
            c = (JComponent)c.getParent();
            if (c == null || !(c instanceof JComponent)) {
                return;
            }
            if (c instanceof JScrollBar) {
                SynthStyle newStyle = getStyle((JScrollBar)c,
                        ((JScrollBar)c).getUI());
                if (newStyle != null) {
                    style = newStyle;
                }
                if (paintSimpleImage(context, g, x, y, w, h, true,
                        ((BlueprintStyle)style).getInfo("STEPPER", info,
                                state, UNDEFINED, UNDEFINED, UNDEFINED,
                                direction, null))) {
                    return;
                }
                if (!paintSimpleImage(context, g, x, y, w, h, true,
                    ((BlueprintStyle)style).getInfo("BOX", info, state,
                                shadowType, UNDEFINED, UNDEFINED,
                                UNDEFINED, null))) {
                    super.paintBox(context, g, state, shadowType, info,
                        x, y, w, h);
                }
                return;
            }
        }
        // If the button is in a spinner get the style of the JSpinner.
        if (c.getName() == "Spinner.nextButton" ||
                c.getName() == "Spinner.previousButton" &&
                arrowDirection != null) {
            if (arrowDirection.intValue() == SwingConstants.NORTH) {
                info = "spinbutton_up";
            } else {
                info = "spinbutton_down";
            }
            c = c.getParent();
            if (c instanceof JSpinner) {
                SynthStyle newStyle = getStyle((JSpinner)c,
                        ((JSpinner)c).getUI());
                if (newStyle != null) {
                    style = newStyle;
                }
            }
        }
        if (id == Region.SCROLL_BAR) {
            if (((JScrollBar)c).getOrientation() ==
                    SwingConstants.HORIZONTAL) {
                orientation = GTKConstants.HORIZONTAL;
            }
            else {
                orientation = GTKConstants.VERTICAL;
            }
        }
        else if (id == Region.SLIDER_TRACK) {
            if (((JSlider)c).getOrientation() ==
                    SwingConstants.HORIZONTAL) {
                orientation = GTKConstants.HORIZONTAL;
            }
            else {
                orientation = GTKConstants.VERTICAL;
            }
        }
        else {
            orientation = UNDEFINED;
        }
        String parentType = null;
        if (c != null) {
            c = c.getParent();
            if (c != null && c instanceof JComponent) {
                parentType = getComponentType((JComponent)c);
            }
        }
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)style).getInfo("BOX", info, state,
                        shadowType, orientation, UNDEFINED, UNDEFINED,
                        parentType))) {
            super.paintBox(context, g, state, shadowType, info, x, y, w, h);
        }
    }
    public void paintBoxGap(SynthContext context, Graphics g, int state,
                            int shadow, String key, int x, int y,
                            int w, int h, int gapSide, int gapStart,
                            int gapSize) {
        BlueprintStyle.Info info = ((BlueprintStyle)context.getStyle()).getInfo(
              "BOX_GAP", key, state, shadow, UNDEFINED, gapSide, UNDEFINED,
              null);
        if (info != null) {
            paintGapImage(context, info, g, x, y, w, h, true, gapSide,
                    gapStart, gapSize);
        } else {
            super.paintBoxGap(context, g, state, shadow, key, x, y, w, h,
                              gapSide, gapStart, gapSize);
        }
    }
    public void paintHandle(SynthContext context, Graphics g, int paintState,
                            int shadowType, String info, int x, int y,
                            int w, int h, int orientation) {
        if (info == "handlebox" || info == "dockitem") {
            w -=2;
            h -=1;
        }
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)context.getStyle()).
                        getInfo("HANDLE", info, paintState, shadowType,
                                orientation, UNDEFINED, UNDEFINED, null))) {
            super.paintHandle(context, g, paintState, shadowType, info, x, y,
                    w, h, orientation);
        }
    }
    public void paintOption(SynthContext context, Graphics g, int paintState,
                            int shadowType, String info, int x, int y,
                            int w, int h) {
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                    ((BlueprintStyle)context.getStyle()).
                         getInfo("OPTION", info, paintState, shadowType,
                                 UNDEFINED, UNDEFINED, UNDEFINED, null))) {
            super.paintOption(context, g, paintState, shadowType, info, x, y,
                              w, h);
        }
    }
    public void paintFocus(SynthContext context, Graphics g, int state,
                           String key, int x, int y, int w, int h) {
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)context.getStyle()).
                        getInfo("FOCUS", key, state, UNDEFINED, UNDEFINED,
                                UNDEFINED, UNDEFINED, null))) {
            super.paintFocus(context, g, state, key, x, y, w, h);
        }
    }
    public void paintShadow(SynthContext context, Graphics g, int state,
                            int shadowType, String info, int x, int y,
                            int w, int h) {
        Component c = context.getComponent();
        String parentType = null;
        SynthStyle style = context.getStyle();
        if (c.getName() == "ComboBox.textField") {
            parentType = "GtkCombo";
        } else if (c.getName() == "ComboBox.renderer") {
            c = c.getParent();
            if (c != null) {
                c = c.getParent();
                parentType = "GtkCombo";
            }
        }
        if (c instanceof JComboBox) {
            // Use the Style from the editor
            JComboBox cb = (JComboBox)c;
            Component editor = cb.getEditor().getEditorComponent();
            if (editor instanceof JTextField) {
                if (!cb.isEditable() && editor.getParent() == null) {
                    // GTKStyleFactory hands back a bogus Style when a
                    // Component doesn't have a parent. As the editor
                    // is only parented when the JComboBox is editable it
                    // means we can get back a bogus style. To force the
                    // real style to be assigned we parent the editor.
                    // YES, this is ugly!
                    cb.add(editor);
                    cb.remove(editor);
                }
                SynthStyle newStyle = getStyle((JTextField)editor,
                        ((JTextField)editor).getUI());
                if (newStyle != null) {
                    style = newStyle;
                }
            }
        }
        if (info == "menu" && parentType == "GtkHBox") {
            return;
        }
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)style).getInfo("SHADOW", info, state,
                        shadowType, UNDEFINED, UNDEFINED, UNDEFINED,
                        parentType))) {
           super.paintShadow(context, g, state, shadowType, info, x, y, w, h);
        }
    }
    public void paintExpander(SynthContext context, Graphics g, int state,
                              int expanderStyle, String info, int x,
                              int y, int w, int h) {
        // It does not appear that there is a way to override this.
        super.paintExpander(context, g, state, expanderStyle, info, x, y, w,h);
    }
    public void paintCheck(SynthContext context, Graphics g, int state,
                           int shadowType, String info, int x, int y,
                           int w, int h) {
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)context.getStyle()).
                        getInfo("CHECK", info, state, shadowType, UNDEFINED,
                                UNDEFINED, UNDEFINED, null))) {
            super.paintCheck(context, g, state, shadowType, info, x, y, w, h);
        }
    }
    public void paintExtension(SynthContext context, Graphics g, int state,
                               int shadowType, String info, int x, int y,
                               int w, int h, int placement, int tabIndex) {
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)context.getStyle()).
                         getInfo("EXTENSION", info, state, shadowType,
                                 UNDEFINED, placement, UNDEFINED, null))) {
            super.paintExtension(context, g, state, shadowType, info, x, y,
                                 w, h, placement, tabIndex);
        }
    }
    public void paintFlatBox(SynthContext context, Graphics g, int state,
                             String key, int x, int y, int w, int h) {
        if (key == "checkbutton" && state == SynthConstants.MOUSE_OVER) {
            return;
        }
        Component c = context.getComponent();
        String parentType = null;
        c = c.getParent();
        if (c instanceof CellRendererPane) {
            // Skip the CellRendererPane
            c = c.getParent();
        }
        if (c != null && c instanceof JComponent) {
            parentType = getComponentType((JComponent)c);
        }
        if (!paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)context.getStyle()).
                         getInfo("FLAT_BOX", key, state, UNDEFINED, UNDEFINED,
                                 UNDEFINED, UNDEFINED, parentType))) {
            super.paintFlatBox(context, g, state, key, x, y, w, h);
        }
    }
    void paintBackground(SynthContext context, Graphics g, int state,
            Color color, int x, int y, int w, int h) {
        JComponent c = context.getComponent();
        if (c instanceof JPopupMenu) {
            if (paintSimpleImage(context, g, x, y, w, h, true,
                ((BlueprintStyle)context.getStyle()).
                         getInfo("BACKGROUND", null, state, UNDEFINED,
                             UNDEFINED, UNDEFINED, UNDEFINED, null))) {
                return;
            }
        }
        super.paintBackground(context, g, state, color, x, y, w, h);
    }
    /**
     * Paints a gap image. This renders the image into a portion of
     * the passed in region that is dictated
     * by the gapSide and size arguments. For
     * example, if gapSide is GTKConstants.TOP,
     * this will render the image into the space:
     * 
| x origin | x+gapStart* | 
| y origin | y* | 
| width | gapSize* | 
| height | size* |