/****************************************************************************/
/*   xmDrawing.C                                                            */
/****************************************************************************/
/*                                                                          */
/*   Copyright (c) 1992, 1993 Bernhard Strassl                              */
/*       Vienna User Interface Group                                        */
/*       Institute for Applied Computer Science and Information Systems     */
/*       University of Vienna, Austria                                      */
/*                                                                          */
/*   See file COPYRIGHT in this directory for details. If this file is      */
/*   missing please mail to xmpp@ani.univie.ac.at                           */
/****************************************************************************/

#include <stdarg.h>
#include <stream.h>
#include <strstream.h>
#include "xmDrawing.h"

/*
#ifndef HP
#undef cb
#endif
*/

extern "C"
{
void exit(int);
int abs(int);
}

#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))

Colormap cmap;
unsigned long cvals[GPR_MAX_COLORS];
Pixmap lightGrayMap = (Pixmap )NULL;
Pixmap darkGrayMap = (Pixmap )NULL;

gprPoint sysCursorLoc;

void initColors();
void initGrayPixmaps();

int dataRefCount = 0;

gprData* newGprData()
{
	gprData* data = new gprData;

	data->shape = GPR_DOT;
	data->linewidth = 0;
	data->linestyle.x = 100;	// solid line
	data->linestyle.y = 0;
	data->lineColor = GPR_BLACK;
	data->fillColor = GPR_WHITE;
	data->linePattern = GPR_SOLID;
	data->fillPattern = GPR_NONE;
	data->numdesc = 0;
	data->descr = NULL;
	data->text = NULL;
	data->fontName = NULL;
	data->hTextAlign = GPR_CENTER;
	data->vTextAlign = GPR_CENTER;
	data->drawInfo = NULL;

	dataRefCount++;

	return(data);
}

gprData* duplicateGprData(gprData* src, gprData* dest)
{
	*dest = *src;
	dest->descr = new gprPoint[src->numdesc];	// + 2];  strange behavior of the v2.0 compiler...
	for(int i = 0; i < src->numdesc; i++)
		dest->descr[i] = src->descr[i];
	dest->text = dest->fontName = NULL;
	if(src->text)
	{	dest->text = strcpy(new char[strlen(src->text) + 1], src->text);
		dest->fontName = strcpy(new char[strlen(src->fontName) + 1], src->fontName);
	}
	dest->drawInfo = NULL;
	if(src->drawInfo)
	{	dest->drawInfo = new gprDrawInfo;
		*(dest->drawInfo) = *(src->drawInfo);
	}
	return(dest);
}

void copyGprDataDescr(gprData* src, gprData* dest)
{
	for(int i = 0; i < src->numdesc; i++)
		dest->descr[i] = src->descr[i];
}

void freeGprData(gprData* src)
{
	delete /* [src->numdesc] ??? */ src->descr;
	delete src->text;
	delete src->fontName;
	delete src->drawInfo;
	delete src;

	dataRefCount--;
}

// drawing primitive classes ////////////////////////////////////////////////////

DrawPrim::DrawPrim(gprShape s, int lw, gprColor c, gprFill f)
{
	data = newGprData();
	data->shape = s;
	data->linewidth = lw;
	data->linestyle.x = 100;	// solid line
	data->linestyle.y = 0;
	data->lineColor = c;
	data->fillColor = GPR_WHITE;
	data->linePattern = GPR_SOLID;
	data->fillPattern = f;
	data->numdesc = 0;
	data->descr = NULL;
	data->text = NULL;
	data->fontName = NULL;
	data->hTextAlign = GPR_CENTER;
	data->vTextAlign = GPR_CENTER;
	data->drawInfo = NULL;

	disp = NULL;
}

DrawPrim::DrawPrim(gprData* d)
{
	data = d;
	disp = NULL;
}

DrawPrim::~DrawPrim()
{
	if(disp && disp->remove(this))
		disp->refresh();

	if(data)
		freeGprData(data);
}

void DrawPrim::copyData(gprData* tmpdata)
{
	duplicateGprData(data, tmpdata);
}

void DrawPrim::changeFlags(unsigned int f, bool sr)
{
	if(disp)
		disp->changeFlags(disp->findPrim(this), f, sr);
	else
		cerr << "class DrawPrim: changing manipulation flags before adding to drawing is illegal (ignored)\n";
}

DrawPrim* DrawPrim::addPoint(int x, int y)
{
	if(!data->descr)
		data->descr = new gprPoint[maxpoints()];

	if(data->numdesc < maxpoints())
	{	data->descr[data->numdesc].x = x;
		data->descr[data->numdesc].y = y;
	}
	data->numdesc++;
	return(this);
}

DrawPrim* DrawPrim::setPoints(int pc,...)
{
	int i = 0, pointcount = (pc < maxpoints()) ? pc : maxpoints();
	va_list ap;

	if(!data->descr)
		data->descr = new gprPoint[maxpoints()];

	va_start(ap, pc);
	while(i < pointcount)
	{	data->descr[i].x = va_arg(ap, int);
		data->descr[i].y = va_arg(ap, int);
		i++;
	}
	va_end(ap);
	data->numdesc = i;
	return(this);
}

DrawPrim* DrawPrim::setPoint(int pndx, int nx, int ny)
{
	if(!data || !data->descr || pndx >= data->numdesc)
		return(this);

	data->descr[pndx].x = nx;
	data->descr[pndx].y = ny;
	return(this);
}

DrawPrim* DrawPrim::setOrigin(int x, int y)
{
	data->descr[0].x = x;
	data->descr[0].y = y;
	return(this);
}

DrawPrim* DrawPrim::setExtent(int x, int y)
{
	if(data->shape != GPR_PLINE && data->numdesc > 1)
	{	data->descr[1].x = x;
		data->descr[1].y = y;
	}
	else
		cerr << "class DrawPrim: ignoring invalid operation for this primitive...\n";
	return(this);
}

DrawPrim* DrawPrim::setCorner(int x, int y)
{
	if(data->shape != GPR_PLINE && data->numdesc > 1)
	{	data->descr[1].x = max(x - data->descr[0].x, 0);
		data->descr[1].y = max(y - data->descr[0].y, 0);
	}
	else
		cerr << "class DrawPrim: ignoring invalid operation for this primitive...\n";
	return(this);
}

DrawPrim* DrawPrim::setText(char* t, char* f)
{
	delete data->text;
	data->text = strcpy(new char[strlen(t) + 1], t);
	if(f)
		return(setFont(f));
	return(this);
}

DrawPrim* DrawPrim::setFont(char* f)
{
	delete data->fontName;
	data->fontName = strcpy(new char[strlen(f) + 1], f);
	return(this);
}

int DrawPrim::maxpoints()
{
	return(2);	// default
}

bool DrawPrim::containsPoint(gprPoint p)
{
	switch(data->shape)
	{	case GPR_DOT:
		return((bool )(data->descr[0].x == p.x && data->descr[0].y == p.y));

        case GPR_PLINE:
		case GPR_POLYGON:
		return(FALSE);			// not implemented yet...!

        case GPR_RECT:
		case GPR_CIRCLE:
        case GPR_ELLIPSE:		// not correct yet...!
		case GPR_TEXT:
		{	gprPoint or = origin(), co = corner();
			return((bool )(or.x <= p.x && or.y <= p.y && co.x >= p.x && co.y >= p.y));
		}
	}
#ifdef AIX
	return(FALSE);	// it seems that xlC doesn't check enums...
#endif
}

bool DrawPrim::hide()
{
	return(disp ? (disp->remove(this) ? TRUE : FALSE) : FALSE);
}

bool DrawPrim::show()
{
	return(disp ? (disp->findPrim(this) == -1 ? (disp->add(this) ? TRUE : FALSE) : FALSE) : FALSE);
}

bool DrawPrim::update()
{
	if(disp)
	{	disp->updatePrim(this);
		return(TRUE);
	}
	return(FALSE);
}

void DrawPrim::dumpOn(ostream& out)
{
	out << "drawing primitive #" << data->shape << " lw " << data->linewidth <<
			" lst " << data->linestyle.x << "," << data->linestyle.y <<
			" color " << data->lineColor << " fill " << data->fillPattern << "\n" << data->numdesc << " points: ";
	for(int i = 0; i < data->numdesc; i++)
		out << data->descr[i].x << "@" << data->descr[i].y << " ";
	out << ".\n";
	if(data->text)
		out << "text: " << data->text << " font: " << data->fontName << "\n";
	else
		out << "no text.\n";
	if(data->drawInfo)
		out << "draw info: flags " << data->drawInfo->flags << " status " << data->drawInfo->status << " index " << data->drawInfo->index << "\n";
	else
		out << "no draw info.\n";
}

Dot::Dot(int x, int y, int lw, gprColor c, gprFill f) : DrawPrim(GPR_DOT, lw, c, f)
{
	setPoints(1, x, y);
}


PolyLine::PolyLine(int lw, gprColor c, gprFill f) : DrawPrim(GPR_PLINE, lw, c, f)
{
}

int PolyLine::maxpoints()
{
	return(10);
}

Line::Line(int x1, int y1, int x2, int y2, int lw, gprColor c, gprFill f) : PolyLine(lw, c, f)
{
#ifndef NO_VARARGS
	setPoints(2, x1, y1, x2, y2);
#else
	addPoint(x1, y1);
	addPoint(x2, y2);
#endif
}

int Line::maxpoints()
{
	return(2);
}

Polygon::Polygon(int lw, gprColor c, gprFill f) : DrawPrim(GPR_POLYGON, lw, c, f)
{
}

int Polygon::maxpoints()
{
	return(10);
}

Rectangle::Rectangle(int x, int y, int w, int h, int lw, gprColor c, gprFill f) : DrawPrim(GPR_RECT, lw, c, f)
{
#ifndef NO_VARARGS
	setPoints(2, x, y, w, h);
#else
	addPoint(x, y);
	addPoint(w, h);
#endif
	data->hTextAlign = GPR_BEGINNING;
}

Circle::Circle(int x, int y, int r, int lw, gprColor c, gprFill f) : DrawPrim(GPR_CIRCLE, lw, c, f)
{
#ifndef NO_VARARGS
	setPoints(2, x, y, r, 0);
#else
	addPoint(x, y);
	addPoint(r, 0);
#endif
}

Ellipse::Ellipse(int x, int y, int r1, int r2, int lw, gprColor c, gprFill f) : DrawPrim(GPR_ELLIPSE, lw, c, f)
{
#ifndef NO_VARARGS
	setPoints(2, x, y, r1, r2);
#else
	addPoint(x, y);
	addPoint(r1, r2);
#endif
}

Text::Text(int x, int y, char* txt, char* fn, int w, int h, gprColor c) : DrawPrim(GPR_TEXT, 0, c, GPR_NONE)
{
#ifndef NO_VARARGS
	setPoints(2, x, y, w, h);
#else
	addPoint(x, y);
	addPoint(w, h);
#endif
	setText(txt);
	if(fn)
		setFont(fn);
}

// Drawing object class /////////////////////////////////////////////////////////////

XmDrawing::XmDrawing(char* name, int x, int y, int w, int h, xmStyle s) : XmControl(name, x, y, w, h, s)
{
	primCount = dragCount = 0;
	buttonDown = primLock = wasOverriding = FALSE;
	desigPrim = prSelection = curLinePt = -1;
	desigOp = 0;
	moveGrid = resizeGrid = 0;
	defaultFont = "fixed";
	defaultFlags = RESIZE_ANY;
	curFont = NULL;
	curFontInfo = NULL;
	valmask = 0;
	gc = dgc = NULL;
	dragTarget = -2;
	dragControl = NULL;
	for(int i = 0; i < MAX_DRAWING_PRIMS; i++)
		selDepend[i] = -1;
}

XmDrawing::~XmDrawing()
{
	for(int i = 0; i < primCount; i++)
		delete drawInfo[i];
}

#ifdef XAW
extern "C" {
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/Simple.h>
}
#else
#include <X11/Xm/ScrolledW.h>
#include <X11/Xm/DrawingA.h>
#endif


#ifdef XAW

void awHandler(Widget, XtPointer rec, XEvent * evt, Boolean* continue_dispatch)
{
	if(evt->type == Expose)
		((XmDrawing* )rec)->redraw(evt);
	else if(evt->type == MotionNotify)
	{	if(((XmDrawing* )rec)->dragCount > 0)
			((XmDrawing* )rec)->drag(evt);
	}
	else
		((XmDrawing* )rec)->mouseClick(evt);
	*continue_dispatch = TRUE;
}

#define InputMask (PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask)

#endif

bool XmDrawing::createControl(XmControlPane* par)
{
	Widget scrolledArea;
#ifdef XAW
	if(style & XmSautohscroll || style & XmSautovscroll)
	{	ostrstream vpname;
		vpname << wname << "-viewport"; vpname.put('\0');
		addArg(XtNallowHoriz, TRUE);
		addArg(XtNallowVert, TRUE);
		XtManageChild(scrolledArea = XtCreateWidget(vpname.str(), viewportWidgetClass, par->handle(), wargs, wargcount));
		wargcount = 0;
		addArg(XtNwidth, 1000);
		addArg(XtNheight, 1000);
		XtManageChild(wid = XtCreateWidget(wname, simpleWidgetClass, scrolledArea, wargs, wargcount));
	}
	else
	{	addArg(XtNborderWidth, 1);
		XtManageChild(wid = XtCreateWidget(wname, simpleWidgetClass, scrolledArea, wargs, wargcount));
	}
	wargcount = 0;

	XtAddEventHandler(wid, ExposureMask, TRUE, awHandler, (XtPointer )this);
	XtAddEventHandler(wid, InputMask, TRUE, awHandler, (XtPointer )this);
#else
	if(style & XmSautohscroll || style & XmSautovscroll)
	{	addArg(XmNscrollingPolicy, XmAUTOMATIC);
		XtManageChild(scrolledArea = XmCreateScrolledWindow(par->handle(), "draw_scroll", wargs, wargcount));
		wargcount = 0;
		addArg(XtNwidth, 1000);
		addArg(XtNheight, 1000);
		XtManageChild(wid = XmCreateDrawingArea(scrolledArea, wname, wargs, wargcount));
	}
	else
	{	addArg(XmNborderWidth, 1);
		XtManageChild(wid = XmCreateDrawingArea(par->handle(), wname, wargs, wargcount));
	}
	wargcount = 0;

	setCallback(XmNexposeCallback, this, (cbProc )&XmDrawing::redraw, TRUE, CB_XM_DATA);
	setCallback(XmNresizeCallback, this, (cbProc )&XmDrawing::redraw, TRUE, CB_XM_DATA);
	setCallback(XmNinputCallback, this, (cbProc )&XmDrawing::mouseClick, TRUE, CB_XM_DATA);
#endif
	parent = par->asObject();
	return(wid ? TRUE : FALSE);
}

void XmDrawing::redraw(void*)
{
	if(!gc)
    {	if(!lightGrayMap)
		{	initColors();
			initGrayPixmaps();
		}
		if(XtWindow(wid))
		{	gc = XCreateGC(display, XtWindow(wid), valmask, &gcvals);
			valmask = 0;
		}
		else
		{	if(XtIsRealized(wid))
				cerr << "drawing: error - cannot create gc (no window for wid " << wid << ")\n";
		}
	}
	if(gc)
    {	for(int i = 0; i < primCount; i++)
		{	draw(i);
			fill(i);
			drawText(i);
			drawAttribs(i);
		}
		XFlush(display);
	}
}

XmDrawing* activeDrawing = NULL;

void sysAlarm(int)
{
	if(activeDrawing)
		activeDrawing->mouseAlarm();
}

struct itimerval old_timer;
unsigned long dclickTime = 0L;

#include <signal.h>

void XmDrawing::mouseClick(void* data)
{
#ifdef XAW
	if(data)
	{	XEvent* evt = (XEvent* )data;
#else
	XmDrawingAreaCallbackStruct* cb = (XmDrawingAreaCallbackStruct* )data;

//	cerr << "drawing internal callback...\n";
	if(cb->reason == XmCR_INPUT)
	{	// cerr << "callback seems to be valid...\n";
		XEvent* evt = cb->event;
#endif
		sysCursorLoc.x = evt->xbutton.x;
		sysCursorLoc.y = evt->xbutton.y;
		switch(evt->type)
		{	case ButtonPress:
			if(evt->xbutton.time - dclickTime < 500 && desigPrim >= 0)
			{	userActivate(prims[desigPrim], evt->xbutton.x, evt->xbutton.y);
				break;
			}
			dclickTime = evt->xbutton.time;
			if(!userClick(evt->xbutton.x, evt->xbutton.y))
				break;
			if(mouseSelect(evt->xbutton.x, evt->xbutton.y, bool(evt->xbutton.state & ShiftMask), bool(evt->xbutton.state & ControlMask)))
			{	buttonDown = TRUE;
				activeDrawing = this;
				timer.it_value.tv_sec     = 0;
				timer.it_value.tv_usec    = 500000;
				timer.it_interval.tv_sec  = 0;
				timer.it_interval.tv_usec = 0;
				setitimer(ITIMER_REAL, &timer, &old_timer);
#ifndef SUN
				signal(14 /* SIGALARM */, sysAlarm);
#else
				signal(14 /* SIGALARM */, (SIG_PF )sysAlarm);
#endif
			}
			break;

			case ButtonRelease:
			buttonDown = FALSE;
			if(dragCount)
				endDrag();
			break;

			case KeyPress:
			case KeyRelease:
			break;

			default:
			cerr << "xmDrawing: strange event of type " << evt->type << " received...\n";
		}
	}
#ifndef XAW
	else
		cerr << "xmDrawing: strange callback reason " << cb->reason << " detected...\n";
#endif
}

void XmDrawing::mouseAlarm()
{
//	cerr << "the timer expired...\n";
	if(buttonDown)
		beginDrag();
}

bool XmDrawing::mouseSelect(int x, int y, bool extend, bool override)
{
	desigPrim = -1;
	desigOp = 0;
	int foundFirst = -1, foundLast = -1;

	if(!(!override && wasOverriding))
	{	for(int i = primCount - 1; i >= 0; i--)
		{	gprDrawInfo* inf = drawInfo[i]->drawInfo;
			if(	x > inf->dimensions[0].x && x < (inf->dimensions[0].x + inf->dimensions[1].x) &&
				y > inf->dimensions[0].y && y < (inf->dimensions[0].y + inf->dimensions[1].y) &&
				inf->flags & SELECT)
			{	if(override && prSelection != -1)
				{	if(foundFirst == -1)
						foundFirst = i;
					if(foundLast != prSelection)
					{	foundLast = i;
						continue;
					}
				}
				desigPrim = i;
				break;
			}
		}
		if(desigPrim == -1 && foundFirst != -1)
			desigPrim = foundFirst;
		wasOverriding = override;
	}
	else
	{	gprDrawInfo* inf = drawInfo[prSelection]->drawInfo;
		if(	x > inf->dimensions[0].x && x < (inf->dimensions[0].x + inf->dimensions[1].x) &&
			y > inf->dimensions[0].y && y < (inf->dimensions[0].y + inf->dimensions[1].y) &&
			inf->flags & SELECT)
			desigPrim = prSelection;
		wasOverriding = FALSE;
	}

	if(desigPrim != -1)
	{	gprDrawInfo* inf = drawInfo[desigPrim]->drawInfo;
		unsigned int tflag = 2;
		for(int j = 0; j < 8; j++)
		{	if(inf->flags & tflag)
			{	if(	x > inf->hotspots[j].x && x < (inf->hotspots[j].x + MARKER_SIZE) &&
					y > inf->hotspots[j].y && y < (inf->hotspots[j].y + MARKER_SIZE))
				{	desigOp = tflag;
					break;
				}
			}
			tflag <<= 1;
		}
		if(!desigOp)
			desigOp = MOVE;
	}
	else
	{	if(!extend && userSelect(NULL, x, y))
			clearSelections();
		return(FALSE);
	}

	if(!extend)
	{	for(int ndx = 0; ndx < primCount; ndx++)
		{	if(ndx != desigPrim && !checkSelDepend(ndx, desigPrim))
				changeStatus(ndx, SELECTED, FALSE, FALSE);
		}
	}
	if(!getStatus(desigPrim, SELECTED))
	{	if(!userSelect(prims[desigPrim], x, y))
			return(FALSE);
	}
	if(prSelection != -1 && extend)
	{	int lastPrimary = prSelection;
		changeStatus(prSelection, PRIMARY_SELECTION, FALSE, FALSE);
		changeStatus(lastPrimary, SELECTED, TRUE, FALSE);
	}
	changeStatus(desigPrim, PRIMARY_SELECTION, TRUE);
	return(TRUE);
}

bool XmDrawing::checkSelDepend(int aPrimIndex, int newSelRoot)
{
	int dependingOn = selDepend[aPrimIndex];

	if(dependingOn != -1)
	{	if(dependingOn == newSelRoot)
			return(TRUE);
		return(checkSelDepend(dependingOn, newSelRoot));
	}
	return(FALSE);
}

void XmDrawing::alignToGrid(int op, gprData* data)
{
	if(op == MOVE && moveGrid)
	{	int diffX = data->descr[0].x % moveGrid;
		int diffY = data->descr[0].y % moveGrid;
		data->descr[0].x += (diffX > moveGrid / 2 ? moveGrid - diffX : -diffX);
		data->descr[0].y += (diffY > moveGrid / 2 ? moveGrid - diffY : -diffY);
	}
	else if(resizeGrid)
		; // not implemented...
}

void sysMouseMove(Widget, XtPointer rec, XEvent* evt, Boolean* continue_dispatch)
{
	*continue_dispatch = ((XmDrawing* )rec)->drag(evt) ? FALSE : TRUE;
}

void XmDrawing::beginDrag()
{
	if(!dgc)
	{	XGCValues dgcvals;
		unsigned long dvalmask = GCFunction | /* GCPlaneMask | */ GCForeground | GCLineStyle | GCLineWidth | GCSubwindowMode;  

		dgcvals.function = GXinvert;    // GXxor;
		// dgcvals.plane_mask = 2;
		dgcvals.foreground = BlackPixel(display, 0);
		dgcvals.line_style = LineSolid;                    // LineOnOffDash;
		dgcvals.line_width = OUTLINE_WIDTH; 
		dgcvals.subwindow_mode = IncludeInferiors;
		dgc = XCreateGC(display, XtWindow(wid), dvalmask, &dgcvals);
	}
	switch(desigOp)
	{	case SELECT:
		case RESIZE_ANY:
		cerr << "something going wrong in class XmTest (invalid beginDrag op)...\n";
		break;

		case RESIZE_TOP_LEFT:
		case RESIZE_TOP_CENTER:
		case RESIZE_TOP_RIGHT:
		case RESIZE_CENTER_LEFT:
		case RESIZE_CENTER_RIGHT:
		case RESIZE_BOTTOM_LEFT:
		case RESIZE_BOTTOM_CENTER:
		case RESIZE_BOTTOM_RIGHT:
		dragInfo[0] = duplicateGprData(drawInfo[prSelection], newGprData());
		dragCount = 1;
		draw(0, TRUE);
		break;

		case MOVE:
		dragCount = 0;
		for(int i = 0; i < primCount; i++)
		{	if((drawInfo[i]->drawInfo)->status & SELECTED)
			{	copyGprDataDescr(drawInfo[i], prims[i]->data);		// ensure consistency...
				dragInfo[dragCount++] = duplicateGprData(drawInfo[i], newGprData());
			}
		}
		dragControl = checkDragTarget(sysCursorLoc.x, sysCursorLoc.y);
		for(int j = 0; j < dragCount; j++)
			draw(j, TRUE);
		break;
	}
#ifdef XAW
/*
	EventMask msk = XtBuildEventMask(wid) | PointerMotionMask;
	XSelectInput(App->getDisplay(), XtWindow(wid), msk);
	if(!(XtBuildEventMask(wid) & PointerMotionMask))
		cerr << "shit!!\n";
*/
#else
	XtAddEventHandler(wid, PointerMotionMask, TRUE, sysMouseMove, (XtPointer )this);
#endif
}

void dumpBools(bool* b, int n)
{
	cerr << b << "-" << ":";
	for(int i = 0; i < n; i++)
		cerr << b[i];
	cerr << ":\n";
}

bool XmDrawing::drag(XEvent* evt)
{
	bool done = FALSE;
	bool chx, chy, chw, chh;

	chx = chy = chw = chh = FALSE;

	switch(desigOp)
	{	case SELECT:
		case RESIZE_ANY:
		cerr << "something going wrong in class XmTest (invalid beginDrag op)...\n";
		done = TRUE;
		break;

		case MOVE:
		bool* oldDrags, * newDrags;
		oldDrags = dragControl;
		newDrags = checkDragTarget(evt->xmotion.x, evt->xmotion.y);
		// dumpBools(oldDrags, dragCount);
		// dumpBools(newDrags, dragCount);
		for(int j = 0; j < dragCount; j++)
		{	dragControl = oldDrags;
			draw(j, TRUE);
			for(int k = 0; k < (dragInfo[j]->shape == GPR_PLINE ? dragInfo[j]->numdesc : 1); k++)
			{	dragInfo[j]->descr[k].x += (evt->xmotion.x - sysCursorLoc.x);
				dragInfo[j]->descr[k].y += (evt->xmotion.y - sysCursorLoc.y);
			}
			dragControl = (newDrags ? newDrags : oldDrags);
			draw(j, TRUE);
		}
		if(newDrags)
		{	dragControl = newDrags;
			delete oldDrags;
		}
		else
			dragControl = oldDrags;
		done = TRUE;
		break;

		case RESIZE_TOP_LEFT:
		chx = chy = chw = chh = TRUE;
		break;
		case RESIZE_TOP_CENTER:
		chy = chh = TRUE;
		break;
		case RESIZE_TOP_RIGHT:
		chy = chw = chh = TRUE;
		break;
		case RESIZE_CENTER_LEFT:
		chx = chw = TRUE;
		break;
		case RESIZE_CENTER_RIGHT:
		chw = TRUE;
		break;
		case RESIZE_BOTTOM_LEFT:
		chx = chw = chh = TRUE;
		break;
		case RESIZE_BOTTOM_CENTER:
		chh = TRUE;
		break;
		case RESIZE_BOTTOM_RIGHT:
		chw = chh = TRUE;
		break;
	}
	if(!done)
	{	draw(0, TRUE);
		if(dragInfo[0]->shape == GPR_PLINE)
		{	if(dragInfo[0]->numdesc == 2)		// reshape single line...
			{	if(curLinePt == -1)
				{	if(desigOp == RESIZE_TOP_LEFT || desigOp == RESIZE_TOP_RIGHT)
						curLinePt = (dragInfo[0]->descr[0].y > dragInfo[0]->descr[1].y) ? 1 : 0;
					else
						curLinePt = (dragInfo[0]->descr[0].y > dragInfo[0]->descr[1].y) ? 0 : 1;
				}
				dragInfo[0]->descr[curLinePt].x += (evt->xmotion.x - sysCursorLoc.x);
				dragInfo[0]->descr[curLinePt].y += (evt->xmotion.y - sysCursorLoc.y);
			}
			else
				; // not supported yet....
		}
		else
		{	int nx = dragInfo[0]->descr[0].x;
			int ny = dragInfo[0]->descr[0].y;
			int nw = dragInfo[0]->descr[1].x;
			int nh = dragInfo[0]->descr[1].y;
			int delta_x = evt->xmotion.x - sysCursorLoc.x;
			int delta_y = evt->xmotion.y - sysCursorLoc.y;
			if(chx)
				nx += delta_x;
			if(chy)
				ny += delta_y;
			if(chw)
			{	if(chx)
					nw -= delta_x;
				else
					nw += delta_x;
			}
			if(chh)
			{	if(chy)
					nh -= delta_y;
				else
					nh += delta_y;
			}
			dragInfo[0]->descr[0].x = (nw >= 0) ? nx : nx + nw;
			dragInfo[0]->descr[0].y = (nh >= 0) ? ny : ny + nh;
			dragInfo[0]->descr[1].x = abs(nw);
			dragInfo[0]->descr[1].y = abs(nh);
		}
		draw(0, TRUE);
	}
	sysCursorLoc.x = evt->xmotion.x;
	sysCursorLoc.y = evt->xmotion.y;
	return(TRUE);
}

void XmDrawing::endDrag()
{
#ifdef XAW
//	XSelectInput(App->getDisplay(), XtWindow(wid), XtBuildEventMask(wid) ^ PointerMotionMask);
#else
	XtRemoveEventHandler(wid, PointerMotionMask, TRUE, sysMouseMove, (XtPointer )this);
#endif
	for(int j = 0; j < dragCount; j++)
		draw(j, TRUE);

	if(desigOp == MOVE)
	{	primLock = TRUE;
		int dragged = 0;
		for(int i = 0; i < primCount; i++)
		{	if((drawInfo[i]->drawInfo)->status & SELECTED)
			{	alignToGrid(MOVE, dragInfo[dragged]);
				if(userMove(prims[i], dragInfo[dragged]->descr[0].x, dragInfo[dragged]->descr[0].y, sysCursorLoc.x, sysCursorLoc.y))
				{	gprData* oldInfo = drawInfo[i];
					freeGprData(prims[i]->data);
					prims[i]->data = dragInfo[dragged++];
					drawInfo[i] = drawingDataFor(prims[i], i);
					freeGprData(oldInfo);
				}
				else
				//{	freeGprData(dragInfo[i]);
				//	dragged++;
				//}
					freeGprData(dragInfo[dragged++]);
			}
		}
		if(dragged != dragCount)
			cerr << "shit!!\n";
		primLock = FALSE;
		userMove(NULL, sysCursorLoc.x, sysCursorLoc.y, 0, 0);		// signal end of list...
		dragTarget = -2;
		delete dragControl;
		dragControl = NULL;
	}
	else
	{	int dpt = 1;
		if(curLinePt != -1)
			dpt = curLinePt;
		alignToGrid(desigOp, dragInfo[0]);
		if(userResize(prims[prSelection], dragInfo[0]->descr[dpt].x, dragInfo[0]->descr[dpt].y))
		{	gprData* oldInfo = drawInfo[prSelection];
			freeGprData(prims[prSelection]->data);
			prims[prSelection]->data = dragInfo[0];
			drawInfo[prSelection] = drawingDataFor(prims[prSelection], prSelection);
			if(drawInfo[prSelection]->shape == GPR_PLINE && drawInfo[prSelection]->numdesc == 2)
				(drawInfo[prSelection]->drawInfo)->flags = defaultFlagsFor(prims[prSelection]);
			freeGprData(oldInfo);
		}
		else
			freeGprData(dragInfo[0]);
	}
	curLinePt = -1;
	dragCount = 0;
	refresh();
}

bool* XmDrawing::checkDragTarget(int x, int y)
{
	DrawPrim* draggedPrims[MAX_DRAWING_PRIMS + 1];
	int oldTarget = dragTarget, draggedCount = 0;

	dragTarget = -1;
	for(int i = primCount - 1; i >= 0; i--)
	{	if((drawInfo[i]->drawInfo)->status & SELECTED)
			draggedPrims[draggedCount++] = prims[i];
		else
		{	if(dragTarget == -1 && prims[i]->containsPoint(gprPoint(x, y)))
			{	dragTarget = i;
				if(dragTarget == oldTarget)
					return(NULL);
			}
		}
	}
	draggedPrims[draggedCount] = NULL;

	if(dragTarget != oldTarget)
	{	if(draggedCount != dragCount)
		{	cerr << "xmDrawing: Warning - internal inconsistency during drag (" << draggedCount << "/" << dragCount << ").\n";
			return(NULL);
		}
/*
		for(int j = 0; j < dragCount; j++)
			cerr << draggedPrims[j] << " ";
		cerr << "\n";
*/
		for(int i = 0; i < dragCount / 2; i++)
		{	DrawPrim* aPrim = draggedPrims[i];
			draggedPrims[i] = draggedPrims[--draggedCount];
			draggedPrims[draggedCount] = aPrim;
		}
/*
		for(j = 0; j < dragCount; j++)
			cerr << draggedPrims[j] << " ";
		cerr << "\n";
*/

		bool* newDragControl = new bool[dragCount];

		if(userDragEnter(dragTarget == -1 ? NULL : prims[dragTarget], draggedPrims, newDragControl))
			return(newDragControl);
		else
			delete newDragControl;
	}
	return(NULL);
}

DrawPrim* XmDrawing::add(DrawPrim* p)
{
	int ndx = primCount;

	if(primLock)
	{	cerr << "class xmDrawing: illegal add operation (primitives locked) - ignored...\n";
		return(NULL);
	}
	if(primCount < MAX_DRAWING_PRIMS)
	{	if(!(p->data)->fontName)
			p->setFont(defaultFont);
		prims[primCount] = p;
		drawInfo[primCount++] = drawingDataFor(p);
		changeFlags(ndx, defaultFlagsFor(p), TRUE, FALSE);
		p->disp = this;
		return(p);
	}
	cerr << "class xmDrawing: maximum primitives exceeded, primitive not drawn!";
	return(NULL);
}
	
DrawPrim* XmDrawing::remove(DrawPrim* p)
{
	if(primLock)
	{	cerr << "class xmDrawing: illegal remove operation (primitives locked) - ignored...\n";
		return(NULL);
	}
	for(int i = 0; i < primCount; i++)
	{	if(prims[i] == p)
		{	if(i == prSelection)
				prSelection = -1;
			freeGprData(drawInfo[i]);
			for(int j = i; j < primCount - 1; j++)
			{	prims[j] = prims[j + 1];
				drawInfo[j] = drawInfo[j + 1];
			}
			primCount--;
			return(p);
		}
	}
	return(NULL);
}

int XmDrawing::findPrim(DrawPrim* aPrim)
{
	int i = 0;

	while(i < primCount)
	{	if(prims[i] == aPrim)
			return(i);
		i++;
	}
	return(-1);
}

void XmDrawing::updatePrim(int ndx)
{
	if(ndx >= 0 && ndx < primCount)
	{	gprData* oldData = drawInfo[ndx];
		drawInfo[ndx] = drawingDataFor(prims[ndx], ndx);
		freeGprData(oldData);
	}
}

void XmDrawing::changeFlags(int ndx, unsigned int flag, bool set, bool repaint)
{
	if(ndx >= 0 || ndx < primCount)
	{	if(set)
			(drawInfo[ndx]->drawInfo)->flags |= flag;
		else
			(drawInfo[ndx]->drawInfo)->flags &= ~flag;
		if(((drawInfo[ndx]->drawInfo)->status & SELECTED) && repaint)
			refresh();
	}
	else
		cerr << "class xmDrawing: invalid index " << ndx << " for changeFlags !\n";
}

void XmDrawing::changeStatus(int ndx, unsigned int status, bool set, bool repaint)
{
	// cerr << "xmDrawing: changing status " << status << " to " << set << "\n";
	if(ndx >= 0 && ndx < primCount)
	{	if(set)
			(drawInfo[ndx]->drawInfo)->status |= status;
		else
			(drawInfo[ndx]->drawInfo)->status &= ~status;
		if(!set && (status == SELECTED || status == PRIMARY_SELECTION))
		{	for(int i = 0; i < primCount; i++)
			{	if(selDepend[i] == ndx)
					selDepend[ndx] = -1;
			}
		}
		if(status == PRIMARY_SELECTION)
		{	prSelection = set ? ndx : -1;
			changeStatus(ndx, SELECTED, set, repaint);
		}
		else
		{	if(repaint)
				refresh();
		}
	}
	else
		cerr << "class xmDrawing: invalid index " << ndx << " for changeStatus !\n";
}

bool XmDrawing::isSelected(DrawPrim* p)
{
	for(int i = 0; i < primCount; i++)
	{	if(prims[i] == p)
		{	if((drawInfo[i]->drawInfo)->status & SELECTED)
				return(TRUE);
			break;
		}
	}
	return(FALSE);
}

bool XmDrawing::makeSelDepend(DrawPrim* pClient, DrawPrim* pMaster)
{
	int pc, pm;

	if((pc = findPrim(pClient)) != -1 && (pm = findPrim(pMaster)) != -1)
	{	selDepend[pc] = pm;
		return(TRUE);
	}
	return(FALSE);
}

int XmDrawing::selectedCount()
{
	int count = 0;

	for(int i = 0; i < primCount; i++)
	{	if((drawInfo[i]->drawInfo)->status & SELECTED)
			count++;
	}
	return(count);
}

void XmDrawing::clearSelections(bool repaint)
{
	for(int i = 0; i < primCount; i++)
		changeStatus(i, PRIMARY_SELECTION, FALSE, FALSE);
	if(repaint)
		refresh();
}

unsigned int XmDrawing::defaultFlagsFor(DrawPrim* p)
{
	if((p->data)->shape != GPR_PLINE || (p->data)->numdesc > 2)
		return(defaultFlags);

	gprPoint& p0 = (p->data)->descr[0];
	gprPoint& p1 = (p->data)->descr[1];
	if((p0.x < p1.x && p0.y > p1.y) || (p0.x > p1.x && p0.y < p1.y))
		return(RESIZE_BOTTOM_LEFT | RESIZE_TOP_RIGHT);
	return(RESIZE_TOP_LEFT | RESIZE_BOTTOM_RIGHT);

//	if((p->data)->descr[0].y > (p->data)->descr[1].y)
}

gprData* XmDrawing::drawingDataFor(DrawPrim* p, int ndx)
{
	int ms = MARKER_SIZE;
	gprDrawInfo* i;
	gprData* gd = newGprData();

	p->copyData(gd);

	int ox = gd->descr[0].x;
	int oy = gd->descr[0].y;
	int ow = 0;
	int oh = 0;
	switch(gd->shape)
	{	case GPR_DOT:
		break;

        case GPR_PLINE:
		case GPR_POLYGON:
		int maxx = 0;
		int maxy = 0;
		for(int i = 0; i < gd->numdesc; i++)
		{	ox = min(ox, gd->descr[i].x);
			oy = min(oy, gd->descr[i].y);
			maxx = max(maxx, gd->descr[i].x);
			maxy = max(maxy, gd->descr[i].y);
		}
		ow = max(maxx - ox, ms);
		oh = max(maxy - oy, ms);
		break;

        case GPR_RECT:
        case GPR_ELLIPSE:
		ow = gd->descr[1].x;
		oh = gd->descr[1].y;
		break;

		case GPR_CIRCLE:
		ow = gd->descr[1].x;
		oh = gd->descr[1].x;
		break;

		case GPR_TEXT:
		break;
	}
// if(gd->drawInfo) cerr << "reusing info...\n";
	i = gd->drawInfo ? gd->drawInfo : new gprDrawInfo;
	i->flags = i->status = 0;
	i->index = 0;
	if(ndx >= 0)
		*i = *drawInfo[ndx]->drawInfo;		// re-use existing flags, status and index...
	i->dimensions[0].x = ox;
	i->dimensions[0].y = oy;
	i->dimensions[1].x = ow;
	i->dimensions[1].y = oh;
	i->hotspots[0].x = ox;
	i->hotspots[0].y = oy;
	i->hotspots[1].x = ox + ow / 2 - ms / 2;
	i->hotspots[1].y = oy;
	i->hotspots[2].x = ox + ow - ms;
	i->hotspots[2].y = oy;
	i->hotspots[3].x = ox;
	i->hotspots[3].y = oy + oh / 2 - ms / 2;
	i->hotspots[4].x = ox + ow - ms;
	i->hotspots[4].y = oy + oh / 2 - ms / 2;
	i->hotspots[5].x = ox;
	i->hotspots[5].y = oy + oh - ms;
	i->hotspots[6].x = ox + ow / 2 - ms / 2;
	i->hotspots[6].y = oy + oh - ms;
	i->hotspots[7].x = ox + ow - ms;
	i->hotspots[7].y = oy + oh - ms;
	gd->drawInfo = i;
	return(gd);
}


void XmDrawing::toForeground(DrawPrim* p)
{
	int ndx = findPrim(p);
	
	if(ndx >= 0)
	{	gprData* d = drawInfo[ndx];
		for(int i = ndx; i < primCount - 1; i++)
		{	prims[i] = prims[i + 1];
			drawInfo[i] = drawInfo[i + 1];
		}
		prims[primCount - 1] = p;
		drawInfo[primCount - 1] = d;
	}
}

void XmDrawing::toBackground(DrawPrim* p)
{
	int ndx = findPrim(p);
	
	if(ndx >= 0)
	{	gprData* d = drawInfo[ndx];
		for(int i = ndx; i > 0; i--)
		{	prims[i] = prims[i - 1];
			drawInfo[i] = drawInfo[i - 1];
		}
		prims[0] = p;
		drawInfo[0] = d;
	}
}

void XmDrawing::refresh(bool updateAll)
{
	if(updateAll)
	{	for(int i = 0; i < primCount; i++)
			updatePrim(i);
	}
	if(wid && XtIsRealized(wid))
	{	XClearWindow(display, XtWindow(wid));
		redraw(NULL);
	}
}
	
void XmDrawing::clearAll(bool deletePrims)
{
	while(--primCount >= 0)
	{	prims[primCount]->disp = NULL;
		if(deletePrims)
			delete prims[primCount];
		freeGprData(drawInfo[primCount]);
	}
	primCount = 0;
}

bool XmDrawing::changeGc()
{
	if(gc)
		XChangeGC(display, gc, valmask, &gcvals);
	valmask = 0;
	return(TRUE);
}

unsigned long XmDrawing::convertColor(gprColor c)
{
    return(cvals[min(c, GPR_MAX_COLORS)]);
}

void XmDrawing::setForeColor(gprColor c)
{
    gcvals.foreground = convertColor(c);
    valmask |= GCForeground;
}

void XmDrawing::setBackColor(gprColor c)
{
    gcvals.background = convertColor(c);
    valmask |= GCBackground;
}

void XmDrawing::setLineWidth(int lw)
{
    gcvals.line_width = lw;
    valmask |= GCLineWidth;
}

void XmDrawing::setLineStyle(gprPoint& ls)
{
    gcvals.fill_style = FillSolid;

	if(ls.x > 80)
		gcvals.line_style = LineSolid;
	else
	{	if(!ls.y)
			gcvals.line_style = LineOnOffDash;
		else 
			gcvals.line_style = LineDoubleDash;
	}		
    valmask |= GCFillStyle | GCLineStyle;
}

void XmDrawing::setLinePattern(gprFill)
{
}

void XmDrawing::setFillPattern(gprFill f)
{
	switch(f)
	{	case GPR_NONE:
		break;

		case GPR_SOLID:
		gcvals.fill_style = FillSolid;
		valmask = GCFillStyle;
		break;

		case GPR_LGRAY:
		gcvals.tile = lightGrayMap;
		gcvals.fill_style = FillTiled;
		valmask = GCTile | GCFillStyle;
		break;

		case GPR_DGRAY:
		gcvals.tile = darkGrayMap;
		gcvals.fill_style = FillTiled; 
		valmask = GCTile | GCFillStyle;
	}
}	

void XmDrawing::setBackground(gprColor c)
{
	addArg(XtNbackground, convertColor(c));

	if(XtIsRealized(wid))
	{	XtSetValues(wid, wargs, wargcount);
		wargcount = 0;
	}
}

void XmDrawing::draw(int idx, bool drag)
{
    gprData* gd = drag ? dragInfo[idx] : drawInfo[idx];
    XPoint* xpts = (XPoint* )gd->descr;
	GC cgc = drag ? dgc : gc;

	win = XtWindow(wid);

	if(!drag)
    {	setLineWidth(gd->linewidth);
		setLineStyle(gd->linestyle);
		setLinePattern(gd->linePattern);
    	setForeColor(gd->lineColor);
    	changeGc();
	}
	else
	{	if(dragControl)
		{	// setLineStyle(dragControl[idx] ? gprPoint(100, 0) : gprPoint(50, 50));
#ifndef LINUX
			gcvals.line_style = dragControl ? (dragControl[idx] ? LineSolid : LineOnOffDash) : LineSolid;
   			valmask = GCLineStyle;
#else	// dashed lines are extremely slow, at least on my 386er...
			gcvals.line_width = dragControl ? (dragControl[idx] ? 2 : 1) : gd->linewidth;
			valmask = GCLineWidth;
#endif
			XChangeGC(display, dgc, valmask, &gcvals);
			valmask = 0;
		}
	}
    switch(gd->shape)
    {   case GPR_DOT:
        if(gd->numdesc == 1)
        	XDrawPoint(display, win, cgc, xpts[0].x, xpts[0].y);
        break;

        case GPR_PLINE:
        case GPR_POLYGON:
        if(gd->numdesc > 1)
        	XDrawLines(display, win, cgc, xpts, gd->numdesc, CoordModeOrigin);
        break;

        case GPR_RECT:
        if(gd->numdesc == 2)
        	XDrawRectangle(display, win, cgc, xpts[0].x, xpts[0].y, xpts[1].x, xpts[1].y);
		else
			cerr << "class xDrawing: " << gd->numdesc << " - invalid number of gpr descriptors for a rect !\n";
        break;

        case GPR_CIRCLE:
        if(gd->numdesc == 2)
            xpts[1].y = xpts[1].x;
        case GPR_ELLIPSE:
        if(gd->numdesc == 2)
		{	if(!drag)
				XDrawArc(display, win, cgc, xpts[0].x, xpts[0].y, xpts[1].x, xpts[1].y, 0, 360 * 64);
			else	// seems to be a bug in Xlib....
			{	int cx, cy;
				Window dum;
#ifndef LINUX
				XTranslateCoordinates(display, win, RootWindow(display, 0), xpts[0].x, xpts[0].y, &cx, &cy, &dum);
#else
				cx = xpts[0].x;
				cy = xpts[0].y;
#endif
				XDrawArc(display, win, cgc, cx, cy, xpts[1].x, xpts[1].y, 0, 360 * 64);
			}
		}
        break;

		case GPR_TEXT:
		if(gd->numdesc > 1 && gd->text)
			XDrawString(display, win, cgc, xpts[0].x, xpts[0].y, gd->text, strlen(gd->text));
		break;
    }
}

void XmDrawing::fill(int idx)
{
    gprData* gd = drawInfo[idx];

	win = XtWindow(wid);

	if(gd->fillPattern != GPR_NONE)
	{	XPoint* xpts = (XPoint* )gd->descr;

		setFillPattern(gd->fillPattern);
		setForeColor(gd->fillColor);
		changeGc();

		switch(gd->shape)
		{	case GPR_DOT:
        	case GPR_PLINE:
	        cerr << "class XDrawing: cannot fill dots or lines!\n";
	        break;

	        case GPR_POLYGON:
	        if(gd->numdesc > 1)
	            XFillPolygon(display, win, gc, xpts, gd->numdesc, Nonconvex, CoordModeOrigin);
	        break;

	        case GPR_RECT:
	        if(gd->numdesc == 2)
	            XFillRectangle(display, win, gc, xpts[0].x + 1, xpts[0].y + 1, xpts[1].x - 1, xpts[1].y - 1);
	        break;

	        case GPR_CIRCLE:
	        if(gd->numdesc == 2)
	            xpts[1].y = xpts[1].x;
	        case GPR_ELLIPSE:
	        if(gd->numdesc == 2)
	            XFillArc(display, win, gc, xpts[0].x + 1, xpts[0].y + 1, xpts[1].x - 2, xpts[1].y - 2, 0, 360 * 64);
	        break;

			case GPR_TEXT:
			break;
		}
    }
}

short lineAngle(gprPoint beg, gprPoint end)
{
	return(0);
}

void XmDrawing::drawText(int ndx)
{
	gprData* gd = drawInfo[ndx];
	XFontStruct* font = curFontInfo;
	short ox, oy, ow, oh, fs, fa, tx, ty, tw;
	bool drawIt = TRUE;

	if(gd->text && gd->shape != GPR_TEXT)
	{	if(!curFont || strcmp(curFont, gd->fontName))
		{	if(curFontInfo)
				XFreeFontInfo(NULL, curFontInfo, 1);
// cerr << "loading font " << gd->fontName << "(" << curFont << ")...\n";
			if(!(font = XLoadQueryFont(display, gd->fontName)))
			{	cerr << "class XmDrawing: unable to load font '" << gd->fontName << "', using default (9x15).\n";
				if(!(font = XLoadQueryFont(display, (curFont = "9x15"))))
				{	cerr << "               - loading default font failed, exiting...\n";
					exit(-1);
				}
			}
			delete curFont;
			curFont = strcpy(new char[strlen(gd->fontName) + 1], gd->fontName);
			curFontInfo = font;
			XSetFont(display, gc, font->fid);
		}
		fs = font->ascent + font->descent;
		tw = XTextWidth(font, gd->text, strlen(gd->text));
		fa = 0;
		if(gd->shape == GPR_PLINE)
		{	switch(gd->hTextAlign)
			{	case GPR_BEGINNING:		// ignore...
				case GPR_END:			// ignore...
				case GPR_CENTER:
				ox = gd->descr[(gd->numdesc / 2) - 1].x;
				oy = gd->descr[(gd->numdesc / 2) - 1].y;
				ow = gd->descr[gd->numdesc / 2].x - ox;
				oh = gd->descr[gd->numdesc / 2].y - oy;
				fa = lineAngle(gd->descr[(gd->numdesc / 2) - 1], gd->descr[gd->numdesc / 2]);
				tx = ox + (ow / 2) - (tw / 2);
				ty = oy + (oh / 2);
				switch(gd->vTextAlign)
				{	case GPR_BEGINNING:
					ty -= (fs + gd->linewidth + 2);
					break;
					case GPR_CENTER:
					ty -= (fs / 2);
					break;
					case GPR_END:
					ty += 2;
					break;
				}
			}
		}
		else
		{	ox = (gd->drawInfo)->dimensions[0].x;
			oy = (gd->drawInfo)->dimensions[0].y;
			ow = (gd->drawInfo)->dimensions[1].x;
			oh = (gd->drawInfo)->dimensions[1].y;
			switch(gd->hTextAlign)
			{	case GPR_BEGINNING:
				tx = ox + 5;
				break;
				case GPR_CENTER:
				tx = ox + (ow / 2) - (tw / 2);
				break;
				case GPR_END:
				tx = ox + ow - (tw + 5);
				break;
			}
			switch(gd->vTextAlign)
			{	case GPR_BEGINNING:
				ty = oy + 5;
				break;
				case GPR_CENTER:
				ty = oy + (oh / 2) - (fs / 2);
				break;
				case GPR_END:
				ty = oy - oh - (fs + 5);
				break;
			}
		}
		setForeColor(GPR_BLACK);
		setBackColor(GPR_WHITE);
		changeGc();
		XDrawImageString(display, win, gc, tx, ty + font->ascent, gd->text, strlen(gd->text));
	}
}

void XmDrawing::drawAttribs(int ndx)
{
	gprData* gd = drawInfo[ndx];
	gprDrawInfo* di = gd->drawInfo;
	short ms = MARKER_SIZE;
	bool drawIt = TRUE;

	win = XtWindow(wid);
	if((gd->drawInfo)->status & SELECTED)
	{	unsigned int tflag = 2;
		for(int i = 0; i < 8; i++)
		{	setForeColor(GPR_BLACK);
			setFillPattern(GPR_SOLID);
			changeGc();
			XSetForeground(display, gc, BlackPixel(display, 0));	// why do I need this ???
			XDrawRectangle(display, win, gc, di->hotspots[i].x, di->hotspots[i].y, MARKER_SIZE, MARKER_SIZE);
			if(ndx == prSelection)
			{	if(!((gd->drawInfo)->flags & tflag))
					setFillPattern(GPR_LGRAY);
			}
			else
			{	setFillPattern(GPR_SOLID);
				setForeColor(GPR_WHITE);
			}
			changeGc();
			XFillRectangle(display, win, gc, di->hotspots[i].x + 1, di->hotspots[i].y + 1, MARKER_SIZE - 1, MARKER_SIZE - 1);
			tflag <<= 1;
		}
	}
}

void XmDrawing::bell(bool loud)
{
	XBell(display, loud ? 100 : 10);
}

// misc ////////////////////////////////////////////////////////////////

#define color TRUE

static char* color_names[] = { "White", "Black", "Red", "Green", "Blue", "Yellow" };

void initColors()
{
    XColor d, c;

	cmap = XCreateColormap(display, RootWindow(display, 0), DefaultVisual(display, 0), AllocNone);

    for(int i = 0; i < GPR_MAX_COLORS; i++)
    {   if(color == TRUE)
        {   if(i < 2)			// ensure that black is black and white is white....
            	cvals[i] = (i > 0) ? BlackPixel(display, 0) : WhitePixel(display, 0);
			else
			{	if(!XAllocNamedColor(display, cmap, color_names[i], &d, &c))
					cerr << "error allocating color " << color_names[i] << "\n";
            	cvals[i] = d.pixel;
			}
        }
        else
            cvals[i] = (i > 0) ? BlackPixel(display, 0) : WhitePixel(display, 0);
    }
}

#define gray_width 16
#define gray_height 16

char light_gray_bits[] = {
     0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
     0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
     0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
     0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22 };
char dark_gray_bits[] = {
     0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55,
     0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55,
     0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55,
     0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55 };

void initGrayPixmaps()
{
	lightGrayMap = XCreatePixmapFromBitmapData(display, RootWindow(display, 0), light_gray_bits, gray_width, gray_height, BlackPixel(display, 0), WhitePixel(display, 0), DefaultDepth(display, 0));
	darkGrayMap = XCreatePixmapFromBitmapData(display, RootWindow(display, 0), dark_gray_bits, gray_width, gray_height, BlackPixel(display, 0), WhitePixel(display, 0), DefaultDepth(display, 0));
}
