/* $Id: Alarm.c,v 1.1 1997/04/15 10:09:23 archer Exp $
 *	ALARM.C - Alarm popup
 *
 * Grab the pointer, then pop up an alarm window. When user selected one of
 * both buttons, destroy the alarm window, and pop down.
 * This allow to implement simple modal dialogs, like "continue" boxes,
 * choice boxes, and system alarms...
 */
#include <errno.h>
#include <Widget/Alarm.h>
#include <Widget/Popup.h>
#include <Widget/Form.h>
#include <Widget/Button.h>
#include <Widget/Separator.h>
#include <Widget/Label.h>
#include <Widget/Manage.h>
#include <Widget/WidgetsP.h>
#include <Widget/EventsP.h>


/**
 ** Private variables
 **/
static X11_widget	dialog;
static X11_widget	cancel_bt, ok_bt, text_lbl, title_lbl;
static int		return_value;
static boolean		grab;


/**
 ** Internal: ALARM_OK
 **	User selected the basic button
 **/
static void alarm_ok(X11_widget wdg, void *dummy)
{
	return_value = 0;
	grab = False;
}


/**
 ** Internal: ALARM_CANCEL
 **	User selected the cancel button
 **/
static void alarm_cancel(X11_widget wdg, void *dummy)
{
	return_value = -1;
	grab = False;
}


/**
 ** Internal: GRABBED_EVENT
 **	Fake the contents of the event, as we're pseudo-grabbed in a specific
 **	widget.
 **/
static void grabbed_event(XEvent *e, X11_widget o, X11_widget d)
{
X11_widget	p;
/*
 * End of recursion
 */
	p = d->parent;
	if (o == d || p == d)
		return;
/*
 * Shift the contents of the event
 */
	e->xbutton.x -= d->x + d->border2/2;
	e->xbutton.y -= d->y + d->border2/2;
	grabbed_event(e, o, p);
}


/**
 ** Internal: FAKE_EVENT
 **	Fake the contents of the event, as we got a false child
 **/
static X11_widget fake_event(XEvent *e, X11_widget o)
{
X11_widget	c;
int		x, y, b;
/*
 * Event in a child?
 */
	x = e->xbutton.x;
	y = e->xbutton.y;
	for (c = o->child; c; c = c->sibling) {
		if (c->unmapped || c->unmanaged)
			continue;
		if (x < c->x || y < c->y)
			continue;
		if (x >= (c->x + c->w) || y >= (c->y + c->h))
			continue;
/*
 * We're inside this child!
 */
		b = c->border2 / 2;
		e->xbutton.x = x - (c->x + b);
		e->xbutton.y = y - (c->y + b);
		e->xbutton.window = c->id;
		return fake_event(e, c);
	}
/*
 * Event is OK inside this window (no child, or outside any child)
 */
	return o;
}


/**
 ** Internal: PROCESS_EVENT
 **	Loop over grabbed events
 **/
static void process_event(void)
{
XEvent		e;
X11_widget	i;
X11_widget	inside;
X11_widget	fake_grab;
/*
 * Loop
 */
	grab      = True;
	inside    = 0;
	fake_grab = 0;
	while (grab) {
/*
 * Get an event
 */
		x11__wait_event(&e);
		switch (e.type) {
/*
 * Release of the button
 */
		    case ButtonPress:
			i = fake_event(&e, dialog);
			if (i && i->on_button_press) {
				fake_grab = i;
				(*i->on_button_press)(i, &e);
			}
			break;
		    case ButtonRelease:
			if ((i = fake_grab) != 0)
				grabbed_event(&e, dialog, fake_grab);
			else
				i = fake_event(&e, dialog);
			if (i && i->on_button_release)
				(*i->on_button_release)(i, &e);
			break;
/*
 * Motion can generate leave events :-)
 */
		    case MotionNotify:
			i = fake_event(&e, dialog);
			if (inside && i != inside) {
				if (inside->on_exit_window)
					(*inside->on_exit_window)(inside, (XEvent *)0);
				inside = 0;
			}
			if (i && i->on_motion_notify) {
				inside = i;
				(*i->on_motion_notify)(i, &e);
			}
			break;
/*
 * Shortcuts?
 */
		    case KeyPress:
			if (dialog->on_key_press)
				(*dialog->on_key_press)(dialog, &e);
		    case KeyRelease:
			break;
/*
 * All other events are dispatched
 */
		    default:
			x11__event_dispatch(&e);
		}
	}
}


/**
 ** X11_ALARM_POPUP
 **	Create a Basic (icon, text, 1/2 buttons) alarm dialog, grab event
 **	loop and wait until done.
 **/
int X11_AlarmPopup(char *title, X11_icon *icon, char *text,
					char *text_1, char *text_2)
{
X11_widget	item;
X11_widget	form;
/*
 * Create the window on screen on first time
 */
	if (!dialog) {
		dialog = X11_Popup("alarm", title);
		form   = X11_Column(dialog, "panel");
/*
 * Title is repeated
 */
		title_lbl = X11_Label(form, "title", title);
		X11_FormMargins(title_lbl, 10, 10);
/*
 * Error message
 */
		text_lbl = X11_Label(form, "message", text);
		X11_LabelSetIcon(text_lbl, icon);
		X11_FormMargins(text_lbl, 10, 10);
		X11_FormAbsoluteX(text_lbl, 10);
/*
 * Separator
 */
		item = X11_Separator(form);
		X11_FormMargins(item, 0, 10);
/*
 * Cancel button
 */
		cancel_bt = X11_Button(form, "cancel", text_2, X11_NO_ICON,
							alarm_cancel, 0);
		if (!text_2)
			X11_UnManage(cancel_bt);
		X11_FormMargins(cancel_bt, 10, 10);
		X11_FormAbsoluteX(cancel_bt, 10);
		X11_FormLabel(cancel_bt);
/*
 * Ok button
 */
		ok_bt = X11_Button(form, "ok", text_1, X11_NO_ICON, alarm_ok,0);
		X11_ButtonDefaults(ok_bt);
		X11_FormMargins(ok_bt, 10, 10);
		X11_FormAbsoluteX(ok_bt, -10);
/*
 * Fill in the values on the second time around
 */
	} else {
		if (text_2) {
			X11_LabelSetString(cancel_bt, text_2);
			X11_Manage(cancel_bt);
		} else
			X11_UnManage(cancel_bt);
		X11_LabelSetString(ok_bt, text_1);
		X11_LabelSetString(text_lbl, text);
		X11_LabelSetIcon(text_lbl, icon);
		X11_LabelSetString(title_lbl, title);
	}
/*
 * Display window
 */
	X11_PopupDisplay(dialog);
/*
 * Grab the pointer!
 */
	XFlush(x11__screen);
	if (XGrabPointer(x11__screen, dialog->id, False,
				(ButtonPressMask|ButtonReleaseMask|
				PointerMotionMask|PointerMotionHintMask),
				GrabModeAsync, GrabModeAsync,
				None, None, CurrentTime) != GrabSuccess)
		fprintf(stderr, "Grab failed...\n");
	XGrabKeyboard(x11__screen, dialog->id, False,
				GrabModeAsync, GrabModeAsync, CurrentTime);
	process_event();
/*
 * Ungrab and exit
 */
	XUngrabKeyboard(x11__screen, CurrentTime);
	XUngrabPointer(x11__screen, CurrentTime);
	X11_UnManage(dialog);
	XFlush(x11__screen);
	return return_value;
}


/**
 ** X11_ALARM_SYS_ERR
 **	Alarm with a system error below
 **/
void X11_AlarmSyserr(char *text, char *item)
{
char	*s;
char	work[2048];
/*
 * Error message
 */
	s = strerror(errno);
/*
 * Which message printed
 */
	if (text)
		if (item)
			snprintf(work, sizeof(work), "%s \"%s\" : %s", text, item, s);
		else
			snprintf(work, sizeof(work), "%s : %s", text, s);
	else {
		strncpy(work, s, sizeof(work));
		work[sizeof(work)-1] = '\0';
	}
/*
 * Print message
 */
	(void)X11_AlarmPopup("System error", &X11_IconError, work, "Ok", 0);
}
