/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-

   caja-cell-renderer-pixbuf-emblem.c: cell renderer which can render
   an emblem on top of a pixbuf (for use in FMListView and FMTreeView)

   Copyright (C) 2003 Juerg Billeter

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this program; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   This is based on GtkCellRendererPixbuf written by
   Jonathan Blandford <jrb@redhat.com>

   Author: Juerg Billeter <j@bitron.ch>
*/

#include "caja-cell-renderer-pixbuf-emblem.h"

static void caja_cell_renderer_pixbuf_emblem_get_property (GObject *object,
        guint		param_id,
        GValue		*value,
        GParamSpec	*pspec);
static void caja_cell_renderer_pixbuf_emblem_set_property (GObject *object,
        guint		param_id,
        const GValue	*value,
        GParamSpec	*pspec);
static void caja_cell_renderer_pixbuf_emblem_init       (CajaCellRendererPixbufEmblem *cellpixbuf);
static void caja_cell_renderer_pixbuf_emblem_class_init  (CajaCellRendererPixbufEmblemClass *klass);
static void caja_cell_renderer_pixbuf_emblem_finalize (GObject *object);
static void caja_cell_renderer_pixbuf_emblem_create_stock_pixbuf (CajaCellRendererPixbufEmblem *cellpixbuf,
        GtkWidget             *widget);
static void caja_cell_renderer_pixbuf_emblem_get_size   (GtkCellRenderer            *cell,
        GtkWidget                  *widget,
        GdkRectangle               *rectangle,
        gint                       *x_offset,
        gint                       *y_offset,
        gint                       *width,
        gint                       *height);
static void     caja_cell_renderer_pixbuf_emblem_render     (GtkCellRenderer            *cell,
        GdkWindow                  *window,
        GtkWidget                  *widget,
        GdkRectangle               *background_area,
        GdkRectangle               *cell_area,
        GdkRectangle               *expose_area,
        GtkCellRendererState                       flags);

enum
{
    PROP_ZERO,
    PROP_PIXBUF,
    PROP_PIXBUF_EXPANDER_OPEN,
    PROP_PIXBUF_EXPANDER_CLOSED,
    PROP_STOCK_ID,
    PROP_STOCK_SIZE,
    PROP_STOCK_DETAIL,
    PROP_PIXBUF_EMBLEM
};

#define CELLINFO_KEY "caja-cell-renderer-pixbuf-emblem-info"

typedef struct _CajaCellRendererPixbufEmblemInfo CajaCellRendererPixbufEmblemInfo;
struct _CajaCellRendererPixbufEmblemInfo
{
    gchar *stock_id;
    GtkIconSize stock_size;
    gchar *stock_detail;
};

G_DEFINE_TYPE (CajaCellRendererPixbufEmblem, caja_cell_renderer_pixbuf_emblem, GTK_TYPE_CELL_RENDERER);

static void
caja_cell_renderer_pixbuf_emblem_init (CajaCellRendererPixbufEmblem *cellpixbuf)
{
    CajaCellRendererPixbufEmblemInfo *cellinfo;

    cellinfo = g_new0 (CajaCellRendererPixbufEmblemInfo, 1);
    cellinfo->stock_size = GTK_ICON_SIZE_MENU;
    g_object_set_data (G_OBJECT (cellpixbuf), CELLINFO_KEY, cellinfo);
}

static void
caja_cell_renderer_pixbuf_emblem_class_init (CajaCellRendererPixbufEmblemClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);
    GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);

    object_class->finalize = caja_cell_renderer_pixbuf_emblem_finalize;

    object_class->get_property = caja_cell_renderer_pixbuf_emblem_get_property;
    object_class->set_property = caja_cell_renderer_pixbuf_emblem_set_property;

    cell_class->get_size = caja_cell_renderer_pixbuf_emblem_get_size;
    cell_class->render = caja_cell_renderer_pixbuf_emblem_render;

    g_object_class_install_property (object_class,
                                     PROP_PIXBUF,
                                     g_param_spec_object ("pixbuf",
                                             "Pixbuf Object",
                                             "The pixbuf to render",
                                             GDK_TYPE_PIXBUF,
                                             G_PARAM_READABLE |
                                             G_PARAM_WRITABLE));

    g_object_class_install_property (object_class,
                                     PROP_PIXBUF_EXPANDER_OPEN,
                                     g_param_spec_object ("pixbuf_expander_open",
                                             "Pixbuf Expander Open",
                                             "Pixbuf for open expander",
                                             GDK_TYPE_PIXBUF,
                                             G_PARAM_READABLE |
                                             G_PARAM_WRITABLE));

    g_object_class_install_property (object_class,
                                     PROP_PIXBUF_EXPANDER_CLOSED,
                                     g_param_spec_object ("pixbuf_expander_closed",
                                             "Pixbuf Expander Closed",
                                             "Pixbuf for closed expander",
                                             GDK_TYPE_PIXBUF,
                                             G_PARAM_READABLE |
                                             G_PARAM_WRITABLE));

    g_object_class_install_property (object_class,
                                     PROP_STOCK_ID,
                                     g_param_spec_string ("stock_id",
                                             "Stock ID",
                                             "The stock ID of the stock icon to render",
                                             NULL,
                                             G_PARAM_READWRITE));

    g_object_class_install_property (object_class,
                                     PROP_STOCK_SIZE,
                                     g_param_spec_enum ("stock_size",
                                             "Size",
                                             "The size of the rendered icon",
                                             GTK_TYPE_ICON_SIZE,
                                             GTK_ICON_SIZE_MENU,
                                             G_PARAM_READWRITE));

    g_object_class_install_property (object_class,
                                     PROP_STOCK_DETAIL,
                                     g_param_spec_string ("stock_detail",
                                             "Detail",
                                             "Render detail to pass to the theme engine",
                                             NULL,
                                             G_PARAM_READWRITE));

    g_object_class_install_property (object_class,
                                     PROP_PIXBUF_EMBLEM,
                                     g_param_spec_object ("pixbuf_emblem",
                                             "Pixbuf Emblem Object",
                                             "The emblem to overlay",
                                             GDK_TYPE_PIXBUF,
                                             G_PARAM_READABLE |
                                             G_PARAM_WRITABLE));
}

static void
caja_cell_renderer_pixbuf_emblem_finalize (GObject *object)
{
    CajaCellRendererPixbufEmblem *cellpixbuf = CAJA_CELL_RENDERER_PIXBUF_EMBLEM (object);
    CajaCellRendererPixbufEmblemInfo *cellinfo = g_object_get_data (object, CELLINFO_KEY);

    if (cellpixbuf->pixbuf && cellinfo->stock_id)
    {
        g_object_unref (cellpixbuf->pixbuf);
    }

    if (cellinfo->stock_id)
    {
        g_free (cellinfo->stock_id);
    }

    if (cellinfo->stock_detail)
    {
        g_free (cellinfo->stock_detail);
    }

    g_free (cellinfo);
    g_object_set_data (object, CELLINFO_KEY, NULL);

    (* G_OBJECT_CLASS (caja_cell_renderer_pixbuf_emblem_parent_class)->finalize) (object);
}

static void
caja_cell_renderer_pixbuf_emblem_get_property (GObject        *object,
        guint           param_id,
        GValue         *value,
        GParamSpec     *pspec)
{
    CajaCellRendererPixbufEmblem *cellpixbuf = CAJA_CELL_RENDERER_PIXBUF_EMBLEM (object);
    CajaCellRendererPixbufEmblemInfo *cellinfo = g_object_get_data (object, CELLINFO_KEY);

    switch (param_id)
    {
    case PROP_PIXBUF:
        g_value_set_object (value,
                            cellpixbuf->pixbuf ? G_OBJECT (cellpixbuf->pixbuf) : NULL);
        break;
    case PROP_PIXBUF_EXPANDER_OPEN:
        g_value_set_object (value,
                            cellpixbuf->pixbuf_expander_open ? G_OBJECT (cellpixbuf->pixbuf_expander_open) : NULL);
        break;
    case PROP_PIXBUF_EXPANDER_CLOSED:
        g_value_set_object (value,
                            cellpixbuf->pixbuf_expander_closed ? G_OBJECT (cellpixbuf->pixbuf_expander_closed) : NULL);
        break;
    case PROP_STOCK_ID:
        g_value_set_string (value, cellinfo->stock_id);
        break;
    case PROP_STOCK_SIZE:
        g_value_set_enum (value, cellinfo->stock_size);
        break;
    case PROP_STOCK_DETAIL:
        g_value_set_string (value, cellinfo->stock_detail);
        break;
    case PROP_PIXBUF_EMBLEM:
        g_value_set_object (value,
                            cellpixbuf->pixbuf_emblem ? G_OBJECT (cellpixbuf->pixbuf_emblem) : NULL);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
        break;
    }
}

static void
caja_cell_renderer_pixbuf_emblem_set_property (GObject      *object,
        guint         param_id,
        const GValue *value,
        GParamSpec   *pspec)
{
    GdkPixbuf *pixbuf;
    CajaCellRendererPixbufEmblem *cellpixbuf = CAJA_CELL_RENDERER_PIXBUF_EMBLEM (object);
    CajaCellRendererPixbufEmblemInfo *cellinfo = g_object_get_data (object, CELLINFO_KEY);

    switch (param_id)
    {
    case PROP_PIXBUF:
        pixbuf = (GdkPixbuf*) g_value_get_object (value);
        if (pixbuf)
        {
            g_object_ref (pixbuf);
        }
        if (cellpixbuf->pixbuf)
        {
            g_object_unref (cellpixbuf->pixbuf);
        }
        cellpixbuf->pixbuf = pixbuf;
        break;
    case PROP_PIXBUF_EXPANDER_OPEN:
        pixbuf = (GdkPixbuf*) g_value_get_object (value);
        if (pixbuf)
        {
            g_object_ref (pixbuf);
        }
        if (cellpixbuf->pixbuf_expander_open)
        {
            g_object_unref (cellpixbuf->pixbuf_expander_open);
        }
        cellpixbuf->pixbuf_expander_open = pixbuf;
        break;
    case PROP_PIXBUF_EXPANDER_CLOSED:
        pixbuf = (GdkPixbuf*) g_value_get_object (value);
        if (pixbuf)
        {
            g_object_ref (pixbuf);
        }
        if (cellpixbuf->pixbuf_expander_closed)
        {
            g_object_unref (cellpixbuf->pixbuf_expander_closed);
        }
        cellpixbuf->pixbuf_expander_closed = pixbuf;
        break;
    case PROP_STOCK_ID:
        if (cellinfo->stock_id)
        {
            g_free (cellinfo->stock_id);
        }
        cellinfo->stock_id = g_strdup (g_value_get_string (value));
        break;
    case PROP_STOCK_SIZE:
        cellinfo->stock_size = g_value_get_enum (value);
        break;
    case PROP_STOCK_DETAIL:
        if (cellinfo->stock_detail)
        {
            g_free (cellinfo->stock_detail);
        }
        cellinfo->stock_detail = g_strdup (g_value_get_string (value));
        break;
    case PROP_PIXBUF_EMBLEM:
        pixbuf = (GdkPixbuf *) g_value_get_object (value);
        if (pixbuf)
        {
            g_object_ref (pixbuf);
        }
        if (cellpixbuf->pixbuf_emblem)
        {
            g_object_unref (cellpixbuf->pixbuf_emblem);
        }
        cellpixbuf->pixbuf_emblem = pixbuf;
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
        break;
    }
}

GtkCellRenderer *
caja_cell_renderer_pixbuf_emblem_new (void)
{
    return g_object_new (CAJA_TYPE_CELL_RENDERER_PIXBUF_EMBLEM, NULL);
}

static void
caja_cell_renderer_pixbuf_emblem_create_stock_pixbuf (CajaCellRendererPixbufEmblem *cellpixbuf,
        GtkWidget             *widget)
{
    CajaCellRendererPixbufEmblemInfo *cellinfo = g_object_get_data (G_OBJECT (cellpixbuf), CELLINFO_KEY);

    if (cellpixbuf->pixbuf)
    {
        g_object_unref (cellpixbuf->pixbuf);
    }

    cellpixbuf->pixbuf = gtk_widget_render_icon (widget,
                         cellinfo->stock_id,
                         cellinfo->stock_size,
                         cellinfo->stock_detail);
}

static void
caja_cell_renderer_pixbuf_emblem_get_size (GtkCellRenderer *cell,
        GtkWidget       *widget,
        GdkRectangle    *cell_area,
        gint            *x_offset,
        gint            *y_offset,
        gint            *width,
        gint            *height)
{
    CajaCellRendererPixbufEmblem *cellpixbuf = (CajaCellRendererPixbufEmblem *) cell;
    CajaCellRendererPixbufEmblemInfo *cellinfo = g_object_get_data (G_OBJECT (cell), CELLINFO_KEY);
    gint pixbuf_width  = 0;
    gint pixbuf_height = 0;
    gint calc_width;
    gint calc_height;
    gint xpad, ypad;

    if (!cellpixbuf->pixbuf && cellinfo->stock_id)
        caja_cell_renderer_pixbuf_emblem_create_stock_pixbuf (cellpixbuf, widget);

    if (cellpixbuf->pixbuf)
    {
        pixbuf_width  = gdk_pixbuf_get_width (cellpixbuf->pixbuf);
        pixbuf_height = gdk_pixbuf_get_height (cellpixbuf->pixbuf);
    }
    if (cellpixbuf->pixbuf_expander_open)
    {
        pixbuf_width  = MAX (pixbuf_width, gdk_pixbuf_get_width (cellpixbuf->pixbuf_expander_open));
        pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (cellpixbuf->pixbuf_expander_open));
    }
    if (cellpixbuf->pixbuf_expander_closed)
    {
        pixbuf_width  = MAX (pixbuf_width, gdk_pixbuf_get_width (cellpixbuf->pixbuf_expander_closed));
        pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (cellpixbuf->pixbuf_expander_closed));
    }

    gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
    calc_width  = xpad * 2 + pixbuf_width;
    calc_height = ypad * 2 + pixbuf_height;

    if (x_offset) *x_offset = 0;
    if (y_offset) *y_offset = 0;

    if (cell_area && pixbuf_width > 0 && pixbuf_height > 0)
    {
        gfloat xalign, yalign;

        gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
        if (x_offset)
        {
            *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
                          1.0 - xalign : xalign) *
                         (cell_area->width - calc_width - 2 * xpad));
            *x_offset = MAX (*x_offset, 0) + xpad;
        }
        if (y_offset)
        {
            *y_offset = (yalign *
                         (cell_area->height - calc_height - 2 * ypad));
            *y_offset = MAX (*y_offset, 0) + ypad;
        }
    }

    if (width)
        *width = calc_width;

    if (height)
        *height = calc_height;
}

static void
caja_cell_renderer_pixbuf_emblem_render (GtkCellRenderer      *cell,
        GdkWindow            *window,
        GtkWidget            *widget,
        GdkRectangle         *background_area,
        GdkRectangle         *cell_area,
        GdkRectangle         *expose_area,
        GtkCellRendererState  flags)

{
    CajaCellRendererPixbufEmblem *cellpixbuf = (CajaCellRendererPixbufEmblem *) cell;
    CajaCellRendererPixbufEmblemInfo *cellinfo = g_object_get_data (G_OBJECT (cell), CELLINFO_KEY);
    GdkPixbuf *pixbuf;
    GdkRectangle pix_rect;
    GdkRectangle pix_emblem_rect;
    GdkRectangle draw_rect;
    gboolean stock_pixbuf = FALSE;
    gint xpad, ypad;
    gboolean is_expander, is_expanded;

    pixbuf = cellpixbuf->pixbuf;
    g_object_get (cell,
                  "is-expander", &is_expander,
                  "is-expanded", &is_expanded,
                  NULL);
    if (is_expander)
    {
        if (is_expanded &&
                cellpixbuf->pixbuf_expander_open != NULL)
        {
            pixbuf = cellpixbuf->pixbuf_expander_open;
        }
        else if (!is_expanded &&
                 cellpixbuf->pixbuf_expander_closed != NULL)
        {
            pixbuf = cellpixbuf->pixbuf_expander_closed;
        }
    }

    if (!pixbuf && !cellinfo->stock_id)
    {
        return;
    }
    else if (!pixbuf && cellinfo->stock_id)
    {
        stock_pixbuf = TRUE;
    }

    caja_cell_renderer_pixbuf_emblem_get_size (cell, widget, cell_area,
            &pix_rect.x,
            &pix_rect.y,
            &pix_rect.width,
            &pix_rect.height);

    if (stock_pixbuf)
        pixbuf = cellpixbuf->pixbuf;

    gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
    pix_rect.x += cell_area->x;
    pix_rect.y += cell_area->y;
    pix_rect.width  -= xpad * 2;
    pix_rect.height -= ypad * 2;

    if (gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect) &&
            gdk_rectangle_intersect (expose_area, &draw_rect, &draw_rect))
    {
        gdk_draw_pixbuf (window,
                         gtk_widget_get_style (widget)->black_gc,
                         pixbuf,
                         /* pixbuf 0, 0 is at pix_rect.x, pix_rect.y */
                         draw_rect.x - pix_rect.x,
                         draw_rect.y - pix_rect.y,
                         draw_rect.x,
                         draw_rect.y,
                         draw_rect.width,
                         draw_rect.height,
                         GDK_RGB_DITHER_NORMAL,
                         0, 0);
    }

    if (cellpixbuf->pixbuf_emblem)
    {
        pix_emblem_rect.width = gdk_pixbuf_get_width (cellpixbuf->pixbuf_emblem);
        pix_emblem_rect.height = gdk_pixbuf_get_height (cellpixbuf->pixbuf_emblem);
        pix_emblem_rect.x = pix_rect.x;
        pix_emblem_rect.y = pix_rect.y + pix_rect.height - pix_emblem_rect.height;
        if (gdk_rectangle_intersect (cell_area, &pix_emblem_rect, &draw_rect) &&
                gdk_rectangle_intersect (expose_area, &draw_rect, &draw_rect))
        {
            gdk_draw_pixbuf (window,
                             gtk_widget_get_style (widget)->black_gc,
                             cellpixbuf->pixbuf_emblem,
                             /* pixbuf 0, 0 is at pix_emblem_rect.x, pix_emblem_rect.y */
                             draw_rect.x - pix_emblem_rect.x,
                             draw_rect.y - pix_emblem_rect.y,
                             draw_rect.x,
                             draw_rect.y,
                             draw_rect.width,
                             draw_rect.height,
                             GDK_RGB_DITHER_NORMAL,
                             0, 0);
        }
    }
}
