/*
	odscrolledbox.c
	
	1999-05-08:	Created test version.
	1999-05-11:	I think I could settle with this now.
				I have not found a way to set the page size correctly.

	This dirty hack was written by Johan Hanson <johan@tiq.com>.
	Pass bug reports and patches concerning this module to him.

	tabsize in this file is 4, in the header file it is 8.
*/

#include "odscrolledbox.h"
#include "../colorutil.h"
#include <gtk/gtk.h>
#include <math.h>

#ifndef DEBUG
#define DEBUG 0
#endif
#if DEBUG
#include <stdio.h>
#endif

#ifndef BORDERLESS
#define	BORDERLESS 0
#endif

#define BOX(widget)			 (GTK_BOX(GTK_VIEWPORT((GTK_BIN(widget)->child))->child))
#define SCROLLBAR_SPACING(w) (GTK_SCROLLED_WINDOW_CLASS (GTK_OBJECT (w)->klass)->scrollbar_spacing)


static void od_scrolled_box_class_init	  (ODScrolledBoxClass *klass);
static void od_scrolled_box_init		  (ODScrolledBox *box);
static void od_scrolled_box_size_request  (GtkWidget *widget, GtkRequisition *requisition);
static gint od_scrolled_box_range_press	  (GtkRange  *range,  GdkEventButton *event, ODScrolledBox *box);
static gint od_scrolled_box_range_release (GtkRange  *range,  GdkEventButton *event, ODScrolledBox *box);

static GtkScrolledWindowClass *parent_class = NULL;

#if BORDERLESS
static void od_scrolled_box_draw_shadow	  (GtkStyle      *style,
										   GdkWindow     *window,
										   GtkStateType   state_type,
										   GtkShadowType  shadow_type,
										   GdkRectangle  *area,
										   GtkWidget     *widget,
										   gchar         *detail,
										   gint           x,
										   gint           y,
										   gint           width,
										   gint           height);

static GtkStyleClass od_scrolled_box_style_class = { 0 };
static void (* old_draw_shadow) (GtkStyle      *style,
								 GdkWindow     *window,
								 GtkStateType   state_type,
								 GtkShadowType  shadow_type,
								 GdkRectangle  *area,
								 GtkWidget     *widget,
								 gchar         *detail,
								 gint           x,
								 gint           y,
								 gint           width,
								 gint           height ) = NULL;
#endif


/* Class functions */
GtkType od_scrolled_box_get_type (void) {
	static GtkType box_type = 0;
	
	if (!box_type) {
		static const GtkTypeInfo box_info = {
			"ODScrolledBox",
			sizeof (ODScrolledBox),
			sizeof (ODScrolledBoxClass),
			(GtkClassInitFunc) od_scrolled_box_class_init,
			(GtkObjectInitFunc) od_scrolled_box_init,
			/* reserved_1 */ NULL,
			/* reserved_2 */ NULL,
			(GtkClassInitFunc) NULL,
		};
		box_type = gtk_type_unique (GTK_TYPE_SCROLLED_WINDOW, &box_info);
	}
	return box_type;
}

static void od_scrolled_box_class_init (ODScrolledBoxClass *klass) {
	GtkScrolledWindowClass *scwin_class;
	GtkWidgetClass *widget_class;
	GtkObjectClass *object_class;

	parent_class = gtk_type_class(gtk_scrolled_window_get_type());
	
	scwin_class = GTK_SCROLLED_WINDOW_CLASS(klass);
	widget_class = GTK_WIDGET_CLASS(klass);
	object_class = GTK_OBJECT_CLASS(klass);

	widget_class->size_request	= od_scrolled_box_size_request;

	scwin_class->scrollbar_spacing = 1;
}


static void od_scrolled_box_init (ODScrolledBox *box) {
	box->num = 0;		/* means "all widgets" */
}


/* new and destroy */
GtkWidget * od_scrolled_box_new (void) {
	GtkWidget *widget, *viewport, *scrollbar, *frame;
	GtkScrolledWindow *scwin;
  #if BORDERLESS	
	static GtkStyle *style = NULL;
  #endif

	widget = gtk_widget_new (od_scrolled_box_get_type(),
							 "hadjustment", NULL,
							 "vadjustment", NULL,
							 NULL);

	if (!(viewport = GTK_BIN(widget)->child)) {
		viewport = gtk_viewport_new(NULL, NULL);
		gtk_container_add(GTK_CONTAINER(widget), viewport);
	}
/*
	gtk_container_add(GTK_CONTAINER(viewport), gtk_vbox_new (TRUE, 0);
*/
	gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);

	scrollbar = GTK_SCROLLED_WINDOW(widget)->vscrollbar;
	gtk_signal_connect (GTK_OBJECT(scrollbar), "button_release_event",
					   (GtkCallback) od_scrolled_box_range_release, widget);
	gtk_signal_connect (GTK_OBJECT(scrollbar), "button_press_event",
					   (GtkCallback) od_scrolled_box_range_press, widget);

  #if BORDERLESS	
	/* A hack to remove the border around the scrollbar... */
	if (old_draw_shadow == NULL) {
		gtk_widget_ensure_style(scrollbar);
		style = col_style_copy(gtk_widget_get_style(scrollbar));

		od_scrolled_box_style_class = *style->klass;
		old_draw_shadow = style->klass->draw_shadow;
		od_scrolled_box_style_class.draw_shadow = od_scrolled_box_draw_shadow;
		style->klass = &od_scrolled_box_style_class;

		style->klass->xthickness = 0;
		style->klass->ythickness = 0;
	}
	gtk_widget_set_style(scrollbar, style);
	gtk_widget_queue_resize (widget);
  #endif

	return widget;
}

/* Events */
static void od_scrolled_box_size_request (GtkWidget *widget, GtkRequisition *requisition) {
	GtkScrolledWindow	*scrolled_window;
	ODScrolledBox		*scrolled_box;
	GtkWidget			*viewport, *ct, *ch;
	GList				*cur;
	guint				i, n, sum;
	
	g_return_if_fail (widget != NULL);
	g_return_if_fail (OD_IS_SCROLLED_BOX (widget));
	g_return_if_fail (requisition != NULL);
	
	scrolled_window = GTK_SCROLLED_WINDOW (widget);
	scrolled_box	= OD_SCROLLED_BOX (widget);
	viewport		= GTK_BIN(widget)->child;
	
	((GtkWidgetClass *)parent_class)->size_request (widget, requisition);
	
	if (   scrolled_box->num
		&& viewport
		&& GTK_IS_VIEWPORT(viewport)
		&& GTK_WIDGET_VISIBLE(viewport)
		&& (ct = GTK_WIDGET(GTK_BIN(viewport)->child))
		&& GTK_WIDGET_VISIBLE(ct))
	{
		sum = 0;
		n = scrolled_box->num;
		
		if (GTK_IS_VBOX(ct)) {
			cur = GTK_BOX(ct)->children;
			for (i=0; i<n && cur; i++) {
				ch = ((GtkBoxChild *)(cur->data))->widget;
				if (ch && GTK_IS_WIDGET(ch)) {
					if (GTK_WIDGET_VISIBLE(ch)) {
						sum += ch->requisition.height;
					  #if DEBUG
						printf("sum = %d\n", sum);
					  #endif
					} else {
						n++;
					}
				}
				cur = cur->next;
			}
		} else if (GTK_IS_TABLE(ct)) {
			n = MIN(n, ((GtkTable *)ct)->nrows);
			if (n==0)
				return;
			
			for (i=0; i<n; i++) {
				sum += ((GtkTable *)ct)->rows[i].requisition;
			}
			sum += (n-1) * ((GtkTable *)ct)->row_spacing;
		} else {
			return;
		}
		
		if (sum > 0)
			requisition->height = sum + GTK_CONTAINER(widget)->border_width * 2;
	}
}


static gint od_scrolled_box_range_press (GtkRange *range, GdkEventButton *event, ODScrolledBox *box) {
	g_return_val_if_fail (range != NULL, FALSE);
	g_return_val_if_fail (event != NULL, FALSE);
	g_return_val_if_fail (box != NULL, FALSE);
	g_return_val_if_fail (GTK_IS_VSCROLLBAR(range), FALSE);
	g_return_val_if_fail (OD_IS_SCROLLED_BOX(box), FALSE);

	switch (event->button) {
	  case 2:
	  case 3:
		gtk_signal_emit_stop_by_name(GTK_OBJECT(range), "button_press_event");
		return TRUE;

	  default:
		return FALSE;
	}
	
	return TRUE;
}

static gint od_scrolled_box_range_release (GtkRange *range, GdkEventButton *event, ODScrolledBox *box) {
	GtkAdjustment *adjustment;
	gfloat val, mod;
	
	g_return_val_if_fail (range != NULL, FALSE);
	g_return_val_if_fail (event != NULL, FALSE);
	g_return_val_if_fail (box != NULL, FALSE);
	g_return_val_if_fail (GTK_IS_VSCROLLBAR(range), FALSE);
	g_return_val_if_fail (OD_IS_SCROLLED_BOX(box), FALSE);

	if (event->button != 2 && event->button != 3)
		return FALSE;

	adjustment = gtk_range_get_adjustment (range);
	val = adjustment->value;

	if (event->button == 3) {
		if (val >= adjustment->upper - adjustment->page_size) {
			val = adjustment->lower;
		} else {
			val = val - fmod(val - adjustment->lower, adjustment->page_size)
				+ adjustment->page_size;
			if (val > adjustment->upper - adjustment->page_size)
				val = adjustment->upper - adjustment->page_size;
		}
	} else {
		mod = fmod(val - adjustment->lower, adjustment->page_size);
		val -= mod;
		if (mod < 1.0e-4)
			val -= adjustment->page_size;

		if (val < adjustment->lower)
			val = adjustment->upper - adjustment->page_size;
	}
	
	gtk_adjustment_set_value (adjustment, val);
	gtk_signal_emit_stop_by_name(GTK_OBJECT(range), "button_release_event");
	return TRUE;
}


/* Interface */
void od_scrolled_box_set_num (ODScrolledBox	*box, guint numwidgets) {
	guint num;
	
	g_return_if_fail (box != NULL);
	g_return_if_fail (OD_IS_SCROLLED_BOX (box));

	num = OD_SCROLLED_BOX(box)->num;
	box->num = numwidgets;

	if (num != numwidgets && GTK_WIDGET (box)->parent)
		gtk_widget_queue_resize (GTK_WIDGET(box));
}


#if BORDERLESS
/* Style */
static void od_scrolled_box_draw_shadow ( GtkStyle      *style,
										  GdkWindow     *window,
										  GtkStateType   state_type,
										  GtkShadowType  shadow_type,
										  GdkRectangle  *area,
										  GtkWidget     *widget,
										  gchar         *detail,
										  gint           x,
										  gint           y,
										  gint           width,
										  gint           height)
{
	if (GTK_IS_VSCROLLBAR (widget)
		&& widget->parent
		&& OD_IS_SCROLLED_BOX(widget->parent)
		&& shadow_type == GTK_SHADOW_IN
		&& !(   GTK_RANGE(widget)->in_child == GTK_RANGE_CLASS (GTK_OBJECT (widget)->klass)->step_forw
             || GTK_RANGE(widget)->in_child == GTK_RANGE_CLASS (GTK_OBJECT (widget)->klass)->step_back))
	{
		old_draw_shadow (style, window, state_type, GTK_SHADOW_NONE, area, widget, detail,
						 x, y, width, height);
	} else {
		old_draw_shadow (style, window, state_type, shadow_type, area, widget, detail,
						 x, y, width, height);
	}
}
#endif
