/*
 * This is xqdtk Release 2
 */
#include	<stdio.h>
#include	<ctype.h>
#include	<sys/time.h>
#include	"xqdtk.h"
#include	"grey.bitmap"
#include	"marker.bitmap"

#include	<X11/xpm.h>
#include	<X11/Xlib.h>
#include	"xgroups.h"

#define		PADDING		2	/* minimum padding around text */
#define		TEXT_BORDER	2	/* width of text window borders */
#define		GRAPH_BORDER	2	/* width of graphics window borders */
#define		BUTTON_BORDER	3	/* width of button window borders */
#define		TOPLEVEL_BORDER	5	/* width of top level window borders */
#define		LINE_WIDTH	2	/* width of lines in graph windows */
#define		MENU_BORDER	2	/* width of pulldownmenu borders */
#define		ENTRY_HEIGHT	20	/* height of each entry in menu */
#define		BAR_BORDER	2	/* width of menu bar borders */
#define		POPUP_BORDER	2	/* width of popup window borders */

#define		FONT_NAME	"-Adobe-Helvetica-Bold-r-Normal--*-120-*"

#define		ABS(x)		((x)<0 ? -(x) : (x))

Display *display;	/* global pointer to display structure */
int	black, white;	/* global pixel values obtained from X server */
int	x_coord, y_coord;	/* global coordinates of a button press */
int	button;			/* global ID of button that was pressed */

static Pixmap mk_labeled_pixmap();
static void find_bbox();
static void adjust_text_origin();
static Button *register_button();
static void draw_item();
static void put_marker();
static void marker_left();
static void marker_right();
static void handle_key_press();
static void erase_eol();
static void delete_char();
static void insert_char();
static void pulldown();
static void pullup();
Window parent_of();

static int default_screen;
static int default_depth;
static Bool pulldown_but = False, menu_but = False;
static Window toplevel;
static GC marker_GC, eraser_GC, invert_GC, default_GC, disable_GC;
static Pixmap grey_pixmap;
static Pixmap marker;
static Pixmap stipple;
static XFontStruct *font;

static Button *button_list = NULL;
static TextBox *text_box_list = NULL;
static EditBox *edit_box_list = NULL, *active_edit_box;
static GraphWindow *graph_window_list = NULL;
static PulldownMenu *menu_list = NULL, *activemenu = NULL;
static MenuBar *menubar_list = NULL;

Window x_init(name, x, y, width, height, argc, argv)
  char *name;		/* name for this application window */
  int x, y;		/* coordinates of upper left corner of window */
  int width, height;	/* width and height of window */
  int argc;		/* from "main(argc, argv)" */
  char *argv[];		/* from "main(argc, argv)" */
	{
	XSizeHints hint;
	XGCValues gc_val;
	char geom_buf[20];
	int i;

	/*
	   Open connection to X Server and get info about the display
	*/
	if ((display = XOpenDisplay("")) == NULL)
		{
		fprintf(stderr, "Can't open display!\n");
		exit(1);
		}
	default_screen = XDefaultScreen(display);
	default_depth = XDisplayPlanes(display, default_screen);
	black = XBlackPixel(display, default_screen);
	white = XWhitePixel(display, default_screen);
	/*
	   Look for relevant command line switches
	*/
	hint.flags = PPosition | PSize;
	for (i=1; i<argc; i++)
		{
		if (!strcmp(argv[i], "-geometry"))
			{
			sprintf(geom_buf, "%dx%d+%d+%d", width, height, x, y);
			XGeometry(display, default_screen,
					argv[++i], geom_buf,
					TOPLEVEL_BORDER, 1, 1, 0, 0,
					&x, &y, &width, &height);
			hint.flags = USPosition | USSize;
			}
		}	
	x -= TOPLEVEL_BORDER;
	y -= TOPLEVEL_BORDER;
	hint.x = x;
	hint.y = y;
	hint.width = width;
	hint.height = height;
	/*
	   Load and query font
	*/
	font = XLoadQueryFont(display, FONT_NAME);
	/*
	   Create top level application window
	*/
	toplevel = XCreateSimpleWindow(display, DefaultRootWindow(display),
			x, y, width, height, TOPLEVEL_BORDER, black, white);
	XSetStandardProperties(display, toplevel, name, name, None,
			argv, argc, &hint);
	XSelectInput(display, toplevel, KeyPressMask | ButtonReleaseMask
						     | EnterWindowMask
						     | LeaveWindowMask);
	/*
	   Create necessary GCs
	*/
	gc_val.font = font->fid;
	gc_val.line_width = LINE_WIDTH;
	gc_val.foreground = black;
	gc_val.background = white;
	default_GC = XCreateGC(display, toplevel,
			GCFont | GCLineWidth | GCForeground | GCBackground,
			&gc_val);
	disable_GC = XCreateGC(display, toplevel,
			GCFont | GCLineWidth | GCForeground | GCBackground,
			&gc_val);
	gc_val.foreground = white;
	eraser_GC = XCreateGC(display, toplevel, GCFont | GCForeground,
				&gc_val);
	gc_val.function = GXinvert;
	gc_val.plane_mask = 1;
	invert_GC = XCreateGC(display, toplevel,
			GCFont | GCPlaneMask | GCFunction, &gc_val);
	marker = XCreateBitmapFromData(display, toplevel, marker_bits,
					marker_width, marker_height);
	gc_val.clip_mask = marker;
	marker_GC = XCreateGC(display, toplevel,
			GCFunction | GCClipMask, &gc_val);
	/*
	   Create a grey pixmap for highlighting active buttons
	*/
	grey_pixmap = XCreatePixmapFromBitmapData(display, toplevel,
			grey_bits, grey_width, grey_height, black, white,
			default_depth);
	/*
	   Create a stipple pixmap for greying out inactive menus
	*/
	stipple = XCreateBitmapFromData(display, toplevel,
			grey_bits, grey_width, grey_height);

	/*
	   Modify GC for disabled menus
	*/
	XSetTile(display, disable_GC, grey_pixmap);
	XSetFillStyle(display, disable_GC, FillTiled);

	/*
	   Map the window to the display
	*/
	XMapWindow(display, toplevel);
	return toplevel;
	}

Window x_label(parent, text, x, y, width, height)
  Window parent;	/* window in which the label will be created */
  char *text;		/* text to be displayed in the label window */
  int x, y;		/* coordinates within toplevel */
  int width, height;	/* width and height */
	{
	Pixmap pmap;
	Window label_w;

	pmap = mk_labeled_pixmap(text, &width, &height, default_GC);
	x -= TEXT_BORDER;
	y -= TEXT_BORDER;
	label_w = XCreateSimpleWindow(display, parent,
			x, y, width, height, TEXT_BORDER, black, white);
	XSetWindowBackgroundPixmap(display, label_w, pmap);
	XMapWindow(display, label_w);
	return label_w;
	}

MenuBar *x_create_menubar(parent, x, y, width, height, num_menus)
  Window parent;
  int x, y;			/* offset of menu bar */
  int width, height;		/* width & height of bar */
  int num_menus;		/* no. of menus in the bar */
	{
	MenuBar *menubar;

	menubar = (MenuBar *)malloc(sizeof(MenuBar));;
	menubar->next = menubar_list;
	menubar_list = menubar;
	menubar->menus = (PulldownMenu **)malloc(num_menus *
						sizeof(PulldownMenu *));
	menubar->num_menus = num_menus;
	menubar->height = (height >= 1 ? height : 1);
	menubar->width = (width >= 1 ? width : 1);
	menubar->x = x;
	menubar->y = y;
	menubar->xstart = 0;
	menubar->window = XCreateSimpleWindow(display, parent,
				x - BAR_BORDER, y - BAR_BORDER, 
				menubar->width, menubar->height, BAR_BORDER,
				black, white); 
	return menubar;
	}

void x_create_pulldown(mb, text, num_entries, entries)
  MenuBar *mb;			/* menu bar menu is to go in */
  char *text;			/* text to appear in caller button */
  int num_entries;		/* number of entries in the menu */
  PulldownEntry *entries;	/* array of entries */
	{
 	PulldownMenu *menu; 
	static MenuBar *mbptr = NULL;
	static unsigned menu_id = 1, menu_num;
	static unsigned widthsum;	/* running total of caller widths */
	Bool last;
	int width,height;		/* of caller */
	int i,temp,dum1,dum2;

 	menu = (PulldownMenu *)malloc(sizeof(PulldownMenu));
 	menu->pullbutton = (Button **)malloc(num_entries * sizeof(Button *));
 	menu->num_entries = num_entries; 
	menu->menu_id = menu_id++;
	menu->width = 0;
	if (mbptr != mb)
		{
		mbptr = mb;
		menu_num = 1;
		widthsum = 0;
		}
	else menu_num++;
	for (i = 0; i<num_entries; i++)  /* find width of largest name */
		{
		find_bbox(entries[i].name,&temp,&dum1,&dum2);
		if (temp > menu->width) menu->width = temp;
		}
	menu->width += 2 * PADDING;
	menu->height = num_entries * ENTRY_HEIGHT;
	find_bbox(text,&width,&height,&dum2);   /* get caller width & height */
	if (height > mb->height) mb->height = height + PADDING;
	width += 2 * PADDING;
	if ((widthsum += width) > mb->width)
		mb->width = widthsum;
	if (last = (menu_num == mb->num_menus)) /* is this the last caller? */
		{
		width += 3;	
		mb->width++;
		}
	XResizeWindow(display, mb->window, mb->width, mb->height); 
	menu_but = True;
	menu->caller = x_labeled_button(mb->window, text, mb->xstart, 0,
				width, mb->height, pulldown, menu->menu_id);
	menu_but = False;
 	menu->window = XCreateSimpleWindow(display, parent_of(mb->window), 
				(mb->xstart == 0 ? mb->x + mb->xstart :
				(last ? mb->x + widthsum - width + 2*MENU_BORDER
					: mb->x + mb->xstart + 1)) - BAR_BORDER, 
				mb->y + mb->height + BAR_BORDER, menu->width,
				menu->height, MENU_BORDER, black, white);
	mb->xstart += width;
 	pulldown_but = True;
 	for (i = 0; i<num_entries; i++) 
   		menu->pullbutton[i] = x_labeled_button(menu->window,
				entries[i].name, 0, i * ENTRY_HEIGHT, 
				menu->width, ENTRY_HEIGHT, entries[i].function, 
				entries[i].arg);
 	pulldown_but = False;
	menu->next = menu_list;
        mb->menus[menu_num - 1] = menu_list = menu;
	if (last) XMapWindow(display, mb->window);
	}

static void pulldown(menu_id)
  unsigned menu_id;
	{
	PulldownMenu *menu;

	for (menu = menu_list; menu != NULL; menu = menu->next)
		if (menu->menu_id == menu_id)
		{
 		XMapRaised(display,menu->window); 
 		activemenu = menu;
		}
	}

static void pullup(menu)
   PulldownMenu *menu;
	{
 	XUnmapWindow(display,menu->window); 
 	activemenu = NULL;
	} 

void x_disable_entry(mb, menu, entry)
/* entry == 0 means disable the entry in the menu bar */
  MenuBar *mb;
  int menu, entry;
	{
	if (entry != 0)
 		x_disable_button(mb->menus[menu-1]->pullbutton[entry-1]);
	else
		x_disable_button(mb->menus[menu-1]->caller);
	}


void x_enable_entry(mb, menu, entry)
/* entry == 0 means enable the entry in the menu bar */
  MenuBar *mb;
  int menu, entry;
	{
	if (entry != 0)
 		x_enable_button(mb->menus[menu-1]->pullbutton[entry-1]);
	else
		x_enable_button(mb->menus[menu-1]->caller);
	}

Window parent_of(w)
  Window w;
	{
 	Window root, parent, *children;
 	int nchildren;

 	XQueryTree(display,w,&root,&parent,&children,&nchildren);
 	XFree(children);
 	return parent;
	}

Window x_create_popup(parent, x, y, width, height) 
  Window parent;
  int x,y,width,height;
	{
	x -= POPUP_BORDER;
	y -= POPUP_BORDER;
 	return XCreateSimpleWindow(display, parent, x, y, width,
			 height, POPUP_BORDER, black, white);
	}

Window x_create_tpopup(parent, x, y, width, height, name) 
  Window parent;
  int x,y,width,height;
  char *name;
	{
	Window r;

	x -= POPUP_BORDER;
	y -= POPUP_BORDER;
 	r=XCreateSimpleWindow(display, parent, x, y, width,
			 height, POPUP_BORDER, black, white);

	XStoreName(display,r,name);

	return r;
	}

void x_popup(w)
  Window w;
	{
 	XMapRaised(display,w);
	}

void x_popdown(w) 
  Window w;
	{
 	XUnmapWindow(display,w);
	}

Button *x_labeled_button(parent, text, x, y, width, height, function, arg)
  Window parent;	/* window in which the label will be created */
  char *text;		/* text to be displayed in the label window */
  int x, y;		/* coordinates within parent */
  int width, height;	/* width and height */
  void (*function)(int);/* function to be invoked when button pressed */
  int arg;		/* argument passed to "function" when button pressed */
	{
	Pixmap pmap, disable_pmap;
	Window button_w;
	int border_width;

	if (!(menu_but || pulldown_but))
	    {
	    x -= BUTTON_BORDER;
	    y -= BUTTON_BORDER;
	    }
	pmap = mk_labeled_pixmap(text, &width, &height, default_GC);
	disable_pmap = mk_labeled_pixmap(text, &width, &height, disable_GC);
	border_width = (pulldown_but || menu_but ? 0 : BUTTON_BORDER);
	button_w = XCreateSimpleWindow(display, parent,
			x, y, width, height, border_width, black, white);
	XSetWindowBackgroundPixmap(display, button_w, pmap);
	XMapWindow(display, button_w);
	return register_button(button_w, function, arg, pmap, disable_pmap,
 			width, height);
	}

void x_put_a_bitmap(parent, bitmap_data, x, y, width, height,border)
  Window parent;        /* window in which the label will be created */
  char *bitmap_data;    /* picture to be displayed in the label window */
  int x, y;             /* coordinates within parent */
  int width, height;    /* width and height */
  int border;           /* border */
	{
	Pixmap pmap;
	Window button_w;

	pmap = XCreatePixmapFromBitmapData(display, parent,
	bitmap_data, width, height, black, white,
		default_depth);
	button_w = XCreateSimpleWindow(display, parent,
		x, y, width, height, border, black, white);
	XSetWindowBackgroundPixmap(display, button_w, pmap);
	XMapWindow(display, button_w);
}

Button *x_picture_button(parent,
			bitmap_data, x, y, width, height, function, arg)
  Window parent;	/* window in which the label will be created */
  char *bitmap_data;	/* picture to be displayed in the label window */
  int x, y;		/* coordinates within parent */
  int width, height;	/* width and height */
  void (*function)(int);/* function to be invoked when button pressed */
  int arg;		/* argument passed to "function" when button pressed */
	{
	Pixmap pmap, disable_pmap;
	Window button_w;

	x -= BUTTON_BORDER;
	y -= BUTTON_BORDER;
	pmap = XCreatePixmapFromBitmapData(display, parent,
			bitmap_data, width, height, black, white,
			default_depth);
	disable_pmap = XCreatePixmap(display, DefaultRootWindow(display), 
			width, height, default_depth);
	XCopyArea(display, pmap, disable_pmap, disable_GC, 0,0,
			width, height, 0,0); 
	button_w = XCreateSimpleWindow(display, parent,
			x, y, width, height, BUTTON_BORDER, black, white);
	XSetWindowBackgroundPixmap(display, button_w, pmap);
	XMapWindow(display, button_w);
	return register_button(button_w, function, arg, pmap, disable_pmap,
			width, height);
	}

void x_highlight_button(button)
  Button *button;	/* the button to be highlighted */
	{
	if (button->flags & HIGHLIGHTED) return; /* already highlighted */
	XFillRectangle(display, button->pixmap, invert_GC,
			0, 0, button->width, button->height);
	XCopyArea(display, button->pixmap, button->window, default_GC,
			0, 0, button->width, button->height, 0, 0);
	button->flags |= HIGHLIGHTED;
	}

void x_unhighlight_button(button)
  Button *button;	/* the button to be highlighted */
	{
	if (!(button->flags & HIGHLIGHTED)) return; /* not highlighted */
	XFillRectangle(display, button->pixmap, invert_GC,
			0, 0, button->width, button->height);
	XCopyArea(display, button->pixmap, button->window, default_GC,
			0, 0, button->width, button->height, 0, 0);
	button->flags &= ~HIGHLIGHTED;
	}

void x_disable_button(button)
  Button *button;	/* the button to be disabled */
	{
	button->flags |= DISABLED;
	XSetWindowBorderPixmap(display, button->window, grey_pixmap);
	XSetWindowBackgroundPixmap(display, button->window, 
			button->disable_pmap);
	XClearWindow(display, button->window);
	}

void x_enable_button(button)
  Button *button;	/* the button to be re-enabled */
	{
	button->flags &= ~DISABLED;
	XSetWindowBackgroundPixmap(display, button->window, button->pixmap);
	XClearWindow(display, button->window);
	}

TextBox *x_text_box(parent, max_chars, just, x, y, width, height)
  Window parent;
  int max_chars;
  Justification just;
  int x, y;
  int width, height;
	{
	Window text_w;
	TextBox *new_box;
	register int i;

	x -= TEXT_BORDER;
	y -= TEXT_BORDER;

	text_w = XCreateSimpleWindow(display, parent,
			x, y, width, height, TEXT_BORDER, black, white);
	new_box = (TextBox *) malloc(sizeof(TextBox));
	new_box->window = text_w;
	new_box->text = (char *) calloc(max_chars+1, sizeof(char));
	for (i=0; i<=max_chars; i++)
		new_box->text[i] = '\0';
	new_box->max_chars = max_chars;
	new_box->just = just;
	new_box->x = 0;
	new_box->y = 0;
	new_box->width = width;
	new_box->height = height;
	new_box->next = text_box_list;
	text_box_list = new_box;
	XSelectInput(display, text_w, ExposureMask | ButtonReleaseMask);
	XMapWindow(display, text_w);
	return new_box;
	}

EditBox *x_edit_box(parent, max_chars, x, y, width, height, function, arg)
  Window parent;
  int max_chars;
  int x, y;
  int width, height;
  void (*function)(int);  /* func. called when return is hit; NULL for none */
  int arg;
	{
	Window edit_w;
	EditBox *new_box;
	register int i;

	x -= TEXT_BORDER;
	y -= TEXT_BORDER;
	edit_w = XCreateSimpleWindow(display, parent,
			x, y, width, height, TEXT_BORDER, black, white);
	new_box = (EditBox *) malloc(sizeof(EditBox));
	new_box->window = edit_w;
	new_box->text = (char *) calloc(max_chars+1, sizeof(char));
	for (i=0; i<=max_chars; i++)
		new_box->text[i] = '\0';
	new_box->max_chars = max_chars;
	new_box->x = 0;
	new_box->y = 0;
	new_box->width = width;
	new_box->height = height;
	new_box->first_char = 0;
	new_box->last_char = 0;
	new_box->current_char = 0;
	new_box->marker_x = 0;
	new_box->function = function;
	new_box->arg = arg;
	new_box->next = edit_box_list;
	edit_box_list = new_box;
	XSelectInput(display, edit_w, ExposureMask |
				      ButtonReleaseMask |
				      ButtonPressMask);
	XMapWindow(display, edit_w);
	return new_box;
	}

char *x_get_edit_text(eb)
  EditBox *eb;
	{
	return(eb->text + eb->first_char);
	}

void x_activate_edit_box(eb, prompt, init_text)
  EditBox *eb;
  char *prompt, *init_text;
	{

	put_marker();
	if (eb != NULL)
	  {
	  strncpy(eb->text, prompt, eb->max_chars);
	  strncat(eb->text, init_text,
		eb->max_chars - strlen(prompt));
	  eb->first_char = strlen(prompt);
	  eb->last_char = eb->current_char = strlen(eb->text);
	  eb->x = PADDING;
	  eb->y = eb->height/2;
	  adjust_text_origin(eb->text,
			West, &(eb->x), &(eb->y));
	  eb->marker_x = eb->x +
		XTextWidth(font, eb->text, eb->current_char);
	  XClearWindow(display, eb->window);
	  XDrawString(display, eb->window, default_GC,
			eb->x, eb->y,
			eb->text, strlen(eb->text));
	  }
        active_edit_box = eb;
	put_marker();
	}

static void erase_eol()
	{
	register EditBox *eb = active_edit_box;
	if (eb == NULL) return;
	if (eb->current_char == eb->last_char) return;
	XDrawString(display, eb->window, eraser_GC,
			eb->marker_x, eb->y,
			eb->text + eb->current_char,
			eb->last_char - eb->current_char);
	}

static void put_marker()
	{
	register EditBox *eb = active_edit_box;
	int x, y;
	if (eb == NULL) return;
	x = eb->marker_x - marker_x_hot - 1;
	y = eb->y - marker_y_hot + font->max_bounds.descent;
	XSetClipOrigin(display, marker_GC, x, y);
	XFillRectangle(display, eb->window, marker_GC, x, y,
			marker_width, marker_height);
	}

static void marker_left()
	{
	register EditBox *eb = active_edit_box;
	if (eb == NULL) return;
	if (eb->current_char > eb->first_char)
		{
		--(eb->current_char);
		put_marker();
		eb->marker_x -= XTextWidth(font,
			eb->text + eb->current_char, 1);
		put_marker();
		}
	}

static void marker_right()
	{
	register EditBox *eb = active_edit_box;
	if (eb == NULL) return;
	if (eb->current_char < eb->last_char)
		{
		put_marker();
		eb->marker_x += XTextWidth(font,
			eb->text + eb->current_char, 1);
		++(eb->current_char);
		put_marker();
		}
	}

static void position_marker(x)
int x;
	{
	register EditBox *eb = active_edit_box;
	int c, cx, closest_c, closest_x, distance, min_distance = 10000;
	if (eb == NULL) return;
	put_marker();
	for (c = eb->first_char; c <= eb->last_char; c++)
		{
		cx = eb->x + XTextWidth(font, eb->text, c);
		distance = ABS(x-cx);
		if (distance < min_distance)
		   {
		   closest_c = c;
		   closest_x = cx;
		   min_distance = distance;
		   }
		}
	eb->current_char = closest_c;
	eb->marker_x = closest_x;
	put_marker();
	}

static void delete_char()
	{
	register EditBox *eb = active_edit_box;
	register int i;
	if (eb == NULL) return;
	if (eb->current_char == eb->first_char) return;
	marker_left();
	erase_eol();
	for (i = eb->current_char; i < eb->last_char; i++)
		eb->text[i] = eb->text[i+1];
	--(eb->last_char);
	XDrawString(display, eb->window, default_GC,
			eb->marker_x, eb->y,
			eb->text + eb->current_char,
			eb->last_char - eb->current_char);
	}

static void insert_char(c)
  char c;
	{
	register EditBox *eb = active_edit_box;
	register int i;
	if (eb == NULL) return;
	if (eb->last_char == eb->max_chars) return;
	erase_eol();
	for (i = eb->last_char; i >= eb->current_char; --i)
		eb->text[i+1] = eb->text[i];
	eb->text[eb->current_char] = c;
	++(eb->last_char);
	XDrawString(display, eb->window, default_GC,
			eb->marker_x, eb->y,
			eb->text + eb->current_char,
			eb->last_char - eb->current_char);
	marker_right();
	}

static void handle_key_press(event)
  XKeyEvent *event;
	{
	KeySym keysym;
	char keytext[10];
	if (active_edit_box == NULL) return;
	XLookupString(event, keytext, 10, &keysym, 0);
	switch (keysym)
		{
		case XK_Left:
			marker_left();
			break;
		case XK_Right:
			marker_right();
			break;
		case XK_Delete:
			delete_char();
			break;
		case XK_Return:
			if (active_edit_box->function != NULL)
			  (*(active_edit_box->function))(active_edit_box->arg);
			break;
		default:
			if ((keysym & 0x0f00) == 0)	/* Latin 1 */
			  {
			  if (isprint(keytext[0]))
				insert_char(keytext[0]);
			  else if (keytext[0] == '\b') 	/* backspace */
				delete_char();
			  }
		}
	}

void x_print_text(mywindow,x,y, text)
Window mywindow;
int x,y;
char *text;
{
	XDrawString(display, mywindow, default_GC, x, y, text, strlen(text));
}

void x_display_text(text_box, text)
  TextBox *text_box;
  char *text;
	{
	strncpy(text_box->text, text, text_box->max_chars);
	switch(text_box->just)
		{
		case Right: case East: case NEast: case SEast:
			text_box->x = text_box->width - 1 - PADDING;
			break;
		case Centered: case North: case South:
			text_box->x = text_box->width/2;
			break;
		case Left: case West: case NWest: case SWest:
			text_box->x = 0 + PADDING;
			break;
		}
	switch(text_box->just)
		{
		case NWest: case North: case NEast:
			text_box->y = 0 + PADDING;
			break;
		case Right: case Left: case West: case Centered: case East:
			text_box->y = text_box->height/2;
			break;
		case SWest: case South: case SEast:
			text_box->y = text_box->height - 1 - PADDING;
			break;
		}
	adjust_text_origin(text,
			text_box->just, &(text_box->x), &(text_box->y));
	XClearWindow(display, text_box->window);
	XDrawString(display, text_box->window, default_GC,
			text_box->x, text_box->y,
			text_box->text, strlen(text_box->text));
	}

GraphWindow *x_graphics_window(parent, x, y, width, height,
	tile_data, tile_width, tile_height, function, arg, border_width)
  Window parent;		/* parent of this window */
  int x, y;			/* coordinates within parent window */
  int width, height;		/* dimensions of this window */
  char *tile_data;		/* bitmap to be used as background tile */
  int tile_width, tile_height;	/* dimensions of background bitmap */
  void (*function)(int);	/* callback (when button pressed) */
  int arg;			/* argument passed to callback */
  int border_width;		/* width of border in pixels */
	{
	GraphWindow *gw;
	Pixmap pmap;

	gw = (GraphWindow *) malloc(sizeof(GraphWindow));
	gw->next = graph_window_list;
	graph_window_list = gw;
	if (border_width < 0) border_width = GRAPH_BORDER; 
	x -= border_width;
	y -= border_width;
	gw->window = XCreateSimpleWindow(display, parent,
			x, y, width, height, border_width, black, white);
	XSelectInput(display, gw->window, ExposureMask | 
					  ButtonPressMask | ButtonReleaseMask);
	gw->gc = XCreateGC(display, gw->window, 0, NULL);
	XCopyGC(display, default_GC, 0x7fffffL, gw->gc);
	gw->function = function;
	gw->arg = arg;
	gw->list = NULL;
	if (tile_data != NULL)
		{
		pmap = XCreatePixmapFromBitmapData(display, parent,
			tile_data, tile_width, tile_height, black, white,
			default_depth);
		XSetWindowBackgroundPixmap(display, gw->window, pmap);
		}
	XMapWindow(display, gw->window);
	return gw;
	}

void x_draw_text(gw, text, x, y, just)
  GraphWindow *gw;	/* Graphics window to draw points in */
  char *text;		/* Null-terminated character string */
  int x, y;		/* Location at which to draw text */
  Justification just;	/* Justification relative to x,y */
	{
	DisplayItem *d_item;
	/*
	  Create a new display item
	*/
	d_item = (DisplayItem *) malloc(sizeof(DisplayItem));
	d_item->type = Text;
	d_item->style = (int) just;
	d_item->size = strlen(text);
	/*
	  Compute the correct coordinates for the justified text
	*/
	adjust_text_origin(text, d_item->style, &x, &y);
	d_item->x = x;
	d_item->y = y;
	/*
	  Link it into the list for this graphics window
	*/
	d_item->next = gw->list;
	gw->list = d_item;
	/*
	  Allocate storage for the text string
	*/
	d_item->data.text = (char *) calloc(d_item->size + 1, sizeof(char));
	/*
	  and copy it into the newly allocated memory
	*/
	strcpy(d_item->data.text, text);
	draw_item(gw, d_item);
	}

void x_draw_points(gw, points, n_points, style)
  GraphWindow *gw;	/* Graphics window to draw points in */
  XPoint *points;	/* Array of short x,y pairs */
  int n_points;		/* length of data array */
  PointStyle style;	/* Connected or Disconnected */
	{
	DisplayItem *d_item;
	register XPoint *dest_data;
	register int i;
	/*
	  Create a new display item
	*/
	d_item = (DisplayItem *) malloc(sizeof(DisplayItem));
	d_item->type = Points;
	d_item->style = (int) style;
	d_item->size = n_points;
	/*
	  Link it into the list for this graphics window
	*/
	d_item->next = gw->list;
	gw->list = d_item;
	/*
	  Allocate storage for the data array
	*/
	d_item->data.points = (XPoint *) calloc(n_points, sizeof(XPoint));
	/*
	  and copy the data into the newly allocated memory
	*/
	dest_data = d_item->data.points;
	for (i=0; i<n_points; i++)
		*dest_data++ = *points++;
	draw_item(gw, d_item);
	}

static void draw_item(gw, dp)
  GraphWindow *gw;
  DisplayItem *dp;
  	{
	switch (dp->type)
	  {
	  case Points:
	     switch (dp->style)
		{
		case Connected:
			XDrawLines(display, gw->window,
				gw->gc, dp->data.points, dp->size,
				CoordModeOrigin);
			break;
		case Disconnected:
			XDrawPoints(display, gw->window, gw->gc,
				dp->data.points, dp->size, CoordModeOrigin);
			break;
		default:
			fprintf(stderr,
				"draw_item: unknown drawing style\n");
		}
	     break;
	  case Text:
	     XDrawString(display, gw->window, gw->gc, dp->x, dp->y,
			dp->data.text, dp->size);
	     break;
	  default:
	     fprintf(stderr, "draw_item: unknown display item type\n");
	  }
	}

void x_erase_graph(gw)
  GraphWindow *gw;	/* Graphics window to erase */
  	{
	DisplayItem *d_list, *d_next;
	d_list = gw->list;
	gw->list = NULL;
	while (d_list != NULL)
		{
		d_next = d_list->next;
		if (d_list->data.text != NULL)
			free(d_list->data.text);
		free(d_list);
		d_list = d_next;
		}
	XClearWindow(display, gw->window);
	}

void x_event_loop()
	{
	XEvent event;
	Button *b = NULL;
	TextBox *t = NULL;
	EditBox *e = NULL;
	GraphWindow *g = NULL;
	DisplayItem *d = NULL;
	MenuBar *mb = NULL;
	static int menu_but = 0;

	while (1)
		{
		XNextEvent(display, &event);
		switch (event.type)
			{

			case EnterNotify:
				for (b = button_list; b != NULL; b = b->next)
				  {
				  if (b->window == event.xcrossing.window &&
				      !(b->flags & DISABLED))
				    { 
				    for (mb = menubar_list; mb != NULL;
							mb = mb->next)
				      if (parent_of(b->window) == mb->window &&
					  event.xcrossing.state & (Button1Mask |
						   		   Button2Mask |
								   Button3Mask)
					  && activemenu != NULL &&
				          parent_of(activemenu->caller->window)
							== mb->window &&
					  b->arg != activemenu->menu_id) 
				      	      {
					      x_unhighlight_button(activemenu->
								   caller);
				       	      pullup(activemenu);
				   	      x_highlight_button(b);	
				       	      pulldown(b->arg);	
					      break;
					      }
				    XSetWindowBorderPixmap(display,
					event.xcrossing.window,
					CopyFromParent);
				    if (b->pulldown)
				    	x_highlight_button(b);
				    break;
				    }
				  }
				break;
			case LeaveNotify:
				for (b = button_list; b != NULL; b = b->next)
				  {
				  if (b->window == event.xcrossing.window &&
				      !(b->flags & DISABLED))
				    { 
				    XSetWindowBorderPixmap(display,
					event.xcrossing.window,
					grey_pixmap);
				    if (b->pulldown)
				    	x_unhighlight_button(b);
				    break;
				    }
				  }
				break;
			case ButtonRelease:
				if (activemenu != NULL)
					{
					if (event.xbutton.button != menu_but)
					  break;
				        XUngrabPointer(display, 
						event.xbutton.time);	
					x_unhighlight_button(activemenu->caller);
					if (parent_of(event.xbutton.window) !=
						activemenu->window)
						{
						pullup(activemenu);
						break; 
						}
					pullup(activemenu);
					}
				else 	
					break;
			case ButtonPress:
				x_coord = event.xbutton.x;
				y_coord = event.xbutton.y;
				button = event.xbutton.button;
				if (active_edit_box != NULL &&
				    event.xbutton.window ==
				    active_edit_box->window)
					{
					position_marker(x_coord);
					break;
					}
				for (b = button_list; b != NULL; b = b->next)
					{
					if (b->window == event.xbutton.window &&
				            !(b->flags & DISABLED))
						{ 
						if (activemenu == NULL)
						{
				    		for (mb = menubar_list;
						     mb != NULL; mb = mb->next)
				      		   if (parent_of(b->window) == 
						       mb->window)
						       {
						       x_highlight_button(b);	
						       XGrabPointer(display,
							  toplevel, True,
							  ButtonReleaseMask,
							  GrabModeAsync,
							  GrabModeAsync,
							  None, None,
							  event.xbutton.time);
						       menu_but = button;
						       }
						(*(b->function))(b->arg);
						}
						break;
						}
					}
				if (b == NULL)
				for (g = graph_window_list; g != NULL;
								g = g->next)
					{
					if (g->window == event.xbutton.window)
						{
						if (g->function != NULL)
						  (*(g->function))(g->arg);
						break;
						}
					}
				break;
			case KeyPress:
				handle_key_press((XKeyEvent *) &event);
				break;
			case Expose:
				if (event.xexpose.count == 0)
					{
					for (t = text_box_list;
					     t != NULL; t = t->next)
						{
						if (t->window ==
						    event.xexpose.window)
						    {
						    XDrawString(display,
							t->window,
							default_GC, t->x, t->y,
							t->text,
							strlen(t->text));
						    break;
						    }
						}
					if (t == NULL)
					  for (e = edit_box_list;
					     e != NULL; e = e->next)
						{
						if (e->window ==
						    event.xexpose.window)
						    {
						    XClearWindow(display,
							e->window);
						    XDrawString(display,
							e->window,
							default_GC, e->x, e->y,
							e->text,
							strlen(e->text));
						    if (e == active_edit_box)
							put_marker();
						    break;
						    }
						}
					if (e == NULL)
					   for (g = graph_window_list;
						g != NULL; g = g->next)
					     	{
						if (g->window ==
						    event.xexpose.window)
						    {
						    for (d = g->list;
						         d != NULL;
							 d = d->next)
							   draw_item(g, d);
						    break;
						    }
						}
					}
				break;
			}
		}
	}

static Button *register_button(w, function, arg, pmap, disable_pmap, width, height)
  Window w;		/* button window */
  void (*function)();	/* callback */
  int arg;		/* argument passed to callback */
  Pixmap pmap;		/* background pixmap for this button */
  Pixmap disable_pmap;
  int width, height;	/* size of this button */
	{
	Button *new_button;

	new_button = (Button *) malloc(sizeof(Button));
	new_button->next = button_list;
	new_button->window = w;
	new_button->function = function;
	new_button->arg = arg;
	new_button->pixmap = pmap;
	new_button->disable_pmap = disable_pmap;
	new_button->width = width;
	new_button->height = height;
	new_button->pulldown = pulldown_but;
	new_button->flags = 0;	/* enabled, not highlighted */
	button_list = new_button;
	XSetWindowBorderPixmap(display, w, grey_pixmap);
	if (pulldown_but)
		XSelectInput(display, w, ButtonReleaseMask |
	 			   EnterWindowMask |
	 			   LeaveWindowMask); 
	else
		XSelectInput(display, w, ButtonPressMask |
				   ButtonReleaseMask |
	 			   EnterWindowMask |
	 			   LeaveWindowMask); 
	return new_button;
	}

static Pixmap mk_labeled_pixmap(text, width, height, gc)
  char *text;		/* text to be displayed in the label window */
  int *width, *height;	/* width and height (may be increased if necessary) */
  GC gc;
	{
	Pixmap pmap;	/* background pixmap which contains the text */
	int t_width, t_height, t_ascent, x, y;

	find_bbox(text, &t_width, &t_height, &t_ascent);

	t_height += 2*PADDING;
	t_width += 2*PADDING;
	if (t_height > *height) *height = t_height;
	if (t_width > *width) *width = t_width;
	x = *width/2;
	y = *height/2;
	adjust_text_origin(text, Centered, &x, &y);
	
	pmap = XCreatePixmap(display, DefaultRootWindow(display),
		*width, *height, default_depth);
	XFillRectangle(display, pmap, eraser_GC, 0, 0, *width, *height);
	XDrawString(display, pmap, gc, x, y, text, strlen(text));
	return pmap;
	}

static void find_bbox(text, width, height, ascent)
  char *text;	/* the text string */
  int *width;	/* width of the text string in pixels */
  int *height;	/* height of the text string in pixels */
  int *ascent;	/* ascent of the text above the baseline */
	{
	XCharStruct info;
	int dir, asc, dsc, n_chars;

	n_chars = strlen(text);
	XQueryTextExtents(display, XGContextFromGC(default_GC),
		text, n_chars, &dir, &asc, &dsc, &info);
	*width = info.width;
	*height = asc + dsc;
	*ascent = asc;
	}

static void adjust_text_origin(text, just, x, y)
  char *text;		/* text to be justified */
  Justification just;	/* justification (compass point or center) */
  int *x, *y;		/* adjusted and returned to caller */
	{
	int width, height, ascent;

	find_bbox(text, &width, &height, &ascent);
	/*
	  compute horizontal offset and add to *x
	*/
	switch (just)
		{
		case Left: case NWest: case West: case SWest:
			/* No x ajustment necessary */
			break;
		case North: case South: case Centered:
			*x -= width/2;
			break;
		case Right: case NEast: case East: case SEast:
			*x -= width;
			break;
		}
	/*
	  compute vertical offset and add to *y
	*/
	switch (just)
		{
		case NWest: case North: case NEast:
			*y += ascent;
			break;
		case Right: case Left: case West: case Centered: case East:
			*y += ascent - height/2;
			break;
		case SWest: case South: case SEast:
			/* No y adjustment necessary */
			break;
		}
	}

/*
	MIE FUNZIONI !!!!!!!!
*/

/*
	Destroy a icon

	first the objects common to all BITMAPS and PIXMAPS, then PIXMAP
	specific
*/

void Destroy_icona(Programma *p)
{
	XDestroyWindow(display,p->window);

	if (p->icona.pixmap) XFreePixmap(display, p->icona.pixmap);

	if (p->type == PIXMAP)
	{
		XFreeColors(display, p->icona.attributes.colormap,
			p->icona.attributes.pixels, p->icona.attributes.npixels, 0);

		if (p->icona.mask) XFreePixmap(display, p->icona.mask);

		XpmFreeAttributes(&p->icona.attributes);
	}
}

/*
	Read a icon from file (PIXMAP or BITMAP)

	if all else fails, try the default
*/

void Read_icona(parent, iconFile, p)
Window parent;        /* window in which the label will be created */
char *iconFile;       /* picture to be displayed in the label window */
Programma *p;
{
	int ErrorStatus;
	char *error = NULL;
	char *warning = NULL;
	Window button_w;
	Pixmap tmp,pmap;
	int x, y;
	unsigned int w, h;

	if (strstr(iconFile,".xpm"))
	{
		ErrorStatus = XpmReadFileToPixmap(display, parent, iconFile,
			&(p->icona.pixmap), &(p->icona.mask), &(p->icona.attributes));

		p->type=PIXMAP;
		p->icona.attributes.colormap=DefaultColormap(display, DefaultScreen(display));
		p->width = p->icona.attributes.width;
		p->height = p->icona.attributes.height;

		switch (ErrorStatus)
		{
			case XpmSuccess:		break;
			case XpmColorError:		warning = "Could not parse or alloc requested color"; break;
			case XpmOpenFailed:		error = "Cannot open file"; break;
			case XpmFileInvalid:	error = "Invalid XPM file"; break;
			case XpmNoMemory:		error = "Not enough memory"; break;
			case XpmColorFailed:	error = "Failed to parse or alloc some color"; break;
			default:				error = "Cannot read pixmap"; break;
		}
	}
	else
	{
		tmp=(Pixmap) NULL;
		p->width=16;
		p->height=16;

		ErrorStatus = XReadBitmapFile (display, parent, iconFile,
			&(p->width), &(p->height), &tmp, &x, &y);

		p->icona.pixmap=XCreatePixmap(display, parent, p->width, p->height,
			default_depth);

		XCopyPlane(display,tmp, p->icona.pixmap,
			XDefaultGC(display, default_screen),
			default_screen, default_screen, p->width, p->height, 0, 0, 1);

		p->type=BITMAP;
		p->icona.attributes.colormap=DefaultColormap(display, DefaultScreen(display));

		switch(ErrorStatus)
		{
			case BitmapSuccess:	break;
/*
			case BitmapSuccess:

								tmp=(Pixmap) NULL;
								w=16; h=16;

printf("0"); fflush(stdout);
								if (XReadBitmapFile (display, parent, iconFile,
									&w, &h,
									&tmp, &x, &y) == BitmapSuccess)
								{
									pmap=XCreatePixmap(display, parent, w, h,
										default_depth);

printf("1"); fflush(stdout);

									if (!pmap)
									{
										error="Cannot allocate Pixmap for the bitmap";
									}

									XCopyPlane(display,tmp,pmap,XDefaultGC(display, default_screen),
										default_screen, default_screen,w,h,0,0,1);

printf("2"); fflush(stdout);
									button_w = XCreateSimpleWindow(display, parent, 10, 10, 20+p->width , 20+p->height, 0, black, white);
									p->window=button_w;
									XSetWindowBackgroundPixmap(display, button_w, pmap);
									XMapWindow(display, button_w);
printf("3"); fflush(stdout);
								}
								else
								{
									printf("eh eh eh\n");
								}
*/
								break;

			case BitmapOpenFailed:	error = "Cannot open file"; break;
			case BitmapFileInvalid:	error = "Invalid XBM file"; break;
			case BitmapNoMemory:	error = "Not enough memory"; break;
			default:				error = "Cannot read bitmap"; break;
		}
	}

	if (warning)
	{
		fprintf(stderr,"WARNING: icon file \"%s\": %s\n",iconFile,warning);
		return;
	}

	if (error)
	{
		fprintf(stderr,"ERROR: icon file \"%s\": %s\n",iconFile,error);

		if (p->icona.pixmap) XFreePixmap(display, p->icona.pixmap);

		if (p->type == PIXMAP)
		{
			XFreeColors(display, p->icona.attributes.colormap,
				p->icona.attributes.pixels, p->icona.attributes.npixels, 0);

			if (p->icona.mask) XFreePixmap(display, p->icona.mask);

			XpmFreeAttributes(&p->icona.attributes);
		}

		if (strstr(DEFAULTICON,".xpm"))
		{
			ErrorStatus = XpmReadFileToPixmap(display, parent, DEFAULTICON,
				&(p->icona.pixmap), &(p->icona.mask), &(p->icona.attributes));

			p->type=PIXMAP;
			p->icona.attributes.colormap=DefaultColormap(display, DefaultScreen(display));
			p->width = p->icona.attributes.width;
			p->height = p->icona.attributes.height;
		}
		else
		{
			ErrorStatus = XReadBitmapFile (display, parent, DEFAULTICON,
				&(p->width), &(p->height), &(p->icona.pixmap), &x, &y);

			p->type=BITMAP;
			p->icona.attributes.colormap=DefaultColormap(display, DefaultScreen(display));

		}

		if (ErrorStatus)
		{
			fprintf(stderr,"Fatal error: cannot read icon %s or default icon\n",iconFile);
			exit(1);
		}
		return;
	}
}

/*
	print a label, but without border
*/

Window x_label_noborder(parent, text, x, y, width, height)
  Window parent;	/* window in which the label will be created */
  char *text;		/* text to be displayed in the label window */
  int x, y;		/* coordinates within toplevel */
  int width, height;	/* width and height */
	{
	Pixmap pmap;
	Window label_w;

	pmap = mk_labeled_pixmap(text, &width, &height, default_GC);
	label_w = XCreateSimpleWindow(display, parent,
			x, y, width, height, 0, black, white);
	XSetWindowBackgroundPixmap(display, label_w, pmap);
	XMapWindow(display, label_w);

	return label_w;
	}

/*
	paint a icon
*/

void Paint_icona( Window parent, Programma *p, void (*function)(int), int arg)
{
	Window button_w;
	int h,w,x,y;

	w=WICONA; if (p->width < WICONA) w=p->width;
	h=HICONA; if (p->height < HICONA) h=p->height;

	x=p->x;
	y=p->y;

	x += (WICONA - w)/2;
	y += HICONA - h;

	button_w = XCreateSimpleWindow(display, parent, x, y, w, h, 0, black, white);
	p->window=button_w;
	p->selected=0;
	XSetWindowBackgroundPixmap(display, button_w, p->icona.pixmap);
	XMapWindow(display, button_w);

	if(strcmp(p->nome,"")!=0)
	{
		x_label_noborder(parent, p->nome, p->x, p->y+HICONA+3, WICONA, 8);

		register_button(button_w, function, arg,
			p->icona.pixmap, p->icona.pixmap,
			w, h);
	}
}

/* seleziona un' icona */

void Select_icona(Window parent, Programma *p)
{
	p->selected=1;
	XSetWindowBorderWidth(display, p->window, 2);
	XClearWindow(display, p->window);
}

/* deseleziona un' icona */

void UnSelect_icona(Window parent, Programma *p)
{
	p->selected=0;
	XSetWindowBorderWidth(display, p->window, 0);
	XClearWindow(display, p->window);
}

/*
	dormi per n microsecondi
*/

void sleep_a_little(int n)
{
	struct timeval value;
  
	if (n <= 0) return;
  
	value.tv_usec = n % 1000000;
	value.tv_sec = n / 1000000;
  
	(void) select(1, 0, 0, 0, &value);
}

/*
	ritorna se ho premuto click o doppio click
*/

int DoubleClick(void)
{
	XEvent d;
	int ClickTime=150;
	int r;

	r=1;

	sleep_a_little(ClickTime*1000);
	if(XCheckMaskEvent (display,ButtonReleaseMask, &d))
	{
		/* printf("CLICK\n"); */

		sleep_a_little(ClickTime*1000);
		if(XCheckMaskEvent (display,ButtonPressMask, &d))
		{
			/* printf("CLICK e 1/2\n"); */
			r=1;

			sleep_a_little(ClickTime*1000);
			if(XCheckMaskEvent (display,ButtonReleaseMask, &d))
			{
				/* printf("DOUBLE CLICK\n"); */
				r=2;
			}
		}
	}

	return(r);
}

