/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Microline Widget Library, originally made available under the NPL by Neuron Data <http://www.neurondata.com>.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * In addition, as a special exception to the GNU GPL, the copyright holders
 * give permission to link the code of this program with the Motif and Open
 * Motif libraries (or with modified versions of these that use the same
 * license), and distribute linked combinations including the two. You
 * must obey the GNU General Public License in all respects for all of
 * the code used other than linking with Motif/Open Motif. If you modify
 * this file, you may extend this exception to your version of the file,
 * but you are not obligated to do so. If you do not wish to do so,
 * delete this exception statement from your version.
 *
 * ***** END LICENSE BLOCK ***** */

#include "XmL.h"
#include <Xm/XmP.h>
#include <Xm/LabelP.h>
#include <Xm/DrawnBP.h>
#include <Xm/MessageB.h>
#include <Xm/Protocols.h>
#include <Xm/AtomMgr.h>
#ifdef MOTIF11
#include <Xm/VendorE.h>
#else
#include <Xm/VendorS.h>
#endif
#include <Xm/BulletinB.h>
#include <Xm/MenuShell.h>

#include <stdlib.h>
#include <stdio.h>

#ifdef SUNOS4
int fprintf(FILE *, char *, ...);
#endif

static void XmLDrawnBDestroyCB(Widget w, XtPointer clientData, XtPointer);
static void XmLDrawnBDrawCB(Widget, XtPointer, XtPointer);
static void XmLDrawnBDrawStringCB(Widget, XtPointer, XtPointer);
static int XmLDrawCalc(Widget w, Dimension width, Dimension height,
	unsigned char alignment, XRectangle *rect, XRectangle *clipRect,
	int *x, int *y);
static void XmLFontGetAverageWidth(XFontStruct *fs, short *width);
static void XmLMessageBoxResponse(Widget, XtPointer, XtPointer);
static void XmLMessageBoxWMDelete(Widget, XtPointer, XtPointer);
static void XmLSortFunc(char *lvec, char *rvec);

struct _XmLArrayRec
	{
	char _autonumber, _growFast;
	int _count, _size;
	void **_items;
	};

XmLArray
XmLArrayNew(char autonumber,
	    char growFast)
	{
	XmLArray array;

	array = (XmLArray)malloc(sizeof(struct _XmLArrayRec));
	array->_count = 0;
	array->_size = 0;
	array->_items = 0;
	array->_autonumber = autonumber;
	array->_growFast = growFast;
	return array;
	}

void
XmLArrayFree(XmLArray array)
	{
	if (array->_items)
		free((char *)array->_items);
	free((char *)array);
	}

void
XmLArrayAdd(XmLArray array,
	    int pos,
	    int count)
	{
	int i;
	void **items;

	if (count < 1)
		return;
	if (pos < 0 || pos > array->_count)
		pos = array->_count;
	if (array->_count + count >= array->_size)
		{
		if (array->_growFast)
			{
			if (!array->_size)
				array->_size = count + 256;
			else
				array->_size = (array->_count + count) * 2;
			}
		else
			array->_size = array->_count + count;
		items = (void **)malloc(sizeof(void *) * array->_size);
		if (array->_items)
			{
			for (i = 0; i < array->_count; i++)
				items[i] = array->_items[i];
			free((char *)array->_items);
			}
		array->_items = items;
		}
	for (i = array->_count + count - 1; i >= pos + count; i--)
		{
		array->_items[i] = array->_items[i - count];
		if (array->_autonumber)
			((XmLArrayItem *)array->_items[i])->pos = i;
		}
	for (i = pos; i < pos + count; i++)
		array->_items[i] = 0;
	array->_count += count;
	}

int
XmLArrayDel(XmLArray array,
	    int pos,
	    int count)
	{
	int i;

	if (pos < 0 || pos + count > array->_count)
		return -1;
	for (i = pos; i < array->_count - count; i++)
		{
		array->_items[i] = array->_items[i + count];
		if (array->_autonumber)
			((XmLArrayItem *)array->_items[i])->pos = i;
		}
	array->_count -= count;
	if (!array->_count)
		{
		if (array->_items)
			free((char *)array->_items);
		array->_items = 0;
		array->_size = 0;
		}
	return 0;
	}

int
XmLArraySet(XmLArray array,
	    int pos,
	    void *item)
	{
	if (pos < 0 || pos >= array->_count)
		return -1;
	if (array->_items[pos])
		fprintf(stderr, "XmLArraySet: warning: overwriting pointer\n");
	array->_items[pos] = item;
	if (array->_autonumber)
		((XmLArrayItem *)array->_items[pos])->pos = pos;
	return 0;
	}

void *
XmLArrayGet(XmLArray array,
	    int pos)
	{
	if (pos < 0 || pos >= array->_count)
		return 0;
	return array->_items[pos];
	}

int
XmLArrayGetCount(XmLArray array)
	{
	return array->_count;
	}

int
XmLArrayMove(XmLArray array,
	     int newPos,
	     int pos,
	     int count)
	{
	void **items;
	int i;

	if (count <= 0)
		return -1;
	if (newPos < 0 || newPos + count > array->_count)
		return -1;
	if (pos < 0 || pos + count > array->_count)
		return -1;
	if (pos == newPos)
		return 0;
	/* copy items to move */
	items = (void **)malloc(sizeof(void *) * count);
	for (i = 0; i < count; i++)
		items[i] = array->_items[pos + i];
	/* move real items around */
	if (newPos < pos)
		for (i = pos + count - 1; i >= newPos + count; i--)
			{
			array->_items[i] = array->_items[i - count];
			if (array->_autonumber)
				((XmLArrayItem *)array->_items[i])->pos = i;
			}
	else
		for (i = pos; i < newPos; i++)
			{
			array->_items[i] = array->_items[i + count];
			if (array->_autonumber)
				((XmLArrayItem *)array->_items[i])->pos = i;
			}
	/* move items copy back */
	for (i = 0; i < count; i++)
		{
		array->_items[newPos + i] = items[i];
		if (array->_autonumber)
			((XmLArrayItem *)array->_items[newPos + i])->pos = newPos + i;
		}
	free((char *)items);
	return 0;
	}

int
XmLArrayReorder(XmLArray array,
		int *newPositions,
		int pos,
		int count)
	{
	int i;
	void **items;

	if (count <= 0)
		return -1;
	if (pos < 0 || pos + count > array->_count)
		return -1;
	for (i = 0; i < count; i++)
		{
		if (newPositions[i] < pos || newPositions[i] >= pos + count)
			return -1;
		}
	items = (void **)malloc(sizeof(void *) * count);
	for (i = 0; i < count; i++)
		items[i] = array->_items[newPositions[i]];
	for (i = 0; i < count; i++)
		{
		array->_items[pos + i] = items[i];
		if (array->_autonumber)
			((XmLArrayItem *)array->_items[pos + i])->pos = pos + i;
		}
	free((char *)items);
	return 0;
	}

int
XmLArraySort(XmLArray array,
	     XmLArrayCompareFunc compare,
	     void *userData,
	     int pos,
	     int count)
	{
	int i;

	if (pos < 0 || pos + count > array->_count)
		return -1;
	XmLSort(&array->_items[pos], count, sizeof(void *),
		(XmLSortCompareFunc)compare, userData);
	if (array->_autonumber)
		for (i = pos; i < pos + count; i++)
			((XmLArrayItem *)array->_items[i])->pos = i;
	return 0;
	}


Boolean
XmLCvtStringToUChar(Display *dpy,
		    char *resname,
		    XmLStringToUCharMap *map,
		    XrmValuePtr fromVal,
		    XrmValuePtr toVal)
{
	char *from;
	int i, /*num,*/ valid;

	from = (char *)fromVal->addr;
	valid = 0;
	i = 0;
	while (map[i].name)
		{
		if (!strcmp(from, map[i].name))
			{
			valid = 1;
			break;
			}
		i++;
		}
	if (!valid)
		{
		XtDisplayStringConversionWarning(dpy, from, resname);
		toVal->size = 0;
		toVal->addr = 0;
		return False;
		}
	if (toVal->addr)
		{
		if (toVal->size < sizeof(unsigned char))
			{
			toVal->size = sizeof(unsigned char);
			return False;
			}
		*(unsigned char *)(toVal->addr) = map[i].value;
		}
	else
		toVal->addr = (caddr_t)&map[i].value;
	toVal->size = sizeof(unsigned char);
	return True;
	}

int
XmLDateDaysInMonth(int m,
		   int y)
{
	static int d[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

	if (m < 1 || m > 12 || y < 1753 || y > 9999)
		return -1;
	if (m == 2 && (!((y % 4) && (y % 100)) || !(y % 400)))
		return 29;
	return d[m - 1];
}

/* Calculation from Communications Of The ACM, Vol 6, No 8, p 444 */
/* sun is 0, sat is 6 */
int
XmLDateWeekDay(int m,
	       int d,
	       int y)
{
	long jd, j1, j2;

	if (m < 1 || m > 12 || d < 1 || d > XmLDateDaysInMonth(m, y) ||
		y < 1753 || y > 9999)
		return -1;
	if (m > 2)
		m -= 3;
	else
		{
		m += 9;
		y--;
		}
	j1 = y / 100;
	j2 = y - 100 * j1;
	jd = (146097 * j1) / 4 + (1461 * j2) / 4 + (153 * m + 2) / 5 +
		1721119 + d;
        return (jd + 1) % 7;
}

typedef struct
	{
	GC gc;
	int type;
	int dir;
	XFontStruct *fontStruct;
	} XmLDrawnBData;

void
XmLDrawnButtonSetType(Widget w,
		      int drawnType,
		      int drawnDir)
	{
	XmLDrawnBData *dd;
	XmDrawnButtonWidget b;
	XmString str;
	XmFontList fontlist;
	XGCValues values;
	XtGCMask mask;
	Dimension width, height, dim;
	Dimension highlightThickness, shadowThickness;
	Dimension marginWidth, marginHeight;
	Dimension marginTop, marginBottom, marginLeft, marginRight;

	if (!XtIsSubclass(w, xmDrawnButtonWidgetClass))
		{
		XmLWarning(w, "DrawnButtonSetType() - not an XmDrawnButton");
		return;
		}
	XtVaSetValues(w,
		XmNpushButtonEnabled, True,
		NULL);
	XtRemoveAllCallbacks(w, XmNexposeCallback);
	XtRemoveAllCallbacks(w, XmNresizeCallback);
	if (drawnType == XmDRAWNB_STRING && drawnDir == XmDRAWNB_RIGHT)
		{
		XtVaSetValues(w,
			XmNlabelType, XmSTRING,
			NULL);
		return;
		}
	b = (XmDrawnButtonWidget)w;
	dd = (XmLDrawnBData *)malloc(sizeof(XmLDrawnBData));
	dd->type = drawnType;
	dd->dir = drawnDir;
	dd->gc = 0;
	if (dd->type == XmDRAWNB_STRING)
		{
		XtVaGetValues(w,
			XmNlabelString, &str,
			XmNfontList, &fontlist,
			XmNhighlightThickness, &highlightThickness,
			XmNshadowThickness, &shadowThickness,
			XmNmarginHeight, &marginHeight,
			XmNmarginWidth, &marginWidth,
			XmNmarginTop, &marginTop,
			XmNmarginBottom, &marginBottom,
			XmNmarginLeft, &marginLeft,
			XmNmarginRight, &marginRight,
			NULL);
		if (!str && XtName(w))
			str = XmStringCreateSimple(XtName(w));
		if (!str)
			str = XmStringCreateSimple("");
		XmStringExtent(fontlist, str, &width, &height);
		XmStringFree(str);
		if (drawnDir == XmDRAWNB_UP || drawnDir == XmDRAWNB_DOWN)
			{
			dim = width;
			width = height;
			height = dim;
			}
		height += (highlightThickness + shadowThickness +
			marginHeight) * 2 + marginTop + marginBottom;
		width += (highlightThickness + shadowThickness +
			marginWidth) * 2 + marginLeft + marginRight;
		/* change to pixmap type so label string isnt drawn */
		XtVaSetValues(w,
			XmNlabelType, XmPIXMAP,
			NULL);
		XtVaSetValues(w,
			XmNwidth, width,
			XmNheight, height,
			NULL);
		XtAddCallback(w, XmNexposeCallback, XmLDrawnBDrawStringCB,
			(XtPointer)dd);
		XtAddCallback(w, XmNresizeCallback, XmLDrawnBDrawStringCB,
			(XtPointer)dd);
		}
	else
		{
		mask = GCForeground;
		values.foreground = b->primitive.foreground;
		dd->gc = XtGetGC(w, mask, &values);
		XtAddCallback(w, XmNexposeCallback, XmLDrawnBDrawCB, (XtPointer)dd);
		XtAddCallback(w, XmNresizeCallback, XmLDrawnBDrawCB, (XtPointer)dd);
		}
	XtAddCallback(w, XmNdestroyCallback, XmLDrawnBDestroyCB, (XtPointer)dd);
	}

static void
XmLDrawnBDestroyCB(Widget w,
		   XtPointer clientData,
		   XtPointer callData)
	{
	XmLDrawnBData *dd;

	dd = (XmLDrawnBData *)clientData;
	if (dd->type == XmDRAWNB_STRING)
		{
		if (dd->gc)
			{
			XFreeGC(XtDisplay(w), dd->gc);
			XFreeFont(XtDisplay(w), dd->fontStruct);
			}
		}
	else
		XtReleaseGC(w, dd->gc);
	free((char *)dd);
	}

static void
XmLDrawnBDrawStringCB(Widget w,
		      XtPointer clientData,
		      XtPointer callData)
	{
	XmLDrawnBData *dd;
	XmFontList fontlist;
	XmString str;
	XmStringDirection stringDir;
	unsigned char drawDir, alignment;
	int width, height, xoff, yoff, drawWidth;
	Pixel fg;
	Dimension highlightThickness;
	Dimension shadowThickness, marginWidth, marginHeight;
	Dimension marginLeft, marginRight, marginTop, marginBottom;

	if (!XtIsRealized(w))
		return;
	dd = (XmLDrawnBData *)clientData;
	XtVaGetValues(w,
		XmNlabelString, &str,
		NULL);
	if (!str && XtName(w))
		str = XmStringCreateSimple(XtName(w));
	if (!str)
		return;
	XtVaGetValues(w,
		XmNforeground, &fg,
		XmNfontList, &fontlist,
		XmNalignment, &alignment,
		XmNhighlightThickness, &highlightThickness,
		XmNshadowThickness, &shadowThickness,
		XmNmarginWidth, &marginWidth,
		XmNmarginHeight, &marginHeight, 
		XmNmarginLeft, &marginLeft,
		XmNmarginRight, &marginRight,
		XmNmarginTop, &marginTop,
		XmNmarginBottom, &marginBottom,
		NULL);
	xoff = highlightThickness + shadowThickness + marginLeft + marginWidth;
	yoff = highlightThickness + shadowThickness + marginTop + marginHeight;
	width = XtWidth(w) - xoff - xoff + marginLeft - marginRight;
	height = XtHeight(w) - yoff - yoff + marginTop - marginBottom;
	if (XmIsManager(XtParent(w)))
		XtVaGetValues(XtParent(w),
			XmNstringDirection, &stringDir,
			NULL);
	else
		stringDir = XmSTRING_DIRECTION_L_TO_R;
	switch (dd->dir)
		{
		case XmDRAWNB_LEFT:
			drawDir = XmSTRING_LEFT;
			break;
		case XmDRAWNB_UP:
			drawDir = XmSTRING_UP;
			break;
		case XmDRAWNB_DOWN:
			drawDir = XmSTRING_DOWN;
			break;
		default:
			drawDir = XmSTRING_RIGHT;
			break;
		}
	if (drawDir == XmSTRING_LEFT || drawDir == XmSTRING_RIGHT)
		drawWidth = width;
	else
		drawWidth = height;
	if (!dd->gc)
		{
		dd->gc = XCreateGC(XtDisplay(w), XtWindow(w), 0, NULL);
		dd->fontStruct = XLoadQueryFont(XtDisplay(w), "fixed");
		if (!dd->fontStruct)
			{
			XmLWarning(w, "DrawnBDrawString() - FATAL can't load fixed font");
			return;
			}
		XSetFont(XtDisplay(w), dd->gc, dd->fontStruct->fid);
		}
	XSetForeground(XtDisplay(w), dd->gc, fg);
	XmLStringDrawDirection(XtDisplay(w), XtWindow(w), fontlist,
		str, dd->gc, xoff, yoff, drawWidth, alignment, stringDir, drawDir);
	XmStringFree(str);
	}

static void
XmLDrawnBDrawCB(Widget w,
		XtPointer clientData,
		XtPointer callData)
	{
	XmLDrawnBData *dd;
	XmDrawnButtonWidget b;
	/*	unsigned char drawDir;*/
	/*	unsigned char alignment;*/
	Display *dpy;
	Window win;
	GC gc;
	XPoint p[2][5];
	XSegment seg;
	int np[2];
	int i, j, temp;
	int md, type, dir;
	int avgx, avgy, xoff, yoff, st;

	if (!XtIsRealized(w))
		return;
	dd = (XmLDrawnBData *)clientData;
	type = dd->type;
	dir = dd->dir;
	gc = dd->gc;
	b = (XmDrawnButtonWidget)w;
	win = XtWindow(w);
	dpy = XtDisplay(w);
	st = b->primitive.shadow_thickness;
	i = st * 2 + b->primitive.highlight_thickness * 2;
	/* calculate max dimension */
	md = XtWidth(w) - i;
	if (md > ((int)XtHeight(w) - i))
		md = XtHeight(w) - i;
	if (md < 4)
		return;
	xoff = ((int)XtWidth(w) - md) / 2;
	yoff = ((int)XtHeight(w) - md) / 2;
	np[0] = 0;
	np[1] = 0;
	switch (type)
		{
		case XmDRAWNB_SMALLARROW:
			p[0][0].x = md / 4;
			p[0][0].y = md / 4;
			p[0][1].x = md / 4;
			p[0][1].y = md - md / 4;
			p[0][2].x = md - md / 4;
			p[0][2].y = md / 2;
			np[0] = 3;
			break;
		case XmDRAWNB_ARROW:
			p[0][0].x = md / 6;
			p[0][0].y = md / 6;
			p[0][1].x = md / 6;
			p[0][1].y = md - md / 6;
			p[0][2].x = md - md / 6;
			p[0][2].y = md / 2;
			np[0] = 3;
			break;
		case XmDRAWNB_ARROWLINE:
			p[0][0].x = md / 5;
			p[0][0].y = md / 5;
			p[0][1].x = md / 5;
			p[0][1].y = md - md / 5;
			p[0][2].x = md - md / 5;
			p[0][2].y = md / 2;
			np[0] = 3;
			p[1][0].x = md - md / 5 + 1;
			p[1][0].y = md / 5;
			p[1][1].x = md - md / 5 + 1;
			p[1][1].y = md - md / 5;
			p[1][2].x = md - md / 10;
			p[1][2].y = md - md / 5;
			p[1][3].x = md - md / 10;
			p[1][3].y = md / 5;
			np[1] = 4;
			break;
		case XmDRAWNB_DOUBLEARROW:
			/* odd major dimensions can give jagged lines */
			if (md % 2)
				md -= 1;
			p[0][0].x = md / 10;
			p[0][0].y = md / 10;
			p[0][1].x = md / 10;
			p[0][1].y = md - md / 10;
			p[0][2].x = md / 2;
			p[0][2].y = md / 2;
			np[0] = 3;
			p[1][0].x = md - md / 2;
			p[1][0].y = md / 10;
			p[1][1].x = md - md / 2;
			p[1][1].y = md - md / 10;
			p[1][2].x = md - md / 10;
			p[1][2].y = md / 2;
			np[1] = 3;
			break;
		case XmDRAWNB_SQUARE:
			p[0][0].x = md / 3;
			p[0][0].y = md / 3;
			p[0][1].x = md / 3;
			p[0][1].y = md - md / 3;
			p[0][2].x = md - md / 3;
			p[0][2].y = md - md / 3;
			p[0][3].x = md - md / 3;
			p[0][3].y = md / 3;
			np[0] = 4;
			break;
		case XmDRAWNB_DOUBLEBAR:
			p[0][0].x = md / 3;
			p[0][0].y = md / 4;
			p[0][1].x = md / 3;
			p[0][1].y = md - md / 4;
			p[0][2].x = md / 2 - md / 10;
			p[0][2].y = md - md / 4;
			p[0][3].x = md / 2 - md / 10;
			p[0][3].y = md / 4;
			np[0] = 4;
			p[1][0].x = md - md / 3;
			p[1][0].y = md / 4;
			p[1][1].x = md - md / 3;
			p[1][1].y = md - md / 4;
			p[1][2].x = md - md / 2 + md / 10;
			p[1][2].y = md - md / 4;
			p[1][3].x = md - md / 2 + md / 10;
			p[1][3].y = md / 4;
			np[1] = 4;
			break;
		}
	for (i = 0; i < 2; i++)
		{
		avgx = 0;
		avgy = 0;
		for (j = 0; j < np[i]; j++)
			{
			switch (dir)
				{
				case XmDRAWNB_RIGHT:
					/* points unchanged */
					break;
				case XmDRAWNB_LEFT:
					p[i][j].x = md - p[i][j].x - 1;
					break;
				case XmDRAWNB_UP:
					temp = p[i][j].x;
					p[i][j].x = p[i][j].y;
					p[i][j].y = md - temp;
					break;
				case XmDRAWNB_DOWN:
					temp = p[i][j].x;
					p[i][j].x = p[i][j].y;
					p[i][j].y = temp;
					break;
			}
			p[i][j].x += xoff;
			p[i][j].y += yoff;
			avgx += p[i][j].x;
			avgy += p[i][j].y;
			}
		if (!np[i])
			continue;
		avgx /= np[i];
		avgy /= np[i];
		XFillPolygon(dpy, win, gc, p[i], np[i], Nonconvex, CoordModeOrigin);
		p[i][np[i]].x = p[i][0].x;
		p[i][np[i]].y = p[i][0].y;
		for (j = 0; j < np[i]; j++)
			{
			seg.x1 = p[i][j].x;
			seg.y1 = p[i][j].y;
			seg.x2 = p[i][j + 1].x;
			seg.y2 = p[i][j + 1].y;
			if ((seg.x1 <= avgx && seg.x2 <= avgx) ||
				(seg.y1 <= avgy && seg.y2 <= avgy))
				XDrawSegments(dpy, win,
					b->primitive.bottom_shadow_GC, &seg, 1);
			else
				XDrawSegments(dpy, win,
					b->primitive.top_shadow_GC, &seg, 1);
			}
		}
	}

#define XmLDrawNODRAW  0
#define XmLDrawNOCLIP  1
#define XmLDrawCLIPPED 2

static int
XmLDrawCalc(Widget w,
	    Dimension width,
	    Dimension height,
	    unsigned char alignment,
	    XRectangle *rect,
	    XRectangle *clipRect,
	    int *x,
	    int *y)
	{
	if (rect->width <= 4 || rect->height <= 4 ||
		clipRect->width < 3 || clipRect->height < 3 ||
		!width || !height ||
		!XtIsRealized(w) ||
		XmLRectIntersect(rect, clipRect) == XmLRectOutside)
		return XmLDrawNODRAW;
	if (alignment == XmALIGNMENT_TOP_LEFT ||
		alignment == XmALIGNMENT_LEFT ||
		alignment == XmALIGNMENT_BOTTOM_LEFT)
		*x = rect->x + 2;
	else if (alignment == XmALIGNMENT_TOP ||
		alignment == XmALIGNMENT_CENTER ||
		alignment == XmALIGNMENT_BOTTOM)
		*x = rect->x + ((int)rect->width - (int)width) / 2;
	else
		*x = rect->x + rect->width - width - 2;
	if (alignment == XmALIGNMENT_TOP ||
		alignment == XmALIGNMENT_TOP_LEFT ||
		alignment == XmALIGNMENT_TOP_RIGHT)
		*y = rect->y + 2;
	else if (alignment == XmALIGNMENT_LEFT ||
		alignment == XmALIGNMENT_CENTER ||
		alignment == XmALIGNMENT_RIGHT)
		*y = rect->y + ((int)rect->height - (int)height) / 2;
	else
		*y = rect->y + rect->height - height - 2;
	if (clipRect->x == rect->x &&
		clipRect->y == rect->y &&
		clipRect->width == rect->width &&
		clipRect->height == rect->height &&
		(int)width + 4 <= (int)clipRect->width &&
		(int)height + 4 <= (int)clipRect->height)
		return XmLDrawNOCLIP;
	return XmLDrawCLIPPED;
	}

void
XmLDrawToggle(Widget w,
	      Boolean state,
	      Dimension size,
	      unsigned char alignment,
	      GC gc,
	      Pixel backgroundColor,
	      Pixel topColor,
	      Pixel bottomColor,
	      Pixel checkColor,
	      XRectangle *rect,
	      XRectangle *clipRect)
	{
	Display *dpy;
	Window win;
	XPoint point[5];
	int x, y, cx[3], cy[4], drawType;

	drawType = XmLDrawCalc(w, size, size, alignment, rect, clipRect, &x, &y);
	if (size < 3 || drawType == XmLDrawNODRAW)
		return;
	dpy = XtDisplay(w);
	win = XtWindow(w);
	if (drawType == XmLDrawCLIPPED)
		XSetClipRectangles(dpy, gc, 0, 0, clipRect, 1, Unsorted);
	/* background */
	XSetForeground(dpy, gc, backgroundColor);
	XFillRectangle(dpy, win, gc, x, y, size, size);
	/* box shadow */
	XSetForeground(dpy, gc, topColor);
	point[0].x = x;
	point[0].y = y + size - 1;
	point[1].x = x;
	point[1].y = y;
	point[2].x = x + size - 1;
	point[2].y = y;
	XDrawLines(dpy, win, gc, point, 3, CoordModeOrigin);
	point[1].x = x + size - 1;
	point[1].y = y + size - 1;
	XSetForeground(dpy, gc, bottomColor);
	XDrawLines(dpy, win, gc, point, 3, CoordModeOrigin);
	if (state == True)
		{
		/* check */
		cx[0] = x + 1;
		cx[1] = x + (((int)size - 3) / 3) + 1;
		cx[2] = x + size - 2;
		cy[0] = y + 1;
		cy[1] = y + (((int)size - 3) / 2) + 1;
		cy[2] = y + ((((int)size - 3) * 2) / 3) + 1;
		cy[3] = y + size - 2;
		point[0].x = cx[0];
		point[0].y = cy[1];
		point[1].x = cx[1];
		point[1].y = cy[3];
		point[2].x = cx[2];
		point[2].y = cy[0];
		point[3].x = cx[1];
		point[3].y = cy[2];
		point[4].x = point[0].x;
		point[4].y = point[0].y;
		XSetForeground(dpy, gc, checkColor);
		XFillPolygon(dpy, win, gc, point, 4, Nonconvex, CoordModeOrigin);
		XDrawLines(dpy, win, gc, point, 5, CoordModeOrigin);
		}
	if (drawType == XmLDrawCLIPPED)
		XSetClipMask(dpy, gc, None);
	}

int
XmLRectIntersect(XRectangle *r1,
		 XRectangle *r2)
	{
	if (!r1->width || !r1->height || !r2->width || !r2->height)
		return XmLRectOutside;
	if (r1->x + (int)r1->width - 1 < r2->x ||
		r1->x > r2->x + (int)r2->width - 1 ||
		r1->y + (int)r1->height - 1 < r2->y ||
		r1->y > r2->y + (int)r2->height - 1)
		return XmLRectOutside;
	if (r1->x >= r2->x &&
		r1->x + (int)r1->width <= r2->x + (int)r2->width &&
		r1->y >= r2->y &&
		r1->y + (int)r1->height <= r2->y + (int)r2->height)
		return XmLRectInside; /* r1 inside r2 */
	return XmLRectPartial;
	}


XmFontList
XmLFontListCopyDefault(Widget widget)
	{
	Widget parent;
	XFontStruct *font;
	XmFontList fontList, fl;

	fontList = 0;
	parent = XtParent(widget);
	while (parent)
		{
		fl = 0;
		if (XmIsVendorShell(parent) || XmIsMenuShell(parent))
			XtVaGetValues(parent, XmNdefaultFontList, &fl, NULL);
		else if (XmIsBulletinBoard(parent))
			XtVaGetValues(parent, XmNbuttonFontList, &fl, NULL);
		if (fl)
			{
			fontList = XmFontListCopy(fl);
			parent = 0;
			}
		if (parent)
			parent = XtParent(parent);
		}
	if (!fontList)
		{
		font = XLoadQueryFont(XtDisplay(widget), "fixed");
		if (!font)
			XmLWarning(widget,
				"FontListCopyDefault() - FATAL ERROR - can't load fixed font");
		fontList = XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET);
		}
	return fontList;
	}

void
XmLFontListGetDimensions(XmFontList fontList,
			 short *width,
			 short *height,
			 Boolean useAverageWidth)
	{
	XmStringCharSet charset;
	XmFontContext context;
	XFontStruct *fs;
	short w, h;
#if XmVersion < 2000
	/* --- begin code to work around Motif 1.x internal bug */
	typedef struct {
		XmFontList nextFontList;
		Boolean unused;
	} XmFontListContextRec;
	typedef struct {
		XFontStruct *font;
		XmStringCharSet unused;
	} XmFontListRec;
	XmFontList nextFontList;
#endif

	*width = 0;
	*height = 0;
	if (XmFontListInitFontContext(&context, fontList))
		{
		while (1)
			{
#if XmVersion < 2000
			/* --- begin code to work around Motif internal bug */
			/* --- this code must be removed for Motif 2.0    */
			nextFontList = ((XmFontListContextRec *)context)->nextFontList;
			if (!nextFontList)
				break;
			if (!((XmFontListRec *)nextFontList)->font)
				break;
			/* --- end Motif workaround code */
#endif
			if (XmFontListGetNextFont(context, &charset, &fs) == False)
				break;
			XtFree(charset);
			if (useAverageWidth == True)
				XmLFontGetAverageWidth(fs, &w);
			else
				w = fs->max_bounds.width;
			h = fs->max_bounds.ascent + fs->max_bounds.descent;
			if (*height < h)
				*height = h;
			if (*width < w)
				*width = w;
			}	
		XmFontListFreeFontContext(context);
		}
	}

static void
XmLFontGetAverageWidth(XFontStruct *fs,
		       short *width)
	{
	long aw, n;
	int r, c, mm, i;
	XCharStruct *cs;

	n = 0;
	aw = 0;
	mm = fs->max_char_or_byte2 - fs->min_char_or_byte2 + 1;
	for (r = fs->min_byte1; r <= fs->max_byte1; r++)
		for (c = fs->min_char_or_byte2; c <= fs->max_char_or_byte2; c++)
			{
			if (!fs->per_char)
				continue;
			i = ((r - fs->min_byte1) * mm) + (c - fs->min_char_or_byte2);
			cs = &fs->per_char[i];
			if (!cs->width)
				continue;
			aw += cs->width;
			n++;
			}
	if (n)
		aw = aw / n;
	else
		aw = fs->min_bounds.width;
	*width = (short)aw;
	}

int _XmLKey;

void XmLInitialize(void)
	{
	static int first = 1;
	
	if (!first)
		return;
	first = 0;

#ifdef XmLEVAL
	fprintf(stderr, "XmL: This is an evalation version of the Microline\n");
	fprintf(stderr, "XmL: Widget Library.  Some features are disabled.\n");
#endif

#ifdef XmLJAVA
	if (_XmLKey != 444)
		{
		fprintf(stderr, "XmL: Error: This version of the library will only");
		fprintf(stderr, "XmL: work with JAVA.\n");
		exit(0);
		}
#endif
	}

int
XmLMessageBox(Widget w,
	      char *string,
	      Boolean okOnly)
	{
	int status = 0;
	Widget dialog, shell;
	Arg args[3];
	XtAppContext context;
	XmString str, titleStr;
	String shellTitle;
	Atom WM_DELETE_WINDOW;

	str = XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET);
	XtSetArg(args[0], XmNmessageString, str);
	XtSetArg(args[1], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL);
	shell = XmLShellOfWidget(w);
	if (shell)
		XtVaGetValues(shell, XmNtitle, &shellTitle, NULL);
	if (shell && shellTitle)
		titleStr = XmStringCreateLtoR(shellTitle,
			XmSTRING_DEFAULT_CHARSET);
	else
		titleStr = XmStringCreateSimple("Notice");
	XtSetArg(args[2], XmNdialogTitle, titleStr);
	if (okOnly == True)
		dialog = XmCreateMessageDialog(XtParent(w), "popup", args, 3);
	else
		dialog = XmCreateQuestionDialog(XtParent(w), "popup", args, 3);
	WM_DELETE_WINDOW = XmInternAtom(XtDisplay(w), "WM_DELETE_WINDOW",
		False);
	XmAddWMProtocolCallback(shell, WM_DELETE_WINDOW, XmLMessageBoxWMDelete,
		(caddr_t)&status);
	XmStringFree(str);
	XmStringFree(titleStr);
	XtAddCallback(dialog, XmNokCallback, XmLMessageBoxResponse,
		(XtPointer)&status);
	if (okOnly == True)
		{
		XtUnmanageChild(XmMessageBoxGetChild(dialog,
			XmDIALOG_CANCEL_BUTTON));
		XtUnmanageChild(XmMessageBoxGetChild(dialog,
			XmDIALOG_HELP_BUTTON));
		}
	else
		{
		XtAddCallback(dialog, XmNcancelCallback, XmLMessageBoxResponse,
			(XtPointer)&status);
		XtAddCallback(dialog, XmNhelpCallback, XmLMessageBoxResponse,
			(XtPointer)&status);
		}
	XtManageChild(dialog);

	context = XtWidgetToApplicationContext(w);
	while (!status ||  XtAppPending(context))
		XtAppProcessEvent(context, XtIMAll);
	XtDestroyWidget(dialog);
	return status;
	}

static void
XmLMessageBoxWMDelete(Widget w,
		      XtPointer clientData,
		      XtPointer callData)
	{
	int *status = (int *)clientData;
	*status = 1;
	}

static void
XmLMessageBoxResponse(Widget w,
		      XtPointer clientData,
		      XtPointer callData)
	{
	int *status = (int *)clientData;
	XmAnyCallbackStruct *reason;

	reason = (XmAnyCallbackStruct *)callData;
	switch (reason->reason)
		{
		case XmCR_OK:
			*status = 1;
			break;
		case XmCR_CANCEL:
			*status = 2;
			break;
		case XmCR_HELP:
			*status = 3;
			break;
		}
	}

void
XmLPixmapDraw(Widget w,
	      Pixmap pixmap,
	      Pixmap pixmask,
	      int pixmapWidth,
	      int pixmapHeight,
	      unsigned char alignment,
	      GC gc,
	      XRectangle *rect,
	      XRectangle *clipRect)
	{
	Display *dpy;
	Window win;
	int px, py, x, y, width, height, drawType;

	if (pixmap == XmUNSPECIFIED_PIXMAP)
		return;
	dpy = XtDisplay(w);
	win = XtWindow(w);
	width = pixmapWidth;
	height = pixmapHeight;
	if (!width || !height)
		{
		alignment = XmALIGNMENT_TOP_LEFT;
		width = clipRect->width - 4;
		height = clipRect->height - 4;
		}
	drawType = XmLDrawCalc(w, width, height, alignment,
		rect, clipRect, &x, &y);
	if (drawType == XmLDrawNODRAW)
		return;
	px = 0;
	py = 0;
	/* clip top */
	if (clipRect->y > y && clipRect->y < y + height - 1)
		{
		py = clipRect->y - y;
		y += py;
		height -= py;
		}
	/* clip bottom */
	if (clipRect->y + (int)clipRect->height - 1 >= y &&
		clipRect->y + (int)clipRect->height - 1 <= y + height - 1)
		height = clipRect->y + clipRect->height - y;
	/* clip left */
	if (clipRect->x > x && clipRect->x < x + width - 1)
		{
		px = clipRect->x - x;
		x += px;
		width -= px;
		}
	/* clip right */
	if (clipRect->x + (int)clipRect->width - 1 >= x &&
		clipRect->x + (int)clipRect->width - 1 <= x + width - 1)
		width = clipRect->x + clipRect->width - x;

	if (pixmask != XmUNSPECIFIED_PIXMAP)
		{
		XSetClipMask(dpy, gc, pixmask);
		XSetClipOrigin(dpy, gc, x - px, y - py);
		}
	XSetGraphicsExposures(dpy, gc, False);
	XCopyArea(dpy, pixmap, win, gc, px, py, width, height, x, y);
	XSetGraphicsExposures(dpy, gc, True);
	if (pixmask != XmUNSPECIFIED_PIXMAP)
		{
		XSetClipMask(dpy, gc, None);
		XSetClipOrigin(dpy, gc, 0, 0);
		}
	}

Widget
XmLShellOfWidget(Widget w)
	{
	while(1)
		{
		if (!w)
			return 0;
		if (XtIsSubclass(w, shellWidgetClass))
			return w;
		w = XtParent(w);
		}	
	}

static XmLSortCompareFunc XmLSortCompare;
static int XmLSortEleSize;
static void *XmLSortUserData;

void
XmLSort(void *base,
	int numItems,
	unsigned int itemSize,
	XmLSortCompareFunc compare,
	void *userData)
	{
	XmLSortCompareFunc oldCompare;
	int oldEleSize;
	void *oldUserData;
	char *lvec, *rvec;

	if (numItems < 2)
		return;

	/* for sorts within a sort compare function, we must
	   save any global sort variables on the local stack
	   and restore them when finished */
	oldCompare = XmLSortCompare;
	oldEleSize = XmLSortEleSize;
	oldUserData = XmLSortUserData;
	XmLSortCompare = compare;
	XmLSortEleSize = itemSize;
	XmLSortUserData = userData;

	lvec = (char *)base;
	rvec = lvec + (numItems - 1) * itemSize;
	XmLSortFunc(lvec, rvec);

	XmLSortCompare = oldCompare;
	XmLSortEleSize = oldEleSize;
	XmLSortUserData = oldUserData;
	}

#define SWAP(p1, p2) \
		{ \
		if (p1 != p2) \
			{ \
			int zi; \
			char zc; \
			for (zi = 0; zi < XmLSortEleSize; zi++) \
				{ \
				zc = (p1)[zi]; \
				(p1)[zi] = (p2)[zi]; \
				(p2)[zi] = zc; \
				} \
			}\
		}

static void
XmLSortFunc(char *lvec,
	    char *rvec)
	{
	int i;
	char *nlvec, *nrvec, *pvec;

start:
	i = (*XmLSortCompare)(XmLSortUserData, lvec, rvec);

	/* two item sort */
	if (rvec == lvec + XmLSortEleSize)
		{
		if (i > 0)
			SWAP(lvec, rvec)
		return;
		}

	/* find mid of three items */
	pvec = lvec + ((rvec - lvec) / (XmLSortEleSize * 2)) * XmLSortEleSize;
	if (i < 0)
		{
		i = (*XmLSortCompare)(XmLSortUserData, lvec, pvec);
		if (i > 0)
			pvec = lvec;
		else if (i == 0)
			pvec = rvec;
		}
	else if (i > 0)
		{
		i = (*XmLSortCompare)(XmLSortUserData, rvec, pvec);
		if (i > 0)
			pvec = rvec;
		else if (i == 0)
			pvec = lvec;
		}
	else
		{
		pvec = lvec + XmLSortEleSize;
		while (1)
			{
			i = (*XmLSortCompare)(XmLSortUserData, lvec, pvec);
			if (i < 0)
				break;
			else if (i > 0)
				{
				pvec = lvec;
				break;
				}
			if (pvec == rvec)
				return;
			pvec += XmLSortEleSize;
			}
		}

	/* partition the set */
	nlvec = lvec;
	nrvec = rvec;
	while (1)
		{
		if (pvec == nrvec)
			pvec = nlvec;
		else if (pvec == nlvec)
			pvec = nrvec;
		SWAP(nrvec, nlvec)
		while ((*XmLSortCompare)(XmLSortUserData, nlvec, pvec) < 0)
			nlvec += XmLSortEleSize;
		while ((*XmLSortCompare)(XmLSortUserData, nrvec, pvec) >= 0)
			nrvec -= XmLSortEleSize;
		if (nlvec > nrvec)
			break;
		}

	/* sort partitioned sets */
	if (lvec < nlvec - XmLSortEleSize)
		XmLSortFunc(lvec, nlvec - XmLSortEleSize);
	if (nlvec < rvec)
		{
		lvec = nlvec;
		goto start;
		}
	}

void
XmLStringDraw(Widget w,
	      XmString string,
	      XmStringDirection stringDir,
	      XmFontList fontList,
	      unsigned char alignment,
	      GC gc,
	      XRectangle *rect,
	      XRectangle *clipRect)
	{
	Display *dpy;
	Window win;
	Dimension width, height;
	int x, y, drawType;
	unsigned char strAlignment;

	if (!string)
		return;
	dpy = XtDisplay(w);
	win = XtWindow(w);
	XmStringExtent(fontList, string, &width, &height);
	drawType = XmLDrawCalc(w, width, height, alignment,
		rect, clipRect, &x, &y);
	if (drawType == XmLDrawNODRAW)
		return;
	x = rect->x + 2;
	if (alignment == XmALIGNMENT_LEFT ||
		alignment == XmALIGNMENT_TOP_LEFT ||
		alignment == XmALIGNMENT_BOTTOM_LEFT)
		strAlignment = XmALIGNMENT_BEGINNING;
	else if (alignment == XmALIGNMENT_CENTER ||
		alignment == XmALIGNMENT_TOP ||
		alignment == XmALIGNMENT_BOTTOM)
        if (width <= rect->width - 4)
            strAlignment = XmALIGNMENT_CENTER;
        else
            strAlignment = XmALIGNMENT_BEGINNING;
	else
		strAlignment = XmALIGNMENT_END;
	/* XmStringDraw clipping doesnt work in all cases
	   so we use a clip region for clipping */
	if (drawType == XmLDrawCLIPPED)
		XSetClipRectangles(dpy, gc, 0, 0, clipRect, 1, Unsorted);
	XmStringDraw(dpy, win, fontList, string, gc,
		x, y, rect->width - 4, strAlignment, stringDir, clipRect);
	if (drawType == XmLDrawCLIPPED)
		XSetClipMask(dpy, gc, None);
	}

void
XmLStringDrawDirection(Display *dpy,
		       Window win,
		       XmFontList fontlist,
		       XmString string,
		       GC gc,
		       int x,
		       int y,
		       Dimension width,
		       unsigned char alignment,
		       unsigned char layout_direction,
		       unsigned char drawing_direction)
{
	Screen *screen;
	XFontStruct *fontStruct;
	XImage *sourceImage, *destImage;
	Pixmap pixmap;
	GC pixmapGC;
	/*	int sourceWidth, sourceHeight;*/
	int destWidth, destHeight;
	int stringWidth, stringHeight;
	int i, j, bytesPerLine;
	Dimension dW, dH;
	char *data;

	screen = DefaultScreenOfDisplay(dpy);
	XmStringExtent(fontlist, string, &dW, &dH);
	stringWidth = (int)dW;
	stringHeight = (int)dH;
	if (!stringWidth || !stringHeight)
		return;

	/* draw string into 1 bit deep pixmap */
	pixmap = XCreatePixmap(dpy, win, stringWidth, stringHeight, 1);
	pixmapGC = XCreateGC(dpy, pixmap, 0, NULL);
	fontStruct = XLoadQueryFont(dpy, "fixed");
	if (!fontStruct)
		{
		fprintf(stderr, "XmLStringDrawDirection: error - ");
		fprintf(stderr, "can't load fixed font\n");
		return;
		}
	XSetFont(dpy, pixmapGC, fontStruct->fid);
	XSetBackground(dpy, pixmapGC, 0L);
	XSetForeground(dpy, pixmapGC, 0L);
	XFillRectangle(dpy, pixmap, pixmapGC, 0, 0, stringWidth, stringHeight);
	XSetForeground(dpy, pixmapGC, 1L);
	XmStringDraw(dpy, pixmap, fontlist, string, pixmapGC, 0, 0, stringWidth,
		XmALIGNMENT_BEGINNING, layout_direction, 0);
	XFreeFont(dpy, fontStruct);

	/* copy 1 bit deep pixmap into source image */
	sourceImage = XGetImage(dpy, pixmap, 0, 0, stringWidth, stringHeight,
		1, XYPixmap);
	XFreePixmap(dpy, pixmap);

	/* draw rotated text into destination image */
	if (drawing_direction == XmSTRING_UP || drawing_direction == XmSTRING_DOWN)
		{
		destWidth = stringHeight;
		destHeight = stringWidth;
		}
	else
		{
		destWidth = stringWidth;
		destHeight = stringHeight;
		}
	bytesPerLine  = (destWidth - 1) / 8 + 1;
	data = (char *)malloc(bytesPerLine * destHeight);
	destImage = XCreateImage(dpy, DefaultVisualOfScreen(screen),
	    1, XYBitmap, 0, data, destWidth, destHeight, 8, 0);
	for (i = 0; i < stringWidth; i++)
		for (j = 0; j < stringHeight; j++)
			{
			if (drawing_direction == XmSTRING_UP)
				XPutPixel(destImage, j, i,
					XGetPixel(sourceImage, stringWidth - i - 1, j));
			else if (drawing_direction == XmSTRING_DOWN)
				XPutPixel(destImage, stringHeight - j - 1, stringWidth - i - 1,
					XGetPixel(sourceImage, stringWidth - i - 1, j));
			else if (drawing_direction == XmSTRING_LEFT)
				XPutPixel(destImage, i, stringHeight - j - 1,
					XGetPixel(sourceImage, stringWidth - i - 1, j));
			else
				XPutPixel(destImage, i, j,
					XGetPixel(sourceImage, i, j));
			}
	XDestroyImage(sourceImage);

	/* copy rotated image into 1 bit deep pixmap */
	pixmap = XCreatePixmap(dpy, win, destWidth, destHeight, 1);
	XPutImage(dpy, pixmap, pixmapGC, destImage, 0, 0, 0, 0,
	    destWidth, destHeight);
	XDestroyImage(destImage);
	XFreeGC(dpy, pixmapGC);

	/* adjust position for alignment */
	if (drawing_direction == XmSTRING_UP || drawing_direction == XmSTRING_DOWN)
		{
		if (alignment == XmALIGNMENT_BEGINNING)
			;
		else if (alignment == XmALIGNMENT_CENTER)
			y += width / 2 - stringWidth / 2;
		else if (alignment == XmALIGNMENT_END)
			y += (int)width - stringWidth;
		}
	else
		{
		if (alignment == XmALIGNMENT_BEGINNING)
			;
		else if (alignment == XmALIGNMENT_CENTER)
			x += width / 2 - stringWidth / 2;
		else if (alignment == XmALIGNMENT_END)
			x += (int)width - stringWidth;
		}

	/* draw the pixmap as a stipple in the window */
	XSetStipple(dpy, gc, pixmap);
	XSetFillStyle(dpy, gc, FillStippled);
	XSetTSOrigin(dpy, gc, x % destWidth, y % destHeight);
	XFillRectangle(dpy, win, gc, x, y, destWidth, destHeight);
	XFreePixmap(dpy, pixmap);
	XSetFillStyle(dpy, gc, FillSolid);
	}

void
XmLWarning(Widget w,
	   char *msg)
	{
	XtAppContext app;
	char s[512], *cname, *name;
	WidgetClass c;

	app = XtWidgetToApplicationContext(w);
	name = XtName(w);
	if (!name)
		name = "[No Name]";
	c = XtClass(w);
	cname = c->core_class.class_name;
	if (!cname)
		cname = "[No Class]";
	sprintf(s, "%s: %s: %s\n", cname, name, msg);
	XtAppWarning(app, s);
	}
