/* $Id: Scroller.c,v 1.2 1997/04/22 13:22:35 archer Exp $
 *	SCROLLER.C - Handle Scrolling windows objects
 *
 * The ScrollingWindow is a special kind of manager that deals with "large"
 * objects within a viewport
 */
#include <Widget/ScrollerP.h>
#include <Widget/WidgetsP.h>
#include <Widget/drawP.h>


/**
 ** Internal: SCROLLER_CHILD
 **	Who, among the scroller 1-3 children, is the managed child
 **/
static X11_widget scroller_child(x11__scroller *s)
{
X11_widget	child;
/*
 * Scan all children
 */
	for (child = s->core.child; child; child = child->sibling) {
		if (child == s->horizontal_bar)
			continue;
		if (child == s->vertical_bar)
			continue;
		if (child->unmanaged)
			continue;
		break;
	}
/*
 * Child has been found (if one exists)
 */
	s->window = child;
	return child;
}


/**
 ** Internal: SCROLLER_RESIZE
 **	Adjust the scroller to the new size
 **/
static void scroller_resize(X11_widget wdg, XEvent *dummy)
{
X11_widget	managed;
X11_widget	h_bar, v_bar;
unsigned int	w, h;
int		v;
int		x, y;
/*
 * Resize the basic object 1st
 */
	x11__core_resize(wdg, dummy);
	w = wdg->w - wdg->border2;
	h = wdg->h - wdg->border2;
/*
 * Sub-objects
 */
	if ((managed = scroller_child(W_SCROLLER)) == 0)
		return;
	if ((h_bar = W_SCROLLER->horizontal_bar) != 0) {
		x = h_bar->focus_thickness;
		if (h_bar->unmanaged)
			h_bar = 0;
	} else
		x = 0;
	if ((v_bar = W_SCROLLER->vertical_bar) != 0) {
		y = v_bar->focus_thickness;
		if (v_bar->unmanaged)
			v_bar = 0;
	} else
		y = 0;
/*
 * Quick 'n dirty
 */
	if (h_bar)
		h -= h_bar->h_min + wdg->margin_inside;
	if (v_bar)
		w -= v_bar->w_min + wdg->margin_inside;
/*
 * Resize and relocate horizontal bar
 */
	if (h_bar) {
		if (h_bar->w != w || h_bar->h != h_bar->h_min) {
			h_bar->w = w;
			h_bar->h = h_bar->h_min;
			(*h_bar->on_resize_window)(h_bar, dummy);
		}
		v = h + wdg->margin_inside;
		if (h_bar->x != 0 || h_bar->y != v) {
			h_bar->x = 0;
			h_bar->y = v;
			if (h_bar->id)
				XMoveWindow(x11__screen, h_bar->id, 0, v);
		}
	}
/*
 * Resize and relocate vertical bar
 */
	if (v_bar) {
		if (v_bar->w != v_bar->w_min || v_bar->h != h) {
			v_bar->w = v_bar->w_min;
			v_bar->h = h;
			(*v_bar->on_resize_window)(v_bar, dummy);
		}
		v = w + wdg->margin_inside;
		if (v_bar->x != v || v_bar->y != 0) {
			v_bar->x = v;
			v_bar->y = 0;
			if (v_bar->id)
				XMoveWindow(x11__screen, v_bar->id, v, 0);
		}
	}
/*
 * Align the Focus. List might have a focus...
 */
	x -= managed->focus_thickness;
	y -= managed->focus_thickness;
	if (x < 0)
		x = 0;
	if (y < 0)
		y = 0;
	w -= x * 2;
	h -= y * 2;
/*
 * Resize and relocate scrolled item
 */
	if (managed->w != w || managed->h != h) {
		managed->w = w;
		managed->h = h;
		(*managed->on_resize_window)(managed, dummy);
	}
	if (managed->x != x || managed->y != y) {
		managed->x = x;
		managed->y = y;
		if (managed->id)
			XMoveWindow(x11__screen, managed->id, x, y);
	}
}


/**
 ** Internal: SCROLLER_GEOMETRY
 **	Adjust the scroller to the appropriate window
 **/
static void scroller_geometry(X11_widget wdg, XEvent *dummy)
{
X11_widget	managed;
X11_widget	h_bar, v_bar;
unsigned int	w_min, h_min;
unsigned int	w, h;
unsigned int	b;
/*
 * Which window do we manage?
 */
	managed = scroller_child(W_SCROLLER);
	if (!managed)
		return;
/*
 * Hints from our children
 */
	(*managed->on_geometry_check)(managed, dummy);
	wdg->fixed_w = managed->fixed_w;
	wdg->fixed_h = managed->fixed_h;
/*
 * Now, for the geometry
 */
	w_min = managed->w_min;
	h_min = managed->h_min;
	w     = managed->w;
	h     = managed->h;
/*
 * Use bars?
 */
	h_bar = W_SCROLLER->horizontal_bar;
	if (h_bar)
		(*h_bar->on_geometry_check)(h_bar, dummy);
	v_bar = W_SCROLLER->vertical_bar;
	if (v_bar)
		(*v_bar->on_geometry_check)(v_bar, dummy);
/*
 * Take bars in account
 */
	if (h_bar && h_bar->w_min > w_min)
		w_min = h_bar->w_min;
	if (v_bar && v_bar->h_min > h_min)
		h_min = v_bar->h_min;
	if (h_bar) {
		b = wdg->margin_inside + h_bar->h_min;
		h_min += b;
		h     += b;
	}
	if (v_bar) {
		b = wdg->margin_inside + v_bar->w_min;
		w_min += b;
		w     += b;
	}
/*
 * Now, size...
 */
	w_min += wdg->border2;
	h_min += wdg->border2;
	w     += wdg->border2;
	h     += wdg->border2;
	if (w < w_min)
		w = w_min;
	if (h < h_min)
		h = h_min;
	wdg->w_min = w_min;
	wdg->h_min = h_min;
/*
 * Need to resize?
 */
	if (wdg->fixed_w)
		w = wdg->w_min;
	if (wdg->fixed_h)
		h = wdg->h_min;
	if (wdg->w == 0 || wdg->h == 0) {
		wdg->w = w;
		wdg->h = h;
		(*wdg->on_resize_window)(wdg, dummy);
	} else
		x11__widget_grow(wdg);
}


/**
 ** Internal: X11_SCROLLER_GET_BARS
 **	Fetch the two bars from the scroller window
 **/
void X11_ScrollerGetBars(X11_widget wdg, X11_widget *horizontal,
					 X11_widget *vertical)
{
	if (horizontal)
		*horizontal = W_SCROLLER->horizontal_bar;
	if (vertical)
		*vertical   = W_SCROLLER->vertical_bar;
}


/**
 ** X11_SCROLLER
 **	Create a scroller window
 **/
X11_widget X11_Scroller(X11_widget parent, boolean horizontal, boolean vertical)
{
x11__scroller	*object;
/*
 * Get resource
 */
	object = allocator(x11__scroller);
	x11__core_setup(&object->core, parent, "Scroller", "scroller");
/*
 * Initialize fields
 */
	object->core.on_geometry_check = scroller_geometry;
	object->core.on_resize_window  = scroller_resize;
/*
 * Resources
 */
	object->core.margin_inside    = 2;
	x11__resource_basic(&object->core);
/*
 * Scrollbars needed
 */
	if (horizontal) {
		object->horizontal_bar = X11_ScrollbarHorizontal(OBJECT_P);
		object->horizontal_bar->unmanaged = True;
	}
	if (vertical) {
		object->vertical_bar = X11_ScrollbarVertical(OBJECT_P);
		object->vertical_bar->unmanaged = True;
	}
/*
 * Ok
 */
	return OBJECT_P;
}
