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

/* 
 * Author : 
 *  Damon Chaplin <damon@helixcode.com>
 *
 * Copyright 1999, Helix Code, Inc.
 *
 * 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
 */

/*
 * EIconBarBgItem - A GnomeCanvasItem which covers the entire EIconBar.
 * It paints the rectangles around items when the mouse moves over them, and
 * the lines between items when dragging.
 */

#include <config.h>
#include "e-icon-bar-bg-item.h"
#include "e-icon-bar.h"

/* This is the size of the border around the icons, for the shadow. */
#define E_ICON_BAR_LARGE_ICON_SHADOW_BORDER	2
#define E_ICON_BAR_SMALL_ICON_SHADOW_BORDER	2

/* These are for the horzontal bar when dragging. */
#define	E_ICON_BAR_BG_ITEM_BAR_HEIGHT		1
#define	E_ICON_BAR_BG_ITEM_BAR_OFFSET		2
#define E_ICON_BAR_BG_ITEM_LARGE_ARROW_HEIGHT	8
#define E_ICON_BAR_BG_ITEM_SMALL_ARROW_HEIGHT	4

static void e_icon_bar_bg_item_class_init (EIconBarBgItemClass *class);
static void e_icon_bar_bg_item_init (EIconBarBgItem *ibitem);

static void e_icon_bar_bg_item_set_arg (GtkObject *o, GtkArg *arg,
					guint arg_id);
static void e_icon_bar_bg_item_update (GnomeCanvasItem *item,
				       double *affine,
				       ArtSVP *clip_path, int flags);
static void e_icon_bar_bg_item_draw (GnomeCanvasItem *item,
				     GdkDrawable *drawable,
				     int x, int y,
				     int width, int height);
static double e_icon_bar_bg_item_point (GnomeCanvasItem *item,
					double x, double y,
					int cx, int cy,
					GnomeCanvasItem **actual_item);
static gint e_icon_bar_bg_item_event (GnomeCanvasItem *item,
				      GdkEvent *event);
static gint e_icon_bar_bg_item_button_press (EIconBarBgItem *ibitem,
					     GdkEvent *event);
static gint e_icon_bar_bg_item_button_release (EIconBarBgItem *ibitem,
					       GdkEvent *event);
static gint e_icon_bar_bg_item_motion_notify (EIconBarBgItem *ibitem,
					      GdkEvent *event);

static GnomeCanvasItemClass *parent_class;

/* The arguments we take */
enum {
	ARG_0,
	ARG_ICON_BAR
};


GtkType
e_icon_bar_bg_item_get_type (void)
{
	static GtkType e_icon_bar_bg_item_type = 0;

	if (!e_icon_bar_bg_item_type) {
		GtkTypeInfo e_icon_bar_bg_item_info = {
			"EIconBarBgItem",
			sizeof (EIconBarBgItem),
			sizeof (EIconBarBgItemClass),
			(GtkClassInitFunc) e_icon_bar_bg_item_class_init,
			(GtkObjectInitFunc) e_icon_bar_bg_item_init,
			NULL, /* reserved_1 */
			NULL, /* reserved_2 */
			(GtkClassInitFunc) NULL
		};

		e_icon_bar_bg_item_type = gtk_type_unique (gnome_canvas_item_get_type (), &e_icon_bar_bg_item_info);
	}

	return e_icon_bar_bg_item_type;
}


static void
e_icon_bar_bg_item_class_init (EIconBarBgItemClass *class)
{
	GtkObjectClass  *object_class;
	GnomeCanvasItemClass *item_class;

	parent_class = gtk_type_class (gnome_canvas_item_get_type());

	object_class = (GtkObjectClass *) class;
	item_class = (GnomeCanvasItemClass *) class;

	gtk_object_add_arg_type ("EIconBarBgItem::icon_bar",
				 GTK_TYPE_POINTER, GTK_ARG_WRITABLE,
				 ARG_ICON_BAR);

	object_class->set_arg = e_icon_bar_bg_item_set_arg;

	/* GnomeCanvasItem method overrides */
	item_class->update      = e_icon_bar_bg_item_update;
	item_class->draw        = e_icon_bar_bg_item_draw;
	item_class->point       = e_icon_bar_bg_item_point;
	item_class->event       = e_icon_bar_bg_item_event;
}


static void
e_icon_bar_bg_item_init (EIconBarBgItem *ibitem)
{
	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (ibitem);
	
	ibitem->icon_bar = NULL;

	item->x1 = 0;
	item->y1 = 0;
	item->x2 = 0;
	item->y2 = 0;
}


static void
e_icon_bar_bg_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
{
	GnomeCanvasItem *item;
	EIconBarBgItem *ibitem;

	item = GNOME_CANVAS_ITEM (o);
	ibitem = E_ICON_BAR_BG_ITEM (o);
	
	switch (arg_id){
	case ARG_ICON_BAR:
		ibitem->icon_bar = GTK_VALUE_POINTER (*arg);
		break;
	}
}


static void
e_icon_bar_bg_item_update (GnomeCanvasItem *item,
			   double *affine,
			   ArtSVP *clip_path,
			   int flags)
{
	if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
		(* GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, clip_path, flags);

	/* The grid covers the entire canvas area. */
	item->x1 = 0;
	item->y1 = 0;
	item->x2 = INT_MAX;
	item->y2 = INT_MAX;
}


/*
 * DRAWING ROUTINES - functions to paint the canvas item.
 */

static void
e_icon_bar_bg_item_draw (GnomeCanvasItem *canvas_item, GdkDrawable *drawable,
			 int x, int y, int width, int height)
{
	EIconBar *icon_bar;
	EIconBarItem *item;
	EIconBarBgItem *ibitem;
	GtkStyle *style;
	GdkGC *gc;
	GtkShadowType shadow;
	gint item_num, border, bar_x, bar_y, bar_w, i, arrow_height;

	ibitem = E_ICON_BAR_BG_ITEM (canvas_item);
	icon_bar = ibitem->icon_bar;
	g_return_if_fail (icon_bar != NULL);
	style = GTK_WIDGET (icon_bar)->style;
	shadow = GTK_SHADOW_NONE;

	/* Draw the highlight around the current highlight item. */
	item_num = -1;
	if (icon_bar->editing_item_num == -1) {
		if (icon_bar->pressed_item_num != -1) {
			item_num = icon_bar->pressed_item_num;
			if (icon_bar->pressed_item_num == icon_bar->mouse_over_item_num)
				shadow = GTK_SHADOW_IN;
			else
				shadow = GTK_SHADOW_OUT;
		} else if (icon_bar->mouse_over_item_num != -1) {
			item_num = icon_bar->mouse_over_item_num;
			shadow = GTK_SHADOW_OUT;
		}
	}

	if (item_num != -1) {
		item = &g_array_index (icon_bar->items, EIconBarItem,
				       item_num);

		if (icon_bar->view_type == E_ICON_BAR_LARGE_ICONS)
			border = E_ICON_BAR_LARGE_ICON_SHADOW_BORDER;
		else
			border = E_ICON_BAR_SMALL_ICON_SHADOW_BORDER;

		gtk_draw_shadow (style, drawable, GTK_STATE_NORMAL, shadow,
				 icon_bar->icon_x - border - x,
				 item->icon_y - border - y,
				 icon_bar->icon_w + border * 2 - 1,
				 icon_bar->icon_h + border * 2 - 1);
	}

	/* Draw the bar between items when dragging, if needed. */
	if (icon_bar->in_drag && icon_bar->dragging_before_item_num != -1) {
		if (icon_bar->dragging_before_item_num < icon_bar->items->len) {
			item = &g_array_index (icon_bar->items, EIconBarItem,
					       icon_bar->dragging_before_item_num);
			bar_y = 0;
		} else {
			/* We need to draw the bar after the last item. */
			item = &g_array_index (icon_bar->items, EIconBarItem,
					       icon_bar->items->len - 1);
			bar_y = item->item_height + icon_bar->spacing;
		}

		if (icon_bar->view_type == E_ICON_BAR_LARGE_ICONS) {
			bar_y += item->icon_y;
		} else {
			bar_y += MIN (item->icon_y, item->text_y);
		}
		bar_y -= y + icon_bar->spacing / 2;

		bar_x = E_ICON_BAR_BG_ITEM_BAR_OFFSET - x;
		bar_w = GTK_WIDGET (icon_bar)->allocation.width - 2 * E_ICON_BAR_BG_ITEM_BAR_OFFSET - 1;

		gc = GTK_WIDGET (icon_bar)->style->fg_gc[GTK_STATE_NORMAL];

		/* Draw the horizontal bar. */
		gdk_draw_rectangle (drawable, gc, TRUE,
				    bar_x, bar_y,
				    bar_w, E_ICON_BAR_BG_ITEM_BAR_HEIGHT);

		if (icon_bar->view_type == E_ICON_BAR_LARGE_ICONS)
			arrow_height = E_ICON_BAR_BG_ITEM_LARGE_ARROW_HEIGHT / 2;
		else
			arrow_height = E_ICON_BAR_BG_ITEM_SMALL_ARROW_HEIGHT / 2;

		/* Draw the arrows at the end of the lines. We use
		   gdk_draw_line() to draw a series of vertical lines, since
		   gdk_draw_polygon() produces odd results. */
		i = 0;
		while (arrow_height > 0) {
			gdk_draw_line (drawable, gc,
				       bar_x + i,
				       bar_y - arrow_height,
				       bar_x + i,
				       bar_y + arrow_height);
			gdk_draw_line (drawable, gc,
				       bar_x + bar_w - i - 1,
				       bar_y - arrow_height,
				       bar_x + bar_w - i - 1,
				       bar_y + arrow_height);
			arrow_height--;
			i++;
		}
	}
}


/* This is supposed to return the nearest item the the point and the distance.
   Since we are the only item we just return ourself and 0 for the distance.
   This is needed so that we get button/motion events. */
static double
e_icon_bar_bg_item_point (GnomeCanvasItem *item, double x, double y,
			  int cx, int cy,
			  GnomeCanvasItem **actual_item)
{
	*actual_item = item;
	return 0.0;
}


static gint
e_icon_bar_bg_item_event (GnomeCanvasItem *item, GdkEvent *event)
{
	EIconBarBgItem *ibitem;

	ibitem = E_ICON_BAR_BG_ITEM (item);

	switch (event->type) {
	case GDK_BUTTON_PRESS:
		return e_icon_bar_bg_item_button_press (ibitem, event);
	case GDK_BUTTON_RELEASE:
		return e_icon_bar_bg_item_button_release (ibitem, event);
	case GDK_MOTION_NOTIFY:
		return e_icon_bar_bg_item_motion_notify (ibitem, event);
	default:
		break;
	}

	return FALSE;
}


static gint
e_icon_bar_bg_item_button_press (EIconBarBgItem *ibitem,
				 GdkEvent *event)
{
	gint item_num;

	if (event->button.button == 4 || event->button.button == 5)
		return FALSE;

	item_num = e_icon_bar_find_item_at_position (ibitem->icon_bar,
						     event->button.x,
						     event->button.y,
						     NULL);
	e_icon_bar_item_pressed (ibitem->icon_bar, item_num, event);
	return TRUE;
}


static gint
e_icon_bar_bg_item_button_release (EIconBarBgItem *ibitem,
				   GdkEvent *event)
{
	gint item_num;

	item_num = e_icon_bar_find_item_at_position (ibitem->icon_bar,
						     event->button.x,
						     event->button.y,
						     NULL);
	e_icon_bar_item_released (ibitem->icon_bar, item_num, event);
	return TRUE;
}


static gint
e_icon_bar_bg_item_motion_notify (EIconBarBgItem *ibitem,
				  GdkEvent *event)
{
	gint item_num;

	item_num = e_icon_bar_find_item_at_position (ibitem->icon_bar,
						     event->motion.x,
						     event->motion.y,
						     NULL);
	e_icon_bar_item_motion (ibitem->icon_bar, item_num, event);
	return TRUE;
}