/*  xfce4
 *  Copyright (C) 2002 Olivier Fourdan (fourdan@xfce.org)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gdk/gdk.h>
#include <gtk/gtkmisc.h>

#include "xfce_decortoggle.h"

#define DECORTOGGLE_WIDTH  10
#define DECORTOGGLE_HEIGHT 10

#define decortoggle_WIDTH  10
#define decortoggle_HEIGHT 10

static const unsigned char arrow_up_dark_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x18, 0x00, 0x0c, 0x00,
    0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_up_mid_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x70, 0x00,
    0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_up_light_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_down_dark_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_down_mid_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_down_light_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00,
    0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_left_dark_bits[] = {
    0x00, 0x00, 0x20, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_left_mid_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30, 0x00, 0x38, 0x00,
    0x30, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_left_light_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00,
    0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_right_dark_bits[] = {
    0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00,
    0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_right_mid_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0x30, 0x00,
    0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char arrow_right_light_bits[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
    0x60, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
};

/* Forward declarations */

static void xfce_decortoggle_class_init (XfceDecortoggleClass * class);
static void xfce_decortoggle_init (XfceDecortoggle * decortoggle);
static void xfce_decortoggle_destroy (GtkObject * object);
static void xfce_decortoggle_finalize (GObject * object);
static void xfce_decortoggle_size_request (GtkWidget * widget,
					   GtkRequisition * requisition);
static gint xfce_decortoggle_expose (GtkWidget * widget,
				     GdkEventExpose * event);
static void xfce_decortoggle_draw (GtkWidget * widget, GdkRectangle * area);

/* Local data */

static GtkWidgetClass *parent_class = NULL;

GtkType
xfce_decortoggle_get_type (void)
{
    static GtkType decortoggle_type = 0;

    if (!decortoggle_type)
    {
	static const GTypeInfo decortoggle_info = {
	    sizeof (XfceDecortoggleClass),
	    NULL,		/* base_init */
	    NULL,		/* base_finalize */
	    (GClassInitFunc) xfce_decortoggle_class_init,
	    NULL,		/* class_finalize */
	    NULL,		/* class_data */
	    sizeof (XfceDecortoggle),
	    0,			/* n_preallocs */
	    (GInstanceInitFunc) xfce_decortoggle_init,
	    NULL		/* value_table */
	};

	decortoggle_type =
	    g_type_register_static (GTK_TYPE_MISC, "XfceDecortoggle",
				    &decortoggle_info, 0);
    }

    return decortoggle_type;
}

static void
xfce_decortoggle_class_init (XfceDecortoggleClass * class)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (class);
    GtkObjectClass *object_class;
    GtkWidgetClass *widget_class;

    object_class = (GtkObjectClass *) class;
    widget_class = (GtkWidgetClass *) class;
    gobject_class = G_OBJECT_CLASS (class);

    parent_class = gtk_type_class (gtk_misc_get_type ());

    widget_class->size_request = xfce_decortoggle_size_request;
    object_class->destroy = xfce_decortoggle_destroy;
    gobject_class->finalize = xfce_decortoggle_finalize;

    widget_class->expose_event = xfce_decortoggle_expose;
}

static void
xfce_decortoggle_init (XfceDecortoggle * decortoggle)
{
    GTK_WIDGET_SET_FLAGS (decortoggle, GTK_NO_WINDOW);
    decortoggle->arrow_up_dark_bmap = NULL;
    decortoggle->arrow_up_mid_bmap = NULL;
    decortoggle->arrow_up_light_bmap = NULL;
    decortoggle->arrow_down_dark_bmap = NULL;
    decortoggle->arrow_down_mid_bmap = NULL;
    decortoggle->arrow_down_light_bmap = NULL;
    decortoggle->arrow_left_dark_bmap = NULL;
    decortoggle->arrow_left_mid_bmap = NULL;
    decortoggle->arrow_left_light_bmap = NULL;
    decortoggle->arrow_right_dark_bmap = NULL;
    decortoggle->arrow_right_mid_bmap = NULL;
    decortoggle->arrow_right_light_bmap = NULL;
    decortoggle->initialized = FALSE;
    decortoggle->arrow_type = GTK_ARROW_LEFT;
}

GtkWidget *
xfce_decortoggle_new (GtkArrowType arrow_type)
{
    XfceDecortoggle *decortoggle;

    decortoggle = g_object_new (xfce_decortoggle_get_type (), NULL);
    decortoggle->arrow_type = arrow_type;
    decortoggle->is_active = FALSE;

    return GTK_WIDGET (decortoggle);
}

static void
xfce_decortoggle_destroy (GtkObject * object)
{
    g_return_if_fail (object != NULL);
    g_return_if_fail (XFCE_IS_DECORTOGGLE (object));

    if (GTK_OBJECT_CLASS (parent_class)->destroy)
	(*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

static void
xfce_decortoggle_finalize (GObject * object)
{
    XfceDecortoggle *decortoggle;

    g_return_if_fail (object != NULL);
    g_return_if_fail (XFCE_IS_DECORTOGGLE (object));

    decortoggle = XFCE_DECORTOGGLE (object);

    if (decortoggle->arrow_up_dark_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_up_dark_bmap));
	decortoggle->arrow_up_dark_bmap = NULL;
    }
    if (decortoggle->arrow_up_mid_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_up_mid_bmap));
	decortoggle->arrow_up_mid_bmap = NULL;
    }
    if (decortoggle->arrow_up_light_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_up_light_bmap));
	decortoggle->arrow_up_light_bmap = NULL;
    }

    if (decortoggle->arrow_down_dark_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_down_dark_bmap));
	decortoggle->arrow_down_dark_bmap = NULL;
    }
    if (decortoggle->arrow_down_mid_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_down_mid_bmap));
	decortoggle->arrow_down_mid_bmap = NULL;
    }
    if (decortoggle->arrow_down_light_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_down_light_bmap));
	decortoggle->arrow_down_light_bmap = NULL;
    }

    if (decortoggle->arrow_left_dark_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_left_dark_bmap));
	decortoggle->arrow_left_dark_bmap = NULL;
    }
    if (decortoggle->arrow_left_mid_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_left_mid_bmap));
	decortoggle->arrow_left_mid_bmap = NULL;
    }
    if (decortoggle->arrow_left_light_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_left_light_bmap));
	decortoggle->arrow_left_light_bmap = NULL;
    }

    if (decortoggle->arrow_right_dark_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_right_dark_bmap));
	decortoggle->arrow_right_dark_bmap = NULL;
    }
    if (decortoggle->arrow_right_mid_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_right_mid_bmap));
	decortoggle->arrow_right_mid_bmap = NULL;
    }
    if (decortoggle->arrow_right_light_bmap)
    {
	g_object_unref (G_OBJECT (decortoggle->arrow_right_light_bmap));
	decortoggle->arrow_right_light_bmap = NULL;
    }
    decortoggle->initialized = FALSE;
    G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
xfce_decortoggle_size_request (GtkWidget * widget,
			       GtkRequisition * requisition)
{
    requisition->width = DECORTOGGLE_WIDTH + 4;
    requisition->height = DECORTOGGLE_HEIGHT + 4;
}

static void
initialize_decortoggle_bmap (GtkWidget * widget)
{
    XfceDecortoggle *decortoggle;

    g_return_if_fail (widget != NULL);
    g_return_if_fail (XFCE_IS_DECORTOGGLE (widget));

    decortoggle = XFCE_DECORTOGGLE (widget);
    g_return_if_fail (decortoggle->initialized == FALSE);

    if (!(decortoggle->arrow_up_dark_bmap))
    {
	decortoggle->arrow_up_dark_bmap =
	    gdk_bitmap_create_from_data (widget->window, (gchar*) arrow_up_dark_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }
    if (!(decortoggle->arrow_up_mid_bmap))
    {
	decortoggle->arrow_up_mid_bmap =
	    gdk_bitmap_create_from_data (widget->window, (gchar*) arrow_up_mid_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }
    if (!(decortoggle->arrow_up_light_bmap))
    {
	decortoggle->arrow_up_light_bmap =
	    gdk_bitmap_create_from_data (widget->window, (gchar*) arrow_up_light_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }

    if (!(decortoggle->arrow_down_dark_bmap))
    {
	decortoggle->arrow_down_dark_bmap =
	    gdk_bitmap_create_from_data (widget->window, (gchar*) arrow_down_dark_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }
    if (!(decortoggle->arrow_down_mid_bmap))
    {
	decortoggle->arrow_down_mid_bmap =
	    gdk_bitmap_create_from_data (widget->window, (gchar*) arrow_down_mid_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }
    if (!(decortoggle->arrow_down_light_bmap))
    {
	decortoggle->arrow_down_light_bmap =
	    gdk_bitmap_create_from_data (widget->window,
					 (gchar*) arrow_down_light_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }

    if (!(decortoggle->arrow_left_dark_bmap))
    {
	decortoggle->arrow_left_dark_bmap =
	    gdk_bitmap_create_from_data (widget->window, (gchar*) arrow_left_dark_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }
    if (!(decortoggle->arrow_left_mid_bmap))
    {
	decortoggle->arrow_left_mid_bmap =
	    gdk_bitmap_create_from_data (widget->window, (gchar*) arrow_left_mid_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }
    if (!(decortoggle->arrow_left_light_bmap))
    {
	decortoggle->arrow_left_light_bmap =
	    gdk_bitmap_create_from_data (widget->window,
					 (gchar*) arrow_left_light_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }

    if (!(decortoggle->arrow_right_dark_bmap))
    {
	decortoggle->arrow_right_dark_bmap =
	    gdk_bitmap_create_from_data (widget->window,
					 (gchar*) arrow_right_dark_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }
    if (!(decortoggle->arrow_right_mid_bmap))
    {
	decortoggle->arrow_right_mid_bmap =
	    gdk_bitmap_create_from_data (widget->window, (gchar*) arrow_right_mid_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }
    if (!(decortoggle->arrow_right_light_bmap))
    {
	decortoggle->arrow_right_light_bmap =
	    gdk_bitmap_create_from_data (widget->window,
					 (gchar*) arrow_right_light_bits,
					 decortoggle_WIDTH,
					 decortoggle_HEIGHT);
    }
    decortoggle->initialized = TRUE;
}

static gint
xfce_decortoggle_expose (GtkWidget * widget, GdkEventExpose * event)
{
    g_return_val_if_fail (XFCE_IS_DECORTOGGLE (widget), FALSE);
    g_return_val_if_fail (event != NULL, FALSE);

    xfce_decortoggle_draw (widget, &event->area);

    return FALSE;
}

static void
draw_part (GdkDrawable * drawable, GdkGC * gc, GdkRectangle * area, gint x,
	   gint y, GdkBitmap * bmap)
{
    if (area)
    {
	gdk_gc_set_clip_rectangle (gc, area);
    }

    gdk_gc_set_ts_origin (gc, x, y);
    gdk_gc_set_stipple (gc, bmap);
    gdk_gc_set_fill (gc, GDK_STIPPLED);

    gdk_draw_rectangle (drawable, gc, TRUE, x, y, DECORTOGGLE_WIDTH,
			DECORTOGGLE_HEIGHT);

    gdk_gc_set_fill (gc, GDK_SOLID);

    if (area)
    {
	gdk_gc_set_clip_rectangle (gc, NULL);
    }
}

static void
xfce_decortoggle_draw (GtkWidget * widget, GdkRectangle * area)
{
    XfceDecortoggle *decortoggle;

    g_return_if_fail (widget != NULL);
    g_return_if_fail (XFCE_IS_DECORTOGGLE (widget));

    decortoggle = XFCE_DECORTOGGLE (widget);

    if (GTK_WIDGET_DRAWABLE (widget))
    {
	guint w = widget->allocation.width;
	guint h = widget->allocation.height;
	gint x = widget->allocation.x + (w - DECORTOGGLE_WIDTH) / 2;
	gint y = widget->allocation.y + (h - DECORTOGGLE_HEIGHT) / 2;

	if (!(decortoggle->initialized))
	{
	    initialize_decortoggle_bmap (widget);
	}

	switch (decortoggle->arrow_type)
	{
	    case GTK_ARROW_UP:
		if (decortoggle->is_active)
		{
		    draw_part (widget->window,
			       widget->style->light_gc[widget->state], area,
			       x, y, decortoggle->arrow_down_light_bmap);
		    draw_part (widget->window,
			       widget->style->bg_gc[GTK_STATE_SELECTED], area,
			       x, y, decortoggle->arrow_down_mid_bmap);
		    draw_part (widget->window,
			       widget->style->dark_gc[widget->state], area, x,
			       y, decortoggle->arrow_down_dark_bmap);
		}
		else
		{
		    draw_part (widget->window,
			       widget->style->light_gc[widget->state], area,
			       x, y, decortoggle->arrow_up_light_bmap);
		    draw_part (widget->window,
			       widget->style->mid_gc[widget->state], area, x,
			       y, decortoggle->arrow_up_mid_bmap);
		    draw_part (widget->window,
			       widget->style->dark_gc[widget->state], area, x,
			       y, decortoggle->arrow_up_dark_bmap);
		}
		break;
	    case GTK_ARROW_DOWN:
		if (decortoggle->is_active)
		{
		    draw_part (widget->window,
			       widget->style->light_gc[widget->state], area,
			       x, y, decortoggle->arrow_up_light_bmap);
		    draw_part (widget->window,
			       widget->style->bg_gc[GTK_STATE_SELECTED], area,
			       x, y, decortoggle->arrow_up_mid_bmap);
		    draw_part (widget->window,
			       widget->style->dark_gc[widget->state], area, x,
			       y, decortoggle->arrow_up_dark_bmap);
		}
		else
		{
		    draw_part (widget->window,
			       widget->style->light_gc[widget->state], area,
			       x, y, decortoggle->arrow_down_light_bmap);
		    draw_part (widget->window,
			       widget->style->mid_gc[widget->state], area, x,
			       y, decortoggle->arrow_down_mid_bmap);
		    draw_part (widget->window,
			       widget->style->dark_gc[widget->state], area, x,
			       y, decortoggle->arrow_down_dark_bmap);
		}
		break;
	    case GTK_ARROW_LEFT:
		if (decortoggle->is_active)
		{
		    draw_part (widget->window,
			       widget->style->light_gc[widget->state], area,
			       x, y, decortoggle->arrow_right_light_bmap);
		    draw_part (widget->window,
			       widget->style->bg_gc[GTK_STATE_SELECTED], area,
			       x, y, decortoggle->arrow_right_mid_bmap);
		    draw_part (widget->window,
			       widget->style->dark_gc[widget->state], area, x,
			       y, decortoggle->arrow_right_dark_bmap);
		}
		else
		{
		    draw_part (widget->window,
			       widget->style->light_gc[widget->state], area,
			       x, y, decortoggle->arrow_left_light_bmap);
		    draw_part (widget->window,
			       widget->style->mid_gc[widget->state], area, x,
			       y, decortoggle->arrow_left_mid_bmap);
		    draw_part (widget->window,
			       widget->style->dark_gc[widget->state], area, x,
			       y, decortoggle->arrow_left_dark_bmap);
		}
		break;
	    case GTK_ARROW_RIGHT:
	    default:
		if (decortoggle->is_active)
		{
		    draw_part (widget->window,
			       widget->style->light_gc[widget->state], area,
			       x, y, decortoggle->arrow_left_light_bmap);
		    draw_part (widget->window,
			       widget->style->bg_gc[GTK_STATE_SELECTED], area,
			       x, y, decortoggle->arrow_left_mid_bmap);
		    draw_part (widget->window,
			       widget->style->dark_gc[widget->state], area, x,
			       y, decortoggle->arrow_left_dark_bmap);
		}
		else
		{
		    draw_part (widget->window,
			       widget->style->light_gc[widget->state], area,
			       x, y, decortoggle->arrow_right_light_bmap);
		    draw_part (widget->window,
			       widget->style->mid_gc[widget->state], area, x,
			       y, decortoggle->arrow_right_mid_bmap);
		    draw_part (widget->window,
			       widget->style->dark_gc[widget->state], area, x,
			       y, decortoggle->arrow_right_dark_bmap);
		}
		break;
	}
    }
}

void
xfce_decortoggle_set_active (XfceDecortoggle * decortoggle,
			     gboolean is_active)
{
    g_return_if_fail (decortoggle != NULL);
    g_return_if_fail (XFCE_IS_DECORTOGGLE (decortoggle));

    decortoggle->is_active = is_active;
    if (GTK_WIDGET_VISIBLE (decortoggle))
    {
	gtk_widget_queue_draw (GTK_WIDGET (decortoggle));
    }
}

void
xfce_decortoggle_set_arrow_type (XfceDecortoggle * decortoggle,
				 GtkArrowType arrow_type)
{
    g_return_if_fail (decortoggle != NULL);
    g_return_if_fail (XFCE_IS_DECORTOGGLE (decortoggle));

    decortoggle->arrow_type = arrow_type;
    if (GTK_WIDGET_VISIBLE (decortoggle))
    {
	gtk_widget_queue_draw (GTK_WIDGET (decortoggle));
    }
}

GtkArrowType
xfce_decortoggle_get_arrow_type (XfceDecortoggle * decortoggle)
{
    g_return_val_if_fail (decortoggle != NULL, GTK_ARROW_LEFT);
    g_return_val_if_fail (XFCE_IS_DECORTOGGLE (decortoggle), GTK_ARROW_LEFT);

    return decortoggle->arrow_type;
}

void
xfce_decortoggle_toggled (XfceDecortoggle * decortoggle)
{
    g_return_if_fail (decortoggle != NULL);
    g_return_if_fail (XFCE_IS_DECORTOGGLE (decortoggle));

    decortoggle->is_active = (decortoggle->is_active ? FALSE : TRUE);
    if (GTK_WIDGET_VISIBLE (decortoggle))
    {
	gtk_widget_queue_draw (GTK_WIDGET (decortoggle));
    }
}
