/* $Id: Arrow.c,v 1.3 1997/06/16 20:50:35 archer Exp $
 *	ARROW.C - Handle buttons with directional arrows inside
 *
 */
#include <Widget/ArrowP.h>
#include <Widget/IconP.h>
#include <Widget/drawP.h>


/**
 ** Arrow fill GC
 **/
static GC	arrow_gc;


/**
 ** X11_DRAW_ARROW
 **	Draw an arrow
 **/
void x11__draw_arrow(int direction, GC fill, GC top, GC bottom)
{
Display	*screen;
Window	win;
XPoint	pts[8];
int	x1, y1, x2, y2, x3, y3, x4, y4;
int	midx, midy;
int	half, size;
int	thick;
/*
 * Centered on
 */
	midx = x11__x_left + (x11__x_w / 2);
	midy = x11__y_top + (x11__y_h / 2);
	if ((size = x11__x_w) > x11__y_h)
		size = x11__y_h;
	if (size < 6)
		size = 6;
	half = size / 2;
	if ((thick = x11__draw_in->shadow_thickness) > 2)
		thick = 2;
	screen = x11__screen;
	win    = x11__cur_id;
/*
 * Create the various points
 */
	switch (direction) {
	    case X11_ARROW_UP:
		x1 = midx - half - 1;
		y1 = midy - half + size;
		if (size & 1) {
			x2 = midx;
			y2 = midy - half - 1;
			y3 = midy - half;
		} else {
			x2 = midx - 1;
			y2 = midy - half;
			y3 = midy - half - 1;
		}
		x3 = x2 + 1;
		x4 = x3 + half;
		y4 = midy - half + size;
		break;
	    case X11_ARROW_DOWN:
		x1 = midx - half - 1;
		y1 = midy - half;
		if (size & 1) {
			x2 = midx;
			y2 = midx - half + size;
			y3 = midx - half + size - 1;
		} else {
			x2 = midx - 1;
			y2 = midx - half + size - 1;
			y3 = midx - half + size;
		}
		x3 = x2 + 1;
		x4 = x3 + half;
		y4 = midy - half;
		break;
	    case X11_ARROW_RIGHT:
		x1 = midx - half;
		y1 = midy - half - 1;
		if (size & 1) {
			x2 = midx - half + size;
			y2 = midy;
			x3 = midx - half + size - 1;
		} else {
			x2 = midx - half + size;
			y2 = midy - 1;
			x3 = midx - half + size;
		}
		y3 = y2 + 1;
		x4 = midx - half;
		y4 = midy - half + size;
		break;
	    case X11_ARROW_LEFT:
		x1 = midx - half + size;
		y1 = midy - half - 1;
		if (size & 1) {
			x2 = midx - half - 1;
			y2 = midy;
			x3 = midx - half + 1;
		} else {
			x2 = midx - half;
			y2 = midy - 1;
			x3 = midx - half;
		}
		y3 = y2 + 1;
		x4 = midx - half + size;
		y4 = midy - half + size;
		break;
	}
/*
 * Fill polygon?
 */
	if (fill) {
		pts[0].x = (short)x1;
		pts[0].y = (short)y1;
		pts[1].x = (short)x2;
		pts[1].y = (short)y2;
		pts[2].x = (short)x3;
		pts[2].y = (short)y3;
		pts[3].x = (short)x4;
		pts[3].y = (short)y4;
		XFillPolygon(screen, x11__cur_id, fill,
					pts, 4, CoordModeOrigin, Complex);
	}
	if (thick == 0)
		return;
/*
 * Draw border
 */
	switch (direction) {
	    case X11_ARROW_UP:
		XDrawLine(screen, win, bottom, x4,y4-1,x1-1,y1-1);
		if (thick == 2)
			XDrawLine(screen, win, bottom, x4,y4-2,x1,y1-2);
		XDrawLine(screen, win, bottom, x3-1, y3,  x4,y4+1);
		if (thick == 2)
			XDrawLine(screen, win, bottom, x3, y3+3,  x4-1,y4);
		XDrawLine(screen, win, top, x1+1,y1-1,x2+1,y2-1);
		XDrawLine(screen, win, top, x2+1,y2,x3+1,y3);
		if (thick == 2)
			XDrawLine(screen, win, top, x1+3,y1-3,x2,y2+2);
		break;
	    case X11_ARROW_DOWN:
		XDrawLine(screen, win, top, x1, y1-2, x2+1, y2+1);
		if (thick == 2)
			XDrawLine(screen, win, top, x1+2, y1, x2, y2-2);
		if (size & 1)
			XDrawLine(screen, win, bottom, x2-1,y2+1,x3,y3-2);
		else
			XDrawLine(screen, win, bottom, x2,y2,x3+1,y3);
		XDrawLine(screen, win, bottom, x3,y3-2,x4,y4-2);
		if (thick == 2)
			XDrawLine(screen, win, bottom, x3-1,y3-1,x4-2,y4+1);
		XDrawLine(screen, win, top, x4,y4,x1,y1);
		if (thick == 2)
			XDrawLine(screen, win, top, x4-2,y4+1,x1+1,y1+1);
		break;
	    case X11_ARROW_RIGHT:
		if (size & 1)
			XDrawLine(screen, win, top, x1-2,y1,x2-1,y2);
		else
			XDrawLine(screen, win, top, x1,y1+1,x2,y2+1);
		XDrawLine(screen, win, bottom, x2,y2-1,x3,y3-1);
		XDrawLine(screen, win, bottom, x4,y4-1, x3,y3-1);
		XDrawLine(screen, win, top, x4,y4,x1,y1);
		break;
	    case X11_ARROW_LEFT:
		XDrawLine(screen, win, bottom, x3-1,y3-1,x4-1,y4-1);
		XDrawLine(screen, win, top, x2+1,y2,x1+1,y1);
		XDrawLine(screen, win, top, x2, y2, x3, y3-1);
		XDrawLine(screen, win, bottom, x1-1,y1+2,x4-1,y4);
		break;
	}
}


/**
 ** Internal: EXPOSE
 **	Expose an arrow button
 **/
static void arrow_expose(X11_widget wdg, XEvent *dummy)
{
/*
 * Start drawing
 */
	x11__draw_start(wdg);
	x11__shadows_gc();
	if (!arrow_gc)
		arrow_gc = x11__widget_gc(wdg);
/*
 * Draw one way or the other?
 */
	if (wdg->is_selected) {
		XSetForeground(x11__screen, arrow_gc, wdg->active_bk);
		x11__draw_arrow(W_ARROW->direction, arrow_gc,
					x11__bottomright_gc, x11__topleft_gc);
	} else {
		XSetForeground(x11__screen, x11__back_gc, wdg->background);
		x11__draw_arrow(W_ARROW->direction, x11__back_gc,
					x11__topleft_gc, x11__bottomright_gc);
	}
}


/**
 ** Internal: ARROW_TIMER
 **	User keeps pressing on the arrow
 **/
static void arrow_timer(X11_widget wdg, void *dummy)
{
/*
 * Re-call the callback if still inside widget
 */
	if (wdg->is_selected && W_ARROW->callback)
		(*W_ARROW->callback)(wdg, W_ARROW->user_data);
}


/**
 ** Internal: ARROW_RELEASE
 **	Release the arrow
 **/
static void arrow_release(X11_widget wdg, XEvent *e)
{
/*
 * Cancel timer, then handle the event
 */
	X11_SignalCancel(&W_ARROW->timer);
	x11__core_release(wdg, e);
}


/**
 ** Internal: ARROW_PRESS
 **	Push the arrow
 **/
static void arrow_press(X11_widget wdg, XEvent *e)
{
/*
 * Handle the event
 */
	x11__core_press(wdg, e);
/*
 * Call back now
 */
	if (W_ARROW->callback)
		(*W_ARROW->callback)(wdg, W_ARROW->user_data);
/*
 * Start timer
 */
	X11_SignalArm(&W_ARROW->timer);
}


/**
 ** X11_ARROW
 **	Create an Arrow window
 **/
X11_widget X11_Arrow(X11_widget parent, int direction,
				X11_callback call, void *data)
{
char		*name[] = { "down", "right", "up", "left" };
x11__arrow	*object;
unsigned int	min;
/*
 * Get resource
 */
	direction &= 3;
	object = allocator(x11__arrow);
	x11__core_setup(&object->core, parent, "Arrow", name[direction]);
/*
 * Initialize fields
 */
	object->core.event_mask        = ButtonPressMask|ButtonReleaseMask|
					PointerMotionMask|PointerMotionHintMask|
					ExposureMask;
	object->core.on_button_press   = arrow_press;
	object->core.on_button_release = arrow_release;
	object->core.on_enter_window   = x11__core_enter;
	object->core.on_exit_window    = x11__core_exit;
	object->core.on_expose         = arrow_expose;
	object->core.on_motion_notify  = x11__core_motion;
/*
 * Default values depend on the style
 */
	object->core.shadow_thickness = 2;
	object->core.is_active        = True;
/*
 * Properties as specified by resources
 */
	x11__resource_basic(&object->core);
/*
 * Compute values based on resources field
 */
	min  = 7;
	min += (object->core.shadow_thickness + object->core.margin_inside) * 2;
	object->core.w_min = object->core.w =
	object->core.h_min = object->core.h = min + object->core.border2;
/*
 * Fixed values
 */
	object->core.fixed_w = True;
	object->core.fixed_h = True;
	object->timer.every.tv_sec  = 0;
	object->timer.every.tv_usec = 100000;
	object->timer.on_timer      = arrow_timer;
	object->timer.wdg           = &object->core;
/*
 * Finished
 */
	object->direction = direction;
	object->callback  = call;
	object->user_data = data;
	return OBJECT_P;
}
