/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
 *
 * nautilus-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
 *
 * Mail Notification
 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
 *
 * 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 3 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

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

static void nautilus_cell_renderer_pixbuf_emblem_get_property (GObject *object,
							guint		param_id,
							GValue		*value,
							GParamSpec	*pspec);
static void nautilus_cell_renderer_pixbuf_emblem_set_property (GObject *object,
							guint		param_id,
							const GValue	*value,
							GParamSpec	*pspec);
static void nautilus_cell_renderer_pixbuf_emblem_init       (NautilusCellRendererPixbufEmblem *cellpixbuf);
static void nautilus_cell_renderer_pixbuf_emblem_class_init  (NautilusCellRendererPixbufEmblemClass *klass);
static void nautilus_cell_renderer_pixbuf_emblem_finalize (GObject *object);
static void nautilus_cell_renderer_pixbuf_emblem_create_stock_pixbuf (NautilusCellRendererPixbufEmblem *cellpixbuf,
                                                          GtkWidget             *widget);
#if GTK_CHECK_VERSION(3,0,0)
static void nautilus_cell_renderer_pixbuf_emblem_get_size   (GtkCellRenderer            *cell,
                                                 GtkWidget                  *widget,
                                                 const GdkRectangle         *rectangle,
                                                 gint                       *x_offset,
                                                 gint                       *y_offset,
                                                 gint                       *width,
                                                 gint                       *height);
static void     nautilus_cell_renderer_pixbuf_emblem_render     (GtkCellRenderer            *cell,
                                                          cairo_t                    *cr,
                                                          GtkWidget                  *widget,
                                                          const GdkRectangle         *background_area,
                                                          const GdkRectangle         *cell_area,
                                                          GtkCellRendererState                       flags);
#else
static void nautilus_cell_renderer_pixbuf_emblem_get_size   (GtkCellRenderer            *cell,
                                                 GtkWidget                  *widget,
                                                 GdkRectangle               *rectangle,
                                                 gint                       *x_offset,
                                                 gint                       *y_offset,
                                                 gint                       *width,
                                                 gint                       *height);
static void     nautilus_cell_renderer_pixbuf_emblem_render     (GtkCellRenderer            *cell,
                                                          GdkWindow                  *window,
                                                          GtkWidget                  *widget,
                                                          GdkRectangle               *background_area,
                                                          GdkRectangle               *cell_area,
                                                          GdkRectangle               *expose_area,
                                                          GtkCellRendererState                       flags);
#endif

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
};

static gpointer parent_class;

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

typedef struct _NautilusCellRendererPixbufEmblemInfo NautilusCellRendererPixbufEmblemInfo;
struct _NautilusCellRendererPixbufEmblemInfo
{
	gchar *stock_id;
	GtkIconSize stock_size;
	gchar *stock_detail;
};

GType
nautilus_cell_renderer_pixbuf_emblem_get_type (void)
{
	static GType cell_pixbuf_type = 0;

	if (!cell_pixbuf_type) {
		static const GTypeInfo cell_pixbuf_info =
		{
			sizeof (NautilusCellRendererPixbufEmblemClass),
			NULL,                                                     /* base_init */
			NULL,                                                     /* base_finalize */
			(GClassInitFunc) nautilus_cell_renderer_pixbuf_emblem_class_init,
			NULL,                                                     /* class_finalize */
			NULL,                                                     /* class_data */
			sizeof (NautilusCellRendererPixbufEmblem),
			0,                                                        /* n_preallocs */
			(GInstanceInitFunc) nautilus_cell_renderer_pixbuf_emblem_init,
		};

		cell_pixbuf_type = g_type_register_static (GTK_TYPE_CELL_RENDERER,
							   "NautilusCellRendererPixbufEmblem",
							   &cell_pixbuf_info, 0);
	}

	return cell_pixbuf_type;
}

static void
nautilus_cell_renderer_pixbuf_emblem_init (NautilusCellRendererPixbufEmblem *cellpixbuf)
{
	NautilusCellRendererPixbufEmblemInfo *cellinfo;

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

static void
nautilus_cell_renderer_pixbuf_emblem_class_init (NautilusCellRendererPixbufEmblemClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);

	parent_class = g_type_class_peek_parent (klass);

	object_class->finalize = nautilus_cell_renderer_pixbuf_emblem_finalize;

	object_class->get_property = nautilus_cell_renderer_pixbuf_emblem_get_property;
	object_class->set_property = nautilus_cell_renderer_pixbuf_emblem_set_property;

	cell_class->get_size = nautilus_cell_renderer_pixbuf_emblem_get_size;
	cell_class->render = nautilus_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
nautilus_cell_renderer_pixbuf_emblem_finalize (GObject *object)
{
	NautilusCellRendererPixbufEmblem *cellpixbuf = NAUTILUS_CELL_RENDERER_PIXBUF_EMBLEM (object);
	NautilusCellRendererPixbufEmblemInfo *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 (parent_class)->finalize) (object);
}

static void
nautilus_cell_renderer_pixbuf_emblem_get_property (GObject        *object,
                                       guint           param_id,
                                       GValue         *value,
                                       GParamSpec     *pspec)
{
	NautilusCellRendererPixbufEmblem *cellpixbuf = NAUTILUS_CELL_RENDERER_PIXBUF_EMBLEM (object);
	NautilusCellRendererPixbufEmblemInfo *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
nautilus_cell_renderer_pixbuf_emblem_set_property (GObject      *object,
                                            guint         param_id,
                                            const GValue *value,
                                            GParamSpec   *pspec)
{
	GdkPixbuf *pixbuf;
	NautilusCellRendererPixbufEmblem *cellpixbuf = NAUTILUS_CELL_RENDERER_PIXBUF_EMBLEM (object);
	NautilusCellRendererPixbufEmblemInfo *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 *
nautilus_cell_renderer_pixbuf_emblem_new (void)
{
	return g_object_new (NAUTILUS_TYPE_CELL_RENDERER_PIXBUF_EMBLEM, NULL);
}

static void
nautilus_cell_renderer_pixbuf_emblem_create_stock_pixbuf (NautilusCellRendererPixbufEmblem *cellpixbuf,
					      GtkWidget             *widget)
{
	NautilusCellRendererPixbufEmblemInfo *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);
}

#if GTK_CHECK_VERSION(3,0,0)
static void
nautilus_cell_renderer_pixbuf_emblem_get_size (GtkCellRenderer *cell,
				   GtkWidget              *widget,
				   const GdkRectangle     *cell_area,
				   gint                   *x_offset,
				   gint                   *y_offset,
				   gint                   *width,
				   gint                   *height)
#else

static void
nautilus_cell_renderer_pixbuf_emblem_get_size (GtkCellRenderer *cell,
                   GtkWidget       *widget,
                   GdkRectangle    *cell_area,
                   gint            *x_offset,
                   gint            *y_offset,
                   gint            *width,
                   gint            *height)
#endif
{
	NautilusCellRendererPixbufEmblem *cellpixbuf = (NautilusCellRendererPixbufEmblem *) cell;
	NautilusCellRendererPixbufEmblemInfo *cellinfo = g_object_get_data (G_OBJECT (cell), CELLINFO_KEY);
	gint pixbuf_width  = 0;
	gint pixbuf_height = 0;
	gint calc_width;
	gint calc_height;
	int xpad;
	int ypad;
	gfloat xalign;
	gfloat yalign;

	if (!cellpixbuf->pixbuf && cellinfo->stock_id)
		nautilus_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);
	gtk_cell_renderer_get_alignment(cell, &xalign, &yalign);

	calc_width = (gint) xpad * 2 + pixbuf_width;
	calc_height = (gint) 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) {
		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;
}

#if GTK_CHECK_VERSION(3,0,0)
static void
nautilus_cell_renderer_pixbuf_emblem_render (GtkCellRenderer      *cell,
                                 cairo_t              *cr,
                                 GtkWidget            *widget,
                                 const GdkRectangle   *background_area,
                                 const GdkRectangle   *cell_area,
                                 GtkCellRendererState  flags)
#else
static void
nautilus_cell_renderer_pixbuf_emblem_render (GtkCellRenderer      *cell,
                                 GdkWindow            *window,
                                 GtkWidget            *widget,
                                 GdkRectangle         *background_area,
                                 GdkRectangle         *cell_area,
                                 GdkRectangle         *expose_area,
                                 GtkCellRendererState  flags)
#endif
{
	NautilusCellRendererPixbufEmblem *cellpixbuf = (NautilusCellRendererPixbufEmblem *) cell;
	NautilusCellRendererPixbufEmblemInfo *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;
	gboolean is_expander = FALSE;
	gboolean is_expanded = FALSE;
	int xpad;
	int ypad;

	g_object_get(cell, "is-expander", &is_expander, "is-expanded", &is_expanded, NULL);

	pixbuf = cellpixbuf->pixbuf;
	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;
	}

	nautilus_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 GTK_CHECK_VERSION(3,0,0)
	if (gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect)) {
		cairo_move_to (cr, draw_rect.x - pix_rect.x, draw_rect.y - pix_rect.y);
		gdk_cairo_set_source_pixbuf (cr, pixbuf, draw_rect.x, draw_rect.y);
		cairo_paint (cr);
	}
#else
    if (gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect) &&
        gdk_rectangle_intersect (expose_area, &draw_rect, &draw_rect)) {
        gdk_draw_pixbuf (window,
            widget->style->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);
    }
#endif

	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 GTK_CHECK_VERSION(3,0,0)
		if (gdk_rectangle_intersect (cell_area, &pix_emblem_rect, &draw_rect)) {
			cairo_move_to (cr, draw_rect.x - pix_emblem_rect.x, draw_rect.y - pix_emblem_rect.y);
			gdk_cairo_set_source_pixbuf (cr, cellpixbuf->pixbuf_emblem, draw_rect.x, draw_rect.y);
			cairo_paint (cr);
		}
#else
        if (gdk_rectangle_intersect (cell_area, &pix_emblem_rect, &draw_rect) &&
            gdk_rectangle_intersect (expose_area, &draw_rect, &draw_rect)) {
            gdk_draw_pixbuf (window,
                widget->style->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);
        }
#endif
	}
}
