/****************************************************************************/
/*   xmObject.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 <stdlib.h>
// #include <unistd.h>
// #include <sys/wait.h>

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

#include "xmObject.h"

extern "C"
{
void XtMoveWidget(Widget, int, int);
void XtResizeWidget(Widget, int, int, int);
void XtConfigureWidget(Widget, int, int, int, int, int);
void exit(int);
}

#undef cb
#undef display
#undef screen
#undef mainWindow

#define XmSDCS XmSTRING_DEFAULT_CHARSET

// Application /////////////////////////////////////////////

XmApp* App;

void (*noX )() = NULL;

XmApp::XmApp(int argc, char** argv)
{
	App = this;
	appArgc = argc;
	appArgv = argv;
	XtToolkitInitialize();
	appShell = NULL;
	appContext = XtCreateApplicationContext();
#if defined(HP) || defined(AIX)
	display = XtOpenDisplay(appContext, NULL, NULL, "XmApp", NULL, 0, (Cardinal* )&appArgc, appArgv);
#else
	display = XtOpenDisplay(appContext, NULL, NULL, "XmApp", NULL, 0, &appArgc, appArgv);
#endif
	screen = NULL;
	rscDb = XtDatabase(display);
#ifdef XAW
	unitType = 0;	// unused...
#else
	unitType = XmPIXELS;
#endif
	active = FALSE;
	initialize();
	run();
}

// #include <X11/Xm/MainW.h>

XmApp::~XmApp()
{
}

void XmApp::run()
{
	if(appShell)
	{	appShell->realize();
		screen = XtScreen(appShell->handle());
		active = TRUE;
		XtAppMainLoop(appContext);
	}
	cerr << "xmApp: Error - no application window created, exiting...\n";
}

char* XmApp::getCmdArg(int n)
{
	return(n < appArgc ? appArgv[n] : NULL);
}

#include <X11/Shell.h>
#ifdef XAW
extern "C" {
#include <X11/Xaw/Dialog.h>
}
#else
#include <X11/Xm/DialogS.h>
#endif

Widget XmApp::getMainWindow()
{
	return(appShell ? appShell->handle() : NULL);
}

void XmApp::setScreenUnitType(unsigned char u)
{
	if(!active)
		unitType = u;
	else
		cerr << "xmApp: Warning - cannot change unit type of running application!\n";
}

Widget XmApp::newMainWindow(XmObject* anObj, char* name, int width, int height, xmStyle s)
{
	Widget w;
	Arg a[10];
	int argcount = 0;
	char* wn = name ? name : "XmApp";

	if(!display)
	{	cerr << "xmApp: cannot run without valid X Windows display, terminating.\n";
		exit(-1);
	}
#ifndef XAW
	if(s)
	{	long func, decor;
		translateWindowStyles(s, func, decor);
		if(func)
			XtSetArg(a[argcount], XmNmwmFunctions, func); argcount++;	// macro, can't use a[argcount++]...
		if(decor)
			XtSetArg(a[argcount], XmNmwmDecorations, decor); argcount++;
	}
#endif
	if(width || height)
	{	XtSetArg(a[argcount], XtNwidth, (width) ? width : 300); argcount++;
		XtSetArg(a[argcount], XtNheight, (height) ? height : 200); argcount++;
		w = XtAppCreateShell(NULL, wn, applicationShellWidgetClass, display, a, argcount);
	}
	else
		w = XtAppCreateShell(NULL, wn, applicationShellWidgetClass, display, a, argcount);

	if(!appShell)
		appShell = anObj;	// set the default application shell
	return(w);
}

void XmApp::removeMainWindow(XmObject* obj)
{
	if(obj == appShell)		// exit, if it is the default window (does this make sense??)
		exit(1);
}


main(int argc, char** argv)
{
	new XmApp(argc, argv);
}

// Manager //////////////////////////////////////////////////

// will provide support for a more sufficient application
// architecture, not really implemented yet....

XmManager::~XmManager()
{
	if(objects)
	{	if(objectCount)
			cerr << "xmManager: Warning - deleting manager with " << objectCount << " references!\n";
	}
}

bool XmManager::addObject(XmObject* anObject, objType t)
{
	XmObject** newArray;

	if(t == WindowObj || t == UserDialog || t == DialogWindow)
	{	if(objectCount == maxObjects)
		{	newArray = new XmObject*[objectCount ? objectCount * 2 : 5];
			for(int i = 0; i < objectCount; i++)
				newArray[i] = objects[i];
			delete objects;
			objects = newArray;
		}
		objects[objectCount++] = anObject;
		return(TRUE);
	}
	return(FALSE);
}

bool XmManager::removeObject(XmObject* anObject)
{
	for(int i = 0; i < objectCount; i++)
	{	if(objects[i] == anObject)
		{	--objectCount;
			for(int j = i; j < objectCount; j++)
				objects[j] = objects[j + 1];
			return(TRUE);
		}
	}
	return(FALSE);
}

XmObject* XmManager::findObject(char* n, objType t)
{
	for(int i = 0; i < objectCount; i++)
	{	if(!strcmp(objects[i]->getName(), n) && objects[i]->objectType() == t)
			return(objects[i]);
	}
	return(NULL);
}

void XmManager::changed(char* txt, XmObject* except)
{
	for(int i = 0; i < objectCount; i++)
	{	// cerr << objects[i]->getName() << ", ";
		if(objects[i] != except)
			objects[i]->update(txt);
	}
	if(manager)
		manager->changed(txt);
}


// Object //////////////////////////////////////////////////


void XmObject::init(char* n, XmObject* p)
{
	magic = MAGIC_OBJECT_CONST;
	basePtr = this;
	parent = p; 
    wid = (Widget )NULL;
    win = (Window )NULL;
	if(!n)
		n = "unnamedObject";
	wname =  strcpy(new char[strlen(n) + 1], n);
    wargcount = 0;
	gc = (GC )NULL;
    inputMask = 0;
#ifndef XAW
	addArg(XmNunitType, XmPIXELS);
#endif
	destroyWidget = FALSE;	// avoids overhead in all cases where a widget tree is destroyed
}

XmObject::~XmObject()
{
	if(magic)
	{	magic = 0L;
		if(destroyWidget)
			XmObject::destroy();	// can't call virtual here...
	}
	else
		cerr << "xmObject: Warning - deleting invalid object pointer!\n";
}

void genericCallback(Widget w, XtPointer cl_data, XtPointer ca_data)
{
	cb_data* d = (cb_data* )cl_data;

//	if(d->object->valid())			// ensure that object has not been deleted...
	{	void* answer = NULL;

		if(d->client_data == CB_XM_DATA)
			answer = ca_data;
		else if(d->client_data == CB_XM_HANDLE)
			answer = w;
		else if(d->client_data == CB_OBJ_NAME)
			answer = XtName(w);
		else if(d->client_data > CB_OBJ_NAME)
			answer = d->client_data;

		((d->object)->*(d->callback))(answer);
	}
}

char XmObject::parse(char* str)		// caution: parses string in place!
{
	char c;

	for(int i = 0; c = str[i]; i++)
	{	if(c == '&')
		{	for(int j = i; str[j]; j++)
				str[j] = str[j + 1];
			return(str[i]);
		}
	}
	return(str[0]);
}

bool XmObject::setCbFor(Widget w, char* evtName, XmObject* obj, cbProc cb, bool set, void* data)
{
	cb_data* d;
	XtCallbackList cbks;

	if(!wid)
		return(FALSE);

	if(set)
	{	d = new cb_data;
		d->callback = cb;
		d->object = obj->getBase();
		d->client_data = (XtPointer )data;
		XtAddCallback(w, evtName, genericCallback, (XtPointer )d);
		return(TRUE);
	}
	else
	{	if(XtHasCallbacks(w, evtName) == XtCallbackHasSome)
		{	Arg a;
			a.name = evtName;
			a.value = (XtArgVal )&cbks;
			XtGetValues(w, &a, 1);
			for(int i = 0; cbks[i].callback == NULL; i++)
			{	if(d = (cb_data* )cbks[i]. /* client_data */ closure)
				{	if(d->object == obj && d->callback == cb)
					{	XtRemoveCallback(w, evtName, genericCallback, (XtPointer )d);
						delete d;
						return(TRUE);
					}
				}
			}
		}
	}
	return(FALSE);
}

// A comment to the usage of the XmObject::XXXarg() functions: use them to collect
// a unknown number of Args in several functions, avoid them in one shot operations
// (use a local arg array instead, there may be some pending things in the wargs
// array which should take affect later).

int XmObject::argIndex(String n)
{
	for(int i = 0; i < wargcount; i++)
	{	if(!strcmp(wargs[i].name, n))
			return(i);
	}
	return(-1);
}

void XmObject::changeArg(String n, XtArgVal  v)
{
	int ndx;

	if((ndx = argIndex(n)) != -1)
		wargs[ndx].value = v;			// dont use this func with in-place-args!!
	else
		addArg(n, v);
}

void XmObject::forceArg(String n, XtArgVal  v)
{
	Arg a;

	if(wid)
	{	if(n)
		{	XtSetArg(a, n, v);
			XtSetValues(wid, &a, 1);
		}
		else
		{	XtSetValues(wid, wargs, wargcount);
			wargcount = 0;
		}
	}
	else
		cerr << "xmObject: Warning - forceArg " << n << " ignored: object not created.\n";
}

void XmObject::syscall(char*)	// callback name, not implemented yet...
{
}

char RPB[500];

char* XmObject::getResourcePath(bool append)
{
	int olen, alen, clen;

	olen = append ? strlen(RPB) : 0;
	alen = parent ? 0 : strlen(App->getName());
	clen = strlen(wname) + alen;

	char* tmp = strcpy(new char[strlen(RPB) + 1], RPB);
	strncpy(&RPB[clen + 1], tmp, olen);
	delete tmp;
	RPB[clen + olen + 1] = '\0';
	RPB[alen] = '*';
	strncpy(&RPB[alen + 1], wname, strlen(wname));

	if(parent)
		return(parent->getResourcePath(TRUE));

	strncpy(RPB, App->getName(), alen);
	return(RPB);
}

bool XmObject::checkResource(char* aName)
{
	XrmValue dum;
	char* ddum;
	ostrstream rstr;

	rstr << getResourcePath() << "." << aName; rstr.put('\0');
	return(XrmGetResource(App->getResourceDatabase(), rstr.str(), rstr.str(), &ddum, &dum) ? TRUE : FALSE);
}

bool XmObject::realize()
{
	if(!wid)
	{	cerr << "xmObject: error - cannot realize object (no widget created).\n";
		return(FALSE);
	}
	if(App->isActive())
	{	if(parent && !XtIsRealized(parent->handle()))
		{	cerr << "xmObject: error - cannot realize object (parent not realized).\n";
			return(FALSE);
		}
		XtRealizeWidget(parent ? wid : XtParent(wid));
		return(TRUE);
	}
	if(!parent)
	{	XtRealizeWidget(XtParent(wid));
		return(TRUE);
	}
	return(FALSE);
}

bool XmObject::destroy()
{
	if(wid)				// may have been destroyed already in subclass
	{	Widget w;
		XtUnmanageChild(wid);
		if(parent)
			w = wid;
		else			// no Xm++ parent object - parent widget can only be a shell
		{	w = XtParent(wid);
			App->removeMainWindow(this);	// may be an app window
		}
		wid = NULL;
		XtDestroyWidget(w);
		return(TRUE);
	}
	return(FALSE);
}

bool XmObject::hide()
{
	if(wid && XtIsManaged(wid))
	{	XtUnmanageChild(topHandle());
		return(TRUE);
	}
	return(FALSE);
}

bool XmObject::show()
{
	if(wid && !XtIsManaged(wid))
	{	XtManageChild(topHandle());
		return(TRUE);
	}
	return(FALSE);
}

bool XmObject::move(int nx, int ny)
{
	if(wid)
	{	XtMoveWidget(topHandle(), nx, ny);
		return(TRUE);
	}
	return(FALSE);
}

bool XmObject::resize(int nw, int nh)
{
	if(wid)
	{	Arg a;
		Dimension bw = 0;
		XtSetArg(a, XtNborderWidth, &bw);
		XtGetValues(wid, &a, 1);
		XtResizeWidget(topHandle(), nw, nh, bw);
		return(TRUE);
	}
	return(FALSE);
}

bool XmObject::reframe(int nx, int ny, int nw, int nh)
{
	if(wid)
	{	Arg a;
		Dimension bw = 0;
		XtSetArg(a, XtNborderWidth, &bw);
		XtGetValues(wid, &a, 1);
		XtConfigureWidget(topHandle(), nx, ny, nw, nh, bw);
		return(TRUE);
	}
	return(FALSE);
}


// Menu base class //////////////////////////////////////////////////////////////

#ifdef XAW
extern "C" {
#include <X11/Xaw/Box.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/Sme.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>
}
#else
#include <X11/Xm/RowColumn.h>
#include <X11/Xm/PushBG.h>
#include <X11/Xm/CascadeB.h>
#include <X11/Xm/SeparatoG.h>
#endif

int* locateItemPtr = NULL;

int locateItem(Widget)
{
	return(locateItemPtr ? *locateItemPtr : 0);
}

XmMenu::XmMenu(char* name, XmObject* par) : XmObject(name, par)
{
	nextItemPos = -1;
	numSubmenues = 0;
}

XmMenu::~XmMenu()
{
}

XmObject* XmMenu::parentOfMenu()	// find the root in a submenue hierarchy
{
	if(parent->objectType() == Menu)
		return(((XmMenu* )parent)->parentOfMenu());
	return(parent);
}

bool XmMenu::checkMenuResource(char* anItem)
{
	XrmValue dum;
	char* ddum;
	ostrstream rstr;

	rstr << getResourcePath() << "*" << anItem << ".labelString"; rstr.put('\0');
	return(XrmGetResource(App->getResourceDatabase(), rstr.str(), rstr.str(), &ddum, &dum) ? TRUE : FALSE);
}

bool XmMenu::addSeparator(char* name)
{
	Arg a;
	char* n = name;
	bool locate = FALSE;

	if(nextItemPos != -1)
	{	XtSetArg(a, XtNinsertPosition, locateItem);
		XtSetValues(getCurrentLabel(), &a, 1);
		locateItemPtr = &nextItemPos;
		locate = TRUE;
	}

	if(!n)
		n = "Separator";
#ifdef XAW
	XtManageChild(XtCreateManagedWidget(n, smeLineObjectClass, getCurrentLabel(), NULL, 0));
#else
	XtManageChild(XmCreateSeparatorGadget(getCurrentLabel(), n, NULL, 0));
#endif
	if(locate)
	{	nextItemPos = -1;
		locateItemPtr = NULL;
		XtSetArg(a, XtNinsertPosition, NULL);
		XtSetValues(getCurrentLabel(), &a, 1);
	}
	return(TRUE);
}

bool XmMenu::removeSeparator(char* name)
{
	char* n = name;
	Widget w;

	if(!n)
		n = "Separator";
	if(!(w = XtNameToWidget(wid, n)))
		return(FALSE);
	XtDestroyWidget(w);
	return(TRUE);
}

void menuCallback(Widget w, XtPointer cl_data, XtPointer)
{
	cb_data* d = (cb_data* )cl_data;
	char* aStr = NULL;

	if(d->client_data)
	{	Arg a;
		XmString lStr = NULL;
#ifdef XAW
		a.name = XtNlabel;
#else
		a.name = XmNlabelString;
#endif
		a.value = (XtArgVal )&lStr;
		XtGetValues(w, &a, 1);

		if(!lStr)
			cerr << "xmMenu (menuCallback): Internal error - cannot get menu label...\n";
#ifdef XAW
		aStr = lStr;
#else
		if(!XmStringGetLtoR(lStr, XmSDCS, &aStr))
			cerr << "xmMenu (menuCallback): Internal error - cannot extract menu label text...\n";
#endif
	}
	else
		aStr = XtName(w);
	((d->object)->*(d->callback))(aStr);
}

#define MAXITEMS 20

bool XmMenu::addItems(Entry entry, ...)
{
	Arg a;
	int argno = 0;
	char* items[MAXITEMS], * xitem, * anItem;
	XmManager* receivers[MAXITEMS];
	cbProc procs[MAXITEMS];
	void* data[MAXITEMS];
	Entry anEntry;
	bool locate = FALSE;

	items[argno] = entry.text;
	receivers[argno] = entry.receiver;
	procs[argno] = entry.callback;
	data[argno++] = entry.client_data;
#ifndef NO_VARARGS
	va_list ap;
	va_start(ap, entry);
	while(1)
	{	anEntry = va_arg(ap, Entry);
		if(!(items[argno] = anEntry.text))
			break;
		receivers[argno] = anEntry.receiver;
		procs[argno] = anEntry.callback;
		data[argno++] = anEntry.client_data;
	}
	va_end(ap);
#else
	items[argno] = NULL;
#endif
	if(nextItemPos != -1)
	{	XtSetArg(a, XtNinsertPosition, locateItem);
		XtSetValues(getCurrentLabel(), &a, 1);
		locateItemPtr = &nextItemPos;
		locate = TRUE;
	}
	for(int i = 0; xitem = items[i]; i++)
	{	ostrstream tmpstrm;
		tmpstrm << xitem; tmpstrm.put('\0');
		anItem = tmpstrm.str();
		char mm = parse(anItem);
		Widget w;
		cb_data* d = new cb_data;
		if(!checkMenuResource(anItem))
#ifdef XAW
			addArg(XtNlabel, (XtArgVal )anItem);
		XtManageChild(w = XtCreateManagedWidget(anItem, smeBSBObjectClass, getCurrentLabel(), wargs, wargcount));
#else
		{	addArg(XmNlabelString, (XtArgVal )XmStringCreateLtoR(anItem, XmSDCS));
			addArg(XmNmnemonic, mm);
		}
		XtManageChild(w = XmCreatePushButtonGadget(getCurrentLabel(), anItem, wargs, wargcount));
#endif
		wargcount = 0;
		d->callback = procs[i];
		d->object = receivers[i] ? receivers[i] : parentOfMenu()->getBase();
		d->client_data = data[i];
#ifdef XAW
		XtAddCallback(w, XtNcallback, menuCallback, (XtPointer )d);
#else
		XtAddCallback(w, XmNactivateCallback, menuCallback, (XtPointer )d);
#endif
		if(locate)
			nextItemPos++;
	}
	if(locate)
	{	nextItemPos = -1;
		locateItemPtr = NULL;
		XtSetArg(a, XtNinsertPosition, NULL);
		XtSetValues(getCurrentLabel(), &a, 1);
	}
	return(TRUE);
}

bool XmMenu::removeItems(char* first, ...)
{
	bool ret = TRUE;
	Widget w;
#ifndef NO_VARARGS
	char* item = first;
	va_list ap;

	va_start(ap, first);
	do
	{	if(!(w = XtNameToWidget(getCurrentLabel(), item)))
		{	ret = FALSE;
			continue;
		}
		XtDestroyWidget(w);
	} while(item = va_arg(ap, char*));
	va_end(ap);
#else
	if(!(w = XtNameToWidget(getCurrentLabel(), first)))
		return(FALSE);
	XtDestroyWidget(w);
#endif
	return(ret);

}

bool XmMenu::enableItems(char* first, ...)
{
	bool ret = TRUE;
	Widget w;
	Arg a;

	XtSetArg(a, XtNsensitive, TRUE);
#ifndef NO_VARARGS
	char* item = first;
	va_list ap;
	va_start(ap, first);
	do
	{	if(!(w = XtNameToWidget(getCurrentLabel(), item)))
		{	ret = FALSE;
			continue;
		}
		XtSetValues(w, &a, 1);
	} while(item = va_arg(ap, char*));
	va_end(ap);
#else
	if(!(w = XtNameToWidget(getCurrentLabel(), first)))
		return(FALSE);
	XtSetValues(w, &a, 1);
#endif
	return(ret);
}

bool XmMenu::disableItems(char* first, ...)
{
	bool ret = TRUE;
	Widget w;
	Arg a;

	XtSetArg(a, XtNsensitive, FALSE);
#ifndef NO_VARARGS
	char* item = first;
	va_list ap;
	va_start(ap, first);
	do
	{	if(!(w = XtNameToWidget(getCurrentLabel(), item)))
		{	ret = FALSE;
			continue;
		}
		XtSetValues(w, &a, 1);
	} while(item = va_arg(ap, char*));
	va_end(ap);
#else
	if(!(w = XtNameToWidget(getCurrentLabel(), first)))
		return(FALSE);
	XtSetValues(w, &a, 1);
#endif
	return(ret);
}

#ifndef XAW
#include <X11/Xm/CascadeBG.h>
#endif

XmSubMenu* XmMenu::addSubmenu(char* xn)
{
#ifdef XAW
	return(NULL);	// not implemented yet...
#else
	XmSubMenu* aMenu;
	bool locate = FALSE;
	Widget w;
	Arg a;

	if(numSubmenues == MAX_SUB_MENUES)
		return(NULL);
	if(nextItemPos != -1)
	{	XtSetArg(a, XmNinsertPosition, locateItem);
		XtSetValues(getCurrentLabel(), &a, 1);
		locateItemPtr = &nextItemPos;
		locate = TRUE;
	}

	ostrstream tmpstrm;
	tmpstrm << xn; tmpstrm.put('\0');
	char* n = tmpstrm.str();
	char mm = parse(n);
	aMenu = new XmSubMenu(n, this);
	if(!checkMenuResource(n))
	{	addArg(XmNlabelString, (XtArgVal )XmStringCreateLtoR(n, XmSDCS));
		addArg(XmNmnemonic, mm);
	}
	addArg(XmNsubMenuId, (XtArgVal )aMenu->handle());
	XtManageChild(w = XmCreateCascadeButtonGadget(getCurrentLabel(), n, wargs, wargcount));
	wargcount = 0;
	if(locate)
	{	nextItemPos = -1;
		locateItemPtr = NULL;
		XtSetArg(a, XmNinsertPosition, NULL);
		XtSetValues(getCurrentLabel(), &a, 1);
	}
	submenues[numSubmenues] = aMenu;
	subHandles[numSubmenues++] = w;
	return(aMenu);
#endif
}

XmSubMenu* XmMenu::submenuAt(char* xn)
{
#ifdef XAW
	return(NULL);
#else
	ostrstream tmpstrm;
	tmpstrm << xn; tmpstrm.put('\0');
	char* n = tmpstrm.str();
		
	parse(n);
	for(int i = 0; i < numSubmenues; i++)
	{	if(!strcmp(XtName(subHandles[i]), n))
			return(submenues[i]);
	}
	return(NULL);
#endif
}

bool XmMenu::removeSubmenu(char* n)
{
#ifdef XAW
	return(FALSE);
#else
	int ndx = -1;
	for(int i = 0; i < numSubmenues; i++)
	{	if(!strcmp(XtName(subHandles[i]), n))
			break;
	}
	if(ndx < 0)
		return(FALSE);
	delete submenues[ndx];
	XtDestroyWidget(subHandles[ndx]);
	for(int j = ndx; j < numSubmenues; j++)
	{	submenues[j] = submenues[j + 1];
		subHandles[j] = subHandles[j + 1];
	}
	numSubmenues--;
	return(TRUE);
#endif
}

bool XmMenu::changeItemText(char* itemName, char* xnewText)
{
	Widget w;
	Arg a[2];
	ostrstream tmpstrm;
	tmpstrm << xnewText; tmpstrm.put('\0');
	char* newText = tmpstrm.str();
	char mm = parse(newText);
		
	if(!(w = XtNameToWidget(getCurrentLabel(), itemName)))
		return(FALSE);
#ifdef XAW
	XtSetArg(a[0], XtNlabel, newText);
	XtSetValues(w, a, 1);
#else
	XtSetArg(a[0], XmNlabelString, XmStringCreateLtoR(newText, XmSDCS));
	XtSetArg(a[1], XmNmnemonic, mm);
	XtSetValues(w, a, 2);
#endif
	return(TRUE);
}


// Popup menu /////////////////////////////////////////////////////////////////

void postPopup(Widget, XtPointer popup, XEvent* e, Boolean*)
{
	if(e->xbutton.button == Button3)
#ifdef XAW
//	{	XtCallActionProc((Widget )popup, "XawPositionSimpleMenu", e, NULL, 0);
	{	Arg a[2];
		XtSetArg(a[0], XtNx, e->xbutton.x);
		XtSetArg(a[1], XtNy, e->xbutton.y);
		XtSetValues((Widget )popup, a, 2);
#else
	{	XmMenuPosition((Widget )popup, &(e->xbutton));
#endif
		XtManageChild((Widget )popup);
	}
}

XmPopupMenu::XmPopupMenu(char* xname, XmObject* par) : XmMenu(xname, par)
{
	Arg a;
	Widget w[2];
	char* acc = "Ctrl <Key> p";
	ostrstream tmpstrm;
	tmpstrm << xname; tmpstrm.put('\0');
	char* name = tmpstrm.str();
	char mn = parse(name);

#ifdef XAW
    if(wid = XtCreatePopupShell(name, simpleMenuWidgetClass, par->handle(), &a, 1))
#else
	acc[11] = mn;
	XtSetArg(a, XmNmenuAccelerator, acc);
	if(wid = XmCreatePopupMenu(par->handle(), name, &a, 1))
#endif
	{	bool extrnl;
		if(!(extrnl = checkMenuResource(name)))
#ifdef XAW
		{	XtSetArg(a, XtNlabel, name);
			XtSetValues(wid, &a, 1);
		}
#else
			XtSetArg(a, XmNlabelString, XmStringCreateLtoR(name, XmSDCS));
		w[0] = XmCreateLabelGadget(wid, "popup-label", &a, extrnl ? 0 : 1);
		w[1] = XmCreateSeparatorGadget(wid, "popup-seperator", NULL, 0);
		XtManageChildren(w, 2);
#endif
		XtAddEventHandler(par->handle(), ButtonPressMask, FALSE, postPopup, (XtPointer )wid);
	}
}

bool XmPopupMenu::setLabel(char* l)
{
	Arg a;
	Widget w;

#ifdef XAW
	w = wid;
	XtSetArg(a, XtNlabel, l);
#else
	if(!(w = XtNameToWidget(wid, "popup-label")))
		return(FALSE);
	XtSetArg(a, XmNlabelString, XmStringCreateLtoR(l, XmSDCS));
#endif
	XtSetValues(w, &a, 1);
	return(TRUE);
}

// Dropdown menu /////////////////////////////////////////////////////////////////

XmDropdownMenu::XmDropdownMenu(char* name, XmObject* par) : XmMenu(name, par)
{
	numDropdowns = 0;
	nextLabelPos = -1;
	currentLabel = (Widget )NULL;

#ifdef XAW
	addArg(XtNleft, XtChainLeft);
	addArg(XtNright, XtChainRight);
	addArg(XtNtop, XtChainTop);
	addArg(XtNbottom, XtChainTop);
	addArg(XtNorientation, XtorientHorizontal);
	addArg(XtNborderWidth, 1);
	wid = XtCreateWidget(name, boxWidgetClass, par->handle(), wargs, wargcount);
#else
	addArg(XmNtopAttachment, XmATTACH_FORM);
	addArg(XmNrightAttachment, XmATTACH_FORM);
	addArg(XmNleftAttachment, XmATTACH_FORM);
	wid = XmCreateMenuBar(par->handle(), name, wargs, wargcount);
#endif
	XtManageChild(wid);
	wargcount = 0;
}

int* locateLabelPtr = NULL;

int locateLabel(Widget)
{
	return(locateLabelPtr ? *locateLabelPtr : 0);
}

#define DROPDOWN_PANE_POSTFIX "-menu-pane"

bool XmDropdownMenu::addLabels(char* first, ...)
{
	int argno = 0;
	char* items[MAX_DROPDOWN_LABELS], * xitem, * item;
	bool locate = FALSE;

	items[argno++] = first;

#ifndef NO_VARARGS
	va_list ap;
	va_start(ap, first);
	while((items[argno++] = va_arg(ap, char *)) != (char *)0);
	va_end(ap);
#endif
	items[argno] = (char* )NULL;
	if(nextLabelPos != -1)
	{	forceArg(XtNinsertPosition, (XtArgVal )locateLabel);
		locateLabelPtr = &nextLabelPos;
		locate = TRUE;
	}
	for(int i = 0; xitem = items[i]; i++)
	{	ostrstream tmpstrm;
		tmpstrm << xitem; tmpstrm.put('\0');
		item = tmpstrm.str();
		char mm = parse(item);
		ostrstream strm;
		Widget w;

		strm << item << DROPDOWN_PANE_POSTFIX; strm.put('\0');
#ifdef XAW
		currentLabel = XtCreatePopupShell(strm.str(), simpleMenuWidgetClass, wid, NULL, 0);
		if(!checkMenuResource(item))
			addArg(XtNlabel, (XtArgVal )item);
		addArg(XtNmenuName, (XtArgVal )strm.str());
		w = XtCreateWidget(item, menuButtonWidgetClass, wid, wargs, wargcount);
#else
		currentLabel = XmCreatePulldownMenu(wid, strm.str(), NULL, 0);
		addArg(XmNsubMenuId, (XtArgVal )currentLabel);
		if(!checkMenuResource(item))
		{	addArg(XmNlabelString, (XtArgVal )XmStringCreateLtoR(item, XmSDCS));
			addArg(XmNmnemonic, mm);
		}
		w = XmCreateCascadeButton(wid, item, wargs, wargcount);
#endif
		XtManageChild(w);
		wargcount = 0;
		if(locate)
			nextLabelPos++;
		dropdowns[numDropdowns++] = currentLabel;
	}
	if(locate)
	{	nextLabelPos = -1;
		locateLabelPtr = NULL;
		forceArg(XtNinsertPosition, (XtArgVal )NULL);
	}
	return(TRUE);
}

bool XmDropdownMenu::removeLabels(char* first, ...)
{
	int argno = 0;
	char* items[MAX_DROPDOWN_LABELS], * xitem, * item;
	bool ret = TRUE;

	items[argno++] = first;

#ifndef NO_VARARGS
	va_list ap;
	va_start(ap, first);
	while((items[argno++] = va_arg(ap, char *)) != (char *)0);
	va_end(ap);
#endif
	items[argno] = (char* )NULL;

	for(int i = 0; xitem = items[i]; i++)
	{	Widget w1, w2;
		ostrstream strm;
		ostrstream tmpstrm;
		tmpstrm << xitem; tmpstrm.put('\0');
		item = tmpstrm.str();
		parse(item);
		strm << item << DROPDOWN_PANE_POSTFIX; strm.put('\0');

		if(!((w1 = XtNameToWidget(wid, strm.str())) && (w2 = XtNameToWidget(wid, item))))
		{	ret = FALSE;
			continue;
		}
		XtDestroyWidget(w1);
		XtDestroyWidget(w2);
	}
	return(ret);
}

bool XmDropdownMenu::setCurrentLabel(char* n)
{
	Widget lastLabel = currentLabel;
	ostrstream lbn;

	lbn << n << DROPDOWN_PANE_POSTFIX; lbn.put('\0');

	for(int i = 0; i < numDropdowns; i++)
	{	if(!strcmp(XtName(currentLabel = dropdowns[i]), lbn.str()))
			return(TRUE);
	}
	cerr << "xmDropdownMenu: Warning - cannot find label " << n << " using: " << hex(long(lastLabel)) << "\n";
	currentLabel = lastLabel;
	return(FALSE);
}

bool XmDropdownMenu::changeLabelText(char*, char*)
{
	return(FALSE);
}

// subMenu /////////////////////////////////////////////////////////////////////

XmSubMenu::XmSubMenu(char* n, XmMenu* par) : XmMenu(n, par)
{
#ifndef XAW
	wid = XmCreatePulldownMenu(par->getCurrentLabel(), n, NULL, 0);
//	XtManageChild(wid);
#endif
	wargcount = 0;
}

XmSubMenu::~XmSubMenu()
{
}

#ifndef XAW
#include <Xm/MwmUtil.h>
#endif

// Dialog base class //////////////////////////////////////////////////////////

XmDialog::XmDialog(char* n, XmObject* par, xmStyle s) : XmObject(n, par)
{
	basePtr = (XmObject* )((void* )this);
	dlgStyle = (s & XmSdlgWinModal || s & XmSdlgAppModal || s & XmSdlgSysModal) ? s : XmSdlgModeless;
}

XmDialog::~XmDialog()
{
}

#ifndef XAW
#define mwmMode(s) 	(s & XmSdlgModeless ? MWM_INPUT_MODELESS : \
					(s & XmSdlgWinModal ? MWM_INPUT_PRIMARY_APPLICATION_MODAL : \
					(s & XmSdlgAppModal ? MWM_INPUT_FULL_APPLICATION_MODAL : \
										  MWM_INPUT_SYSTEM_MODAL)))
#endif

bool XmDialog::setLabelAndMode(char* label)
{
	Widget sh;

	if(!wid || !(sh = XtParent(wid)))
		return(FALSE);
#ifdef XAW
	addArg(XtNlabel, (XtArgVal )label);
#else
	addArg(XmNtitle, (XtArgVal )label);
	addArg(XmNmwmInputMode, (XtArgVal )mwmMode(dlgStyle));
#endif
	XtSetValues(XtParent(wid), wargs, wargcount);
	wargcount = 0;
	return(TRUE);
}

bool XmDialog::run()
{
	if(dlgStyle & XmSdlgModeless)
	{	cerr << "xmDialog: Error - cannot run a modeless dialog.\n";
		return(FALSE);
	}
	completed = returned = FALSE;
	XtManageChild(wid);
#ifdef XAW
	XtRealizeWidget(XtParent(wid));
#endif
	while(!completed)
		XtAppProcessEvent(App->getAppContext(), XtIMAll);
	return(returned);
}

void XmDialog::ok(void*)
{
	returned = completed = TRUE;
}

void XmDialog::cancel(void*)
{
	completed = TRUE;
}

void XmDialog::close(void*)
{
	delete this;
}

// Predefined system dialogs //////////////////////////////////////////////////

XmSystemDialog::XmSystemDialog(char* n, XmObject* par, xmStyle s) : XmDialog(n, par, s)
{
	destroyOnDelete();
}

#ifdef XAW
#include <X11/Xaw/Dialog.h>
#else
#include <X11/Xm/MessageB.h>
#endif

typedef Widget (*DlgCreateFunc )(Widget, char*, Arg*, unsigned int);

XmMsgBox::XmMsgBox(char* text, char* label, xmStyle s, XmObject* rec, cbProc okCb) : XmSystemDialog("msg", NULL, s ? s : XmSmsgOkCancel | XmSdlgAppModal)
{
	Widget aParent, aShell = NULL;

#ifdef XAW
	if(FALSE && rec && rec->handle())	// unsolved transient shell problem...
	{	addArg(XtNwidth, 200);
		addArg(XtNheight, 100);
		addArg(XtNtransientFor, (XtArgVal )rec->handle());
		aShell = XtCreateWidget(label, transientShellWidgetClass, rec->topHandle(), wargs, wargcount);
		wargcount = 0;
	}
	else
		aShell = App->newMainWindow(this, label, 200, 100, s);

	addArg(XtNallowShellResize, TRUE);
	addArg(XtNtitle, (XtArgVal )label);
	XtSetValues(aShell, wargs, wargcount);
	wargcount = 0;
	addArg(XtNlabel, (XtArgVal )text);
/*													// not implemented yet...
	addArg(XtNicon,
			(s & XmSmsgMemo ? XmDIALOG_MESSAGE :
			(s & XmSmsgInfo ? XmDIALOG_INFORMATION :
			(s & XmSmsgQuestion ? XmDIALOG_QUESTION :
			(s & XmSmsgWorking ? XmDIALOG_WORKING :
			(s & XmSmsgWarning ? XmDIALOG_WARNING :
			(s & XmSmsgError ? XmDIALOG_ERROR : XmDIALOG_MESSAGE)))))));
*/
	addArg(XtNresizable, TRUE);
	ostrstream dlgName;
	dlgName << label << "-dlg"; dlgName.put('\0');
	XtManageChild(wid = XtCreateWidget(dlgName.str(), dialogWidgetClass, aShell, wargs, wargcount));
	wargcount = 0;

	if(dlgStyle & XmSmsgOk || dlgStyle & XmSmsgOkCancel || dlgStyle & XmSmsgOkCancelHelp)
		XawDialogAddButton(wid, "OK", NULL, NULL);
	if(dlgStyle & XmSmsgOkCancel || dlgStyle & XmSmsgOkCancelHelp)
		XawDialogAddButton(wid, "Cancel", NULL, NULL);
	if(dlgStyle & XmSmsgOkCancelHelp)
		XawDialogAddButton(wid, "Help", NULL, NULL);
	if(dlgStyle & XmSmsgYesNo)
	{	XawDialogAddButton(wid, "Yes", NULL, NULL);
		XawDialogAddButton(wid, "No", NULL, NULL);
	}
#else
	if(!(aParent = (rec && rec->handle()) ? rec->handle() : App->getMainWindow()))
	{	cerr << "creating dlg shell...\n";
		aShell = App->newMainWindow(this, label, 10, 10, s);
	}

	DlgCreateFunc mfunc;

	if(aShell)
	{	mfunc = XmCreateMessageBox;
		addArg(XmNdialogType,
			(s & XmSmsgMemo ? XmDIALOG_MESSAGE :
			(s & XmSmsgInfo ? XmDIALOG_INFORMATION :
			(s & XmSmsgQuestion ? XmDIALOG_QUESTION :
			(s & XmSmsgWorking ? XmDIALOG_WORKING :
			(s & XmSmsgWarning ? XmDIALOG_WARNING :
			(s & XmSmsgError ? XmDIALOG_ERROR : XmDIALOG_MESSAGE)))))));
	}
	else
		mfunc = (s & XmSmsgMemo ? XmCreateMessageDialog :
				(s & XmSmsgInfo ? XmCreateInformationDialog :
				(s & XmSmsgQuestion ? XmCreateQuestionDialog :
				(s & XmSmsgWorking ? XmCreateWorkingDialog :
				(s & XmSmsgWarning ? XmCreateWarningDialog :
				(s & XmSmsgError ? XmCreateErrorDialog : XmCreateMessageDialog))))));

	addArg(XmNmessageString, (XtArgVal )XmStringCreateLtoR(text, XmSDCS));
	addArg(XmNmessageAlignment, (dlgStyle & XmSleft ? XmALIGNMENT_BEGINNING : (dlgStyle & XmSright ? XmALIGNMENT_END : XmALIGNMENT_CENTER)));
	if(dlgStyle & XmSmsgYesNo || dlgStyle & XmSmsgYesNoCancel)
	{	addArg(XmNokLabelString, (XtArgVal )XmStringCreateLtoR("Yes", XmSDCS));
		addArg(XmNcancelLabelString, (XtArgVal )XmStringCreateLtoR("No", XmSDCS));
		addArg(XmNhelpLabelString, (XtArgVal )XmStringCreateLtoR("Cancel", XmSDCS));
	}
	wid = (*mfunc )(aShell ? aShell : aParent, label, wargs, wargcount);
	wargcount = 0;

	if(dlgStyle & XmSmsgOk || dlgStyle & XmSmsgOkCancel || dlgStyle & XmSmsgYesNo)
		XtUnmanageChild(XmMessageBoxGetChild(wid, XmDIALOG_HELP_BUTTON));
	if(dlgStyle & XmSmsgOk)
		XtUnmanageChild(XmMessageBoxGetChild(wid, XmDIALOG_CANCEL_BUTTON));
#endif
	setDefaultButtonProcs(rec, okCb);

	if(dlgStyle & XmSmsgQuestion)
		dlgStyle |= XmSdlgWinModal;
	if(dlgStyle & XmSmsgWarning)
		dlgStyle |= XmSdlgAppModal;
	if(dlgStyle & XmSmsgError)
		dlgStyle |= XmSdlgSysModal;

	setLabelAndMode(label);
	if(dlgStyle & XmSdlgModeless)
	{	XtManageChild(wid);
#ifdef XAW
		XtRealizeWidget(XtParent(wid));
#endif
	}
}


XmMsgBox::XmMsgBox(char* n, XmObject* par, xmStyle s) : XmSystemDialog(n, par, (s ? s : XmSmsgOkCancel | XmSdlgAppModal))
{
}

void XmMsgBox::setDefaultButtonProcs(XmObject* rec, cbProc okCb)
{
	if(!(dlgStyle & XmSdlgModeless) && validProc(okCb))
	{	cerr << "xmMsgBox: Warning - cannot use callback in modal dialog.\n";
		rec = NULL;
	}
#ifdef XAW
	Widget dlgWid = wid;
	wid = XtNameToWidget(dlgWid, "OK");
	if(wid || (wid = XtNameToWidget(dlgWid, "Yes")))
	{	if(rec && validProc(okCb))
			setCallback(XtNcallback, rec, okCb);
		else
			setCallback(XtNcallback, this, CB(XmDialog::ok));
	}
	else
		cerr << "XmMsgBox: Error - no OK or Yes button found...\n";
	wid = XtNameToWidget(dlgWid, "Cancel");
	if(wid || (wid = XtNameToWidget(dlgWid, "No")))
		setCallback(XtNcallback, this, CB(XmDialog::cancel));
	wid = dlgWid;
#else
	if(rec && validProc(okCb))
		setCallback(XmNokCallback, rec, okCb);
	else
		setCallback(XmNokCallback, this, CB(XmDialog::ok));
	setCallback(XmNcancelCallback, this, CB(XmDialog::cancel));
#endif
}

bool XmMsgBox::showMsg()
{
	bool rcode = run();
	delete this;
	return(rcode);
}

bool XmMsgBox::setButtonText(msgButton b, char* t)
{
	if(!wid)
		return(FALSE);
#ifdef XAW
	return(FALSE);
#else
	String bnames[HELP + 1];
	Arg a;

	bnames[OK] 		= XmNokLabelString;
	bnames[CANCEL] 	= XmNcancelLabelString;
	bnames[HELP] 	= XmNhelpLabelString;
	a.name = bnames[b];
	a.value = (XtArgVal )XmStringCreateLtoR(t, XmSDCS);
	XtSetValues(wid, &a, 1);
	return(TRUE);
#endif
}

#ifndef XAW
#include <X11/Xm/SelectioB.h>
#endif

XmPrompter::XmPrompter(char* n, XmObject* par, xmStyle s) : XmMsgBox(n, par, s)
{
}

#ifdef XAW
XmPrompter::XmPrompter(char* prompt, char* label, char* def, xmStyle s, XmObject* rec, cbProc okCb) :XmMsgBox(prompt, label, s, rec, okCb)
{
	forceArg(XtNvalue, (XtArgVal )(def ? def : ""));
	if((dlgStyle = s) & XmSdlgModeless)
	{	XtManageChild(wid);
		XtRealizeWidget(XtParent(wid));
	}
}
#else
XmPrompter::XmPrompter(char* prompt, char* label, char* def, xmStyle s, XmObject* rec, cbProc okCb) : XmMsgBox("prompter", (XmObject* )NULL, s)
{
	// setText(def);
	addArg(XmNselectionLabelString, (XtArgVal )XmStringCreateLtoR(prompt, XmSDCS));
	wid = XmCreatePromptDialog((rec && rec->handle()) ? rec->handle() : App->getMainWindow(), label, wargs, wargcount);
	wargcount = 0;
	setText(def);	// does not work otherwise...

	if(dlgStyle & XmSmsgOkCancel)
		XtUnmanageChild(XmSelectionBoxGetChild(wid, XmDIALOG_HELP_BUTTON));

	setDefaultButtonProcs(rec, okCb);
	setLabelAndMode(label);
	if(dlgStyle & XmSdlgModeless)
		XtManageChild(wid);
}
#endif

char* XmPrompter::prompt()
{
	char* txt = NULL;

	if(run())
		txt = getText();
	delete this;
	return(txt);
}

bool XmPrompter::setText(char* txt)
{
#ifdef XAW
	addArg(XtNvalue, (XtArgVal )txt);
#else
	addArg(XmNtextString, (XtArgVal )XmStringCreateLtoR(txt, XmSDCS));
#endif
	if(wid)
	{	XtSetValues(wid, wargs, wargcount);
		wargcount = 0;
	}
	return(TRUE);
}

char promptBuf[500];

char* XmPrompter::getText()
{
	char* txt;

	if(!wid)
		return((char* )NULL);
#ifdef XAW
	txt = XawDialogGetValueString(wid);
#else
	getArg(XmNtextString);
	XtGetValues(wid, wargs, wargcount);
	XmStringGetLtoR((XmString )wargs[0].value, XmSDCS, &txt);
#endif
	wargcount = 0;
	return(txt);
}

#ifdef XAW
XmListPrompter::XmListPrompter(char** list, char* prompt, char* label, char* def, xmStyle s, XmObject* rec, cbProc okCb) : XmPrompter("Sorry, list selection not implemented.", label, list[0], s, rec, okCb)
{
	if((dlgStyle = s) & XmSdlgModeless)
	{	XtManageChild(wid);
		XtRealizeWidget(XtParent(wid));
	}
}
#else
XmListPrompter::XmListPrompter(char** list, char* prompt, char* label, char* def, xmStyle s, XmObject* rec, cbProc okCb) : XmPrompter("listPrompter", NULL, s)
{
	char** aStr = list;
	XmString* items;
	int count = 0;

	while(*aStr++ && count < 100)
		count++;

	items = new XmString[count];
	for(int i = 0; i < count; i++)
		items[i] = XmStringCreateLtoR(list[i], XmSDCS);

	setText(def);
	addArg(XmNlistItems, (XtArgVal )items);
	addArg(XmNlistItemCount, count);
	addArg(XmNselectionLabelString, (XtArgVal )XmStringCreateLtoR(prompt, XmSDCS));
	wid = XmCreateSelectionDialog((rec && rec->handle()) ? rec->handle() : App->getMainWindow(), label, wargs, wargcount);
	wargcount = 0;
	delete items;

	XtUnmanageChild(XmSelectionBoxGetChild(wid, XmDIALOG_APPLY_BUTTON));
	if(dlgStyle & XmSmsgOkCancel)
		XtUnmanageChild(XmSelectionBoxGetChild(wid, XmDIALOG_HELP_BUTTON));

	setDefaultButtonProcs(rec, okCb);
	setLabelAndMode(label);
	if(dlgStyle & XmSdlgModeless)
		XtManageChild(wid);
}
#endif

int XmListPrompter::promptIndex()
{
	int ndx = -1;

	if(run())
		ndx = getIndex();
	delete this;
	return(ndx);
}

XmListPrompter* XmListPrompter::selectText(char*)
{
	return(this);
}

XmListPrompter* XmListPrompter::selectIndex(int)
{
	return(this);
}

int XmListPrompter::getIndex()
{
	return(0);
}

#ifndef XAW
#include <X11/Xm/FileSB.h>
#endif

#ifdef XAW
XmFileSelector::XmFileSelector(char* path, char* deflt, xmStyle s, XmObject*rec, cbProc okCb) : XmPrompter("Enter filename:", "File Selector", path, s, rec, okCb)
{
	if((dlgStyle = s) & XmSdlgModeless)
	{	XtManageChild(wid);
		XtRealizeWidget(XtParent(wid));
	}
}
#else
XmFileSelector::XmFileSelector(char* path, char* deflt, xmStyle s, XmObject*rec, cbProc okCb) : XmPrompter("fileSelector", NULL, s)
{
	addArg(XmNdirSpec, (XtArgVal )XmStringCreateLtoR(deflt ? deflt : path, XmSDCS));
	addArg(XmNdirMask, (XtArgVal )XmStringCreateLtoR(path, XmSDCS));
	wid = XmCreateFileSelectionDialog((rec && rec->handle()) ? rec->handle() : App->getMainWindow(), "fileSelector", wargs, wargcount);
	wargcount = 0;

	if(dlgStyle & XmSmsgOkCancel)
		XtUnmanageChild(XmMessageBoxGetChild(wid, XmDIALOG_HELP_BUTTON));

	setDefaultButtonProcs(rec, okCb);
	setLabelAndMode("Select a file...");
	if(dlgStyle & XmSdlgModeless)
		XtManageChild(wid);
}
#endif

char* XmFileSelector::promptFile()
{
	char* txt;

	if(!run())
	{	delete this;
		return(NULL);
	}
	txt = getPath();
	delete this;
	return(txt);
}

bool XmFileSelector::setPath(char* txt)
{
#ifdef XAW
	return(FALSE);
#else
	addArg(XmNdirSpec, (XtArgVal )XmStringCreateLtoR(txt, XmSDCS));
	addArg(XmNdirMask, (XtArgVal )XmStringCreateLtoR(txt, XmSDCS));
	if(wid)
	{	XtSetValues(wid, wargs, wargcount);
		wargcount = 0;
	}
	return(TRUE);
#endif
}

char* XmFileSelector::getPath()
{
	char* txt;

	if(!wid)
		return((char* )NULL);
#ifdef XAW
	return(getText());
#else
	getArg(XmNdirSpec);
	XtGetValues(wid, wargs, wargcount);
	wargcount = 0;
	XmStringGetLtoR((XmString )wargs[0].value, XmSDCS, &txt);
	return(txt);
#endif
}


// Window classes /////////////////////////////////////////////////////////////

#ifndef XAW
#include <X11/Xm/MwmUtil.h>

void translateWindowStyles(xmStyle s, long& f, long& d)
{
	f = d = 0;

	if(s & XmSmaximize)
	{	f |= MWM_FUNC_MAXIMIZE;
		d |= MWM_DECOR_MAXIMIZE;
	}
	if(s & XmSminimize)
	{	f |= MWM_FUNC_MINIMIZE;
		d |= MWM_DECOR_MINIMIZE;
	}
	if(s & XmSbordered)
		d |= MWM_DECOR_BORDER;
	if(s & XmSmoveable)
	{	f |= MWM_FUNC_MOVE;
		d |= MWM_DECOR_TITLE;
	}
	if(s & XmSresizeable)
	{	f |= MWM_FUNC_RESIZE;
		d |= MWM_DECOR_RESIZEH;
	}
	if(s & XmScloseable)
		f |= MWM_FUNC_CLOSE;
	if(s & XmStitled)
		d |= MWM_DECOR_TITLE;
	if(s & XmSsysMenu)
		d |= MWM_DECOR_MENU;
}

#endif

XmDialogWindow::XmDialogWindow(char* n, XmObject* par, xmStyle s) : XmUserDialog(n, par, s)
{
	style = s;
	dropdown = NULL;
	popup = NULL;
}

XmDialogWindow::XmDialogWindow(char* n, XmObject* par, XmManager* mgr, xmStyle s) : XmUserDialog(n, par, mgr, s)
{
	style = s;
	dropdown = NULL;
	popup = NULL;
}

XmDialogWindow::~XmDialogWindow()
{
//	if(manager)
//		manager->removeObject(this);
}

#ifdef XAW
#define DEF_DLGW_WIDTH 300
#define DEF_DLGW_HEIGHT 200

inline void resizeBoxChild(Widget wid, Dimension w, Dimension h, Dimension b)
{
	XtUnmanageChild(wid);
	XtResizeWidget(wid, w, h, b);
	XtManageChild(wid);
}

#endif

bool XmDialogWindow::realize()
{
#ifdef XAW
	if(wid)
	{	int x = 0, y = 0, w = 0, h = 0;
		if(initWindowSize(x, y, w, h))
		{	ix = x; iy = y; iw = w; ih = h;
		}
		if(ix) addArg(XtNx, ix);
		if(iy) addArg(XtNy, iy);
		addArg(XtNwidth, iw ? iw : (iw = DEF_DLGW_WIDTH));
		addArg(XtNheight, ih ? ih : (ih = DEF_DLGW_HEIGHT));
		XtSetValues(XtParent(wid), wargs, wargcount);
		wargcount = 0;
		XtSetMappedWhenManaged(wid, FALSE);
		XtRealizeWidget(XtParent(wid));
		if(dropdown)
		{	Arg a;
			Dimension ddh;
			XtSetArg(a, XtNheight, &ddh);
			XtGetValues(dropdown->handle(), &a, 1);
			resizeBoxChild(dropdown->handle(), iw - 2, ddh, 1);
		}
		XtSetMappedWhenManaged(wid, TRUE);
		return(TRUE);
	}
	return(FALSE);
#else
	return(XmUserDialog::realize());
#endif
}

XmControl* XmDialogWindow::findControl(ctrlType t, char* n, bool w)
{
	return(XmDialogPane::findControl(t, n, w));
}

bool XmDialogWindow::add(XmControl* c, XmObject* rec, cbProc cb, void* cd)	
{
	if(dropdown)
	{	Arg a;
		Dimension h = 0;
		XtSetArg(a, XtNheight, (XtArgVal )&h);
		XtGetValues(dropdown->handle(), &a, 1);
#ifdef XAW
		c->cy += h;
#else
		c->cy += 35; 	// did'nt figure out how to get correct motif menu widget size...
#endif
		c->move(c->cx, c->cy);
	}
	return(XmDialogPane::add(c, rec, cb, cd));
}

void XmWindow::initWindowContents(char* n)
{
	ostrstream pn;

	pn << n << "Panes"; pn.put('\0');
#ifdef XAW
	panes = new XmPaneArea(pn.str(), 0, 0, 1, 1);
	add(panes);
	panes->addArg(XtNtop, XtChainTop);
	panes->addArg(XtNleft, XtChainLeft);
	panes->addArg(XtNright, XtChainRight);
	panes->addArg(XtNbottom, XtChainBottom);
	panes->forceArg(NULL, (XtArgVal )NULL);
#else
	panes = new XmPaneArea(pn.str(), 0, 30, 100, 100);
	panes->addArg(XmNtopAttachment, XmATTACH_FORM);
	panes->addArg(XmNleftAttachment, XmATTACH_FORM);
	panes->addArg(XmNrightAttachment, XmATTACH_FORM);
	panes->addArg(XmNbottomAttachment, XmATTACH_FORM);
	add(panes);
#endif

	toolbars[0] = toolbars[1] = toolbars[2] = toolbars[3] = NULL;
	statusbar = NULL;
}

void XmWindow::menuSet()
{
	checkLayout();
}

bool XmWindow::realize()
{
#ifdef XAW
	if(wid)
	{	XtSetMappedWhenManaged(wid, FALSE);
		int x = 0, y = 0, w = 0, h = 0;
		if(initWindowSize(x, y, w, h))
		{	ix = x; iy = y; iw = w; ih = h;
		}
		if(ix) addArg(XtNx, ix);
		if(iy) addArg(XtNy, iy);
		addArg(XtNwidth, iw ? iw : (iw = DEF_DLGW_WIDTH));
		addArg(XtNheight, ih ? ih : (ih = DEF_DLGW_HEIGHT));
		XtSetValues(XtParent(wid), wargs, wargcount);
		wargcount = 0;
		XtRealizeWidget(XtParent(wid));
		Dimension ddh = 0;
		if(dropdown)
		{	Arg a;
			XtSetArg(a, XtNheight, &ddh);
			XtGetValues(dropdown->handle(), &a, 1);
			resizeBoxChild(dropdown->handle(), iw - 2, ddh, 1);
		}
		Dimension tbw[4], tbh[4];
		for(int i = 0; i < 4; i++)
		{	if(toolbars[i])
			{	toolbars[i]->updateDimensions();
				tbw[i] = toolbars[i]->cw;
				tbh[i] = toolbars[i]->ch;
			}
			else
				tbw[i] = tbh[i] = 0;
		}
		Dimension sth = 0;
		if(statusbar)
		{	statusbar->updateDimensions();
			sth = statusbar->ch;
		}
		Dimension h1 = ih - ddh - tbh[2] - tbh[3] - sth - 4;
		if(toolbars[0])
			resizeBoxChild(toolbars[0]->handle(), tbw[0], h1, 1);
		if(toolbars[1])
			resizeBoxChild(toolbars[1]->handle(), tbw[1], h1, 1);
		if(toolbars[2])
			resizeBoxChild(toolbars[2]->handle(), iw, tbh[2], 1);
		if(toolbars[3])
			resizeBoxChild(toolbars[3]->handle(), iw, tbh[3], 1);
		if(statusbar)
			resizeBoxChild(statusbar->handle(), iw, sth, 1);
		Dimension w1 = iw - tbw[0] - tbw[1] - 4;
		resizeBoxChild(panes->handle(), w1, h1, 1);
		XtSetMappedWhenManaged(wid, TRUE);
		return(TRUE);
	}
	return(FALSE);
#else
	return(XmUserDialog::realize());
#endif
}

inline int orNdx(xmStyle s)
{
	return(s == XmSleft ? 0 : (s == XmSright ? 1 : (s == XmSbottom ? 3 : 2)));
}

#ifdef XAW

char* attachmentNames[] =	{ XtNleft, XtNright, XtNtop, XtNbottom };
char* widgetNames[] =		{ XtNfromHoriz, XtNfromHoriz, XtNfromVert, XtNfromVert };

void XmWindow::checkLayout()
{
	if(toolbars[2])
	{	if(dropdown)
			toolbars[2]->addArg(XtNfromVert, (XtArgVal )dropdown->handle());
		else
			toolbars[2]->addArg(XtNfromVert, (XtArgVal )NULL);
		toolbars[2]->addArg(XtNtop, XtChainTop);
		toolbars[2]->addArg(XtNbottom, XtChainTop);
		toolbars[2]->addArg(XtNleft, XtChainLeft);
		toolbars[2]->addArg(XtNright, XtChainRight);
		toolbars[2]->forceArg(NULL, (XtArgVal )NULL);
	}
	if(toolbars[3])
	{	toolbars[3]->addArg(XtNfromVert, (XtArgVal )panes->handle());
		toolbars[3]->addArg(XtNleft, XtChainLeft);
		toolbars[3]->addArg(XtNright, XtChainRight);
		toolbars[3]->addArg(XtNtop, XtChainBottom);
		toolbars[3]->addArg(XtNbottom, XtChainBottom);
		toolbars[3]->forceArg(NULL, (XtArgVal )NULL);
	}
	if(toolbars[0])
	{	if(toolbars[2])
			toolbars[0]->addArg(XtNfromVert, (XtArgVal )toolbars[2]->handle());
		else if(dropdown)
			toolbars[0]->addArg(XtNfromVert, (XtArgVal )dropdown->handle());
		else
			toolbars[0]->addArg(XtNfromVert, (XtArgVal )NULL);
		toolbars[0]->addArg(XtNtop, XtChainTop);
		toolbars[0]->addArg(XtNbottom, XtChainBottom);
		toolbars[0]->addArg(XtNleft, XtChainLeft);
		toolbars[0]->addArg(XtNright, XtChainLeft);
		toolbars[0]->forceArg(NULL, (XtArgVal )NULL);
	}
	if(toolbars[1])
	{	if(toolbars[2])
			toolbars[1]->addArg(XtNfromVert, (XtArgVal )toolbars[2]->handle());
		else if(dropdown)
			toolbars[1]->addArg(XtNfromVert, (XtArgVal )dropdown->handle());
		else
			toolbars[1]->addArg(XtNfromVert, (XtArgVal )NULL);
		toolbars[1]->addArg(XtNtop, XtChainTop);
		toolbars[1]->addArg(XtNfromHoriz, (XtArgVal )panes->handle());
		toolbars[1]->addArg(XtNleft, XtChainRight);
		toolbars[1]->addArg(XtNright, XtChainRight);
		toolbars[1]->addArg(XtNbottom, XtChainBottom);
		toolbars[1]->forceArg(NULL, (XtArgVal )NULL);
	}
	if(statusbar)
	{	if(toolbars[3])
			statusbar->addArg(XtNfromVert, (XtArgVal )toolbars[3]->handle());
		else
			statusbar->addArg(XtNfromVert, (XtArgVal )panes->handle());
		statusbar->addArg(XtNleft, XtChainLeft);
		statusbar->addArg(XtNright, XtChainRight);
		statusbar->addArg(XtNtop, XtChainBottom);
		statusbar->addArg(XtNbottom, XtChainBottom);
		statusbar->forceArg(NULL, (XtArgVal )NULL);
	}
	if(toolbars[2])
		panes->addArg(XtNfromVert, (XtArgVal )toolbars[2]->handle());
	else if(dropdown)
		panes->addArg(XtNfromVert, (XtArgVal )dropdown->handle());
	else
		panes->addArg(XtNfromVert, (XtArgVal )NULL);
	if(toolbars[0])
		panes->addArg(XtNfromHoriz, (XtArgVal )toolbars[0]->handle());
	else
		panes->addArg(XtNfromHoriz, (XtArgVal )NULL);
	panes->addArg(XtNleft, XtChainLeft);
	panes->addArg(XtNright, XtChainRight);
	panes->addArg(XtNtop, XtChainTop);
	panes->addArg(XtNbottom, XtChainBottom);
	panes->forceArg(NULL, (XtArgVal )NULL);
}

#else

char* attachmentNames[] =	{ XmNleftAttachment, XmNrightAttachment, XmNtopAttachment, XmNbottomAttachment };
char* widgetNames[] =		{ XmNleftWidget, XmNrightWidget, XmNtopWidget, XmNbottomWidget };

void XmWindow::checkLayout()
{
	if(toolbars[2])
	{	toolbars[2]->addArg(XmNtopAttachment, dropdown ? XmATTACH_WIDGET : XmATTACH_FORM);
		if(dropdown)
			toolbars[2]->addArg(XmNtopWidget, (XtArgVal )dropdown->handle());
		toolbars[2]->addArg(XmNleftAttachment, XmATTACH_FORM);
		toolbars[2]->addArg(XmNrightAttachment, XmATTACH_FORM);
		toolbars[2]->addArg(XmNbottomAttachment, XmATTACH_NONE);
		toolbars[2]->addArg(XmNtopOffset, 0);	// why...?
		toolbars[2]->forceArg(NULL, (XtArgVal )NULL);
	}
	if(toolbars[3])
	{	toolbars[3]->addArg(XmNbottomAttachment, statusbar ? XmATTACH_WIDGET : XmATTACH_FORM);
		if(statusbar)
			toolbars[3]->addArg(XmNbottomWidget, (XtArgVal )statusbar->handle());
		toolbars[3]->addArg(XmNleftAttachment, XmATTACH_FORM);
		toolbars[3]->addArg(XmNrightAttachment, XmATTACH_FORM);
		toolbars[3]->addArg(XmNtopAttachment, XmATTACH_NONE);
		toolbars[3]->forceArg(NULL, (XtArgVal )NULL);
	}
	if(toolbars[0])
	{	toolbars[0]->addArg(XmNtopAttachment, ((toolbars[2] || dropdown) ? XmATTACH_WIDGET : XmATTACH_FORM));
		if((toolbars[2]) || dropdown)
			toolbars[0]->addArg(XmNtopWidget, (XtArgVal )(toolbars[2] ? toolbars[2]->handle() : dropdown->handle()));
		toolbars[0]->addArg(XmNbottomAttachment, (toolbars[3] || statusbar) ? XmATTACH_WIDGET : XmATTACH_FORM);
		if(toolbars[3] || statusbar)
			toolbars[0]->addArg(XmNbottomWidget, (XtArgVal )(toolbars[3] ? toolbars[3]->handle() : statusbar->handle()));
		toolbars[0]->addArg(XmNleftAttachment, XmATTACH_FORM);
		toolbars[0]->addArg(XmNrightAttachment, XmATTACH_NONE);
		toolbars[0]->addArg(XmNtopOffset, 0);	// why...?
		toolbars[0]->forceArg(NULL, (XtArgVal )NULL);
	}
	if(toolbars[1])
	{	toolbars[1]->addArg(XmNtopAttachment, (toolbars[2] || dropdown) ? XmATTACH_WIDGET : XmATTACH_FORM);
		if(toolbars[2] || dropdown)
			toolbars[1]->addArg(XmNtopWidget, (XtArgVal )(toolbars[2] ? toolbars[2]->handle() : dropdown->handle()));
		toolbars[1]->addArg(XmNbottomAttachment, (toolbars[3] || statusbar) ? XmATTACH_WIDGET : XmATTACH_FORM);
		if(toolbars[3] || statusbar)
			toolbars[1]->addArg(XmNbottomWidget, (XtArgVal )(toolbars[3] ? toolbars[3]->handle() : statusbar->handle()));
		toolbars[1]->addArg(XmNrightAttachment, XmATTACH_FORM);
		toolbars[1]->addArg(XmNleftAttachment, XmATTACH_NONE);
		toolbars[1]->addArg(XmNtopOffset, 0);	// why...?
		toolbars[1]->forceArg(NULL, (XtArgVal )NULL);
	}
	panes->addArg(XmNtopAttachment, (toolbars[2] || dropdown) ? XmATTACH_WIDGET : XmATTACH_FORM);
	if(toolbars[2] || dropdown)
		panes->addArg(XmNtopWidget, (XtArgVal )(toolbars[2] ? toolbars[2]->handle() : dropdown->handle()));
	panes->addArg(XmNbottomAttachment, (toolbars[3] || statusbar) ? XmATTACH_WIDGET : XmATTACH_FORM);
	if(toolbars[3] || statusbar)
		panes->addArg(XmNbottomWidget, (XtArgVal )(toolbars[3] ? toolbars[3]->handle() : statusbar->handle()));
	panes->addArg(XmNleftAttachment, (toolbars[0]) ? XmATTACH_WIDGET : XmATTACH_FORM);
	if(toolbars[0])
		panes->addArg(XmNleftWidget, (XtArgVal )toolbars[0]->handle());
	panes->addArg(XmNrightAttachment, (toolbars[1]) ? XmATTACH_WIDGET : XmATTACH_FORM);
	if(toolbars[1])
		panes->addArg(XmNrightWidget, (XtArgVal )toolbars[1]->handle());
	panes->forceArg(NULL, (XtArgVal )NULL);
}

#endif

#include "xmDrawing.h"

bool XmWindow::addSubpane(ctrlType ct, char* n, char* l, int y, int h, XmObject* rec, cbProc cb)
{
	XmControl* aControl;

	if(!panes->numSubpanes())
		checkLayout();

	switch(ct)
	{	case Edit:
		aControl = new XmEdit(n, 0, y, 10, h, XmSautovscroll | XmSautohscroll);
		break;
		case ListBox:
		aControl = new XmListBox(n, 0, y, 10, h, XmSautovscroll | XmSautohscroll);
		break;
		case Drawing:
		aControl = new XmDrawing(n, 0, y, 10, h, XmSautovscroll | XmSautohscroll);
		break;
		case GroupBox: case StaticText: case StaticImage: case PushButton:
		case CheckBox: case RadioButton: case ScrollBar: case ComboBox: case Pane: 
		case PaneArea: case ToolBar:
		cerr << "xmWindow: Warning - control type " << ct << " not supported as subpane, ignoring...\n"; 
		return(FALSE);
	}
	return(addSubpane(aControl, l, rec, cb));
}

XmToolBar* XmWindow::addToolbar(xmStyle s)
{
	ostrstream tbName;
	int ndx = orNdx(s);
	XmToolBar* aToolbar;

	if(toolbars[ndx])
		return(NULL);
	tbName << wname << "-Toolbar-" << ndx; tbName.put('\0');
	toolbars[ndx] = aToolbar = new XmToolBar(tbName.str(), (s == XmSleft || s == XmSright) ? XmSvertical : XmShorizontal);
	add(aToolbar);
	checkLayout();
	return(aToolbar);
}

bool XmWindow::removeToolbar(XmToolBar* tb)
{
	int ndx = -1;

	for(int i = 0; i < 4; i++)
	{	if(toolbars[i] == tb)
		{	ndx = i;
			break;
		}
	}
	if(ndx == -1)
		return(FALSE);
	toolbars[ndx] = NULL;
	checkLayout();
	remove((char* )tb->getName());
	return(TRUE);
}

XmToolBar* XmWindow::toolbar(xmStyle s)
{
	return(toolbars[orNdx(s)]);
}

XmStatusBar* XmWindow::createStatusBar()
{
	ostrstream stbName;

	if(statusbar)
		return(NULL);

	stbName << wname << "-Statusbar"; stbName.put('\0');
	statusbar = new XmStatusBar(stbName.str());
#ifdef XAW
	statusbar->addArg(XtNleft, XtChainLeft);
	statusbar->addArg(XtNright, XtChainRight);
	statusbar->addArg(XtNbottom, XtChainBottom);
#else
	statusbar->addArg(XmNleftAttachment, XmATTACH_FORM);
	statusbar->addArg(XmNrightAttachment, XmATTACH_FORM);
	statusbar->addArg(XmNbottomAttachment, XmATTACH_FORM);
#endif
	add(statusbar);
	checkLayout();
	return(statusbar);
}

#ifndef __GNUG__

#define tbWinStyle (XmSdlgModeless | XmSbordered | XmSmoveable | XmScloseable | XmStitled | XmSsysMenu)

XmToolBox::XmToolBox(char* n, XmObject* par, xmStyle s, int r) : XmDialogWindow(n, par, tbWinStyle), XmToolBar(n, s)
{
	setRows(r);
	XmDialogWindow::add(this);
}

XmToolBox::XmToolBox(char* n, XmObject* par, XmManager* m, xmStyle s, int r) : XmDialogWindow(n, par, m, tbWinStyle), XmToolBar(n, s)
{
	setRows(r);
	XmDialogWindow::add(this);
}

#endif
