/*
 * GTK ASTerisk MANager
 * Copyright (C) 2002, Digium
 *
 * Written by Mark Spencer <markster@digium.com>
 *
 * All Rights Reserved
 *
 * Distributed under GNU GPL
 *
 * GTK specific GUI routine implementation
 */

#ifndef _WIN32
#else
#include <winsock.h>
#include "win32dep.h"
#define _WINSOCKAPI_
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <gtk/gtk.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <db.h>
#include <math.h>
#include <time.h>
#include "gtklink.h"
#include "gastman.h"
#include "art/phone.xpm"
#include "art/phone2.xpm"
#include "art/red_on.xpm"
#include "art/red_off.xpm"
#include "art/green_on.xpm"
#include "art/green_off.xpm"
#include "art/inkwell.xpm"
#include "art/tinyphone.xpm"
#ifdef NO_PIXBUF
#include "art/tinycaller.xpm"
#else
#include <gdk-pixbuf/gdk-pixbuf.h>
#endif

#ifndef _WIN32
#define ICON_PATH "/usr/share/gastman/icons"
#else
#define ICON_PATH_SIZE 256
static char icon_path[ICON_PATH_SIZE];
#endif

static GtkWidget *window;

static GtkWidget *dialog;

static GtkWidget *canvas;

static GtkWidget *callmenu;
static GtkWidget *calltitle;

static GtkWidget *extenmenu;
static GtkWidget *extentitle;
static GtkWidget *tabs;

static GtkWidget *extenlist;
static GtkWidget *calllist;
static GtkWidget *qlist;
static GtkWidget *cmdtextbox;
static GtkWidget *cmdentry;
static GdkFont *clifont;


static GtkTooltips *tips;

#define CVAL(x) ((x * (65532/256)))

static GdkColor colors[] = {
	{red:  CVAL(190), green: CVAL(196), blue: CVAL(209) },	/* Gray */
	{red:  CVAL(255), green: CVAL(253), blue: CVAL(219) },	/* Yellow */
	{red:  CVAL(229), green: CVAL(252), blue: CVAL(201) },	/* Green */
	{red:  CVAL(251), green: CVAL(181), blue: CVAL(255) },	/* Magenta */
};

static int choice = -1;
static int remember = 0;
static int fdio = -1;
static int fdfd = -1;
static int usegreen = 1;

static GtkWidget *ew;	/* Button for the "Don't ask again" */

static char *yesno[2] = { "Yes", "No" };
static char *ok[1] = { "OK" };
static char *okcancel[2] = { "OK", "Cancel" };

static GtkTargetEntry target_entry = { "internal" };

static int format = 0;

static char *formats[] = 
{
	"tiny",
	"small",
	"medium",
	"big",
};

struct gui_object {
	int type;
	char name[80];
	char icon[80];
	char ident[80];
	char text[256];
	GtkWidget *widget;
	GtkWidget *pixmap;
	GtkWidget *label;
	GtkWidget *labelw;
	GdkPixmap *pm;
	GdkBitmap *bm;
	GtkWidget *led;
	void *pvt;
	int size;
	int onoff;
	struct gui_object *next;
} *objects = NULL;

struct gui_queue {
	int type;
	char name[80];
	char queue[80];
	char icon[80];
	char ident[80];
	char text[256];
	time_t offset;
	time_t base;
	int position;
	GtkWidget *pixmap;
	GdkPixmap *pm;
	GdkBitmap *bm;
	void *pvt;
	int size;
	int onoff;
	struct gui_queue *next;
} *queues = NULL;

struct gui_link {
	struct gui_object *obj1;
	struct gui_object *obj2;
	GtkWidget *widget;
	struct gui_link *next;
} *links = NULL;

/* In some flavours of Windows we can not reliably get a home dir */
char* gui_get_home_dir(void)
{
	/* g_get_home_dir works for Win NT,2000 and *nix */ 
	if(g_get_home_dir())
		return (char*)g_get_home_dir();
	else
#ifndef _WIN32
		return "/etc";
#else
 	        return "C:";
#endif
}

/* WIN32 - icons should be in install dir */
char* gui_get_icon_path(void)
{
#ifdef _WIN32
	_getcwd(icon_path, ICON_PATH_SIZE);
	if((strlen(icon_path) + strlen(G_DIR_SEPARATOR_S "icons")) < ICON_PATH_SIZE) {
		strcat(icon_path, G_DIR_SEPARATOR_S "icons");
		return icon_path;
	}
	else
		return NULL;
#else
	return ICON_PATH;	
#endif
}

static void gui_minirun(void)
{
	while(g_main_iteration(FALSE));
}

static void fix_icon(GdkWindow *w)
{
	GdkPixmap *gdk_pixmap;
	GdkBitmap *gdk_pixmap_mask;
	gdk_pixmap = gdk_pixmap_create_from_xpm_d(w, &gdk_pixmap_mask, NULL, tinyphone_xpm);
	gdk_window_set_icon(w, NULL, gdk_pixmap, gdk_pixmap_mask);
}

static int make_pixmap_from_object(struct gui_object *object)
{
	char fn[256];
	snprintf(fn, sizeof(fn), "%s" G_DIR_SEPARATOR_S "%s-%s.xpm", gui_get_icon_path(), object->icon, formats[format + object->size]);
	object->pm = gdk_pixmap_create_from_xpm(window->window, &object->bm, NULL, fn);
	object->pixmap = gtk_pixmap_new(object->pm, object->bm);
	gdk_pixmap_unref(object->pm);
	gdk_bitmap_unref(object->bm);
	return 0;
}

static GtkWidget *make_pixmap_from_file(GtkWidget *window, const char *icon, int size)
{
	char fn[256];
	GdkPixmap *pm;
	GdkBitmap *bm;
	GtkWidget *pixmap;
	snprintf(fn, sizeof(fn), "%s" G_DIR_SEPARATOR_S "%s-%s.xpm", gui_get_icon_path(), icon, formats[size]);
	pm = gdk_pixmap_create_from_xpm(window->window, &bm, NULL, fn);
	pixmap = gtk_pixmap_new(pm, bm);
	gdk_pixmap_unref(pm);
	gdk_bitmap_unref(bm);
	return pixmap;
}

static GtkWidget *make_pixmap_from_data(GtkWidget *window, char **data)
{
	GdkPixmap *pm;
	GdkBitmap *bm;
	GtkWidget *w;
	pm = gdk_pixmap_create_from_xpm_d(window->window, &bm, NULL, data);
	w = gtk_pixmap_new(pm, bm);
	gdk_pixmap_unref(pm);
	gdk_bitmap_unref(bm);
	gtk_widget_show(w);
	return w;
}

static struct gui_object *current = NULL;
static struct gui_queue *qcurrent = NULL;
static int xof, yof, origx, origy;
static DB *db;

int gui_object_location(struct gui_object *obj, int *x, int *y)
{
	if (obj && obj->widget) {
		*x = obj->widget->allocation.x;
		*y = obj->widget->allocation.y;
		return 0;
	} else
		return -1;
}

static char *loc_file(void)
{
	static char fn[256];
	snprintf(fn, sizeof(fn), "%s" G_DIR_SEPARATOR_S ".gastman", gui_get_home_dir());
#ifndef _WIN32
	mkdir(fn, 0777);
#else
	_mkdir(fn);
#endif
	if (gui_get_home_dir()) {
		snprintf(fn, sizeof(fn), "%s" G_DIR_SEPARATOR_S ".gastman" 
			 G_DIR_SEPARATOR_S "locations", gui_get_home_dir());
		return fn;
	}
	return NULL;
}


static void resize_canvas(void)
{
	int maxx=0, maxy=0, x, y;
	struct gui_object *cur;
	cur = objects;
	while(cur) {
		x = cur->widget->allocation.x + cur->widget->allocation.width;
		if (x > maxx)
			maxx = x;
		y = cur->widget->allocation.y + cur->widget->allocation.width;
		if (y > maxy)
			maxy = y;
		cur = cur->next;
	}
	gtk_layout_set_size(GTK_LAYOUT(canvas), maxx, maxy);
}

#if 0
void gtk_widget_modify_bg(GtkWidget *w, GtkStateType state, GdkColor *color)
{
	GtkStyle *s;
	s = gtk_style_copy(gtk_widget_get_style(w));
	if (s) {
		gtk_widget_set_style(w, s);
		s->bg[state].red = color->red;
		s->bg[state].green = color->green;
		s->bg[state].blue = color->blue;
		gdk_color_alloc(gtk_widget_get_colormap(w), &s->bg[state]);
	}
}
#endif

void gui_object_set_led(struct gui_object *obj, int on)
{
	GtkWidget *w, *old;
	int x,y;
	if (obj->led && (obj->onoff != on)) {
		gui_minirun();
		if (on)
			w = make_pixmap_from_data(canvas, usegreen ? green_on : red_on);
		else
			w = make_pixmap_from_data(canvas, usegreen ? green_off : red_off);
		old = obj->led;
		x = obj->widget->allocation.x - 20;
		y = obj->widget->allocation.y;
		gtk_widget_destroy(old);
		obj->led = w;
		gtk_layout_put(GTK_LAYOUT(canvas), obj->led, x, y);
		obj->onoff = on;
	}
}

int gui_link(struct gui_object *obj1, struct gui_object *obj2)
{
	struct gui_link *cur;
	/* See if the link is there already */
	cur = links;
	while(cur) {
		if (((cur->obj1 == obj1) && (cur->obj2 == obj2)) ||
		    ((cur->obj1 == obj2) && (cur->obj2 == obj1)))
				break;
		cur = cur->next;
	}
	if (!cur) {
		cur = g_new0(struct gui_link, 1);
		cur->widget = gtk_link_new();
		cur->obj1 = obj1;
		cur->obj2 = obj2;
		gtk_widget_show(cur->widget);
		gtk_layout_put(GTK_LAYOUT(canvas), cur->widget, 0, 0);
		cur->next = links;
		links = cur;
	}
	gtk_link_set_coords(GTK_LINK(cur->widget), 
		obj1->widget->allocation.x + obj1->widget->allocation.width/2,
		obj1->widget->allocation.y + obj1->widget->allocation.height/2,
		obj2->widget->allocation.x + obj2->widget->allocation.width/2,
		obj2->widget->allocation.y + obj2->widget->allocation.height/2);
	return 0;
}

int gui_unlink(struct gui_object *obj1, struct gui_object *obj2)
{
	struct gui_link *cur, *prev, *curn;
	/* See if the link is there already */
	cur = links;
	prev = NULL;
	while(cur) {
		curn = cur->next;
		if (((cur->obj1 == obj1) && (!obj2 || (cur->obj2 == obj2))) ||
		    ((!obj2 || (cur->obj1 == obj2)) && (cur->obj2 == obj1))) {
				if (prev)
					prev->next = cur->next;
				else
					links = cur->next;
				gtk_widget_destroy(cur->widget);
				free(cur);
		} else
			prev = cur;
		cur = curn;
	}
	return 0;
}

static int gui_update_links(struct gui_object *obj1)
{
	struct gui_link *cur;
	/* See if the link is there already */
	cur = links;
	while(cur) {
		if ((cur->obj1 == obj1) || (cur->obj2 == obj1)) {
			gtk_link_set_coords(GTK_LINK(cur->widget), 
				cur->obj1->widget->allocation.x + cur->obj1->widget->allocation.width/2,
				cur->obj1->widget->allocation.y + cur->obj1->widget->allocation.height/2,
				cur->obj2->widget->allocation.x + cur->obj2->widget->allocation.width/2,
				cur->obj2->widget->allocation.y + cur->obj2->widget->allocation.height/2);
		}
		cur = cur->next;
	}
	return 0;
}

int gui_object_move(struct gui_object *obj, int newx, int newy)
{
	if (newx < 0) newx = 0;
	if (newy < 0) newy = 0;
	gtk_layout_move(GTK_LAYOUT(canvas), obj->labelw, newx +
			obj->widget->allocation.width - obj->labelw->allocation.width, newy - 30);
	gtk_layout_move(GTK_LAYOUT(canvas), obj->widget, newx, newy);
	if (obj->led)
		gtk_layout_move(GTK_LAYOUT(canvas), obj->led, newx - 20, newy);
	gui_minirun();
	resize_canvas();
	gui_update_links(obj);
	return 0;
}

static void redirect_channel(gpointer callback_data, guint callback_action, GtkWidget *widget)
{
	if (current)
		gastman_redirect(current->pvt);
}

static void hangup_channel(gpointer callback_data, guint callback_action, GtkWidget *widget)
{
	if (current)
		gastman_hangup(current->pvt);
}

static void move_object_near(struct gui_object *obj, struct gui_object *cur, int *origx, int *origy)
{
	int cx, cy, crad, orad, rad;
	int xdiff, ydiff;
	float hlen;
	
	/* Change where we're going to return the object so that it stays
	   reasonably close to where we dropped it */
	cx = cur->widget->allocation.x + cur->widget->allocation.width/2;
	cy = cur->widget->allocation.y + cur->widget->allocation.height/2;
	crad = cur->widget->allocation.width/2;
	if (crad < cur->widget->allocation.height/2)
		crad = cur->widget->allocation.height/2;
	orad = obj->widget->allocation.width/2;
	if (orad < obj->widget->allocation.height)
		orad = obj->widget->allocation.height/2;
	rad = orad + crad + 40;
	/* Okay, we found the center of both object and their radius.  Now
	   calculate the new position based on the old */
	xdiff = cx - *origx;
	ydiff = cy - *origy;
	hlen = sqrt(((float)(xdiff*xdiff)) + ((float)(ydiff*ydiff)));
	xdiff *= (((float)rad) / hlen);
	ydiff *= (((float)rad) / hlen);
	*origx = cx - xdiff;
	*origy = cy - ydiff;
}

void gui_move_near(struct gui_object *dst, struct gui_object *src)
{
	int x,y;
	gtk_layout_freeze(GTK_LAYOUT(canvas));
	x = src->widget->allocation.x;
	y = src->widget->allocation.y;
	move_object_near(src, dst, &x, &y);
	gui_object_move(src, x, y);
	gtk_layout_thaw(GTK_LAYOUT(canvas));
}

struct gui_object *over_location(struct gui_object *obj, int x, int y)
{
	struct gui_object *cur;
	cur = objects;
	while(cur) {
		if ((cur != obj) &&
			(cur->widget->allocation.x < x)  &&
			(cur->widget->allocation.x + cur->widget->allocation.width > x) &&
			(cur->widget->allocation.y < y)  &&
			(cur->widget->allocation.y + cur->widget->allocation.height > y)) {
				return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
struct gui_object *over_object(struct gui_object *obj)
{
	int x,y;
	x = obj->widget->allocation.x + xof;
	y = obj->widget->allocation.y + yof;
	return over_location(obj, x, y);
}

static void check_drop(struct gui_object *obj)
{
	int res;
	struct gui_object *cur;
	if ((cur = over_object(obj))) {
		res = gastman_pre_drag_drop(obj->pvt, cur->pvt);
		if (res > 0) 
			move_object_near(obj, cur, &origx, &origy);
		/* Return the object to its original position */
		gtk_layout_freeze(GTK_LAYOUT(canvas));
		gui_object_move(obj, origx, origy);
		gtk_layout_thaw(GTK_LAYOUT(canvas));
		if (res > -1)
			gastman_drag_drop(obj->pvt, cur->pvt);
	}
}

static int bbutton;
static int btime;

int gui_show_chan_menu(void)
{
	char tmp[256];
	snprintf(tmp, sizeof(tmp), "%s Ops", current->name);
	gtk_label_set_text(GTK_LABEL(GTK_BIN(calltitle)->child), tmp);
	xof = -9999;
	gtk_menu_popup(GTK_MENU(callmenu), NULL, NULL, NULL, NULL, bbutton, btime);
	return 0;
}

int gui_show_exten_menu(void)
{
	char tmp[256];
	snprintf(tmp, sizeof(tmp), "%s Ops", current->name);
	gtk_label_set_text(GTK_LABEL(GTK_BIN(extentitle)->child), tmp);
	xof = -9999;
	gtk_menu_popup(GTK_MENU(extenmenu), NULL, NULL, NULL, NULL, bbutton, btime);
	return 0;
}

static int object_event(GtkObject *thing, GdkEvent *event, struct gui_object *obj)
{
	GdkEventButton *eb = (GdkEventButton*)event;
	int tookit = FALSE;
	int newx, newy;
	switch(event->type) {
	case GDK_2BUTTON_PRESS:
		if (eb->button == 1) {
			gastman_double_click(obj->pvt);
		}
		break;
	case GDK_BUTTON_PRESS:
		if (eb->button == 1) {
			tookit = TRUE;
			current = obj;
			xof = eb->x;
			yof = eb->y;

			/* Correct for what we're clicking on in case it's not the widget */
			xof -= (current->widget->allocation.x - GTK_WIDGET(thing)->allocation.x);
			yof -= (current->widget->allocation.y - GTK_WIDGET(thing)->allocation.y);

			origx = current->widget->allocation.x;
			origy = current->widget->allocation.y;
			gastman_object_select(obj->pvt);
		} else if (eb->button == 3) {
			current = obj;
			bbutton = eb->button;
			btime = eb->time;
			gastman_right_click(obj->pvt);
			tookit = TRUE;
		}
		break;
	case GDK_MOTION_NOTIFY:
		if (current && (current == obj) && (xof > -9999)) {
			gtk_widget_get_pointer(canvas, &newx, &newy);
			newx -= xof;
			newy -= yof;
			gtk_layout_freeze(GTK_LAYOUT(canvas));
			gui_object_move(current, newx, newy);
			gtk_layout_thaw(GTK_LAYOUT(canvas));
			tookit = TRUE;
		}
		break;
	case GDK_BUTTON_RELEASE:
		if ((eb->button == 1) && current) {
			tookit = TRUE;
			check_drop(current);
			current = NULL;
		}
		break;
	default:
		break;
	}
	if (thing && tookit)
		gtk_signal_emit_stop_by_name(thing, "event");
	return tookit;
}

static int gui_save_object(struct gui_object *cur)
{
	char tmp[256];
	DBT key, data;
	int res;
	if (db && cur) {
		if (strlen(cur->ident)) {
			snprintf(tmp, sizeof(tmp), "%d:%d", cur->widget->allocation.x, cur->widget->allocation.y);
			memset(&key, 0, sizeof(key));
			memset(&data, 0, sizeof(data));
			key.data = cur->ident;
			key.size = strlen(cur->ident) + 1;
			data.data = tmp;
			data.size = strlen(tmp) + 1;
#ifdef __FreeBSD__
			if ((res = db->put(db, &key, &data, 0)))
#else
			if ((res = db->put(db, NULL, &key, &data, 0)))
#endif
				fprintf(stderr, "Unable to save '%s' location\n", cur->ident);
			
		}
	}
	return 0;
}

static int update_queue(char *queuename, int pos);
void gui_del_object(struct gui_object *obj)
{
	struct gui_object *cur, *prev;
	int row;
	if (!obj)
		return;
	gui_unlink(obj, NULL);
	if (obj == current)
		current = NULL;
	cur = objects;
	prev = NULL;
	while(cur) {
		if (cur == obj) {
			if (prev)
				prev->next = obj->next;
			else
				objects = obj->next;
			break;
		}
		prev = cur;
		cur = cur->next;
	}
	gui_save_object(obj);
	gtk_widget_destroy(obj->labelw);
	gtk_widget_destroy(obj->widget);
	if (obj->led)
		gtk_widget_destroy(obj->led);
	if ((row = gtk_clist_find_row_from_data(GTK_CLIST(calllist), obj)) > -1)
		gtk_clist_remove(GTK_CLIST(calllist), row);
	else if ((row = gtk_clist_find_row_from_data(GTK_CLIST(extenlist), obj)) > -1)
		gtk_clist_remove(GTK_CLIST(extenlist), row);
	free(obj);
}

void gui_del_queue(struct gui_queue *obj)
{
	struct gui_queue *cur, *prev;
	int row;
	cur = queues;
	prev = NULL;
	while(cur) {
		if (cur == obj) {
			if (prev)
				prev->next = obj->next;
			else
				queues = obj->next;
			break;
		}
		prev = cur;
		cur = cur->next;
	}
	if ((row = gtk_clist_find_row_from_data(GTK_CLIST(qlist), obj)) > -1)
		gtk_clist_remove(GTK_CLIST(qlist), row);
	update_queue(obj->queue, obj->position);
	free(obj);
}

void gui_object_set_tooltip(struct gui_object *obj, char *tip)
{
	if (!obj)
		return;
	gtk_tooltips_set_tip(tips, obj->widget, tip, NULL);
}

void gui_object_set_text(struct gui_object *obj, char *text)
{
	if (!obj)
		return;
	gtk_label_set_text(GTK_LABEL(GTK_BIN(GTK_BIN(GTK_BIN(obj->labelw)->child)->child)->child), text);
}


struct gui_object * gui_add_object(char *name, char *text, char *icon, char *ident, void *pvt, int size, int withled, int type)
{
	struct gui_object *obj;
	GtkWidget *w, *w2;
	GtkWidget *list = NULL;
	GdkPixmap *pm = NULL;
	GdkBitmap *bm = NULL;
	char *l[10];
	int x = 0, y = 0;
	int res;
	int row;
	obj = g_new0(struct gui_object, 1);
	if (!obj)
		return NULL;
	memset(&l, 0, sizeof(l));
	obj->next = objects;
	objects = obj;
	strncpy(obj->name, name, sizeof(obj->name) - 1);
	strncpy(obj->icon, icon, sizeof(obj->icon) - 1);
	strncpy(obj->ident, ident, sizeof(obj->ident) - 1);
	strncpy(obj->text, text, sizeof(obj->text) - 1);
	obj->size = size;
	make_pixmap_from_object(obj);
	obj->label = gtk_label_new(text);
	obj->widget = gtk_event_box_new();
	obj->labelw = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(obj->labelw), GTK_SHADOW_ETCHED_OUT);
	w = gtk_event_box_new();
	w2 = gtk_event_box_new();
	gtk_container_set_border_width(GTK_CONTAINER(obj->labelw), 10);

	gtk_container_add(GTK_CONTAINER(obj->widget), obj->pixmap);
	gtk_container_add(GTK_CONTAINER(w), obj->label);
	gtk_container_add(GTK_CONTAINER(w2), w);
	gtk_container_set_border_width(GTK_CONTAINER(w), 1);
	gtk_container_add(GTK_CONTAINER(obj->labelw), w2);
	gtk_widget_modify_bg(w2, GTK_STATE_NORMAL, &colors[type]);
	gtk_widget_modify_bg(w, GTK_STATE_NORMAL, &colors[type]);
	gtk_widget_show_all(obj->widget);
	gtk_widget_show_all(obj->labelw);
	gtk_widget_shape_combine_mask(obj->widget, obj->bm, 0,0);
	gtk_signal_connect(GTK_OBJECT(w2), "event", GTK_SIGNAL_FUNC(object_event), obj);
	gtk_signal_connect(GTK_OBJECT(obj->widget), "event", GTK_SIGNAL_FUNC(object_event), obj);
	obj->type = type;
	obj->pvt = pvt;
	if (withled) {
		obj->led = make_pixmap_from_data(canvas, usegreen ? green_off : red_off);
		gtk_widget_show_all(obj->led);
	}
	gtk_layout_freeze(GTK_LAYOUT(canvas));
	gtk_layout_put(GTK_LAYOUT(canvas), obj->widget, 0, 0);
	gtk_layout_put(GTK_LAYOUT(canvas), obj->labelw, 0, 0);
	if (obj->led) {
		gtk_layout_put(GTK_LAYOUT(canvas), obj->led, 0, 0);
		gtk_widget_realize(obj->led);
	}
	l[0] = obj->text;
	if (type == TYPE_CALL) {
		list = calllist;
#ifdef NO_PIXBUF
		pm = gdk_pixmap_create_from_xpm_d(window->window, &bm, NULL, tinycaller_xpm);
#endif		
	} else if (type == TYPE_EXTEN) {
		list = extenlist;
#ifdef NO_PIXBUF
		pm = gdk_pixmap_create_from_xpm_d(window->window, &bm, NULL, tinyphone_xpm);
#endif		
	}
	if (list) {
#ifndef NO_PIXBUF
		GdkPixbuf *pb = NULL;
		GdkPixbuf *pb2;
		char fn[256];

		snprintf(fn, sizeof(fn), "%s" G_DIR_SEPARATOR_S "%s-%s.xpm", gui_get_icon_path(), obj->icon, formats[3]);

#if (GDK_PIXBUF_MAJOR > 1)
		pb2 = gdk_pixbuf_new_from_file(fn, NULL);
#else
		pb2 = gdk_pixbuf_new_from_file(fn);
#endif		
		if (pb2) {
			pb = gdk_pixbuf_scale_simple(pb2, 20, 20, GDK_INTERP_BILINEAR);
			gdk_pixbuf_unref(pb2);
		} else
			fprintf(stderr, "Unable to open %s\n", fn);
		gdk_pixbuf_render_pixmap_and_mask(pb, &pm, &bm, 10);
		gdk_pixbuf_unref(pb);
#endif
		row = gtk_clist_prepend(GTK_CLIST(list), l);
		gtk_clist_set_row_data(GTK_CLIST(list), row, obj);
		gtk_clist_set_pixtext(GTK_CLIST(list), row, 0, obj->text, 2, pm, bm);
	}
	gtk_widget_realize(obj->widget);
	gtk_widget_realize(obj->labelw);
	if (db && strlen(obj->ident)) {
		DBT key, data;
		memset(&key, 0, sizeof(key));
		memset(&data, 0, sizeof(data));
		key.data = obj->ident;
		key.size = strlen(obj->ident) + 1;
#ifdef __FreeBSD__
		if (!(res = db->get(db, &key, &data, 0))) {
#else
		if (!(res = db->get(db, NULL, &key, &data, 0))) {
#endif
			if (((char *)data.data)[data.size - 1] != '\0')
				fprintf(stderr, "Not null terminated?!? (%s, %d)\n", (char *)data.data, data.size);
			else if (sscanf((char *)data.data, "%d:%d", &x, &y) != 2) 
				fprintf(stderr, "Whoa, weird...\n");
			else {
				char old_ident[80];
				get_old_ident(obj->pvt, old_ident, 80);
				key.data = old_ident;
				key.size = strlen(old_ident) + 1;
#ifdef __FreeBSD__
				if (!(res = db->get(db, &key, &data, 0)))
#else
				if (!(res = db->get(db, NULL, &key, &data, 0)))
#endif
					sscanf((char *)data.data, "%d:%d", &x, &y);
			}
		}
	}
	if (!x && !y) {
		/* Try up to 50 times to find a random position that isn't on anything */
		int tries = 50;
		do {
			x = (rand() % (canvas->allocation.width - 150)) + 75;
			y = (rand() % (canvas->allocation.height - 150)) + 75;
			x -= obj->widget->allocation.width/2;
			y -= obj->widget->allocation.height/2;
			if (!over_location(obj, x, y) &&
				!over_location(obj, x + obj->widget->allocation.width, y + obj->widget->allocation.height))
				break;
		} while (--tries);
	}
	gui_minirun();
	gui_object_move(obj, x, y);
#if 0
	printf("Added object to canvas\n");
#endif	
	gtk_layout_thaw(GTK_LAYOUT(canvas));
	return obj;
}

struct gui_queue * gui_add_queue(char *name, char *qname, char *icon, void *pvt, int type)
{
	struct gui_queue *obj;
	GdkPixmap *pm = NULL;
	GdkBitmap *bm = NULL;
	GtkWidget *list;
	char *l[10];
	int row;
	obj = g_new0(struct gui_queue, 1);
	if (!obj)
		return NULL;
#if 0
	printf("Adding '%s' to queue '%s' to %p\n", name, qname, obj);
#endif	
	memset(&l, 0, sizeof(l));
	obj->next = queues;
	queues = obj;
	if (name)
		strncpy(obj->name, name, sizeof(obj->name) - 1);
	if (icon)
		strncpy(obj->icon, icon, sizeof(obj->icon) - 1);
	strncpy(obj->queue, qname, sizeof(obj->queue) - 1);
	obj->type = type;
	obj->pvt = pvt;
	l[0] = strlen(obj->name) ? "" : obj->queue;
	l[2] = obj->name;
	if ((type == TYPE_QUEUE) || (type == TYPE_QUEUENAME)) {
		list = qlist;
#ifdef NO_PIXBUF
		pm = gdk_pixmap_create_from_xpm_d(window->window, &bm, NULL, tinycaller_xpm);
#endif		
	}
	if (list) {
#ifndef NO_PIXBUF
		GdkPixbuf *pb = NULL;
		GdkPixbuf *pb2=NULL;
		char fn[256]="";

		if (strlen(obj->icon)) {
			snprintf(fn, sizeof(fn), "%s" G_DIR_SEPARATOR_S "%s-%s.xpm", gui_get_icon_path(), obj->icon, formats[3]);

#if (GDK_PIXBUF_MAJOR > 1)
			pb2 = gdk_pixbuf_new_from_file(fn, NULL);
#else
			pb2 = gdk_pixbuf_new_from_file(fn);
#endif		
		}
		if (pb2) {
			pb = gdk_pixbuf_scale_simple(pb2, 20, 20, GDK_INTERP_BILINEAR);
			gdk_pixbuf_unref(pb2);
			gdk_pixbuf_render_pixmap_and_mask(pb, &pm, &bm, 10);
			gdk_pixbuf_unref(pb);
		} else if (strlen(obj->icon))
			fprintf(stderr, "Unable to open %s\n", fn);
#endif
		row = gtk_clist_prepend(GTK_CLIST(list), l);
		gtk_clist_set_row_data(GTK_CLIST(list), row, obj);
		if (pm && bm)
			gtk_clist_set_pixtext(GTK_CLIST(list), row, 2, obj->name, 2, pm, bm);
		gtk_clist_sort(GTK_CLIST(qlist));
	}
	gui_minirun();
	return obj;
}

static FILE *open_config_file(char *filename, char *mode)
{
	char fn[256];
	char *home = gui_get_home_dir();

	snprintf(fn, sizeof(fn), "%s" G_DIR_SEPARATOR_S ".gastman", home);
#ifndef _WIN32
	mkdir(fn, 0755);
#else
	_mkdir(fn);
#endif
	snprintf(fn, sizeof(fn), "%s" G_DIR_SEPARATOR_S ".gastman" G_DIR_SEPARATOR_S "%s", home, filename);
	return fopen(fn, mode);
	
}

static int check_entity(char *entity)
{
	FILE *f;
	char buf[256], *buf2, *var;
	int res = -1;
	f = open_config_file("confirms.txt", "r");
	while(f && !feof(f)) {
		fgets(buf, sizeof(buf), f);
		if (!feof(f) && strlen(buf)) {
			/* Trim trailing \n */
			buf[strlen(buf) -1] = '\0';
			buf2 = buf;
			var = strsep(&buf2, "=");
			if (!strcmp(entity, var)) {
				if (*buf2) {
					res = atoi(buf2);
					break;
				}
			}
		}
		
	}
	if (f) fclose(f);
	return res;
}

static int add_entity(char *entity, int val)
{
	FILE *f;
	f = open_config_file("confirms.txt", "a");
	if (f) {
		fprintf(f, "%s=%d\n", entity, val);
		fclose(f);
	}
	return 0;
}

int gui_init(int *argc, char **argv[])
{
	char *fn = loc_file();
	int res = 0;
	if (fn) {
#ifdef __FreeBSD__
		if (!(db = dbopen(fn, O_CREAT | O_RDWR, 0664, DB_BTREE, NULL))) {
			fprintf(stderr, "Unable to open db\n");
#else
		if (!(res = db_create(&db, NULL, 0))) {
#if (DB_VERSION_MAJOR > 4) || ((DB_VERSION_MAJOR > 3) && (DB_VERSION_MINOR > 0))
			if ((res = db->open(db, NULL, fn,NULL, DB_BTREE,  DB_CREATE, 0664)))
#else
			if ((res = db->open(db, fn,NULL, DB_BTREE,  DB_CREATE, 0664)))
#endif			
				fprintf(stderr, "Unable to open db: %s\n", db_strerror(res));
		} else {
			fprintf(stderr, "Unable to create db: %s\n", db_strerror(res));
			db->close(db, 0);
			db = NULL;
#endif
		}
	}
	gtk_init(argc, argv);
	srand(time(NULL));
	return res;
}

void dialog_answer(GtkWidget *widget, gpointer data)
{
	if (ew && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ew))) {
		remember = 1;
	}
	gtk_widget_destroy(dialog);
	dialog = NULL;
	choice = (int)(long)data;
}

static GtkWidget *entry;
static GtkWidget *combo;
static char *outmsg;
static int outmsglen;
void entry_answer(GtkWidget *widget, gpointer data)
{
	choice = (int)(long)data;
	if (!choice) {
		strncpy(outmsg, gtk_entry_get_text(GTK_ENTRY(entry)), outmsglen - 1);
	} else
		strcpy(outmsg, "");
	gtk_widget_destroy(dialog);
	dialog = NULL;
}

void combo_answer(GtkWidget *widget, gpointer data)
{
	GtkCombo *combo2 = GTK_COMBO(combo);
	choice = (int)(long)data;
	if (!choice) {
		char *extra;
		strncpy(outmsg, gtk_entry_get_text(GTK_ENTRY(combo2->entry)), outmsglen - 1);
		if ((extra = strstr(outmsg, " (")))
			extra[0] = '\0';
	} else
		strcpy(outmsg, "");
	gtk_widget_destroy(dialog);
	dialog = NULL;
}


static char lastcmd[512];

void command_ready(GtkWidget *widget, gpointer data)
{
	const char *s;
	gtk_entry_set_editable(GTK_ENTRY(cmdentry), FALSE);
	s = gtk_entry_get_text(GTK_ENTRY(cmdentry));
	snprintf(lastcmd, sizeof(lastcmd), "-- %s --", s);
	gastman_run_command(s);
	gtk_entry_set_text(GTK_ENTRY(cmdentry), "");
	gtk_entry_set_editable(GTK_ENTRY(cmdentry), TRUE);
}

int gui_get_user_input(char *title, char *msg, char *buf, int buflen)
{
	GtkWidget *button;
	GtkWidget *tw;
	GtkWidget *vbox, *hbox;
	GtkWidget *icon;
	char rtext[256];
	int x;
	int choices = 2;


	snprintf(rtext, sizeof(rtext),"GAstMan: %s", title);
	dialog = gtk_dialog_new();
	tw = gtk_label_new(msg);
	gtk_window_set_title(GTK_WINDOW(dialog), rtext);
	gtk_widget_realize(dialog);
	fix_icon(dialog->window);
	gtk_widget_show(tw);
	vbox = gtk_vbox_new(FALSE, 0);
	hbox = gtk_hbox_new(FALSE, 0);
	icon = make_pixmap_from_data(dialog, inkwell_xpm);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 10);
	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 10);
	gtk_box_pack_start(GTK_BOX(vbox), tw, TRUE, TRUE, 15);
	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 10);
	entry = gtk_entry_new();
	gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 5);
	gtk_signal_connect(GTK_OBJECT(entry), "activate", GTK_SIGNAL_FUNC(entry_answer), (gpointer)(long)0);
	choice = -1;
	
	for (x=0;x<choices;x++) {
		button = gtk_button_new_with_label(okcancel[x]);
		gtk_widget_set_usize(button, 80, 30);
		gtk_widget_show(button);
		gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(entry_answer), (gpointer)(long)x);
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 10);
	}
	gtk_widget_grab_focus(entry);
	if (window)
		gtk_widget_set_sensitive(window, FALSE);
	outmsg = buf;
	outmsglen = buflen;
	gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
	gtk_widget_show_all(dialog);
	/* Block as long as dialog is here */
	while(dialog) 
		gtk_main_iteration_do(1);
	if (window)
		gtk_widget_set_sensitive(window, TRUE);
	return choice;
}

int gui_get_hostname(char *buf, int buflen)
{
	GtkWidget *button;
	GtkWidget *tw;
	GtkWidget *vbox, *hbox;
	GtkWidget *icon;
	GList *list = NULL;
	char *rtext = "GAstman: Select hostname";
	int x;
	int choices = 2;
	DIR *gastman_dir;
	struct dirent *gastman_dirent;
	static char fn[256];

	snprintf(fn, sizeof(fn), "%s" G_DIR_SEPARATOR_S ".gastman", gui_get_home_dir());
	gastman_dir = opendir(fn);
	while (( gastman_dirent = readdir(gastman_dir) )) {
		char *thedot;
		if ( gastman_dirent->d_name[0] == '.' )
			continue;
		if (( thedot = strstr(gastman_dirent->d_name, ".extens") )) {
			char *copy;
			struct hostent *hostent;

			thedot[0] = '\0';
			if ((hostent = gethostbyname(gastman_dirent->d_name))) {
				struct in_addr addr;
				memcpy(&addr, hostent->h_addr_list[0], sizeof(addr));
				if (strcmp(inet_ntoa(addr), gastman_dirent->d_name)) {
					copy = alloca(strlen(gastman_dirent->d_name) + strlen(inet_ntoa(addr)) + 4);
					if (copy) {
						sprintf(copy, "%s (%s)", gastman_dirent->d_name, inet_ntoa(addr));
						list = g_list_append(list, copy);
					}
				} else {
					copy = alloca(strlen(gastman_dirent->d_name) + 1);
					if (copy) {
						strcpy(copy, gastman_dirent->d_name);
						list = g_list_append(list, copy);
					}
				}
			} else {
				copy = alloca(strlen(gastman_dirent->d_name) + 1);
				if (copy) {
					strcpy(copy, gastman_dirent->d_name);
					list = g_list_append(list, copy);
				}
			}
		}
	}
	closedir(gastman_dir);

	dialog = gtk_dialog_new();
	tw = gtk_label_new("Select or enter the hostname you wish to connect to:");
	gtk_window_set_title(GTK_WINDOW(dialog), rtext);
	gtk_widget_realize(dialog);
	fix_icon(dialog->window);
	gtk_widget_show(tw);
	vbox = gtk_vbox_new(FALSE, 0);
	hbox = gtk_hbox_new(FALSE, 0);
	icon = make_pixmap_from_data(dialog, inkwell_xpm);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 10);
	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 10);
	gtk_box_pack_start(GTK_BOX(vbox), tw, TRUE, TRUE, 15);
	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 10);
	combo = gtk_combo_new();
	gtk_combo_set_case_sensitive(GTK_COMBO(combo), FALSE);
	gtk_combo_set_value_in_list(GTK_COMBO(combo), FALSE, FALSE);
	gtk_combo_set_use_arrows(GTK_COMBO(combo), TRUE);
	gtk_combo_set_use_arrows_always(GTK_COMBO(combo), TRUE);
	gtk_combo_set_popdown_strings(GTK_COMBO(combo), list);
	gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 5);
	/* gtk_signal_connect(GTK_OBJECT(GTK_COMBO(combo)), "activate", GTK_SIGNAL_FUNC(combo_answer), (gpointer)(long)0); */
	choice = -1;
	
	for (x=0;x<choices;x++) {
		button = gtk_button_new_with_label(okcancel[x]);
		gtk_widget_set_usize(button, 80, 30);
		gtk_widget_show(button);
		gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(combo_answer), (gpointer)(long)x);
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 10);
	}
	gtk_widget_grab_focus(combo);
	if (window)
		gtk_widget_set_sensitive(window, FALSE);
	outmsg = buf;
	outmsglen = buflen;
	gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
	gtk_widget_show_all(dialog);
	/* Block as long as dialog is here */
	while(dialog) 
		gtk_main_iteration_do(1);
	if (window)
		gtk_widget_set_sensitive(window, TRUE);

	/* Clean up associated memory */
	g_list_free(list);

	return choice;
}

int gui_ask_maybe(char *title, char *text, char *entity, int choices, int def, char *ctext[])
{
	GtkWidget *button;
	GtkWidget *tw;
	GtkWidget *defw = NULL;
	char rtext[256];
	int x, entitydef;

	choice = -1;
	remember = 0;
	if (entity) {
		if ((entitydef = check_entity(entity)) > -1) {
			printf("Using default answer for '%s'\n", entity);
			return entitydef;
		}
	}
	if (!choices) {
		/* Reasonable defaults */
		choices = 1;
		ctext = ok;
	}
	snprintf(rtext, sizeof(rtext),"GAstMan: %s", title);
	dialog = gtk_dialog_new();
	gtk_widget_realize(dialog);
	fix_icon(dialog->window);
	tw = gtk_label_new(text);
	gtk_window_set_title(GTK_WINDOW(dialog), rtext);
	gtk_widget_show(tw);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), tw, TRUE, TRUE, 5);
	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 10);
	if (entity) {
		ew = gtk_check_button_new_with_label("Do not ask me again");
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), ew, TRUE, TRUE, 5);
		
	} else
		ew = NULL;
	for (x=0;x<choices;x++) {
		button = gtk_button_new_with_label(ctext[x]);
		if (x == def)
			defw = button;
		gtk_widget_set_usize(button, 80, 30);
		gtk_widget_show(button);
		gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dialog_answer), (gpointer)(long)x);
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 10);
	}
	if (defw)
		gtk_widget_grab_focus(defw);
	if (window)
		gtk_widget_set_sensitive(window, FALSE);
	gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
	gtk_widget_show_all(dialog);
	/* Block as long as dialog is here */
	while(dialog) 
		gtk_main_iteration_do(1);
	if (remember) {
		if (check_entity(entity) == -1)
			add_entity(entity, choice);
	}
	if (window)
		gtk_widget_set_sensitive(window, TRUE);
	return choice;
}

static GtkWidget *username;
static GtkWidget *password;

static char **ruser, **rpass;

void login_answer(GtkWidget *widget, gpointer data)
{
	static char uname[256];
	static char pword[256];
	strncpy(uname, gtk_entry_get_text(GTK_ENTRY(username)), sizeof(uname) - 1);
	strncpy(pword, gtk_entry_get_text(GTK_ENTRY(password)), sizeof(pword) - 1);
	gtk_widget_destroy(dialog);
	dialog = NULL;
	choice = (int)(long)data;
	*ruser = uname;
	*rpass = pword;
}

int gui_login(char **user, char **pass, char *hostname)
{
	GtkWidget *button;
	GtkWidget *tw, *defw;
	GtkWidget *table;
	GtkWidget *icon;
	char *ctext[] = { "Login", "Cancel" };
	int choices = 2;
	char rtext[256];
	int x;

	snprintf(rtext, sizeof(rtext),"GAstMan: %s login", hostname);
	dialog = gtk_dialog_new();
	gtk_widget_realize(dialog);
	fix_icon(dialog->window);
	table = gtk_table_new(3, 3, 0);
	gtk_table_set_row_spacings(GTK_TABLE(table), 5);
	gtk_table_set_col_spacing(GTK_TABLE(table), 0, 15);
	gtk_table_set_col_spacing(GTK_TABLE(table), 1, 15);
	icon = make_pixmap_from_data(dialog, phone_xpm);
	gtk_table_attach_defaults(GTK_TABLE(table), icon, 0, 1, 0, 3);
	tw = gtk_label_new("Host: ");
	gtk_misc_set_alignment(GTK_MISC(tw), 0.0f, 0.5f);
	gtk_widget_show(tw);
	gtk_table_attach_defaults(GTK_TABLE(table), tw, 1, 2, 0, 1);
	tw = gtk_label_new(hostname);
	gtk_misc_set_alignment(GTK_MISC(tw), 0.0f, 0.5f);
	gtk_widget_show(tw);
	gtk_table_attach_defaults(GTK_TABLE(table), tw, 2, 3, 0, 1);

	tw = gtk_label_new("Username: ");
	gtk_misc_set_alignment(GTK_MISC(tw), 0.0f, 0.5f);
	gtk_widget_show(tw);
	gtk_table_attach_defaults(GTK_TABLE(table), tw, 1, 2, 1, 2);
	defw = username = gtk_entry_new();
	gtk_widget_show(username);
	gtk_widget_set_usize(username, 80, 0);
	gtk_table_attach_defaults(GTK_TABLE(table), username, 2, 3, 1, 2);

	tw = gtk_label_new("Password: ");
	gtk_misc_set_alignment(GTK_MISC(tw), 0.0f, 0.5f);
	gtk_widget_show(tw);
	gtk_table_attach_defaults(GTK_TABLE(table), tw, 1, 2, 2, 3);
	password = gtk_entry_new();
	gtk_widget_set_usize(password, 80, 0);
	gtk_entry_set_visibility(GTK_ENTRY(password), FALSE);
	gtk_widget_show(password);
	gtk_table_attach_defaults(GTK_TABLE(table), password, 2, 3, 2, 3);

	gtk_signal_connect_object(GTK_OBJECT(username), "activate", GTK_SIGNAL_FUNC(gtk_widget_grab_focus), GTK_OBJECT(password));

	gtk_window_set_title(GTK_WINDOW(dialog), rtext);
	gtk_widget_show(table);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 5);
	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 10);
	for (x=0;x<choices;x++) {
		button = gtk_button_new_with_label(ctext[x]);
		if (!x)
 			gtk_signal_connect_object(GTK_OBJECT(password), "activate", GTK_SIGNAL_FUNC(gtk_widget_grab_focus), GTK_OBJECT(button));
		gtk_widget_set_usize(button, 80, 30);
		gtk_widget_show(button);
		gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(login_answer), (gpointer)(long)x);
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 10);
	}
	if (defw)
		gtk_widget_grab_focus(defw);
	if (window)
		gtk_widget_set_sensitive(window, FALSE);
	gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
	gtk_widget_show_all(dialog);
	ruser = user;
	rpass = pass;
	/* Block as long as dialog is here */
	while(dialog) 
		gtk_main_iteration_do(1);
	if (window)
		gtk_widget_set_sensitive(window, TRUE);
	return choice;
}

static GtkWidget *icon_widget_new(void)
{
	DIR *dir;
	struct dirent *de;
	char search[256];
	char tmp[256];
	char *t;
	GtkWidget *w;
	GList *l = NULL;

	/* Make them only take our stuff */
	w = gtk_combo_new();
	gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(w)->entry), FALSE);
	
	snprintf(search, sizeof(search), "-%s.xpm", formats[format + 1]);
	if ((dir = opendir(gui_get_icon_path()))) {
		while((de = readdir(dir))) {
			strncpy(tmp, de->d_name, sizeof(tmp) - 1);
			if ((t = strstr(tmp, search))) {
				if (t[strlen(search)] == '\0') {
					*t = '\0';
					/* Add this entry */
					l = g_list_append(l, strdup(tmp));
				}
			}
		}
		closedir(dir);
	}
	gtk_combo_set_popdown_strings(GTK_COMBO(w), l);
	return w;
}

static GtkWidget *extenw;
static GtkWidget *iconw;
static GtkWidget *labelw;
static GtkWidget *chanw;

static char **rexten, **ricon, **rlabel, **rchans;

void exten_answer(GtkWidget *widget, gpointer data)
{
	static char exten[256];
	static char icon[256];
	static char label[256];
	static char chans[256];
	strncpy(exten, gtk_entry_get_text(GTK_ENTRY(extenw)), sizeof(exten) - 1);
	strncpy(chans, gtk_entry_get_text(GTK_ENTRY(chanw)), sizeof(chans) - 1);
	strncpy(icon, gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(iconw)->entry)), sizeof(icon) - 1);
	strncpy(label, gtk_entry_get_text(GTK_ENTRY(labelw)), sizeof(label) - 1);
	gtk_widget_destroy(dialog);
	dialog = NULL;
	choice = (int)(long)data;
	*rexten = exten;
	*ricon = icon;
	*rlabel = label;
	*rchans = chans;
}

/* Preview icon and frame */
static GtkWidget *pframe;
static GtkWidget *picon;

static void change_icon(GtkWidget *w)
{
	const char *newn;
	newn = gtk_entry_get_text(GTK_ENTRY(w));
	if (strlen(newn)) {
		gtk_widget_destroy(picon);
		picon = make_pixmap_from_file(dialog, newn, 2);
		gtk_widget_show(picon);
		gtk_container_add(GTK_CONTAINER(pframe), picon);
	}
}

int gui_exten(char **exten, char **chans, char **icon, char **label, char *title)
{
	GtkWidget *button;
	GtkWidget *tw, *defw;
	GtkWidget *siconw;
	GtkWidget *table;
	char *ctext[] = { "Add", "Cancel" };
	int choices = 2;
	char rtext[256];
	int x;

	snprintf(rtext, sizeof(rtext),"GAstMan: %s", title);
	dialog = gtk_dialog_new();
	table = gtk_table_new(4, 4, 0);
	gtk_table_set_row_spacings(GTK_TABLE(table), 5);
	gtk_table_set_col_spacings(GTK_TABLE(table), 5);


	gtk_widget_realize(dialog);
	fix_icon(dialog->window);
	siconw = make_pixmap_from_data(dialog, phone2_xpm);

	gtk_table_attach_defaults(GTK_TABLE(table), siconw, 0, 1, 0, 4);

	tw = gtk_label_new("Extension: ");
	gtk_misc_set_alignment(GTK_MISC(tw), 0.0f, 0.5f);
	gtk_widget_show(tw);
	gtk_table_attach_defaults(GTK_TABLE(table), tw, 1, 2, 0, 1);
	defw = extenw = gtk_entry_new();
	if (*exten) {
		ctext[0] = "Update";
		gtk_entry_set_text(GTK_ENTRY(extenw), *exten);
	}
	gtk_widget_show(extenw);
	gtk_table_attach_defaults(GTK_TABLE(table), extenw, 2, 3, 0, 1);

	tw = gtk_label_new("Name: ");
	gtk_misc_set_alignment(GTK_MISC(tw), 0.0f, 0.5f);
	gtk_widget_show(tw);
	gtk_table_attach_defaults(GTK_TABLE(table), tw, 1, 2, 1, 2);
	labelw = gtk_entry_new();
	if (*label)
		gtk_entry_set_text(GTK_ENTRY(labelw), *label);
	gtk_widget_show(labelw);
	gtk_table_attach_defaults(GTK_TABLE(table), labelw, 2, 3, 1, 2);

	tw = gtk_label_new("Icon: ");
	gtk_misc_set_alignment(GTK_MISC(tw), 0.0f, 0.5f);
	gtk_widget_show(tw);
	gtk_table_attach_defaults(GTK_TABLE(table), tw, 1, 2, 2, 3);
	iconw = icon_widget_new();
	if (*icon)
		gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(iconw)->entry), *icon);
	gtk_signal_connect(GTK_OBJECT(GTK_COMBO(iconw)->entry), "changed", GTK_SIGNAL_FUNC(change_icon), NULL);

	/* Preview icon */
	picon = make_pixmap_from_file(dialog, (char*)gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(iconw)->entry)), 2);
	pframe = gtk_frame_new("Icon Preview");
	gtk_container_add(GTK_CONTAINER(pframe), picon);
	gtk_widget_set_usize(pframe, 150, 150);
	gtk_table_attach_defaults(GTK_TABLE(table), pframe, 3, 4, 0, 4);
	gtk_table_set_col_spacing(GTK_TABLE(table), 2, 10);
	
	gtk_widget_show(iconw);
	gtk_table_attach_defaults(GTK_TABLE(table), iconw, 2, 3, 2, 3);

	tw = gtk_label_new("Channels: ");
	gtk_misc_set_alignment(GTK_MISC(tw), 0.0f, 0.5f);
	gtk_widget_show(tw);
	gtk_table_attach_defaults(GTK_TABLE(table), tw, 1, 2, 3, 4);
	chanw = gtk_entry_new();
	if (*chans)
		gtk_entry_set_text(GTK_ENTRY(chanw), *chans);
	gtk_widget_show(chanw);
	gtk_table_attach_defaults(GTK_TABLE(table), chanw, 2, 3, 3, 4);

	gtk_signal_connect_object(GTK_OBJECT(extenw), "activate", GTK_SIGNAL_FUNC(gtk_widget_grab_focus), GTK_OBJECT(iconw));

	gtk_window_set_title(GTK_WINDOW(dialog), rtext);
	gtk_widget_show(table);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 5);
	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 10);
	for (x=0;x<choices;x++) {
		button = gtk_button_new_with_label(ctext[x]);
		if (!x)
 			gtk_signal_connect_object(GTK_OBJECT(labelw), "activate", GTK_SIGNAL_FUNC(gtk_widget_grab_focus), GTK_OBJECT(button));
		gtk_widget_set_usize(button, 80, 30);
		gtk_widget_show(button);
		gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(exten_answer), (gpointer)(long)x);
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 10);
	}
	if (defw)
		gtk_widget_grab_focus(defw);
	if (window)
		gtk_widget_set_sensitive(window, FALSE);
	gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
	gtk_widget_show_all(dialog);
	rexten = exten;
	ricon = icon;
	rlabel = label;
	rchans = chans;
	/* Block as long as dialog is here */
	while(dialog) 
		gtk_main_iteration_do(1);
	if (window)
		gtk_widget_set_sensitive(window, TRUE);
	return choice;
}

int gui_ask(char *title, char *text, int choices, int def, char *ctext[])
{
	return gui_ask_maybe(title, text, NULL, choices, def, ctext);
}

int gui_yesno(char *title, char *text, int defaultyes)
{
	if (!gui_ask(title, text, 2, defaultyes ? 0 : 1, yesno))
		return 1;
	return 0;
}

int gui_show_message(char *title, char *text)
{
	gui_ask(title, text, 1, 0, okcancel);
	return 0;
}

int gui_confirm(char *title, char *text, int defaultyes, char *entity)
{
	if (!gui_ask_maybe(title, text, entity, 2, defaultyes ? 0 : 1, yesno))
		return 1;
	return 0;
}

static GtkWidget *doingw;
static GtkWidget *dtext;

int gui_show_doing(char *title, char *text)
{
	char rtext[256];

	snprintf(rtext, sizeof(rtext),"GAstMan: %s", title);
	if (!doingw) {
		doingw = gtk_dialog_new();
		dtext = gtk_label_new(text);
		gtk_widget_show(dtext);
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(doingw)->vbox), dtext, TRUE, TRUE, 5);
		gtk_window_position(GTK_WINDOW(doingw), GTK_WIN_POS_CENTER);
		gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(doingw)->vbox), 10);
	} else {
		gtk_label_set_text(GTK_LABEL(dtext), text);
	}
	gtk_window_set_title(GTK_WINDOW(doingw), rtext);
	if (window)
		gtk_widget_set_sensitive(window, FALSE);
	gtk_widget_show_all(doingw);
	while(!gtk_main_iteration_do(0));
	return 0;
}

int gui_hide_doing(void)
{
	if (doingw) {
		gtk_widget_destroy(doingw);
		doingw = NULL;
	}
	if (window)
		gtk_widget_set_sensitive(window, TRUE);
	while(!gtk_main_iteration_do(0));
	return 0;
}

static void dataready(void *ign, int fd, GdkInputCondition c)
{
	gastman_input_ready();
}

int gui_watch_fd(int fd)
{
	fdio = gdk_input_add(fd, GDK_INPUT_READ, dataready, NULL);
	fdfd = fd;
	return 0;
}

int gui_run(void)
{
	gtk_main();
	return 0;
}

int gui_exit(void)
{
	struct gui_object *cur;
	char tmp2[256];
	char tmp[256];
	DBT key, data;
	int res;
	/* Save positions of icons if they have identities */
	if (db) {
		cur = objects;
		while(cur) {
			gui_save_object(cur);
			cur = cur->next;
		}
		if (window) {
			snprintf(tmp, sizeof(tmp), "%d:%d", window->allocation.width, window->allocation.height);
			snprintf(tmp2, sizeof(tmp2), "[%s]window", gastman_curhost());
			memset(&key, 0, sizeof(key));
			memset(&data, 0, sizeof(data));
			key.data = tmp2;
			key.size = strlen(tmp2) + 1;
			data.data = tmp;
			data.size = strlen(tmp) + 1;
#ifdef __FreeBSD__
			if ((res = db->put(db, &key, &data, 0)))
#else
			if ((res = db->put(db, NULL, &key, &data, 0)))
#endif
				fprintf(stderr, "Unable to save window location\n");
		}
#ifdef __FreeBSD__
		db->close(db);
#else
		db->close(db, 0);
#endif
	}
	gtk_exit(0);
	return 0;
}

static void quit_program(gpointer callback_data, guint callback_action, GtkWidget *widget)
{
	gastman_quit();
}

static void new_exten(gpointer callback_data, guint callback_action, GtkWidget *widget)
{
	gastman_add_exten();
}

static void edit_exten(gpointer callback_data, guint callback_action, GtkWidget *widget)
{
	if (current)
		gastman_double_click(current->pvt);
}

static void del_exten(gpointer callback_data, guint callback_action, GtkWidget *widget)
{
	if (current)
		gastman_del_exten(current->pvt);
}

static void originate_exten(gpointer callback_data, guint callback_action, GtkWidget *widget)
{
	if (current)
		gastman_originate_exten(current->pvt);
}

static void invite_exten(gpointer callback_data, guint callback_action, GtkWidget *widget)
{
	if (current)
		gastman_invite_exten(current->pvt);
}

static void list_select(GtkWidget *w, gint row, gint column, GdkEventButton *eb, gpointer data)
{
	struct gui_object *obj;
	GdkPixmap *pm = NULL;
	GdkBitmap *bm = NULL;
	char *text;
	guint8 spacing;
	obj = gtk_clist_get_row_data(GTK_CLIST(w), row);
	if (!eb)
		return;
	current = obj;
	if (eb->button == 1) {
		gtk_clist_get_pixtext(GTK_CLIST(w), row, 0, &text, &spacing, &pm, &bm);
		gtk_drag_set_default_icon(gdk_colormap_get_system(), pm, bm, 10, 10);
		gastman_object_select(obj->pvt);
	} else if (eb->button == 3) {
		bbutton = eb->button;
		btime = eb->time;
		gastman_right_click(obj->pvt);
	}
}

static void qlist_select(GtkWidget *w, gint row, gint column, GdkEventButton *eb, gpointer data)
{
	struct gui_queue *obj;
	GdkPixmap *pm = NULL;
	GdkBitmap *bm = NULL;
	char *text;
	guint8 spacing;
	obj = gtk_clist_get_row_data(GTK_CLIST(w), row);
	if (!eb)
		return;
	if (obj->type != TYPE_QUEUE) {
		qcurrent = NULL;
		return;
	}
	qcurrent = obj;
	if (eb->button == 1) {
		gtk_clist_get_pixtext(GTK_CLIST(w), row, 0, &text, &spacing, &pm, &bm);
		gtk_drag_set_default_icon(gdk_colormap_get_system(), pm, bm, 10, 10);
		gastman_queue_select(obj->pvt);
	}
#if 0
	 else if (eb->button == 3) {
		bbutton = eb->button;
		btime = eb->time;
		gastman_right_click(obj->pvt);
	}
#endif	
}

static GtkItemFactoryEntry entries[] = {
	{ "/_File", NULL, NULL, 0, "<Branch>" },
	{ "/_File/_Quit", "<CTRL>Q", quit_program, 0, "<Item>" },
	{ "/_Exten", NULL, NULL, 0, "<Branch>" },
	{ "/_Exten/_New", "<CTRL>N", new_exten, 0, "<Item>" },
	{ "/_Exten/_Edit", "<CTRL>E", edit_exten, 0, "<Item>" },
	{ "/_Exten/_Delete", "<CTRL>D", del_exten, 0, "<Item>" },
};

static GtkItemFactoryEntry callentries[] = {
	{ "/Title", NULL, NULL, 0, "<Item>" },
	{ "/Separator", NULL, NULL, 0, "<Separator>" },
	{ "/_Redirect", "<CTRL>R", redirect_channel, 0, "<Item>" },
	{ "/_Hangup", "<CTRL>H", hangup_channel, 0, "<Item>" },
};

static GtkItemFactoryEntry extenentries[] = {
	{ "/Title", NULL, NULL, 0, "<Item>" },
	{ "/Separator", NULL, NULL, 0, "<Separator>" },
	{ "/_Invite", "<CTRL>I", invite_exten, 0, "<Item>" },
	{ "/_Originate", "<CTRL>O", originate_exten, 0, "<Item>" },
	{ "/_Delete", "<CTRL>D", del_exten, 0, "<Item>" },
	{ "/_Edit", "<CTRL>E", edit_exten, 0, "<Item>" },
};

static int schedid = -1;
static int updateid = -1;

int sillytimeout(void *ign)
{
	schedid = -1;
	printf("Whee!\n");
	return 0;
}

int gui_run_a_little(int *timeout)
{
	struct timeval start, end;
	gettimeofday(&start, NULL);
	if (schedid > -1)
		gtk_timeout_remove(schedid);
	schedid = gtk_timeout_add(*timeout, sillytimeout, NULL);
	gtk_main_iteration_do(1);
	gettimeofday(&end, NULL);
	if (*timeout >= 0) {
		*timeout -= (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec)/1000;
		if (*timeout < 0)
			*timeout = 0;
	}
	return 0;
}


static GtkWidget *statusbar;
static int statusid;

int gui_status(char *str)
{
	gtk_statusbar_pop(GTK_STATUSBAR(statusbar), statusid);
	gtk_statusbar_push(GTK_STATUSBAR(statusbar), statusid, str);
	return 0;
}

int gui_set_icon(int nformat)
{
	format = nformat;
	return 0;
}

static void qdnd_picked(GtkWidget *widget, GdkDragContext *dc, GtkSelectionData *selection_data, guint info, guint t, gpointer data)
{
	struct gui_queue *obj;
	int row;
	if (GTK_CLIST(widget)->selection_end)
		row = (gint) (long)GTK_CLIST(widget)->selection_end->data;
	else
		return;
	obj = gtk_clist_get_row_data(GTK_CLIST(widget), row);
	gtk_selection_data_set(selection_data, GDK_SELECTION_TYPE_ATOM, 8, (char *)&obj, sizeof(obj));
	
}

static void qdnd_dropped(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info, guint t, gpointer data)
{
	struct gui_queue *src, *dst;
	int row, column;
	int res;
	if (widget == NULL)	
		return;
	if (!selection_data || (selection_data->length < sizeof(src)))
		return;
	if (!gtk_clist_get_selection_info(GTK_CLIST(widget), x, y, &row, &column))
		return;
	memcpy(&src, selection_data->data, sizeof(src));
	dst = gtk_clist_get_row_data(GTK_CLIST(widget), row);
	if (dst != src) {
		res = gastman_pre_drag_drop(src->pvt, dst->pvt);
		if (res > -1)
			gastman_drag_drop(src->pvt, dst->pvt);
	}
}

static void dnd_picked(GtkWidget *widget, GdkDragContext *dc, GtkSelectionData *selection_data, guint info, guint t, gpointer data)
{
	struct gui_object *obj;
	int row;
	if (GTK_CLIST(widget)->selection_end)
		row = (gint) (long)GTK_CLIST(widget)->selection_end->data;
	else
		return;
	obj = gtk_clist_get_row_data(GTK_CLIST(widget), row);
	gtk_selection_data_set(selection_data, GDK_SELECTION_TYPE_ATOM, 8, (char *)&obj, sizeof(obj));
	
}

static void dnd_dropped(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info, guint t, gpointer data)
{
	struct gui_object *src, *dst;
	int row, column;
	int res;
	if (widget == NULL)	
		return;
	if (!selection_data || (selection_data->length < sizeof(src)))
		return;
	if (!gtk_clist_get_selection_info(GTK_CLIST(widget), x, y, &row, &column))
		return;
	memcpy(&src, selection_data->data, sizeof(src));
	dst = gtk_clist_get_row_data(GTK_CLIST(widget), row);
	if (dst != src) {
		res = gastman_pre_drag_drop(src->pvt, dst->pvt);
		if (res > -1)
			gastman_drag_drop(src->pvt, dst->pvt);
	}
}

void gui_object_set_callinfo(struct gui_object *obj, char *callerid, char *linked, char *location, struct gui_object *cobj, struct gui_object  *eobj)
{
	int row, row2;
	GdkPixmap *cpm = NULL, *epm = NULL;
	GdkBitmap *cbm = NULL, *ebm = NULL;
#if 0
	printf("Being called... ('%s', '%s', '%s')\n", linked, location, callerid);
#endif
	row = gtk_clist_find_row_from_data(GTK_CLIST(calllist), obj);
	if (row > -1) {
		gtk_clist_set_text(GTK_CLIST(calllist), row, 1, callerid);

		if (cobj && (row2 = gtk_clist_find_row_from_data(GTK_CLIST(calllist), cobj)) > -1) {
			gtk_clist_get_pixtext(GTK_CLIST(calllist), row2, 0, NULL, NULL, &cpm, &cbm);
		}

		if (eobj && (row2 = gtk_clist_find_row_from_data(GTK_CLIST(extenlist), eobj)) > -1) {
			gtk_clist_get_pixtext(GTK_CLIST(extenlist), row2, 0, NULL, NULL, &epm, &ebm);
		}

		if (cpm && cbm)
			gtk_clist_set_pixtext(GTK_CLIST(calllist), row, 2, linked, 5, cpm, cbm);
		else
			gtk_clist_set_text(GTK_CLIST(calllist), row, 2, linked);
		if (epm && ebm)
			gtk_clist_set_pixtext(GTK_CLIST(calllist), row, 3, location, 5, epm, ebm);
		else
			gtk_clist_set_text(GTK_CLIST(calllist), row, 3, location);
	}
#if 0
	printf("Done called... ('%s', '%s')\n", linked, location);
#endif
}

static int qupdate(void *ign)
{
	int wait;
	int x;
	struct gui_queue *obj;
	char swait[40];
	time_t now;
	time(&now);
	for (x=0;x<GTK_CLIST(qlist)->rows;x++) {
		obj = gtk_clist_get_row_data(GTK_CLIST(qlist), x);
		if (obj->type == TYPE_QUEUE) {
			wait = now - obj->base + obj->offset;
			if (wait > 3600) 
				snprintf(swait, sizeof(swait), "%d:%02d:%02d", wait/3600, (wait % 3600) / 60, wait % 60);
			else
				snprintf(swait, sizeof(swait), "%d:%02d", wait / 60, wait % 60);
			gtk_clist_set_text(GTK_CLIST(qlist), x, 4, swait);
		}
	}
	return 1;
}

static int update_queue(char *queuename, int pos)
{
	int x;
	struct gui_queue *obj;
	char spos[40];
	time_t now;
	time(&now);
	for (x=0;x<GTK_CLIST(qlist)->rows;x++) {
		obj = gtk_clist_get_row_data(GTK_CLIST(qlist), x);
		if (obj->type == TYPE_QUEUE) {
			if (!strcasecmp(queuename, obj->queue) && (obj->position > pos)) {
				obj->position--;
				snprintf(spos, sizeof(spos), "%d", obj->position);
				gtk_clist_set_text(GTK_CLIST(qlist), x, 1, spos);
			}
		}
	}
	return 1;
}

int queuesort(GtkCList *clist, const void *ptr1, const void *ptr2)
{
	int res;
	const GtkCListRow *row1, *row2;
	const struct gui_queue *q1, *q2;

	row1 = ptr1;
	row2 = ptr2;
	q1 = row1->data;
	q2 = row2->data;
	if (!q1 || !q2)
		return 0;
	res = strcasecmp(q1->queue, q2->queue);
	if (!res) {
		res = q1->position > q2->position;
	}
	return res;
}

void gui_queue_set_callinfo(struct gui_queue *obj, char *callerid, int position, int wait)
{
	int row;
	GdkPixmap *cpm = NULL;
	GdkBitmap *cbm = NULL;
	char sposition[40];
#if 0
	printf("Being called... ('%s', '%d', '%d')\n", callerid, position, wait);
#endif
	row = gtk_clist_find_row_from_data(GTK_CLIST(qlist), obj);
	if (row > -1) {
		time(&obj->base);
		obj->offset = wait;
		obj->position = position;
		gtk_clist_set_text(GTK_CLIST(qlist), row, 3, callerid);
		snprintf(sposition, sizeof(sposition), "%d", position);
		gtk_clist_set_text(GTK_CLIST(qlist), row, 1, sposition);
		if (cpm && cbm)
			gtk_clist_set_pixtext(GTK_CLIST(qlist), row, 3, callerid, 5, cpm, cbm);
		else
			gtk_clist_set_text(GTK_CLIST(qlist), row, 3, callerid);
		gtk_clist_sort(GTK_CLIST(qlist));
	} else
		printf("Weird, not found...\n");
#if 0
	printf("Done called... ('%s', '%s')\n", linked, location);
#endif
}

void gui_queue_set_queueinfo(struct gui_queue *obj, int current, int max)
{
	int row;
	char status[40];
	row = gtk_clist_find_row_from_data(GTK_CLIST(qlist), obj);
	if (row > -1) {
		if (max)
			snprintf(status, sizeof(status), "    %d of %d calls", current, max);
		else
			snprintf(status, sizeof(status), "    %d calls", current);
		gtk_clist_set_text(GTK_CLIST(qlist), row, 2, status);
	} else
		printf("Weird, not found...\n");
#if 0
	printf("Done called... ('%s', '%s')\n", linked, location);
#endif
}

static char *ctitles[] = {
	"Channel",
	"Caller*ID",
	"Linked to",
	"Location",
};

static char *qtitles[] = {
	"Queue",
	"Position",
	"Channel",
	"Caller",
	"Hold Time",
};

void gui_cli_result(char *s)
{
	char *o;
	float p;
	GtkStyle *style;
	style = gtk_widget_get_style(cmdtextbox);
	gtk_text_freeze(GTK_TEXT(cmdtextbox));
	o = gtk_editable_get_chars(GTK_EDITABLE(cmdtextbox), 0, -1);
	gtk_editable_delete_text(GTK_EDITABLE(cmdtextbox), 0, -1);
	gtk_text_insert(GTK_TEXT(cmdtextbox), clifont, &style->fg[GTK_STATE_INSENSITIVE], NULL, o, -1);
	gtk_text_insert(GTK_TEXT(cmdtextbox), clifont, &colors[3], NULL, lastcmd, -1);
	/* Apparently we *have* to insert something here, or it will crash, go figure */
	if (!s || !strlen(s))
		s = "        \n";
	gtk_text_insert(GTK_TEXT(cmdtextbox), clifont, NULL, NULL, "\n", -1);
	gtk_text_thaw(GTK_TEXT(cmdtextbox));
	p = GTK_TEXT(cmdtextbox)->vadj->upper;
	p -= (float)(cmdtextbox->allocation.height - 1.0);
	if (p < 0.0)
		p = 0.0;
	gtk_adjustment_set_value(GTK_TEXT(cmdtextbox)->vadj, p);
	gtk_text_insert(GTK_TEXT(cmdtextbox), clifont, NULL, NULL, s, -1);
	gtk_editable_set_position(GTK_EDITABLE(cmdtextbox), -1);
	g_free(o);
}

static int focus_tabs(GtkObject *thing, GdkEvent *event, struct gui_object *obj)
{
	if (event->type == GDK_BUTTON_RELEASE)
		gtk_widget_grab_focus(cmdentry);
	return FALSE;
}

static int command_event(GtkObject *thing, GdkEvent *event, struct gui_object *obj)
{
	GdkEventKey *ek;
	int keyval;
	int flags;
	int pos;
	int absorbed = FALSE;
	char *s;
	if (event->type == GDK_KEY_PRESS) {
		ek = (GdkEventKey *)event;
		keyval = ek->keyval & 0xff;
		flags = (ek->keyval & 0xff00) >> 8;
		if (keyval == 0x9) {
			/* Tab pressed -- command complete... */
			gtk_widget_set_sensitive(GTK_WIDGET(thing), FALSE);
#if (GTK_MAJOR_VERSION > 1)
			pos = GTK_ENTRY(thing)->current_pos;
#else
			pos = GTK_EDITABLE(thing)->current_pos;
#endif			
			s = gastman_complete(gtk_entry_get_text(GTK_ENTRY(thing)), &pos);
			gtk_entry_set_text(GTK_ENTRY(thing), s);
			gtk_widget_set_sensitive(GTK_WIDGET(thing), TRUE);
			gtk_widget_grab_focus(GTK_WIDGET(thing));
			gtk_entry_select_region(GTK_ENTRY(thing), pos, pos);
			absorbed = TRUE;
		} else if (flags == 0xff) {
			if (keyval == 0x52) {
				s = gastman_prevhist();
				if (s) {
					gtk_entry_set_text(GTK_ENTRY(thing), s);
					gtk_entry_set_position(GTK_ENTRY(thing), -1);
				} else
					gdk_beep();
				absorbed = 1;
			} else if (keyval == 0x54) {
				s = gastman_nexthist();
				if (s) {
					gtk_entry_set_text(GTK_ENTRY(thing), s);
					gtk_entry_set_position(GTK_ENTRY(thing), -1);
				} else {
					gtk_entry_set_text(GTK_ENTRY(thing), "");
					gtk_entry_set_position(GTK_ENTRY(thing), -1);
					gdk_beep();
				}
				absorbed = 1;
			}
		}
	}
	return absorbed;
}

int gui_show_box(char *host)
{
	char buf[256];
	GtkWidget *vbox;
	GtkWidget *menu;
	GtkWidget *sw;
	GtkWidget *tw;
	GtkWidget *pane;
	GtkWidget *frame;
	GtkWidget *qframe;
	GtkWidget *hbox;
	GtkItemFactory *fact;
	GtkAccelGroup *group;
	int xwin=640,ywin=480;

	/* The purely graphical piece */
	
	snprintf(buf, sizeof(buf), "Asterisk Manager at %s", host);
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_realize(window);
	fix_icon(window->window);
	group =  gtk_accel_group_new();
	fact = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<GastManMain>", group);
	gtk_item_factory_create_items(fact, sizeof(entries) / sizeof(entries[0]), entries, NULL);
	menu = gtk_item_factory_get_widget(fact, "<GastManMain>");

	fact = gtk_item_factory_new(GTK_TYPE_MENU, "<GastCallMenu>", group);
	gtk_item_factory_create_items(fact, sizeof(callentries) / sizeof(callentries[0]), callentries, NULL);
	callmenu = gtk_item_factory_get_widget(fact, "<GastCallMenu>");
	calltitle = gtk_item_factory_get_widget(fact, "<GastCallMenu>/Title");

	fact = gtk_item_factory_new(GTK_TYPE_MENU, "<GastExtenMenu>", group);
	gtk_item_factory_create_items(fact, sizeof(extenentries) / sizeof(extenentries[0]), extenentries, NULL);
	extenmenu = gtk_item_factory_get_widget(fact, "<GastExtenMenu>");
	extentitle = gtk_item_factory_get_widget(fact, "<GastExtenMenu>/Title");

	statusbar = gtk_statusbar_new();
	statusid = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), "status");
	
	tips = gtk_tooltips_new();
	
	canvas = gtk_layout_new(NULL, NULL);
	gtk_widget_modify_bg(canvas, GTK_STATE_NORMAL, &colors[COLOR_GRAY]);
	
	sw = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_container_add(GTK_CONTAINER(sw), canvas);
	gtk_container_set_border_width(GTK_CONTAINER(sw), 1);
	frame = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
	gtk_container_add(GTK_CONTAINER(frame), sw);

	/* Make notebook */
	tabs = gtk_notebook_new();
	gtk_notebook_append_page(GTK_NOTEBOOK(tabs), frame, gtk_label_new("Graphical View"));
	gtk_signal_connect(GTK_OBJECT(tabs), "event", GTK_SIGNAL_FUNC(focus_tabs), NULL);

	/* List of calls and extensions */
	pane = gtk_vpaned_new();
	calllist = gtk_clist_new_with_titles(4, ctitles);
	gtk_clist_set_column_auto_resize(GTK_CLIST(calllist), 0, TRUE);
	gtk_clist_set_column_auto_resize(GTK_CLIST(calllist), 1, TRUE);
	gtk_clist_set_column_auto_resize(GTK_CLIST(calllist), 2, TRUE);
	gtk_clist_set_column_auto_resize(GTK_CLIST(calllist), 3, TRUE);
	gtk_signal_connect(GTK_OBJECT(calllist), "select_row", GTK_SIGNAL_FUNC(list_select), NULL);
	gtk_clist_set_selection_mode(GTK_CLIST(calllist), GTK_SELECTION_BROWSE);
	gtk_clist_set_shadow_type(GTK_CLIST(calllist), GTK_SHADOW_IN);
	gtk_clist_set_row_height(GTK_CLIST(calllist), 20);
	gtk_clist_set_button_actions(GTK_CLIST(calllist), 2, GTK_BUTTON_SELECTS);
	gtk_drag_source_set(calllist, GDK_BUTTON1_MASK, &target_entry, 1, GDK_ACTION_MOVE | GDK_ACTION_COPY);
	gtk_signal_connect(GTK_OBJECT(calllist), "drag_data_get", GTK_SIGNAL_FUNC(dnd_picked), NULL);
	gtk_signal_connect(GTK_OBJECT(calllist), "drag_data_received", GTK_SIGNAL_FUNC(dnd_dropped), NULL);

	/* Queue status */
	qlist = gtk_clist_new_with_titles(5, qtitles);
	gtk_clist_set_column_auto_resize(GTK_CLIST(qlist), 0, TRUE);
	gtk_clist_set_column_auto_resize(GTK_CLIST(qlist), 1, TRUE);
	gtk_clist_set_column_auto_resize(GTK_CLIST(qlist), 2, TRUE);
	gtk_clist_set_column_auto_resize(GTK_CLIST(qlist), 3, TRUE);
	gtk_clist_set_column_auto_resize(GTK_CLIST(qlist), 4, TRUE);
	gtk_clist_set_compare_func(GTK_CLIST(qlist), queuesort);
	gtk_signal_connect(GTK_OBJECT(qlist), "select_row", GTK_SIGNAL_FUNC(qlist_select), NULL);
	gtk_clist_set_selection_mode(GTK_CLIST(qlist), GTK_SELECTION_BROWSE);
	gtk_clist_set_shadow_type(GTK_CLIST(qlist), GTK_SHADOW_IN);
	gtk_clist_set_row_height(GTK_CLIST(qlist), 20);
	gtk_clist_set_button_actions(GTK_CLIST(qlist), 2, GTK_BUTTON_SELECTS);
	gtk_drag_source_set(qlist, GDK_BUTTON1_MASK, &target_entry, 1, GDK_ACTION_MOVE | GDK_ACTION_COPY);
	gtk_signal_connect(GTK_OBJECT(qlist), "drag_data_get", GTK_SIGNAL_FUNC(qdnd_picked), NULL);
	gtk_signal_connect(GTK_OBJECT(qlist), "drag_data_received", GTK_SIGNAL_FUNC(qdnd_dropped), NULL);
	qframe = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(qframe), GTK_SHADOW_IN);
	gtk_container_add(GTK_CONTAINER(qframe), qlist);
	
	extenlist = gtk_clist_new(1);
	gtk_signal_connect(GTK_OBJECT(extenlist), "select_row", GTK_SIGNAL_FUNC(list_select), NULL);
	gtk_clist_set_selection_mode(GTK_CLIST(extenlist), GTK_SELECTION_BROWSE);
	gtk_clist_set_shadow_type(GTK_CLIST(extenlist), GTK_SHADOW_IN);
	gtk_clist_set_row_height(GTK_CLIST(extenlist), 20);
	gtk_clist_set_button_actions(GTK_CLIST(extenlist), 2, GTK_BUTTON_SELECTS);
	gtk_drag_dest_set(extenlist, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, &target_entry, 1, GDK_ACTION_MOVE | GDK_ACTION_COPY);
	gtk_drag_source_set(extenlist, GDK_BUTTON1_MASK, &target_entry, 1, GDK_ACTION_MOVE | GDK_ACTION_COPY);
	gtk_signal_connect(GTK_OBJECT(extenlist), "drag_data_get", GTK_SIGNAL_FUNC(dnd_picked), NULL);
	gtk_signal_connect(GTK_OBJECT(extenlist), "drag_data_received", GTK_SIGNAL_FUNC(dnd_dropped), NULL);

	vbox = gtk_vbox_new(FALSE, 2);
	gtk_box_pack_start(GTK_BOX(vbox), (tw = gtk_label_new("Active Channels:")), FALSE, FALSE, 0);
	gtk_misc_set_alignment(GTK_MISC(tw), 0.01f, 0.5f);
	sw = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	gtk_container_add(GTK_CONTAINER(sw), calllist);
	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
	gtk_paned_pack1(GTK_PANED(pane), vbox, 1, 1);

	vbox = gtk_vbox_new(FALSE, 2);
	gtk_box_pack_start(GTK_BOX(vbox), (tw = gtk_label_new("Extensions:")), FALSE, FALSE, 0);
	gtk_misc_set_alignment(GTK_MISC(tw), 0.01f, 0.5f);
	sw = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	gtk_container_add(GTK_CONTAINER(sw), extenlist);
	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
	gtk_paned_pack2(GTK_PANED(pane), vbox, 1, 1);
	gtk_container_set_border_width(GTK_CONTAINER(pane), 5);

	gtk_notebook_append_page(GTK_NOTEBOOK(tabs), pane, gtk_label_new("List View"));

	gtk_notebook_append_page(GTK_NOTEBOOK(tabs), qframe, gtk_label_new("Call Queues"));

	/* Command window */
	vbox = gtk_vbox_new(FALSE, 0);
	hbox = gtk_hbox_new(FALSE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
	cmdtextbox = gtk_text_new(FALSE, FALSE);
	gtk_text_set_word_wrap(GTK_TEXT(cmdtextbox), FALSE);
	gtk_text_set_line_wrap(GTK_TEXT(cmdtextbox), FALSE);

	sw = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_container_add(GTK_CONTAINER(sw), cmdtextbox);

	cmdentry = gtk_entry_new();
	gtk_signal_connect(GTK_OBJECT(cmdentry), "activate", GTK_SIGNAL_FUNC(command_ready), (gpointer)(long)0);
	gtk_signal_connect(GTK_OBJECT(cmdentry), "event", GTK_SIGNAL_FUNC(command_event), NULL);
	gtk_widget_grab_focus(cmdentry);
	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
	clifont = gdk_font_load("-*-clean-*-*-*-*-16-*-*-*-*-*-*-*");
	
	gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("CLI>"), FALSE, FALSE, 0);
	gtk_box_pack_end(GTK_BOX(hbox), cmdentry, TRUE, TRUE, 2);
	gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
	gtk_notebook_append_page(GTK_NOTEBOOK(tabs), vbox, gtk_label_new("Command Window"));

	vbox = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(window), vbox);
	gtk_box_pack_start(GTK_BOX(vbox), menu, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), tabs, TRUE, TRUE, 0);
	gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
	gtk_window_set_title(GTK_WINDOW(window), buf);
	if (db) {
		int res;
		DBT key, data;
		char tmp2[256];
		memset(&key, 0, sizeof(key));
		memset(&data, 0, sizeof(data));
		snprintf(tmp2, sizeof(tmp2), "[%s]window", gastman_curhost());
		key.data = tmp2;
		key.size = strlen(tmp2) + 1;
#ifdef __FreeBSD__
		if (!(res = db->get(db, &key, &data, 0))) {
#else
		if (!(res = db->get(db, NULL, &key, &data, 0))) {
#endif
			if (((char *)data.data)[data.size - 1] != '\0')
				fprintf(stderr, "Not null terminated?!?\n");
			else if (sscanf((char *)data.data, "%d:%d", &xwin, &ywin) != 2) 
				fprintf(stderr, "Whoa, weird...\n");
			
		}
	}
	gtk_widget_set_usize(window, xwin, ywin);
	gtk_window_set_policy(GTK_WINDOW(window), 1, 1, 0);
	gtk_window_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(quit_program), NULL);
	gtk_widget_show_all(window);
	updateid = gtk_timeout_add(1000, qupdate, NULL);
	gui_status("Manager Ready.");
	return 0;
}
