/****************************************************************************
**
*A  xcmds.c                     XGAP Source                      Frank Celler
**
*H  @(#)$Id: xcmds.c,v 1.7 1993/12/23 08:48:22 fceller Exp $
**
*Y  Copyright 1993-1995,   Frank Celler,   Lehrstuhl D fuer Mathematik,  RWTH
**
*H  $Log: xcmds.c,v $
*H  Revision 1.7  1993/12/23  08:48:22  fceller
*H  fixed a string length bug
*H  used unsigned bitmaps
*H
*H  Revision 1.6  1993/10/18  11:04:47  fceller
*H  added fast updated,  fixed timing problem
*H
*H  Revision 1.5  1993/08/18  10:56:16  fceller
*H  fixed pop menu from dropping out of the window
*H
*H  Revision 1.4  1993/08/18  10:32:00  fceller
*H  silenced GCC
*H
*H  Revision 1.3  1993/08/12  13:47:07  fceller
*H  fixed 'FunResize'
*H
*H  Revision 1.2  1993/04/13  07:30:44  fceller
*H  added text selectors
*H
*H  Revision 1.1  1993/04/06  08:56:08  fceller
*H  renamed 'MouseClick' to 'PointerButtonDown',
*H  renamed 'MenuClick' to 'MenuSelected'
*H
*H  Revision 1.0  1993/04/05  11:42:18  fceller
*H  Initial revision
*/
#include    <stdio.h>			/* standard C library */

#include    <X11/Intrinsic.h>		/* X Intrinsic */
#include    <X11/StringDefs.h>
#include    <X11/cursorfont.h>

#include    <X11/Xaw/Label.h>		/* Athena widgets */
#include    <X11/Xaw/Viewport.h>
#include    <X11/Xaw/Box.h>
#include    <X11/Xaw/MenuButton.h>
#include    <X11/Xaw/SimpleMenu.h>
#include    <X11/Xaw/SmeLine.h>
#include    <X11/Xaw/SmeBSB.h>
#include    <X11/Xaw/Paned.h>
#include    <X11/Xaw/Text.h>
#include    <X11/Xaw/AsciiText.h>
#include    <X11/Xaw/TextSink.h>
#include    <X11/Xaw/List.h>

#include    <X11/IntrinsicP.h>
#include    <X11/StringDefs.h>
#include    <X11/Xaw/XawInit.h>
#include    <X11/Xaw/ViewportP.h>

#include    "popdial.h"
#include    "gapgraph.h"
#include    "gaptext.h"

#include    "utils.h"
#include    "xcmds.h"
#include    "pty.h"
#include    "main.h"


/****************************************************************************
**
*V  GapWindows	. . . . . . . . . . . . . . . . . . . list of all gap windows
*/
TypeList GapWindows;


/****************************************************************************
**
*V  TextSelectors . . . . . . . . . . . . . . . .  list of all text selectors
*/
TypeList TextSelectors;


/****************************************************************************
**
*V  PopupMenus  . . . . . . . . . . . . . . . . . . . list of all popup menus
*/
TypeList PopupMenus;


/****************************************************************************
**
*V  DialogOkCancel  . . . . . . . . . . . . . . . . .  Cancel/OK popup dialog
*/
TypePopupDialog DialogOkCancel;


/****************************************************************************
**
*V  TinyFont  . . . . . . . . . . . . . . . . . . . tiny font for text output
*V  SmallFont . . . . . . . . . . . . . . . . . .  small font for text output
*V  NormalFont  . . . . . . . . . . . . . . . . . normal font for text output
*V  LargeFont . . . . . . . . . . . . . . . . . .  large font for text output
*V  HugeFont  . . . . . . . . . . . . . . . . . . . huge font for text output
*/
XFontStruct * TinyFont;
XFontStruct * SmallFont;
XFontStruct * NormalFont;
XFontStruct * LargeFont;
XFontStruct * HugeFont;


/****************************************************************************
**
*V  CursorTL  . . . . . . . . . . . . . . . . . . . . . . . .  top left arrow
*/
Cursor CursorTL;


/****************************************************************************
**
*V  MenuSymbol	. . . . . . . . . . . . . . . . .  symbol for drop down menus
*V  CheckMarkSymbol . . . . . . . . . . . . . symbol for checked menu entries
*V  EmptyMarkSymbol . . . . . . . . . . . . symbol for unchecked menu entries
*/
Pixmap MenuSymbol;
Pixmap CheckMarkSymbol;
Pixmap EmptyMarkSymbol;


/****************************************************************************
**

*F  ParseLong( <buf>, <val> )	. . . . . . . . . . . . . .  get a long value
*/
#ifdef __STDC__
static int ParseLong ( char**, long* );
#endif

static int ParseLong ( buf, val )
    char     ** buf;
    long      * val;
{
    long        mult;

    if ( *(*buf)++ != 'I' )
	return 0;
    *val = 0;
    mult = 1;
    do
    {
	if ( **buf == '+' )
	{
	    (*buf)++;
	    return 1;
	}
	else if ( **buf == '-' )
	{
	    (*buf)++;
	    *val = -*val;
	    return 1;
	}
	else if ( '0' <= **buf && **buf <= '9' )
	    *val += mult * (*((*buf)++)-'0');
	else
	    return 0;
	mult = mult * 10;
    } while (1);
}


/****************************************************************************
**
*F  ParseString( <buf>, <str>, <len> )	. . . . . . . get a string from <ptr>
*/
#ifdef __STDC__
static int ParseString( char**, char**, int* );
#endif

static int ParseString ( buf, str, len )
     char	 ** buf;
     char	 ** str;
     int          * len;
{
     char         * ptr;
     long           m;
     int            i;

     if ( (*buf)[0] != 'S' )
         return 0;
     for ( i = 0, *len = 0, (*buf)++, m = 1; i < 8; i++, (*buf)++, m *= 10 )
         *len += (**buf -'0') * m;
     *str = XtMalloc( (*len)+1 );
     for ( ptr = *str, i = *len;  0 < i;  i--, ptr++, (*buf)++ )
         *ptr = **buf;
     *ptr = 0;
     return 1;
}


/****************************************************************************
**
*F  AnswerGap( <format>, <arg1>, <arg2>, <arg3>, <arg4> ) . . . return to gap
*/
extern TypeWindowCommand WindowCommands[];
extern int write P(( int, char*, int ));

#define ANSWER_GAP(a,b,c,d,e)  \
                         AnswerGap(a,(long)(b),(long)(c),(long)(d),(long)(e))

int AnswerGap ( format, a1, a2, a3, a4 )
    char      * format;
    long         a1;
    long         a2;
    long         a3;
    long         a4;
{
    long         n;
    long         m;
    long         len;
    long         args[4];
    int          arg;
    char       * qtr;
    char       * ptr;
    char       * wtr;
    char       * str;

    /* give debug information */
    DEBUG(( "AnswerGap( \"%s\", ... )\n", format ))

    /* compute length of return string */
    args[0] = a1;  args[1] = a2;  args[2] = a3;  args[3] = a4;
    len = 12;
    arg = 0;
    for ( ptr = format;  *ptr;  ptr++ )
    {
	if ( arg == 4 )
	    return 1;
	switch ( *ptr )
	{
	    case 'O':
	    case 'o':  len += 9;  break;
	    case 'E':
	    case 'e':  len += 9;  break;
	    case 'D':
	    case 'd':  len += 9;  arg++;  break;
	    case 'S':
	    case 's':  len += 9 + strlen((char*)args[arg++]);  break;
	    default :  return 1;
	}
    }

    /* allocate a string of length <len> */
    str = XtMalloc( len );

    /* parse arguments again */
    arg = 0;
    qtr = str + 10;
    for ( ptr = format;  *ptr;  ptr++ )
    {
	switch ( *ptr )
	{
	    case 'O':
	    case 'o':
	        strcpy( qtr, "I0+" );
		qtr += 3;
		break;
	    case 'E':
	    case 'e':
		strcpy( qtr, "I1+" );
		qtr += 3;
		break;
	    case 'D':
	    case 'd':
		*qtr++ = 'I';
		n = args[arg++];
		for ( m = ( 0 < n ) ? n : -n;  0 < m;  m /= 10 )
		    *qtr++ = '0' + (m%10);
		if ( n < 0 )
		    *qtr++ = '-';
		else
		    *qtr++ = '+';
		break;
	    case 'S':
	    case 's':
		*qtr++ = 'S';
		wtr = (char*)(args[arg++]);
		for ( n = 7,  m = strlen(wtr);  0 <= n;  n--, m /= 10 )
		    *qtr++ = m%10 + '0';
		while ( *wtr )
		    *qtr++ = *wtr++;
		break;
	}
    }
    *qtr = '\0';
    len = qtr - str - 10;

    /* add header */
    qtr = str;
    *qtr++ = '@';
    *qtr++ = 'a';
    for ( n = 7,  m = len;  0 <= n;  n--,  m /= 10 )
	*qtr++ = m%10 + '0';

    /* write result back to gap process */
    DEBUG(( "  >>> '%s' (%ld)\n", str, len+10 ))
    WriteGap( str, len+10 );
    XtFree(str);
    return 0;
}


/****************************************************************************
**
*F  GapWindowCmd( <cstr>, <len> ) . . . . . . . execute window command <cstr>
*/
int GapWindowCmd ( cstr,  len )
    char              * cstr;
    int                 len;
{
    TypeWindowCommand * cmd;
    TypeArg	        arg;
    int                 ret;
    int			ci;
    int			cs;
    long		ca;
    long                i;
    char              * pa;
    char              * str = cstr+3;
    char                name[4];

    /* give debug information */
    name[0] = cstr[0];  name[1] = cstr[1];  name[2] = cstr[2];  name[3] = 0;
    DEBUG(( "GapWindowCmd( \"%s\" )\n", name ))

    /* try to find the command in <WindowCommands> */
    cmd = WindowCommands;
    while ( cmd->name )
	if ( !strncmp( cmd->name, name, 3 ) )
	    break;
	else
	    cmd++;
    if ( !cmd->name )
	return ANSWER_GAP( "esss", "unknown command '", name, "'", 0 );

    /* parse arguments */
    arg.opts = 0;
    ci = cs = 0;
    ca = 1;
    pa = cmd->args;
    while ( *pa )
    {
	if ( *pa == '*' )
	{
	    arg.opts = str;
	    break;
	}
	else if ( *pa == 'S' || *pa == 's' )
	{
	    if ( !ParseString( &str, &(arg.sargs[cs]), &len ) )
	    {
		ret = ANSWER_GAP("eds",ca,".th arg must be a string",0,0);
		goto free_strings;
	    }
	    else
		cs++;
	}
	else if ( *pa == '#' )
	{
	    if ( !ParseLong( &str, &(arg.iargs[ci++]) ) )
	    {
		ret = ANSWER_GAP("eds",ca,".th arg must be a window nr",0,0);
		goto free_strings;
	    }
	    if ( GapWindows->len <= arg.iargs[0] || arg.iargs[0] < 0 )
	    {
		ret = ANSWER_GAP( "es", "illegal window number", 0, 0, 0 );
		goto free_strings;
	    }
	    arg.win = (TypeGapWindow*)(GapWindows->ptr[arg.iargs[0]]);
	    if ( ! arg.win->used )
	    {
		ret = ANSWER_GAP( "es", "window not used", 0, 0, 0 );
		goto free_strings;
	    }
	}
	else if ( *pa == 'T' )
	{
	    if ( !ParseLong( &str, &(arg.iargs[ci++]) ) )
	    {
		ret = ANSWER_GAP("eds",ca,".th arg must be a selector",0,0);
		goto free_strings;
	    }
	    if ( TextSelectors->len <= arg.iargs[0] || arg.iargs[0] < 0 )
	    {
		ret = ANSWER_GAP( "es", "illegal selector number", 0, 0, 0 );
		goto free_strings;
	    }
	    arg.sel = (TypeTextSelector*)(TextSelectors->ptr[arg.iargs[0]]);
	    if ( arg.sel == 0)
	    {
		ret = ANSWER_GAP( "es", "selector not used", 0, 0, 0 );
		goto free_strings;
	    }
	}
	else if ( *pa == 'F' )
	{
	    if ( !ParseLong( &str, &(arg.iargs[ci++]) ) )
	    {
		ret = ANSWER_GAP("eds",ca,".th arg must be an integer",0,0);
		goto free_strings;
	    }
	    switch ( (int)arg.iargs[ci-1] )
	    {
		case 1:  arg.font = TinyFont;   break;
		case 2:  arg.font = SmallFont;  break;
		case 3:  arg.font = NormalFont; break;
		case 4:  arg.font = LargeFont;  break;
		case 5:  arg.font = HugeFont;   break;
		default:
		    ret = ANSWER_GAP( "eds", ca,
				      ".th arg must be a font number", 0, 0 );
		    goto free_strings;
		    break;
	    }
	}
	else
	{
	    if ( !ParseLong( &str, &(arg.iargs[ci++]) ) )
	    {
		ret = ANSWER_GAP("eds",ca,".th arg must be an integer",0,0);
		goto free_strings;
	    }
	}
	ca++;
	pa++;
    }
    if ( *pa != '*' && *str )
    {
	ret = ANSWER_GAP( "es", "too many arguments", 0, 0, 0 );
	goto free_strings;
    }


    /* call command */
    ret = cmd->func( &arg );

    /* free argument strings */
free_strings:
    for ( i = 0;  i < cs;  i++ )
	XtFree( arg.sargs[i] );
    return ret;
}


/****************************************************************************
**

*D  CHECK_BBOX( <obj> )	. . . . . . . . . .  check dimensions of bounding box
*/
#define	CHECK_BBOX(obj)	obj->x = ( obj->x < 0 ) ? 0 : obj-> x; \
                        obj->y = ( obj->y < 0 ) ? 0 : obj-> y


/****************************************************************************
**
*F  FunPopupShell( <name>, <str> )  . . . . . . . . . .  create a popup shell
*/
static int ChosenPane = 0;

static void PaneChosen ( w, cl, ca )
    Widget	    w;
    XtPointer       cl;
    XtPointer       ca;
{
    TypePaneData  * pd = (TypePaneData*) cl;

    DEBUG(( "PaneChosen( #%d, #%d )\n", pd->popup, pd->pane ))
    ChosenPane = pd->pane;
}

static void PopingDown ( w, cl, ca )
    Widget	w;
    XtPointer   cl;
    XtPointer   ca;
{
    DEBUG(( "PopingDown\n" ))
    ANSWER_GAP( "od", ChosenPane, 0, 0, 0 );
}

static char PopingDownTrans[] =
    "<BtnUp>: notify() MenuPopdown() unhighlight()"; 

int FunPopupShell ( arg )
    TypeArg       * arg;
{
    TypePaneData  * pd;
    TypeMenu      * menu;
    Widget          pane;
    Widget          pshell;
    char	    buf[128];
    char          * ptr;
    char          * qtr;
    int             i;

    /* search for an identical popup shell */
    for ( i = 0;  i < PopupMenus->len;  i++ )
    {
	menu = PopupMenus->ptr[i];
	if (    ! strcmp( menu->name,   arg->sargs[0]  )
	     && ! strcmp( menu->string, arg->sargs[1]) )
	    break;
    }
    if ( i < PopupMenus->len )
	return ANSWER_GAP( "od", i, 0, 0, 0 );

    /* create a shell */
    pshell = XtVaCreatePopupShell( "pshell", simpleMenuWidgetClass,
    	    	    	    	   GapTalk, XtNcursor, CursorTL, 0 );
    XtOverrideTranslations( pshell, 
			    XtParseTranslationTable(PopingDownTrans) );

    /* create headline */
    DEBUG(( "PopupShell( \"%s\", ... )\n", arg->sargs[0] ))
    XtVaCreateManagedWidget( "menulabel", smeBSBObjectClass, pshell,
    	    	    	     XtNsensitive, False,
    	    	    	     XtNlabel,     arg->sargs[0],
    	    	    	     0 );
    XtVaCreateManagedWidget( "line", smeLineObjectClass, pshell, 0 );

    /* add popdown callback */
    XtAddCallback( pshell, XtNpopdownCallback, PopingDown, 0 );

    /* create menu entries */
    menu = (TypeMenu*) XtMalloc( sizeof(TypeMenu) );
    menu->shell   = pshell;
    menu->entries = List(0);
    menu->name    = XtMalloc(strlen(arg->sargs[0])+1);
    menu->string  = XtMalloc(strlen(arg->sargs[1])+1);
    strcpy( menu->name,   arg->sargs[0] );
    strcpy( menu->string, arg->sargs[1] );
    for ( ptr = arg->sargs[1], i = 1;  *ptr;  i++ )
    {
    	qtr = buf;
    	while ( *ptr && *ptr != '|' )
    	    *qtr++ = *ptr++;
    	*qtr = 0;
    	if ( *ptr )  ptr++;
    	DEBUG(( "  entry = \"%s\"\n", buf ))
    	pane = XtVaCreateManagedWidget( "menupane", smeBSBObjectClass,
    	    	    	    	    	pshell, XtNlabel, buf, 0 );
    	pd = (TypePaneData*) XtMalloc( sizeof(TypePaneData) );
    	pd->pane   = i;
    	pd->popup  = PopupMenus->len;
    	pd->shell  = pshell;
    	XtAddCallback( pane, XtNcallback, PaneChosen, pd );
	AddList( menu->entries, (void*) pd );
    }

    /* add shell to popup shell list */
    AddList( PopupMenus, (void*) menu );
    return ANSWER_GAP( "od", PopupMenus->len-1, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunShowPopup( <nr> )  . . . . . . . . . . . . . . . . popup a popup shell
*/
int FunShowPopup ( arg )
    TypeArg       * arg;
{
    Widget     	    popup;
    Window          root;
    Window          child;
    Position        tmp;
    Dimension       w1,  h1,  bw;
    int        	    x,  y,  x2,  y2;
    unsigned int    bt;

    /* check popup number */
    if ( PopupMenus->len <= arg->iargs[0] || arg->iargs[0] < 0 )
	return ANSWER_GAP("esd","illegal popup menu ",arg->iargs[0],0,0);
    popup = ((TypeMenu*)PopupMenus->ptr[arg->iargs[0]])->shell;

    /* get size of popup dialog */
    XtVaGetValues( popup,
                   XtNwidth,       &w1,
                   XtNheight,      &h1,
                   XtNborderWidth, &bw,
                   0,              0 );

    /* compute screen position */
    XQueryPointer( XtDisplay(GapTalk), XtWindow(GapTalk),
    	    	   &root, &child, &x, &y, &x2, &y2, &bt );
    tmp = DisplayWidth( XtDisplay(GapTalk), 
		        DefaultScreen(XtDisplay(GapTalk)) );
    if ( x+w1 > tmp )
	x = tmp-w1;
    tmp = DisplayHeight( XtDisplay(GapTalk), 
		         DefaultScreen(XtDisplay(GapTalk)) );
    if ( y+h1 > tmp )
	y = tmp-h1;

    /* popup the popup shell */
    XtVaSetValues( popup, XtNx, x-10, XtNy, y-10, 0 );
    XawSimpleMenuClearActiveEntry( popup );
    XtPopupSpringLoaded( popup );
    XtGrabPointer( popup, True, ButtonPressMask|ButtonReleaseMask,
		   GrabModeAsync, GrabModeAsync, None, None, CurrentTime );

    /* reset 'ChosenPane' */
    ChosenPane = 0;

    /* gap will be answered by 'PopingDown' */
    return 0;
}


/****************************************************************************
**
*F  FunShowDialog( <nr>, <msg>, <def> )	. . . . . . . . . . show popup dialog
*/
int FunShowDialog ( arg )
    TypeArg       * arg;
{
    TypePopupDialog dialog;
    String          str;
    long            first;
    long            res;

    /* check the dialog number */
    if ( arg->iargs[0] == 1 )
    {
	dialog = DialogOkCancel;
	first  = 4;
    }
    else
	return ANSWER_GAP("esd","illegal popup dialog ",arg->iargs[0],0,0);

    /* do the dialog */
    res = PopupDialog( dialog, arg->sargs[0], arg->sargs[1], &str );

    /* return the result */
    return ANSWER_GAP( "ods", (res==first)?0:1, str, 0, 0 );
}


/****************************************************************************
**
*F  FunOpenSelector( <name>, <list>, <buttons> )  .  open a new text selector
*/
static void ButtonSelected ( w, cl, ca )
    Widget	    w;
    XtPointer       cl;
    XtPointer       ca;
{
    long            n = ((long) cl) / 256;
    long            i = ((long) cl) % 256;
    char            buf[128];

    DEBUG(("ButtonSelected( #%ld, #%ld )\n", n, i))
    sprintf( buf, "ButtonSelected(%ld,%ld);\n", n, i );
    SimulateInput(buf);
}

static void TextSelected ( w, cl, ca )
    Widget	            w;
    XtPointer               cl;
    XtPointer               ca;
{
    long                    n = ((long) cl);
    XawListReturnStruct	  * ret = (XawListReturnStruct*) ca;
    char                    buf[128];

    DEBUG(("TextSelected( #%ld, $%d )\n", n, ret->list_index ))
    sprintf( buf, "TextSelected(%ld,%ld);\n", n, (long)ret->list_index+1 );
    SimulateInput(buf);
}

static void NotifyClick ( w, evt, str, n )
    Widget	    	  w;
    XEvent        	  * evt;
    String        	  * str;
    Cardinal      	  * n;
{
    XawListReturnStruct	  * ret;
    long                    i;
    char                    buf[128];

    /* get currently selected */
    ret = XawListShowCurrent(w);

    /* if something is set,  return */
    if ( ret->list_index != XAW_LIST_NONE )
	return;

    /* find widget */
    for ( i = 0;  i < TextSelectors->len;  i++ )
	if ( ((TypeTextSelector*)TextSelectors->ptr[i])->list == w )
	{
	    sprintf( buf, "TextSelected(%ld,0);\n", i );
	    SimulateInput(buf);
	    return;
	}
    
}

static char ButtonPressTrans[] =
    "<Btn1Down>,<Btn1Up>: Notify() NotifyClick()"; 

int FunOpenSelector ( arg )
    TypeArg           * arg;
{
    char              * name = arg->sargs[0];
    char              * ptr;
    char              * qtr;
    char                buf[512];
    long                i;
    long                n;
    Widget              viewport;
    Widget              paned;
    Widget              button;
    Widget              box;
    TypeTextSelector  * selector;


    /* give debug info */
    DEBUG(( "OpenSelector( \"%s\", \"%s\", \"%s\" )\n",
	    arg->sargs[0], arg->sargs[1], arg->sargs[2] ))

    /* create a new selector entry */
    selector = (TypeTextSelector*) XtMalloc( sizeof(TypeTextSelector) );

    /* create a new top level shell */
    selector->top = XtAppCreateShell(
		        "TextSelector", "XGap", topLevelShellWidgetClass,
			XtDisplay(GapTalk), 0, 0 );

    /* create a "paned" for the menu and text window */
    paned = XtVaCreateManagedWidget(
	        "textSelector", panedWidgetClass, selector->top, 0 );

    /* create a headline */
    XtVaCreateManagedWidget(
        "textSelectorTitle", labelWidgetClass, paned,
        XtNlabel, name,
	0 );
				    

    /* create a viewport for the text selectors */
    viewport = XtVaCreateManagedWidget(
	           "textSelectorViewport", viewportWidgetClass, paned,
		   XtNallowHoriz,       False,
		   XtNallowVert,        True,
		   XtNuseBottom,        True,
		   XtNshowGrip,         False,
		   0 );

    /* compute number of entries */
    for ( i = 2, qtr = arg->sargs[1];  *qtr;  qtr++ )
	if ( *qtr == '|' )
	    i++;
    selector->text = (String*) XtMalloc(i*sizeof(String));

    /* parse text */
    for ( ptr = arg->sargs[1], i = 0;  *ptr;  i++ )
    {
    	qtr = buf;
    	while ( *ptr && *ptr != '|' )
    	    *qtr++ = *ptr++;
    	*qtr = 0;
    	if ( *ptr )  ptr++;
    	DEBUG(( "  entry = \"%s\"\n", buf ))
	selector->text[i] = (String) XtMalloc(strlen(buf)+1);
	strcpy( selector->text[i], buf );
    }
    selector->text[i] = 0;

    /* find free entry in <TextSelectors> */
    for ( n = 0;  n < TextSelectors->len;  n++ )
	if ( TextSelectors->ptr[n] == 0 )
	    break;
    if ( n == TextSelectors->len )
	AddList( TextSelectors, 0 );
    TextSelectors->ptr[n] = selector;
    
    /* create a list widget containing the text */
    selector->list = XtVaCreateManagedWidget(
		         "textSelectorList", listWidgetClass, viewport,
			 XtNlist,           selector->text,
			 XtNdefaultColumns, 1,
			 XtNforceColumns,   True,
			 0 );
    XtOverrideTranslations( selector->list, 
			    XtParseTranslationTable(ButtonPressTrans) );
    XtAddCallback( selector->list, XtNcallback, TextSelected, (XtPointer)n );

    /* create a box containing the buttons */
    box = XtVaCreateManagedWidget(
	      "textSelectorBox", boxWidgetClass, paned,
	      XtNorientation,           XtorientHorizontal,
       	      XtNshowGrip,              False,
	      XtNskipAdjust,            True,
	      XtNresizeToPreferred, 	True,
	      0 );

    /* parse buttons */
    selector->buttons = List(0);
    for ( ptr = arg->sargs[2], i = 1;  *ptr;  i++ )
    {
    	qtr = buf;
    	while ( *ptr && *ptr != '|' )
    	    *qtr++ = *ptr++;
    	*qtr = 0;
    	if ( *ptr )  ptr++;
    	DEBUG(( "  button = \"%s\"\n", buf ))
	button = XtVaCreateManagedWidget(
	             "textSelectorButton", commandWidgetClass, box,
	             XtNlabel,      buf,
	             XtNshapeStyle, XmuShapeOval,
	             0 );
	XtAddCallback(button,XtNcallback,ButtonSelected,(XtPointer)(i+n*256));
	AddList( selector->buttons, button );
    }


    /* realize the window and return the number */
    XtRealizeWidget(selector->top);

    /* add window to list and return window number */
    return ANSWER_GAP( "od", n, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunChangeList( <sel>, <buttons> )   . . . .  change list in text selector
*/
int FunChangeList ( arg )
    TypeArg       * arg;
{
    long            i;
    char            buf[512];
    char          * ptr;
    char          * qtr;
    String        * text;

    /* give debug info */
    DEBUG(( "ChangeList( #%ld, \"%s\" )\n", arg->iargs[0], arg->sargs[0] ))

    /* compute number of entries */
    for ( i = 2, qtr = arg->sargs[0];  *qtr;  qtr++ )
	if ( *qtr == '|' )
	    i++;
    text = (String*) XtMalloc(i*sizeof(String));

    /* parse text */
    for ( ptr = arg->sargs[0], i = 0;  *ptr;  i++ )
    {
    	qtr = buf;
    	while ( *ptr && *ptr != '|' )
    	    *qtr++ = *ptr++;
    	*qtr = 0;
    	if ( *ptr )  ptr++;
    	DEBUG(( "  entry = \"%s\"\n", buf ))
	text[i] = (String) XtMalloc(strlen(buf)+1);
	strcpy( text[i], buf );
    }
    text[i] = 0;

    /* change list */
    XawListChange( arg->sel->list, text, 0, 0, True );

    /* clear old text */
    for ( i = 0;  arg->sel->text[i];  i++ )
	XtFree( arg->sel->text[i] );
    XtFree( (char*) arg->sel->text );
    arg->sel->text = text;

    /* return OK */
    return AnswerGap( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunEnableButton( <sel>, <but>, <enable> ) . . . . enable/disable a button
*/
int FunEnableButton ( arg )
    TypeArg   * arg;
{
    long        i;
    Widget      entry;

    /* give debug info */
    DEBUG(( "EnableButton( #%ld, #%ld, #%ld )\n",
	    arg->iargs[0], arg->iargs[1], arg->iargs[2] ))

    /* check button number */
    i = arg->iargs[1]-1;
    if ( arg->sel->buttons->len <= i || i < 0 )
	return ANSWER_GAP( "es", "illegal button number", 0, 0, 0 );
    entry = arg->sel->buttons->ptr[i];

    /* enable/disable */
    if ( arg->iargs[2] )
	XtVaSetValues( entry, XtNsensitive, True, 0 );
    else
	XtVaSetValues( entry, XtNsensitive, False, 0 );

    /* return OK */
    return AnswerGap( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunUnhighlihtSelector( <sel> )  . . . . . . . . . . ..  unhighlight entry
*/
int FunUnhighlihtSelector ( arg )
    TypeArg   * arg;
{
    /* give debug info */
    DEBUG(( "Unhighlight( #%ld )\n", arg->iargs[0] ))

    /* unhighlight entry */
    XawListUnhighlight(arg->sel->list);

    /* return OK */
    return AnswerGap( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunCloseSelector( <sel> ) . . . . . . . . . destroy an open text selector
*/
int FunCloseSelector ( arg )
    TypeArg   * arg;
{
    long        i;

    /* give debug info */
    DEBUG(( "CloseSelector( #%ld )\n", arg->iargs[0] ))

    /* destroy top level shell (this will destroy all children) */
    XtDestroyWidget(arg->sel->top);

    /* clear text */
    for ( i = 0;  arg->sel->text[i];  i++ )
	XtFree( arg->sel->text[i] );
    XtFree( (char*) arg->sel->text );

    /* clear button list */
    XtFree( (char*) arg->sel->buttons->ptr );
    XtFree( (char*) arg->sel->buttons );

    /* unset entry in <TextSelectors> */
    TextSelectors->ptr[arg->iargs[0]] = 0;
    XtFree((char*)arg->sel);

    /* return OK */
    return AnswerGap( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunOpenWindow( <name>, <width>, <height> )	. . . . . . open a new window
*/
static void MouseClick ( talk, cd, evt, ctd )
    Widget 	    talk;
    XtPointer       cd;
    XEvent        * evt;
    Boolean       * ctd;
{
    TypeGapWindow * gap = GapWindows->ptr[(int)cd];
    int             bn;
    char            buf[100];

    /* we only have a two button mouse */
    if ( evt->xbutton.button == Button1 )
        bn = 1;
    else if ( evt->xbutton.button == Button3 )
        bn = 2;
    else
	return;

    /* check boundaries */
    if ( gap->width <= evt->xbutton.x || gap->height <= evt->xbutton.y )
	return;

    /* give debug information */
    DEBUG(( "MouseClick( %d, %d, %d, %d )\n", (int)cd,
	    (int)(evt->xbutton.x), (int)(evt->xbutton.y), bn ))

    /* construct gap command */
    sprintf( buf, "PointerButtonDown(%d,%d,%d,%d);\n", (int)cd,
	     (int)evt->xbutton.x, (int)evt->xbutton.y, bn );
    SimulateInput(buf);
}

static Widget CreateTitle ( paned )
    Widget	    paned;
{
    return XtVaCreateManagedWidget(
		       "xgapWindowText",
		       labelWidgetClass,     paned,
		       XtNlabel,             "GAP window",
 		       XtNskipAdjust,        True,
		       XtNshowGrip,          False,
		       0 );
}


int FunOpenWindow ( arg )
    TypeArg       * arg;
{
    char          * name = arg->sargs[0];
    char          * title;
    long            w    = arg->iargs[0];
    long            h    = arg->iargs[1];
    short           w1;
    short           h1;
    Display       * dis  = XtDisplay(GapTalk);
    TypeGapWindow * window;
    Widget          paned;
    Widget          button;

    /* check arguments */
    if ( arg->iargs[0] < 1 || arg->iargs[1] < 1 )
	return ANSWER_GAP( "esdsd", "illegal window dimensions ",
			   arg->iargs[0], "x", arg->iargs[1] );
    /* give debug info */
    DEBUG(( "OpenWindow( \"%s\", %ld, %ld )\n", arg->sargs[0],
	    arg->iargs[0], arg->iargs[1] ))

    /* setup a new window structure, this structure will live forever */
    window = (TypeGapWindow*) XtMalloc( sizeof(TypeGapWindow) );
    window->line_width = 0;
    window->menus      = List(0);
    window->used       = True;
    window->text       = 0;

    /* find title position */
    XtVaGetValues( GapTalk, XtNtitlePosition, &title, 0 );

    /* create a new top level shell */
    window->top = XtAppCreateShell( "GraphicSheet", "XGap",
				    topLevelShellWidgetClass,
				    dis, 0, 0 );

    /* create a "paned" for the menu and text window */
    paned = XtCreateManagedWidget( "xgapWindow",
                                   panedWidgetClass,
				   window->top, 0, 0 );

    /* add TOP tile */
    if ( *title == 'T' || *title == 't' )
	window->text = CreateTitle(paned);

    /* create a menu box for the menu buttons */
    window->box = XtVaCreateManagedWidget(
		      "xgapWindowMenu", boxWidgetClass, paned,
 		      XtNskipAdjust,   		True,
		      XtNresizeToPreferred, 	True,
		      XtNshowGrip,     		False,
		      0 );

    /* create a dummy menu button */
    button = XtVaCreateManagedWidget( "dummy", commandWidgetClass,
				      window->box, XtNx, 0, 0 );

    /* add MIDDLE tile */
    if ( *title == 'M' || *title == 'm' )
	window->text = CreateTitle(paned);

    /* create a viewport for the window */
    window->viewport = XtVaCreateManagedWidget(
		          "xgapWindowViewport",
			  viewportWidgetClass, paned,
			  XtNallowHoriz,       True,
			  XtNallowVert,        True,
			  XtNuseBottom,        True,
			  XtNshowGrip,         False,
                          XtNresizable,        True,
			  0 );

    /* create a drawable */
    window->draw = XtVaCreateManagedWidget(
	               "xgapWindowDrawable",
		       gapGraphicWidgetClass, window->viewport,
                       XtNwidth,              w,
	               XtNheight,             h,
		       0 );
    window->width  = w;
    window->height = h;

    /* fix dimensions of viewport */
    XtVaGetValues( window->viewport, XtNwidth, &w1, XtNheight, &h1, 0 );
    w1 = ( w1 < w ) ? w1 : w;
    h1 = ( h1 < h ) ? h1 : h;
    XtVaSetValues( window->viewport, XtNwidth, w1, XtNheight, h1, 0 );

    /* add BOTTOM tile */
    if ( window->text == 0 )
	window->text = CreateTitle(paned);

    /* realize the window and return the number */
    XtRealizeWidget(window->top);

    /* add event handler for mouse clicks */
    XtAddEventHandler( window->draw, ButtonPressMask,
		       False, MouseClick, (XtPointer)GapWindows->len );

    /* remove dummy button and dummy text */
    XtDestroyWidget(button);
    XtVaSetValues( window->text, XtNlabel, name, 0 );

    /* add window to list and return window number */
    AddList( GapWindows, (void*) window );
    return ANSWER_GAP( "od", GapWindows->len-1, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunCloseWindow( <win> ) . . . . . . . . . . . . .  destroy an open window
*/
int FunCloseWindow ( arg )
    TypeArg   * arg;
{
    TypeMenu  * menu;
    long        i,  j;

    /* give debug info */
    DEBUG(( "CloseWindow( #%ld )\n", arg->iargs[0] ))

    /* set <used> to false */
    arg->win->used = False;

    /* free all menus */
    for ( i = 0;  i < arg->win->menus->len;  i++ )
	if ( (menu=arg->win->menus->ptr[i]) != 0 )
	{
	    for ( j = 0;  j < menu->entries->len;  j++ )
		XtFree((char*)(menu->entries->ptr[j]));
	    XtFree((char*)menu->entries->ptr);
	    XtFree((char*)menu->entries);
	    XtFree((char*)menu);
	}
    XtFree((char*)arg->win->menus->ptr);
    XtFree((char*)arg->win->menus);

    /* destroy top level shell (this will destroy all children) */
    XtDestroyWidget(arg->win->top);

    /* return OK */
    return AnswerGap( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunClearAll( <win> )  . . . . . . . . . . . . . . . . . clear all objects
*/
int FunClearAll ( arg )
    TypeArg   * arg;
{
    GGFreeAllObjects( arg->win->draw );
    return ANSWER_GAP( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunResize( <win>, <width>, <height> ) . . . . . . . . . . . resize window
*/
int FunResize ( arg )
    TypeArg * 		arg;
{
    ViewportWidget	viewport = (ViewportWidget)arg->win->viewport;
    Widget              dummy;

    /* check arguments */
    if ( arg->iargs[1] < 1 || arg->iargs[2] < 1 )
	return ANSWER_GAP( "esdsd", "illegal window dimensions ",
			   arg->iargs[1], "x", arg->iargs[2] );

    /* resize window */
    arg->win->width  = arg->iargs[1];
    arg->win->height = arg->iargs[2];
    GGResize( arg->win->draw, arg->iargs[1], arg->iargs[2] );

    /* try to update scrollbars */
    XtUnmanageChild(arg->win->draw);
    dummy = XtVaCreateManagedWidget(
	               "xgapWindowDrawable",
		       gapGraphicWidgetClass, (Widget)viewport,
                       XtNwidth,              arg->win->width,
	               XtNheight,             arg->win->height,
		       0 );
    XtUnmanageChild(dummy);
    XtManageChild(arg->win->draw);
    XtDestroyWidget(dummy);

    /* and return */
    return ANSWER_GAP( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunMenu( <win>, <name>, <str> ) . . . . . . . . . . . create a menu entry
*/
static void MenuClick ( w, cl, ca )
    Widget	    w;
    XtPointer       cl;
    XtPointer       ca;
{
    TypeMenuData  * pd = (TypeMenuData*) cl;
    char            buf[128];

    DEBUG(("MenuClick( #%d, #%d, #%d )\n", pd->window, pd->popup, pd->pane))
    sprintf(buf,"MenuSelected(%d,%d,%d);\n",pd->window,pd->popup,pd->pane);
    SimulateInput(buf);
}

int FunMenu ( arg )
    TypeArg       * arg;
{
    TypeMenuData  * pd;
    TypeMenu      * menu;
    Widget          pane;
    Widget          pshell;
    Widget          button;
    char	    buf[128];
    char          * ptr;
    char          * qtr;
    int             i;

    /* create a menu button */
    DEBUG(( "Menu( \"%s\", ... )\n", arg->sargs[0] ))
    button = XtVaCreateManagedWidget( "menuButton", menuButtonWidgetClass,
				      arg->win->box,
				      XtNlabel,      arg->sargs[0],
				      XtNshapeStyle, XmuShapeOval,
				      XtNleftBitmap, MenuSymbol,
				      0 );

    /* create a shell */
    pshell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass,
    	    	    	    	   button, XtNcursor, CursorTL, 0 );

    /* create menu entries */
    menu = (TypeMenu*) XtMalloc( sizeof(TypeMenu) );
    menu->shell   = pshell;
    menu->entries = List(0);
    for ( ptr = arg->sargs[1], i = 1;  *ptr; )
    {
    	qtr = buf;
    	while ( *ptr && *ptr != '|' )
    	    *qtr++ = *ptr++;
    	*qtr = 0;
    	if ( *ptr )  ptr++;
    	DEBUG(( "  entry = \"%s\"\n", buf ))
	if ( *buf == '-' )
	    XtVaCreateManagedWidget( "line", smeLineObjectClass, pshell, 0 );
	else
	{
	    pane = XtVaCreateManagedWidget( buf, smeBSBObjectClass,
    	    	    	    	    	    pshell,
					    XtNlabel,       buf,
					    XtNrightMargin, 14,
                                            XtNrightBitmap, EmptyMarkSymbol,
	                                    0 );
	    pd = (TypeMenuData*) XtMalloc( sizeof(TypeMenuData) );
	    pd->window = arg->iargs[0];
	    pd->pane   = i;
	    pd->popup  = arg->win->menus->len;
	    pd->shell  = pane;
	    XtAddCallback( pane, XtNcallback, MenuClick, pd );
	    i++;
	    AddList( menu->entries, (void*) pd );
	}
    }

    /* add shell to popup shell list */
    AddList( arg->win->menus, (void*) menu );
    return ANSWER_GAP( "od", arg->win->menus->len-1, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunCheckMenuEntry( <win>, <menu>, <entry>, <check> )  . .  add check mark
*/
int FunCheckMenuEntry ( arg )
    TypeArg   * arg;
{
    TypeMenu  * menu;
    Widget      entry;

    /* check menu number */
    DEBUG(( "CheckMenuEntry( #%ld, #%ld, #%ld, %ld )\n", arg->iargs[0],
	    arg->iargs[1], arg->iargs[2], arg->iargs[3] ))
    if ( arg->win->menus->len <= arg->iargs[1] || arg->iargs[1] < 0 )
	return ANSWER_GAP("esd","illegal menu number ",arg->iargs[1],0,0);
    menu = (TypeMenu*) arg->win->menus->ptr[arg->iargs[1]];

    /* check menu entry number */
    if ( menu->entries->len<arg->iargs[2] || arg->iargs[2] <= 0 )
	return ANSWER_GAP("esd","illegal menu entry ",arg->iargs[2],0,0);
    entry = ((TypeMenu*) menu->entries->ptr[arg->iargs[2]-1])->shell;

    /* set or clear check mark */
    if ( arg->iargs[3] )
	XtVaSetValues( entry, XtNrightBitmap, CheckMarkSymbol, 0 );
    else
	XtVaSetValues( entry, XtNrightBitmap, EmptyMarkSymbol, 0 );
    return ANSWER_GAP( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunEnableMenuEntry( <win>, <menu>, <entry>, <check> ) . .  enable/disable
*/
int FunEnableMenuEntry ( arg )
    TypeArg   * arg;
{
    TypeMenu  * menu;
    Widget      entry;

    /* check menu number */
    DEBUG(( "EnableMenuEntry( #%ld, #%ld, #%ld, %ld )\n", arg->iargs[0],
	    arg->iargs[1], arg->iargs[2], arg->iargs[3] ))
    if ( arg->win->menus->len <= arg->iargs[1] || arg->iargs[1] < 0 )
	return ANSWER_GAP("esd","illegal menu number ",arg->iargs[1],0,0);
    menu = (TypeMenu*) arg->win->menus->ptr[arg->iargs[1]];

    /* check menu entry number */
    if ( menu->entries->len<arg->iargs[2] || arg->iargs[2] <= 0 )
	return ANSWER_GAP("esd","illegal menu entry ",arg->iargs[2],0,0);
    entry = ((TypeMenu*) menu->entries->ptr[arg->iargs[2]-1])->shell;

    /* set or clear check mark */
    if ( arg->iargs[3] )
	XtVaSetValues( entry, XtNsensitive, True, 0 );
    else
	XtVaSetValues( entry, XtNsensitive, False, 0 );
    return ANSWER_GAP( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunQueryPointer( <win> )  . . . . . . . . . . . . . . . . . query pointer
*/
int FunQueryPointer ( arg )
    TypeArg       * arg;
{
    Window          root;
    Window          child;
    int        	    x,  y,  x2,  y2;
    unsigned int    pt;
    unsigned int    bt;
    unsigned int    md;

    /* query pointer */
    XQueryPointer( XtDisplay(arg->win->draw), XtWindow(arg->win->draw),
    	    	   &root, &child, &x, &y, &x2, &y2, &pt );

    /* and make a sanity check */
    if ( arg->win->width < x2 || x2 < 0 )
	x2 = -1;
    if ( arg->win->height < y2 || y2 < 0 )
	y2 = -1;

    /* check mouse buttons */
    bt = 0;
    if ( pt & Button1Mask )
	bt |= 1;
    if ( pt & Button3Mask )
	bt |= 2;

    /* check modifier keys */
    md = 0;
    if ( pt & ShiftMask )
	md |= 1;
    if ( pt & ControlMask )
	md |= 2;
    if ( pt & Mod1Mask )
	md |= 4;

    /* gap will be answered by 'PopingDown' */
    return ANSWER_GAP( "odddd", x2, y2, bt, md );
}


/****************************************************************************
**
*F  FunRemoveObjects( <win>, <obj>, ... )   . . . . .  remove a window object
*/
int FunRemoveObjects ( arg )
    TypeArg     * arg;
{
    char	* str;
    long          n;

    /* return if no optional args are given */
    if ( (str=arg->opts) == 0 )
	return ANSWER_GAP( "es", "no objects given", 0, 0, 0 );

    /* give debug info */
    DEBUG(( "RemoveObject( #%ld, %s )\n", arg->iargs[0], str ))

    /* remove objects */
    if ( !arg->win->fast_update )
	GGStartRemove(arg->win->draw);
    while ( *str )
    {
	if ( !ParseLong( &str, &n ) )
	{
	    if ( !arg->win->fast_update )
		GGStopRemove(arg->win->draw);
	    return ANSWER_GAP( "es", "illegal argument", 0, 0, 0 );
	}
	if ( GGRemoveObject( arg->win->draw, n ) )
	{
	    if ( !arg->win->fast_update )
		GGStopRemove(arg->win->draw);
	    return ANSWER_GAP("esds","illegal object number: '",n,"'",0);
	}
    }
    if ( !arg->win->fast_update )
	GGStopRemove(arg->win->draw);
    return ANSWER_GAP( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunFastUpdate( <win>, <flag> )  . . . . . . . . . en-/disable fast update
*/
int FunFastUpdate ( arg )
    TypeArg *	arg;
{
    Boolean	flag;

    flag = ( arg->iargs[1] == 0 ) ? False : True;
    if ( arg->win->fast_update != flag )
    {
	arg->win->fast_update = flag;
	GGFastUpdate( arg->win->draw, flag );
    }
    return ANSWER_GAP( "o", 0, 0, 0, 0 );
}



/****************************************************************************
**
*F  FunSetLineWidth( <win>, <wdt> ) . . . . . set line width for next objects
*/
int FunSetLineWidth ( arg )
    TypeArg   * arg;
{
    arg->win->line_width = ( arg->iargs[1] <= 1 ) ? 0 : arg->iargs[1];
    return ANSWER_GAP( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunDrawLine( <win>, <x1>, <y1>, <x2>, <y2> )  . . . . . . . . draw a line
*/
int FunDrawLine ( arg )
    TypeArg               * arg;
{
    TypeGapGraphicObject  * obj;
    long	            n;
    long                    w;

    /* create a line object */
    obj = (TypeGapGraphicObject*) XtMalloc( sizeof(TypeGapGraphicObject) );
    obj->type = T_LINE;
    obj->desc.line.x1 = arg->iargs[1];
    obj->desc.line.y1 = arg->iargs[2];
    obj->desc.line.x2 = arg->iargs[3];
    obj->desc.line.y2 = arg->iargs[4];
    w = obj->desc.line.w  = arg->win->line_width;
    obj->x = MIN( obj->desc.line.x1, obj->desc.line.x2 ) - w;
    obj->y = MIN( obj->desc.line.y1, obj->desc.line.y2 ) - w;
    obj->w = MAX( obj->desc.line.x1, obj->desc.line.x2 ) - obj->x + 1 + 2*w;
    obj->h = MAX( obj->desc.line.y1, obj->desc.line.y2 ) - obj->y + 1 + 2*w;
    CHECK_BBOX(obj);
    DEBUG(( "DrawLine( #%ld, %ld, %ld, %ld, %ld )\n", arg->iargs[0],
	    obj->desc.line.x1, obj->desc.line.y1,
	    obj->desc.line.x2, obj->desc.line.y2 ))

    /* use 'GGAddObject' to draw the object */
    n = GGAddObject( arg->win->draw, obj );
    return ANSWER_GAP( "od", n, 0, 0, 0 );
}



/****************************************************************************
**
*F  FunDrawCircle( <win>, <x>, <y>, <r> ) . . . . . . . . . . . draw a circle
*/
int FunDrawCircle ( arg )
    TypeArg               * arg;
{
    TypeGapGraphicObject  * obj;
    long	            n;
    long                    w;

    /* create a circle object */
    obj = (TypeGapGraphicObject*) XtMalloc( sizeof(TypeGapGraphicObject) );

    /* convert <x> and <y> coordinates to X windows style */
    obj->type = T_CIRCLE;
    obj->desc.circle.r = 2 * arg->iargs[3];
    obj->desc.circle.x = arg->iargs[1] - arg->iargs[3];
    obj->desc.circle.y = arg->iargs[2] - arg->iargs[3];
    w = obj->desc.circle.w  = arg->win->line_width;
    obj->x = obj->desc.circle.x - w - 1;
    obj->y = obj->desc.circle.y - w - 1;
    obj->w = obj->desc.circle.r+1 + 2*w + 2;
    obj->h = obj->desc.circle.r+1 + 2*w + 2;
    CHECK_BBOX(obj);
    DEBUG(( "DrawCircle( #%ld, %ld, %ld, %ld )\n", arg->iargs[0],
	    arg->iargs[1], arg->iargs[2], arg->iargs[3] ))

    /* use 'GGAddObject' to draw the object */
    n = GGAddObject( arg->win->draw, obj );
    return ANSWER_GAP( "od", n, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunDrawDisc( <win>, <x>, <y>, <r> ) . . . . . . . . . . draw a solid disc
*/
int FunDrawDisc ( arg )
    TypeArg               * arg;
{
    TypeGapGraphicObject  * obj;
    long	            n;

    /* create a disc object */
    obj = (TypeGapGraphicObject*) XtMalloc( sizeof(TypeGapGraphicObject) );

    /* convert <x> and <y> coordinates to X windows style */
    obj->type = T_DISC;
    obj->desc.disc.r = 2 * arg->iargs[3];
    obj->desc.disc.x = arg->iargs[1] - arg->iargs[3];
    obj->desc.disc.y = arg->iargs[2] - arg->iargs[3];
    obj->x = obj->desc.disc.x;
    obj->y = obj->desc.disc.y;
    obj->w = obj->desc.disc.r+1;
    obj->h = obj->desc.disc.r+1;
    CHECK_BBOX(obj);
    DEBUG(( "DrawCircle( #%ld, %ld, %ld, %ld )\n", arg->iargs[0],
	    arg->iargs[1], arg->iargs[2], arg->iargs[3] ))

    /* use 'GGAddObject' to draw the object */
    n = GGAddObject( arg->win->draw, obj );
    return ANSWER_GAP( "od", n, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunDrawBox( <win>, <x1>, <y1>, <x2>, <y2> ) . . . draw a filled rectangle
*/
int FunDrawBox ( arg )
    TypeArg               * arg;
{
    TypeGapGraphicObject  * obj;
    long	            n;

    /* create a line object */
    obj = (TypeGapGraphicObject*) XtMalloc( sizeof(TypeGapGraphicObject) );

    /* convert <x> and <y> coordinates to X windows style */
    obj->type = T_BOX;
    obj->desc.rect.x1 = MIN( arg->iargs[1], arg->iargs[3] );
    obj->desc.rect.y1 = MIN( arg->iargs[2], arg->iargs[4] );
    obj->desc.rect.x2 = MAX( arg->iargs[1], arg->iargs[3] );
    obj->desc.rect.y2 = MAX( arg->iargs[2], arg->iargs[4] );
    obj->x = MIN( obj->desc.rect.x1, obj->desc.rect.x2 )-1;
    obj->y = MIN( obj->desc.rect.y1, obj->desc.rect.y2 )-1;
    obj->w = MAX( obj->desc.rect.x1, obj->desc.rect.x2 )+1;
    obj->h = MAX( obj->desc.rect.y1, obj->desc.rect.y2 )+1;
    CHECK_BBOX(obj);
    DEBUG(( "DrawBox( #%ld, %ld, %ld, %ld )\n", arg->iargs[0],
	    arg->iargs[1], arg->iargs[2], arg->iargs[3] ))

    /* use 'GGAddObject' to draw the object */
    n = GGAddObject( arg->win->draw, obj );
    return ANSWER_GAP( "od", n, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunDrawRectangle( <win>, <x1>, <y1>, <x2>, <y2> ) . . .  draw a rectangle
*/
int FunDrawRectangle ( arg )
    TypeArg               * arg;
{
    TypeGapGraphicObject  * obj;
    long	            n;
    long                    w;

    /* create a line object */
    obj = (TypeGapGraphicObject*) XtMalloc( sizeof(TypeGapGraphicObject) );

    /* convert <x> and <y> coordinates to X windows style */
    obj->type = T_RECT;
    obj->desc.rect.x1 = MIN( arg->iargs[1], arg->iargs[3] );
    obj->desc.rect.y1 = MIN( arg->iargs[2], arg->iargs[4] );
    obj->desc.rect.x2 = MAX( arg->iargs[1], arg->iargs[3] );
    obj->desc.rect.y2 = MAX( arg->iargs[2], arg->iargs[4] );
    w = obj->desc.rect.w  = arg->win->line_width;
    obj->x = MIN( obj->desc.rect.x1, obj->desc.rect.x2 ) - w;
    obj->y = MIN( obj->desc.rect.y1, obj->desc.rect.y2 ) - w;
    obj->w = MAX( obj->desc.rect.x1, obj->desc.rect.x2 ) - obj->x + 1 + 2*w;
    obj->h = MAX( obj->desc.rect.y1, obj->desc.rect.y2 ) - obj->y + 1 + 2*w;
    CHECK_BBOX(obj);
    DEBUG(( "DrawRectangle( #%ld, %ld, %ld, %ld )\n", arg->iargs[0],
	    arg->iargs[1], arg->iargs[2], arg->iargs[3] ))

    /* use 'GGAddObject' to draw the object */
    n = GGAddObject( arg->win->draw, obj );
    return ANSWER_GAP( "od", n, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunDrawText( <win>, <fid>, <x>, <y>, <str> )  . . . . . . draw text <str>
*/
int FunDrawText ( arg )
    TypeArg               * arg;
{
    TypeGapGraphicObject  * obj;
    XFontStruct           * font = arg->font;
    long	            n;

    /* create a TEXT object */
    obj = (TypeGapGraphicObject*) XtMalloc( sizeof(TypeGapGraphicObject) );

    /* convert <x> and <y> coordinates to X windows style */
    obj->type = T_TEXT;
    obj->desc.text.x    = arg->iargs[2];
    obj->desc.text.y    = arg->iargs[3];
    obj->desc.text.font = font->fid;
    obj->desc.text.len  = strlen(arg->sargs[0]);
    obj->desc.text.str  = XtMalloc(obj->desc.text.len+1);
    strcpy( obj->desc.text.str, arg->sargs[0] );
    obj->x = arg->iargs[2] - 1;
    obj->y = arg->iargs[3] - font->ascent - 1;
    obj->w = obj->desc.text.len*font->max_bounds.width + 2;
    obj->h = font->descent + font->ascent + 2;
    CHECK_BBOX(obj);
    DEBUG(( "DrawText( #%ld, %ld, %ld, %s )\n", arg->iargs[0],
	    arg->iargs[1], arg->iargs[2], arg->sargs[0] ))

    /* use 'GGAddObject' to draw the object */
    n = GGAddObject( arg->win->draw, obj );
    return ANSWER_GAP( "od", n, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunAddTitle( <win>, <str> ) . . . . . . . . . . . . . . add a (sub) title
*/
int FunAddTitle ( arg )
    TypeArg   * arg;
{
    XtVaSetValues( arg->win->text, XtNlabel, arg->sargs[0], 0 );
    return ANSWER_GAP( "o", 0, 0, 0, 0 );
}


/****************************************************************************
**
*F  FunFontInfo( <win>, <fid> ) . . . . . . . . . .   information about fonts
*/
int FunFontInfo ( arg )
    TypeArg	* arg;
{
    XFontStruct * font = arg->font;

    return ANSWER_GAP( "oddd", font->ascent, font->descent,
		               font->max_bounds.width, 0 );
}


/****************************************************************************
**

*F  InitXCMDS()	. . . . . . . . . . . . . . .  initalize all global variables
*/
#define Azeichen_width 32
#define Azeichen_height 32
static unsigned char Azeichen_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00,
   0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x30, 0x06, 0x00,
   0x00, 0x30, 0x06, 0x00, 0x00, 0x18, 0x0c, 0x00, 0x00, 0x98, 0x0c, 0x00,
   0x00, 0x8c, 0x18, 0x00, 0x00, 0xcc, 0x19, 0x00, 0x00, 0xc6, 0x31, 0x00,
   0x00, 0xc6, 0x31, 0x00, 0x00, 0xe3, 0x63, 0x00, 0x00, 0xe3, 0x63, 0x00,
   0x80, 0xe1, 0xc3, 0x00, 0x80, 0xe1, 0xc3, 0x00, 0xc0, 0xc0, 0x81, 0x01,
   0xc0, 0xc0, 0x81, 0x01, 0x60, 0xc0, 0x01, 0x03, 0x60, 0x80, 0x00, 0x03,
   0x30, 0x80, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x18, 0x80, 0x00, 0x0c,
   0x18, 0xc0, 0x01, 0x0c, 0x0c, 0xe0, 0x03, 0x18, 0x0c, 0xc0, 0x01, 0x18,
   0x06, 0x80, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0x7f,
   0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00};

static XtActionsRec PrivateActions[] =
{
    { "NotifyClick",  NotifyClick }
};

void InitXCMDS ()
{
    Pixmap  sym;

    /* load the fonts */
    XtVaGetValues( GapTalk,
		   XtNtinyFont,     &TinyFont,
		   XtNsmallFont,    &SmallFont,
		   XtNnormalFont,   &NormalFont,
		   XtNlargeFont,    &LargeFont,
                   XtNhugeFont,     &HugeFont,
		   0 );

    /* create top left arrow cusor */
    CursorTL = XCreateFontCursor( XtDisplay(GapTalk), XC_top_left_arrow );

    /* create menu symbol */
    MenuSymbol = XCreateBitmapFromData(XtDisplay(GapTalk),XtWindow(GapTalk),
             "\376\3\2\2\2\6\162\6\2\6\162\6\2\6\162\6\2\6\2\6\376\7\370\7",
             12,12);

    /* create check mark and empty mark */
    CheckMarkSymbol =
	XCreateBitmapFromData(XtDisplay(GapTalk),XtWindow(GapTalk),
	"\0\0\0\4\0\6\0\3\200\1\300\0\142\0\66\0\34\0\10\0\0\0\0\0",
	12,12);
    EmptyMarkSymbol =
	XCreateBitmapFromData(XtDisplay(GapTalk),XtWindow(GapTalk),
	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
	12,12);

    /* create a list of windows and popups */
    GapWindows    = List(0);
    PopupMenus    = List(0);
    TextSelectors = List(0);

    /* create popup dialogs */
    sym = XCreateBitmapFromData( XtDisplay(GapTalk), XtWindow(GapTalk),
				 Azeichen_bits, Azeichen_width,
				 Azeichen_height );
    DialogOkCancel = CreatePopupDialog( AppContext, GapTalk,
				        "OkCancelDialog", 12, 4, sym );

    /* register private actions */
    XtAppAddActions( AppContext, PrivateActions, XtNumber(PrivateActions) );
}

/****************************************************************************
**
*F  ExitXCMDS()	. . . . . . . . . . . . . . . . .  clear all global variables
*/
void ExitXCMDS ()
{
    TypeArg         arg;
    TypeMenu      * menu;
    long            i,  j;

    /* clear list of windows and popups */
    for ( i = 0;  i < GapWindows->len;  i++ )
    {
	arg.win = GapWindows->ptr[i];
	if ( arg.win->used )
	    FunCloseWindow(&arg);
	XtFree((char*)(GapWindows->ptr[i]));
    }
    XtFree((char*)GapWindows);
    for ( i = 0;  i < TextSelectors->len;  i++ )
    {
	arg.sel = TextSelectors->ptr[i];
	if ( arg.sel != 0 )
	{
	    FunCloseSelector(&arg);
	    XtFree((char*)(TextSelectors->ptr[i]));
	}
    }
    XtFree((char*)TextSelectors);
    for (i = 0;  i < PopupMenus->len;  i++ )
	if ( (menu = PopupMenus->ptr[i]) != 0 )
	{
	    for ( j = 0;  j < menu->entries->len;  j++ )
		XtFree((char*)(menu->entries->ptr[j]));
	    XtFree((char*)menu->entries);
	    XtFree((char*)menu);
	}
    XtFree((char*)PopupMenus);
}

/****************************************************************************
**
*V  WindowCommands[]  . . . . . . . . . .  . . . . .  list of window commands
*/
TypeWindowCommand WindowCommands[] =
{
    { "XAT",    "#S",       FunAddTitle             },
    { "XCA",    "#",        FunClearAll             },
    { "XCL",    "TS",       FunChangeList           },
    { "XCM",    "#III",     FunCheckMenuEntry 	    },
    { "XCS",    "T",        FunCloseSelector        },
    { "XCW",    "#",        FunCloseWindow          },
    { "XDB",    "#IIII",    FunDrawBox              },
    { "XDC",    "#III",     FunDrawCircle           },
    { "XDD",    "#III",     FunDrawDisc             },
    { "XDL",    "#IIII",    FunDrawLine             },
    { "XDR",    "#IIII",    FunDrawRectangle        },
    { "XDT",    "#FIIS",    FunDrawText             },
    { "XEM",    "#III",     FunEnableMenuEntry 	    },
    { "XEB",    "TII",      FunEnableButton         },
    { "XFI",    "F",        FunFontInfo             },
    { "XFU",    "#I",       FunFastUpdate           },
    { "XLW",    "#I",       FunSetLineWidth         },
    { "XME",    "#SS",      FunMenu                 },
    { "XOS",    "SSS",      FunOpenSelector         },
    { "XOW",  	"SII",      FunOpenWindow           },
    { "XPS",    "SS" ,      FunPopupShell           },
    { "XQP",    "#",        FunQueryPointer         },
    { "XRE",    "#II",      FunResize               },
    { "XRO",    "#*",       FunRemoveObjects        },
    { "XSD",    "ISS",      FunShowDialog           },
    { "XSP",    "I",        FunShowPopup            },
    { "XUS",    "T",        FunUnhighlihtSelector   },
    { 0L,       0L,         0L              	    }
};
