/*
 * ButtonBox - a program launcher
 *
 * Copyright (C) 2000 Paul Collins
 *
 * 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
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "assertions.h"
#include "button.h"



static char* default_font = "7x13";

static Bool button_window_proc (Dispatcher* dispatcher, XEvent* event, void* data);

static Bool button_globals_initialised = False;

static void initialise_button_globals (Dispatcher*, ButtonGlobals*);

static void spawn_and_disown (char* command);

static ButtonGlobals button_globals;




Button*
button_new (Dispatcher* dispatcher, Window parent, button_info* b)
{
	Display* d = NULL;
	Button* button = NULL;
	XFontStruct* fs = NULL;
	XSetWindowAttributes attributes;

	d = dispatcher_get_display (dispatcher);
	I (d != NULL);
	if (!button_globals_initialised) {
		initialise_button_globals (dispatcher, &button_globals);
		button_globals_initialised = True;
	}
	button = malloc (sizeof (Button));
	I (button != NULL);
	button->globals = &button_globals;
	button->dispatcher = dispatcher;
	button->title = strdup (b->bi_text);
	button->command = strdup (b->bi_command);
	button->font = None;
	button->min_width = 0;
	button->min_height = 0;
	button->foreground_pixel = b->bi_foreground_pixel;
	button->background_pixel = b->bi_background_pixel;
	button->border_pixel = b->bi_border_pixel;

	L ("button_new:\n\tname = %s\n\tcommand = %s\n",
	   button->title, button->command);

	/* Find out our minimum dimensions. */
	fs = XQueryFont (d, XGContextFromGC (button_globals.gc));
	I (fs != NULL);
	button->min_width = XTextWidth (fs, button->title, strlen (button->title));
	L ("button_new: button->min_width = %d\n", button->min_width);
	button->min_height = fs->ascent + fs->descent;
	L ("button_new: button->min_height = %d\n", button->min_height);
	
	XFreeFontInfo (NULL, fs, 0);

	attributes.background_pixel = button->background_pixel;
	attributes.border_pixel = button->border_pixel;
	attributes.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask;
	button->window = XCreateWindow (d, parent,
					0, 0,
					button->min_width + 2,
					button->min_height + 2,
					1,
					CopyFromParent, InputOutput,
					CopyFromParent,
					(CWBackPixel 
					 | CWEventMask
					 | CWBorderPixel),
					&attributes);
	if (button->window != None) {
		L ("create_button_box_window: XCreateWindow: successful\n");
		XMapWindow (d, button->window);
		dispatcher_register (dispatcher, button->window,
				     button_window_proc, (void*)button);
	} else {
		L ("create_button_box_window: XCreateWindow: returned None\n");
	}

	return button;
}



Window
button_get_window (Button* b)
{
	I (b != NULL);
	return b->window;
}



static Bool
button_window_proc (Dispatcher* dispatcher, XEvent* event, void* data)
{
	Button* self = (Button*)data;
	Display* d = NULL;
	XGCValues gcv;
	unsigned long saved_foreground = 0; /* Used to restore the GC. */
	
	d = dispatcher_get_display (dispatcher);
	I (d != NULL);
	I(self->window == event->xany.window);
	switch (event->type) {
	case ButtonRelease:
		if (event->xbutton.button == 1) {
			L("button_window_proc: calling spawn_and_disown with arg '%s'\n",
			  self->command);
			spawn_and_disown (self->command);
		}
		break;
	case Expose:
		if (XGetGCValues (d, self->globals->gc, GCForeground, &gcv)) {
			if (gcv.foreground != self->foreground_pixel) {
				saved_foreground = gcv.foreground;
				gcv.foreground = self->foreground_pixel;
				XChangeGC (d, self->globals->gc, GCForeground, &gcv);
				XDrawString (d, event->xexpose.window, self->globals->gc,
					     1, self->min_height - 1,
					     self->title, strlen (self->title));
				gcv.foreground = saved_foreground;
				XChangeGC (d, self->globals->gc, GCForeground, &gcv);
				
				break;
			}
		}
		XDrawString (d, event->xexpose.window, self->globals->gc,
			     1, self->min_height - 1,
			     self->title, strlen (self->title));
		break;
	default:
		return False;
		break;
	}

	return True;
}



static void
initialise_button_globals (Dispatcher* dispatcher, ButtonGlobals* globals)
{
	Display* d = NULL;
	Font font = None;
	
	d = dispatcher_get_display (dispatcher);
	I (d != NULL);

	font = XLoadFont (d, default_font);
	I(font != None);

	globals->gc = XCreateGC (d, RootWindowOfScreen (DefaultScreenOfDisplay (d)),
				 0, NULL);
	XSetFont (d, globals->gc, font);
}



#ifdef WANT_DRIVER

int
main (int argc, char** argv)
{
	Display* display = NULL;
	Dispatcher* dispatcher = NULL;
	Window main_window = None;
	Button* button = NULL;
	
	display = XOpenDisplay (NULL);
	if (!display) {
		L ("XOpenDisplay: failed.\n");
		exit (EXIT_FAILURE);
	}
	dispatcher = dispatcher_new (display);
	I (dispatcher != NULL);

	button = button_new (dispatcher, RootWindowOfScreen (DefaultScreenOfDisplay (display)),
			     "GFontSel", "gfontsel &");
	
	dispatcher_run_loop (dispatcher);
	
}

#endif


/* Forks and then `system's the given command.  If the fork succeeds,
   the parent reaps any lingering zombies. */
static void
spawn_and_disown (char* command)
{
	pid_t child_pid = 0;
	if ((child_pid = fork ()) == 0) {
		setsid ();
		if ((child_pid = fork ()) == 0) {
			execl ("/bin/sh", "/bin/sh", "-c", command, NULL);
			_exit (EXIT_FAILURE);
		} else {
			_exit (EXIT_SUCCESS);
		}
	}
}




void
button_set_default_font (char* name)
{
	I (name != NULL);
	L ("button_set_default_font: \"%s\"\n");
	default_font = strdup (name);
	I (default_font != NULL);
}
