/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * Copyright (c) 2004 Nokia. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of Nokia nor the names of its contributors may be
 * used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <gdk/gdk.h>
#include <math.h>
#include "KWQPainter.h"

#include "KWQAssertions.h"
#include "KWQFontMetrics.h"
#include "KWQPaintDevice.h"
#include "KWQPixmap.h"
#include "KWQPointArray.h"
#include "KWQPrinter.h"
#include "KWQWidget.h"
#include "KWQPtrStack.h"

#include "WebCoreTextRendererFactory.h"
#include "WebCoreTextRenderer.h"
#include "WebCoreImageRenderer.h"
#include "KWIQCGContext.h"
#include "KWQFoundationExtras.h"

#include "KWQLogging.h"

#include <gdk/gdkx.h>
#include <cairo.h>
#include <cairo-xlib.h>


/*
 *FIXME: will crash for example in resizing frames, because drawing context is not set.
 * Apple has global context, and they construct painter with statement like "QPainter painter;" 
 * for example  see render_frames.cpp:440
 *
 */


static inline
void initGdkRectangle(GdkRectangle* rect, int x, int y, int w, int h)
{
    rect->x=x;
    rect->y=y;
    rect->width=w;
    rect->height=h;

}

struct QPState {
    QPState() 
        :  paintingDisabled(false)
    { } 

    QFont font;
    QPen pen;
    QBrush brush;
    bool paintingDisabled;
};

struct QPainterPrivate 
{
    QPainterPrivate() 
        : textRenderer(0) 
        , context(0)
        , cr(0)
    { }

    ~QPainterPrivate() 
    {
        KWQRelease(textRenderer); 
        if (cr) cairo_destroy(cr); 
    }

    QPState state;
    QPtrStack<QPState> stack;
    WebCoreTextRenderer* textRenderer;
    QFont textRendererFont;

    CGContextRef context;
    cairo_t* cr;
};



QPainter::QPainter() 
    : data(new QPainterPrivate)
    , _isForPrinting(false)
    , _usesInactiveTextBackgroundColor(false)
    , _drawsFocusRing(true)
{
    
}

QPainter::QPainter(bool forPrinting) 
    : data(new QPainterPrivate)
    , _isForPrinting(forPrinting)
    , _usesInactiveTextBackgroundColor(false)
    , _drawsFocusRing(true)
{

}

QPainter::~QPainter()
{
    delete data;
}

QPaintDevice *QPainter::device() const
{
    static QPrinter printer;
    static QPaintDevice screen;
    return _isForPrinting ? &printer : &screen;
}

const QFont &QPainter::font() const
{
    return data->state.font;
}

void QPainter::setFont(const QFont &aFont)
{
    data->state.font = aFont;
}

QFontMetrics QPainter::fontMetrics() const
{
    return data->state.font;
}

const QPen &QPainter::pen() const
{
    return data->state.pen;
}

void QPainter::setPen(const QPen &pen)
{
    data->state.pen = pen;
}

void QPainter::setPen(PenStyle style)
{
    data->state.pen.setStyle(style);
    data->state.pen.setColor(Qt::black);
    data->state.pen.setWidth(0);
}

void QPainter::setPen(QRgb rgb)
{
    data->state.pen.setStyle(SolidLine);
    data->state.pen.setColor(rgb);
    data->state.pen.setWidth(0);
}

void QPainter::setBrush(const QBrush &brush)
{
    data->state.brush = brush;
}

void QPainter::setBrush(BrushStyle style)
{
    data->state.brush.setStyle(style);
    data->state.brush.setColor(Qt::black);
}

void QPainter::setBrush(QRgb rgb)
{
    data->state.brush.setStyle(SolidPattern);
    data->state.brush.setColor(rgb);
}

const QBrush &QPainter::brush() const
{
    return data->state.brush;
}

QRect QPainter::xForm(const QRect &aRect) const
{
    // No difference between device and model coords, so the identity transform is ok.
    return aRect;
}

void QPainter::save()
{
    data->stack.push(new QPState(data->state));

    currentContext()->saveGraphicsState(); 
    cairo_save(data->cr);
}

void QPainter::restore()
{
    if (data->stack.isEmpty()) {
        ERROR("ERROR void QPainter::restore() stack is empty");
        return;
    }

    QPState *ps = data->stack.pop();
    data->state = *ps;
    delete ps;

    currentContext()->restoreGraphicsState();
    cairo_restore(data->cr);
}

// Draws a filled rectangle with a stroked border.
void QPainter::drawRect(int x, int y, int w, int h)
{
    if (data->state.paintingDisabled)
        return;

    if (data->state.brush.style() != NoBrush) {
        cairo_rectangle(data->cr, x, y, w, h);
        _setColorFromBrush();
        cairo_fill(data->cr);
        
    }

    if (data->state.pen.style() != NoPen) {
        cairo_rectangle(data->cr, x, y, w, h);
        _setColorFromPen();
        cairo_stroke(data->cr);
    }
}

void QPainter::_setColorFromBrush()
{
    ASSERT(data->state.brush.color().isValid());
    QColor qc(data->state.brush.color());
    cairo_set_fill_rule(data->cr, CAIRO_FILL_RULE_WINDING);
    cairo_set_rgb_color(data->cr, 
                        ((double)qc.red()) / 255.0,    
                        ((double)qc.green())/ 255.0,
                        ((double)qc.blue()) / 255.0);
}

void QPainter::_setColorFromPen()
{
    QPen pen(data->state.pen);
    QColor qc(pen.color());
    ASSERT(qc.isValid());

    cairo_set_rgb_color(data->cr, 
                        ((double)qc.red()) / 255.0,    
                        ((double)qc.green())/ 255.0,
                        ((double)qc.blue()) / 255.0);

    double pw = (double) pen.width();
    if (pen.width() == 0)
        pw = 1.;

    cairo_set_line_width(data->cr, pw);
    cairo_set_fill_rule(data->cr, CAIRO_FILL_RULE_WINDING);

    cairo_set_line_cap(data->cr, CAIRO_LINE_CAP_BUTT);
    cairo_set_line_join(data->cr, CAIRO_LINE_JOIN_MITER);

    switch (pen.style()) {
    case Qt::SolidLine:
        cairo_set_dash(data->cr, 0, 0, 0.);
        break;
    case Qt::DotLine: 
    {
        const double dot_len_factor = 1.;
        const double dot_space_len_factor = 1.;
        double dots = dot_len_factor * pw;
        cairo_set_dash(data->cr, &dots, 1, dot_space_len_factor * pw);
        break;
    }
    case Qt::DashLine:
    {
        const double dash_len_factor = 3.;
        const double dash_space_len_factor = 1.; 
        double dashes = dash_len_factor * pw;
        cairo_set_dash(data->cr, &dashes, 1, dash_space_len_factor * pw);
        break;
    }
    case Qt::NoPen:
    {
        break;
    }
    }
}

// This is only used to draw borders.
// This is also used to draw lines under text
void QPainter::drawLine(int x1, int y1, int x2, int y2)
{
    if (data->state.paintingDisabled)
        return;

    if (data->state.pen.style() == NoPen)
        return;
    
    cairo_move_to(data->cr, x1, y1);
    cairo_line_to(data->cr, x2, y2);
    _setColorFromPen();
    cairo_stroke(data->cr);
}


// This method is only used to draw the little circles used in lists.
void QPainter::drawEllipse(int x, int y, int w, int h)
{
    // This code can only handle circles, not ellipses. But khtml only
    // uses it for circles.
    ASSERT(w == h);

    if (data->state.paintingDisabled)
        return;
    double r = w/2.;
    cairo_arc(data->cr, x + r, y + r, r, 0., 2 * M_PI);
    
    BrushStyle b(data->state.brush.style());
    PenStyle p(data->state.pen.style());

    if ( b != NoBrush && p == NoPen) {
        _setColorFromBrush();
        cairo_fill(data->cr);
    }else if (b != NoBrush && p != NoPen) {
        _setColorFromBrush();
        cairo_save(data->cr);
        cairo_fill(data->cr);
        cairo_restore(data->cr);
        _setColorFromPen();
        cairo_stroke(data->cr);
    }else if (b == NoBrush && p != NoPen) {
        _setColorFromPen();
        cairo_stroke(data->cr);
    }else {
        // b==NoBrush && p == NoPen 
    }
}


void QPainter::drawArc (int x, int y, int w, int h, int a, int alen)
{
    // Only supports arc on circles.  That's all khtml needs.
    ASSERT(w == h);

    if (data->state.paintingDisabled)
        return;

    if (data->state.pen.style() != NoPen) { 
        _setColorFromPen();
        
        /* qt: positive angles counter-clockwise, cairo: positive angles clockwise*/
        /* a, alen: 1 equals 1/16 * 360 */
        double r = double(w) / 2.;
        
        const double angle_scale = (360. / 16.) * (M_PI / 180.);  
        double angle1 = angle_scale * a;
        double angle2 = angle1 - (angle_scale * alen);

        cairo_arc(data->cr, x+r, y+r, r, angle1, angle2);
        cairo_stroke(data->cr);
    }
}

void QPainter::drawLineSegments(const QPointArray &points, int index, int nlines)
{
    if (data->state.paintingDisabled)
        return;

    _drawPoints (points, true, index, nlines, false);
}

void QPainter::drawPolyline(const QPointArray &points, int index, int npoints)
{
    _drawPoints (points, true, index, npoints, false);
}

void QPainter::drawPolygon(const QPointArray &points, bool winding, int index,
                           int npoints)
{
    _drawPoints (points, winding, index, npoints, true);
}

void QPainter::drawConvexPolygon(const QPointArray &points)
{
    _drawPoints (points, false, 0, -1, true);
}

void QPainter::_drawPoints (const QPointArray &_points, bool winding, int index, int _npoints, bool fill)
{
    if (data->state.paintingDisabled)
        return;	

    int npoints = _npoints != -1 ? _npoints : _points.size()-index;

    if (npoints > 0) {
        cairo_move_to(data->cr, _points[0].x(), _points[0].y());
    }

    for (int i = 1; i < npoints; i++) {
        cairo_line_to(data->cr, _points[index+i].x(), _points[index+i].y());
    }    
    
    bool doFill = fill && data->state.brush.style() != NoBrush;

    PenStyle p(data->state.pen.style());
    if (doFill == true && p == NoPen) {
        _setColorFromBrush();
        cairo_set_fill_rule(data->cr, winding ? CAIRO_FILL_RULE_WINDING : CAIRO_FILL_RULE_EVEN_ODD);
        cairo_fill(data->cr);
    } else if (doFill == true && p != NoPen) {
        _setColorFromBrush();
        cairo_save(data->cr);
        cairo_set_fill_rule(data->cr, winding ? CAIRO_FILL_RULE_WINDING : CAIRO_FILL_RULE_EVEN_ODD);
        cairo_fill(data->cr);
        cairo_restore(data->cr);
        _setColorFromPen();
        cairo_stroke(data->cr);
    } else if (doFill == false && p != NoPen) {
        _setColorFromPen();
        cairo_stroke(data->cr);
    } else{
        //doFill == false && p == NoPen
    }
}


void QPainter::drawPixmap(const QPoint &p, const QPixmap &pix)
{
    drawPixmap(p.x(), p.y(), pix);
}


void QPainter::drawPixmap(const QPoint &p, const QPixmap &pix, const QRect &r)
{
    drawPixmap(p.x(), p.y(), pix, r.x(), r.y(), r.width(), r.height());
}

struct CompositeOperator
{
    const char *name;
    NSCompositingOperation value;
};

#define NUM_COMPOSITE_OPERATORS 14
struct CompositeOperator compositeOperators[NUM_COMPOSITE_OPERATORS] = {
    { "clear", NSCompositeClear },
    { "copy", NSCompositeCopy },
    { "source-over", NSCompositeSourceOver },
    { "source-in", NSCompositeSourceIn },
    { "source-out", NSCompositeSourceOut },
    { "source-atop", NSCompositeSourceAtop },
    { "destination-over", NSCompositeDestinationOver },
    { "destination-in", NSCompositeDestinationIn },
    { "destination-out", NSCompositeDestinationOut },
    { "destination-atop", NSCompositeDestinationAtop },
    { "xor", NSCompositeXOR },
    { "darker", NSCompositePlusDarker },
    { "highlight", NSCompositeHighlight },
    { "lighter", NSCompositePlusLighter }
};

int QPainter::getCompositeOperation(CGContextRef context)
{
    return (int) WebCoreImageRendererFactory::sharedFactory()->CGCompositeOperationInContext(context);
}

void QPainter::setCompositeOperation (CGContextRef context, QString op)
{
    WebCoreImageRendererFactory::sharedFactory()->setCGCompositeOperationFromString(context, op.latin1());
}

void QPainter::setCompositeOperation (CGContextRef context, int op)
{
    WebCoreImageRendererFactory::sharedFactory()->setCGCompositeOperation(context, op);
}

int QPainter::compositeOperatorFromString (QString aString)
{
    NSCompositingOperation op = NSCompositeSourceOver;
    
    if (aString.length()) {
        const char *operatorString = aString.ascii();
        int i;
        
        for (i = 0; i < NUM_COMPOSITE_OPERATORS; i++) {
            if (strcasecmp (operatorString, compositeOperators[i].name) == 0) {
                return compositeOperators[i].value;
            }
        }
    }
    return (int)op;
}

void QPainter::drawPixmap(const QPoint &p, const QPixmap &pix, const QRect &r, const QString &compositeOperator)
{
    drawPixmap(p.x(), p.y(), pix, r.x(), r.y(), r.width(), r.height(), compositeOperatorFromString(compositeOperator));
}

void QPainter::drawPixmap( int x, int y, const QPixmap &pixmap,
                           int sx, int sy, int sw, int sh, int compositeOperator, CGContextRef context)
{
    drawPixmap(x, y, sw, sh, pixmap, sx, sy, sw, sh, compositeOperator, context);
}

static 
cairo_surface_t* cairoSurfaceFromGdkPixbuf(GdkPixbuf* pixbuf)
{
    cairo_surface_t* image;
    int width = gdk_pixbuf_get_width (pixbuf);
    int height = gdk_pixbuf_get_height (pixbuf);
    int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
    g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
    g_assert (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
    g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
    g_assert (n_channels == 4);

    int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
    guchar* buffer = gdk_pixbuf_get_pixels(pixbuf);

    image = cairo_surface_create_for_image((char*)buffer, CAIRO_FORMAT_ARGB32, width , height, rowstride);
    return image;    
}

void QPainter::drawPixmap( int x, int y, int w, int h, const QPixmap &pixmap,
                           int sx, int sy, int sw, int sh, int compositeOperator, CGContextRef context )
{
    if (data->state.paintingDisabled)
        return;

    if (pixmap.isNull())
        return;

    if (!context) {
        context = currentContext();
    } else {
    }
    
    if (sw < 0) sw = pixmap.width();
    if (sh < 0) sh = pixmap.height();
    if (w < 0) w = pixmap.width();
    if (h < 0) h = pixmap.height();
        
    GdkRectangle inRect;
    initGdkRectangle(&inRect, x, y, w, h);

    GdkRectangle fromRect;
    initGdkRectangle(&fromRect, sx, sy, w, h);

    cairo_surface_t *image;

    image = cairoSurfaceFromGdkPixbuf(const_cast<WebCoreImageRenderer*>(pixmap.image())->handle());

    cairo_save(data->cr);
    cairo_translate(data->cr, x, y);
    cairo_move_to(data->cr, sx, sy);
    cairo_show_surface(data->cr, image, w, h);
    cairo_restore(data->cr);

    cairo_surface_destroy(image);
}

void QPainter::drawTiledPixmap( int x, int y, int w, int h,
                                const QPixmap &pixmap, int sx, int sy, CGContextRef context)
{
    ASSERT(w >= 0);
    ASSERT(h >= 0);

    if (data->state.paintingDisabled)
        return;

    if (pixmap.isNull())
        return;

    if (!context) {
        context = currentContext();
    }

    /** sw = source width, sh = source height*/
    int sw = pixmap.width(), sh = pixmap.height(); 

    if (sx <0) sx = 0;
    if (sy <0) sy = 0;
    /* (due to translation) we might get negative -x (not drawn) 
        clip it to 0 and adjust other params too*/
    if (x < 0) {
        sx += -x;
        w += x;
        x = 0;
    }
    
    /* (due to translation) we might get negative -y (not drawn)  */
    if (y < 0) {
        sy += -y;
        h += y;
        y = 0;        
    }

    /* if source start value is bigger than source dim, adjust it to mean same but be within
       the source dim */
    if (sx >= sw) 
        sx %= sw;    
    if (sy >= sh)
        sy %= sh;

    if (w <= 0 || h <= 0)
        return;

    GdkRectangle inRect;    
    initGdkRectangle(&inRect, x, y, w, h);
    GdkRectangle fromRect;
    initGdkRectangle(&fromRect, x, y, w, h);

    pixmap.image()->tileInRect(&inRect, sx, sy, context);
        
}

void QPainter::_updateRenderer()
{
    if (data->textRenderer == 0 || data->state.font != data->textRendererFont) {
        data->textRendererFont = data->state.font;
        WebCoreTextRenderer* oldRenderer = data->textRenderer;
        data->textRenderer = 
            WebCoreTextRendererFactory::sharedFactory()->rendererWithFont(data->textRendererFont.getNSFont(),
                                                                          data->textRendererFont.isPrinterFont());
        
        KWQRetain(data->textRenderer);
        ASSERT(data->textRenderer);
        KWQRelease(oldRenderer);
    }
}

void QPainter::drawText(int x, int y, int, int, int alignmentFlags, const QString &qstring)
{
    if (data->state.paintingDisabled)
        return;

    // Avoid allocations, use stack array to pass font families.  Normally these
    // css fallback lists are small <= 3.
    CREATE_FAMILY_ARRAY(data->state.font, families);

    _updateRenderer();

    const UniChar* str = (const UniChar*)qstring.unicode();

    WebCoreTextRun run(str, qstring.length(), 0, qstring.length());
    WebCoreTextStyle style;
    data->state.pen.color().getGdkColor(&style.textColor);
    style.families = families;
    
    if (alignmentFlags & Qt::AlignRight)
        x -= ROUND_TO_INT(data->textRenderer->floatWidthForRun(&run, &style, 0));

    data->textRenderer->setContext(currentContext());
    data->textRenderer->drawRun(&run, &style, x, y);

}

void QPainter::drawText(int x, int y, const QChar *str, int len, int from, int to, int toAdd, const QColor &backgroundColor, QPainter::TextDirection d, bool visuallyOrdered, int letterSpacing, int wordSpacing, bool smallCaps)    
{
    if (data->state.paintingDisabled || len <= 0)
        return;

    // Avoid allocations, use stack array to pass font families.  Normally these
    // css fallback lists are small <= 3.
    CREATE_FAMILY_ARRAY(data->state.font, families);

    _updateRenderer();

    if (from < 0)
        from = 0;
    if (to < 0)
        to = len;

    WebCoreTextRun run((const UniChar *)str, len, from, to);
    WebCoreTextStyle style;
    data->state.pen.color().getGdkColor(&style.textColor);
    backgroundColor.getGdkColor(&style.backgroundColor); // !isValid() => sets style.backgroundColor "nil"
    style.rtl = d == RTL ? true : false;
    style.visuallyOrdered =  visuallyOrdered;
    style.letterSpacing = letterSpacing;
    style.wordSpacing = wordSpacing;
    style.smallCaps = smallCaps;

    style.padding = toAdd;
    style.families = families;

    data->textRenderer->setContext(currentContext());
    data->textRenderer->drawRun(&run, &style, x, y );
}

void QPainter::drawHighlightForText(int x, int minX, int maxX, int y, int h, 
                                    const QChar *str, int len, int from, int to, int toAdd,
                                    const QColor& backgroundColor, QPainter::TextDirection d, bool visuallyOrdered,
                                    int letterSpacing, int wordSpacing, bool smallCaps)

{
    if (data->state.paintingDisabled || len <= 0)
        return;

    // Avoid allocations, use stack array to pass font families.  Normally these
    // css fallback lists are small <= 3.
    CREATE_FAMILY_ARRAY(data->state.font, families);

    _updateRenderer();
    
    if (from < 0)
        from = 0;
    if (to < 0)
        to = len;

    WebCoreTextRun run((const UniChar *)str, len, from, to);
    WebCoreTextStyle style;
    data->state.pen.color().getGdkColor(&style.textColor);
    backgroundColor.getGdkColor(&style.backgroundColor); // !isValid() => sets style.backgroundcolor "nil"
    style.rtl = d == RTL ? true : false;
    style.visuallyOrdered = visuallyOrdered;        
    style.letterSpacing = letterSpacing;
    style.wordSpacing = wordSpacing;
    style.smallCaps = smallCaps;
    style.padding = toAdd;
    style.families = families;

    WebCoreTextGeometry geometry(x,y, y, h, minX, maxX, false);

    data->textRenderer->setContext(currentContext());
    data->textRenderer->drawHighlightForRun(&run, &style, &geometry);
}

void QPainter::drawLineForText(int x, int y, int yOffset, int width)
{
    if (data->state.paintingDisabled)
        return;

    drawLine(x, y+yOffset+2, x+width, y+yOffset+2);
   
#if 0
    GdkColor col;
    data->state.pen.color().getGdkColor(&col);
    data->textRenderer->drawLineForCharacters(x, 
                                              y,
                                              yOffset,
                                              width,
                                              col);
#endif
}

QColor QPainter::selectedTextBackgroundColor() const
{
    static QColor secondarySelectedControlColor("gray");
    static QColor selectedTextBackgroundColor(qRgba((int)(255*0.1f),
                                                    (int)(255*0.1f),
                                                    (int)(255*1.0f),
                                                    (int) (255 * 0.3f)));
    
    return _usesInactiveTextBackgroundColor ?  secondarySelectedControlColor : selectedTextBackgroundColor;
}

void QPainter::fillRect(int x, int y, int w, int h, const QBrush &brush)
{
    if (data->state.paintingDisabled)
        return;

    if (brush.style() == SolidPattern) {
        QBrush old = this->brush();
        setBrush(brush);
        cairo_rectangle(data->cr, x, y, w, h);
        _setColorFromBrush();
        cairo_fill(data->cr);
        setBrush(old);
    }
}

void QPainter::addClip(const QRect &rect)
{
    GdkRectangle gr;        
    rect.getGdkRectangle(&gr);
    currentContext()->addClip(&gr);

    // XXX: is this incremental
    cairo_rectangle(data->cr, rect.x(),rect.y(),rect.width(),rect.height());
    cairo_clip(data->cr);
    cairo_new_path(data->cr);
}

Qt::RasterOp QPainter::rasterOp() const
{
    return CopyROP;
}

void QPainter::setRasterOp(RasterOp)
{
}

void QPainter::setPaintingDisabled(bool f)
{
    data->state.paintingDisabled = f;
}

bool QPainter::paintingDisabled() const
{
    return data->state.paintingDisabled;
}

void QPainter::setContext(CGContextRef context)
{
    if (context == currentContext()) 
        return;

    data->context = context;
    
    data->cr = cairo_create();

    cairo_set_target_drawable(data->cr, 
                              GDK_DRAWABLE_XDISPLAY(data->context->realDrawable()),
                              GDK_DRAWABLE_XID(data->context->realDrawable()));
    int trans_x = 0;
    int trans_y = 0;
    context->realTranslate(&trans_x, &trans_y);    

    trans_x = -trans_x;
    trans_y = -trans_y;
    cairo_translate(data->cr, trans_x, trans_y);
    
    // FIXME: init with current state.
}

CGContextRef QPainter::currentContext()
{
    return data->context;
}

void QPainter::beginTransparencyLayer(float opacity)
{
    cairo_save(data->cr);    
    cairo_set_alpha(data->cr, opacity);
}

void QPainter::endTransparencyLayer()
{
    cairo_restore(data->cr);
}

void QPainter::setShadow(int x, int y, int blur, const QColor& color)
{
#if !KWIQ
    // Check for an invalid color, as this means that the color was not set for the shadow
    // and we should therefore just use the default shadow color.
    CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
    if (!color.isValid())
        CGContextSetShadow(context, CGSizeMake(x,-y), blur); // y is flipped.
    else {
        NSColor* deviceColor = [color.getNSColor() colorUsingColorSpaceName: @"NSDeviceRGBColorSpace"];
        float red = [deviceColor redComponent];
        float green = [deviceColor greenComponent];
        float blue = [deviceColor blueComponent];
        float alpha = [deviceColor alphaComponent];
        const float components[] = { red, green, blue, alpha };

        CGContextSetShadowWithColor(context,
                                    CGSizeMake(x,-y), // y is flipped.
                                    blur,
                                    CGColorCreate(CGColorSpaceCreateDeviceRGB(),
                                                  components));
    }
#endif
}

void QPainter::clearShadow()
{
#if !KWIQ
    CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
    CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
#endif
}

void QPainter::initFocusRing(int width, int offset)
{
#if 0
    if (!_drawsFocusRing)
        return;

    clearFocusRing();
    data->focusRingWidth = width;
    data->hasFocusRingColor = false;
    data->focusRingOffset = offset;
    data->focusRingPath = [[NSBezierPath alloc] init];
    [data->focusRingPath setWindingRule:NSNonZeroWindingRule];
#endif     
}


void QPainter::initFocusRing(int width, int offset, const QColor &color)
{
#if 0    
    if (!_drawsFocusRing)
        return;

    initFocusRing(width, offset);
    data->hasFocusRingColor = true;
    data->focusRingColor = color;
#endif    
}

void QPainter::addFocusRingRect(int x, int y, int width, int height)
{
#if 0
    if (!_drawsFocusRing)
        return;

    ASSERT(data->focusRingPath);
    NSRect rect = NSMakeRect(x, y, width, height);
    int offset = (data->focusRingWidth-1)/2 + data->focusRingOffset;
    rect = NSInsetRect(rect, -offset, -offset);
    [data->focusRingPath appendBezierPathWithRect:rect];
#endif    
}

void QPainter::drawFocusRing()
{
#if 0
    if (!_drawsFocusRing)
        return;
    
    ASSERT(data->focusRingPath);
    if (data->state.paintingDisabled)
        return;

    if ([data->focusRingPath elementCount] == 0) {
        ERROR("Request to draw focus ring with no control points");
        return;
    }
    
    NSRect bounds = [data->focusRingPath bounds];
    if (!NSIsEmptyRect(bounds)) {
        int radius = (data->focusRingWidth-1)/2;
        NSColor *color = data->hasFocusRingColor ? data->focusRingColor.getNSColor() : nil;
        [NSGraphicsContext saveGraphicsState];
        [[WebCoreGraphicsBridge sharedBridge] setFocusRingStyle:NSFocusRingOnly radius:radius color:color];
        [data->focusRingPath fill];
        [NSGraphicsContext restoreGraphicsState];   
    }
#endif    
}

void QPainter::clearFocusRing()
{
#if 0    
    if (data->focusRingPath) {
        [data->focusRingPath release];
        data->focusRingPath = nil;
    }
#endif
}



