/*
 * Electric(tm) VLSI Design Systems
 *
 * File: graphunixx11.c
 * X Window System interface with Athena/Motif/Lesstif widgets
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "global.h"
#include "database.h"
#include "egraphics.h"
#include "usr.h"
#include "eio.h"
#include "usrtrack.h"
#include "edialogs.h"
#include "config.h"
#include <signal.h>
#include <errno.h>
#include <pwd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>
#include <X11/Shell.h>
#ifdef USETIF
#  include <Xm/Xm.h>
#  include <Xm/Text.h>
#  include <Xm/RowColumn.h>
#  include <Xm/CascadeB.h>
#  include <Xm/ToggleBG.h>
#  include <Xm/PushB.h>
#  include <Xm/Separator.h>
#  include <Xm/FileSB.h>
#  include <Xm/Protocols.h>
#  include <Xm/MwmUtil.h>
#  include <Xm/Display.h>
#  include <Xm/CutPaste.h>
#else
#  include <X11/Xaw/Box.h>
#  include <X11/Xaw/Text.h>
#  include <X11/Xaw/AsciiText.h>
#endif

#ifdef TRUETYPE
#  include "t1lib.h"
#endif

#ifdef HAVE_SYS_TYPES_H
#  include <sys/types.h>
#endif

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif

#ifdef HAVE_FCNTL_H
#  include <fcntl.h>
#endif

#ifdef HAVE_TERMIOS_H
#  include <termios.h>
#else
#  ifdef HAVE_TERMIO_H
#    include <termio.h>
#  else
#    include <sgtty.h>
#  endif
#endif

#ifdef TIME_WITH_SYS_TIME
#  include <sys/time.h>
#  include <time.h>
#else
#  ifdef HAVE_SYS_TIME_H
#    include <sys/time.h>
#  else
#    include <time.h>
#  endif
#endif

#ifdef HAVE_SYS_TIMEB_H
#  include <sys/timeb.h>
#endif

#ifdef HAVE_SYS_WAIT_H
#  include <sys/wait.h>
#endif

#ifdef HAVE_VFORK_H
#  include <vfork.h>
#endif

#ifdef HAVE_DIRENT_H
#  include <dirent.h>
#  define NAMELEN(dirent) strlen((dirent)->d_name)
#else
#  define dirent direct
#  define NAMELEN(dirent) (dirent)->d_namelen
#  ifdef HAVE_SYS_NDIR_H
#    include <sys/ndir.h>
#  endif
#  ifdef HAVE_SYS_DIR_H
#    include <sys/dir.h>
#  endif
#  ifdef HAVE_NDIR_H
#    include <ndir.h>
#  endif
#endif

#if LANGTCL
#  include "dblang.h"
#  include "tcl.h"
#  ifdef USETK
#    include "tk.h"
    Tk_Window gra_tktopwindow;
    void      gra_initializetk(void);
#  endif
  Tcl_Interp *gra_tclinterp;
  INTBIG gra_initializetcl(void);
#endif

/****** windows ******/

#define WMLEFTBORDER    8						/* size of left border for windows */
#define WMTOPBORDER    30						/* size of top border for windows */
#define MAXSTATUSLINES  1						/* number of status lines */
#define FLUSHTICKS     60						/* ticks between display flushes */
#define SBARWIDTH      15						/* size of text-edit scroll bars */
#define PALETTEWIDTH  110

#ifdef __cplusplus
#  define VisualClass(v) v->c_class
#else
#  define VisualClass(v) v->class
#endif

static XtAppContext  gra_xtapp;					/* the main XT application */
static Display      *gra_maindpy;				/* the main X display (edit) */
static Display      *gra_altdpy;				/* the alternate X display (messages/other edit) */
static INTBIG        gra_mainscreen;
static INTBIG        gra_altscreen;
static WINDOWDISPLAY gra_mainwd;
static WINDOWDISPLAY gra_altwd;
static Colormap      gra_maincolmap;			/* main display colormap */
static Colormap      gra_altcolmap;				/* alternate display colormap */
static XSizeHints   *gra_editxsh;
static INTBIG        gra_editposx;
static INTBIG        gra_editposy;
static INTBIG        gra_editsizex;
static INTBIG        gra_editsizey;
static INTBIG        gra_palettewidth;
static INTBIG        gra_paletteheight;
static INTBIG        gra_palettetop;
static UINTBIG       gra_internalchange;
static Pixmap        gra_programicon;
static INTBIG        gra_windowframeindex = 0;
static INTBIG        gra_windownumber;			/* number of the window that was created */
static BOOLEAN       gra_checkinginterrupt;		/* nonzero if checking for interrupt key */
static INTBIG        gra_status_height;			/* space for lines at bottom of screen */
static WINDOWFRAME  *gra_deletethisframe = 0;	/* window that is to be deleted */
static Atom          gra_wm_delete_window;		/* to trap window kills */
static int           gra_argc;					/* "argc" for display */
static char        **gra_argv;					/* "argv" for display */
static char          gra_localstring[256];
XtSignalId           gra_intsignalid;
extern GRAPHICS      us_box, us_ebox, us_menutext;

#ifndef ANYDEPTH
  static INTBIG      gra_maphigh;				/* highest entry to use in color map */
  static BOOLEAN     gra_use_backing;			/* flag if backing store used */
#endif
#ifdef USETIF
  static Atom        gra_movedisplayprotocol;
  static Atom        gra_movedisplaymsg;
  static INTBIG      gra_windowbeingdeleted = 0;
#else
  static Atom        gra_wm_protocols;
#endif

int          gra_xerrors(Display *dpy, XErrorEvent *err);
void         gra_xterrors(char *msg);
void         gra_adjustwindowframeon(Display *dpy, INTBIG screen, INTBIG how);
void         gra_finddisplays(BOOLEAN wantmultiscreen);
BOOLEAN      gra_buildwindow(Display *dpy, WINDOWDISPLAY *wd, Colormap colmap, WINDOWFRAME *wf,
				BOOLEAN floating, BOOLEAN samesize);
void         gra_reloadmap(void);
void         gra_getwindowattributes(WINDOWFRAME *wf, XWindowAttributes *xwa);
void         gra_getwindowinteriorsize(WINDOWFRAME *wf, INTBIG *wid, INTBIG *hei);
void         gra_intsignalfunc(void);
BOOLEAN      gra_loggetnextaction(char *extramessage); 
void         gra_logwriteaction(INTBIG inputstate, INTBIG special, INTBIG cursorx,
				INTBIG cursory, void *extradata);
void         gra_logwritecomment(char *comment);
BOOLEAN      gra_logreadline(char *string, INTBIG limit);
void         gra_removewindowextent(RECTAREA *r, Widget wnd);
BOOLEAN      gra_makewindowdisplaybuffer(WINDOWDISPLAY *wd, Display *dpy);
void         gra_getdisplaydepth(Display *dpy, INTBIG *depth, INTBIG *truedepth);
void         gra_handlequeuedframedeletion(void);
WINDOWFRAME *gra_getframefromindex(INTBIG index);
#ifdef USETIF
  void       gra_movedisplay(Widget w, XtPointer client_data, XtPointer *call_data);
  INTBIG     gra_nativepopuptif(POPUPMENU **menuptr, BOOLEAN header, INTBIG left, INTBIG top);
#endif
#ifndef ANYDEPTH
  void       gra_loadstipple(WINDOWFRAME *wf, UINTSML raster[]);
  void       gra_setxstate(WINDOWFRAME *wf, GC gc, GRAPHICS *desc);
#endif

/****** the messages window ******/
#ifndef USETIF
#  define MESSAGEBUFFERSIZE 10000				/* size of XT messages window */
#endif
#define MESLEADING              1				/* extra character spacing */

static Display      *gra_topmsgdpy;				/* the X display for the messages widget */
static Window        gra_topmsgwin;				/* the X window for the messages widget */
static Widget        gra_msgtoplevelwidget;		/* the outer messages widget */
static Widget        gra_messageswidget = 0;	/* the inner messages widget */
static BOOLEAN       gra_messages_obscured;		/* true if messages window covered */
static BOOLEAN       gra_messages_typingin;		/* true if typing into messages window */
static BOOLEAN       gra_msgontop;				/* true if messages window has focus */

void gra_makemessageswindow(void);

/****** the menus ******/
#ifdef USETIF
  static INTBIG      gra_popupmenuresult;
  static INTBIG      gra_pulldownmenucount = 0;	/* number of top-level pulldown menus */
  static POPUPMENU  *gra_pulldowns[50];			/* the current top-level pulldown menus */

  Widget   gra_makepdmenu(WINDOWFRAME *wf, POPUPMENU *pm, INTBIG value, Widget parent,
			INTBIG pindex);
  void     gra_pulldownindex(WINDOWFRAME *wf, POPUPMENU *pm, INTBIG order, Widget parent);
  void     gra_menucb(Widget W, int item_number, XtPointer call_data);
  void     gra_menupcb(Widget w, int item_number, XtPointer call_data);
  void     gra_nativemenudoone(WINDOWFRAME *wf, INTBIG low, INTBIG high);
  void     gra_pulldownmenuload(WINDOWFRAME *wf);
#endif

/****** the dialogs ******/
#define MAXSCROLLMULTISELECT 1000

#ifdef USETIF

#  define MAXDIALOGS            3   /* maximum nested dialogs */
#  define DIALOGNUM            16
#  define DIALOGDEN            12
#  define MAXICONS             20

  typedef struct
  {
	Widget      window;
	Widget      windowframe;
	DIALOG     *itemdesc;
	INTBIG      defaultbutton;
	Widget      items[100];
	INTBIG      redrawitem;
	void      (*redrawroutine)(RECTAREA*);
	INTBIG      editline;
	char        lastedittext[300];
	INTBIG      opaqueitem;
	INTBIG      inix, iniy;
	INTBIG      usertextsize;
  } TDIALOG;

  TDIALOG         gra_dialogs[MAXDIALOGS];
  TDIALOG        *gra_curdialog;
  INTBIG          gra_curdialogindex = -1;
  INTBIG          gra_dialoghit;
  INTBIG          gra_dialoghitchar;
  char            gra_opaquefield[300];
  GC              gra_gcdia;
  char           *gra_icontruedata = 0, *gra_iconrowdata;   /* working memory for "gra_makeicon()" */
  static void   (*gra_diaeachdown)(INTBIG x, INTBIG y);

  /* dialog support routines */
  void     gra_flushdialog(void);
  void     gra_getdialogcoordinates(RECTAREA *rect, INTBIG *x, INTBIG *y,
			INTBIG *wid, INTBIG *hei);
  INTBIG   gra_scaledialogcoordinate(INTBIG x);
  INTBIG   gra_makedpycolor(Display *dpy, INTBIG r, INTBIG g, INTBIG b);
  void     gra_dialogaction(Widget w, XtPointer client_data,
			XmSelectionBoxCallbackStruct *call_data);
  void     gra_dialogredraw(Widget w, XtPointer client_data,
			XmSelectionBoxCallbackStruct *call_data);
  void     gra_dialogredrawsep(Widget w, XtPointer client_data,
			XmSelectionBoxCallbackStruct *call_data);
  void     gra_dialogopaqueaction(Widget w, XtPointer client_data,
			XmTextVerifyCallbackStruct *call_data);
  void     gra_dialogdraw(Widget widget, XEvent *event, String *args, int *num_args);
  void     gra_dialogcopywholelist(Widget widget, XEvent *event, String *args, int *num_args);
  Pixmap   gra_makeicon(char *data, Widget widget);
  void     gra_dialog_event_handler(Widget w, XtPointer data, XEvent *event, Boolean *cont);
#endif

static INTBIG  gra_dialogstringpos;
static BOOLEAN gra_diaeachdownhandler(INTBIG x, INTBIG y);
static int     gra_stringposascending(const void *e1, const void *e2);

/****** events ******/
/* the meaning of "gra_inputstate" */
#define CHARREAD             0377			/* character that was read */
#define ISKEYSTROKE          0400			/* set if key typed */
#define ISBUTTON            07000			/* set if button pushed (or released) */
#define ISLEFT              01000			/*    left button */
#define ISMIDDLE            02000			/*    middle button */
#define ISRIGHT             03000			/*    right button */
#define ISWHLFWD            04000			/*    forward on the mouse wheel */
#define ISWHLBKW            05000			/*    backward on the mouse wheel */
#define BUTTONUP           010000			/* set if button was released */
#define SHIFTISDOWN        020000			/* set if shift key was held down */
#define CONTROLISDOWN      040000			/* set if control key was held down */
#define METAISDOWN        0100000			/* set if meta key was held down */
#define DOUBLECL          0200000			/* set if double-click */
#define MOTION            0400000			/* set if mouse motion detected */
#define WINDOWSIZE       01000000			/* set if window grown */
#define WINDOWMOVE       02000000			/* set if window moved */
#define MENUEVENT        04000000			/* set if menu entry selected (values in cursor) */
#define FILEREPLY       010000000			/* set if file selected in standard file dialog */
#define DIAITEMCLICK    020000000			/* set if item clicked in dialog */
#define DIASCROLLSEL    040000000			/* set if scroll item selected in dialog */
#define DIAEDITTEXT    0100000000			/* set if edit text changed in dialog */
#define DIAPOPUPSEL    0200000000			/* set if popup item selected in dialog */
#define DIASETCONTROL  0400000000			/* set if control changed in dialog */
#define DIAUSERMOUSE  01000000000			/* set if mouse moved in user area of dialog */
#define DIAENDDIALOG  02000000000			/* set when dialog terminates */
#define POPUPSELECT   04000000000			/* popup selection made */
#define NOEVENT                -1			/* set if nothing happened */

#define INITIALTIMERDELAY      9			/* ratio of initial repeat to others */
#define TICKSPERSECOND        10			/* frequency of timer for repeats */

typedef RETSIGTYPE (*SIGNALCAST)(int);

struct
{
	INTBIG x;
	INTBIG y;
} gra_action;

typedef struct
{
	INTBIG                cursorx, cursory;		/* current position of mouse */
	INTBIG                inputstate;			/* current state of device input */
	INTBIG                special;				/* special command to do */
} MYEVENTQUEUE;

#define EVENTQUEUESIZE	100

static MYEVENTQUEUE  gra_eventqueue[EVENTQUEUESIZE];
static MYEVENTQUEUE *gra_eventqueuehead;		/* points to next event in queue */
static MYEVENTQUEUE *gra_eventqueuetail;		/* points to first free event in queue */

static INTBIG        gra_inputstate;			/* current state of device input */
static INTBIG        gra_inputspecial;			/* current "special" keyboard value */
static INTBIG        gra_doublethresh;			/* threshold of double-click */
static INTBIG        gra_cursorx, gra_cursory;	/* current position of mouse */
static INTBIG        gra_lstcurx, gra_lstcury;	/* former position of mouse */
static INTBIG        gra_logrecordcount = 0;	/* count for session flushing */
static INTBIG        gra_lastloggedaction = NOEVENT;
static INTBIG        gra_lastloggedx, gra_lastloggedy;
static INTBIG        gra_lastloggedindex;
static UINTBIG       gra_lastplaybacktime = 0;	/* offset to system time */
static UINTBIG       gra_logbasetime;
static INTBIG        gra_firstbuttonwait = 0;
static INTBIG        gra_lasteventstate = 0;	/* last event state (for repeating) */
static INTBIG        gra_lasteventx;			/* X of last event (for repeating) */
static INTBIG        gra_lasteventy;			/* Y of last event (for repeating) */
static INTBIG        gra_lasteventspecial;		/* special factor of last event (for repeating) */
static INTBIG        gra_lasteventtime;			/* time of last event (for repeating) */
static UINTBIG       gra_timeoffset = 0;		/* offset to system time */
static INTBIG        gra_cureventtime = 0;		/* current time counter (for repeating) */
static XEvent        gra_samplemotionevent;		/* fake event for repeating */
#ifdef USETIF
  static XEvent      gra_lastbuttonpressevent;	/* last button press event for popup menus */
#endif

#if 0		/* for debugging X events */
static char *eventNames[] =
{
	"",
	"",
	"KeyPress",
	"KeyRelease",
	"ButtonPress",
	"ButtonRelease",
	"MotionNotify",
	"EnterNotify",
	"LeaveNotify",
	"FocusIn",
	"FocusOut",
	"KeymapNotify",
	"Expose",
	"GraphicsExpose",
	"NoExpose",
	"VisibilityNotify",
	"CreateNotfiy",
	"DestroyNotfiy",
	"UnmapNotify",
	"MapNotify",
	"MapRequest",
	"ReparentNotfiy",
	"ConfigureNotfiy",
	"ConfigureRequest",
	"GravityNotfiy",
	"ResizeRequest",
	"CirculateNotify",
	"CirculateRequest",
	"PropertyNotfiy",
	"SelectionClear",
	"SelectionRequest",
	"SelectionNotify",
	"ColormapNotify",
	"ClientMessage",
	"MappingNotify"
};
#endif

void       gra_nextevent(void);
void       gra_repaint(WINDOWFRAME *wf, BOOLEAN redo);
void       gra_recalcsize(WINDOWFRAME *wf, INTBIG wid, INTBIG hei);
void       gra_addeventtoqueue(INTBIG state, INTBIG special, INTBIG x, INTBIG y);
void       gra_pickupnextevent(void);
void       gra_graphics_event_handler(Widget w, XtPointer data, XEvent *event, Boolean *cont);
void       gra_messages_event_handler(Widget w, XtPointer data, XEvent *event, Boolean *cont);
Boolean    gra_dowork(XtPointer client_data);
void       gra_timerticked(XtPointer closure, XtIntervalId *id);
INTBIG     gra_translatekey(XEvent *event, INTBIG *special);
RETSIGTYPE gra_sigill_trap(void);
RETSIGTYPE gra_sigfpe_trap(void);
RETSIGTYPE gra_sigbus_trap(void);
RETSIGTYPE gra_sigsegv_trap(void);
WINDOWFRAME *gra_getcurrentwindowframe(Widget widget, BOOLEAN set);
#ifdef USETIF
  void     gra_windowdelete(Widget w, XtPointer client_data, XtPointer *call_data);
#else
  void     gra_destroygraphics(Widget w, XtPointer data, XEvent *event, Boolean *cont);
#endif

/****** mouse buttons ******/
#define BUTTONS     45		/* cannot exceed NUMBUTS in "usr.h" */
#define REALBUTS    5		/* actual number of buttons */

struct {
	char  *name;				/* button name */
	INTBIG unique;				/* number of letters that make it unique */
} gra_buttonname[BUTTONS] =
{
	{"LEFT",1},   {"MIDDLE",2},   {"RIGHT",1},   {"FORWARD",1},   {"BACKWARD",1},	/* unshifted */
	{"SLEFT",2},  {"SMIDDLE",3},  {"SRIGHT",2},  {"SFORWARD",2},  {"SBACKWARD",2},	/* shift held down */
	{"CLEFT",2},  {"CMIDDLE",3},  {"CRIGHT",2},  {"CFORWARD",2},  {"CBACKWARD",2},	/* control held down */
	{"MLEFT",2},  {"MMIDDLE",3},  {"MRIGHT",2},  {"MFORWARD",2},  {"MBACKWARD",2},	/* meta held down */
	{"SCLEFT",3}, {"SCMIDDLE",4}, {"SCRIGHT",3}, {"SCFORWARD",3}, {"SCBACKWARD",3},	/* shift and control held down*/
	{"SMLEFT",3}, {"SMMIDDLE",4}, {"SMRIGHT",3}, {"SMFORWARD",3}, {"SMBACKWARD",3},	/* shift and meta held down */
	{"CMLEFT",3}, {"CMMIDDLE",4}, {"CMRIGHT",3}, {"CMFORWARD",3}, {"CMBACKWARD",3},	/* control and meta held down */
	{"SCMLEFT",4},{"SCMMIDDLE",4},{"SCMRIGHT",4},{"SCMFORWARD",4},{"SCMBACKWARD",4},/* shift, control, and meta */
	{"DLEFT",2},  {"DMIDDLE",2},  {"DRIGHT",2},  {"DFORWARD",2},  {"DBACKWARD",2}	/* double-click */
};

INTBIG     gra_makebutton(INTBIG);

/****** cursors and icons ******/
static Cursor        gra_realcursor;
static Cursor        gra_nomousecursor;
static Cursor        gra_drawcursor;
static Cursor        gra_nullcursor;
static Cursor        gra_menucursor;
static Cursor        gra_handcursor;
static Cursor        gra_techcursor;
static Cursor        gra_ibeamcursor;
static Cursor        gra_lrcursor;
static Cursor        gra_udcursor;
static XColor        gra_xfc, gra_xbc;			/* cursor color structure  */

/* the icon for Electric  */
#ifdef sun
#  define PROGICONSIZE 32
  static UINTSML gra_icon[64] =
  {
	0x0000, 0x0000, /*                                  */
	0x0000, 0x0000, /*                                  */
	0xFF80, 0x01FF, /*        XXXXXXXXXXXXXXXXXX        */
	0x0040, 0x0200, /*       X                  X       */
	0x0020, 0x0400, /*      X                    X      */
	0x0010, 0x0800, /*     X                      X     */
	0x0008, 0x1000, /*    X                        X    */
	0x0008, 0x1000, /*    X                        X    */
	0x0004, 0x2000, /*   X                          X   */
	0x0784, 0x21E0, /*   X    XXXX          XXXX    X   */
	0x0782, 0x41E0, /*  X     XXXX          XXXX     X  */
	0x0782, 0x41E0, /*  X     XXXX          XXXX     X  */
	0x0781, 0x81E0, /* X      XXXX          XXXX      X */
	0x0781, 0x81E0, /* X      XXXX          XXXX      X */
	0x0781, 0x81E0, /* X      XXXX          XXXX      X */
	0x0781, 0x81E0, /* X      XXXX          XXXX      X */
	0x0781, 0x81E0, /* X      XXXX          XXXX      X */
	0x0781, 0x81E0, /* X      XXXX          XXXX      X */
	0x0781, 0x8000, /* X      XXXX                    X */
	0x0001, 0x8000, /* X                              X */
	0x8002, 0x4001, /*  X             XX             X  */
	0xC002, 0x4003, /*  X            XXXX            X  */
	0xE004, 0x2007, /*   X          XXXXXX          X   */
	0xE004, 0x2007, /*   X          XXXXXX          X   */
	0xE008, 0x1007, /*    X         XXXXXX         X    */
	0xE008, 0x1007, /*    X         XXXXXX         X    */
	0x0010, 0x0800, /*     X                      X     */
	0x0020, 0x0400, /*      X                    X      */
	0x0040, 0x0200, /*       X                  X       */
	0xFF80, 0x01FF, /*        XXXXXXXXXXXXXXXXXX        */
	0x0000, 0x0000, /*                                  */
	0x0000, 0x0000  /*                                  */
  };
#else
#  define PROGICONSIZE 64
  static UINTSML gra_icon[256] =
  {
	0x0000, 0x0000, 0x0000, 0x0000, /*                                                                  */
	0x0000, 0x0000, 0x0000, 0x0000, /*                                                                  */
	0x0000, 0x0000, 0x0000, 0x0000, /*                                                                  */
	0x0000, 0x0000, 0x0000, 0x0000, /*                                                                  */
	0xF800, 0xFFFF, 0xFFFF, 0x001F, /*            XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX            */
	0x0400, 0x0000, 0x0000, 0x0020, /*           X                                          X           */
	0x0200, 0x0000, 0x0000, 0x0040, /*          X                                            X          */
	0x0100, 0x0000, 0x0000, 0x0080, /*         X                                              X         */
	0x0080, 0x0000, 0x0000, 0x0100, /*        X                                                X        */
	0x0080, 0x0000, 0x0000, 0x0100, /*        X                                                X        */
	0x0040, 0x0000, 0x0000, 0x0200, /*       X                                                  X       */
	0x0040, 0x0000, 0x0000, 0x0200, /*       X                                                  X       */
	0x0020, 0x0000, 0x0000, 0x0400, /*      X                                                    X      */
	0x0020, 0x0000, 0x0000, 0x0400, /*      X                                                    X      */
	0x8010, 0x001F, 0xFC00, 0x0800, /*     X          XXXXXX                     XXXXXX           X     */
	0x8010, 0x001F, 0xFC00, 0x0800, /*     X          XXXXXX                     XXXXXX           X     */
	0x8008, 0x001F, 0xFC00, 0x1000, /*    X           XXXXXX                     XXXXXX            X    */
	0x8008, 0x001F, 0xFC00, 0x1000, /*    X           XXXXXX                     XXXXXX            X    */
	0x8004, 0x001F, 0xFC00, 0x2000, /*   X            XXXXXX                     XXXXXX             X   */
	0x8004, 0x001F, 0xFC00, 0x2000, /*   X            XXXXXX                     XXXXXX             X   */
	0x8004, 0x001F, 0xFC00, 0x2000, /*   X            XXXXXX                     XXXXXX             X   */
	0x8002, 0x001F, 0xFC00, 0x4000, /*  X             XXXXXX                     XXXXXX              X  */
	0x8002, 0x001F, 0xFC00, 0x4000, /*  X             XXXXXX                     XXXXXX              X  */
	0x8002, 0x001F, 0xFC00, 0x4000, /*  X             XXXXXX                     XXXXXX              X  */
	0x8002, 0x001F, 0xFC00, 0x4000, /*  X             XXXXXX                     XXXXXX              X  */
	0x8002, 0x001F, 0xFC00, 0x4000, /*  X             XXXXXX                     XXXXXX              X  */
	0x8001, 0x001F, 0xFC00, 0x8000, /* X              XXXXXX                     XXXXXX               X */
	0x8001, 0x001F, 0xFC00, 0x8000, /* X              XXXXXX                     XXXXXX               X */
	0x8001, 0x001F, 0xFC00, 0x8000, /* X              XXXXXX                     XXXXXX               X */
	0x8001, 0x001F, 0xFC00, 0x8000, /* X              XXXXXX                     XXXXXX               X */
	0x8001, 0x001F, 0xFC00, 0x8000, /* X              XXXXXX                     XXXXXX               X */
	0x8001, 0x001F, 0xFC00, 0x8000, /* X              XXXXXX                     XXXXXX               X */
	0x0001, 0x0000, 0x0000, 0x8000, /* X                                                              X */
	0x0001, 0x0000, 0x0000, 0x8000, /* X                                                              X */
	0x0001, 0x0000, 0x0000, 0x8000, /* X                                                              X */
	0x0001, 0x0000, 0x0000, 0x8000, /* X                                                              X */
	0x0001, 0x0000, 0x0000, 0x8000, /* X                                                              X */
	0x0001, 0xF000, 0x0007, 0x8000, /* X                           XXXXXXX                            X */
	0x0002, 0xF800, 0x000F, 0x4000, /*  X                         XXXXXXXXX                          X  */
	0x0002, 0xFC00, 0x001F, 0x4000, /*  X                        XXXXXXXXXXX                         X  */
	0x0002, 0xFE00, 0x003F, 0x4000, /*  X                       XXXXXXXXXXXXX                        X  */
	0x0002, 0xFE00, 0x003F, 0x4000, /*  X                       XXXXXXXXXXXXX                        X  */
	0x0002, 0xFE00, 0x003F, 0x4000, /*  X                       XXXXXXXXXXXXX                        X  */
	0x0004, 0xFE00, 0x003F, 0x2000, /*   X                      XXXXXXXXXXXXX                       X   */
	0x0004, 0xFE00, 0x003F, 0x2000, /*   X                      XXXXXXXXXXXXX                       X   */
	0x0004, 0xFE00, 0x003F, 0x2000, /*   X                      XXXXXXXXXXXXX                       X   */
	0x0008, 0xFE00, 0x003F, 0x1000, /*    X                     XXXXXXXXXXXXX                      X    */
	0x0008, 0xFE00, 0x003F, 0x1000, /*    X                     XXXXXXXXXXXXX                      X    */
	0x0010, 0xFE00, 0x003F, 0x0800, /*     X                    XXXXXXXXXXXXX                     X     */
	0x0010, 0xFE00, 0x003F, 0x0800, /*     X                    XXXXXXXXXXXXX                     X     */
	0x0020, 0xFE00, 0x003F, 0x0400, /*      X                   XXXXXXXXXXXXX                    X      */
	0x0020, 0x0000, 0x0000, 0x0400, /*      X                                                    X      */
	0x0040, 0x0000, 0x0000, 0x0200, /*       X                                                  X       */
	0x0040, 0x0000, 0x0000, 0x0200, /*       X                                                  X       */
	0x0080, 0x0000, 0x0000, 0x0100, /*        X                                                X        */
	0x0080, 0x0000, 0x0000, 0x0100, /*        X                                                X        */
	0x0100, 0x0000, 0x0000, 0x0080, /*         X                                              X         */
	0x0200, 0x0000, 0x0000, 0x0040, /*          X                                            X          */
	0x0400, 0x0000, 0x0000, 0x0020, /*           X                                          X           */
	0xF800, 0xFFFF, 0xFFFF, 0x001F, /*            XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX            */
	0x0000, 0x0000, 0x0000, 0x0000, /*                                                                  */
	0x0000, 0x0000, 0x0000, 0x0000, /*                                                                  */
	0x0000, 0x0000, 0x0000, 0x0000, /*                                                                  */
	0x0000, 0x0000, 0x0000, 0x0000  /*                                                                  */
  };
#endif

/* the normal cursor (NORMALCURSOR, an "X") */
static UINTSML gra_realcursordata[16] = {
	0xC003, /* XX            XX */
	0xE007, /* XXX          XXX */
	0x700E, /*  XXX        XXX  */
	0x381C, /*   XXX      XXX   */
	0x1C38, /*    XXX    XXX    */
	0x0E70, /*     XXX  XXX     */
	0x07E0, /*      XXXXXX      */
	0x0240, /*       X  X       */
	0x0240, /*       X  X       */
	0x07E0, /*      XXXXXX      */
	0x0E70, /*     XXX  XXX     */
	0x1C38, /*    XXX    XXX    */
	0x381C, /*   XXX      XXX   */
	0x700E, /*  XXX        XXX  */
	0xE007, /* XXX          XXX */
	0xC003  /* XX            XX */
};

/* the "use the TTY" cursor (WANTTTYCURSOR, the word "tty" above keyboard) */
static UINTSML gra_nomousecursordata[16] = {
	0x7757, /* XXX X X XXX XXX  */
	0x1552, /*  X  X X X X X    */
	0x3772, /*  X  XXX XXX XX   */
	0x1122, /*  X   X  X   X    */
	0x7122, /*  X   X  X   XXX  */
	0x0000, /*                  */
	0xFFFF, /* XXXXXXXXXXXXXXXX */
	0x8001, /* X              X */
	0x8AA9, /* X  X X X X X   X */
	0x9551, /* X   X X X X X  X */
	0x8AA9, /* X  X X X X X   X */
	0x9551, /* X   X X X X X  X */
	0x8001, /* X              X */
	0x9FF9, /* X  XXXXXXXXXX  X */
	0x8001, /* X              X */
	0xFFFF  /* XXXXXXXXXXXXXXXX */
};

/* the "draw with pen" cursor (PENCURSOR, a pen) */
static UINTSML gra_drawcursordata[16] = {
	0x3000, /*             XX   */
	0x7800, /*            XXXX  */
	0xF400, /*           X XXXX */
	0xE200, /*          X   XXX */
	0x4100, /*         X     X  */
	0x2080, /*        X     X   */
	0x1040, /*       X     X    */
	0x0820, /*      X     X     */
	0x0410, /*     X     X      */
	0x0208, /*    X     X       */
	0x010C, /*   XX    X        */
	0x008C, /*   XX   X         */
	0x007E, /*  XXXXXX          */
	0x003E, /*  XXXXX           */
	0x000F, /* XXXX             */
	0x0003  /* XX               */
};

/* the null cursor (NULLCURSOR, an egg timer) */
static UINTSML gra_nullcursordata[16] = {
	0x1FF8, /*    XXXXXXXXXX    */
	0x2004, /*   X          X   */
	0x2004, /*   X          X   */
	0x2004, /*   X          X   */
	0x1AA8, /*    X X X X XX    */
	0x0FF0, /*     XXXXXXXX     */
	0x07E0, /*      XXXXXX      */
	0x03C0, /*       XXXX       */
	0x0240, /*       X  X       */
	0x0420, /*      X    X      */
	0x0810, /*     X      X     */
	0x1008, /*    X        X    */
	0x2AA4, /*   X  X X X X X   */
	0x3FFC, /*   XXXXXXXXXXXX   */
	0x3FFC, /*   XXXXXXXXXXXX   */
	0x1FF8  /*    XXXXXXXXXX    */
};

/* the menu selection cursor (MENUCURSOR, a sideways arrow) */
static UINTSML gra_menucursordata[16] = {
	0x0000, /*                  */
	0x0000, /*                  */
	0x0400, /*           X      */
	0x0C00, /*           XX     */
	0x1C00, /*           XXX    */
	0x3C00, /*           XXXX   */
	0x7FFF, /* XXXXXXXXXXXXXXX  */
	0xFFFF, /* XXXXXXXXXXXXXXXX */
	0x7FFF, /* XXXXXXXXXXXXXXX  */
	0x3C00, /*           XXXX   */
	0x1C00, /*           XXX    */
	0x0C00, /*           XX     */
	0x0400, /*           X      */
	0x0000, /*                  */
	0x0000, /*                  */
	0x0000  /*                  */
};

/* the "move" command dragging cursor (HANDCURSOR, a dragging hand) */
static UINTSML gra_handcursordata[16] = {
	0x0000, /*                  */
	0x3F80, /*        XXXXXXX   */
	0xC040, /*       X       XX */
	0x0380, /*        XXX       */
	0x0040, /*       X          */
	0x0020, /*      X           */
	0x01C0, /*       XXX        */
	0x0020, /*      X           */
	0x0010, /*     X            */
	0x03FE, /*  XXXXXXXXX       */
	0x0001, /* X                */
	0x0001, /* X                */
	0x1FFE, /*  XXXXXXXXXXXX    */
	0x8080, /*        X       X */
	0x4100, /*         X     X  */
	0x3E00  /*          XXXXX   */
};

/* the "technology" command dragging cursor (TECHCURSOR, a "T") */
static UINTSML gra_techcursordata[16] = {
	0x0100, /*         X        */
	0x0380, /*        XXX       */
	0x0540, /*       X X X      */
	0x0100, /*         X        */
	0x0100, /*         X        */
	0x3FF8, /*    XXXXXXXXXXX   */
	0x3FF8, /*    XXXXXXXXXXX   */
	0x2388, /*    X   XXX   X   */
	0x0380, /*        XXX       */
	0x0380, /*        XXX       */
	0x0380, /*        XXX       */
	0x0380, /*        XXX       */
	0x0380, /*        XXX       */
	0x0380, /*        XXX       */
	0x07C0, /*       XXXXX      */
	0x0000  /*                  */
};

/* the "ibeam" text selection cursor (IBEAMCURSOR, an "I") */
static UINTSML gra_ibeamcursordata[16] = {
	0x0360, /*      XX XX       */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0080, /*        X         */
	0x0360  /*      XX XX       */
};

/* the "left/right arrow" cursor (LRCURSOR) */
static UINTSML gra_lrcursordata[16] = {
	0x0000, /*                  */
	0x0000, /*                  */
	0x0000, /*                  */
	0x1008, /*    X        X    */
	0x300C, /*   XX        XX   */
	0x700E, /*  XXX        XXX  */
	0xFFFF, /* XXXXXXXXXXXXXXXX */
	0xFFFF, /* XXXXXXXXXXXXXXXX */
	0x700E, /*  XXX        XXX  */
	0x300C, /*   XX        XX   */
	0x1008, /*    X        X    */
	0x0000, /*                  */
	0x0000, /*                  */
	0x0000, /*                  */
	0x0000, /*                  */
	0x0000  /*                  */
};

/* the "up/down arrow" cursor (UDCURSOR) */
static UINTSML gra_udcursordata[16] = {
	0x0180, /*        XX        */
	0x03C0, /*       XXXX       */
	0x07E0, /*      XXXXXX      */
	0x0FF0, /*     XXXXXXXX     */
	0x0180, /*        XX        */
	0x0180, /*        XX        */
	0x0180, /*        XX        */
	0x0180, /*        XX        */
	0x0180, /*        XX        */
	0x0180, /*        XX        */
	0x0180, /*        XX        */
	0x0180, /*        XX        */
	0x0FF0, /*     XXXXXXXX     */
	0x07E0, /*      XXXXXX      */
	0x03C0, /*       XXXX       */
	0x0180  /*        XX        */
};

void       gra_recolorcursor(Display *dpy, XColor *fg, XColor *bg);

/****** fonts ******/

/*
 * Default font - this font MUST exist, or Electric will die.  Best to choose a
 * font that is commonly used, so the X server is likely to have it loaded.
 */
#define DEFAULTFONTNAME      "fixed"
#define FONTLISTSIZE           10
#define FONTHASHSIZE          211		/* must be prime */
#define TRUETYPEITALICSLANT   0.3		/* slant to make text italic */
#define TRUETYPEBOLDEXTEND    1.5		/* extend factor to make text bold */

typedef struct
{
	XFontStruct *font;
	char        *fontname;
	char        *altfontname;
#ifdef ANYDEPTH
	INTSML       width[128];
	INTSML       height[128];
	char        *data[128];
#endif
} FontRec;

typedef struct
{
	INTBIG   ascent, descent;
	INTBIG   face;
	INTBIG   italic;
	INTBIG   bold;
	INTBIG   size;
	INTBIG   spacewidth;
} FONTHASH;

       FONTHASH      gra_fonthash[FONTHASHSIZE];
static INTBIG        gra_textrotation = 0;

#ifdef ANYDEPTH
#  define FONTINIT , {0}, {0}, {0}
  static char       *gra_textbitsdata;				/* buffer for converting text to bits */
  static INTBIG      gra_textbitsdatasize = 0;		/* size of "gra_textbitsdata" */
  static char      **gra_textbitsrowstart;			/* pointers to "gra_textbitsdata" */
  static INTBIG      gra_textbitsrowstartsize = 0;	/* size of "gra_textbitsrowstart" */
#else
#  define FONTINIT
#endif

static FontRec       gra_messages_font = {(XFontStruct *)0, "fixed", "fixed" FONTINIT};
static FontRec       gra_defaultFont;
static XFontStruct  *gra_curfont;					/* current writing font */
static INTBIG        gra_curfontnumber;
static INTBIG        gra_truetypesize;				/* true size of current font */
#ifdef TRUETYPE
  static INTBIG      gra_truetypeon;				/* nonzero if TrueType initialized */
  static INTBIG      gra_truetypeitalic;			/* true italic factor of current font */
  static INTBIG      gra_truetypebold;				/* true bold factor of current font */
  static INTBIG      gra_truetypeunderline;			/* true underline factor of current font */
  static INTBIG      gra_truetypedescent;			/* true descent value of current font */
  static INTBIG      gra_truetypeascent;			/* true ascent value of current font */
  static INTBIG      gra_spacewidth;				/* true width of space for current font */
  static INTBIG      gra_truetypedeffont;			/* default TrueType font */
  static INTBIG      gra_truetypefont;				/* current TrueType font */
  static INTBIG     *gra_descentcache;				/* cache of maximum descent for font size */
  static INTBIG     *gra_ascentcache;				/* cache of maximum ascent for font size */
  static INTBIG     *gra_swidthcache;				/* cache of space width for font size */
  static INTBIG      gra_descentcachesize = 0;		/* size of ascent/descent cache */
  static INTBIG      gra_numfaces = 0;
  static char      **gra_facelist;
#endif

static char *gra_resource_fontname[] =
{
	"font0", "font1", "font2", "font3", "font4", "font5",
	"font6", "font7", "font8", "fontmenu", "fontedit", "fontstatus"
};

static FontRec gra_font[] =
{
	{(XFontStruct *)0,
	 "-*-helvetica-medium-r-normal-*-8-*-*-*-*",				/* 4 points */
	 "-*-helvetica-medium-r-normal-*-8-*-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-helvetica-medium-r-normal-*-10-*-*-*-*",				/* 6 points */
	 "-*-helvetica-medium-r-normal-*-10-*-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-helvetica-medium-r-normal-*-12-*-*-*-*",				/* 8 points */
	 "-*-helvetica-medium-r-normal-*-12-*-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-helvetica-medium-r-normal-*-14-*-*-*-*",				/* 10 points */
	 "-*-helvetica-medium-r-normal-*-14-*-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-helvetica-medium-r-normal-*-17-*-*-*-*",				/* 12 points */
	 "-*-fixed-bold-r-normal-*-16-*-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-helvetica-medium-r-normal-*-18-*-*-*-*",				/* 14 points */
	 "-*-helvetica-medium-r-normal-*-18-*-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-helvetica-bold-r-normal-*-20-*-*-*-*",					/* 16 points */
	 "-*-fixed-medium-r-normal-*-20-*-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-helvetica-medium-r-normal-*-24-*-*-*-*",				/* 18 points */
	 "-*-helvetica-medium-r-normal-*-24-*-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-helvetica-bold-r-normal-*-25-*-*-*-*",					/* 20 points */
	 "-*-*-medium-r-normal-*-25-*-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-helvetica-bold-r-normal-*-*-120-*-*-*",				/* MENU */
	 "-*-helvetica-bold-r-normal-*-*-120-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "-*-fixed-*-*-normal-*-*-120-*-*-*",						/* EDIT */
	 "-*-fixed-*-*-normal-*-*-120-*-*-*" FONTINIT},
	{(XFontStruct *)0,
	 "fixed",													/* STATUS */
	 "fixed" FONTINIT},
	{(XFontStruct *)0, NULL, NULL FONTINIT}  					/* terminator */
};

void     gra_makefontchoices(char **fontlist, INTBIG namecount, char *required[14],
			char ****fchoices, INTBIG **fnumchoices);
int      gra_sortnumbereascending(const void *e1, const void *e2);
void     gra_settextsize(WINDOWPART *win, INTBIG fnt, INTBIG face, INTBIG italic,
			INTBIG bold, INTBIG underline);
#ifdef TRUETYPE
  INTBIG gra_getT1stringsize(char *str, INTBIG *wid, INTBIG *hei);
#endif

#ifndef ANYDEPTH
/****** rectangle saving ******/
#define NOSAVEDBOX ((SAVEDBOX *)-1)
typedef struct Isavedbox
{
	Pixmap      pix;
	WINDOWPART *win;
	INTBIG      code;
	INTBIG      lx, hx, ly, hy;
	struct Isavedbox *nextsavedbox;
} SAVEDBOX;

static SAVEDBOX     *gra_firstsavedbox = NOSAVEDBOX;
static INTBIG        gra_savedboxcodes = 0;
#endif

/****** time ******/
static Time          gra_lasttime = 0;			/* time of last click */
static time_t        gra_timebasesec;
#ifdef HAVE_FTIME
  static INTBIG      gra_timebasems;
#endif

/****** files ******/
static void         *gra_fileliststringarray = 0;
static char          gra_curpath[255] = {0};
static char          gra_requiredextension[20];
static char         *gra_initialdirectory;
static char         *gra_logfile, *gra_logfilesave;
static char        **gra_printerlist;
static INTBIG        gra_printerlistcount = 0;
static INTBIG        gra_printerlisttotal = 0;

#ifdef USETIF
  static BOOLEAN     gra_fileselectdone;
#endif

int        gra_fileselectall(const struct dirent *a);
BOOLEAN    gra_addfiletolist(char *file);
void       gra_initfilelist(void);
void       gra_setcurrentdirectory(char *path);
BOOLEAN    gra_addprinter(char *buf);
#ifdef USETIF
  void     gra_fileselectcancel(Widget w, XtPointer client_data,
			XmSelectionBoxCallbackStruct *call_data);
  void     gra_fileselectok(Widget w, XtPointer client_data,
			XmSelectionBoxCallbackStruct *call_data);
#endif

/******************** INITIALIZATION ********************/

int main(int argc, char *argv[])
{
	/* catch signals if Electric trys to bomb out */
	(void)signal(SIGILL, (SIGNALCAST)gra_sigill_trap);
	(void)signal(SIGFPE, (SIGNALCAST)gra_sigfpe_trap);
	(void)signal(SIGBUS, (SIGNALCAST)gra_sigbus_trap);
	(void)signal(SIGSEGV, (SIGNALCAST)gra_sigsegv_trap);

	/* primary initialization of Electric */
	osprimaryosinit();

#if LANGTCL
	/* initialize TCL here */
	if (gra_initializetcl() == TCL_ERROR)
		error(_("Failed to initialize TCL: %s\n"), gra_tclinterp->result);

#  ifdef USETK
	/* initialize TK here */
	gra_initializetk();
#  endif
#endif

	/* secondary initialization of Electric */
	ossecondaryinit(argc, argv);

	/* add a timer that ticks TICKSPERSECOND times per second */
	XtAppAddTimeOut(gra_xtapp, 1000/TICKSPERSECOND, gra_timerticked, 0);

	/* now run Electric */
	(void)XtAppAddWorkProc(gra_xtapp, (XtWorkProc)gra_dowork, 0);
	XtAppMainLoop(gra_xtapp);
	return(0);
}

/*
 * Routine to establish the default display define tty codes.
 */
void graphicsoptions(char *name, INTBIG *argc, char **argv)
{
	INTBIG ttydevice;

	/* save command arguments */
	gra_argc = *argc;
	gra_argv = argv;

	/* initialize keyboard input */
	ttydevice = fileno(stdin);
#ifdef HAVE_TERMIOS_H
	{
		struct termios sttybuf;
		tcgetattr(ttydevice, &sttybuf);
		us_erasech = sttybuf.c_cc[VERASE];
		us_killch = sttybuf.c_cc[VKILL];
	}
#else
#  ifdef HAVE_TERMIO_H
	{
		struct termio sttybuf;
		(void)ioctl(ttydevice, TCGETA, &sttybuf);
		us_erasech = sttybuf.c_cc[VERASE];
		us_killch = sttybuf.c_cc[VKILL];
	}
#  else
	{
		struct sgttyb sttybuf;
		(void)gtty(ttydevice, &sttybuf);
		us_erasech = sttybuf.sg_erase;
		us_killch = sttybuf.sg_kill;
	}
#  endif
#endif
}

/*
 * Routine to initialize the display device.
 */
BOOLEAN initgraphics(BOOLEAN messages)
{
	REGISTER INTBIG i, j;
	INTBIG bitmask, depth;
	BOOLEAN wantmultiscreen;
	int namecount;
	char *ptr, **fontlist;
	char *geomSpec;
	Pixmap pixm;
	Visual *visual;
#ifdef USETIF
	REGISTER INTBIG ac;
	Arg arg[10];
	Widget dpywidget;
#endif
#ifdef ANYDEPTH
	GC gc;
	XGCValues gcv;
	XImage *image;
	XFontStruct *thefont;
	XCharStruct xcs;
	int direction, asc, des;
	char *dptr;
	REGISTER INTBIG amt, k;
	INTBIG width, height, x, y;
	Pixmap textmap;
#else
	XVisualInfo vinfo;
#endif

	/* remember the initial directory and the log file locations */
	(void)allocstring(&gra_initialdirectory, currentdirectory(), db_cluster);
	(void)initinfstr();
	(void)addstringtoinfstr(gra_initialdirectory);
	(void)addstringtoinfstr(".electric.log");
	(void)allocstring(&gra_logfile, returninfstr(), db_cluster);
	(void)initinfstr();
	(void)addstringtoinfstr(gra_initialdirectory);
	(void)addstringtoinfstr(".electriclast.log");
	(void)allocstring(&gra_logfilesave, returninfstr(), db_cluster);

	/* initialize X and the toolkit */
	(void)XSetErrorHandler(gra_xerrors);
	XtToolkitInitialize();
	gra_xtapp = XtCreateApplicationContext();
	gra_intsignalid = XtAppAddSignal(gra_xtapp, (XtSignalCallbackProc)gra_intsignalfunc, 0);
	gra_checkinginterrupt = FALSE;

	/* on machines with reverse byte ordering, fix the icon and the cursors */
#ifdef BYTES_SWAPPED
	j = PROGICONSIZE * PROGICONSIZE / 16;
	for(i=0; i<j; i++)
		gra_icon[i] = ((gra_icon[i] >> 8) & 0xFF) | ((gra_icon[i] << 8) & 0xFF00);
	for(i=0; i<16; i++)
	{
		gra_realcursordata[i] = ((gra_realcursordata[i] >> 8) & 0xFF) |
			((gra_realcursordata[i] << 8) & 0xFF00);
		gra_nomousecursordata[i] = ((gra_nomousecursordata[i] >> 8) & 0xFF) |
			((gra_nomousecursordata[i] << 8) & 0xFF00);
		gra_drawcursordata[i] = ((gra_drawcursordata[i] >> 8) & 0xFF) |
			((gra_drawcursordata[i] << 8) & 0xFF00);
		gra_nullcursordata[i] = ((gra_nullcursordata[i] >> 8) & 0xFF) |
			((gra_nullcursordata[i] << 8) & 0xFF00);
		gra_menucursordata[i] = ((gra_menucursordata[i] >> 8) & 0xFF) |
			((gra_menucursordata[i] << 8) & 0xFF00);
		gra_handcursordata[i] = ((gra_handcursordata[i] >> 8) & 0xFF) |
			((gra_handcursordata[i] << 8) & 0xFF00);
		gra_techcursordata[i] = ((gra_techcursordata[i] >> 8) & 0xFF) |
			((gra_techcursordata[i] << 8) & 0xFF00);
		gra_ibeamcursordata[i] = ((gra_ibeamcursordata[i] >> 8) & 0xFF) |
			((gra_ibeamcursordata[i] << 8) & 0xFF00);
		gra_lrcursordata[i] = ((gra_lrcursordata[i] >> 8) & 0xFF) |
			((gra_lrcursordata[i] << 8) & 0xFF00);
		gra_udcursordata[i] = ((gra_udcursordata[i] >> 8) & 0xFF) |
			((gra_udcursordata[i] << 8) & 0xFF00);
	}
#endif

	/* get switch settings */
	wantmultiscreen = FALSE;
	geomSpec = NULL;
	for(i=1; i < gra_argc; i++)
	{
		if (gra_argv[i][0] == '-')
		{
			switch (gra_argv[i][1])
			{
				case 'm':
					wantmultiscreen = TRUE;
					continue;

				case 'g':       /* Geometry */
					if (++i >= gra_argc) continue;
					geomSpec = gra_argv[i];
					continue;
			}
		}
	}

	/* find the displays for the edit and messages windows */
	gra_finddisplays(wantmultiscreen);

	/* double-click threshold (in milliseconds) */
	gra_doublethresh = XtGetMultiClickTime(gra_maindpy);

	/* get the information about the edit display */
	depth = DefaultDepth(gra_maindpy, gra_mainscreen);
	if (depth == 1)
		fprintf(stderr, _("Cannot run on 1-bit deep displays\n"));
	el_colcursor = CURSOR;	/* also done in usr.c later */
#ifdef ANYDEPTH
	el_maplength = 256;
#else
	el_maplength = DisplayCells(gra_maindpy, gra_mainscreen);
	gra_maphigh = el_maplength - 1;
#endif
	resetpaletteparameters();

	/* Determine initial position and size of edit window */
	gra_editxsh = XAllocSizeHints();
	if (geomSpec == NULL) geomSpec = XGetDefault(gra_maindpy, "Electric", "geometry");
	if (geomSpec != NULL)
	{
		/* use defaults from database */
		bitmask = XWMGeometry(gra_maindpy, gra_mainscreen, geomSpec, NULL, 1, gra_editxsh,
			&gra_editxsh->x, &gra_editxsh->y, &gra_editxsh->width, &gra_editxsh->height,
			&gra_editxsh->win_gravity);
		if (bitmask & (XValue | YValue)) gra_editxsh->flags |= USPosition;
		if (bitmask & (WidthValue | HeightValue)) gra_editxsh->flags |= USSize;
	} else
	{
		/* nothing in defaults database */
		gra_editxsh->flags = PPosition | PSize;
		gra_editxsh->height = DisplayHeight(gra_maindpy, gra_mainscreen) - 2;
		gra_editxsh->width = DisplayWidth(gra_maindpy, gra_mainscreen) - PALETTEWIDTH - 8;
		if (gra_maindpy == gra_altdpy)
		{
			/* all on one screen */
			gra_editxsh->height = gra_editxsh->height / 4 * 3;
		} else
		{
			/* two screens */
			gra_editxsh->height = DisplayHeight(gra_maindpy, gra_mainscreen) - 24;
		}
		gra_editxsh->width = gra_editxsh->width / 5 * 4;
		gra_editxsh->height = gra_editxsh->height / 5 * 4;
		gra_editxsh->x = PALETTEWIDTH;
		gra_editxsh->y = 0;
	}
	gra_editposx = gra_editxsh->x;
	gra_editposy = gra_editxsh->y;
	gra_editsizex = gra_editxsh->width;
	gra_editsizey = gra_editxsh->height;
	gra_windownumber = 0;

#ifdef USETIF
	/* disable drag-and-drop (because it crashes for some unknown reason) */
	dpywidget = XmGetXmDisplay(gra_maindpy);
	ac = 0;
	XtSetArg(arg[ac], XmNdragInitiatorProtocolStyle, XmDRAG_NONE);   ac++;
	XtSetArg(arg[ac], XmNdragReceiverProtocolStyle, XmDRAG_NONE);   ac++;
	XtSetValues(dpywidget, arg, ac);
	if (gra_altdpy != gra_maindpy)
	{
		dpywidget = XmGetXmDisplay(gra_altdpy);
		ac = 0;
		XtSetArg(arg[ac], XmNdragInitiatorProtocolStyle, XmDRAG_NONE);   ac++;
		XtSetArg(arg[ac], XmNdragReceiverProtocolStyle, XmDRAG_NONE);   ac++;
		XtSetValues(dpywidget, arg, ac);
	}

	/* get atoms for disabling window kills, switching displays */
	gra_wm_delete_window = XmInternAtom(gra_maindpy, "WM_DELETE_WINDOW", False);
	gra_movedisplayprotocol = XmInternAtom(gra_maindpy, "_MOVE_DISPLAYS", False);
	gra_movedisplaymsg = XmInternAtom(gra_maindpy, "_MOTIF_WM_MESSAGES", False);
#else
	/* get atoms for disabling window kills, switching displays */
	gra_wm_delete_window = XInternAtom(gra_maindpy, "WM_DELETE_WINDOW", False);
	gra_wm_protocols = XInternAtom(gra_maindpy, "WM_PROTOCOLS", False);
#endif

#ifdef ANYDEPTH
	visual = DefaultVisual(gra_maindpy, gra_mainscreen);
	if (VisualClass(visual) == PseudoColor || VisualClass(visual) == StaticColor)
		gra_maincolmap = DefaultColormap(gra_maindpy, gra_mainscreen);
	if (gra_maindpy != gra_altdpy)
	{
		visual = DefaultVisual(gra_altdpy, gra_altscreen);
		if (VisualClass(visual) == PseudoColor || VisualClass(visual) == StaticColor)
			gra_altcolmap = DefaultColormap(gra_altdpy, gra_altscreen);
	}
#else
	/* Some servers change the default screen to StaticColor class, so that
	 * Electric cannot change the color map.  Try to find one that can be used.
	 */
	if (depth > 1)
	{
		visual = DefaultVisual(gra_maindpy, gra_mainscreen);
		if (VisualClass(visual) != PseudoColor)
		{
			/* look for pseudo color only for now */
			if (XMatchVisualInfo(gra_maindpy, gra_mainscreen, depth, PseudoColor, &vinfo))
			{
				visual = vinfo.visual;
			} else
			{
				fprintf(stderr, _("Cannot find 8-bit PseudoColor visual, try building with 'ANYDEPTH'\n"));
			}
		}
	}
#endif

	/* get fonts */
#ifdef TRUETYPE
	for(i=0; i<FONTHASHSIZE; i++) gra_fonthash[i].descent = 1;
	if (T1_SetBitmapPad(16) == 0 && T1_InitLib(NO_LOGFILE) != 0)
	{
		gra_truetypeon = 1;

		/* Preload T1 fonts here, because T1_LoadFont spoils memory */
		for(j=0; j<T1_Get_no_fonts(); j++) T1_LoadFont(j);

		gra_truetypedeffont = 0;		/* presume that the first font is good to use */
		ptr = getenv("ELECTRIC_TRUETYPE_FONT");
		if (ptr != NULL)
		{
			j = T1_Get_no_fonts();
			for(gra_truetypedeffont=0; gra_truetypedeffont<j; gra_truetypedeffont++)
			{
				T1_LoadFont(gra_truetypedeffont);
				if (namesame(ptr, T1_GetFontName(gra_truetypedeffont)) == 0) break;
			}
			if (gra_truetypedeffont >= j)
			{
				printf(_("Warning: environment variable 'ELECTRIC_TRUETYPE_FONT' is '%s' which is not a truetype font\n"),
					ptr);
				printf(_("Choices are:\n"));
				for(gra_truetypedeffont=0; gra_truetypedeffont<j; gra_truetypedeffont++)
				{
					T1_LoadFont(gra_truetypedeffont);
					printf("  %s\n", T1_GetFontName(gra_truetypedeffont));
				}
				gra_truetypedeffont = 0;
			}
		}
	} else
	{
		gra_truetypeon = 0;
		fprintf(stderr, _("Cannot load TrueType, is T1LIB_CONFIG set?\n"));
	}
#endif
	gra_defaultFont.fontname = XGetDefault(gra_maindpy, "Electric", "font");
	if (gra_defaultFont.fontname == NULL) gra_defaultFont.fontname = DEFAULTFONTNAME;
	gra_defaultFont.font = XLoadQueryFont(gra_maindpy, gra_defaultFont.fontname);
	if (gra_defaultFont.font == NULL)
		fprintf(stderr, _("Cannot open default font \"%s\"\n"), gra_defaultFont.fontname);
	for(i=0; gra_font[i].fontname != 0; i++)
	{
		/*
		 * Check the .Xdefaults or .Xresources file for a value for each of the
		 * requested fonts; they will have resource names .Font4, .Font6,...
		 * .FontStatus. Look for a matching font, if found, use it; otherwise look
		 * for the font that is hard-coded.
		 */
		ptr = XGetDefault(gra_maindpy, "Electric", gra_resource_fontname[i]);
		if (ptr != NULL)
		{
			fontlist = XListFonts(gra_maindpy, ptr, 10, &namecount);
			if (namecount == 0)
			{
				/* server could not find a match */
				fprintf(stderr, _("Cannot find font '%s', using '%s'\n"),
					ptr, gra_font[i].fontname);
			} else
			{
				/* replace the default (this is for debugging only) */
				(void)allocstring(&gra_font[i].fontname, fontlist[0], db_cluster);
			}
		} else
		{
			fontlist = XListFonts(gra_maindpy, gra_font[i].fontname, 1, &namecount);
			if (namecount == 0)
			{
				fontlist = XListFonts(gra_maindpy, gra_font[i].altfontname, 1, &namecount);
			}
		}

		if (namecount != 0)
		{
			/* at least one version should have worked */
			gra_font[i].font = XLoadQueryFont(gra_maindpy, fontlist[0]);
			XFreeFontNames(fontlist);
		}

		if (gra_font[i].font == NULL)
		{
			fprintf(stderr, _("Cannot find font '%s', using default\n"),
				gra_font[i].fontname);
			fprintf(stderr, _("  To avoid this message, add the following line to your .Xdefaults file:\n"));
			fprintf(stderr, M_("    Electric.%s: CORRECT-FONT-NAME\n"), gra_resource_fontname[i]);
			gra_font[i].font = gra_defaultFont.font;
		}
	}
	if (gra_font[11].font != NULL)
	{
		gra_messages_font.font = gra_font[11].font;
		gra_messages_font.fontname = gra_font[11].fontname;
	}

	/* make the messages window */
	gra_makemessageswindow();

	/* create the cursors */
	gra_xfc.flags = gra_xbc.flags = DoRed | DoGreen | DoBlue;
	XQueryColor(gra_topmsgdpy, DefaultColormap(gra_topmsgdpy, DefaultScreen(gra_topmsgdpy)),
		&gra_xfc);
	XQueryColor(gra_topmsgdpy, DefaultColormap(gra_topmsgdpy, DefaultScreen(gra_topmsgdpy)),
		&gra_xbc);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_realcursordata, 16, 16);
	gra_realcursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc, &gra_xbc, 8, 8);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_nomousecursordata, 16, 16);
	gra_nomousecursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc, &gra_xbc, 8, 8);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_drawcursordata, 16, 16);
	gra_drawcursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc,  &gra_xbc, 0, 16);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_nullcursordata, 16, 16);
	gra_nullcursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc, &gra_xbc, 8, 8);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_menucursordata, 16, 16);
	gra_menucursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc, &gra_xbc, 16, 8);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_handcursordata, 16, 16);
	gra_handcursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc, &gra_xbc, 0, 10);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_techcursordata, 16, 16);
	gra_techcursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc, &gra_xbc, 8, 0);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_ibeamcursordata, 16, 16);
	gra_ibeamcursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc, &gra_xbc, 8, 8);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_lrcursordata, 16, 16);
	gra_lrcursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc, &gra_xbc, 8, 8);
	pixm = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin, (const char *)gra_udcursordata, 16, 16);
	gra_udcursor = XCreatePixmapCursor(gra_topmsgdpy, pixm, pixm, &gra_xfc, &gra_xbc, 8, 8);

#ifdef ANYDEPTH
	/* precache font bits */
	depth = DefaultDepth(gra_topmsgdpy, DefaultScreen(gra_topmsgdpy));
	textmap = XCreatePixmap(gra_topmsgdpy, gra_topmsgwin, 100, 100, depth);
	if (textmap == 0)
	{
		fprintf(stderr, _("Error allocating initial text pixmap\n"));
		exitprogram();
	}
	gcv.foreground = BlackPixel(gra_topmsgdpy, DefaultScreen(gra_topmsgdpy));
	gc = XtGetGC(gra_messageswidget, GCForeground, &gcv);
	for(i=0; gra_font[i].fontname != 0; i++)
	{
		thefont = gra_font[i].font;
		XSetFont(gra_topmsgdpy, gc, thefont->fid);
		for(j=0; j<128; j++)
		{
			gra_localstring[0] = (j < ' ' ? ' ' : (char)j);
			gra_localstring[1] = 0;
			XTextExtents(thefont, gra_localstring, 1, &direction, &asc, &des, &xcs);
			width = xcs.width;
			height = thefont->ascent + thefont->descent;
			gra_font[i].width[j] = width;
			gra_font[i].height[j] = height;
			amt = width * height;
			if (amt == 0) { gra_font[i].data[j] = 0;   continue; }
			gra_font[i].data[j] = (char *)emalloc(amt, us_tool->cluster);
			if (gra_font[i].data[j] == 0)
			{
				fprintf(stderr, _("Error allocating %ld bytes for font data\n"), amt);
				exitprogram();
			}
			for(k=0; k<amt; k++) gra_font[i].data[j][k] = 0;
			XSetForeground(gra_topmsgdpy, gc, 0);
			XFillRectangle(gra_topmsgdpy, textmap, gc, 0, 0, 100, 100);
			XSetForeground(gra_topmsgdpy, gc, 0xFFFFFF);
			XDrawString(gra_topmsgdpy, textmap, gc, 0,
				height-thefont->descent, gra_localstring, 1);
			image = XGetImage(gra_topmsgdpy, textmap, 0, 0, width, height,
				AllPlanes, ZPixmap);
			if (image == 0)
			{
				fprintf(stderr, _("Error allocating %ldx%ld image for fonts\n"),
					width, height);
				exitprogram();
			}
			dptr = gra_font[i].data[j];
			for(y=0; y<height; y++)
			{
				for(x=0; x<width; x++)
					*dptr++ = (XGetPixel(image, x, y) == 0) ? 0 : 1;
			}
			XDestroyImage(image);
		}
	}
	XtReleaseGC(gra_messageswidget, gc);
#endif

	/* allocate space for the full-depth buffer on the main display */
	if (gra_makewindowdisplaybuffer(&gra_mainwd, gra_maindpy)) exitprogram();

	/* allocate space for the full-depth buffer on the alternate display */
	if (gra_maindpy != gra_altdpy)
	{
		if (gra_makewindowdisplaybuffer(&gra_altwd, gra_altdpy)) exitprogram();
	}

#ifndef ANYDEPTH
	/*
	 * See if there are enought X-Server resources for backing store.
	 * To request this, declare the X resource "Electric.retained:True"
	 * in the .Xdefaults or equivalent file before starting.
	 */
	gra_use_backing = FALSE;
	ptr = XGetDefault(gra_maindpy, "Electric", "retained");
	if (ptr != NULL)
	{
		if (namesamen(ptr, "True", 4) == 0)
		{
			if (DoesBackingStore(ScreenOfDisplay(gra_maindpy, gra_mainscreen)))
				gra_use_backing = TRUE; else
			{
				fprintf(stderr, _("Backing Store is not available.\n"));
				fprintf(stderr, _("  To avoid this message, remove this line from your .Xdefaults file:\n"));
				fprintf(stderr, M_("    Electric.retained:True\n"));
			}
		}
	}
#endif

	/* initialize the window frames */
	el_firstwindowframe = el_curwindowframe = NOWINDOWFRAME;

	/* initialize the mouse */
	us_cursorstate = -1;
	us_normalcursor = NORMALCURSOR;
	setdefaultcursortype(us_normalcursor);
	gra_inputstate = NOEVENT;
	gra_eventqueuehead = gra_eventqueuetail = gra_eventqueue;

	return(FALSE);
}

/*
 * Routine to establish the displays for edit and messages and fill the globals:
 *   gra_maindpy
 *   gra_altdpy
 */
void gra_finddisplays(BOOLEAN wantmultiscreen)
{
	char displayname[200], maindisplayname[200], altdisplayname[200];
	INTBIG numfiles, maxdispnum, i, j;
	REGISTER INTBIG pixels, bestpixels, secondbestpixels, scr, graphicsheight, graphicswidth, dep;
	char **filelist, *eprogramname;
	int argc;
	Display *dpy;
	char *scrnumpos;

	/* set default displays for edit and messages windows */
	strcpy(displayname, XDisplayName(NULL));
	scrnumpos = displayname + strlen(displayname) - 1;
	strcpy(maindisplayname, displayname);
	strcpy(altdisplayname, displayname);

	/* if multiple screens requested, search for them */
	maxdispnum = 0;
	if (wantmultiscreen)
	{
		/* count the number of displays (look for files named "/dev/fb*") */
		numfiles = filesindirectory("/dev", &filelist);
		for(i=0; i<numfiles; i++)
		{
			if (filelist[i][0] == 'f' && filelist[i][1] == 'b')
			{
				j = atoi(&filelist[i][2]);
				if (j > maxdispnum) maxdispnum = j;
			}
		}

		/* if there are multiple displays, select the best for edit and messages */
		if (maxdispnum > 0)
		{
			bestpixels = 0;
			secondbestpixels = 0;
			for(i=0; i<=maxdispnum; i++)
			{
				sprintf(scrnumpos, "%ld", i);
				dpy = XOpenDisplay(displayname);
				if (dpy == NULL) continue;
				scr = DefaultScreen(dpy);
				dep = DefaultDepth(dpy, scr);
				graphicsheight = DisplayHeight(dpy, scr);
				graphicswidth = DisplayWidth(dpy, scr);
#if 0		/* for debugging multiple monitors */
				{
					Visual *visual;
					char *vistype;

					visual = DefaultVisual(dpy, scr);
					if (VisualClass(visual) == PseudoColor) vistype = "pseudo"; else
						if (VisualClass(visual) == StaticColor) vistype = "static"; else
							vistype = "direct";
					printf(_("Display on screen %ld is %ldx%ld, %ld-bit %s color\n"), scr,
						graphicswidth, graphicsheight, dep, vistype);
				}
#endif
				pixels = graphicsheight * graphicswidth * dep;
				if (pixels > bestpixels)
				{
					if (bestpixels > secondbestpixels)
					{
						secondbestpixels = bestpixels;
						strcpy(altdisplayname, maindisplayname);
					}
					bestpixels = pixels;
					strcpy(maindisplayname, displayname);
				} else if (pixels > secondbestpixels)
				{
					secondbestpixels = pixels;
					strcpy(altdisplayname, displayname);
				}
				XCloseDisplay(dpy);
			}
		}
	}

	/* open the display for the edit window */
	argc = 0;
#ifdef EPROGRAMNAME
	eprogramname = EPROGRAMNAME;
#else
	eprogramname = "Electric";
#endif
	gra_maindpy = XtOpenDisplay(gra_xtapp, maindisplayname, NULL, eprogramname, NULL,
		0, &argc, NULL);
	if (gra_maindpy == NULL)
	{
		fprintf(stderr, _("Cannot open main display %s\n"), maindisplayname);
		exitprogram();
	}

	/* open the display for the messages window */
	if (strcmp(maindisplayname, altdisplayname) == 0) gra_altdpy = gra_maindpy; else
	{
		gra_altdpy = XtOpenDisplay(gra_xtapp, altdisplayname, NULL, eprogramname, NULL,
			0, &argc, NULL);
		if (gra_altdpy == NULL)
		{
			fprintf(stderr, _("Cannot open alternate display %s\n"), altdisplayname);
			exitprogram();
		}
	}
	gra_mainscreen = DefaultScreen(gra_maindpy);
	gra_altscreen = DefaultScreen(gra_altdpy);
}

/*
 * Routine to create the messages window.
 */
void gra_makemessageswindow(void)
{
	XTextProperty swintitle, sicontitle;
	XWMHints *xwmh;
	XSizeHints *xsh;
	Arg arg[10];
	REGISTER INTBIG ac, screenmw, screenmh, j, msgwidth, msgheight;
	char *title;
	Cursor ibeam;
#ifndef USETIF
	char *buffer;
#endif

	/* Determine initial position and size of messages window */
	screenmw = DisplayWidth(gra_altdpy, gra_altscreen);
	screenmh = DisplayHeight(gra_altdpy, gra_altscreen);
	xsh = XAllocSizeHints();
	j = gra_messages_font.font->descent + gra_messages_font.font->ascent + MESLEADING;
	gra_status_height = MAXSTATUSLINES * j + 4;
	xsh->width = screenmh / 3 * 2;
	xsh->height = screenmh / 4 - 80;
	xsh->x = (screenmw - xsh->width) / 2;
	xsh->y = screenmh - xsh->height - 52;
	xsh->min_width = 0;
	xsh->min_height = 0;
	xsh->flags = (PPosition | PSize | PMinSize);
	msgwidth = xsh->width;
	msgheight = xsh->height;

	/* make second top-level shell for messages */
	gra_msgtoplevelwidget = XtAppCreateShell("Electric", "electric",
		applicationShellWidgetClass, gra_altdpy, NULL, 0);
	if (gra_msgtoplevelwidget == 0)
	{
		fprintf(stderr, _("Cannot make messages window\n"));
		exitprogram();
	}
	ac = 0;
	XtSetArg(arg[ac], XtNx, xsh->x);   ac++;
	XtSetArg(arg[ac], XtNy, xsh->y);   ac++;
	XtSetArg(arg[ac], XtNwidth, msgwidth);   ac++;
	XtSetArg(arg[ac], XtNheight, msgheight);   ac++;
#ifdef USETIF
	XtSetArg(arg[ac], XmNdeleteResponse,  XmDO_NOTHING);   ac++;
	XtSetArg(arg[ac], XmNmwmFunctions,    MWM_FUNC_RESIZE|MWM_FUNC_MOVE|MWM_FUNC_MINIMIZE|MWM_FUNC_MAXIMIZE);   ac++;
#endif
#ifdef ANYDEPTH
	XtSetArg(arg[ac], XtNbackground, WHITE);   ac++;
	XtSetArg(arg[ac], XtNforeground, BLACK);   ac++;
#endif
	XtSetValues(gra_msgtoplevelwidget, arg, ac);

	/* make text widget for messages */
#ifdef USETIF
	ac = 0;
	XtSetArg(arg[ac], XmNeditable,   True);   ac++;
	XtSetArg(arg[ac], XmNeditMode,   XmMULTI_LINE_EDIT);   ac++;
	XtSetArg(arg[ac], XmNcolumns,    80);   ac++;
	XtSetArg(arg[ac], XmNrows,       10);   ac++;
	gra_messageswidget = (Widget)XmCreateScrolledText(gra_msgtoplevelwidget,
		"messages", arg, ac);
	if (gra_messageswidget == 0)
	{
		fprintf(stderr, _("Cannot make messages widget\n"));
		exitprogram();
	}
	XtManageChild(gra_messageswidget);
	XtAddEventHandler(gra_messageswidget, KeyPressMask | VisibilityChangeMask |
		ExposureMask | FocusChangeMask, FALSE, gra_messages_event_handler, NULL);
#else
	buffer = (char *)emalloc(MESSAGEBUFFERSIZE, db_cluster);
	if (buffer == 0)
	{
		fprintf(stderr, _("Cannot make messages buffer\n"));
		exitprogram();
	}
	buffer[0] = 0;
	gra_messageswidget = XtVaCreateManagedWidget("messages", asciiTextWidgetClass,
		gra_msgtoplevelwidget,
		XtNstring,           buffer,
#  ifndef ANYDEPTH
		XtNbackground,       WHITE,
		XtNborderColor,      BLACK,
		XtNforeground,       BLACK,
#  endif
		XtNlength,           MESSAGEBUFFERSIZE,
		XtNeditType,         XawtextEdit,
		XtNscrollHorizontal, XawtextScrollAlways,
		XtNscrollVertical,   XawtextScrollAlways,
		NULL);
	if (gra_messageswidget == 0)
	{
		fprintf(stderr, _("Cannot make messages widget\n"));
		exitprogram();
	}
	XtAddEventHandler(gra_messageswidget, ButtonPressMask | KeyPressMask |
		VisibilityChangeMask | FocusChangeMask, FALSE,
		gra_messages_event_handler, NULL);
#endif
	XtRealizeWidget(gra_msgtoplevelwidget);

	gra_topmsgwin = XtWindow(gra_msgtoplevelwidget);
	gra_topmsgdpy = XtDisplay(gra_msgtoplevelwidget);
	gra_messages_obscured = FALSE;
	gra_messages_typingin = FALSE;

	/* set I-beam cursor */
	ibeam = XCreateFontCursor(gra_topmsgdpy, XC_xterm);
	XDefineCursor(gra_topmsgdpy, gra_topmsgwin, ibeam);

	/* make the program icon */
	gra_programicon = XCreateBitmapFromData(gra_topmsgdpy, gra_topmsgwin,
		(const char *)gra_icon, PROGICONSIZE, PROGICONSIZE);
	if (gra_programicon == 0)
	{
		fprintf(stderr, _("Cannot make program icon\n"));
		exitprogram();
	}

	/* load window manager information */
#ifdef EPROGRAMNAME
	title = EPROGRAMNAME;
#else
	title = "Electric";
#endif
	(void)sprintf(gra_localstring, _("%s Messages"), title);
	title = gra_localstring;
	XStringListToTextProperty(&title, 1, &swintitle);
	(void)strcpy(gra_localstring, _("Messages"));
	title = gra_localstring;
	XStringListToTextProperty(&title, 1, &sicontitle);
	xwmh = XAllocWMHints();
	xwmh->input = True;
	xwmh->initial_state = NormalState;
	xwmh->icon_pixmap = xwmh->icon_mask = gra_programicon;
	xwmh->flags = InputHint | StateHint | IconPixmapHint;
	XSetWMProperties(gra_topmsgdpy, gra_topmsgwin, &swintitle, &sicontitle,
		gra_argv, gra_argc, xsh, xwmh, NULL);
	XFree(xsh);
	XFree(xwmh);

	/* set up to trap window kills */
#ifdef USETIF
	XmAddWMProtocolCallback(gra_msgtoplevelwidget, gra_wm_delete_window,
		(XtCallbackProc)gra_windowdelete, (XtPointer)gra_messageswidget);
#else
	XtAddEventHandler(gra_msgtoplevelwidget, NoEventMask, TRUE, gra_destroygraphics,
		NULL);
	XSetWMProtocols(gra_topmsgdpy, gra_topmsgwin, &gra_wm_delete_window, 1);
#endif

#if 0	/* This code used to be part of the !(ANYDEPTH) case (Dmitry Nadezchin) */
	XSetWindowAttributes xswa;

	/* setup properties of the messages window */
	xswa.background_pixel = WhitePixel(gra_topmsgdpy, DefaultScreen(gra_topmsgdpy));
	xswa.border_pixel = BlackPixel(gra_topmsgdpy, DefaultScreen(gra_topmsgdpy));
	xswa.bit_gravity = NorthWestGravity;
	if (gra_use_backing)
	{
		xswa.backing_store = Always;
		xswa.backing_pixel = xswa.background_pixel;
		xswa.backing_planes = AllPlanes;
	}
	if (DoesSaveUnders(ScreenOfDisplay(gra_altdpy, gra_altscreen)))
		xswa.save_under = True;
	XChangeWindowAttributes(gra_topmsgdpy, gra_topmsgwin, CWColormap, &xswa);
#endif
}

#if LANGTCL
INTBIG gra_initializetcl(void)
{
	INTBIG err;
	char *newArgv[2];

	/* set the program name/path */
	newArgv[0] = "Electric";
	newArgv[1] = NULL;
	(void)Tcl_FindExecutable(newArgv[0]);

	gra_tclinterp = Tcl_CreateInterp();
	if (gra_tclinterp == 0) error(_("from Tcl_CreateInterp"));

	/* tell Electric the TCL interpreter handle */
	el_tclinterpreter(gra_tclinterp);

	/* Make command-line arguments available in the Tcl variables "argc" and "argv" */
	Tcl_SetVar(gra_tclinterp, "argv", "", TCL_GLOBAL_ONLY);
	Tcl_SetVar(gra_tclinterp, "argc", "0", TCL_GLOBAL_ONLY);
	Tcl_SetVar(gra_tclinterp, "argv0", "electric", TCL_GLOBAL_ONLY);

	/* Set the "tcl_interactive" variable */
	Tcl_SetVar(gra_tclinterp, "tcl_interactive", "1", TCL_GLOBAL_ONLY);

	/* initialize the interpreter */
	err = Tcl_Init(gra_tclinterp);
	if (err != TCL_OK) error(_("(from Tcl_Init) %s"), gra_tclinterp->result);
	return(err);
}

#  ifdef USETK
void gra_initializetk(void)
{
	INTBIG code;

	if (Tk_Init(gra_tclinterp) == TCL_ERROR)
	{
		ttyputerr(_("Tk_Init failed: %s"), tcl_interp->result);
		return;
	}

	/* get the top Tk window handle */
	gra_tktopwindow = Tk_MainWindow(gra_tclinterp);
	if (gra_tktopwindow == NULL)
		error(_("(from Tk_MainWindow) %s\n"), gra_tclinterp->result);

	/* withdraw the top window */
	if (Tcl_VarEval(gra_tclinterp, "wm withdraw .", (char *)NULL) == TCL_ERROR)
		error(_("(from Tcl_VarEval) %s\n"), gra_tclinterp->result);

	(void)Tk_SetClass(gra_tktopwindow, "Electric");
	(void)Tcl_ResetResult(gra_tclinterp);

	/* initialize tk package */
	if (Tcl_PkgRequire(gra_tclinterp, "Tcl", TCL_VERSION, 1) == NULL)
		error(_("(from Tcl_PkgRequire) %s"), gra_tclinterp->result);
	code = Tcl_PkgProvide(gra_tclinterp, "Tk", TK_VERSION);
	if (code != TCL_OK) error(_("(from Tcl_PkgProvide) %s"), gra_tclinterp->result);
}
#  endif
#endif

/*
 * Routine to establish the library directories from the environment.
 */
void setupenvironment(void)
{
	REGISTER char *pt, *buf;
	REGISTER INTBIG len;

	/* set machine name */
	nextchangequiet();
	(void)setval((INTBIG)us_tool, VTOOL, "USER_machine",
		(INTBIG)"UNIX", VSTRING|VDONTSAVE);

	pt = getenv("ELECTRIC_LIBDIR");
	if (pt != NULL)
	{
		/* environment variable ELECTRIC_LIBDIR is set: use it */
		buf = (char *)emalloc(strlen(pt)+5, el_tempcluster);
		if (buf == 0)
		{
			fprintf(stderr, _("Cannot make environment buffer\n"));
			exitprogram();
		}
		strcpy(buf, pt);
		if (buf[strlen(buf)-1] != '/') strcat(buf, "/");
		(void)allocstring(&el_libdir, buf, db_cluster);
		efree(buf);
		return;
	}

	/* try the #defined library directory */
	(void)initinfstr();
	if (LIBDIR[0] != '/') (void)addstringtoinfstr(currentdirectory());
	(void)addstringtoinfstr(LIBDIR);
	(void)addstringtoinfstr(".cadrc");
	pt = returninfstr();
	if (fileexistence(pt) == 1)
	{
		len = strlen(pt);
		pt[len-6] = 0;
		(void)allocstring(&el_libdir, pt, db_cluster);
		return;
	}

	/* try the current location (look for "lib") */
	(void)initinfstr();
	(void)addstringtoinfstr(currentdirectory());
	(void)addstringtoinfstr("lib/.cadrc");
	pt = returninfstr();
	if (fileexistence(pt) == 1)
	{
		len = strlen(pt);
		pt[len-6] = 0;
		(void)allocstring(&el_libdir, pt, db_cluster);
		return;
	}

	/* no environment, #define, or current directory: look for ".cadrc" */
	if (fileexistence(".cadrc") == 1)
	{
		/* found ".cadrc" in current directory: use that */
		(void)allocstring(&el_libdir, currentdirectory(), db_cluster);
		return;
	}

	/* look for "~/.cadrc" */
	(void)initinfstr();
	(void)addstringtoinfstr(truepath("~/.cadrc"));
	pt = returninfstr();
	if (fileexistence(pt) == 1)
	{
		(void)allocstring(&el_libdir, pt, db_cluster);
		return;
	}

	ttyputerr(_("Warning: cannot find '.cadrc' startup file"));
	(void)allocstring(&el_libdir, ".", db_cluster);
}

void setlibdir(char *libdir)
{
	(void)initinfstr();
	if (libdir[0] != '/') (void)addstringtoinfstr(currentdirectory());
	(void)addstringtoinfstr(libdir);
	if (libdir[strlen(libdir)-1] != DIRSEP) (void)addtoinfstr(DIRSEP);
	(void)reallocstring(&el_libdir, returninfstr(), db_cluster);
}

/******************** TERMINATION ********************/

void termgraphics(void)
{
	REGISTER INTBIG i;
#ifdef ANYDEPTH
	REGISTER INTBIG j;
#endif

	gra_termgraph();

	/* deallocate offscreen buffer for displays */
	efree((char *)gra_mainwd.addr);
	efree((char *)gra_mainwd.rowstart);
	if (gra_maindpy != gra_altdpy)
	{
		efree((char *)gra_altwd.addr);
		efree((char *)gra_altwd.rowstart);
	}

# ifdef TRUETYPE
	if (gra_descentcachesize > 0)
	{
		efree((char *)gra_descentcache);
		efree((char *)gra_ascentcache);
		efree((char *)gra_swidthcache);
	}
	for(i=0; i<gra_numfaces; i++) efree((char *)gra_facelist[i]);
	if (gra_numfaces > 0) efree((char *)gra_facelist);
#endif

#ifdef ANYDEPTH
	for(i=0; gra_font[i].fontname != 0; i++)
	{
		for(j=0; j<128; j++)
		{
			if (gra_font[i].data[j] != 0)
				efree((char *)gra_font[i].data[j]); 
		}
	}

	if (gra_textbitsdatasize > 0) efree((char *)gra_textbitsdata);
	if (gra_textbitsrowstartsize > 0) efree((char *)gra_textbitsrowstart);
#endif

	/* free printer list */
	for(i=0; i<gra_printerlistcount; i++)
		efree((char *)gra_printerlist[i]);
	if (gra_printerlisttotal > 0) efree((char *)gra_printerlist);

	XCloseDisplay(gra_maindpy);
	gra_messageswidget = 0; /* to print messages to stderr */
#ifdef USETIF
	if (gra_icontruedata != 0)
	{
		efree((char *)gra_icontruedata);
		efree((char *)gra_iconrowdata);
	}
/*	XtDestroyApplicationContext(gra_xtapp); ...causes a segmentation fault for some reason */
#endif
	efree((char *)gra_initialdirectory);
	efree((char *)gra_logfile);
	efree((char *)gra_logfilesave);
}

void exitprogram(void)
{
	exit(0);
}

/******************** WINDOW CONTROL ********************/

/*
 * These routines implement multiple windows on the display (each display window
 * is called a "windowframe".  Currently these routines are unimplemented.
 */

WINDOWFRAME *newwindowframe(BOOLEAN floating, RECTAREA *r)
{
	WINDOWFRAME *wf, *oldlisthead;

	/* allocate one */
	wf = (WINDOWFRAME *)emalloc((sizeof (WINDOWFRAME)), us_tool->cluster);
	if (wf == 0) return(NOWINDOWFRAME);
	wf->numvar = 0;
	wf->firstmenu = 0;
	wf->windindex = gra_windowframeindex++;

	/* insert window-frame in linked list */
	oldlisthead = el_firstwindowframe;
	wf->nextwindowframe = el_firstwindowframe;
	el_firstwindowframe = wf;

	/* load an editor window into this frame */
	if (gra_buildwindow(gra_maindpy, &gra_mainwd, gra_maincolmap, wf, floating, FALSE))
	{
		efree((char *)wf);
		el_firstwindowframe = oldlisthead;
		return(NOWINDOWFRAME);
	}

	/* remember that this is the current window frame */
	if (!floating) el_curwindowframe = wf;

	return(wf);
}

void killwindowframe(WINDOWFRAME *owf)
{
	WINDOWFRAME *wf, *lastwf;
#ifdef USETIF
	REGISTER INTBIG i;

	/* don't do this if window is being deleted from another place */
	if (gra_windowbeingdeleted != 0) return;
#endif

	/* kill the actual window, that will remove the frame, too */
	XtDestroyWidget(owf->toplevelwidget);

	/* ind this frame in the list */
	lastwf = NOWINDOWFRAME;
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf == owf) break;
		lastwf = wf;
	}
	if (wf == NOWINDOWFRAME) return;
	if (lastwf == NOWINDOWFRAME) el_firstwindowframe = owf->nextwindowframe; else
		lastwf->nextwindowframe = owf->nextwindowframe;
	if (el_curwindowframe == owf) el_curwindowframe = NOWINDOWFRAME;

#ifdef ANYDEPTH
	efree((char *)wf->dataaddr8);
	efree((char *)wf->rowstart);
#endif
#ifdef USETIF
	for(i=0; i<wf->pulldownmenucount; i++) efree((char *)wf->pulldownmenulist[i]);
	if (wf->pulldownmenucount != 0)
	{
		efree((char *)wf->pulldownmenus);
		efree((char *)wf->pulldowns);
		efree((char *)wf->pulldownmenusize);
		efree((char *)wf->pulldownmenulist);
	}
#endif
	efree((char *)owf);
}

WINDOWFRAME *getwindowframe(BOOLEAN canfloat)
{
	return(el_curwindowframe);
}

/*
 * routine to return size of window "win" in "wid" and "hei"
 */
void getwindowframesize(WINDOWFRAME *wf, INTBIG *wid, INTBIG *hei)
{
	*wid = wf->swid;
	*hei = wf->shei;
}

/*
 * Routine to get the extent of the messages window.
 */
void getmessagesframeinfo(INTBIG *top, INTBIG *left, INTBIG *bottom, INTBIG *right)
{
	Arg arg[4];
	short x, y, wid, hei;

	XtSetArg(arg[0], XtNx, &x);
	XtSetArg(arg[1], XtNy, &y);
	XtSetArg(arg[2], XtNwidth, &wid);
	XtSetArg(arg[3], XtNheight, &hei);
	XtGetValues(gra_msgtoplevelwidget, arg, 4);
	*top = y;
	*left = x;
	*bottom = y+hei;
	*right = x+wid;
}

/*
 * Routine to set the size and position of the messages window.
 */
void setmessagesframeinfo(INTBIG top, INTBIG left, INTBIG bottom, INTBIG right)
{
	XtResizeWidget(gra_msgtoplevelwidget, right-left, bottom-top, 0);
	XtMoveWidget(gra_msgtoplevelwidget, left-WMLEFTBORDER, top-WMTOPBORDER);
}

void sizewindowframe(WINDOWFRAME *wf, INTBIG wid, INTBIG hei)
{
	XWindowAttributes xwa;
	INTBIG intwid, inthei;
	static WINDOWFRAME *lastwf = 0;
	static INTBIG lastwid, lasthei, repeat;

	if (wid < 0 || hei < 0) return;
	gra_getwindowattributes(wf, &xwa);
	if (wid == xwa.width && hei == xwa.height) return;
	if (wf == lastwf && wid == lastwid && hei == lasthei)
	{
		repeat++;
		if (repeat > 2) return;
	} else
	{
		lastwf = wf;   lastwid = wid;   lasthei = hei;   repeat = 1;
	}
	gra_internalchange = ticktime();
	XtResizeWidget(wf->toplevelwidget, wid, hei, 0);
	if (wf->floating)
	{
		gra_getwindowinteriorsize(wf, &intwid, &inthei);
		gra_recalcsize(wf, intwid, inthei);
	}
}

void movewindowframe(WINDOWFRAME *wf, INTBIG left, INTBIG top)
{
	XWindowAttributes xwa;

	gra_getwindowattributes(wf, &xwa);
	if (wf->floating) top += gra_palettetop;
	if (left == xwa.x && top == xwa.y) return;
	gra_internalchange = ticktime();
	XtMoveWidget(wf->toplevelwidget, left, top);
}

void gra_getwindowattributes(WINDOWFRAME *wf, XWindowAttributes *xwa)
{
	Arg arg[4];
	short x, y, wid, hei;

	XtSetArg(arg[0], XtNx, &x);
	XtSetArg(arg[1], XtNy, &y);
	XtSetArg(arg[2], XtNwidth, &wid);
	XtSetArg(arg[3], XtNheight, &hei);
	XtGetValues(wf->toplevelwidget, arg, 4);
	xwa->x = x;         xwa->y = y;
	xwa->width = wid;   xwa->height = hei;
}

void gra_getwindowinteriorsize(WINDOWFRAME *wf, INTBIG *wid, INTBIG *hei)
{
	Arg arg[2];
	short width, height;

	XtSetArg(arg[0], XtNwidth, &width);
	XtSetArg(arg[1], XtNheight, &height);
	XtGetValues(wf->graphicswidget, arg, 2);
	*wid = width;   *hei = height;
}

/*
 * Routine to close the messages window if it is in front.  Returns true if the
 * window was closed.
 */
BOOLEAN closefrontmostmessages(void)
{
	return(FALSE);
}

/*
 * Routine to bring window "wf" to the front.
 */
void bringwindowtofront(WINDOWFRAME *wf)
{
	XRaiseWindow(wf->topdpy, wf->topwin);
	XSetInputFocus(wf->topdpy, wf->topwin, RevertToNone, CurrentTime);
}

/*
 * Routine to organize the windows according to "how" (0: tile horizontally,
 * 1: tile vertically, 2: cascade).
 */
void adjustwindowframe(INTBIG how)
{
	gra_adjustwindowframeon(gra_maindpy, gra_mainscreen, how);
	if (gra_maindpy != gra_altdpy)
		gra_adjustwindowframeon(gra_altdpy, gra_altscreen, how);
}

void gra_adjustwindowframeon(Display *dpy, INTBIG screen, INTBIG how)
{
	RECTAREA r, wr;
	REGISTER INTBIG children, child, sizex, sizey;
	REGISTER WINDOWFRAME *wf, *compmenu;

	/* figure out how many windows need to be rearranged */
	children = 0;
	compmenu = 0;
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->topdpy != dpy) continue;
		if (!wf->floating) children++; else
			compmenu = wf;
	}
	if (children <= 0) return;

	/* determine area for windows */
	r.left = 0;   r.right = DisplayWidth(dpy, screen) - WMLEFTBORDER*2;
	r.top = 0;   r.bottom = DisplayHeight(dpy, screen) - WMLEFTBORDER*2;

	if (compmenu != NULL)
	{
		/* remove component menu from area of tiling */
		gra_removewindowextent(&r, compmenu->toplevelwidget);
	}
	if (dpy == gra_topmsgdpy && gra_msgtoplevelwidget != 0)
	{
		/* remove messages menu from area of tiling */
		gra_removewindowextent(&r, gra_msgtoplevelwidget);
	}

	/* rearrange the windows */
	child = 0;
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->topdpy != dpy) continue;
		if (wf->floating) continue;
		switch (how)
		{
			case 0:		/* tile horizontally */
				wr.left = r.left;
				wr.right = r.right;
				sizey = (r.bottom - r.top) / children;
				wr.top = r.top + child*sizey;
				wr.bottom = wr.top + sizey;
				break;
			case 1:		/* tile vertically */
				wr.top = r.top;
				wr.bottom = r.bottom;
				sizex = (r.right - r.left) / children;
				wr.left = r.left + child*sizex;
				wr.right = wr.left + sizex;
				break;
			case 2:		/* cascade */
				sizex = (r.right - r.left) / children / 2;
				sizey = (r.bottom - r.top) / children / 2;
				wr.left = r.left + child*sizex;
				wr.right = wr.left + (r.right-r.left)/2;
				wr.top = r.top + child*sizey;
				wr.bottom = wr.top + (r.bottom-r.top)/2;
				break;
		}
		wr.right -= WMLEFTBORDER + WMLEFTBORDER;
		wr.bottom -= WMTOPBORDER + WMLEFTBORDER;
		sizewindowframe(wf, wr.right-wr.left, wr.bottom-wr.top);
		gra_repaint(wf, FALSE);
		movewindowframe(wf, wr.left, wr.top);
		child++;
	}
}

/*
 * Helper routine to remove the location of window "wnd" from the rectangle "r".
 */
void gra_removewindowextent(RECTAREA *r, Widget wnd)
{
	RECTAREA er;
	Arg arg[4];
	short x, y, wid, hei;

	XtSetArg(arg[0], XtNx, &x);
	XtSetArg(arg[1], XtNy, &y);
	XtSetArg(arg[2], XtNwidth, &wid);
	XtSetArg(arg[3], XtNheight, &hei);
	XtGetValues(wnd, arg, 4);
	er.left = x;   er.right = er.left + wid;
	er.top = y;    er.bottom = er.top + hei;

	if (er.right-er.left > er.bottom-er.top)
	{
		/* horizontal occluding window */
		if (er.bottom - r->top < r->bottom - er.top)
		{
			/* occluding window on top */
			r->top = er.bottom + WMLEFTBORDER;
		} else
		{
			/* occluding window on bottom */
			r->bottom = er.top - WMTOPBORDER;
		}
	} else
	{
		/* vertical occluding window */
		if (er.right - r->left < r->right - er.left)
		{
			/* occluding window on left */
			r->left = er.right + WMLEFTBORDER;
		} else
		{
			/* occluding window on right */
			r->right = er.left - WMLEFTBORDER;
		}
	}
}

void getpaletteparameters(INTBIG *wid, INTBIG *hei, INTBIG *palettewidth)
{
	*wid = gra_palettewidth;
	*hei = gra_paletteheight - gra_palettetop;
	*palettewidth = PALETTEWIDTH;
}

/*
 * Routine called when the component menu has moved to a different location
 * on the screen (left/right/top/bottom).  Resets local state of the position.
 */
void resetpaletteparameters(void)
{
	gra_palettewidth = DisplayWidth(gra_maindpy, gra_mainscreen) - WMLEFTBORDER;
	gra_paletteheight = DisplayHeight(gra_maindpy, gra_mainscreen) - WMTOPBORDER;
	gra_palettetop = 0;
}

BOOLEAN gra_buildwindow(Display *dpy, WINDOWDISPLAY *wd, Colormap colmap, WINDOWFRAME *wf,
	BOOLEAN floating, BOOLEAN samesize)
{
	XTextProperty icontitle, wintitle;
	XWindowAttributes xwa;
	XWMHints *xwmh;
	Arg arg[10];
	int ac;
	char *ptr;
#ifndef USETIF
	XEvent event;
#endif
#ifdef ANYDEPTH
	REGISTER INTBIG amt, i;
#else
	XGCValues gcv;
	Visual *visual;
	XSetWindowAttributes xswa;
	INTBIG planes[8], pixelarray[256];
#endif

	wf->floating = floating;
	wf->wd = wd;
	wf->colormap = colmap;

	/* determine window position */
	if (samesize)
	{
		gra_getwindowattributes(wf, &xwa);
		gra_editxsh->x = xwa.x;
		gra_editxsh->y = xwa.y;
		gra_editxsh->width = xwa.width;
		gra_editxsh->height = xwa.height;
	} else
	{
		if (!floating)
		{
			gra_editxsh->x = gra_editposx;
			gra_editxsh->y = gra_editposy;
			gra_editxsh->width = gra_editsizex;
			gra_editxsh->height = gra_editsizey;
			gra_editxsh->x += (gra_windownumber%5) * 40;
			gra_editxsh->y += (gra_windownumber%5) * 40;
			if (gra_editxsh->x + gra_editxsh->width > wd->wid)
				gra_editxsh->width = wd->wid - gra_editxsh->x;
			if (gra_editxsh->y + gra_editxsh->height > wd->hei)
				gra_editxsh->height = wd->hei - gra_editxsh->y;
			gra_windownumber++;
		} else
		{
			gra_editxsh->x = 0;
			gra_editxsh->y = 1 + gra_palettetop;
			gra_editxsh->width = PALETTEWIDTH;
			gra_editxsh->height = wd->hei - 50;
			gra_internalchange = ticktime();
		}
	}

	/*
	 * make top-level graphics widget
	 * this call may generate warnings like this:
	 *   Warning: Cannot convert string "<Key>Escape,_Key_Cancel" to type VirtualBinding
	 */
	wf->toplevelwidget = XtAppCreateShell("electric", "electric", applicationShellWidgetClass,
		dpy, NULL, 0);
	if (wf->toplevelwidget == 0)
	{
		fprintf(stderr, _("Could not create top-level window\n"));
		return(TRUE);
	}
	(void)XtAppSetErrorHandler(gra_xtapp, gra_xterrors);
	ac = 0;
	XtSetArg(arg[ac], XtNx, gra_editxsh->x);   ac++;
	XtSetArg(arg[ac], XtNy, gra_editxsh->y);   ac++;
#ifdef USETIF
	XtSetArg(arg[ac], XtNwidth, gra_editxsh->width);   ac++;
	XtSetArg(arg[ac], XtNheight, gra_editxsh->height);   ac++;
	XtSetArg(arg[ac], XmNdeleteResponse,  XmDO_NOTHING);   ac++;
	if (floating)
	{
/*		XtSetArg(arg[ac], XmNoverrideRedirect,  True);   ac++;  ...wanted to make component menu float */
		XtSetArg(arg[ac], XmNmwmFunctions, MWM_FUNC_RESIZE|MWM_FUNC_MOVE|
			MWM_FUNC_MINIMIZE|MWM_FUNC_CLOSE);   ac++;
	}
#endif
	XtSetValues(wf->toplevelwidget, arg, ac);

#ifdef USETIF
	/* make graphics widget frame */
	ac = 0;
	XtSetArg(arg[ac], XmNwidth, gra_editxsh->width);   ac++;
	XtSetArg(arg[ac], XmNheight, gra_editxsh->height);   ac++;
	wf->intermediategraphics = (Widget)XmCreateMainWindow(wf->toplevelwidget,
		"graphics", arg, ac);
	if (wf->intermediategraphics == 0)
	{
		fprintf(stderr, _("Could not create main window\n"));
		return(TRUE);
	}
	XtManageChild(wf->intermediategraphics);

	/* make graphics widget drawing area */
	ac = 0;
	XtSetArg(arg[ac], XmNresizePolicy, XmRESIZE_ANY);   ac++;
	wf->graphicswidget = (Widget)XmCreateDrawingArea(wf->intermediategraphics,
		"drawing_area", arg, ac);
	if (wf->graphicswidget == 0)
	{
		fprintf(stderr, _("Could not create main widget\n"));
		return(TRUE);
	}
	XtManageChild(wf->graphicswidget);

	/* make menus */
	wf->pulldownmenucount = 0;
	if (floating) wf->menubar = 0; else
	{
		wf->menubar = (Widget)XmCreateMenuBar(wf->intermediategraphics, "MainWin", NULL, 0);
		XtManageChild(wf->menubar);
		gra_pulldownmenuload(wf);
	}
	XmMainWindowSetAreas(wf->intermediategraphics, wf->menubar, NULL,
		NULL, NULL, wf->graphicswidget);

	/* add a window-manager protocol to move displays */
	if (gra_maindpy != gra_altdpy)
	{
		XmAddProtocols(wf->toplevelwidget, gra_movedisplaymsg, &gra_movedisplayprotocol, 1);
		XmAddProtocolCallback(wf->toplevelwidget, gra_movedisplaymsg, gra_movedisplayprotocol,
			(XtCallbackProc)gra_movedisplay, NULL);
		sprintf(gra_localstring, "Move\\ to\\ Other\\ Display _D f.send_msg %d",
			gra_movedisplayprotocol);
		XtVaSetValues(wf->toplevelwidget, XmNmwmMenu, gra_localstring, NULL);
	}
#else
	/* make graphics widget */
	wf->graphicswidget = XtVaCreateManagedWidget("graphics", boxWidgetClass,
		wf->toplevelwidget,
		XtNwidth,    gra_editxsh->width,
		XtNheight,   gra_editxsh->height,
		NULL);
	if (wf->graphicswidget == 0)
	{
		fprintf(stderr, _("Could not create edit window contents\n"));
		return(TRUE);
	}
	XtManageChild(wf->graphicswidget);
#endif
	XtAddEventHandler(wf->graphicswidget, ButtonPressMask | ButtonReleaseMask |
		KeyPressMask | PointerMotionMask | ExposureMask | StructureNotifyMask |
		FocusChangeMask, FALSE, gra_graphics_event_handler, NULL);
	XtAddEventHandler(wf->toplevelwidget, StructureNotifyMask, FALSE,
		gra_graphics_event_handler, NULL);
	XtRealizeWidget(wf->toplevelwidget);

	/* get info about this widget */
	wf->topdpy = XtDisplay(wf->toplevelwidget);
	wf->topwin = XtWindow(wf->toplevelwidget);
	wf->win = XtWindow(wf->graphicswidget);

	/* set up to trap window kills */
#ifdef USETIF
	XmAddWMProtocolCallback(wf->toplevelwidget, gra_wm_delete_window,
		(XtCallbackProc)gra_windowdelete, (XtPointer)wf->intermediategraphics);
#else
	XtAddEventHandler(wf->toplevelwidget, NoEventMask, TRUE, gra_destroygraphics, NULL);
	XSetWMProtocols(wf->topdpy, wf->topwin, &gra_wm_delete_window, 1);
#endif

	/* build graphics contexts */
#ifndef ANYDEPTH
	gcv.foreground = BlackPixel(wf->topdpy, DefaultScreen(wf->topdpy));
	gcv.graphics_exposures = False;     /* no event after XCopyArea */
	wf->gc = XtGetGC(wf->graphicswidget, GCForeground | GCGraphicsExposures, &gcv);
	gcv.function = GXinvert;
	wf->gcinv = XtGetGC(wf->graphicswidget, GCFunction | GCGraphicsExposures, &gcv);
	gcv.fill_style = FillStippled;
	wf->gcstip = XtGetGC(wf->graphicswidget, GCFillStyle | GCGraphicsExposures, &gcv);
#endif
	/* get window size and depth after creation */
	XGetWindowAttributes(wf->topdpy, wf->win, &xwa);
	wf->swid = xwa.width;
	wf->trueheight = xwa.height;  /* we keep the true height locally */
	wf->shei = wf->trueheight;
	if (!floating) wf->shei -= gra_status_height;
	wf->revy = wf->shei - 1;

	/* load window manager information */
	if (!floating) (void)strcpy(gra_localstring, _("Electric")); else
		(void)strcpy(gra_localstring, _("Components"));
	ptr = gra_localstring;
	XStringListToTextProperty(&ptr, 1, &wintitle);
	XStringListToTextProperty(&ptr, 1, &icontitle);
	xwmh = XAllocWMHints();
	xwmh->input = True;
	xwmh->initial_state = NormalState;
	xwmh->icon_pixmap = xwmh->icon_mask = gra_programicon;
	xwmh->flags = InputHint | StateHint | IconPixmapHint;
	XSetWMProperties(wf->topdpy, wf->topwin, &wintitle, &icontitle,
		gra_argv, gra_argc, gra_editxsh, xwmh, NULL);
	XFree(xwmh);
#ifdef ANYDEPTH
	/* allocate space for the 8-bit deep buffer */
	amt = wf->swid * wf->trueheight;
	wf->dataaddr8 = (char *)emalloc(amt, us_tool->cluster);
	if (wf->dataaddr8 == 0)
	{
		fprintf(stderr, _("Could not allocate space for offscreen buffer\n"));
		return(TRUE);
	}
	for(i=0; i<amt; i++) wf->dataaddr8[i] = 0;
	wf->rowstart = (char **)emalloc(wf->trueheight * SIZEOFINTBIG, us_tool->cluster);
	if (wf->rowstart == 0)
	{
		fprintf(stderr, _("Could not allocate space for row pointers\n"));
		return(TRUE);
	}
	for(i=0; i<wf->trueheight; i++)
		wf->rowstart[i] = wf->dataaddr8 + i * wf->swid;

	/* fill the color map  */
	if (DefaultDepth(wf->topdpy, DefaultScreen(wf->topdpy)) != 1)
	{
		us_getcolormap(el_curtech, COLORSDEFAULT, FALSE);
		gra_reloadmap();
	}
#else
	/* setup properties of the edit window */
	xswa.background_pixel = WhitePixel(wf->topdpy, DefaultScreen(wf->topdpy));
	xswa.border_pixel = BlackPixel(wf->topdpy, DefaultScreen(wf->topdpy));
	if (gra_use_backing)
	{
		xswa.backing_store = WhenMapped;

		/* load backing store property (Dmitry Nadezchin) */
		XChangeWindowAttributes(wf->topdpy, wf->win, CWBackingStore, &xswa);

		/* default backing_pixel and backing_planes are not set */
		xswa.backing_pixel = xswa.background_pixel;
		xswa.backing_planes = AllPlanes;
	}
	visual = DefaultVisual(wf->topdpy, DefaultScreen(wf->topdpy));
	wf->colormap = XCreateColormap(wf->topdpy, RootWindow(wf->topdpy, DefaultScreen(wf->topdpy)),
		visual, AllocNone);
	xswa.colormap = wf->colormap;
	XChangeWindowAttributes(wf->topdpy, wf->topwin, CWColormap, &xswa);

	/* fill the color map  */
	if (DefaultDepth(wf->topdpy, DefaultScreen(wf->topdpy)) != 1)
	{
		if (XAllocColorCells(wf->topdpy, wf->colormap, 1, (unsigned long *)planes, 0,
			(unsigned long *)pixelarray, el_maplength) == 0)
				fprintf(stderr, "XAllocColorCells failed\n");

		/* load any map the first time to establish the map segment length */
		us_getcolormap(el_curtech, COLORSDEFAULT, FALSE);
		gra_reloadmap();
		gra_xfc.pixel = el_colcursor;
		XQueryColor(wf->topdpy, wf->colormap, &gra_xfc);
		gra_xbc.pixel = 0;
		XQueryColor(wf->topdpy, wf->colormap, &gra_xbc);
		gra_recolorcursor(wf->topdpy, &gra_xfc, &gra_xbc);
	}
#endif

	/* initialize the window frames */
#ifndef USETIF
#  ifndef ANYDEPTH
	if (gra_use_backing)
	{
		while (XCheckTypedWindowEvent(wf->topdpy, wf->win, MapNotify, &event) == False);
	} else
#  endif
	{
		/* avoid double repaint at startup */
		while (XCheckTypedWindowEvent(wf->topdpy, wf->win, Expose, &event) == False);
	}
#endif
	return(FALSE);
}

#ifdef USETIF
void gra_movedisplay(Widget w, XtPointer client_data, XtPointer *call_data)
{
	Display *dpy;
	REGISTER WINDOWFRAME *wf;
	REGISTER WINDOWDISPLAY *wd;
	Colormap colmap;

	wf = gra_getcurrentwindowframe(w, FALSE);
	if (wf == NOWINDOWFRAME) return;
	XtDestroyWidget(wf->toplevelwidget);
#ifdef ANYDEPTH
	efree((char *)wf->dataaddr8);
	efree((char *)wf->rowstart);
#endif
	if (wf->topdpy == gra_maindpy)
	{
		dpy = gra_altdpy;
		wd = &gra_altwd;
		colmap = gra_altcolmap;
	} else
	{
		dpy = gra_maindpy;
		wd = &gra_mainwd;
		colmap = gra_maincolmap;
	}
	if (gra_buildwindow(dpy, wd, colmap, wf, wf->floating, TRUE))
		ttyputerr(_("Problem moving the display"));

	/* redisplay */
	gra_reloadmap();
	us_drawmenu(1, wf);
	us_redostatus(wf);
	us_endbatch();
}
#endif

/******************** MISCELLANEOUS EXTERNAL ROUTINES ********************/

/*
 * return nonzero if the capabilities in "want" are present
 */
BOOLEAN graphicshas(INTBIG want)
{
	if ((want & CANHAVENOWINDOWS) != 0) return(FALSE);
#ifndef USETIF
	if ((want & CANSHOWPOPUP) != 0) return(FALSE);
#endif

	/* cannot modify typefaces */
	if ((want&(CANCHOOSEFACES|CANMODIFYFONTS|CANSCALEFONTS)) != 0)
	{
#ifdef TRUETYPE
		if (gra_truetypeon != 0) return(TRUE);
#endif
		return(FALSE);
	}

	return(TRUE);
}

/*
 * internal routine to beep the status display.  If beeps are turned off, no
 * sound is made (unless "force" is nonzero)
 */
void ttybeep(INTBIG force)
{
	if ((us_tool->toolstate & TERMBEEP) != 0 || force != 0)
	{
		XBell(gra_maindpy, 100);
	}
}

DIALOGITEM db_severeerrordialogitems[] =
{
 /*  1 */ {0, {80,8,104,72}, BUTTON, N_("Exit")},
 /*  2 */ {0, {80,96,104,160}, BUTTON, N_("Save")},
 /*  3 */ {0, {80,184,104,256}, BUTTON, N_("Continue")},
 /*  4 */ {0, {8,8,72,256}, MESSAGE, ""}
};
DIALOG db_severeerrordialog = {{50,75,163,341}, N_("Fatal Error"), 0, 4, db_severeerrordialogitems};

/* special items for the severe error dialog: */
#define DSVE_EXIT      1		/* Exit (button) */
#define DSVE_SAVE      2		/* Save (button) */
#define DSVE_CONTINUE  3		/* Continue (button) */
#define DSVE_MESSAGE   4		/* Error Message (stat text) */

void error(char *s, ...)
{
	va_list ap;
	char line[500];
	REGISTER INTBIG itemHit, *curstate;
	REGISTER INTBIG retval;
	REGISTER LIBRARY *lib;

	/* disable any session logging */
	if (us_logplay != NULL)
	{
		xclose(us_logplay);
		us_logplay = NULL;
	}

	/* build the error message */
	var_start(ap, s);
	evsnprintf(line, 500, s, ap);
	va_end(ap);

	/* display the severe error dialog box */
	if (DiaInitDialog(&db_severeerrordialog)) return;

	/* load the message */
	DiaSetText(DSVE_MESSAGE, line);

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == DSVE_EXIT) exitprogram();
		if (itemHit == DSVE_CONTINUE) break;
		if (itemHit == DSVE_SAVE)
		{
			/* save libraries: make sure that backups are kept */
			curstate = io_getstatebits();
			if ((curstate[0]&BINOUTBACKUP) == BINOUTNOBACK)
			{
				curstate[0] |= BINOUTONEBACK;
				io_setstatebits(curstate);
			}

			/* save each modified library */
			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			{
				if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
				if ((lib->userbits&(LIBCHANGEDMAJOR|LIBCHANGEDMINOR)) == 0) continue;

				/* save the library in binary format */
				makeoptionstemporary(lib);
				retval = asktool(io_tool, "write", (INTBIG)lib, (INTBIG)"binary");
				restoreoptionstate(lib);
				if (retval != 0) return;
			}
		}
	}
	DiaDoneDialog();
}

/*
 * Routine to get the environment variable "name" and return its value.
 */
char *egetenv(char *name)
{
	return(getenv(name));
}

/*
 * Routine to get the current language that Electric speaks.
 */
char *elanguage(void)
{
	char *lang;

	lang = getenv("LANGUAGE");
	if (lang == 0) lang = "en";
	return(lang);
}

/*
 * Routine to fork a new process.  Returns the child process number if this is the
 * parent thread.  Returns 0 if this is the child thread.
 * Returns 1 if forking is not possible (process 1 is INIT on UNIX and can't possibly
 * be assigned to a normal process).
 */
INTBIG efork(void)
{
	return(fork());
}

/*
 * Routine to run the string "command" in a shell.
 * Returns nonzero if the command cannot be run.
 */
INTBIG esystem(char *command)
{
	system(command);
	return(0);
}

/*
 * Routine to execute the program "program" with the arguments "args"
 */
void eexec(char *program, char *args[])
{
	execvp(program, args);
}

/*
 * routine to kill process "process".
 */
INTBIG ekill(INTBIG process)
{
	return(kill(process, SIGKILL));
}

/*
 * routine to wait for the completion of child process "process"
 */
void ewait(INTBIG process)
{
	REGISTER INTBIG pid;

	for(;;)
	{
		pid = wait((int *)0);
		if (pid == process) return;
		if (pid == -1)
		{
			perror("Waiting");
			return;
		}
	}
}

/*
 * Routine to determine the list of printers and return it.
 * The list terminates with a zero.
 */
char **eprinterlist(void)
{
	static char *nulllplist[1];
	int pipechans[2];
	char *execargs[5];
	INTBIG process, i, save, bptr;
	char val, buf[200], *pt;
	FILE *io;

	/* remove former list */
	for(i=0; i<gra_printerlistcount; i++)
		efree((char *)gra_printerlist[i]);
	gra_printerlistcount = 0;
	nulllplist[0] = 0;

	/* look for "/etc/printcap" */
	io = fopen("/etc/printcap", "r");
	if (io != 0)
	{
		for(;;)
		{
			if (xfgets(buf, 200, io)) break;
			if (buf[0] == '#' || buf[0] == ' ' || buf[0] == '\t') continue;
			for(pt = buf; *pt != 0; pt++)
				if (*pt == ':') break;
			if (*pt == 0) continue;
			*pt = 0;
			if (gra_addprinter(buf))
				return(nulllplist);
		}
		fclose(io);
		if (gra_printerlistcount <= 0) return(nulllplist);
		return(gra_printerlist);
	}

	/* no file "/etc/printcap": try to run "lpget" */
	(void)epipe(pipechans);
	process = efork();
	if (process == 1)
		return(nulllplist);

	/* fork the process */
	if (process == 0)
	{
		save = channelreplacewithchannel(1, pipechans[1]);
		execargs[0] = "lpget";
		execargs[1] = "list";
		execargs[2] = 0;
		eexec("lpget", execargs);
		channelrestore(1, save);
		ttyputerr(_("Cannot find 'lpget'"));
		exit(1);
	}
	eclose(pipechans[1]);

	/* read the list from the program */
	bptr = 0;
	for(;;)
	{
		i = eread(pipechans[0], &val, 1);
		if (i != 1) break;
		if (val == ':')
		{
			if (gra_addprinter(buf))
				return(nulllplist);
		}
		buf[bptr++] = val;   buf[bptr] = 0;
		if (val == '\n' || val == '\r') bptr = 0;
	}
	ewait(process);
	if (gra_printerlistcount <= 0) return(nulllplist);
	return(gra_printerlist);
}

BOOLEAN gra_addprinter(char *buf)
{
	char **newlist;
	INTBIG i, j, newtotal;

	if (strcmp(buf, "_default") == 0) return(FALSE);
	if (buf[0] == ' ' || buf[0] == '\t') return(FALSE);

	/* stop if it is already in the list */
	for(i=0; i<gra_printerlistcount; i++)
		if (strcmp(buf, gra_printerlist[i]) == 0) return(FALSE);

	/* make room in the list */
	if (gra_printerlistcount >= gra_printerlisttotal-1)
	{
		newtotal = gra_printerlistcount + 10;
		newlist = (char **)emalloc(newtotal * (sizeof (char *)), us_tool->cluster);
		if (newlist == 0) return(TRUE);
		for(i=0; i<gra_printerlistcount; i++)
			newlist[i] = gra_printerlist[i];
		if (gra_printerlisttotal > 0) efree((char *)gra_printerlist);
		gra_printerlist = newlist;
		gra_printerlisttotal = newtotal;
	}

	/* insert into the list */
	for(i=0; i<gra_printerlistcount; i++)
		if (strcmp(buf, gra_printerlist[i]) < 0) break;
	for(j=gra_printerlistcount; j>i; j--) gra_printerlist[j] = gra_printerlist[j-1];
	gra_printerlist[i] = (char *)emalloc(strlen(buf)+1, us_tool->cluster);
	strcpy(gra_printerlist[i], buf);
	gra_printerlistcount++;
	gra_printerlist[gra_printerlistcount] = 0;
	return(FALSE);
}

/******************** STATUS BAR ROUTINES ********************/

/*
 * Routine to return the number of status lines on the display.
 */
INTBIG ttynumstatuslines(void)
{
	return(MAXSTATUSLINES);
}

/*
 * Routine to free status field object "sf".
 */
void ttyfreestatusfield(STATUSFIELD *sf)
{
	efree(sf->label);
	efree((char *)sf);
}

/*
 * Routine to display "message" in the status "field" of window "frame" (uses all windows
 * if "frame" is zero).  If "cancrop" is false, field cannot be cropped and should be
 * replaced with "*" if there isn't room.
 */
void ttysetstatusfield(WINDOWFRAME *wwf, STATUSFIELD *field, char *message, BOOLEAN cancrop)
{
	INTBIG len, lx, ly, hx, hy, wid, hei, sizey;
	INTBIG oldfont;
	char *ptr;
	XTextProperty wintitle;
	REGISTER WINDOWFRAME *wf;
	WINDOWPART win;

	if (field == 0) return;
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->floating) continue;
		if (wwf != NOWINDOWFRAME && wwf != wf) continue;

		/* make a window that corresponds to this frame */
		win.frame = wf;
		win.screenlx = win.uselx = 0;
		win.screenhx = win.usehx = wf->swid;
		win.screenly = win.usely = -gra_status_height;
		win.screenhy = win.usehy = wf->shei;
		win.state = DISPWINDOW;
		computewindowscale(&win);

		if (field->line == 0)
		{
			/* should set title bar here */
#ifdef EPROGRAMNAME
			(void)strcpy(gra_localstring, EPROGRAMNAME);
#else
			(void)strcpy(gra_localstring, _("Electric"));
#endif
			if (strlen(message) > 0)
			{
				(void)strcat(gra_localstring, " (");
				(void)strcat(gra_localstring, field->label);
				(void)strcat(gra_localstring, message);
				(void)strcat(gra_localstring, ")");
			}
			len = strlen(gra_localstring);
			while (len > 0 && gra_localstring[len-1] == ' ') gra_localstring[--len] = 0;
			ptr = gra_localstring;
			XStringListToTextProperty(&ptr, 1, &wintitle);
			XSetWMName(wf->topdpy, wf->topwin, &wintitle);
			continue;
		}

		/* construct the status line */
		(void)strcpy(gra_localstring, field->label);
		(void)strcat(gra_localstring, message);
		len = strlen(gra_localstring);
		while (len > 0 && gra_localstring[len-1] == ' ') gra_localstring[--len] = 0;

		oldfont = gra_truetypesize;
# ifdef TRUETYPE
		if (gra_truetypeon != 0)
		{
			gra_settextsize(&win, 12, 0, 0, 0, 0);
		} else
#endif
		gra_settextsize(&win, 6, 0, 0, 0, 0);
		sizey = gra_curfont->ascent + gra_curfont->descent;

		lx = wf->swid * field->startper / 100;
		hx = wf->swid * field->endper / 100;
		ly = - (gra_status_height / field->line);
		hy = ly + sizey;
		while (gra_localstring[0] != 0)
		{
			screengettextsize(&win, gra_localstring, &wid, &hei);
			if (wid <= hx-lx) break;
			gra_localstring[strlen(gra_localstring)-1] = 0;
		}
		screendrawbox(&win, lx, hx - 1, ly, hy, &us_ebox);
		screendrawtext(&win, lx, ly+1, gra_localstring, &us_menutext);  /* added "+1" */
		gra_settextsize(&win, oldfont, 0, 0, 0, 0);

		/* draw the separator line between drawing and status areas */
		us_box.col = BLACK;
		screendrawline(&win, 1, -2, wf->swid - 1, -2, &us_box, 0);
	}
}

/******************** MESSAGES WINDOW ROUTINES ********************/

/*
 * Routine to put the string "s" into the messages window.
 * Pops up the messages window if "important" is nonzero.
 */
void putmessagesstring(char *s, BOOLEAN important)
{
#ifdef USETIF
	XmTextPosition tp;
	XWindowAttributes xwa;

	/* print to stderr if messages are not initiaized */
	if (gra_messageswidget == 0) {
		fprintf(stderr, "%s\n", s);
		return;
	}

	/* make sure the window isn't iconified or obscured */
	if (important)
	{
		if (XGetWindowAttributes(gra_topmsgdpy, gra_topmsgwin, &xwa) == 0) return;
		if (xwa.map_state != IsViewable || gra_messages_obscured)
			XMapRaised(gra_topmsgdpy, gra_topmsgwin);
	}

	tp = XmTextGetLastPosition(gra_messageswidget);
	XmTextInsert(gra_messageswidget, tp, s);
	tp = XmTextGetLastPosition(gra_messageswidget);
	XmTextInsert(gra_messageswidget, tp, "\n");
	tp = XmTextGetLastPosition(gra_messageswidget);
	XmTextSetSelection(gra_messageswidget, tp, tp, 0);
	XmTextSetInsertionPosition(gra_messageswidget, tp);
	XmTextShowPosition(gra_messageswidget, tp);
#else
	XawTextBlock tb;
	char endline[2];
	XWindowAttributes xwa;
	XawTextPosition ip;

	/* print to stderr if messages are not initiaized */
	if (gra_messageswidget == 0) {
		fprintf(stderr, "%s\n", s);
		return;
	}

	/* make sure the window isn't iconified or obscured */
	if (important)
	{
		if (XGetWindowAttributes(gra_topmsgdpy, gra_topmsgwin, &xwa) == 0) return;
		if (xwa.map_state != IsViewable || gra_messages_obscured)
			XMapRaised(gra_topmsgdpy, gra_topmsgwin);
	}

	ip = XawTextGetInsertionPoint(gra_messageswidget);
	if (ip + 1000 > MESSAGEBUFFERSIZE)
	{
		tb.firstPos = 0;
		tb.length = 0;
		tb.ptr = s;
		tb.format = FMT8BIT;
		XawTextReplace(gra_messageswidget, 0, 1000, &tb);
	}
	tb.firstPos = 0;
	tb.length = strlen(s);
	tb.ptr = s;
	tb.format = FMT8BIT;
	XawTextReplace(gra_messageswidget, MESSAGEBUFFERSIZE, MESSAGEBUFFERSIZE, &tb);
	endline[0] = '\n';   endline[1] = 0;
	tb.length = 1;
	tb.ptr = endline;
	XawTextReplace(gra_messageswidget, MESSAGEBUFFERSIZE, MESSAGEBUFFERSIZE, &tb);
	XawTextSetInsertionPoint(gra_messageswidget, MESSAGEBUFFERSIZE);
#endif
}

/*
 * Routine to return the name of the key that ends a session from the messages window.
 */
char *getmessageseofkey(void)
{
	return("^D");
}

char *getmessagesstring(char *prompt)
{
#ifdef USETIF
	XmTextPosition tp, starttp;
	char ch[2], *block;
	INTBIG c, total, special;

	/* show the prompt */
	tp = XmTextGetLastPosition(gra_messageswidget);
	XmTextInsert(gra_messageswidget, tp, prompt);

	/* get initial text position */
	starttp = XmTextGetLastPosition(gra_messageswidget);
	XmTextSetSelection(gra_messageswidget, starttp, starttp, 0);
	XmTextSetInsertionPosition(gra_messageswidget, starttp);

	/* loop while typing is done */
	total = 0;
	gra_messages_typingin = TRUE;
	for(;;)
	{
		c = getnxtchar(&special);
		if (c == '\n' || c == '\r' || c == CTRLDKEY) break;
		if (c == DELETEKEY || c == BACKSPACEKEY)
		{
			tp = XmTextGetLastPosition(gra_messageswidget);
			XmTextSetSelection(gra_messageswidget, tp-1, tp, 0);
			XmTextRemove(gra_messageswidget);
			total--;
			continue;
		}
		if (c == us_killch)
		{
			tp = XmTextGetLastPosition(gra_messageswidget);
			XmTextSetSelection(gra_messageswidget, starttp, tp, 0);
			XmTextRemove(gra_messageswidget);
			total = 0;
			continue;
		}
		tp = XmTextGetLastPosition(gra_messageswidget);
		ch[0] = c;
		ch[1] = 0;
		XmTextInsert(gra_messageswidget, tp, ch);
		tp = XmTextGetLastPosition(gra_messageswidget);
		XmTextSetSelection(gra_messageswidget, tp, tp, 0);
		total++;
	}
	gra_messages_typingin = FALSE;

	block = (char *)emalloc(total+2, el_tempcluster);
	if (block == 0)
	{
		fprintf(stderr, _("Cannot make type-in buffer\n"));
		return(0);
	}
	XmTextGetSubstring(gra_messageswidget, starttp, total+1, total+2, block);
	block[total] = 0;
	(void)initinfstr();
	(void)addstringtoinfstr(block);
	efree((char *)block);
	block = returninfstr();

	/* add in final carriage return */
	tp = XmTextGetLastPosition(gra_messageswidget);
	XmTextInsert(gra_messageswidget, tp, "\n");

	/* return the string (0 on EOF or null) */
	if (*block == 0 && c == 4) return(0);
	return(block);
#else
	char c;
	INTBIG i, special;
	XawTextPosition posstart, posend;
	Arg arg;
	char *str, ch[3];
	XawTextBlock tb;

	/* show the prompt */
	tb.firstPos = 0;
	tb.length = strlen(prompt);
	tb.ptr = prompt;
	tb.format = FMT8BIT;
	XawTextReplace(gra_messageswidget, MESSAGEBUFFERSIZE, MESSAGEBUFFERSIZE, &tb);

	/* get initial text position */
	XawTextSetInsertionPoint(gra_messageswidget, MESSAGEBUFFERSIZE);
	posstart = XawTextGetInsertionPoint(gra_messageswidget);

	/* loop while typing is done */
	tb.ptr = ch;
	gra_messages_typingin = TRUE;
	for(;;)
	{
		c = getnxtchar(&special);
		if (c == '\n' || c == '\r' || c == CTRLDKEY) break;
		if (c == DELETEKEY || c == BACKSPACEKEY)
		{
			posend = XawTextGetInsertionPoint(gra_messageswidget);
			if (posend > posstart)
			{
				tb.length = 0;
				XawTextReplace(gra_messageswidget, posend-1, posend, &tb);
				XawTextSetInsertionPoint(gra_messageswidget, MESSAGEBUFFERSIZE);
			}
			continue;
		}
		if (c == us_killch)
		{
			posend = XawTextGetInsertionPoint(gra_messageswidget);
			if (posend > posstart)
			{
				tb.length = 0;
				XawTextReplace(gra_messageswidget, posstart, posend, &tb);
				XawTextSetInsertionPoint(gra_messageswidget, MESSAGEBUFFERSIZE);
			}
			continue;
		}
		tb.length = 1;
		ch[0] = c;
		XawTextReplace(gra_messageswidget, MESSAGEBUFFERSIZE, MESSAGEBUFFERSIZE, &tb);
		XawTextSetInsertionPoint(gra_messageswidget, MESSAGEBUFFERSIZE);
	}
	gra_messages_typingin = FALSE;

	/* get current text position */
	XawTextSetInsertionPoint(gra_messageswidget, MESSAGEBUFFERSIZE);
	posend = XawTextGetInsertionPoint(gra_messageswidget);

	/* get typed string */
	XtSetArg(arg, XtNstring, &str);
	XtGetValues(gra_messageswidget, &arg, 1);
	(void)initinfstr();
	for(i=posstart; i<posend; i++) (void)addtoinfstr(str[i]);
	str = returninfstr();

	/* add in final carriage return */
	ch[0] = '\n';
	tb.length = 1;
	XawTextReplace(gra_messageswidget, MESSAGEBUFFERSIZE, MESSAGEBUFFERSIZE, &tb);
	if (*str == 0 && c == 4) return(0);
	return(str);
#endif
}

/* X Font Selection */
DIALOGITEM gra_xfontdialogitems[] =
{
 /*  1 */ {0, {312,592,336,652}, BUTTON, N_("OK")},
 /*  2 */ {0, {312,312,336,372}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {8,8,24,88}, MESSAGE, N_("Foundry")},
 /*  4 */ {0, {8,96,24,296}, POPUP, ""},
 /*  5 */ {0, {32,8,48,88}, MESSAGE, N_("Family")},
 /*  6 */ {0, {32,96,48,296}, POPUP, ""},
 /*  7 */ {0, {56,8,72,88}, MESSAGE, N_("Weight")},
 /*  8 */ {0, {56,96,72,296}, POPUP, ""},
 /*  9 */ {0, {80,8,96,88}, MESSAGE, N_("Slant")},
 /* 10 */ {0, {80,96,96,296}, POPUP, ""},
 /* 11 */ {0, {104,8,120,88}, MESSAGE, N_("S Width")},
 /* 12 */ {0, {104,96,120,296}, POPUP, ""},
 /* 13 */ {0, {128,8,144,88}, MESSAGE, N_("Ad Style")},
 /* 14 */ {0, {128,96,144,296}, POPUP, ""},
 /* 15 */ {0, {152,8,168,88}, MESSAGE, N_("Pixel Size")},
 /* 16 */ {0, {152,96,168,296}, POPUP, ""},
 /* 17 */ {0, {176,8,192,88}, MESSAGE, N_("Point Size")},
 /* 18 */ {0, {176,96,192,296}, POPUP, ""},
 /* 19 */ {0, {200,8,216,88}, MESSAGE, N_("X Resolution")},
 /* 20 */ {0, {200,96,216,296}, POPUP, ""},
 /* 21 */ {0, {224,8,240,88}, MESSAGE, N_("Y Resolution")},
 /* 22 */ {0, {224,96,240,296}, POPUP, ""},
 /* 23 */ {0, {248,8,264,88}, MESSAGE, N_("Spacing")},
 /* 24 */ {0, {248,96,264,296}, POPUP, ""},
 /* 25 */ {0, {272,8,288,88}, MESSAGE, N_("Avg Width")},
 /* 26 */ {0, {272,96,288,296}, POPUP, ""},
 /* 27 */ {0, {296,8,312,88}, MESSAGE, N_("Registry")},
 /* 28 */ {0, {296,96,312,296}, POPUP, ""},
 /* 29 */ {0, {320,8,336,88}, MESSAGE, N_("Encoding")},
 /* 30 */ {0, {320,96,336,296}, POPUP, ""},
 /* 31 */ {0, {8,304,300,656}, SCROLL, ""}
};
DIALOG gra_xfontdialog = {{75,75,421,740}, N_("Font Selection"), 0, 31, gra_xfontdialogitems};

/* special items for the messages window font dialog: */
#define DMSF_FIRSTPOPUP    4		/* first popup (popup) */
#define DMSF_LASTPOPUP    30		/* last popup (popup) */
#define DMSF_FONTLIST     31		/* list of fonts (scroll) */

/*
 * routine to select fonts in the messages window
 */
void setmessagesfont(void)
{
	char **fontlist, *required[14], ***choices;
	int namecount;
	INTBIG i, j, k, *numchoices;
	INTBIG itemHit;
#ifdef USETIF
	Arg arg[1];
	XmFontListEntry entry;
	XmFontList xfontlist;
#endif

	/* get the list of available fonts */
	fontlist = XListFonts(gra_altdpy, "-*-*-*-*-*-*-*-*-*-*-*-*-*-*",
		2000, &namecount);

	/* show the dialog */
	if (DiaInitDialog(&gra_xfontdialog)) return;
	DiaInitTextDialog(DMSF_FONTLIST, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone,
		-1, SCDOUBLEQUIT|SCSELMOUSE|SCSMALLFONT|SCFIXEDWIDTH|SCHORIZBAR);

	/* setup so that all fields are possible */
	for(j=0; j<14; j++)
	{
		required[j] = (char *)emalloc(200, el_tempcluster);
		if (required[j] == 0)
		{
			fprintf(stderr, _("Cannot make field for dialog\n"));
			return;
		}
		strcpy(required[j], "*");
	}

	/* examine all of the fonts and make a lists of choices for each position */
	gra_makefontchoices(fontlist, namecount, required, &choices, &numchoices);
	for(j=0; j<14; j++)
	{
		DiaSetPopup(j*2+DMSF_FIRSTPOPUP, numchoices[j], choices[j]);
		DiaSetPopupEntry(j*2+DMSF_FIRSTPOPUP, 0);
	}
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
		if (itemHit >= DMSF_FIRSTPOPUP && itemHit <= DMSF_LASTPOPUP && (itemHit / 2) * 2 == itemHit)
		{
			/* popup changed */
			for(j=0; j<14; j++)
			{
				i = DiaGetPopupEntry(j*2+DMSF_FIRSTPOPUP);
				strcpy(required[j], choices[j][i]);
			}
			gra_makefontchoices(fontlist, namecount, required, &choices, &numchoices);
			for(j=0; j<14; j++)
			{
				DiaSetPopup(j*2+DMSF_FIRSTPOPUP, numchoices[j], choices[j]);
				for(i=0; i<numchoices[j]; i++)
					if (namesame(choices[j][i], required[j]) == 0) break;
				DiaSetPopupEntry(j*2+DMSF_FIRSTPOPUP, i);
			}
			continue;
		}
	}
	if (itemHit == OK)
	{
		i = DiaGetCurLine(DMSF_FONTLIST);
		strcpy(required[0], DiaGetScrollLine(DMSF_FONTLIST, i));
#ifdef USETIF
		entry = XmFontListEntryLoad(gra_altdpy, required[0],
			XmFONT_IS_FONT, "TAG");
		xfontlist = XmFontListAppendEntry(NULL, entry);
		XmFontListEntryFree(&entry);
		XtSetArg(arg[0], XmNfontList, xfontlist);
		XtSetValues(gra_messageswidget, arg, 1);
		XmFontListFree(xfontlist);
#endif
	}
	DiaDoneDialog();
	XFreeFontPath(fontlist);

	/* free the memory */
	for(j=0; j<14; j++)
	{
		for(k=0; k<numchoices[j]; k++)
			efree((char *)choices[j][k]);
		efree((char *)required[j]);
	}
}

void gra_makefontchoices(char **fontlist, INTBIG namecount, char *required[14],
	char ****fchoices, INTBIG **fnumchoices)
{
	char *pt, *start, save, **newchoices;
	INTBIG i, j, k, newsize;
	static INTBIG totalchoices[14], numchoices[14];
	static char **choices[14], *current[14];
	static BOOLEAN inited = FALSE;

	/* load the return parameters */
	*fchoices = (char ***)choices;
	*fnumchoices = (INTBIG *)numchoices;

	/* one-time memory allocation and initialization */
	if (!inited)
	{
		inited = TRUE;
		for(j=0; j<14; j++)
		{
			totalchoices[j] = 5;
			choices[j] = (char **)emalloc(5 * (sizeof (char *)),
				el_tempcluster);
			if (choices[j] == 0)
			{
				fprintf(stderr, _("Cannot make choice field for dialog\n"));
				return;
			}
			numchoices[j] = 0;
			current[j] = (char *)emalloc(200, el_tempcluster);
			if (current[j] == 0)
			{
				fprintf(stderr, _("Cannot make answer field for dialog\n"));
				return;
			}
		}
	}

	/* initialize the popup choices */
	for(j=0; j<14; j++)
	{
		/* free former memory */
		for(k=0; k<numchoices[j]; k++)
			efree((char *)choices[j][k]);
		numchoices[j] = 1;
		choices[j][0] = (char *)emalloc(2, el_tempcluster);
		if (choices[j][0] == 0)
		{
			fprintf(stderr, _("Cannot make popup for dialog\n"));
			return;
		}
		strcpy(choices[j][0], "*");
	}

	/* initialize the scroll area */
	DiaLoadTextDialog(DMSF_FONTLIST, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, -1);

	/* examine each font and build the choice list */
	for(i=0; i<namecount; i++)
	{
		/* break the font name into fields, make sure it matches filter */
		pt = fontlist[i];
		for(j=0; j<14; j++)
		{
			if (*pt == '-') pt++;
			start = pt;
			while (*pt != 0 && *pt != '-') pt++;
			save = *pt;
			*pt = 0;
			if (*start == 0) strcpy(current[j], "(nil)"); else
				strcpy(current[j], start);
			*pt = save;
			if (required[j][0] == '*') continue;
			if (namesame(required[j], current[j]) != 0) break;
		}
		if (j < 14) continue;
		DiaStuffLine(DMSF_FONTLIST, fontlist[i]);

		/* add this to the list of choices */
		for(j=0; j<14; j++)
		{
			for(k=0; k<numchoices[j]; k++)
				if (namesame(current[j], choices[j][k]) == 0) break;
			if (k < numchoices[j]) continue;

			/* add this choice */
			if (numchoices[j] >= totalchoices[j])
			{
				newsize = totalchoices[j] * 2;
				newchoices = (char **)emalloc(newsize * (sizeof (char *)),
					el_tempcluster);
				if (newchoices == 0)
				{
					fprintf(stderr, _("Cannot make choice list for dialog\n"));
					return;
				}
				for(k=0; k<numchoices[j]; k++)
					newchoices[k] = choices[j][k];
				efree((char *)choices[j]);
				choices[j] = newchoices;
				totalchoices[j] = newsize;
			}
			choices[j][numchoices[j]] = (char *)emalloc(strlen(current[j])+1,
				el_tempcluster);
			if (choices[j][numchoices[j]] == 0)
			{
				fprintf(stderr, _("Cannot make choice entry for dialog\n"));
				return;
			}
			strcpy(choices[j][numchoices[j]], current[j]);
			numchoices[j]++;
		}
	}

	/* sort the choice lists */
	for(j=0; j<14; j++)
	{
		switch (j)
		{
			case 6:
			case 7:
			case 8:
			case 9:
			case 11:
				esort(&choices[j][1], numchoices[j]-1, sizeof (char *), gra_sortnumbereascending);
				break;
			default:
				esort(&choices[j][1], numchoices[j]-1, sizeof (char *), sort_stringascending);
				break;
		}
	}
	DiaSelectLine(DMSF_FONTLIST, -1);
}

int gra_sortnumbereascending(const void *e1, const void *e2)
{
	REGISTER char *c1, *c2;

	c1 = *((char **)e1);
	c2 = *((char **)e2);
	return(atoi(c1) - atoi(c2));
}

/*
 * routine to cut text from the messages window if it is current.  Returns true
 * if sucessful.
 */
BOOLEAN cutfrommessages(void)
{
#ifdef USETIF
	/* stop now if messages window is not on top */
	if (!gra_msgontop) return(FALSE);

	XmTextCut(gra_messageswidget, CurrentTime);
#else
	INTBIG length, i;
	XawTextPosition pos1, pos2;
	unsigned char *str;
	Arg arg;
	XawTextBlock tb;

	/* stop now if messages window is not on top */
	if (!gra_msgontop) return(FALSE);

	/* store selection in cut buffer */
	XawTextGetSelectionPos(gra_messageswidget, &pos1, &pos2);
	length = pos2 - pos1;
	if (length <= 0) return(FALSE);
	XtSetArg(arg, XtNstring, &str);
	XtGetValues(gra_messageswidget, &arg, 1);
	(void)initinfstr();
	for(i=pos1; i<pos2; i++) (void)addtoinfstr(str[i]);
	setcutbuffer(returninfstr());

	/* remove selected text */
	tb.firstPos = 0;
	tb.length = 0;
	tb.ptr = 0;
	tb.format = FMT8BIT;
	XawTextReplace(gra_messageswidget, pos1, pos2, &tb);
#endif
	return(TRUE);
}

/*
 * routine to copy text from the messages window if it is current.  Returns true
 * if sucessful.
 */
BOOLEAN copyfrommessages(void)
{
#ifdef USETIF
	/* stop now if messages window is not on top */
	if (!gra_msgontop) return(FALSE);

	XmTextCopy(gra_messageswidget, CurrentTime);
#else
	INTBIG length, i;
	XawTextPosition pos1, pos2;
	unsigned char *str;
	Arg arg;

	/* stop now if messages window is not on top */
	if (!gra_msgontop) return(FALSE);

	/* store selection in cut buffer */
	XawTextGetSelectionPos(gra_messageswidget, &pos1, &pos2);
	length = pos2 - pos1;
	if (length <= 0) return(FALSE);
	XtSetArg(arg, XtNstring, &str);
	XtGetValues(gra_messageswidget, &arg, 1);
	(void)initinfstr();
	for(i=pos1; i<pos2; i++) (void)addtoinfstr(str[i]);
	setcutbuffer(returninfstr());
#endif
	return(TRUE);
}

/*
 * routine to paste text to the messages window if it is current.  Returns true
 * if sucessful.
 */
BOOLEAN pastetomessages(void)
{
#ifdef USETIF
	/* stop now if messages window is not on top */
	if (!gra_msgontop) return(FALSE);

	XmTextPaste(gra_messageswidget);
#else
	char *string;
	XawTextBlock tb;
	XawTextPosition pos1, pos2;

	/* stop now if messages window is not on top */
	if (!gra_msgontop) return(FALSE);

	/* get cut buffer */
	string = getcutbuffer();

	/* insert cut buffer into messages window */
	tb.firstPos = 0;
	tb.length = strlen(string);
	tb.ptr = string;
	tb.format = FMT8BIT;
	XawTextGetSelectionPos(gra_messageswidget, &pos1, &pos2);
	XawTextReplace(gra_messageswidget, pos1, pos2, &tb);
	XawTextSetInsertionPoint(gra_messageswidget, pos1+tb.length);
#endif
	return(TRUE);
}

/*
 * routine to get the contents of the system cut buffer
 */
char *getcutbuffer(void)
{
	int returned;
	char *string;

	/* get cut buffer */
	string = XFetchBuffer(gra_topmsgdpy, &returned, 0);
	(void)initinfstr();
	if (*string != 0)
	{
		(void)addstringtoinfstr(string);
		XFree(string);
	}
	return(returninfstr());
}
        
/*
 * routine to set the contents of the system cut buffer to "msg"
 */
void setcutbuffer(char *msg)
{
	XStoreBuffer(gra_topmsgdpy, msg, strlen(msg), 0);
}

/*
 * Routine to clear all text from the messages window.
 */
void clearmessageswindow(void)
{
#ifdef USETIF
	XmTextPosition tp;

	tp = XmTextGetLastPosition(gra_messageswidget);
	XmTextSetSelection(gra_messageswidget, 0, tp, 0);
	XmTextRemove(gra_messageswidget);
#else
	char clr[1];
	XawTextBlock tb;

	tb.firstPos = 0;
	tb.length = 0;
	clr[0] = 0;
	tb.ptr = clr;
	tb.format = FMT8BIT;
	XawTextReplace(gra_messageswidget, 0, MESSAGEBUFFERSIZE, &tb);
#endif
}

/******************** GRAPHICS CONTROL ROUTINES ********************/

void flushscreen(void)
{
#ifdef ANYDEPTH
	REGISTER INTBIG x, y, lx, hx, ly, hy;
	REGISTER unsigned char *srow, *drow, *sval;
	REGISTER char **srowstart, **drowstart;
	REGISTER INTBIG *colorvalue;
	REGISTER WINDOWFRAME *wf;
	REGISTER WINDOWDISPLAY *wd;
	GC gc;
	XGCValues gcv;

	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		/* nothing to do if no changes have been made to offscreen buffer */
		if (wf->offscreendirty == 0) continue;
		wf->offscreendirty = 0;

		/* get bounds of display update */
		lx = wf->copyleft;   hx = wf->copyright;
		ly = wf->copytop;    hy = wf->copybottom;
		if (lx < 0) lx = 0;
		if (hx > wf->swid) hx = wf->swid;
		if (ly < 0) ly = 0;
		if (hy > wf->trueheight) hy = wf->trueheight;

		/* copy from offscreen buffer to Ximage while doing color mapping */
		wd = wf->wd;
		srowstart = wf->rowstart;
		colorvalue = wd->colorvalue;
		drowstart = wd->rowstart;
		switch (wd->depth)
		{
			case 8:
				for(y=ly; y<hy; y++)
				{
					srow = (unsigned char *)(srowstart[y] + lx);
					drow = (unsigned char *)(drowstart[y] + lx);
					for(x=lx; x<hx; x++)
						*drow++ = colorvalue[*srow++];
				}
				break;

			case 16:
				for(y=ly; y<hy; y++)
				{
					srow = (unsigned char *)(srowstart[y] + lx);
					drow = (unsigned char *)(drowstart[y] + (lx << 1));
					for(x=lx; x<hx; x++)
					{
						sval = (unsigned char *)&colorvalue[*srow++];
						*drow++ = sval[0];
						*drow++ = sval[1];
					}
				}
				break;

			case 24:
			case 32:
				for(y=ly; y<hy; y++)
				{
					srow = (unsigned char *)(srowstart[y] + lx);
					drow = (unsigned char *)(drowstart[y] + (lx << 2));
					for(x=lx; x<hx; x++)
					{
						sval = (unsigned char *)&colorvalue[*srow++];
#ifdef BYTES_SWAPPED
						*drow++ = sval[0];
						*drow++ = sval[1];
						*drow++ = sval[2];
#else
						*drow++ = sval[2];
						*drow++ = sval[1];
						*drow++ = sval[0];
#endif
						*drow++ = sval[3];
					}
				}
				break;
		}

		/* copy XImage to the screen */
		gcv.foreground = BlackPixel(wf->topdpy, DefaultScreen(wf->topdpy));
		gc = XtGetGC(wf->graphicswidget, GCForeground, &gcv);
		XPutImage(wf->topdpy, wf->win, gc, wd->image, lx, ly, lx, ly, hx-lx, hy-ly);
		XtReleaseGC(wf->graphicswidget, gc);
		XFlush(wf->topdpy);
	}
#endif
}

/*
 * Routine to mark an area of the offscreen buffer as "changed".
 */
void gra_setrect(WINDOWFRAME *wf, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy)
{
	UINTBIG thistime;

	thistime = ticktime();
	if (wf->offscreendirty == 0)
	{
		wf->copyleft = lx;   wf->copyright = hx;
		wf->copytop = ly;    wf->copybottom = hy;
		wf->offscreendirty = 1;
		wf->starttime = thistime;
	} else
	{
		if (lx < wf->copyleft) wf->copyleft = lx;
		if (hx > wf->copyright) wf->copyright = hx;
		if (ly < wf->copytop) wf->copytop = ly;
		if (hy > wf->copybottom) wf->copybottom = hy;
	}

	/* flush the screen every two seconds */
	if (thistime - wf->starttime > FLUSHTICKS) flushscreen();
}

void gra_getdisplaydepth(Display *dpy, INTBIG *depth, INTBIG *truedepth)
{
	*depth = DefaultDepth(dpy, DefaultScreen(dpy));
	*truedepth = *depth;
	if (*truedepth == 24) *truedepth = 32;
}

BOOLEAN gra_makewindowdisplaybuffer(WINDOWDISPLAY *wd, Display *dpy)
{
	INTBIG amt, i, scr;
	Visual *visual;

	/* get information about this display */
	gra_getdisplaydepth(dpy, &wd->depth, &wd->truedepth);
	scr = DefaultScreen(dpy);
	wd->wid = DisplayWidth(dpy, scr);
	wd->hei = DisplayHeight(dpy, scr);
	visual = DefaultVisual(dpy, scr);

	/* allocate data for ximage */
	amt = wd->wid * wd->hei * wd->truedepth / 8;
	wd->addr = (char *)emalloc(amt, us_tool->cluster);
	if (wd->addr == 0)
	{
		fprintf(stderr, _("Error allocating %ld image array\n"), amt);
		return(TRUE);
	}

	/* make the ximage */
	wd->image = XCreateImage(gra_maindpy, visual, wd->depth, ZPixmap,
		0, wd->addr, wd->wid, wd->hei, wd->truedepth, 0);
	if (wd->image == 0)
	{
		fprintf(stderr, _("Error allocating %ldx%ld image array\n"), wd->wid, wd->hei);
		return(TRUE);
	}

	/* make the row-start pointers for this image */
	wd->rowstart = (char **)emalloc(wd->hei * SIZEOFINTBIG, us_tool->cluster);
	if (wd->rowstart == 0)
	{
		fprintf(stderr, _("Error allocating %ld-long row pointers\n"), wd->hei);
		return(TRUE);
	}
	for(i=0; i<wd->hei; i++)
		wd->rowstart[i] = wd->addr + i * wd->image->bytes_per_line;
	return(FALSE);
}

/*
 * Routine to change the default cursor (to indicate modes).
 */
void setnormalcursor(INTBIG curs)
{
	us_normalcursor = curs;
	setdefaultcursortype(us_normalcursor);
}

void gra_reloadmap(void)
{
	REGISTER VARIABLE *varred, *vargreen, *varblue;

	varred = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
	varblue = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
	if (varred == NOVARIABLE || vargreen == NOVARIABLE || varblue == NOVARIABLE) return;
	colormapload((INTBIG *)varred->addr, (INTBIG *)vargreen->addr, (INTBIG *)varblue->addr, 0, 255);
}

void colormapload(INTBIG *red, INTBIG *green, INTBIG *blue, INTBIG low, INTBIG high)
{
#ifdef ANYDEPTH
	REGISTER INTBIG i;
	REGISTER INTBIG r, g, b, depth;
	XColor xc;
	REGISTER WINDOWFRAME *wf;
	Visual *visual;

	/* clip to the number of map entries requested */
	if (high >= el_maplength) high = el_maplength - 1;

	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		visual = DefaultVisual(wf->topdpy, DefaultScreen(wf->topdpy));
		for(i=low; i<=high; i++)
		{
			xc.flags = DoRed | DoGreen | DoBlue;
			r = red[i-low];
			g = green[i-low];
			b = blue[i-low];

			/* always complement the highest color */
			if (low < 255 && i == 255)
			{
				r = 255 - red[i-low];
				g = 255 - green[i-low];
				b = 255 - blue[i-low];
			}

			/* for 16bpp, crop values to 5 bits */
			xc.red   = r << 8;
			xc.green = g << 8;
			xc.blue  = b << 8;

			/* if this is color-mapped, get closest entry in that map */
			if (VisualClass(visual) == PseudoColor ||
				VisualClass(visual) == StaticColor)
			{
				XAllocColor(wf->topdpy, wf->colormap, &xc);
				wf->wd->colorvalue[i] = xc.pixel;
			} else
			{
				depth = DefaultDepth(wf->topdpy, DefaultScreen(wf->topdpy));
				if (depth == 16)
				{
					wf->wd->colorvalue[i] = ((r & 0xF8) << 8) |
						((g & 0xF8) << 3) | ((b & 0xF8) >> 3);
				} else
				{
					wf->wd->colorvalue[i] = (b << 16) | (g << 8) | r;
				}
			}
		}

		/* mark the entire screen for redrawing */
		if (low == 0 && high == 255)
		{
			gra_setrect(wf, 0, wf->swid, 0, wf->trueheight);
			flushscreen();
		}

		/* set the cursor color */
		if (low == 0 || (low <= el_colcursor && high >= el_colcursor))
		{
			gra_xfc.flags = gra_xbc.flags = DoRed | DoGreen | DoBlue;
			gra_xbc.red = red[0] << 8;
			gra_xbc.green = green[0] << 8;
			gra_xbc.blue = blue[0] << 8;
			gra_xfc.red = red[el_colcursor] << 8;
			gra_xfc.green = green[el_colcursor] << 8;
			gra_xfc.blue = blue[el_colcursor] << 8;
			gra_recolorcursor(wf->topdpy, &gra_xfc, &gra_xbc);
		}
	}
#else
	REGISTER INTBIG i;
	XColor xc[256];
	REGISTER WINDOWFRAME *wf;

	/* clip to the number of map entries requested */
	if (high > gra_maphigh) high = gra_maphigh;

	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		for(i=low; i<=high; i++)
		{
			xc[i-low].flags = DoRed | DoGreen | DoBlue;
			xc[i-low].pixel = i;
			xc[i-low].red   =   red[i-low] << 8;
			xc[i-low].green = green[i-low] << 8;
			xc[i-low].blue  =  blue[i-low] << 8;

			/* always complement the highest color */
			if (low < 255 && i == 255)
			{
				xc[i-low].red   = (255 -   red[i-low]) << 8;
				xc[i-low].green = (255 - green[i-low]) << 8;
				xc[i-low].blue  = (255 -  blue[i-low]) << 8;
			}
		}

		XStoreColors(wf->topdpy, wf->colormap, xc, high-low + 1);

		if (low == 0 || (low <= el_colcursor && high >= el_colcursor))
		{
			gra_xfc.pixel = el_colcursor;
			XQueryColor(wf->topdpy, wf->colormap, &gra_xfc);
			gra_xbc.pixel = 0;
			XQueryColor(wf->topdpy, wf->colormap, &gra_xbc);
			gra_recolorcursor(wf->topdpy, &gra_xfc, &gra_xbc);
		}
	}
#endif
}

/*
 * Helper routine to change the cursor color.
 * fg and bg are the XColor structures to be used. We will look up the current
 * values for the cursor and the background from the installed color map, and
 * copy them into bg and fg.
 */
void gra_recolorcursor(Display *dpy, XColor *fg, XColor *bg)
{
	XRecolorCursor(dpy, gra_realcursor, fg, bg);
	XRecolorCursor(dpy, gra_nomousecursor, fg, bg);
	XRecolorCursor(dpy, gra_drawcursor, fg, bg);
	XRecolorCursor(dpy, gra_nullcursor, fg, bg);
	XRecolorCursor(dpy, gra_menucursor, fg, bg);
	XRecolorCursor(dpy, gra_handcursor, fg, bg);
	XRecolorCursor(dpy, gra_techcursor, fg, bg);
	XRecolorCursor(dpy, gra_ibeamcursor, fg, bg);
	XRecolorCursor(dpy, gra_lrcursor, fg, bg);
	XRecolorCursor(dpy, gra_udcursor, fg, bg);
}

#ifndef ANYDEPTH
/*
 * routine to load the global Graphic Context "wf->gcstip" with the
 * stipple pattern in "raster"
 */
void gra_loadstipple(WINDOWFRAME *wf, UINTSML raster[])
{
	static Pixmap buildup = (Pixmap)NULL;
	static XImage ximage;
	static INTBIG data[8];
	static GC pgc;
	XGCValues gcv;
	REGISTER INTBIG y, pattern, depth, scr;
	static BOOLEAN reverse = FALSE;   /* for debugging and testing */

	/* initialize raster structure for patterned write */
	if (buildup == (Pixmap)NULL)
	{
		buildup = XCreatePixmap(wf->topdpy, wf->win, 32, 8, 1);
		scr = DefaultScreen(wf->topdpy);
		depth = DefaultDepth(wf->topdpy, scr);
		if (depth != 1)
		{
			/* choose polarity of stipples */
#ifdef i386
			gcv.background = BlackPixel(wf->topdpy, scr);
			gcv.foreground = WhitePixel(wf->topdpy, scr);
#else
			gcv.foreground = BlackPixel(wf->topdpy, scr);
			gcv.background = WhitePixel(wf->topdpy, scr);
#endif
		} else
		{
			/* monochrome */
			gcv.foreground = BlackPixel(wf->topdpy, scr);
			gcv.background = WhitePixel(wf->topdpy, scr);
		}
		if (reverse)
		{
			y = gcv.foreground;
			gcv.foreground = gcv.background;
			gcv.background = y;
		}
		gcv.fill_style = FillStippled;
		pgc = XCreateGC(wf->topdpy, buildup, GCForeground | GCBackground | GCFillStyle, &gcv);
		ximage.width = 32;
		ximage.height = 8;
		ximage.xoffset = 0;
		ximage.format = XYBitmap;
		ximage.data = (char *)data;
		ximage.byte_order = XImageByteOrder(wf->topdpy);
		ximage.bitmap_unit = XBitmapUnit(wf->topdpy);
		ximage.bitmap_bit_order = XBitmapBitOrder(wf->topdpy);
		ximage.bitmap_pad = XBitmapPad(wf->topdpy);
		ximage.depth = 1;
		ximage.bytes_per_line = 4;
	}

	for(y=0; y<8; y++)
	{
		pattern = raster[y] & 0xFFFF;
		data[y] = pattern | (pattern << 16);
	}

	XPutImage(wf->topdpy, buildup, pgc, &ximage, 0, 0, 0, 0, 32, 8);
	XSetStipple(wf->topdpy, wf->gcstip, buildup);
}

void gra_setxstate(WINDOWFRAME *wf, GC gc, GRAPHICS *desc)
{
	XSetPlaneMask(wf->topdpy, gc, desc->bits & gra_maphigh);
	XSetForeground(wf->topdpy, gc, desc->col);
}
#endif

/*
 * Helper routine to set the cursor shape to "state".
 */
void setdefaultcursortype(INTBIG state)
{
	WINDOWFRAME *wf;

	if (us_cursorstate == state) return;

	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		switch (state)
		{
			case NORMALCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_realcursor);
				break;
			case WANTTTYCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_nomousecursor);
				break;
			case PENCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_drawcursor);
				break;
			case NULLCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_nullcursor);
				break;
			case MENUCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_menucursor);
				break;
			case HANDCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_handcursor);
				break;
			case TECHCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_techcursor);
				break;
			case IBEAMCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_ibeamcursor);
				break;
			case LRCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_lrcursor);
				break;
			case UDCURSOR:
				XDefineCursor(wf->topdpy, wf->topwin, gra_udcursor);
				break;
		}
	}

	us_cursorstate = state;
}

/******************** GRAPHICS LINES ********************/

/*
 * Routine to draw a line in the graphics window.
 */
void screendrawline(WINDOWPART *win, INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2, GRAPHICS *desc,
	INTBIG texture)
{
#ifdef ANYDEPTH
	gra_drawline(win, x1, y1, x2, y2, desc, texture);
#else
	static INTBIG on[] =  {0, 1, 6, 1};
	static INTBIG off[] = {0, 3, 2, 7};
	static INTBIG linestyle[] = {LineSolid, LineOnOffDash, LineOnOffDash, LineOnOffDash};
	char dashes[2];
	REGISTER WINDOWFRAME *wf;

	/* adjust the co-ordinates for display mapping */
	wf = win->frame;
	y1 = wf->revy - y1;
	y2 = wf->revy - y2;

	gra_setxstate(wf, wf->gc, desc);
#  ifdef sun
	/*
	 * strange OpenWindows bug requires that the width change in order to
	 * also change the line style
	 */
	XSetLineAttributes(wf->topdpy, wf->gc, 1, linestyle[texture], CapButt, JoinMiter);
#  endif
	XSetLineAttributes(wf->topdpy, wf->gc, 0, linestyle[texture], CapButt, JoinMiter);

	if (texture != 0)
	{
		dashes[0] = on[texture];
		dashes[1] = off[texture];
		XSetDashes(wf->topdpy, wf->gc, 0, dashes, 2);
	}

	XDrawLine(wf->topdpy, wf->win, wf->gc, x1, y1, x2, y2);
#endif
}

/*
 * Routine to invert bits of the line in the graphics window
 */
void screeninvertline(WINDOWPART *win, INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2)
{
#ifdef ANYDEPTH
	gra_invertline(win, x1, y1, x2, y2);
#else
	REGISTER WINDOWFRAME *wf;

	/* adjust the co-ordinates for display mapping */
	wf = win->frame;
	y1 = wf->revy - y1;
	y2 = wf->revy - y2;

	XSetPlaneMask(wf->topdpy, wf->gcinv, LAYERH & gra_maphigh);
	XSetLineAttributes(wf->topdpy, wf->gcinv, 0, LineSolid, CapButt, JoinMiter);
	XSetForeground(wf->topdpy, wf->gcinv, HIGHLIT);
	XDrawLine(wf->topdpy, wf->win, wf->gcinv, x1, y1, x2, y2);
#endif
}

/******************** GRAPHICS POLYGONS ********************/

/*
 * Routine to draw a polygon in the graphics window.
 */
void screendrawpolygon(WINDOWPART *win, INTBIG *x, INTBIG *y, INTBIG count, GRAPHICS *desc)
{
#ifdef ANYDEPTH
	gra_drawpolygon(win, x, y, count, desc);
#else
	static XPoint *pointlist;
	static INTBIG pointcount = 0;
	REGISTER INTBIG i;
	REGISTER WINDOWFRAME *wf;

	wf = win->frame;
	if (count > pointcount)
	{
		if (pointcount != 0) efree((char *)pointlist);
		pointcount = 0;
		pointlist = (XPoint *)emalloc(count * (sizeof (XPoint)), us_tool->cluster);
		if (pointlist == 0) return;
		pointcount = count;
	}

	for(i=0; i<count; i++)
	{
		pointlist[i].x = x[i];
		pointlist[i].y = wf->revy - y[i];
	}

	if ((desc->colstyle & NATURE) == PATTERNED && desc->col != 0)
	{
		gra_loadstipple(wf, desc->raster);
		gra_setxstate(wf, wf->gcstip, desc);
		XFillPolygon(wf->topdpy, wf->win, wf->gcstip, pointlist, count, Complex,
			CoordModeOrigin);
	} else
	{
		gra_setxstate(wf, wf->gc, desc);
		XFillPolygon(wf->topdpy, wf->win, wf->gc, pointlist, count, Complex, CoordModeOrigin);
	}
#endif
}

/******************** GRAPHICS BOXES ********************/

/*
 * Routine to draw a box in the graphics window.
 */
void screendrawbox(WINDOWPART *win, INTBIG lowx, INTBIG highx, INTBIG lowy, INTBIG highy,
	GRAPHICS *desc)
{
#ifdef ANYDEPTH
	gra_drawbox(win, lowx, highx, lowy, highy, desc);
#else
	REGISTER WINDOWFRAME *wf;

	/* special code for patterned writes */
	wf = win->frame;
	if ((desc->colstyle & NATURE) == PATTERNED && desc->col != 0)
	{
		gra_loadstipple(wf, desc->raster);
		gra_setxstate(wf, wf->gcstip, desc);
		XFillRectangle(wf->topdpy, wf->win, wf->gcstip, lowx, wf->revy - highy,
			highx - lowx + 1, highy-lowy + 1);
#ifdef sun
		/*
		 * there seems to be a bug in OpenWindows: the right edge of stipple
		 * patterns goes haywire if the pattern is more than 32 pixels wide
		 */
		if (highx - lowx + 1 > 32)
		{
			XFillRectangle(wf->topdpy, wf->win, wf->gcstip, highx - 31,
				wf->revy - highy, 32, highy - lowy + 1);
		}
#endif
	} else
	{
		gra_setxstate(wf, wf->gc, desc);
		XFillRectangle(wf->topdpy, wf->win, wf->gc, lowx, wf->revy - highy,
			highx - lowx + 1, highy - lowy + 1);
	}
#endif
}

/*
 * routine to invert the bits in the box from (lowx, lowy) to (highx, highy)
 */
void screeninvertbox(WINDOWPART *win, INTBIG lowx, INTBIG highx, INTBIG lowy, INTBIG highy)
{
#ifdef ANYDEPTH
	gra_invertbox(win, lowx, highx, lowy, highy);
#else
	REGISTER WINDOWFRAME *wf;

	wf = win->frame;
	XSetPlaneMask(wf->topdpy, wf->gcinv, AllPlanes);
	XFillRectangle(wf->topdpy, wf->win, wf->gcinv, lowx, wf->revy - highy,
		highx - lowx + 1, highy - lowy + 1);
#endif
}

/*
 * routine to move bits on the display starting with the area at
 * (sx,sy) and ending at (dx,dy).  The size of the area to be
 * moved is "wid" by "hei".
 */
void screenmovebox(WINDOWPART *win, INTBIG sx, INTBIG sy, INTBIG wid, INTBIG hei,
	INTBIG dx, INTBIG dy)
{
#ifdef ANYDEPTH
	gra_movebox(win, sx, sy, wid, hei, dx, dy);
#else
	REGISTER WINDOWFRAME *wf;

	/* make sure all bit planes are copied */
	wf = win->frame;
	XSetPlaneMask(wf->topdpy, wf->gc, AllPlanes);

	XCopyArea(wf->topdpy, wf->win, wf->win, wf->gc, sx, wf->revy + 1 - sy - hei,
			  wid, hei, dx, wf->revy + 1 - dy - hei);
#endif
}

/*
 * routine to save the contents of the box from "lx" to "hx" in X and from
 * "ly" to "hy" in Y.  A code is returned that identifies this box for
 * overwriting and restoring.  The routine returns negative if there is a error.
 */
INTBIG screensavebox(WINDOWPART *win, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy)
{
#ifdef ANYDEPTH
	return(gra_savebox(win, lx, hx, ly, hy));
#else
	REGISTER WINDOWFRAME *wf;
	SAVEDBOX *box;
	REGISTER INTBIG i;
	REGISTER INTBIG depth;

	wf = win->frame;
	i = ly;
	ly = wf->revy - hy;
	hy = wf->revy - i;

	box = (SAVEDBOX *)emalloc((sizeof (SAVEDBOX)), us_tool->cluster);
	if (box == 0) return(-1);
	box->win = win;

	depth = DefaultDepth(wf->topdpy, DefaultScreen(wf->topdpy));
	box->pix = XCreatePixmap(wf->topdpy, wf->win, hx - lx + 1, hy - ly + 1, depth);
	if (box->pix == 0) return(-1);

	box->nextsavedbox = gra_firstsavedbox;
	gra_firstsavedbox = box;
	gra_savedboxcodes++;
	box->code = gra_savedboxcodes;
	box->lx = lx;
	box->hx = hx;
	box->ly = ly;
	box->hy = hy;

	/* make sure the whole buffer is accessable */
	XSetPlaneMask(wf->topdpy, wf->gc, AllPlanes);
	XCopyArea(wf->topdpy, wf->win, box->pix, wf->gc, lx, ly, hx - lx + 1, hy - ly + 1, 0, 0);

	return(box->code);
#endif
}

/*
 * routine to shift the saved box "code" so that it is restored in a different
 * lcoation, offset by (dx,dy)
 */
void screenmovesavedbox(INTBIG code, INTBIG dx, INTBIG dy)
{
#ifdef ANYDEPTH
	gra_movesavedbox(code, dx, dy);
#else
	REGISTER SAVEDBOX *box;

	for(box = gra_firstsavedbox; box != NOSAVEDBOX; box = box->nextsavedbox)
		if (box->code == code) break;
	if (box == NOSAVEDBOX) return;
	box->lx += dx;       box->hx += dx;
	box->ly -= dy;       box->hy -= dy;
#endif
}

/*
 * routine to restore saved box "code" to the screen.  "destroy" is:
 *  0   restore box, do not free memory
 *  1   restore box, free memory
 * -1   free memory
 */
void screenrestorebox(INTBIG code, INTBIG destroy)
{
#ifdef ANYDEPTH
	(void)gra_restorebox(code, destroy);
#else
	REGISTER WINDOWFRAME *wf;
	REGISTER SAVEDBOX *box, *lbox;

	/* find the box corresponding to the "code" */
	lbox = NOSAVEDBOX;
	for(box = gra_firstsavedbox; box != NOSAVEDBOX; box = box->nextsavedbox)
	{
		if (box->code == code) break;
		lbox = box;
	}
	if (box == NOSAVEDBOX) return;
	wf = box->win->frame;

	if (destroy >= 0)
	{
		XSetPlaneMask(wf->topdpy, wf->gc, AllPlanes);

		/* restore the bits in the box */
		XCopyArea(wf->topdpy, box->pix, wf->win, wf->gc, 0, 0, box->hx - box->lx + 1,
			box->hy - box->ly + 1, box->lx, box->ly);
	}

	/* destroy this box's memory if requested */
	if (destroy != 0)
	{
		if (lbox == NOSAVEDBOX)
		{
			gra_firstsavedbox = box->nextsavedbox;
		} else
		{
			lbox->nextsavedbox = box->nextsavedbox;
		}
		XFreePixmap(wf->topdpy, box->pix);
		efree((char *)box);
	}
#endif
}

/******************** GRAPHICS TEXT ********************/

/*
 * Routine to return the number of typefaces available on the system
 * and to return their names in the array "list".
 */
INTBIG screengetfacelist(char ***list)
{
#ifdef TRUETYPE
	REGISTER INTBIG i, total, res;

	if (gra_numfaces == 0)
	{
		total = T1_Get_no_fonts();
		gra_numfaces = total + 1;
		gra_facelist = (char **)emalloc(gra_numfaces * (sizeof (char *)), us_tool->cluster);
		if (gra_facelist == 0) return(0);
		(void)allocstring(&gra_facelist[0], _("DEFAULT FACE"), us_tool->cluster);
		for(i=0; i<total; i++)
		{
			res = T1_LoadFont(i);
			(void)allocstring(&gra_facelist[i+1], (res == 0 ? T1_GetFontName(i) : "BROKEN"),
				us_tool->cluster);
		}
	}
	if (gra_numfaces > VTMAXFACE)
	{
		ttyputerr(_("Warning: found %ld fonts, but can only keep track of %d"), gra_numfaces,
			VTMAXFACE);
		gra_numfaces = VTMAXFACE;
	}
	*list = gra_facelist;
	return(gra_numfaces);
#else
	return(0);
#endif
}
        
/*
 * Routine to return the default typeface used on the screen.
 */
char *screengetdefaultfacename(void)
{
#ifdef TRUETYPE
	if (gra_truetypeon != 0)
	{
		T1_LoadFont(gra_truetypedeffont);
		return(T1_GetFontName(gra_truetypedeffont));
	}
#endif
	/* this isn't right!!! */
	return("Helvetica");
}

void screensettextinfo(WINDOWPART *win, TECHNOLOGY *tech, UINTBIG *descript)
{
	REGISTER INTBIG face, size;

	size = TDGETSIZE(descript);
	size = truefontsize(size, win, tech);
	face = TDGETFACE(descript);
	gra_textrotation = TDGETROTATION(descript);
	gra_settextsize(win, size, face, TDGETITALIC(descript), TDGETBOLD(descript),
		TDGETUNDERLINE(descript));
}

void gra_settextsize(WINDOWPART *win, INTBIG size, INTBIG face, INTBIG italic, INTBIG bold, INTBIG underline)
{
#ifdef TRUETYPE
	GLYPH *glyph;
	char **facelist;
	INTBIG i, newsize, *newdcache, *newacache, *newswidth, hash;
#endif

	if (size == TXTMENU)
	{
		gra_curfontnumber = 9;
		size = 10;
	} else if (size == TXTEDITOR)
	{
		gra_curfontnumber = 10;
		size = 12;
	} else
	{
		if (size <= 4) gra_curfontnumber = 0; else
			if (size <= 6) gra_curfontnumber = 1; else
				if (size <= 8) gra_curfontnumber = 2; else
					if (size <= 10) gra_curfontnumber = 3; else
						if (size <= 12) gra_curfontnumber = 4; else
							if (size <= 14) gra_curfontnumber = 5; else
								if (size <= 16) gra_curfontnumber = 6; else
									if (size <= 18) gra_curfontnumber = 7; else
										gra_curfontnumber = 8;
	}
	gra_curfont = gra_font[gra_curfontnumber].font;

#ifdef TRUETYPE
	if (gra_truetypeon == 0) return;

	/* remember TrueType settings */
	if (size < 8) size = 8;
	gra_truetypesize = size;
	if (gra_truetypesize > 500) gra_truetypesize = 500;
	gra_truetypefont = gra_truetypedeffont;
	if (face > 0)
	{
		if (gra_numfaces == 0)
			screengetfacelist(&facelist);
		if (face < gra_numfaces)
			gra_truetypefont = face-1;
	}
	gra_truetypeitalic = italic;
	gra_truetypebold = bold;
	gra_truetypeunderline = underline;

	/* for unusual fonts, remember ascent/descent in hash table */
	if (italic != 0 || bold != 0 || gra_truetypefont != gra_truetypedeffont)
	{
		hash = size + 3*italic + 5*bold + 7*face;
		hash %= FONTHASHSIZE;
		for(i=0; i<FONTHASHSIZE; i++)
		{
			if (gra_fonthash[hash].descent <= 0) break;
			if (gra_fonthash[hash].face == face && gra_fonthash[hash].size == size &&
				gra_fonthash[hash].italic == italic && gra_fonthash[hash].bold == bold)
			{
				gra_truetypeascent = gra_fonthash[hash].ascent;
				gra_truetypedescent = gra_fonthash[hash].descent;
				return;
			}
			hash++;
			if (hash >= FONTHASHSIZE) hash = 0;
		}
		gra_fonthash[hash].face = face;
		gra_fonthash[hash].size = size;
		gra_fonthash[hash].italic = italic;
		gra_fonthash[hash].bold = bold;
		glyph = T1_SetString(gra_truetypefont,
			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
			0, 0, T1_KERNING, gra_truetypesize, 0);
		if (glyph != 0)
		{
			gra_fonthash[hash].ascent = glyph->metrics.ascent;
			gra_fonthash[hash].descent = glyph->metrics.descent;
			gra_fonthash[hash].spacewidth = (glyph->metrics.rightSideBearing - glyph->metrics.leftSideBearing) / 62;
		}
		glyph = T1_SetString(gra_truetypefont, "!@#$%^&?~_+-*/=(){}[]<>|\\`'\",.;:",
			0, 0, T1_KERNING, gra_truetypesize, 0);
		if (glyph != 0)
		{
			if (glyph->metrics.descent < gra_fonthash[hash].descent)
				gra_fonthash[hash].descent = glyph->metrics.descent;
			if (glyph->metrics.ascent > gra_fonthash[hash].ascent)
				gra_fonthash[hash].ascent = glyph->metrics.ascent;
		}
		glyph = T1_SetString(gra_truetypefont, "A", 0, 0, T1_KERNING, gra_truetypesize, 0);
		if (glyph != 0)
		{
			i = glyph->metrics.leftSideBearing;
			glyph = T1_SetString(gra_truetypefont, " A", 0, 0, T1_KERNING, gra_truetypesize, 0);
			if (glyph != 0)
				gra_fonthash[hash].spacewidth = glyph->metrics.leftSideBearing - i;
		}
		gra_truetypeascent = gra_fonthash[hash].ascent;
		gra_truetypedescent = gra_fonthash[hash].descent;
		gra_spacewidth = gra_fonthash[hash].spacewidth;
	} else
	{
		/* standard font: cache the maximum ascent / descent value for each font size */
		if (gra_truetypesize >= gra_descentcachesize)
		{
			newsize = gra_truetypesize + 1;
			newdcache = (INTBIG *)emalloc(newsize * SIZEOFINTBIG, us_tool->cluster);
			newacache = (INTBIG *)emalloc(newsize * SIZEOFINTBIG, us_tool->cluster);
			newswidth = (INTBIG *)emalloc(newsize * SIZEOFINTBIG, us_tool->cluster);
			for(i=0; i<gra_descentcachesize; i++)
			{
				newdcache[i] = gra_descentcache[i];
				newacache[i] = gra_ascentcache[i];
				newswidth[i] = gra_swidthcache[i];
			}
			for(i=gra_descentcachesize; i<newsize; i++)
			{
				newdcache[i] = 1;
				newacache[i] = -1;
				newswidth[i] = 0;
			}
			if (gra_descentcachesize > 0)
			{
				efree((char *)gra_descentcache);
				efree((char *)gra_ascentcache);
				efree((char *)gra_swidthcache);
			}
			gra_descentcache = newdcache;
			gra_ascentcache = newacache;
			gra_swidthcache = newswidth;
			gra_descentcachesize = newsize;
		}
		if (gra_descentcache[gra_truetypesize] > 0)
		{
			glyph = T1_SetString(gra_truetypefont,
				"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
				0, 0, T1_KERNING, gra_truetypesize, 0);
			if (glyph != 0)
			{
				gra_descentcache[gra_truetypesize] = glyph->metrics.descent;
				gra_ascentcache[gra_truetypesize] = glyph->metrics.ascent;
				gra_swidthcache[gra_truetypesize] = (glyph->metrics.rightSideBearing - glyph->metrics.leftSideBearing) / 62;
			}
			glyph = T1_SetString(gra_truetypefont, "!@#$%^&?~_+-*/=(){}[]<>|\\`'\",.;:",
				0, 0, T1_KERNING, gra_truetypesize, 0);
			if (glyph != 0)
			{
				if (glyph->metrics.descent < gra_descentcache[gra_truetypesize])
					gra_descentcache[gra_truetypesize] = glyph->metrics.descent;
				if (glyph->metrics.ascent > gra_ascentcache[gra_truetypesize])
					gra_ascentcache[gra_truetypesize] = glyph->metrics.ascent;
			}
			glyph = T1_SetString(gra_truetypefont, "A", 0, 0, T1_KERNING, gra_truetypesize, 0);
			if (glyph != 0)
			{
				i = glyph->metrics.leftSideBearing;
				glyph = T1_SetString(gra_truetypefont, " A", 0, 0, T1_KERNING, gra_truetypesize, 0);
				if (glyph != 0)
					gra_swidthcache[gra_truetypesize] = glyph->metrics.leftSideBearing - i;
			}
		}
		gra_truetypeascent = gra_ascentcache[gra_truetypesize];
		gra_truetypedescent = gra_descentcache[gra_truetypesize];
		gra_spacewidth = gra_swidthcache[gra_truetypesize];
	}
#endif
}

void screengettextsize(WINDOWPART *win, char *str, INTBIG *x, INTBIG *y)
{
#ifdef ANYDEPTH
	INTBIG thechar, i, width, len;
	INTBIG wid, hei;

	len = strlen(str);
	if (len == 0)
	{
		*x = *y = 0;
		return;
	}

# ifdef TRUETYPE
	if (gra_truetypeon != 0 && gra_truetypesize > 0 && gra_curfontnumber <= 8)
	{
		if (gra_getT1stringsize(str, &wid, &hei) != 0)
		{
			static INTBIG errorcount = 0;

			fprintf(stderr, "Warning: cannot calculate screen size of string '%s' at scale %ld\n",
				str, gra_truetypesize);
			if (errorcount++ > 10)
			{
				fprintf(stderr, "Too many errors, turning off TrueType\n");
				gra_truetypeon = 0;
			}
		} else
		{
			switch (gra_textrotation)
			{
				case 0:			/* normal */
					*x = wid;
					*y = hei;
					break;
				case 1:			/* 90 degrees counterclockwise */
					*x = -hei;
					*y = wid;
					break;
				case 2:			/* 180 degrees */
					*x = -wid;
					*y = -hei;
					break;
				case 3:			/* 90 degrees clockwise */
					*x = hei;
					*y = -wid;
					break;
			}
			return;
		}
	}
# endif

	width = 0;
	for(i=0; i<len; i++)
	{
		thechar = str[i] & 0377;
		width += gra_font[gra_curfontnumber].width[thechar];
	}
	wid = width;
	hei = gra_font[gra_curfontnumber].height[thechar];
	switch (gra_textrotation)
	{
		case 0:			/* normal */
			*x = wid;
			*y = hei;
			break;
		case 1:			/* 90 degrees counterclockwise */
			*x = -hei;
			*y = wid;
			break;
		case 2:			/* 180 degrees */
			*x = -wid;
			*y = -hei;
			break;
		case 3:			/* 90 degrees clockwise */
			*x = hei;
			*y = -wid;
			break;
	}
#else
	int direction, asc, desc, len;   /* must be "int", and not "INTBIG" */
	XCharStruct xcs;

	len = strlen(str);
	XTextExtents(gra_curfont, str, len, &direction, &asc, &desc, &xcs);
	*x = xcs.width;
	*y = gra_curfont->ascent + gra_curfont->descent;
#endif
}

#ifdef TRUETYPE
/*
 * Routine to calculate the TrueType size of string "str" and return it in "wid" and "hei".
 * Returns nonzero on error.
 */
INTBIG gra_getT1stringsize(char *str, INTBIG *wid, INTBIG *hei)
{
	GLYPH *glyph;
	char *startpos, *endpos;
	float slant, extend;
	int modflag, len;
	T1_TMATRIX matrix;

	/* use: gra_truetypeitalic, gra_truetypebold, gra_truetypeunderline */
	modflag = T1_KERNING;
	if (gra_truetypeunderline != 0) modflag |= T1_UNDERLINE;
	*wid = 0;
	for(startpos = str; *startpos != 0; startpos++)
	{
		if (*startpos == ' ')
		{
			*wid += gra_spacewidth;
			continue;
		}
		for(endpos = startpos+1; *endpos != 0; endpos++)
			if (*endpos == ' ') break;
		len = endpos - startpos;
		if (gra_truetypeitalic == 0 && gra_truetypebold == 0)
		{
			glyph = T1_SetString(gra_truetypefont, startpos, len, 0,
				modflag, gra_truetypesize, 0);
		} else
		{
			matrix.cxx = matrix.cyy = 1.0;
			matrix.cyx = matrix.cxy = 0.0;
			if (gra_truetypeitalic == 0) slant = 0.0; else slant = TRUETYPEITALICSLANT;
			(void)T1_ShearHMatrix(&matrix, slant);
			if (gra_truetypebold == 0) extend = 1.0; else extend = TRUETYPEBOLDEXTEND;
			(void)T1_ExtendHMatrix(&matrix, extend);
			glyph = T1_SetString(gra_truetypefont, startpos, len, 0,
				modflag, gra_truetypesize, &matrix);
		}
		startpos = endpos-1;
		if (glyph == 0) return(1);
		*wid += glyph->metrics.rightSideBearing - glyph->metrics.leftSideBearing;
	}
	*hei = gra_truetypeascent - gra_truetypedescent;
	if (*hei < gra_truetypesize) *hei = gra_truetypesize;
	return(0);
}
#endif

/*
 * Routine to draw a text on the graphics window
 */
void screendrawtext(WINDOWPART *win, INTBIG atx, INTBIG aty, char *s, GRAPHICS *desc)
{
#ifdef ANYDEPTH
	gra_drawtext(win, atx, aty, gra_textrotation, s, desc);
#else
	INTBIG l;
	REGISTER WINDOWFRAME *wf;

	wf = win->frame;
	l = strlen(s);

	/* we have to wait for the window to be visible */

	/* special case for highlight layer: always complement the data */
	if (desc->bits == LAYERH)
	{
		XSetPlaneMask(wf->topdpy, wf->gc, AllPlanes);
		XSetFont(wf->topdpy, wf->gcinv, gra_curfont->fid);
		XDrawString(wf->topdpy, wf->win, wf->gcinv, atx,
			wf->revy - aty - gra_curfont->descent, s, l);
		return;
	}

	gra_setxstate(wf, wf->gc, desc);
	XSetFont(wf->topdpy, wf->gc, gra_curfont->fid);
	XDrawString(wf->topdpy, wf->win, wf->gc, atx,
		wf->revy - aty - gra_curfont->descent, s, l);
#endif
}

/*
 * Routine to convert the string "msg" (to be drawn into window "win") into an
 * array of pixels.  The size of the array is returned in "wid" and "hei", and
 * the pixels are returned in an array of character vectors "rowstart".
 * The routine returns true if the operation cannot be done.
 */
BOOLEAN gettextbits(WINDOWPART *win, char *msg, INTBIG *wid, INTBIG *hei, char ***rowstart)
{
#ifdef ANYDEPTH
	REGISTER INTBIG i, len, thechar, x, y, atx, height, width, datasize;
	REGISTER char *ptr, *dest;
# ifdef TRUETYPE
	INTBIG bytesperline, topoffset, trueheight, xpos, xwid;
	REGISTER char *startpos, *endpos;
	GLYPH *glyph;
	float slant, extend;
	int modflag;
	T1_TMATRIX matrix;
# endif

	/* quit if string is null */
	*wid = *hei = 0;
	len = strlen(msg);
	if (len == 0) return(FALSE);

	/* determine the size of the text */
# ifdef TRUETYPE
	if (gra_truetypeon != 0 && gra_truetypesize > 0 && gra_curfontnumber <= 8)
	{
		if (gra_getT1stringsize(msg, wid, hei) != 0) return(TRUE);
	} else
# endif
	{
		for(i=0; i<len; i++)
		{
			thechar = msg[i] & 0377;
			*wid += gra_font[gra_curfontnumber].width[thechar];
			if (gra_font[gra_curfontnumber].height[thechar] > *hei)
				*hei = gra_font[gra_curfontnumber].height[thechar];
		}
	}

	/* allocate space for this */
	datasize = *wid * *hei;
	if (datasize > gra_textbitsdatasize)
	{
		if (gra_textbitsdatasize > 0) efree((char *)gra_textbitsdata);
		gra_textbitsdatasize = 0;

		gra_textbitsdata = (char *)emalloc(datasize, us_tool->cluster);
		if (gra_textbitsdata == 0) return(TRUE);
		gra_textbitsdatasize = datasize;
	}
	if (*hei > gra_textbitsrowstartsize)
	{
		if (gra_textbitsrowstartsize > 0) efree((char *)gra_textbitsrowstart);
		gra_textbitsrowstartsize = 0;
		gra_textbitsrowstart = (char **)emalloc(*hei * (sizeof (char *)), us_tool->cluster);
		if (gra_textbitsrowstart == 0) return(TRUE);
		gra_textbitsrowstartsize = *hei;
	}

	/* load the row start array, clear the data */
	for(y=0; y < *hei; y++)
	{
		gra_textbitsrowstart[y] = &gra_textbitsdata[*wid * y];
		for(x=0; x < *wid; x++)
			gra_textbitsrowstart[y][x] = 0;
	}
	*rowstart = gra_textbitsrowstart;

# ifdef TRUETYPE
	if (gra_truetypeon != 0 && gra_truetypesize > 0 && gra_curfontnumber <= 8)
	{
		/* load the image array */
		modflag = T1_KERNING;
		if (gra_truetypeunderline != 0) modflag |= T1_UNDERLINE;
		xpos = 0;
		for(startpos = msg; *startpos != 0; startpos++)
		{
			if (*startpos == ' ')
			{
				xpos += gra_spacewidth;
				continue;
			}
			for(endpos = startpos+1; *endpos != 0; endpos++)
				if (*endpos == ' ') break;
			len = endpos - startpos;
			if (gra_truetypeitalic == 0 && gra_truetypebold == 0)
			{
				glyph = T1_SetString(gra_truetypefont, startpos, len, 0,
					modflag, gra_truetypesize, 0);
			} else
			{
				matrix.cxx = matrix.cyy = 1.0;
				matrix.cyx = matrix.cxy = 0.0;
				if (gra_truetypeitalic == 0) slant = 0.0; else slant = TRUETYPEITALICSLANT;
				(void)T1_ShearHMatrix(&matrix, slant);
				if (gra_truetypebold == 0) extend = 1.0; else extend = TRUETYPEBOLDEXTEND;
				(void)T1_ExtendHMatrix(&matrix, extend);
				glyph = T1_SetString(gra_truetypefont, startpos, len, 0,
					modflag, gra_truetypesize, &matrix);
			}
			if (glyph == 0) return(TRUE);
			startpos = endpos-1;

			xwid = glyph->metrics.rightSideBearing - glyph->metrics.leftSideBearing;
			*hei = gra_truetypeascent - gra_truetypedescent;
			topoffset = gra_truetypeascent - glyph->metrics.ascent;
			trueheight = glyph->metrics.ascent - glyph->metrics.descent;

			bytesperline = (xwid + 15) / 16 * 2;
			ptr = glyph->bits;
			if (ptr != 0)
			{
				for(y = 0; y < trueheight; y++)
				{
					dest = gra_textbitsrowstart[y+topoffset];
					for(x = 0; x < xwid; x++)
					{
						i = ptr[x>>3] & (1 << (x&7));
						if (i != 0) dest[x+xpos] = 1;
					}
					ptr += bytesperline;
				}
			}
			xpos += xwid;
		}
	} else
# endif
	{
		/* load the image array */
		atx = 0;
		for(i=0; i<len; i++)
		{
			thechar = msg[i] & 0377;
			width = gra_font[gra_curfontnumber].width[thechar];
			height = gra_font[gra_curfontnumber].height[thechar];

			ptr = gra_font[gra_curfontnumber].data[thechar];
			for(y=0; y<height; y++)
			{
				dest = gra_textbitsrowstart[y];
				for(x=0; x<width; x++)
				{
					if (*ptr++ != 0) dest[atx + x] = 1;
				}
			}
			atx += width;
		}
	}
	return(FALSE);
#else
	return(TRUE);
#endif
}

/******************** CIRCLE DRAWING ********************/

/*
 * Routine to draw a circle on the graphics window
 */
void screendrawcircle(WINDOWPART *win, INTBIG atx, INTBIG aty, INTBIG radius, GRAPHICS *desc)
{
#ifdef ANYDEPTH
	gra_drawcircle(win, atx, aty, radius, desc);
#else
	REGISTER WINDOWFRAME *wf;

	wf = win->frame;
	gra_setxstate(wf, wf->gc, desc);
	XDrawArc(wf->topdpy, wf->win, wf->gc, atx - radius, wf->revy - aty - radius,
		radius*2, radius*2, 0, 360*64);
#endif
}

/*
 * Routine to draw a thick circle on the graphics window
 */
void screendrawthickcircle(WINDOWPART *win, INTBIG atx, INTBIG aty, INTBIG radius,
	GRAPHICS *desc)
{
#ifdef ANYDEPTH
	gra_drawthickcircle(win, atx, aty, radius, desc);
#else
	screendrawcircle(win, atx, aty, radius, desc);
#endif
}

/******************** DISC DRAWING ********************/

/*
 * routine to draw a filled-in circle of radius "radius"
 */
void screendrawdisc(WINDOWPART *win, INTBIG atx, INTBIG aty, INTBIG radius, GRAPHICS *desc)
{
#ifdef ANYDEPTH
	gra_drawdisc(win, atx, aty, radius, desc);
#else
	REGISTER WINDOWFRAME *wf;

	wf = win->frame;
	if ((desc->colstyle & NATURE) == PATTERNED && desc->col != 0)
	{
		gra_loadstipple(wf, desc->raster);
		gra_setxstate(wf, wf->gcstip, desc);
		XFillArc(wf->topdpy, wf->win, wf->gcstip, atx - radius,
			wf->revy - aty - radius, radius*2, radius*2, 0, 360*64);
	} else
	{
		gra_setxstate(wf, wf->gc, desc);
		XFillArc(wf->topdpy, wf->win, wf->gc, atx - radius, wf->revy - aty - radius,
			radius*2, radius*2, 0, 360*64);
	}
#endif
}

/******************** ARC DRAWING ********************/

/*
 * draws a thin arc centered at (centerx, centery), clockwise,
 * passing by (x1,y1) and (x2,y2)
 */
void screendrawcirclearc(WINDOWPART *win, INTBIG centerx, INTBIG centery, INTBIG x1, INTBIG y1,
	INTBIG x2, INTBIG y2, GRAPHICS *desc)
{
#ifdef ANYDEPTH
	gra_drawcirclearc(win, centerx, centery, x1, y1, x2, y2, FALSE, desc);
#else
	REGISTER WINDOWFRAME *wf;
	REGISTER INTBIG radius, startangle, endangle;

	wf = win->frame;
	gra_setxstate(wf, wf->gc, desc);
	radius = computedistance(centerx, wf->revy - centery, x1,
		wf->revy - y1);
	startangle = figureangle(centerx, centery, x1, y1);
	endangle = figureangle(centerx, centery, x2, y2);
	if (startangle < endangle) startangle += 3600;
	XDrawArc(wf->topdpy, wf->win, wf->gc, centerx - radius,
		wf->revy - centery - radius, radius*2, radius*2, (endangle*64 + 5) / 10,
		((startangle - endangle)*64 + 5) / 10);
#endif
}

/*
 * draws a thick arc centered at (centerx, centery), clockwise,
 * passing by (x1,y1) and (x2,y2)
 */
void screendrawthickcirclearc(WINDOWPART *win, INTBIG centerx, INTBIG centery, INTBIG p1_x, INTBIG p1_y,
	INTBIG p2_x, INTBIG p2_y, GRAPHICS *desc)
{
#ifdef ANYDEPTH
	gra_drawcirclearc(win, centerx, centery, p1_x, p1_y, p2_x, p2_y, TRUE, desc);
#else
	screendrawcirclearc(win, centerx, centery, p1_x, p1_y, p2_x, p2_y, desc);
#endif
}

/******************** GRID CONTROL ********************/

/*
 * fast grid drawing routine
 */
void screendrawgrid(WINDOWPART *win, POLYGON *obj)
{
#ifdef ANYDEPTH
	gra_drawgrid(win, obj);
#else
	REGISTER WINDOWFRAME *wf;
	REGISTER INTBIG i, gridsize;
	REGISTER INTBIG x, y;
	static Pixmap grid_pix;
	static GC grid_gc;
	XGCValues gcv;
	static INTBIG bestsize = 0;

	/* grid pixrect */
	wf = win->frame;
	gridsize = obj->xv[3] - obj->xv[2];

	/* for some reason, must always reallocate SEEMS OK NOW: SRP */
	if (wf->swid > bestsize)
	{
		if (bestsize != 0)
		{
			XFreePixmap(wf->topdpy, grid_pix);
			XFreeGC(wf->topdpy, grid_gc);
		}
		grid_pix = XCreatePixmap(wf->topdpy, wf->win, bestsize = wf->swid, 1, 1);
		gcv.background = 0;
		gcv.foreground = 0;
		grid_gc = XCreateGC(wf->topdpy, (Drawable)grid_pix, GCBackground | GCForeground, &gcv);
	}

	XSetForeground(wf->topdpy, grid_gc, 0);
	XFillRectangle(wf->topdpy, (Drawable)grid_pix, grid_gc, 0, 0, bestsize, 1);

	/* calculate the horizontal dots position */
	XSetForeground(wf->topdpy, grid_gc, 1);
	for(i = obj->xv[1]; i < obj->xv[5]; i += obj->xv[0])
	{
		x = muldiv(i - obj->xv[4], gridsize, obj->xv[5] - obj->xv[4]) + obj->xv[2];
		XDrawPoint(wf->topdpy, (Drawable)grid_pix, grid_gc, x, 0);
	}

	/* draw the vertical columns */
	XSetStipple(wf->topdpy, wf->gcstip, grid_pix);
	gra_setxstate(wf, wf->gcstip, obj->desc);
	for(i = obj->yv[1]; i < obj->yv[5]; i += obj->yv[0])
	{
		y = muldiv(i - obj->yv[4], obj->yv[3] - obj->yv[2], obj->yv[5] - obj->yv[4]) +
			obj->yv[2];
		XFillRectangle(wf->topdpy, wf->win, wf->gcstip, obj->xv[2],
			wf->revy - y, gridsize, 1);
	}
#endif
}

/******************** MOUSE CONTROL ********************/

/*
 * routine to return the number of buttons on the mouse
 */
INTBIG buttoncount(void)
{
	return(mini(BUTTONS, NUMBUTS));
}

/*
 * routine to tell whether button "but" is a double-click
 */
BOOLEAN doublebutton(INTBIG b)
{
	if (b >= BUTTONS - REALBUTS) return(TRUE);
	return(FALSE);
}

/*
 * routine to tell whether button "but" has the "shift" key held
 */
BOOLEAN shiftbutton(INTBIG b)
{
	b = b / REALBUTS;

	/* this "switch" statement is taken from the array "gra_buttonname" */
	switch (b)
	{
		case 0: return(FALSE);	/* no shift keys */
		case 1: return(TRUE);	/* shift */
		case 2: return(FALSE);	/* control */
		case 3: return(FALSE);	/* alt */
		case 4: return(TRUE);	/* shift-control */
		case 5: return(TRUE);	/* shift-alt */
		case 6: return(FALSE);	/* control-alt */
		case 7: return(TRUE);	/* shift-control-alt */
		case 8: return(FALSE);	/* double-click */
	}
	return(FALSE);
}

/*
 * routine to tell whether button "but" is a "mouse wheel" button
 */
BOOLEAN wheelbutton(INTBIG b)
{
	b = b % REALBUTS;
	if (b == 3 || b == 4) return(TRUE);
	return(FALSE);
}

/*
 * routine to return the name of button "b" (from 0 to "buttoncount()").
 * The number of letters unique to the button is placed in "important".
 */
char *buttonname(INTBIG b, INTBIG *important)
{
	*important = gra_buttonname[b].unique;
	return(gra_buttonname[b].name);
}

/*
 * routine to convert from "gra_inputstate" (the typical input parameter)
 * to button numbers (the table "gra_buttonname")
 */
INTBIG gra_makebutton(INTBIG state)
{
	REGISTER INTBIG base;

	switch (state&ISBUTTON)
	{
		case ISLEFT:    base = 0;   break;
		case ISMIDDLE:  base = 1;   break;
		case ISRIGHT:   base = 2;   break;
		case ISWHLFWD:  base = 3;   break;
		case ISWHLBKW:  base = 4;   break;
	}

	if ((state&DOUBLECL) != 0) return(base+REALBUTS*8);
	switch (state & (SHIFTISDOWN|CONTROLISDOWN|METAISDOWN))
	{
		case SHIFTISDOWN                         : return(base+REALBUTS*1);
		case             CONTROLISDOWN           : return(base+REALBUTS*2);
		case                           METAISDOWN: return(base+REALBUTS*3);
		case SHIFTISDOWN|CONTROLISDOWN           : return(base+REALBUTS*4);
		case SHIFTISDOWN|              METAISDOWN: return(base+REALBUTS*5);
		case             CONTROLISDOWN|METAISDOWN: return(base+REALBUTS*6);
		case SHIFTISDOWN|CONTROLISDOWN|METAISDOWN: return(base+REALBUTS*7);
	}
	return(base);
}

/*
 * routine to wait for a button push and return its index (0 based) in "*but".
 * The coordinates of the cursor are placed in "*x" and "*y".  If there is no
 * button push, the value of "*but" is negative.
 */
void waitforbutton(INTBIG *x, INTBIG *y, INTBIG *but)
{
	/* now the first button wait has happened: graphics is inited */
	gra_firstbuttonwait = 1;

	/* if there is a button push pending, return it */
	if ((gra_inputstate != NOEVENT) && ((gra_inputstate & ISBUTTON) != 0) &&
		((gra_inputstate & BUTTONUP) == 0))
	{
		*but = gra_makebutton(gra_inputstate);
		*x = gra_cursorx;
		*y = gra_cursory;
		gra_inputstate = NOEVENT;
		if (us_cursorstate != IBEAMCURSOR) setdefaultcursortype(us_normalcursor);
		return;
	}

	/* get some input (keyboard or mouse) */
	gra_nextevent();

	/* if a button push was read, return it */
	if ((gra_inputstate != NOEVENT) && ((gra_inputstate & ISBUTTON) != 0) &&
		((gra_inputstate & BUTTONUP) == 0))
	{
		*but = gra_makebutton(gra_inputstate);
		*x = gra_cursorx;
		*y = gra_cursory;
		gra_inputstate = NOEVENT;
		if (us_cursorstate != IBEAMCURSOR) setdefaultcursortype(us_normalcursor);
		return;
	}

	/* no button input yet */
	*but = -1;
}

/*
 * routine to do modal loop, calling "charhandler" for each typed key and "buttonhandler"
 * for each pushed button. The "charhandler" routine is called with the character value
 * that was typed. The "buttonhandler" routine is called with coordinates of cursor.
 * The "charhandler" and "buttonhandler" returns true to abort loop.
 * The value of "cursor" determines cursor appearance.
 */
void modalloop(BOOLEAN (*charhandler)(INTSML chr, INTBIG special),
	BOOLEAN (*buttonhandler)(INTBIG x, INTBIG y, INTBIG but), INTBIG cursor)
{
	INTBIG oldnormalcursor, special, x, y, but;
	INTSML chr;

	/* set cursor */
	oldnormalcursor = us_normalcursor;
	setnormalcursor(cursor);

	for (;;)
	{
		if (ttydataready())
		{
			chr = getnxtchar(&special);
			if ((*charhandler)(chr, special)) break;
		} else
		{
			waitforbutton(&x, &y, &but);
			if (but >= 0 && buttonhandler != 0)
				if ((*buttonhandler)(x, y, but)) break;
		}
	}

	/* restore cursor */
	setnormalcursor(oldnormalcursor);
}

/*
 * routine to track the cursor until a button is released, calling "whileup" for
 * each co-ordinate when the mouse moves before the first button push, calling
 * "whendown" once when the button goes down, calling "eachdown" for each
 * co-ordinate when the mouse moves after the button is pushed, calling
 * "eachchar" for each key that is typed at any time, and calling "done" once
 * when done.  The "whendown" and "done" routines are called with no parameters;
 * "whileup" and "eachdown" are called with the X and Y coordinates of the
 * cursor; and "eachchar" is called with the X, Y, and character value that was
 * typed.  The "whileup", "eachdown", and "eachchar" routines return nonzero to
 * abort tracking.
 * If "waitforpush" is nonzero then the routine will wait for a button to
 * actually be pushed before tracking (otherwise it will begin tracking
 * immediately).  The value of "purpose" determines what the cursor will look
 * like during dragging: 0 for normal (the X cursor), 1 for drawing (a pen),
 * 2 for dragging (a hand), 3 for popup menu selection (a horizontal arrow), 4 for
 * hierarchical popup menu selection (arrow, stays at end).
 */
void trackcursor(BOOLEAN waitforpush, BOOLEAN (*whileup)(INTBIG, INTBIG),
	void (*whendown)(void), BOOLEAN (*eachdown)(INTBIG, INTBIG),
	BOOLEAN (*eachchar)(INTBIG, INTBIG, INTSML), void (*done)(void), INTBIG purpose)
{
	REGISTER BOOLEAN keepon;
	REGISTER INTBIG oldcursor, action;

	/* change the cursor to an appropriate icon */
	oldcursor = us_normalcursor;
	switch (purpose)
	{
		case TRACKDRAWING:    us_normalcursor = PENCURSOR;    break;
		case TRACKDRAGGING:   us_normalcursor = HANDCURSOR;   break;
		case TRACKSELECTING:
		case TRACKHSELECTING: us_normalcursor = MENUCURSOR;   break;
	}
	setdefaultcursortype(us_normalcursor);

	/* now wait for a button to go down, if requested */
	keepon = FALSE;
	if (waitforpush != 0)
	{
		while (!keepon)
		{
			gra_nextevent();
			if (gra_inputstate == NOEVENT) continue;
			action = gra_inputstate;
			gra_inputstate = NOEVENT;

			/* if button just went down, stop this loop */
			if ((action&ISBUTTON) != 0 && (action & BUTTONUP) == 0) break;
			if ((action&MOTION) != 0)
			{
				keepon = (*whileup)(gra_cursorx, gra_cursory);
			} else if ((action&ISKEYSTROKE) != 0 && gra_inputspecial == 0)
			{
				keepon = (*eachchar)(gra_cursorx, gra_cursory, (INTSML)(action&CHARREAD));
			}
		}
	}

	/* button is now down, real tracking begins */
	if (!keepon)
	{
		(*whendown)();
		keepon = (*eachdown)(gra_cursorx, gra_cursory);
	}

	/* now track while the button is down */
	while (!keepon)
	{
		gra_nextevent();

		/* for each motion, report the coordinates */
		if (gra_inputstate == NOEVENT) continue;
		action = gra_inputstate;
		gra_inputstate = NOEVENT;
		if ((action&ISBUTTON) != 0 && (action&BUTTONUP) != 0) break;
		if ((action&MOTION) != 0)
		{
			keepon = (*eachdown)(gra_cursorx, gra_cursory);
		} else if ((action&ISKEYSTROKE) != 0 && gra_inputspecial == 0)
		{
			keepon = (*eachchar)(gra_cursorx, gra_cursory, (INTSML)(action&CHARREAD));
		}
		if (el_pleasestop != 0) keepon = TRUE;
	}

	/* inform the user that all is done */
	(*done)();

	/* restore the state of the world */
	us_normalcursor = oldcursor;
	setdefaultcursortype(us_normalcursor);
}

/*
 * routine to read the current co-ordinates of the tablet and return them in "*x" and "*y"
 */
void readtablet(INTBIG *x, INTBIG *y)
{
	*x = gra_cursorx;
	*y = gra_cursory;
}

/*
 * routine to turn off the cursor tracking if it is on
 */
void stoptablet(void)
{
	if (us_cursorstate != IBEAMCURSOR) setdefaultcursortype(us_normalcursor);
}

/******************** KEYBOARD CONTROL ********************/

/*
 * routine to get the next character from the keyboard
 */
INTSML getnxtchar(INTBIG *special)
{
	REGISTER INTSML i;

	if ((gra_inputstate != NOEVENT) && ((gra_inputstate & ISKEYSTROKE) != 0))
	{
		i = gra_inputstate & CHARREAD;
		*special = gra_inputspecial;
		gra_inputstate = NOEVENT;
		return(i);
	}
	if (us_cursorstate != IBEAMCURSOR)
		setdefaultcursortype(WANTTTYCURSOR);
	for(;;)
	{
		gra_nextevent();
		if ((gra_inputstate != NOEVENT) && (gra_inputstate & ISKEYSTROKE) != 0)
			break;
	}
	i = gra_inputstate & CHARREAD;
	*special = gra_inputspecial;
	gra_inputstate = NOEVENT;
	if (us_cursorstate != IBEAMCURSOR) setdefaultcursortype(us_normalcursor);
	return(i);
}

/* not required with X11 */
void checkforinterrupt(void)
{
	XtInputMask mask;
	XEvent event;

	gra_checkinginterrupt = TRUE;
	mask = XtAppPending(gra_xtapp);
	if ((mask&XtIMXEvent) != 0)
	{
		XtAppNextEvent(gra_xtapp, &event);
		XtDispatchEvent(&event);
	}
	gra_checkinginterrupt = FALSE;
	setdefaultcursortype(NULLCURSOR);
}

#define BIT(c, x)   (c[x>>3] & (1<<(x&7)))

/*
 * Routine to return which "bucky bits" are held down (shift, control, etc.)
 */
INTBIG getbuckybits(void)
{
	REGISTER INTBIG bits, width, i;
	char keys[32];
	static XModifierKeymap *mmap;
	static INTBIG first = 1;
	KeyCode code;

	if (first != 0)
	{
		first = 0;
		mmap = XGetModifierMapping(gra_maindpy);
	}

	bits = 0;

	XQueryKeymap(gra_maindpy, keys);
	width = mmap->max_keypermod;
	for (i=0; i<width; i++)
	{
		code = mmap->modifiermap[ControlMapIndex*width+i];
		if (code != 0 && BIT(keys, code) != 0) bits |= CONTROLDOWN | ACCELERATORDOWN;

		code = mmap->modifiermap[ShiftMapIndex*width+i];
		if (code != 0 && BIT(keys, code) != 0) bits |= SHIFTDOWN;

		code = mmap->modifiermap[Mod1MapIndex*width+i];
		if (code != 0 && BIT(keys, code) != 0) bits |= OPTALTMETDOWN;
	}

	return(bits);
}

/*
 * routine to tell whether data is waiting at the terminal.  Returns true
 * if data is ready.
 */
BOOLEAN ttydataready(void)
{
	/* see if something is already pending */
	if (gra_inputstate != NOEVENT)
	{
		if ((gra_inputstate & ISKEYSTROKE) != 0) return(TRUE);
		return(FALSE);
	}

	/* wait for something and analyze it */
	if (us_cursorstate != IBEAMCURSOR) setdefaultcursortype(us_normalcursor);
	gra_nextevent();
	if ((gra_inputstate != NOEVENT) && (gra_inputstate & ISKEYSTROKE) != 0) return(TRUE);
	return(FALSE);
}

/****************************** FILES ******************************/

BOOLEAN gra_topoffile(char **a)
{
	char *b, **c;
	BOOLEAN retval;

	b = gra_curpath;
	c = &b;
	retval = topoffile(c);
	requiredextension(gra_requiredextension);
	return(retval);
}

#ifdef USETIF
void gra_fileselectcancel(Widget w, XtPointer client_data,
	XmSelectionBoxCallbackStruct *call_data)
{
	gra_curpath[0] = 0;
	gra_fileselectdone = TRUE;
}

void gra_fileselectok(Widget w, XtPointer client_data,
	XmSelectionBoxCallbackStruct *call_data)
{
	char *filename;

	gra_curpath[0] = 0;
	if (XmStringGetLtoR(call_data->value, XmSTRING_DEFAULT_CHARSET,
		&filename))
	{
		strcpy(gra_curpath, filename);
		XtFree(filename);
	}
	gra_fileselectdone = TRUE;
}
#endif

/*
 * Routine to prompt for multiple files of type "filetype", giving the
 * message "msg".  Returns a string that contains all of the file names,
 * separated by the NONFILECH (a character that cannot be in a file name).
 */
char *multifileselectin(char *msg, INTBIG filetype)
{
	return(fileselect(msg, filetype, ""));
}

DIALOGITEM gra_fileindialogitems[] =   /* File Input */
{
 /*  1 */ {0, {128,256,152,336}, BUTTON, N_("Open")},
 /*  2 */ {0, {176,256,200,336}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {40,8,216,240}, SCROLL, ""},
 /*  4 */ {0, {8,8,40,336}, MESSAGE, ""},
 /*  5 */ {0, {80,256,104,336}, BUTTON, N_("Up")}
};
DIALOG gra_fileindialog = {{50,75,278,421}, 0, 0, 5, gra_fileindialogitems};

/* special items for the file input dialog */
#define DFIN_FILELIST    3		/* File list (scroll) */
#define DFIN_FILENAME    4		/* File name (stat text) */
#define DFIN_UPBUTTON    5		/* Up (button) */

DIALOGITEM gra_fileoutdialogitems[] =   /* File Output */
{
 /*  1 */ {0, {146,256,170,336}, BUTTON, N_("OK")},
 /*  2 */ {0, {194,256,218,336}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {50,8,218,240}, SCROLL, ""},
 /*  4 */ {0, {8,8,40,336}, MESSAGE, ""},
 /*  5 */ {0, {50,256,74,336}, BUTTON, N_("Up")},
 /*  6 */ {0, {98,256,122,336}, BUTTON, N_("Down")},
 /*  7 */ {0, {226,8,242,336}, EDITTEXT, ""}
};
DIALOG gra_fileoutdialog = {{50,75,301,421}, 0, 0, 7, gra_fileoutdialogitems};

/* special items for the file output dialog */
#define DFOU_FILELIST    3		/* File list (scroll) */
#define DFOU_DIRNAME     4		/* Directory name (stat text) */
#define DFOU_UPBUTTON    5		/* Up (button) */
#define DFOU_DOWNBUTTON  6		/* Down (button) */
#define DFOU_FILENAME    7		/* File name (edit text) */

/*
 * Routine to display a standard file prompt dialog and return the selected file.
 * The prompt message is in "msg" and the kind of file is in "filetype".  The default
 * output file name is in "defofile" (only used if "filetype" is for output files).
 */
char *fileselect(char *msg, INTBIG filetype, char *defofile)
{
	INTBIG i, l, count;
	INTBIG mactype;
	BOOLEAN binary;
	char *extension, *winfilter, *shortname, *longname, directory[256], fullmsg[300], *pars[5]; 
	extern COMCOMP us_yesnop;
#ifdef USETIF
	XmString mprompt, mfilter, mtitle, mpattern, mdirectory;
	Widget w, child;
	INTBIG wid, hei, ac, screenw, screenh;
	Arg arg[10];
	XEvent event;
	char filename[256], temp[256], *childmsg;
	REGISTER WINDOWFRAME *wf;
#else
	INTBIG itemHit, oldlen;
	char *ths, banner[200];
#endif

	if (us_logplay != NULL)
	{
		if (gra_loggetnextaction(gra_curpath)) return(0);
	} else
	{
		if ((filetype&FILETYPEWRITE) == 0)
		{
			/* input file selection: display the file input dialog box */
#ifdef USETIF
			describefiletype(filetype, &extension, &winfilter, &mactype, &binary, &shortname, &longname);
			sprintf(temp, _("%s Selection"), msg);
			mtitle = XmStringCreateLocalized(temp);
			mprompt = XmStringCreateLocalized(msg);
			strcpy(temp, winfilter);
			for(i=0; temp[i] != 0; i++) if (temp[i] == ';') break;
			temp[i] = 0;
			mfilter = XmStringCreateLocalized(temp);
			wf = el_curwindowframe;
			if (wf == NOWINDOWFRAME) wf = el_firstwindowframe;
			screenw = wf->wd->wid;
			screenh = wf->wd->hei;
			wid = 400;   if (wid > screenw) wid = screenw;
			hei = 480;   if (hei > screenh) hei = screenh;
			strcpy(directory, currentdirectory());
			mdirectory = XmStringCreateLocalized(directory);
			ac = 0;
			XtSetArg(arg[ac], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL);   ac++;
			XtSetArg(arg[ac], XmNdirMask, mfilter);   ac++;
			XtSetArg(arg[ac], XmNselectionLabelString, mprompt);   ac++;
			XtSetArg(arg[ac], XmNwidth, 400);   ac++;
			XtSetArg(arg[ac], XmNheight, 480);   ac++;
			XtSetArg(arg[ac], XmNdialogTitle, mtitle);   ac++;
			XtSetArg(arg[ac], XmNdirectory, mdirectory);   ac++;
			w = XmCreateFileSelectionDialog(wf->graphicswidget, _("File Selection"), arg, ac);
			XtAddCallback(w, XmNcancelCallback, (XtCallbackProc)gra_fileselectcancel, NULL);
			XtAddCallback(w, XmNokCallback, (XtCallbackProc)gra_fileselectok, NULL);
			XtManageChild(w);
			XmStringFree(mprompt);
			XmStringFree(mfilter);
			XmStringFree(mtitle);

			gra_fileselectdone = FALSE;
			while (!gra_fileselectdone)
			{
				XmUpdateDisplay(wf->toplevelwidget);
				XtAppNextEvent(gra_xtapp, &event);
				XtDispatchEvent(&event);
			}
			XtUnmanageChild(w);
			XtDestroyWidget(w);
#else
			describefiletype(filetype, &extension, &winfilter, &mactype, &binary, &shortname, &longname);
			strcpy(gra_requiredextension, extension);
			gra_fileindialog.movable = msg;
			if (DiaInitDialog(&gra_fileindialog)) return(0);
			(void)strcpy(gra_curpath, currentdirectory());
			DiaInitTextDialog(DFIN_FILELIST, gra_topoffile, nextfile, DiaNullDlogDone, 0,
				SCSELKEY|SCSELMOUSE|SCDOUBLEQUIT);
			DiaSetText(DFIN_FILENAME, gra_curpath);

			/* loop until done */
			for(;;)
			{
				itemHit = DiaNextHit();
				if (itemHit == CANCEL) break;
				if (itemHit == OK)
				{
					oldlen = strlen(gra_curpath);
					ths = DiaGetScrollLine(DFIN_FILELIST, DiaGetCurLine(DFIN_FILELIST));
					(void)strcat(gra_curpath, ths);
					if (fileexistence(gra_curpath) != 2) break;
					DiaSetText(DFIN_FILENAME, gra_curpath);
					DiaLoadTextDialog(DFIN_FILELIST, gra_topoffile, nextfile, DiaNullDlogDone, 0);
					continue;
				}
				if (itemHit == DFIN_UPBUTTON)
				{
					l = strlen(gra_curpath);
					if (l > 0 && gra_curpath[l-1] == DIRSEP) l--;
					if (l <= 0) continue;
					for(l--; l>=0; l--) if (gra_curpath[l] == DIRSEP) break;
					gra_curpath[l+1] = 0;
					DiaSetText(DFIN_FILENAME, gra_curpath);
					DiaLoadTextDialog(DFIN_FILELIST, gra_topoffile, nextfile, DiaNullDlogDone, 0);
					continue;
				}
			}

			setdefaultcursortype(WAITCURSOR);
			DiaDoneDialog();
#endif

			/* set current directory from file path */
			l = strlen(gra_curpath);
			for(i=l-1; i>0; i--) if (gra_curpath[i] == DIRSEP) break;
			if (i > 0)
			{
				i++;
				(void)strcpy(directory, gra_curpath);
				directory[i] = 0;
				gra_setcurrentdirectory(directory);
			}
		} else
		{
#ifdef USETIF
			/* determine the default directory and file name */
			strcpy(directory, currentdirectory());
			filename[0] = 0;
			if (defofile[0] != 0)
			{
				l = strlen(defofile);
				for(i=l-1; i>0; i--) if (defofile[i] == DIRSEP) break;
				if (i > 0)
				{
					i++;
					(void)strcpy(directory, defofile);
					directory[i] = 0;
				}
				strcpy(filename, &defofile[i]);
			}

			describefiletype(filetype, &extension, &winfilter, &mactype, &binary, &shortname, &longname);
			wf = el_curwindowframe;
			if (wf == NOWINDOWFRAME) wf = el_firstwindowframe;
			screenw = wf->wd->wid;
			screenh = wf->wd->hei;
			wid = 400;   if (wid > screenw) wid = screenw;
			hei = 480;   if (hei > screenh) hei = screenh;
			mprompt = XmStringCreateLocalized(msg);
			mdirectory = XmStringCreateLocalized(directory);
			strcpy(temp, winfilter);
			for(i=0; temp[i] != 0; i++) if (temp[i] == ';') break;
			temp[i] = 0;
			mpattern = XmStringCreateLocalized(temp);
			sprintf(temp, _("%s Creation"), msg);
			mtitle = XmStringCreateLocalized(temp);
			ac = 0;
			XtSetArg(arg[ac], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL);   ac++;
			XtSetArg(arg[ac], XmNwidth, 400);   ac++;
			XtSetArg(arg[ac], XmNheight, 480);   ac++;
			XtSetArg(arg[ac], XmNselectionLabelString, mprompt);   ac++;
			XtSetArg(arg[ac], XmNdirectory, mdirectory);   ac++;
			XtSetArg(arg[ac], XmNpattern, mpattern);   ac++;
			XtSetArg(arg[ac], XmNdialogTitle, mtitle);   ac++;
			w = XmCreateFileSelectionDialog(wf->graphicswidget, "", arg, ac);
			XtAddCallback(w, XmNcancelCallback, (XtCallbackProc)gra_fileselectcancel, NULL);
			XtAddCallback(w, XmNokCallback, (XtCallbackProc)gra_fileselectok, NULL);
			XtManageChild(w);
			XmStringFree(mprompt);
			XmStringFree(mdirectory);
			XmStringFree(mpattern);
			XmStringFree(mtitle);

			/* append the default output file name to the selection path */
			child = XmFileSelectionBoxGetChild(w, XmDIALOG_TEXT);
			childmsg = XmTextGetString(child);
			sprintf(temp, "%s%s", childmsg, filename);
			XmTextSetString(child, temp);

			gra_fileselectdone = FALSE;
			while (!gra_fileselectdone)
			{
				XmUpdateDisplay(wf->toplevelwidget);
				XtAppNextEvent(gra_xtapp, &event);
				XtDispatchEvent(&event);
			}
			XtUnmanageChild(w);
			XtDestroyWidget(w);
#else
			/* input file selection: display the file input dialog box */
			sprintf(banner, _("Write %s"), msg);
			gra_fileoutdialog.movable = banner;
			if (DiaInitDialog(&gra_fileoutdialog)) return(0);
			(void)strcpy(gra_curpath, currentdirectory());
			if (defofile[0] != 0)
			{
				l = strlen(defofile);
				for(i=l-1; i>0; i--) if (defofile[i] == DIRSEP) break;
				if (i > 0)
				{
					i++;
					(void)strcpy(gra_curpath, defofile);
					gra_curpath[i] = 0;
				}
				DiaSetText(-DFOU_FILENAME, &defofile[i]);
			}
			gra_requiredextension[0] = 0;
			DiaInitTextDialog(DFOU_FILELIST, gra_topoffile, nextfile, DiaNullDlogDone, 0,
				SCSELKEY|SCREPORT|SCSELMOUSE);
			DiaSetText(DFOU_DIRNAME, gra_curpath);

			/* loop until done */
			for(;;)
			{
				itemHit = DiaNextHit();
				if (itemHit == CANCEL) break;
				if (itemHit == OK)
				{
					ths = DiaGetText(DFOU_FILENAME);
					(void)strcat(gra_curpath, ths);
					break;
				}
				if (itemHit == DFOU_FILELIST)
				{
					/* handle selection in the directory list */
					oldlen = strlen(gra_curpath);
					ths = DiaGetScrollLine(DFOU_FILELIST, DiaGetCurLine(DFOU_FILELIST));
					(void)strcat(gra_curpath, ths);
					if (fileexistence(gra_curpath) != 2) DiaSetText(DFOU_FILENAME, ths);
					gra_curpath[oldlen] = 0;
					continue;
				}
				if (itemHit == DFOU_DOWNBUTTON)
				{
					/* go down the directory */
					oldlen = strlen(gra_curpath);
					ths = DiaGetScrollLine(DFOU_FILELIST, DiaGetCurLine(DFOU_FILELIST));
					(void)strcat(gra_curpath, ths);
					if (fileexistence(gra_curpath) != 2) gra_curpath[oldlen] = 0; else
					{
						DiaSetText(DFOU_DIRNAME, gra_curpath);
						DiaLoadTextDialog(DFOU_FILELIST, gra_topoffile, nextfile, DiaNullDlogDone, 0);
					}
					continue;
				}
				if (itemHit == DFOU_UPBUTTON)
				{
					/* go up the directory */
					l = strlen(gra_curpath);
					if (l > 0 && gra_curpath[l-1] == DIRSEP) l--;
					if (l <= 0) continue;
					for(l--; l>=0; l--) if (gra_curpath[l] == DIRSEP) break;
					gra_curpath[l+1] = 0;
					DiaSetText(DFOU_DIRNAME, gra_curpath);
					DiaLoadTextDialog(DFOU_FILELIST, gra_topoffile, nextfile, DiaNullDlogDone, 0);
					continue;
				}
			}

			setdefaultcursortype(WAITCURSOR);
			DiaDoneDialog();
			if (itemHit == CANCEL) gra_curpath[0] = 0;
#endif
			/* set current directory from file path */
			l = strlen(gra_curpath);
			for(i=l-1; i>0; i--) if (gra_curpath[i] == DIRSEP) break;
			if (i > 0)
			{
				i++;
				(void)strcpy(directory, gra_curpath);
				directory[i] = 0;
				gra_setcurrentdirectory(directory);
			}
		}
	}

	/* log this result */
	gra_logwriteaction(FILEREPLY, 0, 0, 0, gra_curpath);

	/* if writing a file, check for overwrite */
	if ((filetype&FILETYPEWRITE) != 0)
	{
		/* give warning if creating file that already exists */
		if (gra_curpath[0] != 0)
		{
			if (fileexistence(gra_curpath) != 0)
			{
				sprintf(fullmsg, _("File '%s' exists.  Overwrite? "), gra_curpath);
				count = ttygetparam(fullmsg, &us_yesnop, 2, pars);
				if (count > 0 && namesamen(pars[0], "no", strlen(pars[0])) == 0)
					gra_curpath[0] = 0;
			}
		}
	}

	if (gra_curpath[0] == 0) return((char *)NULL); else
	{
		return(gra_curpath);
	}
}

/*
 * Helper routine to initialize the list of files in a directory.
 */
void gra_initfilelist(void)
{
	if (gra_fileliststringarray == 0)
	{
		gra_fileliststringarray = newstringarray(db_cluster);
		if (gra_fileliststringarray == 0) return;
	}
	clearstrings(gra_fileliststringarray);
}

/*
 * Helper routine to add "file" to the list of files in a directory.
 * Returns true on error.
 */
BOOLEAN gra_addfiletolist(char *file)
{
	addtostringarray(gra_fileliststringarray, file);
	return(FALSE);
}

int gra_fileselectall(const struct dirent *a)
{
	REGISTER char *pt;

	pt = (char *)a->d_name;
	if (strcmp(pt, "..") == 0) return(0);
	if (strcmp(pt, ".") == 0) return(0);
	return(1);
}

/*
 * Routine to search for all of the files/directories in directory "directory" and
 * return them in the array of strings "filelist".  Returns the number of files found.
 */
INTBIG filesindirectory(char *directory, char ***filelist)
{
	INTBIG len;
	char localname[256];
	struct dirent **filenamelist;
	struct dirent *dir;
	INTBIG total, i;
#ifndef HAVE_SCANDIR
#  define DIRFILECOUNT 2048  /* max number of entries in a directory */
#  ifndef _POSIX_PATH_MAX
#    define _POSIX_PATH_MAX 255
#  endif
	DIR *dirp;
	REGISTER INTBIG tdirsize;
#endif

	/* make sure there is no ending "/" */
	strcpy(localname, directory);
	i = strlen(localname) - 1;
	if (i > 0 && localname[i] == '/') localname[i] = 0;
	if (i == -1) sprintf(localname, ".");

	/* scan the directory */
#ifdef HAVE_SCANDIR
	/*
	 * On BSD, last arg is: "(int (*)(const void *, const void *))0"
	 * On non-BSD, last arg is: "(int (*)(const __ptr_t, const __ptr_t))0"
	 */
	total = scandir(localname, &filenamelist, gra_fileselectall, NULL);
#else
	/* get space for directory */
	filenamelist = (struct dirent **)calloc(DIRFILECOUNT,
		sizeof(struct dirent *));
	if (filenamelist == 0) return(0);
	tdirsize = sizeof (struct dirent);
	dir = (struct dirent *)emalloc(tdirsize + _POSIX_PATH_MAX, us_tool->cluster);
	if (dir == 0) return(0);

	/* get the directory */
	dirp = opendir(localname);
	if (dirp == NULL) return(-1);

	/* construct the list */
	total = 0;
	while ((struct dirent *)readdir_r(dirp, dir) != NULL)
	{
		if (gra_fileselectall(dir))
		{
			filenamelist[total] = (struct dirent *)emalloc(tdirsize + _POSIX_PATH_MAX,
				us_tool->cluster);
			if (filenamelist[total] == 0) { closedir(dirp);   return(0); }
			memcpy(filenamelist[total], dir, sizeof(dir)+_POSIX_PATH_MAX);
			total++;
		}
	}
	closedir(dirp);
#endif

	gra_initfilelist();
	for(i=0; i<total; i++)
	{
		dir = filenamelist[i];
		if (gra_addfiletolist(dir->d_name)) return(0);
		free((char *)dir);
	}
	free((char *)filenamelist);
	*filelist = getstringarray(gra_fileliststringarray, &len);
	return(len);
}

/* routine to convert a path name with "~" to a real path */
char *truepath(char *line)
{
	static char outline[100], home[50];
	REGISTER char save, *ch;
	static BOOLEAN gothome = FALSE;
	struct passwd *tmp;

	if (line[0] != '~') return(line);

	/* if it is the form "~username", figure it out */
	if (line[1] != '/')
	{
		for(ch = &line[1]; *ch != 0 && *ch != '/'; ch++);
		save = *ch;   *ch = 0;
		tmp = getpwnam(&line[1]);
		if (tmp == 0) return(line);
		*ch = save;
		(void)strcpy(outline, tmp->pw_dir);
		(void)strcat(outline, ch);
		return(outline);
	}

	/* get the user's home directory once only */
	if (!gothome)
	{
		/* get name of user */
		tmp = getpwuid(getuid());
		if (tmp == 0) return(line);
		(void)strcpy(home, tmp->pw_dir);
		gothome = TRUE;
	}

	/* substitute home directory for the "~" */
	(void)strcpy(outline, home);
	(void)strcat(outline, &line[1]);
	return(outline);
}

/*
 * Routine to return the full path to file "file".
 */
char *fullfilename(char *file)
{
	static char fullfile[MAXPATHLEN];

	if (file[0] == '/') return(file);
	strcpy(fullfile, currentdirectory());
	strcat(fullfile, file);
	return(fullfile);
}

/*
 * Routine to turn the file name "file" into a unique one by changing the
 * "XXX" part to a number that makes the file unique.
 */
void emaketemp(char *file)
{
	mktemp(file);
}

/*
 * routine to rename file "file" to "newfile"
 * returns nonzero on error
 */
INTBIG erename(char *file, char *newfile)
{
	if (link(file, newfile) < 0) return(1);
	if (unlink(file) < 0) return(1);
	return(0);
}

/*
 * routine to delete file "file"
 */
INTBIG eunlink(char *file)
{
	return(unlink(file));
}

/*
 * Routine to return information about the file or directory "name":
 *  0: does not exist
 *  1: is a file
 *  2: is a directory
 */
INTBIG fileexistence(char *name)
{
	struct stat buf;

	if (stat(name, &buf) < 0) return(0);
	if ((buf.st_mode & S_IFMT) == S_IFDIR) return(2);
	return(1);
}

/*
 * Routine to create a directory.
 * Returns true on error.
 */
BOOLEAN createdirectory(char *dirname)
{
	char cmd[256];

	sprintf(cmd, "mkdir %s", dirname);
	if (system(cmd) == 0) return(FALSE);
	return(TRUE);
}

/*
 * Routine to return the current directory name
 */
char *currentdirectory(void)
{
	static char line[MAXPATHLEN];
	REGISTER INTBIG len;

#ifdef HAVE_GETCWD
	(void)getcwd(line, MAXPATHLEN);
#else
#  ifdef HAVE_GETWD
	(void)getwd(line);
#  else
	line[0] = 0;
#  endif
#endif
	len = strlen(line);
	if (len > 0 && line[len-1] != DIRSEP)
		strcat(line, DIRSEPSTR);
	return(line);
}

/*
 * Routine to set the current directory name
 */
void gra_setcurrentdirectory(char *path)
{
	chdir(path);
}

/*
 * Routine to return the home directory (returns 0 if it doesn't exist)
 */
char *hashomedir(void)
{
	return("~/");
}

/*
 * Routine to return the path to the "options" library.
 */
char *optionsfilepath(void)
{
	return("~/.electricoptions.elib");
}

/*
 * routine to return the date of the last modification to file "filename"
 */
UINTBIG filedate(char *filename)
{
	struct stat buf;

	stat(filename, &buf);
	buf.st_mtime -= machinetimeoffset();
	return(buf.st_mtime);
}

/*
 * Routine to lock a resource called "lockfilename" by creating such a file
 * if it doesn't exist.  Returns true if successful, false if unable to
 * lock the file.
 */
BOOLEAN lockfile(char *lockfilename)
{
	INTBIG fd;

	fd = creat(lockfilename, 0);
	if (fd == -1 && errno == EACCES) return(FALSE);
	if (fd == -1 || close(fd) == -1) return(FALSE);
	return(TRUE);
}

/*
 * Routine to unlock a resource called "lockfilename" by deleting such a file.
 */
void unlockfile(char *lockfilename)
{
	if (unlink(lockfilename) == -1)
		ttyputerr(_("Error unlocking %s"), lockfilename);
}

/*
 * Routine to show file "filename" in a browser window.
 * Returns true if the operation cannot be done.
 */
BOOLEAN browsefile(char *filename)
{
	char line[300];

	sprintf(line, "netscape %s", filename);
	if (system(line) == 0) return(FALSE);
	return(TRUE);
}

/****************************** CHANNELS ******************************/

/*
 * routine to create a pipe connection between the channels in "channels"
 */
INTBIG epipe(int channels[2])
{
	return(pipe(channels));
}

/*
 * Routine to set channel "channel" into an appropriate mode for single-character
 * interaction (i.e. break mode).
 */
void setinteractivemode(int channel)
{
#ifdef HAVE_TERMIOS_H
	struct termios sbuf;

	tcgetattr(channel, &sbuf);
	sbuf.c_iflag |= (BRKINT|IGNPAR|ISTRIP|IXON);
	sbuf.c_oflag |= OPOST;
	sbuf.c_lflag |= (ICANON|ISIG|ECHO);
	sbuf.c_cc[VMIN] = 1;	/* Input should wait for at least one char */
	sbuf.c_cc[VTIME] = 0;	/* no matter how long that takes. */
	tcsetattr(channel, TCSANOW, &sbuf);
#else
#  ifdef HAVE_TERMIO_H
	struct termio sbuf;

	(void)ioctl(channel, TCGETA, &sbuf);
	sbuf.c_iflag |= (BRKINT|IGNPAR|ISTRIP|IXON);
	sbuf.c_oflag |= OPOST;

	/* bogus hp baud rate sanity */
	sbuf.c_cflag = (sbuf.c_cflag & ~CBAUD) | B9600;
	sbuf.c_lflag |= (ICANON|ISIG|ECHO);
	sbuf.c_cc[VMIN] = 1;	/* Input should wait for at least one char */
	sbuf.c_cc[VTIME] = 0;	/* no matter how long that takes. */
	(void)ioctl(channel, TCSETA, &sbuf);
#  else
	struct sgttyb sbuf;

	(void)gtty(channel, &sbuf);
	sbuf.sg_flags |= CBREAK;
	(void)stty(channel, &sbuf);
#  endif
#endif
}

/*
 * Routine to replace channel "channel" with a pointer to file "file".
 * Returns a pointer to the original channel.
 */
INTBIG channelreplacewithfile(int channel, char *file)
{
	INTBIG save;

	save = dup(channel);
	(void)close(channel);
	(void)open(file, 1);
	return(save);
}

/*
 * Routine to replace channel "channel" with new channel "newchannel".
 * Returns a pointer to the original channel.
 */
INTBIG channelreplacewithchannel(int channel, int newchannel)
{
	INTBIG save;

	save = dup(channel);
	(void)close(channel);
	(void)dup(newchannel);
	return(save);
}

/*
 * Routine to restore channel "channel" to the pointer that was returned
 * by "channelreplacewithfile" or "channelreplacewithchannel" (and is in "saved").
 */
void channelrestore(int channel, INTBIG saved)
{
	(void)close(channel);
	(void)dup(saved);
}

/*
 * Routine to read "count" bytes from channel "channel" into "addr".
 * Returns the number of bytes read.
 */
INTBIG eread(int channel, char *addr, INTBIG count)
{
	return(read(channel, addr, count));
}

/*
 * Routine to write "count" bytes to channel "channel" from "addr".
 * Returns the number of bytes written.
 */
INTBIG ewrite(int channel, char *addr, INTBIG count)
{
	return(write(channel, addr, count));
}

/*
 * routine to close a channel in "channel"
 */
INTBIG eclose(int channel)
{
	return(close(channel));
}

/******************** TIME ROUTINES ********************/

/*
 * This routine returns the amount to add to the operating-system time
 * to adjust for a common time format.
 */
UINTBIG machinetimeoffset(void)
{
	return(0);
}

/* returns the current time in 60ths of a second */
UINTBIG ticktime(void)
{
#ifdef HAVE_FTIME
	struct timeb tp;
	static INTBIG basesecs = 0;
	INTBIG ticks;

	ftime(&tp);
	if (basesecs == 0) basesecs = tp.time;
	ticks = (tp.time-basesecs) * 60 + (tp.millitm * 6 / 100);
	return(ticks + gra_timeoffset);
#else
#  ifdef HAVE_GETTIMEOFDAY
	struct timeval tp;
	struct timezone tzp;

	gettimeofday(&tp, &tzp);
	return(tp.tv_sec * 60 + tp.tv_usec * 6 / 100000 + gra_timeoffset);
#  else
	static UINTBIG theTime = 0;

	return(theTime++ + gra_timeoffset);
#  endif
#endif
}

/* returns the double-click interval in 60ths of a second */
INTBIG doubleclicktime(void)
{
	INTBIG dct;

	dct = gra_doublethresh;
	dct = dct * 60 / 1000;
	return(dct);
}

/*
 * Routine to wait "ticks" sixtieths of a second and then return.
 */
void gotosleep(INTBIG ticks)
{
	sleep(ticks);
}

/*
 * Routine to start counting time.
 */
void starttimer(void)
{
#ifdef HAVE_FTIME
	struct timeb tp;

	ftime(&tp);
	gra_timebasesec = tp.time;
	gra_timebasems = tp.millitm;
#else
	gra_timebasesec = time(0);
#endif
}

/*
 * Routine to stop counting time and return the number of elapsed seconds
 * since the last call to "starttimer()".
 */
float endtimer(void)
{
	float seconds;
#ifdef HAVE_FTIME
	INTBIG milliseconds;
	struct timeb timeptr;

	ftime(&timeptr);
	milliseconds = (timeptr.time - gra_timebasesec) * 1000 +
		(timeptr.millitm-gra_timebasems);
	seconds = ((float)milliseconds) / 1000.0;
#else
	seconds = time(0) - gra_timebasesec;
#endif
	return(seconds);
}

/*************************** EVENT ROUTINES ***************************/

/*
 * Work procedure
 */
Boolean gra_dowork(XtPointer client_data)
{
	tooltimeslice();
	return(False);
}

#ifdef USETIF

/*
 * Routine to handle window deletion
 */
void gra_windowdelete(Widget w, XtPointer client_data, XtPointer *call_data)
{
	/* simply disable deletion of the messages window */
	if (w == gra_msgtoplevelwidget)
	{
		ttyputerr(_("Cannot delete the messages window"));
		return;
	}

	/* queue this frame for deletion */
	gra_deletethisframe = gra_getcurrentwindowframe(w, FALSE);
}

void gra_handlequeuedframedeletion(void)
{
	char *par[MAXPARS];
	REGISTER INTBIG windows;
	REGISTER WINDOWFRAME *wf, *delwf;
	REGISTER WINDOWPART *win, *nextw, *neww;
	REGISTER NODEPROTO *np;

	if (gra_deletethisframe == 0) return;
	delwf = gra_deletethisframe;
	gra_deletethisframe = 0;

	/* turn off component menu if it was deleted */
	if (delwf->floating)
	{
		par[0] = "off";
		us_menu(1, par);
		return;
	}

	/* make sure there is another window */
	windows = 0;
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
		if (!wf->floating) windows++;
	if (windows < 2)
	{
		/* no other windows: kill the program */
		if (us_preventloss(NOLIBRARY, 0, TRUE)) return;
		bringdown();
	} else
	{
		/* there are other windows: allow this one to be killed (code copied from us_window("delete")) */

		/* remember that a window is being deleted */
		gra_windowbeingdeleted = 1;

		/* save highlighting and turn it off */
		us_pushhighlight();
		us_clearhighlightcount();

		startobjectchange((INTBIG)us_tool, VTOOL);

		/* kill all editor windows on this frame */
		neww = NOWINDOWPART;
		for(win = el_topwindowpart; win != NOWINDOWPART; win = nextw)
		{
			nextw = win->nextwindowpart;
			if (win->frame != delwf)
			{
				neww = win;
				continue;
			}

			/* kill this window */
			killwindowpart(win);
		}
		endobjectchange((INTBIG)us_tool, VTOOL);

		if (neww == NOWINDOWPART) el_curwindowpart = NOWINDOWPART;
		(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)neww, VWINDOWPART|VDONTSAVE);
		if (neww != NOWINDOWPART) np = neww->curnodeproto; else np = NONODEPROTO;
		(void)setval((INTBIG)el_curlib, VLIBRARY, "curnodeproto", (INTBIG)np, VNODEPROTO);

		/* restore highlighting */
		us_pophighlight(FALSE);

		/* now no widget being deleted, and kill the window properly */
		gra_windowbeingdeleted = 0;
		killwindowframe(delwf);
	}
}

#else

/*
 * helper routine to wait for some keyboard or mouse input
 */
void gra_destroygraphics(Widget w, XtPointer data, XEvent *event, Boolean *cont)
{
	if (event->type == ClientMessage &&
		event->xclient.message_type == gra_wm_protocols)
	{
		if (event->xclient.data.l[0] == gra_wm_delete_window)
		{
			if (w == gra_msgtoplevelwidget)
			{
				/* iconify the messages window */
				XIconifyWindow(gra_topmsgdpy, gra_topmsgwin, DefaultScreen(gra_topmsgdpy));
			} else
			{
				/* queue this frame for deletion */
				gra_deletethisframe = gra_getcurrentwindowframe(w, FALSE);
			}
		}
	}
}

void gra_handlequeuedframedeletion(void)
{
	char *par[MAXPARS];
	REGISTER INTBIG windows;
	REGISTER WINDOWFRAME *wf;

	if (gra_deletethisframe == 0) return;
	gra_deletethisframe = 0;

	/* make sure there is another window */
	windows = 0;
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
		if (!wf->floating) windows++;
	if (windows < 2)
	{
		/* no other windows: kill the program */
		if (us_preventloss(NOLIBRARY, 0, TRUE)) return;
		bringdown();
	} else
	{
		/* there are other windows: allow this one to be killed */
		par[0] = "delete";
		us_window(1, par);
	}
}

#endif

/*
 * Routine called TICKSPERSECOND times each second.  Sees if nothing has happened since
 * the last button-down action, and repeats it if appropriate.
 */
void gra_timerticked(XtPointer closure, XtIntervalId *id)
{
	/* requeue the timer event */
	XtAppAddTimeOut(gra_xtapp, 1000/TICKSPERSECOND, gra_timerticked, 0);

	/* see if it is time to fake another motion event */
	gra_cureventtime++;
	if (gra_cureventtime > gra_lasteventtime+INITIALTIMERDELAY)
	{
		/* see if the last event was a mouse button going down */
		if ((gra_lasteventstate&ISBUTTON) != 0 && (gra_lasteventstate&BUTTONUP) == 0)
		{
			/* turn it into a null motion */
			gra_addeventtoqueue((gra_lasteventstate & ~ISBUTTON) | MOTION,
				gra_lasteventspecial, gra_lasteventx, gra_lasteventy);
			gra_lasteventtime -= INITIALTIMERDELAY;
			if (el_curwindowframe != NOWINDOWFRAME)
				XSendEvent(gra_samplemotionevent.xmotion.display, gra_samplemotionevent.xmotion.window,
					True, PointerMotionMask, &gra_samplemotionevent);
			return;
		}

		/* see if the last event was a motion with button down */
		if ((gra_lasteventstate&(MOTION|BUTTONUP)) == MOTION)
		{
			/* repeat the last event */
			gra_addeventtoqueue(gra_lasteventstate, gra_lasteventspecial, gra_lasteventx, gra_lasteventy);
			gra_lasteventtime -= INITIALTIMERDELAY;
			if (el_curwindowframe != NOWINDOWFRAME)
				XSendEvent(gra_samplemotionevent.xmotion.display, gra_samplemotionevent.xmotion.window,
					True, PointerMotionMask, &gra_samplemotionevent);
			return;
		}
	}
}

/*
 * Routine to queue an event of type "state" (with special code "special")
 * at (x,y).
 */
void gra_addeventtoqueue(INTBIG state, INTBIG special, INTBIG x, INTBIG y)
{
	MYEVENTQUEUE *next;

	next = gra_eventqueuetail + 1;
	if (next >= &gra_eventqueue[EVENTQUEUESIZE])
		next = gra_eventqueue;
	if (next == gra_eventqueuehead)
	{
		/* queue is full */
		if (!gra_checkinginterrupt) XBell(gra_maindpy, 100);
		return;
	}

	gra_lasteventstate = gra_eventqueuetail->inputstate = state;
	gra_eventqueuetail->special = special;
	gra_lasteventspecial = gra_eventqueuetail->special = special;
	gra_lasteventx = gra_eventqueuetail->cursorx = x;
	gra_lasteventy = gra_eventqueuetail->cursory = y;
	gra_lasteventtime = gra_cureventtime;
	gra_eventqueuetail = next;
}

/*
 * Routine to dequeue the next event and place it in the globals
 * "gra_inputstate", "gra_cursorx", "gra_cusory".  If the event is
 * special, handle it.
 */
void gra_pickupnextevent(void)
{
	char *par[2];
	INTBIG special, state;
	INTBIG x, y;
#ifdef USETIF
	XmTextPosition tp;
#endif

	/* get the next event from the queue */
	gra_inputstate = NOEVENT;
	while (gra_eventqueuehead != gra_eventqueuetail)
	{
		special = gra_eventqueuehead->special;
		state = gra_eventqueuehead->inputstate;
		x = gra_eventqueuehead->cursorx;
		y = gra_eventqueuehead->cursory;
		gra_eventqueuehead++;
		if (gra_eventqueuehead >= &gra_eventqueue[EVENTQUEUESIZE])
			gra_eventqueuehead = gra_eventqueue;

		/* stop if this is the last event in the queue */
		if (gra_eventqueuehead == gra_eventqueuetail) break;

		/* stop if this and the next event are not motion */
		if ((gra_inputstate&MOTION) == 0) break;
		if ((gra_eventqueuehead->inputstate&MOTION) == 0) break;
	}

	/* handle special actions */
	if (special != 0 && (special&SPECIALKEYDOWN) != 0)
	{
		switch ((special&SPECIALKEY)>>SPECIALKEYSH)
		{
			case SPECIALCUT:
				par[0] = "cut";
				us_text(1, par);
				return;
			case SPECIALCOPY:
				par[0] = "copy";
				us_text(1, par);
				return;
			case SPECIALPASTE:
				par[0] = "paste";
				us_text(1, par);
				return;
			case SPECIALUNDO:
				us_undo(0, par);
				return;
			case SPECIALEND:
				/* shift to end of messages window */
#ifdef USETIF
				tp = XmTextGetLastPosition(gra_messageswidget);
				XmTextSetSelection(gra_messageswidget, tp, tp, 0);
				XmTextSetInsertionPosition(gra_messageswidget, tp);
				XmTextShowPosition(gra_messageswidget, tp);
#else
				XawTextSetInsertionPoint(gra_messageswidget, MESSAGEBUFFERSIZE);
#endif
				return;
		}
	}

	gra_inputstate = state;
	gra_inputspecial = special;
	gra_cursorx = x;
	gra_cursory = y;
	us_state &= ~GOTXY;
}

void gra_nextevent(void)
{
	XEvent event;
	REGISTER INTBIG windowindex;
	REGISTER INTBIG x, y;
	BOOLEAN verbose;
#ifdef USETIF
	POPUPMENU *pm;
	REGISTER WINDOWFRAME *wf;
	REGISTER INTBIG i, j;
#endif

	/* handle any queued window deletions */
	gra_handlequeuedframedeletion();

	/* flush the screen */
	flushscreen();

	/* get any event on the queue */
	if (gra_eventqueuehead != gra_eventqueuetail)
	{
		gra_pickupnextevent();
	} else
	{
#ifdef USETIF
		for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
			XmUpdateDisplay(wf->toplevelwidget);
#endif
		/* handle any pending event */
		XtAppNextEvent(gra_xtapp, &event);
		XtDispatchEvent(&event);

		/* get any event on the queue */
		if (gra_eventqueuehead != gra_eventqueuetail)
			gra_pickupnextevent();
	}

	/* override the event if playing back */
	if (us_logplay != NULL)
	{
		/* playing back log file: get event from disk */
		if (gra_inputstate != NOEVENT || gra_curdialogindex >= 0)
			(void)gra_loggetnextaction(0);
	}

	/* record valid events */
	if (gra_inputstate != NOEVENT)
	{
		if (gra_inputstate == WINDOWSIZE || gra_inputstate == WINDOWMOVE)
		{
			windowindex = gra_cursorx;
			x = gra_cursory >> 16;
			y = gra_cursory & 0xFFFF;
			gra_logwriteaction(gra_inputstate, 0, x, y, (void *)windowindex);
			gra_inputstate = NOEVENT;
		} else
		{
			windowindex = -1;
			if (el_curwindowframe != NOWINDOWFRAME) windowindex = el_curwindowframe->windindex;
			gra_logwriteaction(gra_inputstate, gra_inputspecial, gra_cursorx, gra_cursory, (void *)windowindex);
		}

#ifdef USETIF
		/* handle menu events */
		if (gra_inputstate == MENUEVENT)
		{
			gra_inputstate = NOEVENT;
			i = gra_cursorx;
			j = gra_cursory;
			for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
				if (!wf->floating) break;
			if (wf != NOWINDOWFRAME && i >= 0 && i < wf->pulldownmenucount)
			{
				pm = wf->pulldowns[i];
				if (j >= 0 && j < pm->total)
				{
					us_state |= DIDINPUT;
					us_state &= ~GOTXY;
					setdefaultcursortype(NULLCURSOR);
					us_forceeditchanges();
					if ((us_tool->toolstate&ECHOBIND) != 0) verbose = TRUE; else
						verbose = FALSE;
					us_execute(pm->list[j].response, verbose, TRUE, TRUE);
					db_setcurrenttool(us_tool);
					setactivity(pm->list[j].attribute);
				}
			}
		}
#endif
	}
}

void gra_messages_event_handler(Widget w, XtPointer data, XEvent *event, Boolean *cont)
{
	char buffer[2];
	REGISTER INTBIG state;
	INTBIG special;
	KeySym key;

	switch (event->type)
	{
		case VisibilityNotify:
			if (event->xvisibility.state == VisibilityUnobscured)
				gra_messages_obscured = FALSE; else
					gra_messages_obscured = TRUE;
			break;

		case FocusIn:
			gra_msgontop = TRUE;
			break;

		case FocusOut:
			gra_msgontop = FALSE;
			break;

		case KeyPress:
			(void)XLookupString((XKeyEvent *)event, buffer, 2, &key, NULL);
			if ((key == 'c' || key == 'C') && (event->xbutton.state & ControlMask) != 0)
			{
				/* we have to do interrupts here */
				XtNoticeSignal(gra_intsignalid);
				el_pleasestop = 1;
				state = 0;
				special = 0;
			} else state = gra_translatekey(event, &special);
			if (state != 0)
			{
				us_state |= DIDINPUT;
				gra_addeventtoqueue(state, special, 0, 0);
			}
			event->type = 0;     /* disable this event so key doesn't go into window */
			break;

#if 0		/* for debugging X events */
		default:
			fprintf(stderr, _("Unhandled event %s in messages window\n"),
				eventNames[event->type]);
			break;
#endif
	}
}

void gra_graphics_event_handler(Widget w, XtPointer data, XEvent *event, Boolean *cont)
{
	BOOLEAN setcursor, inmenu;
	INTBIG wid, hei, special;
	char *str;
	REGISTER INTBIG x, y, state;
	REGISTER UINTBIG thetime;
	INTBIG lx, hx, ly, hy;
	REGISTER WINDOWPART *win;
	REGISTER EDITOR *e;
	REGISTER VARIABLE *var;
	XWindowAttributes xwa;
	COMMANDBINDING commandbinding;
	REGISTER WINDOWFRAME *wf;
	static BOOLEAN overrodestatus = FALSE;

	switch (event->type)
	{
		case ConfigureNotify:
			wf = gra_getcurrentwindowframe(w, FALSE);
			if (wf == NOWINDOWFRAME) return;
			if (w != wf->toplevelwidget) break;

			gra_getwindowinteriorsize(wf, &wid, &hei);
			gra_getwindowattributes(wf, &xwa);

			/* update user's positioning of component menu */
			thetime = ticktime();
			if (thetime - 120 > gra_internalchange && wf->floating)
			{
				gra_palettetop = xwa.y;
				if (wid > hei) gra_palettewidth = wid; else
					gra_paletteheight = hei;
			}
			gra_internalchange = thetime;

			if (wid != wf->swid || hei != wf->trueheight)
			{
				/* window changed size */
				gra_addeventtoqueue(WINDOWSIZE, 0, wf->windindex,
					((xwa.width & 0xFFFF) << 16) | (xwa.height & 0xFFFF));
				gra_repaint(wf, TRUE);
			}

			x = xwa.x;   y = xwa.y;
			gra_addeventtoqueue(WINDOWMOVE, 0, wf->windindex,
				((x & 0xFFFF) << 16) | (y & 0xFFFF));
			break;

		case Expose:
			/* ignore partial events */
			if (event->xexpose.count != 0) break;
#if 0
#  ifdef USETIF
			if (gra_curdialogindex >= 0)
			{
				wf = gra_getcurrentwindowframe(w, FALSE);
			} else
#  endif
			{
				wf = gra_getcurrentwindowframe(w, TRUE);
			}
#else
			wf = gra_getcurrentwindowframe(w, FALSE);
#endif
			if (wf == NOWINDOWFRAME) return;
#ifdef ANYDEPTH
			wf->copyleft = 0;   wf->copyright = wf->swid;
			wf->copytop = 0;    wf->copybottom = wf->trueheight;
			wf->offscreendirty = 1;
			flushscreen();
#else
			gra_repaint(wf, FALSE);
#endif
			break;

		case ClientMessage:
#ifndef USETIF
			/* watch for deletes of our windows */
			if (event->xclient.message_type == gra_wm_protocols)
			{
				if (event->xclient.data.l[0] == gra_wm_delete_window)
				{
					ttybeep(0);
					ttyputerr(_("WARNING! An attempt was made to kill your graphics window."));
					ttyputmsg(_("Always exit from within Electric to prevent loss of data."));
					break;
				}
			}
#endif
			break;

		case KeyPress:
#ifdef USETIF
			if (gra_curdialogindex >= 0) return;
#endif
			wf = gra_getcurrentwindowframe(w, TRUE);
			if (wf == NOWINDOWFRAME) return;
			state = gra_translatekey(event, &special);
			if (state == 0) break;
			us_state |= DIDINPUT;

			/* don't track in the status window */
			gra_cursorx = event->xkey.x;
			gra_cursory = maxi(wf->revy - event->xkey.y, 0);
			gra_addeventtoqueue(state, special, gra_cursorx, gra_cursory);
			break;

		case ButtonPress:  /* these are ignored in the messages window */
			if (us_logplay != NULL)
			{
				ttyputerr(_("Session playback aborted by mouse click"));
				xclose(us_logplay);
				us_logplay = NULL;
				break;
			}
#ifdef USETIF
			gra_lastbuttonpressevent = *event;
			if (gra_curdialogindex >= 0) return;
#endif
			wf = gra_getcurrentwindowframe(w, TRUE);
			if (wf == NOWINDOWFRAME) return;
			switch (event->xbutton.button)
			{
				case 1: state = ISLEFT;    break;
				case 2: state = ISMIDDLE;  break;
				case 3: state = ISRIGHT;   break;
				case 4: state = ISWHLFWD;  break;
				case 5: state = ISWHLBKW;  break;
			}
			if ((event->xbutton.state & ShiftMask) != 0) state |= SHIFTISDOWN;
			if ((event->xbutton.state & LockMask) != 0) state |= SHIFTISDOWN;
			if ((event->xbutton.state & ControlMask) != 0) state |= CONTROLISDOWN;
			if ((event->xbutton.state & (Mod1Mask|Mod4Mask)) != 0) state |= METAISDOWN;
			gra_cursorx = event->xbutton.x;
			gra_cursory = maxi(wf->revy - event->xbutton.y, 0);
			if ((event->xbutton.time - gra_lasttime) <= gra_doublethresh &&
				(state & (SHIFTISDOWN|CONTROLISDOWN|METAISDOWN)) == 0 &&
				gra_cursorx == gra_lstcurx && gra_cursory == gra_lstcury)
			{
				state |= DOUBLECL;
				gra_lasttime = event->xbutton.time - gra_doublethresh - 1;
			} else
			{
				gra_lasttime = event->xbutton.time;
			}
			gra_addeventtoqueue(state, 0, gra_cursorx, gra_cursory);
			gra_lstcurx = gra_cursorx;
			gra_lstcury = gra_cursory;
			us_state |= DIDINPUT;
			break;

		case ButtonRelease:
#ifdef USETIF
			if (gra_curdialogindex >= 0) return;
#endif
			wf = gra_getcurrentwindowframe(w, TRUE);
			if (wf == NOWINDOWFRAME) return;
			switch (event->xbutton.button)
			{
				case 1: state = ISLEFT | BUTTONUP;   break;
				case 2: state = ISMIDDLE | BUTTONUP; break;
				case 3: state = ISRIGHT | BUTTONUP;  break;
			}
			if ((event->xbutton.state & ShiftMask) != 0) state |= SHIFTISDOWN;
			if ((event->xbutton.state & LockMask) != 0) state |= SHIFTISDOWN;
			if ((event->xbutton.state & ControlMask) != 0) state |= CONTROLISDOWN;
			if ((event->xbutton.state & (Mod1Mask|Mod4Mask)) != 0) state |= METAISDOWN;
			gra_cursorx = event->xbutton.x;
			gra_cursory = maxi(wf->revy - event->xbutton.y, 0);
			gra_addeventtoqueue(state, 0, gra_cursorx, gra_cursory);
			us_state |= DIDINPUT;
			break;

		case MotionNotify:
			/* save this event for faking others */
			gra_samplemotionevent = *event;

			/* ignore faked events (done to wake up event loop) */
			if (event->xmotion.send_event) break;

			if (gra_firstbuttonwait == 0) break;
			if ((event->xmotion.state & (Button1Mask | Button2Mask | Button3Mask)) == 0)
			{
				/* motion while up considers any window */
				wf = gra_getcurrentwindowframe(w, FALSE);
				if (wf == NOWINDOWFRAME) return;
				gra_cursorx = event->xmotion.x;
				gra_cursory = wf->revy - event->xmotion.y;
				if (gra_cursory < 0) gra_cursory = 0;

				/* report the menu if over one */
				inmenu = FALSE;
				if (wf->floating)
				{
					x = (gra_cursorx-us_menulx) / us_menuxsz;
					y = (gra_cursory-us_menuly) / us_menuysz;
					if (x >= 0 && y >= 0 && x < us_menux && y < us_menuy)
					{
						var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
						if (var != NOVARIABLE)
						{
							if (us_menupos <= 1) str = ((char **)var->addr)[y * us_menux + x]; else
								str = ((char **)var->addr)[x * us_menuy + y];
							us_parsebinding(str, &commandbinding);
							if (*commandbinding.command != 0)
							{
								if (commandbinding.nodeglyph != NONODEPROTO)
								{
									ttysetstatusfield(NOWINDOWFRAME, us_statusarc,
										describearcproto(us_curarcproto), TRUE);
									ttysetstatusfield(NOWINDOWFRAME, us_statusnode,
										us_describemenunode(&commandbinding), TRUE);
									inmenu = TRUE;
									overrodestatus = TRUE;
								}
								if (commandbinding.arcglyph != NOARCPROTO)
								{
									ttysetstatusfield(NOWINDOWFRAME, us_statusarc,
										describearcproto(commandbinding.arcglyph), TRUE);
									if (us_curnodeproto == NONODEPROTO) str = ""; else
										str = describenodeproto(us_curnodeproto);
									ttysetstatusfield(NOWINDOWFRAME, us_statusnode, str, TRUE);
									inmenu = TRUE;
									overrodestatus = TRUE;
								}
							}
							us_freebindingparse(&commandbinding);
						}
					}
				}
				if (!inmenu && overrodestatus)
				{
					ttysetstatusfield(NOWINDOWFRAME, us_statusarc,
						describearcproto(us_curarcproto), TRUE);
					if (us_curnodeproto == NONODEPROTO) str = ""; else
						str = describenodeproto(us_curnodeproto);
					ttysetstatusfield(NOWINDOWFRAME, us_statusnode, str, TRUE);
					overrodestatus = FALSE;
				}

				setcursor = FALSE;
				for(win = el_topwindowpart; win != NOWINDOWPART; win = win->nextwindowpart)
				{
					/* see if the cursor is over a window partition separator */
					if (win->frame != wf) continue;
					us_gettruewindowbounds(win, &lx, &hx, &ly, &hy);
					if (gra_cursorx >= lx-1 && gra_cursorx <= lx+1 && gra_cursory > ly+1 &&
						gra_cursory < hy-1 && us_hasotherwindowpart(lx-10, gra_cursory, win))
					{
						setdefaultcursortype(LRCURSOR);
						setcursor = TRUE;
						break;
					} else if (gra_cursorx >= hx-1 && gra_cursorx <= hx+1 && gra_cursory > ly+1 &&
						gra_cursory < hy-1 && us_hasotherwindowpart(hx+10, gra_cursory, win))
					{
						setdefaultcursortype(LRCURSOR);
						setcursor = TRUE;
						break;
					} else if (gra_cursory >= ly-1 && gra_cursory <= ly+1 && gra_cursorx > lx+1 &&
						gra_cursorx < hx-1 && us_hasotherwindowpart(gra_cursorx, ly-10, win))
					{
						setdefaultcursortype(UDCURSOR);
						setcursor = TRUE;
						break;
					} else if (gra_cursory >= hy-1 && gra_cursory <= hy+1 && gra_cursorx > lx+1 &&
						gra_cursorx < hx-1 && us_hasotherwindowpart(gra_cursorx, hy+10, win))
					{
						setdefaultcursortype(UDCURSOR);
						setcursor = TRUE;
						break;
					}

					if (gra_cursorx < win->uselx || gra_cursorx > win->usehx ||
						gra_cursory < win->usely || gra_cursory > win->usehy) continue;
					if ((win->state&WINDOWTYPE) == POPTEXTWINDOW ||
						(win->state&WINDOWTYPE) == TEXTWINDOW)
					{
						e = win->editor;
						if ((e->state&EDITORTYPE) == PACEDITOR)
						{
							if (gra_cursorx <= win->usehx - SBARWIDTH &&
								gra_cursory >= win->usely + SBARWIDTH &&
								gra_cursory < e->revy)
							{
								setdefaultcursortype(IBEAMCURSOR);
								setcursor = TRUE;
								break;
							}
						}
					}
				}
				if (!setcursor) setdefaultcursortype(us_normalcursor);

				/* record the action */
				state = MOTION | BUTTONUP;
				gra_addeventtoqueue(state, 0, gra_cursorx, gra_cursory);
			} else
			{
				/* motion while button down: use last window */
				wf = el_curwindowframe;
				if (wf == NOWINDOWFRAME) return;
				gra_cursorx = event->xmotion.x;
				gra_cursory = wf->revy - event->xmotion.y;
				if (gra_cursory < 0) gra_cursory = 0;

				/* record the action */
				state = MOTION;
				gra_addeventtoqueue(state, 0, gra_cursorx, gra_cursory);
			}
			break;

#if 0
		case FocusIn:
#  ifdef USETIF
			if (gra_curdialogindex < 0)
#  endif
				wf = gra_getcurrentwindowframe(w, TRUE);

			break;
#endif

		case ReparentNotify:
			break;

#if 0		/* for debugging X events */
		default:
			fprintf(stderr, _("Unhandled event %s in graphics window.\n"),
				eventNames[event->type]);
			break;
#endif
	}
}

/*
 * routine to determine the current window frame and load the global "el_curwindowframe".
 */
WINDOWFRAME *gra_getcurrentwindowframe(Widget widget, BOOLEAN set)
{
	WINDOWFRAME *wf;
	WINDOWPART *w;

	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->toplevelwidget == widget) break;
		if (wf->intermediategraphics == widget) break;
		if (wf->graphicswidget == widget) break;
	}
	if (!set) return(wf);

	el_curwindowframe = wf;
	if (wf == NOWINDOWFRAME) return(NOWINDOWFRAME);

	/* ignore floating windows */
	if (wf->floating) return(wf);

	/* see if the change of window frame invalidates the current window */
	if (el_curwindowpart == NOWINDOWPART || el_curwindowpart->frame != wf)
	{
		/* must choose new window (if not broadcasting) */
		if (db_broadcasting == 0)
		{
			for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
			{
				if (w->frame == wf)
				{
					(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)w,
						VWINDOWPART|VDONTSAVE);
					(void)setval((INTBIG)el_curlib, VLIBRARY, "curnodeproto",
						(INTBIG)w->curnodeproto, VNODEPROTO);
					return(NOWINDOWFRAME);
				}
			}
		}
	}
	return(el_curwindowframe);
}

/*
 * Routine to translate key symbols to electric state.
 * Sets "special" to a nonzero code if a special function key was hit
 * Returns 0 if the key is unknown.
 */
INTBIG gra_translatekey(XEvent *event, INTBIG *special)
{
	char buffer[2];
	KeySym key;
	INTBIG state;

	*special = 0;
	(void)XLookupString((XKeyEvent *)event, buffer, 2, &key, NULL);
	if (key > 0 && key < 0200) state = key; else
	{
		switch (key)
		{
			case XK_KP_0:      state = '0';            break;
			case XK_KP_1:      state = '1';            break;
			case XK_KP_2:      state = '2';            break;
			case XK_KP_3:      state = '3';            break;
			case XK_KP_4:      state = '4';            break;
			case XK_KP_5:      state = '5';            break;
			case XK_KP_6:      state = '6';            break;
			case XK_KP_7:      state = '7';            break;
			case XK_KP_8:      state = '8';            break;
			case XK_KP_9:      state = '9';            break;
			case XK_BackSpace: state = BACKSPACEKEY;   break;
			case XK_Tab:       state = 011;            break;
			case XK_Linefeed:  state = 012;            break;
			case XK_Return:    state = 015;            break;
			case XK_Escape:    state = ESCKEY;         break;
			case XK_Delete:    state = 0177;           break;
			case XK_KP_Enter:  state = 015;            break;

			case XK_End:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALEND<<SPECIALKEYSH);
				break;
			case XK_L4:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALUNDO<<SPECIALKEYSH);
				break;
			case XK_L6:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALCOPY<<SPECIALKEYSH);
				break;
			case XK_L8:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALPASTE<<SPECIALKEYSH);
				break;
			case XK_L10:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALCUT<<SPECIALKEYSH);
				break;

			case XK_Left:
			case XK_KP_Left:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYARROWL<<SPECIALKEYSH);
				if ((event->xbutton.state & ShiftMask) != 0) *special |= SHIFTDOWN;
				break;
			case XK_Right:
			case XK_KP_Right:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYARROWR<<SPECIALKEYSH);
				if ((event->xbutton.state & ShiftMask) != 0) *special |= SHIFTDOWN;
				break;
			case XK_Up:
			case XK_KP_Up:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYARROWU<<SPECIALKEYSH);
				if ((event->xbutton.state & ShiftMask) != 0) *special |= SHIFTDOWN;
				break;
			case XK_Down:
			case XK_KP_Down:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYARROWD<<SPECIALKEYSH);
				if ((event->xbutton.state & ShiftMask) != 0) *special |= SHIFTDOWN;
				break;

			case XK_F1:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF1<<SPECIALKEYSH);
				break;
			case XK_F2:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF2<<SPECIALKEYSH);
				break;
			case XK_F3:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF3<<SPECIALKEYSH);
				break;
			case XK_F4:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF4<<SPECIALKEYSH);
				break;
			case XK_F5:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF5<<SPECIALKEYSH);
				break;
			case XK_F6:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF6<<SPECIALKEYSH);
				break;
			case XK_F7:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF7<<SPECIALKEYSH);
				break;
			case XK_F8:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF8<<SPECIALKEYSH);
				break;
			case XK_F9:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF9<<SPECIALKEYSH);
				break;
			case XK_F10:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF10<<SPECIALKEYSH);
				break;
			case XK_F11:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF11<<SPECIALKEYSH);
				break;
			case XK_F12:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF12<<SPECIALKEYSH);
				break;

			default:
				state = 0;
				break;
		}
	}
	if (state == 0 && *special == 0) return(0);

	if ((event->xbutton.state & ControlMask) != 0)
	{
		if (!gra_messages_typingin) *special |= ACCELERATORDOWN; else
		{
			if (state >= 'a' && state <= 'z') state -= 'a' - 1; else
				if (state >= 'A' && state <= 'Z') state -= 'A' - 1;
		}
	}
	state |= ISKEYSTROKE;
	return(state);
}

void gra_repaint(WINDOWFRAME *wf, BOOLEAN redo)
{
	INTBIG wid, hei;
	static BOOLEAN inrepaint = FALSE;

	if (inrepaint) return;
	inrepaint = TRUE;

	gra_getwindowinteriorsize(wf, &wid, &hei);
	gra_recalcsize(wf, wid, hei);

	if (wf->floating)
	{
		us_startbatch(NOTOOL, FALSE);
		if (redo) us_drawmenu(1, wf); else
			us_drawmenu(-1, wf);
		us_endbatch();
		inrepaint = FALSE;
		return;
	}

	if (el_topwindowpart != NOWINDOWPART)
	{
		/* mark start of redisplay */
		us_startbatch(NOTOOL, FALSE);

		/* save and clear highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		/* rewrite status area */
		if (redo) us_drawmenu(1, wf); else
			us_drawmenu(-1, wf);

		/* restore highlighting */
		us_pophighlight(FALSE);

		/* finish drawing */
		us_endbatch();

		us_redostatus(wf);

		/* describe this change */
		setactivity(_("Window Resize"));
	}
	inrepaint = FALSE;
}

void gra_recalcsize(WINDOWFRAME *wf, INTBIG newwid, INTBIG newhei)
{
#ifdef ANYDEPTH
	INTBIG i, j;
	XImage *newimage;
	char *newdataaddr8, **newrowstart, *newdataaddr, **newoffrowstart;
	INTBIG depth, truedepth;
	Visual *visual;

	/* make sure the height isn't too small for the status bar */
	if (wf->floating && newhei < gra_status_height + 2)
	{
		newhei = gra_status_height + 2;
		ttyputmsg(_("Window too short: made minimum height"));
	}

	if (newwid != wf->swid || newhei != wf->trueheight)
	{
		/* allocate space for the 8-bit deep buffer */
		newdataaddr8 = (char *)emalloc(newwid * newhei, us_tool->cluster);
		if (newdataaddr8 == 0) return;
		newrowstart = (char **)emalloc(newhei * SIZEOFINTBIG, us_tool->cluster);
		if (newrowstart == 0) return;
		for(i=0; i<newhei; i++)
		{
			newrowstart[i] = newdataaddr8 + i * newwid;
			for(j=0; j<newwid; j++)
				newrowstart[i][j] = 0;
		}

		/* deallocate old buffers and fill in new */
		efree((char *)wf->dataaddr8);
		efree((char *)wf->rowstart);
		wf->dataaddr8 = newdataaddr8;
		wf->rowstart = newrowstart;

		/* allocate space for the full-depth buffer */
		if (newwid > wf->wd->wid || newhei > wf->wd->hei)
		{
			gra_getdisplaydepth(wf->topdpy, &depth, &truedepth);
			newdataaddr = (char *)emalloc(newwid * newhei *
				truedepth / 8, us_tool->cluster);
			if (newdataaddr == 0) return;
			newoffrowstart = (char **)emalloc(newhei * SIZEOFINTBIG, us_tool->cluster);
			if (newoffrowstart == 0) return;
			visual = DefaultVisual(wf->topdpy, DefaultScreen(wf->topdpy));
			newimage = XCreateImage(wf->topdpy, visual, truedepth,
				ZPixmap, 0, newdataaddr, newwid, newhei, depth, 0);
			if (newimage == 0)
			{
				fprintf(stderr, _("Error allocating new image array that is %ldx%ld\n"),
					newwid, newhei);
				return;
			}
			for(i=0; i<newhei; i++)
				newoffrowstart[i] = newdataaddr + i * newimage->bytes_per_line;

			/* deallocate old data and set new */
			efree((char *)wf->wd->addr);
			efree((char *)wf->wd->rowstart);
			XFree(wf->wd->image);
			wf->wd->addr = newdataaddr;
			wf->wd->rowstart = newoffrowstart;
			wf->wd->image = newimage;
			wf->wd->wid = newwid;   wf->wd->hei = newhei;
		}
	}
#else
	XClearWindow(wf->topdpy, wf->win);
#endif
	wf->swid = newwid;
	wf->trueheight = newhei;
	wf->shei = wf->trueheight;
	if (!wf->floating) wf->shei -= gra_status_height;
	wf->revy = wf->shei - 1;
}

/*
 * support routine to print any internal errors
 */
int gra_xerrors(Display *dpy, XErrorEvent *err)
{
	char buffer[100];

	XGetErrorText(dpy, err->error_code, buffer, 100);
	/* buffer is now in the "encoding of the current locale" */
	ttyputerr(_("ERROR: X Window System routine %d has %s"), err->request_code, buffer);
	return(0);
}

void gra_xterrors(char *msg)
{
	ttyputerr(_("ERROR: X Toolkit: %s"), msg);
}

/* error events */
RETSIGTYPE gra_sigill_trap(void)
{
	(void)signal(SIGILL, (SIGNALCAST)gra_sigill_trap);
	error(_("FATAL ERROR: An illegal instruction has been trapped"));
}

RETSIGTYPE gra_sigfpe_trap(void)
{
	(void)signal(SIGFPE, (SIGNALCAST)gra_sigfpe_trap);
	error(_("FATAL ERROR: A numerical error has occurred"));
}

RETSIGTYPE gra_sigbus_trap(void)
{
	(void)signal(SIGBUS, (SIGNALCAST)gra_sigbus_trap);
	error(_("FATAL ERROR: A bus error has occurred"));
}

RETSIGTYPE gra_sigsegv_trap(void)
{
	(void)signal(SIGSEGV, (SIGNALCAST)gra_sigsegv_trap);
	error(_("FATAL ERROR: A segmentation violation has occurred"));
}

void gra_intsignalfunc(void)
{
	ttyputerr(_("Interrupted..."));
}

/*************************** SESSION LOGGING ROUTINES ***************************/

/* Session Playback */
DIALOGITEM gra_sesplaydialogitems[] =
{
 /*  1 */ {0, {100,132,124,212}, BUTTON, N_("Yes")},
 /*  2 */ {0, {100,8,124,88}, BUTTON, N_("No")},
 /*  3 */ {0, {4,8,20,232}, MESSAGE, N_("Electric has found a session log file")},
 /*  4 */ {0, {24,8,40,232}, MESSAGE, N_("which may be from a recent crash.")},
 /*  5 */ {0, {52,8,68,232}, MESSAGE, N_("Do you wish to replay this session")},
 /*  6 */ {0, {72,8,88,232}, MESSAGE, N_("and reconstruct the lost work?")}
};
DIALOG gra_sesplaydialog = {{75,75,208,316}, N_("Replay Log?"), 0, 6, gra_sesplaydialogitems};

/* special items for the session playback dialog: */
#define DSPL_YES    1		/* Yes (button) */
#define DSPL_NO     2		/* No (button) */

/*
 * routine to create a session logging file
 */
void logstartrecord(void)
{
	REGISTER INTBIG itemhit, count;
	REGISTER LIBRARY *lib;
	REGISTER WINDOWFRAME *wf;
	REGISTER WINDOWPART *w;
	REGISTER VARIABLE *var;
	XWindowAttributes xwa;

	/* if there is already a log file, it may be from a previous crash */
	if (fileexistence(gra_logfile) == 1)
	{
		(void)DiaInitDialog(&gra_sesplaydialog);
		for(;;)
		{
			itemhit = DiaNextHit();
			if (itemhit == DSPL_YES || itemhit == DSPL_NO) break;
		}
		DiaDoneDialog();
		if (itemhit == DSPL_YES)
		{
			if (fileexistence(gra_logfilesave) == 1)
				eunlink(gra_logfilesave);
			rename(gra_logfile, gra_logfilesave);
			(void)logplayback(gra_logfilesave);
		}
	}

	us_logrecord = xcreate(gra_logfile, us_filetypelog, 0, 0);
	if (us_logrecord == 0) return;
	gra_logbasetime = ticktime();

	/* document the header */
	xprintf(us_logrecord, "; ============= The header:\n");
	xprintf(us_logrecord, "; LC n           Library count\n");
	xprintf(us_logrecord, "; LD name file   Library read from disk\n");
	xprintf(us_logrecord, "; LM name file   Library not read from disk\n");
	xprintf(us_logrecord, "; WO n           Window offset currently 'n'\n");
	xprintf(us_logrecord, "; WT n           Window count\n");
	xprintf(us_logrecord, "; WC n x y w h   Component window, index 'n' at (x,y), size (wXh)\n");
	xprintf(us_logrecord, "; WE n x y w h   Edit window, index 'n' at (x,y), size (wXh)\n");
	xprintf(us_logrecord, "; WP n           Window has 'n' partitions\n");
	xprintf(us_logrecord, "; WB lx hx ly hy slx shx sly shy state facet loc    Window partition (background)\n");
	xprintf(us_logrecord, "; WF lx hx ly hy slx shx sly shy state facet loc    Window partition (foreground)\n");
	xprintf(us_logrecord, "; CI n           Current window index\n");
	xprintf(us_logrecord, "; CT tech        Current technology\n");

	/* log current libraries */
	count = 0;
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		if ((lib->userbits&HIDDENLIBRARY) == 0) count++;
	xprintf(us_logrecord, "LC %ld\n", count);
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
		if ((lib->userbits&READFROMDISK) != 0)
		{
			xprintf(us_logrecord, "LD %s %s\n", lib->libname, lib->libfile);
		} else
		{
			xprintf(us_logrecord, "LM %s %s\n", lib->libname, lib->libfile);
		}
	}

	/* log current windows */
	xprintf(us_logrecord, "WO %ld\n", gra_windownumber);
	count = 0;
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe) count++;
	xprintf(us_logrecord, "WT %ld\n", count);
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		gra_getwindowattributes(wf, &xwa);
		if (wf->floating != 0)
		{
			xprintf(us_logrecord, "WC %ld %ld %ld %ld %ld\n", wf->windindex, xwa.x, xwa.y,
				xwa.width, xwa.height);
		} else
		{
			xprintf(us_logrecord, "WE %ld %ld %ld %ld %ld\n", wf->windindex, xwa.x, xwa.y,
				xwa.width, xwa.height);
		}

		count = 0;
		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
			if (w->frame == wf) count++;
		xprintf(us_logrecord, "WP %ld\n", count);
		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		{
			if (w->frame != wf) continue;
			if (w == el_curwindowpart)
			{
				xprintf(us_logrecord, "WF %ld %ld %ld %ld %ld %ld %ld %ld %ld %s %s\n",
					w->uselx, w->usehx, w->usely, w->usehy, w->screenlx, w->screenhx,
					w->screenly, w->screenhy, w->state, describenodeproto(w->curnodeproto),
					w->location);
			} else
			{
				xprintf(us_logrecord, "WB %ld %ld %ld %ld %ld %ld %ld %ld %ld %s %s\n",
					w->uselx, w->usehx, w->usely, w->usehy, w->screenlx, w->screenhx,
					w->screenly, w->screenhy, w->state, describenodeproto(w->curnodeproto),
					w->location);
			}
		}
	}
	xprintf(us_logrecord, "CI %ld\n", gra_windowframeindex);

	/* log current technology (macros store this in %H) */
	var = getval((INTBIG)us_tool, VTOOL, VSTRING, "USER_local_caph");
	if (var != NOVARIABLE)
	{
		/* technology name found in local variable */
		xprintf(us_logrecord, "CT %s\n", (char *)var->addr);
	} else
	{
		/* just write the current technology name */
		xprintf(us_logrecord, "CT %s\n", el_curtech->techname);
	}

	/* document the body */
	xprintf(us_logrecord, "; ============= The body:\n");
	xprintf(us_logrecord, "; KT k x y s i t Key 'k' typed at (x,y), special 's', window 'i', time 't'\n");
	xprintf(us_logrecord, "; BP n x y s i t Button 'n' pressed at (x,y), special 's', window 'i', time 't'\n");
	xprintf(us_logrecord, "; BD n x y s i t Button 'n' double-clicked at (x,y), special 's', window 'i', time 't'\n");
	xprintf(us_logrecord, "; BR n x y s i t Button 'n' released at (x,y), special 's', window 'i', time 't'\n");
	xprintf(us_logrecord, "; MP x y s i     Motion with button pressed at (x,y), special 's', window 'i'\n");
	xprintf(us_logrecord, "; MR x y s i     Motion with button released at (x,y), special 's', window 'i'\n");
	xprintf(us_logrecord, "; ME m i t       Invoked menu 'm', item 'i', time 't'\n");
	xprintf(us_logrecord, "; WS n w h t     Window 'n' grows to (wXh), time 't'\n");
	xprintf(us_logrecord, "; WM n x y t     Window 'n' moves to (x,y), time 't'\n");
	xprintf(us_logrecord, "; FS path        File selected is 'path'\n");
	xprintf(us_logrecord, "; PM v t         Popup menu selected 'v', time 't'\n");
	xprintf(us_logrecord, "; DI i           Item 'i' of dialog selected\n");
	xprintf(us_logrecord, "; DS i c values  Dialog scroll item 'i' selects 'c' lines in 'values'\n");
	xprintf(us_logrecord, "; DE i hc s      Dialog edit item 'i' hit character 'hc', text now 's'\n");
	xprintf(us_logrecord, "; DP i e         Dialog popup item 'i' set to entry 'e'\n");
	xprintf(us_logrecord, "; DC i v         Dialog item 'i' set to value 'v'\n");
	xprintf(us_logrecord, "; DM x y         Dialog coordinates at (x,y)\n");
	xprintf(us_logrecord, "; DD             Dialog done\n");
}

/*
 * routine to begin playback of session logging file "file".  The routine
 * returns true if there is an error.  If "all" is nonzero, playback
 * the entire file with no prompt.
 */
BOOLEAN logplayback(char *file)
{
	char *filename, tempstring[300], *pt, *start;
	BOOLEAN cur, fromdisk;
	BOOLEAN floating;
	REGISTER WINDOWFRAME *wf;
	RECTAREA r;
	REGISTER FILE *saveio;
	REGISTER WINDOWPART *w, *nextw;
	REGISTER INTBIG i, j, wcount, count, wid, hei, uselx, usehx, usely, usehy, sindex;
	REGISTER INTBIG screenlx, screenhx, screenly, screenhy, state;
	REGISTER LIBRARY *lib, *firstlib;

	us_logplay = xopen(file, us_filetypelog, "", &filename);
	if (us_logplay == NULL) return(TRUE);
	ttyputmsg(_("Playing log file..."));
	ttyputmsg(_("   Move mouse continuously to advance playback"));
	ttyputmsg(_("   Click mouse to abort playback"));
	gra_lastplaybacktime = 0;

	/* get current libraries */
	(void)gra_logreadline(tempstring, 300);
	if (strncmp(tempstring, "LC", 2) != 0)
	{
		ttyputerr(_("Log file is corrupt (error %d)"), 1);
		return(TRUE);
	}
	count = atoi(&tempstring[3]);
	firstlib = NOLIBRARY;
	for(i=0; i<count; i++)
	{
		(void)gra_logreadline(tempstring, 300);
		if (strncmp(tempstring, "LD", 2) == 0)
		{
			fromdisk = TRUE;
		} else if (strncmp(tempstring, "LM", 2) == 0)
		{
			fromdisk = FALSE;
		} else
		{
			ttyputerr(_("Log file is corrupt (error %d)"), 2);
			return(TRUE);
		}
		start = &tempstring[3];
		for(pt = start; *pt != 0; pt++) if (*pt == ' ') break;
		if (*pt == 0)
		{
			ttyputerr(_("Log file is corrupt (error %d)"), 3);
			return(TRUE);
		}
		*pt++ = 0;
		lib = getlibrary(start);
		if (lib == NOLIBRARY)
		{
			/* read library file "gra_localstring" */
			lib = newlibrary(start, pt);
			if (lib == NOLIBRARY) continue;
		}
		if (fromdisk)
		{
			saveio = us_logplay;
			us_logplay = 0;
			(void)asktool(io_tool, "read", (INTBIG)lib, (INTBIG)"binary", 0);
			us_logplay = saveio;
		}
		if (firstlib == NOLIBRARY) firstlib = lib;
	}
	selectlibrary(firstlib);

	/* delete all existing windows */
	for(w = el_topwindowpart; w != NOWINDOWPART; w = nextw)
	{
		nextw = w->nextwindowpart;
		db_retractwindowpart(w);
	}
	el_curwindowpart = NOWINDOWPART;

	/* get current windows */
	(void)gra_logreadline(tempstring, 300);
	if (strncmp(tempstring, "WO", 2) != 0)
	{
		ttyputerr(_("Log file is corrupt (error %d)"), 4);
		return(TRUE);
	}
	gra_windownumber = atoi(&tempstring[3]);

	/* get current windows */
	(void)gra_logreadline(tempstring, 300);
	if (strncmp(tempstring, "WT", 2) != 0)
	{
		ttyputerr(_("Log file is corrupt (error %d)"), 5);
		return(TRUE);
	}
	count = atoi(&tempstring[3]);

	for(i=0; i<count; i++)
	{
		(void)gra_logreadline(tempstring, 300);
		if (strncmp(tempstring, "WC", 2) == 0)
		{
			floating = 1;
		} else if (strncmp(tempstring, "WE", 2) == 0)
		{
			floating = 0;
		} else
		{
			ttyputerr(_("Log file is corrupt (error %d)"), 6);
			return(TRUE);
		}
		pt = &tempstring[3];
		sindex = atoi(getkeyword(&pt, " "));
		r.left = atoi(getkeyword(&pt, " "));
		r.top = atoi(getkeyword(&pt, " "));
		wid = atoi(getkeyword(&pt, " "));
		hei = atoi(getkeyword(&pt, " "));
		r.right = r.left + (INTSML)wid;
		r.bottom = r.top + (INTSML)hei;
		if (floating)
		{
			/* get the floating window frame */
			for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
				if (wf->floating) break;
			if (wf == NOWINDOWFRAME) wf = newwindowframe(TRUE, &r);
		} else
		{
			/* create a new window frame */
			wf = newwindowframe(FALSE, &r);
		}
		wf->windindex = sindex;

		(void)gra_logreadline(tempstring, 300);
		if (strncmp(tempstring, "WP", 2) != 0)
		{
			ttyputerr(_("Log file is corrupt (error %d)"), 7);
			return(TRUE);
		}
		wcount = atoi(&tempstring[3]);
		for(j=0; j<wcount; j++)
		{
			(void)gra_logreadline(tempstring, 300);
			if (strncmp(tempstring, "WB", 2) == 0)
			{
				cur = FALSE;
			} else if (strncmp(tempstring, "WF", 2) == 0)
			{
				cur = TRUE;
			} else
			{
				ttyputerr(_("Log file is corrupt (error %d)"), 8);
				return(TRUE);
			}
			pt = &tempstring[3];
			uselx = atoi(getkeyword(&pt, " "));
			usehx = atoi(getkeyword(&pt, " "));
			usely = atoi(getkeyword(&pt, " "));
			usehy = atoi(getkeyword(&pt, " "));
			screenlx = atoi(getkeyword(&pt, " "));
			screenhx = atoi(getkeyword(&pt, " "));
			screenly = atoi(getkeyword(&pt, " "));
			screenhy = atoi(getkeyword(&pt, " "));
			state = atoi(getkeyword(&pt, " "));
			start = getkeyword(&pt, " ");
			while (*pt != 0 && *pt != ' ') pt++;
			if (*pt == ' ') pt++;

			w = newwindowpart(pt, NOWINDOWPART);
			w->buttonhandler = DEFAULTBUTTONHANDLER;
			w->charhandler = DEFAULTCHARHANDLER;
			w->changehandler = DEFAULTCHANGEHANDLER;
			w->termhandler = DEFAULTTERMHANDLER;
			w->redisphandler = DEFAULTREDISPHANDLER;
			w->uselx = uselx;   w->usehx = usehx;
			w->usely = usely;   w->usehy = usehy;
			w->screenlx = screenlx;   w->screenhx = screenhx;
			w->screenly = screenly;   w->screenhy = screenhy;
			computewindowscale(w);
			w->state = state;
			w->curnodeproto = getnodeproto(start);
			w->frame = wf;
			if (cur) el_curwindowpart = w;
			us_redisplay(w);
		}
	}
	(void)gra_logreadline(tempstring, 300);
	if (strncmp(tempstring, "CI", 2) != 0)
	{
		ttyputerr(_("Log file is corrupt (error %d)"), 9);
		return(TRUE);
	}
	gra_windowframeindex = atoi(&tempstring[3]);

	/* switch to proper technology */
	(void)gra_logreadline(tempstring, 300);
	if (strncmp(tempstring, "CT", 2) != 0)
	{
		ttyputerr(_("Log file is corrupt (error %d)"), 10);
		return(TRUE);
	}
	us_ensurepropertechnology(NONODEPROTO, &tempstring[3], TRUE);

	return(FALSE);
}

/*
 * routine to terminate session logging
 */
void logfinishrecord(void)
{
	if (us_logrecord != NULL)
	{
		xclose(us_logrecord);
		if (fileexistence(gra_logfilesave) == 1)
			eunlink(gra_logfilesave);
		rename(gra_logfile, gra_logfilesave);
	}
	us_logrecord = NULL;
}

/*
 * Routine to log an event (if logging) of type "inputstate".
 * The event has parameters (cursorx,cursory) and "extradata", depending on "inputstate":
 *   WINDOWSIZE:    window "extradata" is now "cursorx" x "cursory"
 *   WINDOWMOVE:    window "extradata" is now at (cursorx, cursory)
 *   MENUEVENT:     selected menu "cursorx", item "cursory"
 *   FILEREPLY:     file selected by standard-file dialog is in "extradata"
 *   POPUPSELECT:   popup menu returned "cursorx"
 *   DIAITEMCLICK:  dialog item "cursorx" clicked
 *   DIASCROLLSEL:  dialog scroll item "cursorx" set to "cursory" entries in "extradata"
 *   DIAEDITTEXT:   dialog edit item "cursorx" addec char "cursory" and changed to "extradata"
 *   DIAPOPUPSEL:   dialog popup item "cursorx" set to entry "cursory"
 *   DIASETCONTROL: dialog control item "cursorx" changed to "cursory"
 *   DIAENDDIALOG:  dialog terminated
 *   all others:    cursor in (cursorx,cursory) and window index in "extradata"
 */
void gra_logwriteaction(INTBIG inputstate, INTBIG special, INTBIG cursorx, INTBIG cursory,
	void *extradata)
{
	REGISTER char *filename;
	REGISTER INTBIG i, trueItem;
	REGISTER POPUPMENU *pm;
	REGISTER POPUPMENUITEM *mi;
	REGISTER WINDOWFRAME *wf;

	if (us_logrecord == NULL) return;

	/* ignore redundant cursor motion */
	if (inputstate == MOTION || inputstate == (MOTION|BUTTONUP))
	{
		i = (INTBIG)extradata;
		if (inputstate == gra_lastloggedaction && cursorx == gra_lastloggedx &&
			cursory == gra_lastloggedy && i == gra_lastloggedindex)
				return;
	}
	gra_lastloggedaction = inputstate;
	gra_lastloggedx = cursorx;
	gra_lastloggedy = cursory;
	gra_lastloggedindex = (INTBIG)extradata;

	if ((inputstate&MOTION) != 0)
	{
		if ((inputstate&BUTTONUP) != 0)
		{
			xprintf(us_logrecord, "MR %ld %ld %ld %ld\n",
				cursorx, cursory, special, extradata);
		} else
		{
			xprintf(us_logrecord, "MP %ld %ld %ld %ld\n",
				cursorx, cursory, special, extradata);
		}
	} else if ((inputstate&ISKEYSTROKE) != 0)
	{
		xprintf(us_logrecord, "KT '%s' %ld %ld %ld %ld\n",
			us_describeboundkey(inputstate&CHARREAD, special, 1),
				cursorx, cursory, extradata, ticktime()-gra_logbasetime);
	} else if ((inputstate&ISBUTTON) != 0)
	{
		if ((inputstate&BUTTONUP) != 0)
		{
			xprintf(us_logrecord, "BR %ld %ld %ld %ld %ld %ld\n",
				inputstate&(ISBUTTON|SHIFTISDOWN|METAISDOWN|CONTROLISDOWN),
					cursorx, cursory, special, extradata, ticktime()-gra_logbasetime);
		} else
		{
			if ((inputstate&DOUBLECL) != 0)
			{
				xprintf(us_logrecord, "BD %ld %ld %ld %ld %ld %ld\n",
					inputstate&(ISBUTTON|SHIFTISDOWN|METAISDOWN|CONTROLISDOWN),
						cursorx, cursory, special, extradata, ticktime()-gra_logbasetime);
			} else
			{
				xprintf(us_logrecord, "BP %ld %ld %ld %ld %ld %ld\n",
					inputstate&(ISBUTTON|SHIFTISDOWN|METAISDOWN|CONTROLISDOWN),
						cursorx, cursory, special, extradata, ticktime()-gra_logbasetime);
			}
		}
	} else if (inputstate == MENUEVENT)
	{
		xprintf(us_logrecord, "ME %ld %ld %ld", cursorx, cursory,
			ticktime()-gra_logbasetime);
		for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
			if (!wf->floating) break;
		if (wf != NOWINDOWFRAME && cursorx >= 0 && cursorx < wf->pulldownmenucount)
		{
			pm = wf->pulldowns[cursorx];
			if (cursory >= 0 && cursory < pm->total)
			{
				mi = &pm->list[cursory];
				xprintf(us_logrecord, "   ; command=%s",
					us_stripampersand(mi->attribute));
			}
		}
		xprintf(us_logrecord, "\n");
	} else if (inputstate == FILEREPLY)
	{
		filename = (char *)extradata;
		xprintf(us_logrecord, "FS %s\n", filename);
	} else if (inputstate == POPUPSELECT)
	{
		xprintf(us_logrecord, "PM %ld %ld\n", cursorx, ticktime()-gra_logbasetime);
	} else if (inputstate == DIAEDITTEXT)
	{
		filename = (char *)extradata;
		xprintf(us_logrecord, "DE %ld %ld %s\n", cursorx, cursory, filename);
	} else if (inputstate == DIASCROLLSEL)
	{
		xprintf(us_logrecord, "DS %ld %ld", cursorx, cursory);
		for(i=0; i<cursory; i++) xprintf(us_logrecord, " %ld", ((INTBIG *)extradata)[i]);
		xprintf(us_logrecord, "\n");
	} else if (inputstate == DIAPOPUPSEL)
	{
		xprintf(us_logrecord, "DP %ld %ld\n", cursorx, cursory);
	} else if (inputstate == DIASETCONTROL)
	{
		xprintf(us_logrecord, "DC %ld %ld\n", cursorx, cursory);
	} else if (inputstate == DIAITEMCLICK)
	{
		xprintf(us_logrecord, "DI %ld\n", cursorx);
	} else if (inputstate == DIAUSERMOUSE)
	{
		xprintf(us_logrecord, "DM %ld %ld\n", cursorx, cursory);
	} else if (inputstate == DIAENDDIALOG)
	{
		xprintf(us_logrecord, "DD\n");
	} else if (inputstate == WINDOWMOVE)
	{
		xprintf(us_logrecord, "WM %ld %ld %ld %ld\n", extradata,
			cursorx, cursory, ticktime()-gra_logbasetime);
	} else if (inputstate == WINDOWSIZE)
	{
		xprintf(us_logrecord, "WS %ld %ld %ld %ld\n", extradata,
			cursorx, cursory, ticktime()-gra_logbasetime);
	} else
	{
		ttyputmsg("Unknown event being logged: %ld", inputstate);
	}

	/* flush the log file every so often */
	gra_logrecordcount++;
	if (gra_logrecordcount >= us_logflushfreq)
	{
		gra_logrecordcount = 0;
		xflushbuf(us_logrecord);
	}
}

void gra_logwritecomment(char *comment)
{
	if (us_logrecord == 0) return;
	xprintf(us_logrecord, "; %s\n", comment);
}

BOOLEAN gra_loggetnextaction(char *message)
{
	REGISTER BOOLEAN eof;
	char tempstring[300], *pt, *start;
	REGISTER WINDOWFRAME *wf;
	INTSML boundkey;
	INTBIG sellist[MAXSCROLLMULTISELECT], i, x, y, item, count;
	UINTBIG nowtime;

	eof = gra_logreadline(tempstring, 300);
	if (stopping(STOPREASONPLAYBACK)) eof = TRUE;
	if (eof)
	{
		/* stop playback */
		ttyputmsg(_("End of session playback file"));
		xclose(us_logplay);
		us_logplay = NULL;
		return(TRUE);
	}

	/* load the event structure */
	if (strncmp(tempstring, "WS", 2) == 0)
	{
		gra_inputstate = NOEVENT;
		pt = &tempstring[3];
		x = atoi(getkeyword(&pt, " "));
		y = atoi(getkeyword(&pt, " "));
		wf = gra_getframefromindex(atoi(getkeyword(&pt, " ")));
		nowtime = atoi(getkeyword(&pt, " "));
		gra_timeoffset += nowtime - gra_lastplaybacktime;
		gra_lastplaybacktime = nowtime;
		if (wf != NOWINDOWFRAME)
			sizewindowframe(wf, x, y);
		return(FALSE);
	}
	if (strncmp(tempstring, "WM", 2) == 0)
	{
		gra_inputstate = NOEVENT;
		pt = &tempstring[3];
		x = atoi(getkeyword(&pt, " "));
		y = atoi(getkeyword(&pt, " "));
		wf = gra_getframefromindex(atoi(getkeyword(&pt, " ")));
		nowtime = atoi(getkeyword(&pt, " "));
		gra_timeoffset += nowtime - gra_lastplaybacktime;
		gra_lastplaybacktime = nowtime;
		if (wf != NOWINDOWFRAME)
			movewindowframe(wf, x, y);
		return(FALSE);
	}
#ifdef USETIF
	if (strncmp(tempstring, "DI", 2) == 0)
	{
		gra_inputstate = NOEVENT;
		pt = &tempstring[3];
		gra_dialoghit = atoi(getkeyword(&pt, " "));
		return(FALSE);
	}
	if (strncmp(tempstring, "DS", 2) == 0)
	{
		gra_inputstate = NOEVENT;
		pt = &tempstring[3];
		item = atoi(getkeyword(&pt, " "));
		count = atoi(getkeyword(&pt, " "));
		for(i=0; i<count; i++) sellist[i] = atoi(getkeyword(&pt, " "));
		if (count == 1)
		{
			DiaSelectLine(item, sellist[0]);
		} else
		{
			DiaSelectLines(item, count, sellist);
		}
		return(FALSE);
	}
	if (strncmp(tempstring, "DP", 2) == 0)
	{
		gra_inputstate = NOEVENT;
		pt = &tempstring[3];
		item = atoi(getkeyword(&pt, " "));
		i = atoi(getkeyword(&pt, " "));
		DiaSetPopupEntry(item, i);
		return(FALSE);
	}
	if (strncmp(tempstring, "DC", 2) == 0)
	{
		gra_inputstate = NOEVENT;
		pt = &tempstring[3];
		item = atoi(getkeyword(&pt, " "));
		i = atoi(getkeyword(&pt, " "));
		DiaSetControl(item, i);
		return(FALSE);
	}
	if (strncmp(tempstring, "DE", 2) == 0)
	{
		gra_inputstate = NOEVENT;
		pt = &tempstring[3];
		gra_dialoghit = atoi(getkeyword(&pt, " "));
		gra_dialoghitchar = atoi(getkeyword(&pt, " "));
		while (*pt != 0 && *pt != ' ') pt++;
		if (*pt == ' ') pt++;
		DiaSetText(gra_dialoghit, pt);
		return(FALSE);
	}
	if (strncmp(tempstring, "DM", 2) == 0)
	{
		gra_inputstate = DIAUSERMOUSE;
		pt = &tempstring[3];
		gra_action.x = atoi(getkeyword(&pt, " "));
		gra_action.y = atoi(getkeyword(&pt, " "));
		return(FALSE);
	}
	if (strncmp(tempstring, "DD", 2) == 0)
	{
		gra_inputstate = DIAENDDIALOG;
		return(FALSE);
	}
#endif
	if (strncmp(tempstring, "ME", 2) == 0)
	{
		gra_inputstate = MENUEVENT;
		pt = &tempstring[3];
		gra_cursorx = atoi(getkeyword(&pt, " "));
		gra_cursory = atoi(getkeyword(&pt, " "));
		nowtime = atoi(getkeyword(&pt, " "));
		gra_timeoffset += nowtime - gra_lastplaybacktime;
		gra_lastplaybacktime = nowtime;
		return(FALSE);
	}
	if (strncmp(tempstring, "FS", 2) == 0)
	{
		gra_inputstate = FILEREPLY;
		pt = &tempstring[3];
		if (message != 0) strcpy(message, pt);
		return(FALSE);
	}
	if (strncmp(tempstring, "PM", 2) == 0)
	{
		gra_inputstate = POPUPSELECT;
		pt = &tempstring[3];
		gra_cursorx = atoi(getkeyword(&pt, " "));
		nowtime = atoi(getkeyword(&pt, " "));
		gra_timeoffset += nowtime - gra_lastplaybacktime;
		gra_lastplaybacktime = nowtime;
		return(FALSE);
	}

	if (strncmp(tempstring, "MR", 2) == 0 || strncmp(tempstring, "MP", 2) == 0)
	{
		pt = &tempstring[3];
		gra_inputstate = MOTION;
		gra_cursorx = atoi(getkeyword(&pt, " "));
		gra_cursory = atoi(getkeyword(&pt, " "));
		gra_inputspecial = atoi(getkeyword(&pt, " "));
		i = atoi(getkeyword(&pt, " "));
		if (tempstring[1] == 'R') gra_inputstate |= BUTTONUP;
	} else if (strncmp(tempstring, "KT", 2) == 0)
	{
		start = &tempstring[4];
		for(i = strlen(start)-1; i>0; i--)
			if (start[i] == '\'') break;
		if (i <= 0) return(FALSE);
		start[i] = 0;
		pt = &start[i+2];
		(void)us_getboundkey(start, &boundkey, &gra_inputspecial);
		gra_inputstate = ISKEYSTROKE | (boundkey & CHARREAD);
		gra_cursorx = atoi(getkeyword(&pt, " "));
		gra_cursory = atoi(getkeyword(&pt, " "));
		i = atoi(getkeyword(&pt, " "));
		nowtime = atoi(getkeyword(&pt, " "));
		gra_timeoffset += nowtime - gra_lastplaybacktime;
		gra_lastplaybacktime = nowtime;
	} else if (tempstring[0] == 'B')
	{
		pt = &tempstring[3];
		gra_inputstate = atoi(getkeyword(&pt, " "));
		gra_cursorx = atoi(getkeyword(&pt, " "));
		gra_cursory = atoi(getkeyword(&pt, " "));
		gra_inputspecial = atoi(getkeyword(&pt, " "));
		i = atoi(getkeyword(&pt, " "));
		if (tempstring[1] == 'D') gra_inputstate |= DOUBLECL;
		if (tempstring[1] == 'R') gra_inputstate |= BUTTONUP;
		nowtime = atoi(getkeyword(&pt, " "));
		gra_timeoffset += nowtime - gra_lastplaybacktime;
		gra_lastplaybacktime = nowtime;
	}
	wf = gra_getframefromindex(i);
	if (wf != NOWINDOWFRAME && wf != el_curwindowframe)
	{
		XMapRaised(wf->topdpy, wf->topwin);
		(void)gra_getcurrentwindowframe(wf->toplevelwidget, TRUE);
	}
	us_state &= ~GOTXY;
	return(FALSE);
}

BOOLEAN gra_logreadline(char *string, INTBIG limit)
{
	for(;;)
	{
		if (xfgets(string, limit, us_logplay) != 0) return(TRUE);
		if (string[0] != ';') break;
	}
	return(FALSE);
}

WINDOWFRAME *gra_getframefromindex(INTBIG index)
{
	REGISTER WINDOWFRAME *wf;

	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
		if (wf->windindex == index) return(wf);
	return(NOWINDOWFRAME);
}

/****************************** MENUS ******************************/

void getacceleratorstrings(char **acceleratorstring, char **acceleratorprefix)
{
	*acceleratorstring = "Ctrl";
	*acceleratorprefix = "Ctrl-";
}

char *getinterruptkey(void)
{
	return(_("Control-C in Messages Window"));
}

INTBIG nativepopupmenu(POPUPMENU **menuptr, BOOLEAN header, INTBIG left, INTBIG top)
{
#ifdef USETIF
	INTBIG retval;

	retval = gra_nativepopuptif(menuptr, header, left, top);
	gra_logwriteaction(POPUPSELECT, 0, retval, 0, 0);
	return(retval);
#else
	return(-1);
#endif
}

#ifdef USETIF
INTBIG gra_nativepopuptif(POPUPMENU **menuptr, BOOLEAN header, INTBIG left, INTBIG top)
{
	Widget popmenu, child, submenu, subchild, *submenulist;
	Arg arg[10];
	INTBIG ac, i, j, submenuindex, submenus;
	char msg[200], *pt;
	POPUPMENUITEM *mi, *submi;
	POPUPMENU *menu, **subpmlist;
	REGISTER WINDOWFRAME *wf;
	REGISTER USERCOM *uc;

	while (us_logplay != NULL)
	{
		if (gra_loggetnextaction(0)) break;
		j = gra_inputstate;
		gra_inputstate = NOEVENT;
		if (j == POPUPSELECT) return(gra_cursorx);
	}

	menu = *menuptr;
	wf = el_curwindowframe;
	ac = 0;
	popmenu = (Widget)XmCreatePopupMenu(wf->graphicswidget, "menu", arg, ac);
	if (header)
	{
		header = 2;
		child = (Widget)XmCreatePushButton(popmenu, menu->header, NULL, 0);
		XtManageChild(child);
		XtAddCallback(child, XmNactivateCallback,
			(XtCallbackProc)gra_menupcb, (XtPointer)-2);
		XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, popmenu, NULL);
	}

	/* count the number of submenus */
	submenus = 0;
	for(j=0; j<menu->total; j++)
	{
		mi = &menu->list[j];
		if (*mi->attribute != 0 && mi->response != NOUSERCOM &&
			mi->response->menu != NOPOPUPMENU) submenus++;
	}
	if (submenus > 0)
	{
		subpmlist = (POPUPMENU **)emalloc(submenus * (sizeof (POPUPMENU *)), us_tool->cluster);
		if (subpmlist == 0) return(-1);
		submenulist = (Widget *)emalloc(submenus * (sizeof (Widget)), us_tool->cluster);
		if (submenulist == 0) return(-1);
	}

	/* load the menus */
	submenus = 0;
	for(j=0; j<menu->total; j++)
	{
		mi = &menu->list[j];
		mi->changed = FALSE;
		if (*mi->attribute == 0)
		{
			XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, popmenu, NULL);
		} else
		{
			i = 0;
			for(pt = mi->attribute; *pt != 0; pt++)
				if (*pt != '&') msg[i++] = *pt;
			msg[i] = 0;

			uc = mi->response;
			if (uc != NOUSERCOM && uc->menu != NOPOPUPMENU)
			{
				submenu = (Widget)XmVaCreateSimplePulldownMenu(popmenu, "menu", j+header,
					(XtCallbackProc)gra_menupcb, NULL);
				if (submenu == 0) return(-1);
				submenulist[submenus] = submenu;
				subpmlist[submenus] = uc->menu;
				submenus++;
				XtSetArg(arg[0], XmNsubMenuId, submenu);
				child = XmCreateCascadeButton(popmenu, msg, arg, 1);
				XtManageChild(child);
				for(i=0; i<uc->menu->total; i++)
				{
					submi = &uc->menu->list[i];
					subchild = (Widget)XmCreatePushButton(submenu, submi->attribute, NULL, 0);
					XtManageChild(subchild);
					XtAddCallback(subchild, XmNactivateCallback,
						(XtCallbackProc)gra_menupcb, (XtPointer)((submenus<<16) | i));
				}
			} else
			{
				child = (Widget)XmCreatePushButton(popmenu, msg, NULL, 0);
				XtManageChild(child);
				XtAddCallback(child, XmNactivateCallback,
					(XtCallbackProc)gra_menupcb, (XtPointer)j);
			}
		}
	}
	XtAddCallback(popmenu, XmNunmapCallback,
		(XtCallbackProc)gra_menupcb, (XtPointer)-2);
	XmMenuPosition(popmenu, (XButtonPressedEvent *)&gra_lastbuttonpressevent);
	XtManageChild(popmenu);

	gra_popupmenuresult = -1;
	gra_inputstate = NOEVENT;
	while (gra_popupmenuresult == -1)
	{
		gra_nextevent();
		if (gra_inputstate == NOEVENT) continue;
		if ((gra_inputstate & ISBUTTON) != 0 && (gra_inputstate & BUTTONUP) != 0)
		{
			gra_inputstate = NOEVENT;
			break;
		}
	}
	XtDestroyWidget(popmenu);
#if 0
	for(j=0; j<submenus; j++)
		XtDestroyWidget(submenulist[j]);
#endif
	if (gra_popupmenuresult >= 0)
	{
		submenuindex = gra_popupmenuresult >> 16;
		if (submenuindex != 0) *menuptr = subpmlist[submenuindex-1];
	}
	if (submenus > 0)
	{
		efree((char *)submenulist);
		efree((char *)subpmlist);
	}
	if (gra_popupmenuresult < 0) return(-1);
	return(gra_popupmenuresult & 0xFFFF);
}
#endif

void gra_nativemenudoone(WINDOWFRAME *wf, INTBIG low, INTBIG high)
{
#ifdef USETIF
	gra_addeventtoqueue(MENUEVENT, 0, low, high);
#endif
}

BOOLEAN nativemenuload(INTBIG count, char *par[])
{
#ifdef USETIF
	REGISTER INTBIG i;
	REGISTER WINDOWFRAME *wf;

	/* remember the top-level pulldown menus */
	for(i=0; i<count; i++)
		gra_pulldowns[i] = us_getpopupmenu(par[i]);
	gra_pulldownmenucount = count;

	/* build the pulldown menu bar */
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->floating) continue;

		/* delete any dummy menu created during initialization */
		if (wf->firstmenu != 0)
		{
			XtUnmanageChild(wf->firstmenu);
			XtDestroyWidget(wf->firstmenu);
			wf->firstmenu = 0;
		}

		/* load menus into this window */
		gra_pulldownmenuload(wf);
	}
	return(FALSE);
#else
	return(TRUE);
#endif
}

void gra_pulldownmenuload(WINDOWFRAME *wf)
{
#ifdef USETIF
	REGISTER INTBIG i, j;
	REGISTER INTBIG keysym;
	char myline[256], *pt;
	char mstring[2];
	REGISTER POPUPMENU *pm;
	Widget thismenu, child;

	if (gra_pulldownmenucount == 0)
	{
		/* no menus defined yet: make a dummy one */
		wf->firstmenu = XtVaCreateManagedWidget("File", xmCascadeButtonWidgetClass, wf->menubar, NULL);
		return;
	}

	/* load full menus */
	mstring[1] = 0;
	for(i=0; i<gra_pulldownmenucount; i++)
	{
		pm = gra_pulldowns[i];
		if (pm == NOPOPUPMENU) continue;

		/* see if there is a mnemonic */
		mstring[0] = 0;
		pt = myline;
		for(j=0; pm->header[j] != 0; j++)
		{
			if (pm->header[j] == '&') mstring[0] = pm->header[j+1]; else
				*pt++ = pm->header[j];
		}
		*pt = 0;

		child = XtVaCreateManagedWidget(myline, xmCascadeButtonWidgetClass, wf->menubar, NULL);
		if (mstring[0] != 0)
		{
			keysym = XStringToKeysym(mstring);
			XtVaSetValues(child, XmNmnemonic, keysym, NULL);
		}
		thismenu = XmVaCreateSimplePulldownMenu(wf->menubar, "menu", i,
			(XtCallbackProc)gra_menucb, NULL);
		if (thismenu == 0) return;
		gra_pulldownindex(wf, pm, i, thismenu);
	}
#endif
}

void nativemenurename(POPUPMENU *pm, INTBIG pindex)
{
#ifdef USETIF
	INTBIG i, j, len, checked, special;
	INTSML key;
	char line[100], *pt, metaline[50];
	Widget w;
	Arg arg[5];
	XmString label, acctext;
	REGISTER WINDOWFRAME *wf;

	/* see if there is a mnemonic */
	pt = line;
	for(j=0; pm->list[pindex].attribute[j] != 0; j++)
	{
		if (pm->list[pindex].attribute[j] != '&')
			*pt++ = pm->list[pindex].attribute[j];
	}
	*pt = 0;

	/* see if there is a check */
	checked = -1;
	len = strlen(line) - 1;
	if (len > 0 && line[len] == '<')
	{
		line[len] = 0;
		if (line[0] == '>')
		{
			checked = 1;
			strcpy(line, &line[1]);
		} else
		{
			checked = 0;
		}
	}

	/* look for accelerators */
	len = strlen(line);
	metaline[0] = 0;
	for(pt = line; *pt != 0; pt++) if (*pt == '/' || *pt == '\\') break;
	if (*pt != 0)
	{
		if (pt[1] != 0)
		{
			(void)us_getboundkey(pt, &key, &special);
			strcpy(metaline, us_describeboundkey(key, special, 1));
		}
		*pt = 0;
	}

	/* set each window's menu */
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->floating) continue;
		for(i=0; i<wf->pulldownmenucount; i++)
			if (wf->pulldowns[i] == pm) break;
		if (i >= wf->pulldownmenucount) break;
		if (pindex < 0 || pindex > wf->pulldownmenusize[i]) break;
		w = wf->pulldownmenulist[i][pindex];
		if (w == 0) continue;

		label = XmStringCreateLocalized(line);
		XtSetArg(arg[0], XmNlabelString, label);
		XtSetValues(w, arg, 1);
		XmStringFree(label);

		acctext = XmStringCreateLocalized(metaline);
		XtSetArg(arg[0], XmNacceleratorText, acctext);
		XtSetValues(w, arg, 1);
		XmStringFree(acctext);

		if (checked == 0)
		{
			XmToggleButtonSetState(w, False, 0);
		} else if (checked == 1)
		{
			XmToggleButtonSetState(w, True, 0);
		}
	}
#endif
}

#ifdef USETIF

/*
 * Routine to create a pulldown menu from popup menu "pm".
 * Returns an index to the table of pulldown menus (-1 on error).
 */
void gra_pulldownindex(WINDOWFRAME *wf, POPUPMENU *pm, INTBIG order, Widget parent)
{
	REGISTER INTBIG i, pindex, *newpulldownmenucount;
	Widget *newpulldownmenus, **newpulldownmenulist;
	POPUPMENU **newpulldowns;

	/* see if it is in the list already */
	for(i=0; i<wf->pulldownmenucount; i++)
		if (wf->pulldowns[i] == pm) return;

	/* allocate new space with one more */
	newpulldownmenus = (Widget *)emalloc((wf->pulldownmenucount+1) *
		(sizeof (Widget)), us_tool->cluster);
	if (newpulldownmenus == 0) return;
	newpulldowns = (POPUPMENU **)emalloc((wf->pulldownmenucount+1) *
		(sizeof (POPUPMENU *)), us_tool->cluster);
	if (newpulldowns == 0) return;
	newpulldownmenucount = (INTBIG *)emalloc((wf->pulldownmenucount+1) *
		SIZEOFINTBIG, us_tool->cluster);
	if (newpulldownmenucount == 0) return;
	newpulldownmenulist = (Widget **)emalloc((wf->pulldownmenucount+1) *
		(sizeof (Widget *)), us_tool->cluster);
	if (newpulldownmenulist == 0) return;

	/* copy former arrays then delete them */
	for(i=0; i<wf->pulldownmenucount; i++)
	{
		newpulldownmenus[i] = wf->pulldownmenus[i];
		newpulldowns[i] = wf->pulldowns[i];
		newpulldownmenucount[i] = wf->pulldownmenusize[i];
		newpulldownmenulist[i] = wf->pulldownmenulist[i];
	}
	if (wf->pulldownmenucount != 0)
	{
		efree((char *)wf->pulldownmenus);
		efree((char *)wf->pulldowns);
		efree((char *)wf->pulldownmenusize);
		efree((char *)wf->pulldownmenulist);
	}
	wf->pulldownmenus = newpulldownmenus;
	wf->pulldowns = newpulldowns;
	wf->pulldownmenusize = newpulldownmenucount;
	wf->pulldownmenulist = newpulldownmenulist;

	pindex = wf->pulldownmenucount++;
	wf->pulldownmenus[pindex] = parent;
	wf->pulldowns[pindex] = pm;
	wf->pulldownmenusize[pindex] = pm->total;
	wf->pulldownmenulist[pindex] = (Widget *)emalloc(pm->total * (sizeof (Widget)),
		us_tool->cluster);
	if (wf->pulldownmenulist[pindex] == 0) return;

	gra_makepdmenu(wf, pm, order, parent, pindex);
}

/*
 * Routine to create pulldown menu number "value" from the popup menu in "pm" and return
 * the menu handle.
 */
Widget gra_makepdmenu(WINDOWFRAME *wf, POPUPMENU *pm, INTBIG value, Widget thismenu, INTBIG pindex)
{
	REGISTER INTBIG i, j, keysym, checked, haveacc, len;
	char myline[256], metaline[50], *pt;
	char mstring[2];
	REGISTER USERCOM *uc;
	REGISTER POPUPMENUITEM *mi;
	Widget submenu, *childlist;
	INTBIG special;
	INTSML key;
	Arg arg[5];
	XmString acctext;

	/* build the actual menu */
	childlist = wf->pulldownmenulist[pindex];
	mstring[1] = 0;
	for(i=0; i<pm->total; i++)
	{
		childlist[i] = 0;
		mi = &pm->list[i];

		/* see if there is a mnemonic */
		mstring[0] = 0;
		pt = myline;
		for(j=0; mi->attribute[j] != 0; j++)
		{
			if (mi->attribute[j] == '&') mstring[0] = mi->attribute[j+1]; else
				*pt++ = mi->attribute[j];
		}
		*pt = 0;

		uc = mi->response;
		if (uc->active < 0)
		{
			if (*mi->attribute == 0)
			{
				/* separator */
				XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, thismenu, NULL);
			} else
			{
				/* dimmed item (DIM IT!!!) */
				childlist[i] = (Widget)XmCreatePushButton(thismenu, myline, NULL, 0);
			}
			continue;
		}

		/* see if this command is another menu */
		if (uc->menu != NOPOPUPMENU)
		{
			submenu = (Widget)XmVaCreateSimplePulldownMenu(thismenu, "menu", i,
				(XtCallbackProc)gra_menucb, NULL);
			if (submenu == 0) return(0);
			XtVaSetValues(submenu, XmNtearOffModel, XmTEAR_OFF_ENABLED, NULL);
			XtSetArg(arg[0], XmNsubMenuId, submenu);
			childlist[i] = XmCreateCascadeButton(thismenu, myline, arg, 1);
			if (mstring[0] != 0)
			{
				keysym = XStringToKeysym(mstring);
				XtVaSetValues(childlist[i], XmNmnemonic, keysym, NULL);
			}
			XtManageChild(childlist[i]);
			gra_pulldownindex(wf, uc->menu, i, submenu);
			continue;
		}

		/* see if there is a check */
		checked = -1;
		len = strlen(myline) - 1;
		if (myline[len] == '<')
		{
			myline[len] = 0;
			if (myline[0] == '>')
			{
				checked = 1;
				strcpy(myline, &myline[1]);
			} else
			{
				checked = 0;
			}
		}

		/* get command title and accelerator */
		len = strlen(myline);
		haveacc = 0;
		for(pt = myline; *pt != 0; pt++) if (*pt == '/' || *pt == '\\') break;
		if (*pt != 0)
		{
			haveacc = 1;
			(void)us_getboundkey(pt, &key, &special);
			strcpy(metaline, us_describeboundkey(key, special, 1));
			acctext = XmStringCreateLocalized(metaline);
			*pt = 0;
		}
		if (checked == -1)
		{
			childlist[i] = (Widget)XmCreatePushButton(thismenu, myline, NULL, 0);
		} else
		{
			childlist[i] = (Widget)XmCreateToggleButton(thismenu, myline, NULL, 0);
		}
		if (mstring[0] != 0)
			XtVaSetValues(childlist[i], XmNmnemonic, XStringToKeysym(mstring), NULL);
		XtManageChild(childlist[i]);
		if (checked == 0) XmToggleButtonSetState(childlist[i], False, 0); else
			if (checked == 1) XmToggleButtonSetState(childlist[i], True, 0);
		if (haveacc != 0)
		{
			XtVaSetValues(childlist[i], XmNacceleratorText, acctext, NULL);
			XmStringFree(acctext);
		}
		if (checked == -1)
		{
			XtAddCallback(childlist[i], XmNactivateCallback,
				(XtCallbackProc)gra_menucb, (XtPointer)i);
		} else
		{
			XtAddCallback(childlist[i], XmNvalueChangedCallback,
				(XtCallbackProc)gra_menucb, (XtPointer)i);
		}
	}
	return(thismenu);
}

void gra_menucb(Widget w, int item_number, XtPointer call_data)
{
	Widget parent;
	INTBIG i;
	REGISTER WINDOWFRAME *wf;

	parent = XtParent(w);
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->floating) continue;

		for(i=0; i<wf->pulldownmenucount; i++)
		{
			if (wf->pulldownmenus[i] != parent) continue;
			gra_nativemenudoone(wf, i, item_number);
			return;
		}
	}
}

void gra_menupcb(Widget w, int item_number, XtPointer call_data)
{
	gra_popupmenuresult = item_number;
}
#endif


#ifdef USETIF

/****************************** MOTIF/LESSTIF DIALOGS ******************************/

/*
 * Routine to initialize a dialog described by "dialog".
 * Returns true if dialog cannot be initialized.
 */
BOOLEAN DiaInitDialog(DIALOG *dialog)
{
	Widget temp, focus, base;
	Arg arg[20];
	char *title, *line, **fontnames;
	INTBIG i, j, amt, itemtype, x, y, wid, hei;
	int ac, direction, asc, desc;
	XmString str;
	Display *dpy;
	static XFontStruct *font;
	static INTBIG founddialogfont = 0;
	XFontStruct **fontstructs;
	XCharStruct xcs;
	Window win;
	Pixmap map;
	String translation;
	XtActionsRec actions;
	XmFontList xfontlist;
	XmFontContext fontContext;
	XmFontListEntry fontListEntry;
	XmFontType fonttype;
	GC gc;
	XGCValues gcv;
	static INTBIG userdrawactionset = 0, listcopyactionset = 0;

	/* be sure the dialog is translated */
	DiaTranslate(dialog);

	/* get the current dialog structure */
	if (el_curwindowpart == NOWINDOWPART) base = gra_msgtoplevelwidget; else
		base = el_curwindowpart->frame->toplevelwidget;

	gra_curdialogindex++;
	gra_curdialog = &gra_dialogs[gra_curdialogindex];

	/* compute size of some items to automatically scale them */
	if (founddialogfont == 0)
	{
		founddialogfont = 1;
		XtSetArg(arg[0], XmNbuttonFontList, &xfontlist);
		XtGetValues(base, arg, 1);
		XmFontListInitFontContext(&fontContext, xfontlist);
		font = 0;
		for(;;)
		{
			fontListEntry = XmFontListNextEntry(fontContext);
			if (fontListEntry == 0) break;
			font = (XFontStruct *)XmFontListEntryGetFont(fontListEntry, &fonttype);
			if (fonttype == XmFONT_IS_FONT) break;
			i = XFontsOfFontSet((XFontSet)font, &fontstructs, &fontnames);
			if (i > 0)
			{
				font = fontstructs[0];
				break;
			}
			font = 0;
		}
		XmFontListFreeFontContext(fontContext);
	}
	if (font != 0)
	{
		for(i=0; i<dialog->items; i++)
		{
			itemtype = dialog->list[i].type & ITEMTYPE;
			switch (itemtype)
			{
				case DEFBUTTON:
				case BUTTON:
				case CHECK:
				case RADIO:
				case MESSAGE:
					line = dialog->list[i].msg;
					XTextExtents(font, line, strlen(line), &direction, &asc, &desc, &xcs);
					j = xcs.width;
					amt = j - (dialog->list[i].r.right - dialog->list[i].r.left);
					if (amt > 0)
					{
						for(j=0; j<dialog->items; j++)
						{
							if (j == i) continue;
							if (dialog->list[j].r.left >= dialog->list[i].r.right)
								dialog->list[j].r.left += amt;
							if (dialog->list[j].r.right >= dialog->list[i].r.right)
								dialog->list[j].r.right += amt;
						}
						dialog->list[i].r.right += amt;
					}
					break;
			}
		}
		for(i=0; i<dialog->items; i++)
		{
			j = dialog->list[i].r.right + 9;
			if (j < dialog->windowRect.right - dialog->windowRect.left) continue;
			dialog->windowRect.right = dialog->windowRect.left + j;
		}
	}

	/* create the dialog */
	title = dialog->movable;
	if (title == 0 || *title == 0) title = " ";
	gra_getdialogcoordinates(&dialog->windowRect, &x, &y, &wid, &hei);
	ac = 0;
	XtSetArg(arg[ac], XmNdialogStyle,     XmDIALOG_PRIMARY_APPLICATION_MODAL);   ac++;
	XtSetArg(arg[ac], XmNx,               x);   ac++;
	XtSetArg(arg[ac], XmNy,               y);   ac++;
	XtSetArg(arg[ac], XmNwidth,           wid);   ac++;
	XtSetArg(arg[ac], XmNheight,          hei);   ac++;
	str = XmStringCreateLocalized(title);
	XtSetArg(arg[ac], XmNdialogTitle,     str);   ac++;
	XtSetArg(arg[ac], XmNautoUnmanage,    False);   ac++;
	XtSetArg(arg[ac], XmNmarginWidth,     0);   ac++;
	XtSetArg(arg[ac], XmNmarginHeight,    0);   ac++;
	XtSetArg(arg[ac], XmNallowOverlap,    True);   ac++;
	XtSetArg(arg[ac], XmNresizePolicy,    XmRESIZE_NONE);   ac++;
	XtSetArg(arg[ac], XmNdeleteResponse,  XmDO_NOTHING);   ac++;
	XtSetArg(arg[ac], XmNmwmFunctions,    MWM_FUNC_MOVE);   ac++;
	XtSetArg(arg[ac], XmNdefaultPosition, False);   ac++;
	
	gra_curdialog->window = (Widget)XmCreateBulletinBoardDialog(base,
		"dialog", arg, ac);
	gra_curdialog->redrawroutine = 0;
	gra_curdialog->itemdesc = dialog;
	gra_curdialog->editline = -1;
	gra_curdialog->lastedittext[0] = 0;
	gra_curdialog->opaqueitem = -1;
	gra_curdialog->usertextsize = 8;
	gra_curdialog->inix = gra_curdialog->iniy = -1;
	XtManageChild(gra_curdialog->window);
	XtAddEventHandler(gra_curdialog->window, FocusChangeMask,
		FALSE, gra_dialog_event_handler, NULL);

	gcv.foreground = 0;
	gra_gcdia = XtGetGC(gra_curdialog->window, GCForeground, &gcv);	

	/* determine the default button */
	gra_curdialog->defaultbutton = 1;
	for(i=0; i<dialog->items; i++)
	{
		itemtype = dialog->list[i].type;
		if ((itemtype&ITEMTYPE) == DEFBUTTON)
			gra_curdialog->defaultbutton = i+1;
	}

	/* load the items */
	focus = 0;
	for(i=0; i<dialog->items; i++)
	{
		gra_getdialogcoordinates(&dialog->list[i].r, &x, &y, &wid, &hei);
		itemtype = dialog->list[i].type;
		switch (itemtype&ITEMTYPE)
		{
			case RADIO:				/* radio buttons seem too low, so raise them */
				y -= 5;
				break;
			case EDITTEXT:			/* raise text and force it to minimum height */
				y -= 5;
				if (hei < 32) hei = 32;
				break;
			case SCROLL:			/* shrink to make room for vertical scrollbar */
			case SCROLLMULTI:
				wid -= 20;
				break;
			case POPUP:				/* popups seem too low, so raise them */
				y -= 7;
				hei += 7;
				break;
		}
		ac = 0;
		XtSetArg(arg[ac], XmNx, x);   ac++;
		XtSetArg(arg[ac], XmNy, y);   ac++;
		XtSetArg(arg[ac], XmNwidth, wid);   ac++;
		XtSetArg(arg[ac], XmNheight, hei);   ac++;
		switch (itemtype&ITEMTYPE)
		{
			case BUTTON:
			case DEFBUTTON:
				gra_curdialog->items[i] = (Widget)XmCreatePushButton(gra_curdialog->window,
					dialog->list[i].msg, arg, ac);
				XtManageChild(gra_curdialog->items[i]);
				XtAddCallback(gra_curdialog->items[i], XmNactivateCallback,
					(XtCallbackProc)gra_dialogaction, (XtPointer)i);
				break;
			case CHECK:
				XtSetArg(arg[ac], XmNalignment, XmALIGNMENT_BEGINNING);   ac++;
				gra_curdialog->items[i] = (Widget)XmCreateToggleButton(gra_curdialog->window,
					dialog->list[i].msg, arg, ac);
				XtManageChild(gra_curdialog->items[i]);
				XtAddCallback(gra_curdialog->items[i], XmNvalueChangedCallback,
					(XtCallbackProc)gra_dialogaction, (XtPointer)i);
				break;
			case RADIO:
				temp = (Widget)XmCreateRadioBox(gra_curdialog->window, "radio", arg, ac);
				XtManageChild(temp);
				gra_curdialog->items[i] = XtVaCreateManagedWidget(dialog->list[i].msg,
					xmToggleButtonGadgetClass, temp, XmNindicatorSize, hei-8, NULL);
				XtAddCallback(gra_curdialog->items[i], XmNvalueChangedCallback,
					(XtCallbackProc)gra_dialogaction, (XtPointer)i);
				break;
			case EDITTEXT:
				gra_curdialog->items[i] = (Widget)XmCreateText(gra_curdialog->window, "text",
					arg, ac);
				XtManageChild(gra_curdialog->items[i]);
				XtAddCallback(gra_curdialog->items[i], XmNvalueChangedCallback,
					(XtCallbackProc)gra_dialogaction, (XtPointer)i);
				if (focus == 0) focus = gra_curdialog->items[i];
				break;
			case MESSAGE:
				XtSetArg(arg[ac], XmNalignment, XmALIGNMENT_BEGINNING);   ac++;
				gra_curdialog->items[i] = (Widget)XmCreateLabel(gra_curdialog->window,
					dialog->list[i].msg, arg, ac);
				XtManageChild(gra_curdialog->items[i]);
				break;
			case PROGRESS:
				gra_curdialog->items[i] = (Widget)XmCreateDrawingArea(gra_curdialog->window,
					"user", arg, ac);
				XtManageChild(gra_curdialog->items[i]);
				break;
			case DIVIDELINE:
				gra_curdialog->items[i] = (Widget)XmCreateDrawingArea(gra_curdialog->window,
					"user", arg, ac);
				XtManageChild(gra_curdialog->items[i]);
				XtAddCallback(gra_curdialog->items[i], XmNexposeCallback,
					(XtCallbackProc)gra_dialogredrawsep, (XtPointer)i);
				dpy = XtDisplay(gra_curdialog->items[i]);
				win = XtWindow(gra_curdialog->items[i]);
				gcv.foreground = BlackPixelOfScreen(XtScreen(gra_curdialog->items[i]));
				gc = XtGetGC(gra_curdialog->items[i], GCForeground, &gcv);
				XSetForeground(dpy, gc, BlackPixelOfScreen(XtScreen(gra_curdialog->items[i])));
				XFillRectangle(dpy, win, gc, 0, 0, wid, hei);
				XtReleaseGC(gra_curdialog->items[i], gc);
				break;
			case USERDRAWN:
				if (userdrawactionset == 0)
				{
					actions.string = "draw";
					actions.proc = (XtActionProc)gra_dialogdraw;
					XtAppAddActions(gra_xtapp, &actions, 1);
					userdrawactionset = 1;
				}
				translation =
					"<BtnDown>:   draw(down)   ManagerGadgetArm() \n\
					 <BtnUp>:     draw(up)     ManagerGadgetActivate() \n\
					 <BtnMotion>: draw(motion) ManagerGadgetButtonMotion()";
				XtSetArg(arg[ac], XmNtranslations, XtParseTranslationTable(translation));   ac++;
				gra_curdialog->items[i] = (Widget)XmCreateDrawingArea(gra_curdialog->window,
					"user", arg, ac);
				XtManageChild(gra_curdialog->items[i]);
				XtAddCallback(gra_curdialog->items[i], XmNexposeCallback,
					(XtCallbackProc)gra_dialogredraw, (XtPointer)i);
				break;
			case POPUP:
				XtSetArg(arg[ac], XmNresizeWidth, False);   ac++;
				XtSetArg(arg[ac], XmNresizeHeight, False);   ac++;
				gra_curdialog->items[i] = (Widget)XmCreateOptionMenu(gra_curdialog->window,
					"menu", arg, ac);
				dialog->list[i].data = 0;
				XtAddCallback(gra_curdialog->items[i], XmNentryCallback,
					(XtCallbackProc)gra_dialogaction, (XtPointer)i);
				break;
			case ICON:
				map = gra_makeicon(dialog->list[i].msg, gra_curdialog->window);
				if (map == 0) break;
				if ((itemtype&INACTIVE) != 0)
				{
					/* inactive: make it a label */
					XtSetArg(arg[ac], XmNlabelType, XmPIXMAP);   ac++;
					XtSetArg(arg[ac], XmNlabelPixmap, map);   ac++;
					gra_curdialog->items[i] = (Widget)XmCreateLabel(gra_curdialog->window,
						dialog->list[i].msg, arg, ac);
				} else
				{
					/* active: make it a pushbutton */
					XtSetArg(arg[ac], XmNlabelType, XmPIXMAP);   ac++;
					XtSetArg(arg[ac], XmNlabelPixmap, map);   ac++;
					XtSetArg(arg[ac], XmNhighlightThickness, 0);   ac++;
					XtSetArg(arg[ac], XmNshadowThickness, 0);   ac++;
					XtSetArg(arg[ac], XmNborderWidth, 0);   ac++;
					gra_curdialog->items[i] = (Widget)XmCreatePushButton(gra_curdialog->window,
						dialog->list[i].msg, arg, ac);
					XtManageChild(gra_curdialog->items[i]);
					XtAddCallback(gra_curdialog->items[i], XmNactivateCallback,
						(XtCallbackProc)gra_dialogaction, (XtPointer)i);
				}
				XtManageChild(gra_curdialog->items[i]);
				break;
			case SCROLL:
			case SCROLLMULTI:
				if (listcopyactionset == 0)
				{
					actions.string = "copywholelist";
					actions.proc = (XtActionProc)gra_dialogcopywholelist;
					XtAppAddActions(gra_xtapp, &actions, 1);
					listcopyactionset = 1;
				}
				translation = "#override <Key>osfCopy: copywholelist()";
				XtSetArg(arg[ac], XmNtranslations, XtParseTranslationTable(translation));   ac++;
				if ((itemtype&ITEMTYPE) == SCROLL)
				{
					XtSetArg(arg[ac], XmNselectionPolicy, XmBROWSE_SELECT);
				} else
				{
					XtSetArg(arg[ac], XmNselectionPolicy, XmEXTENDED_SELECT);
				}
				ac++;
				XtSetArg(arg[ac], XmNresizePolicy, XmRESIZE_NONE);   ac++;
				XtSetArg(arg[ac], XmNlistSizePolicy, XmCONSTANT);   ac++;
				gra_curdialog->items[i] = (Widget)XmCreateScrolledList(gra_curdialog->window,
					"scroll", arg, ac);
				XtManageChild(gra_curdialog->items[i]);
				if ((itemtype&ITEMTYPE) == SCROLL)
				{
					XtAddCallback(gra_curdialog->items[i], XmNbrowseSelectionCallback,
						(XtCallbackProc)gra_dialogaction, (XtPointer)i);
				} else
				{
					XtAddCallback(gra_curdialog->items[i], XmNextendedSelectionCallback,
						(XtCallbackProc)gra_dialogaction, (XtPointer)i);
				}
				break;
			default:
				break;
		}
	}

	/* set focus to first edit text field */
	if (focus != 0)
	{
		XtSetArg(arg[0], XmNinitialFocus, focus);
		XtSetValues(gra_curdialog->window, arg, 1);
	}

	/* set default button if appropriate */
	itemtype = dialog->list[gra_curdialog->defaultbutton-1].type;
	if ((itemtype&ITEMTYPE) == BUTTON || (itemtype&ITEMTYPE) == DEFBUTTON)
	{
		temp = gra_curdialog->items[gra_curdialog->defaultbutton-1];
		if (focus == 0)
		{
			XtSetArg(arg[0], XmNinitialFocus, temp);
			XtSetValues(gra_curdialog->window, arg, 1);
		}
		XtSetArg(arg[0], XmNdefaultButton, temp);
		XtSetValues(gra_curdialog->window, arg, 1);
		XtSetArg(arg[0], XmNshowAsDefault, 1);
		XtSetValues(temp, arg, 1);
	}

	if (*title == 0) title = _("UNTITLED");
	(void)initinfstr();
	(void)formatinfstr(_("Start dialog %s"), title);
	gra_logwritecomment(returninfstr());
	gra_dialoghit = -1;
	gra_flushdialog();
	return(FALSE);
}

/*
 * Routine to handle actions and return the next item hit.
 */
INTBIG DiaNextHit(void)
{
	INTBIG item;

	for(;;)
	{
		gra_nextevent();
		if (gra_dialoghit != -1)
		{
			item = gra_dialoghit;
			gra_dialoghit = -1;
			return(item);
		}
	}
}

/*
 * Routine to parse the next input event and return the next character typed.
 * If the routine returns -1, nothing has happened.  If the
 * routine returns -2, an item has been hit (and is in "itemHit").
 */
INTSML DiaGetNextCharacter(INTBIG *itemHit)
{
	INTBIG chr;

	gra_nextevent();
	if (gra_dialoghit == -1) return(-1);
	*itemHit = gra_dialoghit;
	if (gra_dialoghitchar == 0) return(-2);
	chr = gra_dialoghitchar;
	gra_dialoghitchar = 0;
	return(chr);
}

void DiaDoneDialog(void)
{
	Arg arg[3];
	INTBIG dx, dy;
	short newx, newy;

	/* if playing back, search for end-dialog marker */
	while (us_logplay != NULL)
	{
		if (gra_loggetnextaction(0)) break;
		if (gra_inputstate == DIAENDDIALOG) break;
		gra_inputstate = NOEVENT;
	}
	gra_inputstate = NOEVENT;
	gra_logwriteaction(DIAENDDIALOG, 0, 0, 0, 0);

	/* adjust the dialog position if it was moved */
	if (gra_curdialog->inix != -1 && gra_curdialog->iniy != -1)
	{
		XtSetArg(arg[0], XtNx, &newx);
		XtSetArg(arg[1], XtNy, &newy);
		XtGetValues(gra_curdialog->window, arg, 2);
		dx = (newx - gra_curdialog->inix) * DIALOGDEN / DIALOGNUM;
		dy = (newy - gra_curdialog->iniy) * DIALOGDEN / DIALOGNUM;
		gra_curdialog->itemdesc->windowRect.left += dx;
		gra_curdialog->itemdesc->windowRect.right += dx;
		gra_curdialog->itemdesc->windowRect.top += dy;
		gra_curdialog->itemdesc->windowRect.bottom += dy;
	}

	XtUnmanageChild(gra_curdialog->window);
	XtDestroyWidget(gra_curdialog->window);
	gra_curdialogindex--;
	if (gra_curdialogindex >= 0)
		gra_curdialog = &gra_dialogs[gra_curdialogindex];
}

/*
 * Routine to set the text in item "item" to "msg"
 */
void DiaSetText(INTBIG item, char *msg)
{
	Arg arg[5];
	INTBIG type, highlight;
	XmString str, s1, s2;
	static XmString separator = 0;
	Widget w;
	INTBIG bigitem, count, i, textwid, x, y, itemwid, itemhei, len;
	int direction, asc, desc;
	XmFontList fontlist;
	XmFontListEntry fontentry;
	XmFontContext fontlistcontext;
	XmFontType entrytype;
	XFontSet fontset;
	XtPointer retval;
	char **fontnamelist, *pt;
	XFontStruct **fontstructlist;
	XCharStruct xcs;
	static XFontStruct *theFont = 0;
	static char *brokenbuf;
	static INTBIG brokenbufsize = 0;

	highlight = 0;
	if (item < 0)
	{
		item = -item;
		highlight = 1;
	}
	item--;
	w = gra_curdialog->items[item];
	type = gra_curdialog->itemdesc->list[item].type;
	if ((type&ITEMTYPE) == EDITTEXT)
	{
		bigitem = (INTBIG)item;
		XtRemoveCallback(w, XmNvalueChangedCallback,
			(XtCallbackProc)gra_dialogaction, (XtPointer)bigitem);
		XmTextSetString(w, msg);
		count = XmTextGetLastPosition(w);
		if (highlight != 0)
		{
			XmTextSetSelection(w, 0, count, 0);
			XtSetArg(arg[0], XmNinitialFocus, w);
			XtSetValues(gra_curdialog->window, arg, 1);
		} else
		{
			XmTextSetSelection(w, count, count, 0);
			XmTextSetInsertionPosition(w, count); 
		}
		XtAddCallback(w, XmNvalueChangedCallback,
			(XtCallbackProc)gra_dialogaction, (XtPointer)bigitem);
		if (item == gra_curdialog->editline)
			strcpy(gra_curdialog->lastedittext, msg);
	} else
	{
		/* cache the font used to draw messages (once only) */
		if (theFont == 0)
		{
			XtSetArg(arg[0], XmNfontList, &fontlist);
			XtGetValues(w, arg, 1);
			if (XmFontListInitFontContext(&fontlistcontext, fontlist))
			{
				fontentry = XmFontListNextEntry(fontlistcontext);
				if (fontentry != 0)
				{
					retval = XmFontListEntryGetFont(fontentry, &entrytype);
					if (entrytype == XmFONT_IS_FONT)
					{
						theFont = (XFontStruct *)retval;
					} else if (entrytype == XmFONT_IS_FONTSET)
					{
						fontset = (XFontSet)retval;
						count = XFontsOfFontSet(fontset, &fontstructlist, &fontnamelist);
						if (count > 0)
							theFont = fontstructlist[0];
					}
				}
				XmFontListFreeFontContext(fontlistcontext);
			}
		}

		/* see if the text fits */
		if (theFont == 0) textwid = itemwid = 10; else
		{
			XTextExtents(theFont, msg, strlen(msg), &direction, &asc, &desc, &xcs);
			textwid = xcs.width;
			gra_getdialogcoordinates(&gra_curdialog->itemdesc->list[item].r, &x, &y, &itemwid, &itemhei);
		}
		if (textwid <= itemwid) str = XmStringCreateLocalized(msg); else
		{
			/* break up the text */
			len = strlen(msg)+1;
			if (len > brokenbufsize)
			{
				if (brokenbufsize > 0) efree(brokenbuf);
				brokenbufsize = 0;
				brokenbuf = (char *)emalloc(len, us_tool->cluster);
				if (brokenbuf == 0) return;
				brokenbufsize = len;
			}
			if (separator == 0) separator = XmStringSeparatorCreate();
			str = XmStringCreateLocalized("");
			pt = msg;
			for(;;)
			{
				/* look backwards for blank space that fits */
				for(len = strlen(pt); len > 0; len--)
				{
					if (pt[len] != ' ' && pt[len] != 0) continue;
					XTextExtents(theFont, pt, len, &direction, &asc, &desc, &xcs);
					if (xcs.width <= itemwid) break;
				}
				if (len == 0)
				{
					/* no place to break in blank space: break where possible */
					for(len = strlen(pt)-1; len > 0; len--)
					{
						XTextExtents(theFont, pt, len, &direction, &asc, &desc, &xcs);
						if (xcs.width <= itemwid) break;
					}
				}

				for(i=0; i<len; i++) brokenbuf[i] = pt[i];
				brokenbuf[len] = 0;
				s1 = XmStringCreateLocalized(brokenbuf);
				s2 = XmStringConcat(str, s1);
				XmStringFree(s1);
				XmStringFree(str);
				str = s2;
				if (len == strlen(pt)) break;
				s2 = XmStringConcat(str, separator);
				XmStringFree(str);
				str = s2;
				pt = &pt[len];
				if (*pt == ' ') pt++;
			}
		}
		XtSetArg(arg[0], XmNlabelString, str);
		XtSetValues(w, arg, 1);
		XmStringFree(str);
	}
	gra_flushdialog();
}

/*
 * Routine to return the text in item "item"
 */
char *DiaGetText(INTBIG item)
{
	Arg arg;
	INTBIG type;
	XmString str;
	char *text, *retstring;

	item--;
	if (gra_curdialog->opaqueitem == item)
		return(gra_opaquefield);

	type = gra_curdialog->itemdesc->list[item].type;
	if ((type&ITEMTYPE) == EDITTEXT)
		return(XmTextGetString(gra_curdialog->items[item]));

	if ((type&ITEMTYPE) == MESSAGE)
	{
		XtSetArg(arg, XmNlabelString, &str);
		XtGetValues(gra_curdialog->items[item], &arg, 1);
		XmStringGetLtoR(str, XmFONTLIST_DEFAULT_TAG, &text);
		(void)initinfstr();
		(void)addstringtoinfstr(text);
		retstring = returninfstr();
		XtFree(text);
		return(retstring);
	}
	return("");
}

/*
 * Routine to set the value in item "item" to "value"
 */
void DiaSetControl(INTBIG item, INTBIG value)
{
	item--;
	XmToggleButtonSetState(gra_curdialog->items[item], value, False);
	gra_flushdialog();
	gra_logwriteaction(DIASETCONTROL, 0, item + 1, value, 0);
}

/*
 * Routine to return the value in item "item"
 */
INTBIG DiaGetControl(INTBIG item)
{
	INTBIG value;

	item--;
	value = XmToggleButtonGetState(gra_curdialog->items[item]);
	return(value);
}

/*
 * Routine to check item "item" to make sure that there is
 * text in it.  If so, it returns true.  Otherwise it beeps and returns false.
 */
BOOLEAN DiaValidEntry(INTBIG item)
{
	char *msg;

	msg = DiaGetText(item);
	while (*msg == ' ') msg++;
	if (*msg != 0) return(TRUE);
	ttybeep(1);
	return(FALSE);
}

/*
 * Routine to dim item "item"
 */
void DiaDimItem(INTBIG item)
{
	REGISTER INTBIG type;

	item--;
	type = gra_curdialog->itemdesc->list[item].type;
	if ((type&ITEMTYPE) == EDITTEXT) DiaNoEditControl(item+1); else
		XtSetSensitive(gra_curdialog->items[item], False);
	gra_flushdialog();
}

/*
 * Routine to un-dim item "item"
 */
void DiaUnDimItem(INTBIG item)
{
	REGISTER INTBIG type;

	item--;
	type = gra_curdialog->itemdesc->list[item].type;
	if ((type&ITEMTYPE) == EDITTEXT) DiaEditControl(item+1); else
		XtSetSensitive(gra_curdialog->items[item], True);
	gra_flushdialog();
}

/*
 * Routine to change item "item" to be a message rather
 * than editable text
 */
void DiaNoEditControl(INTBIG item)
{
	Widget w;
	Arg arg;

	item--;
	w = gra_curdialog->items[item];
	XtSetArg(arg, XmNeditable, False);
	XtSetValues(w, &arg, 1);
}

/*
 * Routine to change item "item" to be editable text rather
 * than a message
 */
void DiaEditControl(INTBIG item)
{
	Widget w;
	Arg arg;

	item--;
	w = gra_curdialog->items[item];
	XtSetArg(arg, XmNeditable, True);
	XtSetValues(w, &arg, 1);
}

void DiaOpaqueEdit(INTBIG item)
{
	Widget w;

	item--;
	gra_curdialog->opaqueitem = item;

	w = gra_curdialog->items[item];
	gra_opaquefield[0] = 0;
	XtAddCallback(w, XmNmodifyVerifyCallback, (XtCallbackProc)gra_dialogopaqueaction, 0);
}

/*
 * Routine to cause item "item" to report character hits
 */
void DiaCharacterEdit(INTBIG item)
{
	Widget w;

	item--;
	gra_curdialog->editline = item;
	w = gra_curdialog->items[item];
	XmTextSetString(w, gra_curdialog->lastedittext);
}

/*
 * Routine to cause item "item" to be the default button
 */
void DiaDefaultButton(INTBIG item)
{
	Widget newdef, olddef;
	Arg arg[2];

	if (item == gra_curdialog->defaultbutton) return;
	olddef = gra_curdialog->items[gra_curdialog->defaultbutton-1];
	newdef = gra_curdialog->items[item-1];
	gra_curdialog->defaultbutton = item;

	XtSetArg(arg[0], XmNdefaultButton, newdef);
	XtSetValues(gra_curdialog->window, arg, 1);
	XtSetArg(arg[0], XmNshowAsDefault, 0);
	XtSetValues(olddef, arg, 1);
	XtSetArg(arg[0], XmNshowAsDefault, 1);
	XtSetValues(newdef, arg, 1);
}

/*
 * Routine to change item "item" into a popup with "count" entries
 * in "names".
 */
void DiaSetPopup(INTBIG item, INTBIG count, char **names)
{
	Widget child, menu;
	Arg arg[3];
	INTBIG i, x, y, wid, hei;

	item--;
	gra_getdialogcoordinates(&gra_curdialog->itemdesc->list[item].r, &x, &y, &wid, &hei);
	menu = (Widget)XmCreatePulldownMenu(gra_curdialog->window, "menu", NULL, 0);
	if (count == 0)
	{
		child = (Widget)XmCreatePushButton(menu, "", NULL, 0);
		XtManageChild(child);
	}
	for(i=0; i<count; i++)
	{
		XtSetArg(arg[0], XmNwidth, wid);
		child = (Widget)XmCreatePushButton(menu, names[i], arg, 1);
		XtManageChild(child);
		XtAddCallback(child, XmNactivateCallback,
			(XtCallbackProc)gra_dialogaction, (XtPointer)item);
	}
	XtSetArg(arg[0], XmNsubMenuId, menu);
	XtSetValues(gra_curdialog->items[item], arg, 1);

	if (gra_curdialog->itemdesc->list[item].data == 0)
	{
		gra_curdialog->itemdesc->list[item].data = 1;
		XtManageChild(gra_curdialog->items[item]);
	}

	/* force the width of the popup */
	XtSetArg(arg[0], XmNwidth, wid);
	XtSetValues(gra_curdialog->items[item], arg, 1);
	gra_flushdialog();
}

/*
 * Routine to change popup item "item" so that the current entry is "entry".
 */
void DiaSetPopupEntry(INTBIG item, INTBIG entry)
{
	Widget w, child, *kids;
	Arg arg[1];

	item--;
	w = gra_curdialog->items[item];
	XtVaGetValues(w, XmNsubMenuId, &child, NULL);
	XtVaGetValues(child, XmNchildren, &kids, NULL);
	XtSetArg(arg[0], XmNmenuHistory, kids[entry]);
	XtSetValues(w, arg, 1);
	gra_flushdialog();
}

/*
 * Routine to return the current item in popup menu item "item".
 */
INTBIG DiaGetPopupEntry(INTBIG item)
{
	Widget w, child, *kids, kid;
	INTBIG numkids, i;

	item--;
	w = gra_curdialog->items[item];
	XtVaGetValues(w, XmNsubMenuId, &child, NULL);
	XtVaGetValues(child, XmNchildren, &kids, NULL);
	XtVaGetValues(child, XmNnumChildren, &numkids, NULL);
	XtVaGetValues(w, XmNmenuHistory, &kid, NULL);
	for(i=0; i<numkids; i++)
		if (kids[i] == kid) break;
	return(i);
}

void DiaInitTextDialog(INTBIG item, BOOLEAN (*toplist)(char **), char *(*nextinlist)(void),
	void (*donelist)(void), INTBIG sortpos, INTBIG flags)
{
	Widget w;
	INTBIG bigitem, lines, fontheight;
	Arg arg[2];
	Display *dpy;
	XmFontListEntry entry;
	static XmFontList fontlist;
	static INTBIG havefontlist = 0;
	RECTAREA r;

	item--;
	w = gra_curdialog->items[item];
	if ((flags&SCDOUBLEQUIT) != 0)
	{
		XtAddCallback(w, XmNdefaultActionCallback,
			(XtCallbackProc)gra_dialogaction, (XtPointer)0);
	}
	if ((flags&SCREPORT) != 0)
	{
		bigitem = (INTBIG)item;
		XtAddCallback(w, XmNbrowseSelectionCallback,
			(XtCallbackProc)gra_dialogaction, (XtPointer)bigitem);
	}
	if ((flags&SCFIXEDWIDTH) != 0)
	{
		if (havefontlist == 0)
		{
			dpy = XtDisplay(w);
			entry = XmFontListEntryLoad(dpy, gra_font[11].fontname,
				XmFONT_IS_FONT, "TAG");
			fontlist = XmFontListAppendEntry(NULL, entry);
			XmFontListEntryFree(&entry);
			havefontlist = 1;
		}
		XtSetArg(arg[0], XmNfontList, fontlist);
		fontheight = gra_font[11].font->ascent + gra_font[11].font->descent;
		r = gra_curdialog->itemdesc->list[item].r;
		lines = (r.bottom - r.top) / fontheight;
		XtSetArg(arg[1], XmNvisibleItemCount, lines);
		XtSetValues(w, arg, 2);
	}
	gra_flushdialog();
	DiaLoadTextDialog(item+1, toplist, nextinlist, donelist, sortpos);
}

void DiaLoadTextDialog(INTBIG item, BOOLEAN (*toplist)(char **), char *(*nextinlist)(void),
	void (*donelist)(void), INTBIG sortpos)
{
	Widget w;
	INTBIG i, items;
	char *next, **list, line[256];
	XmString str;

	item--;
	w = gra_curdialog->items[item];

	/* clear the list */
	XmListDeleteAllItems(w);

	if (sortpos < 0)
	{
		/* unsorted: load the list directly */
		line[0] = 0;
		next = line;
		(void)(*toplist)(&next);
		for(items=0; ; items++)
		{
			next = (*nextinlist)();
			if (next == 0) break;
			str = XmStringCreateLocalized(next);
			XmListAddItemUnselected(w, str, 0);
			XmStringFree(str);
		}
		(*donelist)();
	} else
	{
		/* count the number of items to be put in the text editor */
		line[0] = 0;
		next = line;
		(void)(*toplist)(&next);
		for(items=0; ; items++) if ((*nextinlist)() == 0) break;
		(*donelist)();

		/* allocate space for the strings */
		if (items > 0)
		{
			list = (char **)emalloc(items * (sizeof (char *)), el_tempcluster);
			if (list == 0) return;
		}

		/* get the list */
		line[0] = 0;
		next = line;
		(void)(*toplist)(&next);
		for(i=0; i<items; i++)
		{
			next = (*nextinlist)();
			if (next == 0) next = "???";
			list[i] = (char *)emalloc(strlen(next)+1, el_tempcluster);
			if (list[i] == 0) return;
			strcpy(list[i], next);
		}
		(*donelist)();

		/* sort the list */
		gra_dialogstringpos = sortpos;
		esort(list, items, sizeof (char *), gra_stringposascending);

		/* stuff the list into the text editor */
		for(i=0; i<items; i++)
		{
			str = XmStringCreateLocalized(list[i]);
			XmListAddItemUnselected(w, str, 0);
			XmStringFree(str);
		}

		/* deallocate the list */
		if (items > 0)
		{
			for(i=0; i<items; i++) efree((char *)list[i]);
			efree((char *)(char *)list);
		}
	}
	if (items > 0) XmListSelectPos(w, 1, False);
	gra_flushdialog();
}

/*
 * Routine to stuff line "line" at the end of the edit buffer.
 */
void DiaStuffLine(INTBIG item, char *line)
{
	Widget w;
	XmString str;

	item--;
	w = gra_curdialog->items[item];
	str = XmStringCreateLocalized(line);
	XmListAddItemUnselected(w, str, 0);
	XmStringFree(str);
	gra_flushdialog();
}

/*
 * Routine to select line "line" of scroll item "item".
 */
void DiaSelectLine(INTBIG item, INTBIG line)
{
	Widget w;
	INTBIG first, visible;
	Arg arg[2];

	item--;
	w = gra_curdialog->items[item];
	if (line < 0) XmListDeselectAllItems(w); else
	{
		line++;
		XmListSelectPos(w, line, False);
		XtSetArg(arg[0], XmNtopItemPosition, &first);
		XtSetArg(arg[1], XmNvisibleItemCount, &visible);
		XtGetValues(w, arg, 2);
		if (line < first || line >= first+visible)
		{
			first = line - visible/2;
			if (first < 1) first = 1;
			XmListSetPos(w, first);
		}
	}
	gra_flushdialog();
}

/*
 * Routine to select "count" lines in "lines" of scroll item "item".
 */
void DiaSelectLines(INTBIG item, INTBIG count, INTBIG *lines)
{
	Widget w;
	INTBIG first, visible, i, low, high, line;
	Arg arg[2];

	item--;
	w = gra_curdialog->items[item];
	XmListDeselectAllItems(w);

	/* temporarily set the selection policy to "multiple" to enable multiple select */
	XtSetArg(arg[0], XmNselectionPolicy, XmMULTIPLE_SELECT);
	XtSetValues(w, arg, 1);

	/* make the selections */
	for(i=0; i<count; i++)
	{
		line = lines[i] + 1;
		XmListSelectPos(w, line, False);
		if (i == 0) low = high = line; else
		{
			if (line < low) low = line;
			if (line > high) high = line;
		}
	}

	/* make sure lines are visible */
	XtSetArg(arg[0], XmNtopItemPosition, &first);
	XtSetArg(arg[1], XmNvisibleItemCount, &visible);
	XtGetValues(w, arg, 2);
	if (high < first || low >= first+visible)
	{
		first = low;
		XmListSetPos(w, first);
	}

	/* restore the selection policy to "extended" */
	XtSetArg(arg[0], XmNselectionPolicy, XmEXTENDED_SELECT);
	XtSetValues(w, arg, 1);
	gra_flushdialog();
}

/*
 * Returns the currently selected line in the scroll list "item".
 */
INTBIG DiaGetCurLine(INTBIG item)
{
	Widget w;
	INTBIG selected;
	int count, *pos;   /* must be "int", and not "INTBIG" to work on Alpha */

	item--;
	w = gra_curdialog->items[item];
	selected = -1;
	if (XmListGetSelectedPos(w, &pos, &count))
	{
		if (count >= 1) selected = pos[0]-1;
		XtFree((XtPointer)pos);
	}
	return(selected);
}

/*
 * Returns the currently selected lines in the scroll list "item".  The returned
 * array is terminated with -1.
 */
INTBIG *DiaGetCurLines(INTBIG item)
{
	Widget w;
	static INTBIG selected[MAXSCROLLMULTISELECT];
	REGISTER INTBIG i;
	int count, *pos;   /* must be "int", and not "INTBIG" to work on Alpha */

	item--;
	w = gra_curdialog->items[item];
	selected[0] = -1;
	if (XmListGetSelectedPos(w, &pos, &count))
	{
		for(i=0; i<count; i++)
			selected[i] = pos[i] - 1;
		selected[count] = -1;
		XtFree((XtPointer)pos);
	}
	return(selected);
}

char *DiaGetScrollLine(INTBIG item, INTBIG line)
{
	Widget w;
	INTBIG count;
	XmString *strlist;
	char *text, *retstring;

	item--;
	if (line < 0) return("");
	w = gra_curdialog->items[item];
	XtVaGetValues(w, XmNitemCount, &count, XmNitems, &strlist, NULL);
	if (line >= count) retstring = ""; else
	{
		XmStringGetLtoR(strlist[line], XmFONTLIST_DEFAULT_TAG, &text);
		(void)initinfstr();
		(void)addstringtoinfstr(text);
		retstring = returninfstr();
		XtFree(text);
	}
	return(retstring);
}

void DiaSetScrollLine(INTBIG item, INTBIG line, char *msg)
{
	Widget w;
	int count, *pos;   /* must be "int", and not "INTBIG" to work on Alpha */
	INTBIG selected;
	XmString strlist[1];

	item--;
	w = gra_curdialog->items[item];

	/* remember which line was selected */
	selected = 0;
	if (XmListGetSelectedPos(w, &pos, &count))
	{
		if (count == 1) selected = pos[0];
		XtFree((XtPointer)pos);
	}

	/* change the desired line */
	strlist[0] = XmStringCreateLocalized(msg);
	XmListReplaceItemsPosUnselected(w, strlist, 1, line+1);
	XmStringFree(strlist[0]);

	/* restore the selected line */
	if (selected == 0) XmListDeselectAllItems(w); else
		XmListSelectPos(w, selected, False);
	gra_flushdialog();
}

void DiaItemRect(INTBIG item, RECTAREA *rect)
{
	item--;
	if (item < 0 || item >= gra_curdialog->itemdesc->items) return;
	rect->left = gra_curdialog->itemdesc->list[item].r.left+1;
	rect->right = gra_curdialog->itemdesc->list[item].r.right-1;
	rect->top = gra_curdialog->itemdesc->list[item].r.top+1;
	rect->bottom = gra_curdialog->itemdesc->list[item].r.bottom-1;
}

void DiaPercent(INTBIG item, INTBIG percent)
{
	Widget w;
	Display *dpy;
	Window win;
	INTBIG x, y, wid, hei, rwid;
	XGCValues gcv;
	GC gc;

	item--;
	w = gra_curdialog->items[item];
	dpy = XtDisplay(w);
	win = XtWindow(w);

	/* determine size and draw interior bar */
	gra_getdialogcoordinates(&gra_curdialog->itemdesc->list[item].r, &x, &y, &wid, &hei);
	rwid = wid * percent / 100;
	gcv.foreground = BlackPixelOfScreen(XtScreen(w));
	gc = XtGetGC(w, GCForeground, &gcv);
	XSetForeground(dpy, gc, BlackPixelOfScreen(XtScreen(w)));
	XFillRectangle(dpy, win, gc, 0, 0, rwid, hei);

	/* draw outline */
	XSetForeground(dpy, gc, BlackPixelOfScreen(XtScreen(w)));
	XDrawLine(dpy, win, gc, 0, 0, wid, 0);
	XDrawLine(dpy, win, gc, wid, 0, wid, hei);
	XDrawLine(dpy, win, gc, wid, hei, 0, hei);
	XDrawLine(dpy, win, gc, 0, hei, 0, 0);
	XtReleaseGC(w, gc);
}

void DiaRedispRoutine(INTBIG item, void (*routine)(RECTAREA*))
{
	item--;
	gra_curdialog->redrawroutine = routine;
	gra_curdialog->redrawitem = item;
}

void DiaAllowUserDoubleClick(void)
{
}

void DiaDrawRect(INTBIG item, RECTAREA *rect, INTBIG r, INTBIG g, INTBIG b)
{
	Widget w;
	Display *dpy;
	Window win;
	INTBIG xoff, yoff, lx, hx, ly, hy;
	XGCValues gcv;
	GC gc;

	item--;
	w = gra_curdialog->items[item];
	xoff = gra_curdialog->itemdesc->list[item].r.left;
	yoff = gra_curdialog->itemdesc->list[item].r.top;
	dpy = XtDisplay(w);
	win = XtWindow(w);

	gcv.foreground = gra_makedpycolor(dpy, r, g, b);
	gc = XtGetGC(w, GCForeground, &gcv);

	lx = gra_scaledialogcoordinate(rect->left-xoff);
	hx = gra_scaledialogcoordinate(rect->right-xoff);
	ly = gra_scaledialogcoordinate(rect->top-yoff);
	hy = gra_scaledialogcoordinate(rect->bottom-yoff);
	XFillRectangle(dpy, win, gc, lx, ly, hx-lx, hy-ly);
	XtReleaseGC(w, gc);
}

void DiaInvertRect(INTBIG item, RECTAREA *r)
{
	Widget w;
	Display *dpy;
	Window win;
	INTBIG xoff, yoff, lx, hx, ly, hy;
	XGCValues gcv;
	GC gc;

	item--;
	w = gra_curdialog->items[item];
	xoff = gra_curdialog->itemdesc->list[item].r.left;
	yoff = gra_curdialog->itemdesc->list[item].r.top;
	dpy = XtDisplay(w);
	win = XtWindow(w);

	/* get rectangle bounds */
	lx = gra_scaledialogcoordinate(r->left-xoff);
	hx = gra_scaledialogcoordinate(r->right-xoff);
	ly = gra_scaledialogcoordinate(r->top-yoff);
	hy = gra_scaledialogcoordinate(r->bottom-yoff);

	/* invert the rectangle */
	gcv.function = GXinvert;
	gc = XtGetGC(w, GCFunction, &gcv);
	XFillRectangle(dpy, win, gc, lx, ly, hx-lx, hy-ly);
	XtReleaseGC(w, gc);
}

void DiaFrameRect(INTBIG item, RECTAREA *r)
{
	Widget w;
	Display *dpy;
	Window win;
	INTBIG xoff, yoff, lx, hx, ly, hy;
	XGCValues gcv;
	GC gc;

	item--;
	w = gra_curdialog->items[item];
	xoff = gra_curdialog->itemdesc->list[item].r.left;
	yoff = gra_curdialog->itemdesc->list[item].r.top;
	dpy = XtDisplay(w);
	win = XtWindow(w);

	/* get rectangle bounds */
	lx = gra_scaledialogcoordinate(r->left-xoff);
	hx = gra_scaledialogcoordinate(r->right-xoff);
	ly = gra_scaledialogcoordinate(r->top-yoff);
	hy = gra_scaledialogcoordinate(r->bottom-yoff);

	/* erase the rectangle */
	gcv.foreground = WhitePixelOfScreen(XtScreen(w));
	gc = XtGetGC(w, GCForeground, &gcv);
	XFillRectangle(dpy, win, gc, lx, ly, hx-lx, hy-ly);
	XtReleaseGC(w, gc);

	/* draw outline */
	gcv.foreground = BlackPixelOfScreen(XtScreen(w));
	gc = XtGetGC(w, GCForeground, &gcv);
	XDrawLine(dpy, win, gc, lx, ly, hx-1, ly);
	XDrawLine(dpy, win, gc, hx-1, ly, hx-1, hy-1);
	XDrawLine(dpy, win, gc, hx-1, hy-1, lx, hy-1);
	XDrawLine(dpy, win, gc, lx, hy-1, lx, ly);
	XtReleaseGC(w, gc);
}

void DiaDrawLine(INTBIG item, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty, INTBIG mode)
{
	Widget w;
	Display *dpy;
	Window win;
	INTBIG xoff, yoff, x1, y1, x2, y2;
	XGCValues gcv;
	GC gc;

	item--;
	w = gra_curdialog->items[item];
	xoff = gra_curdialog->itemdesc->list[item].r.left;
	yoff = gra_curdialog->itemdesc->list[item].r.top;
	dpy = XtDisplay(w);
	win = XtWindow(w);

	x1 = gra_scaledialogcoordinate(fx-xoff);
	y1 = gra_scaledialogcoordinate(fy-yoff);
	x2 = gra_scaledialogcoordinate(tx-xoff);
	y2 = gra_scaledialogcoordinate(ty-yoff);
	switch (mode)
	{
		case DLMODEON:
			gcv.foreground = BlackPixelOfScreen(XtScreen(w));
			gc = XtGetGC(w, GCForeground, &gcv);
			break;
		case DLMODEOFF:
			gcv.foreground = WhitePixelOfScreen(XtScreen(w));
			gc = XtGetGC(w, GCForeground, &gcv);
			break;
		case DLMODEINVERT:
			gcv.function = GXinvert;
			gc = XtGetGC(w, GCFunction, &gcv);
			break;
	}
	XDrawLine(dpy, win, gc, x1, y1, x2, y2);
	XtReleaseGC(w, gc);
}

void DiaFillPoly(INTBIG item, INTBIG *xv, INTBIG *yv, INTBIG count, INTBIG r, INTBIG g, INTBIG b)
{
	Widget w;
	Display *dpy;
	Window win;
	INTBIG xoff, yoff, i;
	XPoint pointlist[20];
	XGCValues gcv;
	GC gc;

	item--;
	w = gra_curdialog->items[item];
	xoff = gra_curdialog->itemdesc->list[item].r.left;
	yoff = gra_curdialog->itemdesc->list[item].r.top;
	dpy = XtDisplay(w);
	win = XtWindow(w);

	for(i=0; i<count; i++)
	{
		pointlist[i].x = gra_scaledialogcoordinate(xv[i]-xoff);
		pointlist[i].y = gra_scaledialogcoordinate(yv[i]-yoff);
	}

	/* determine color to use */
	gcv.foreground = gra_makedpycolor(dpy, r, g, b);
	gc = XtGetGC(w, GCForeground, &gcv);

	XFillPolygon(dpy, win, gc, pointlist, count, Complex, CoordModeOrigin);
	XtReleaseGC(w, gc);
	gra_flushdialog();
}

void DiaPutText(INTBIG item, char *msg, INTBIG x, INTBIG y)
{
	INTBIG fontnumber, xoff, yoff, xp, yp;
	XFontStruct *font;
	Widget w;
	Display *dpy;
	Window win;
	XGCValues gcv;
	GC gc;

	item--;
	w = gra_curdialog->items[item];
	xoff = gra_curdialog->itemdesc->list[item].r.left;
	yoff = gra_curdialog->itemdesc->list[item].r.top;
	dpy = XtDisplay(w);
	win = XtWindow(w);

	fontnumber = (gra_curdialog->usertextsize - 4) / 2;
	if (fontnumber < 0) fontnumber = 0;
	if (fontnumber > 8) fontnumber = 8;
	font = gra_font[fontnumber].font;
	gcv.foreground = BlackPixelOfScreen(XtScreen(w));
	gc = XtGetGC(w, GCForeground, &gcv);
	XSetFont(dpy, gc, font->fid);
	xp = gra_scaledialogcoordinate(x-xoff);
	yp = gra_scaledialogcoordinate(y-yoff);
	XDrawString(dpy, win, gc, xp, yp+font->ascent+font->descent, msg,
		strlen(msg));
	XtReleaseGC(w, gc);
}

void DiaSetTextSize(INTBIG size)
{
	gra_curdialog->usertextsize = size;
}

void DiaGetTextInfo(char *msg, INTBIG *wid, INTBIG *hei)
{
	int direction, asc, desc;
	INTBIG len, fontnumber;
	XCharStruct xcs;
	XFontStruct *font;

	fontnumber = (gra_curdialog->usertextsize - 4) / 2;
	if (fontnumber < 0) fontnumber = 0;
	if (fontnumber > 8) fontnumber = 8;
	font = gra_font[fontnumber].font;
	len = strlen(msg);
	XTextExtents(font, msg, len, &direction, &asc, &desc, &xcs);
	*wid = xcs.width;
	*hei = font->ascent + font->descent;
}

void DiaTrackCursor(void (*eachdown)(INTBIG x, INTBIG y))
{
	gra_diaeachdown = eachdown;
	trackcursor(FALSE, us_nullup, us_nullvoid, gra_diaeachdownhandler,
		us_nullchar, us_nullvoid, TRACKNORMAL);
}

BOOLEAN gra_diaeachdownhandler(INTBIG ox, INTBIG oy)
{
	INTBIG x, y;

	DiaGetMouse(&x, &y);
	(void)((*gra_diaeachdown)(x, y));
	return(FALSE);
}

void DiaGetMouse(INTBIG *x, INTBIG *y)
{
	if (us_logplay != NULL)
	{
		if (!gra_loggetnextaction(0))
		{
			*x = gra_action.x;
			*y = gra_action.y;
		}
	}
	if (us_logplay == NULL)
	{
		*x = gra_cursorx;
		*y = gra_cursory;
	}
	gra_logwriteaction(DIAUSERMOUSE, 0, *x, *y, 0);
}

BOOLEAN DiaNullDlogList(char **c) { return(FALSE); }

char *DiaNullDlogItem(void) { return(0); }

void DiaNullDlogDone(void) {}

/****************************** MOTIF/LESSTIF DIALOG SUPPORT ******************************/

/*
 * Routine to make sure that changes to the dialog are on the screen
 */
void gra_flushdialog(void)
{
	Display *dpy;

	dpy = XtDisplay(gra_curdialog->window);
	XFlush(dpy);
	XmUpdateDisplay(gra_curdialog->window);
}

/*
 * Routine to convert dialog coordinates
 */
void gra_getdialogcoordinates(RECTAREA *rect, INTBIG *x, INTBIG *y, INTBIG *wid, INTBIG *hei)
{
	*x = gra_scaledialogcoordinate(rect->left);
	*y = gra_scaledialogcoordinate(rect->top);
	*wid = gra_scaledialogcoordinate(rect->right - rect->left);
	*hei = gra_scaledialogcoordinate(rect->bottom - rect->top);
}

INTBIG gra_scaledialogcoordinate(INTBIG x)
{
	return((x * DIALOGNUM + DIALOGDEN/2) / DIALOGDEN);
}

INTBIG gra_makedpycolor(Display *dpy, INTBIG r, INTBIG g, INTBIG b)
{
	XColor xc;
	INTBIG colorvalue, depth, scr;
	Visual *visual;

	/* determine color to use */
	scr = DefaultScreen(dpy);
	visual = DefaultVisual(dpy, scr);
	if (VisualClass(visual) == PseudoColor ||
		VisualClass(visual) == StaticColor)
	{
		xc.red   = r << 8;
		xc.green = g << 8;
		xc.blue  = b << 8;
		XAllocColor(dpy, gra_maincolmap, &xc);
		colorvalue = xc.pixel;
	} else
	{
		depth = DefaultDepth(dpy, scr);
		if (depth == 16)
		{
			colorvalue = ((r & 0xF8) << 8) |
				((g & 0xF8) << 3) | ((b & 0xF8) >> 3);
		} else
		{
			colorvalue = (b << 16) | (g << 8) | r;
		}
	}
	return(colorvalue);
}

/*
 * Routine to make a Pixmap from image data
 */
Pixmap gra_makeicon(char *data, Widget widget)
{
	static Pixmap buildupmain[MAXICONS], buildupalt[MAXICONS];
	REGISTER Pixmap *buildup;
	static INTBIG which = 0;
	static INTBIG foreground, background, screen, bgr, bgg, bgb;
	char *tdptr, *setptr;
	Window win;
	Display *dpy;
	Visual *visual;
	INTBIG i, j, wid, hei, bytesperrow, datasize, x, y, byte, bit,
		outx, lastoutx, outy, lastouty, sdep, pad, allocsdep;
	static GC pgc;
	static XImage *ximage;
	Arg arg[2];
	XGCValues gcv;

	/* determine sizes */
	dpy = XtDisplay(widget);
	if (dpy == gra_maindpy) buildup = buildupmain; else
		if (dpy == gra_altdpy) buildup = buildupalt; else
	{
		ttyputerr(_("Cannot find display for icon"));
		return(0);
	}
	screen = DefaultScreen(dpy);
	wid = gra_scaledialogcoordinate(32);
	hei = gra_scaledialogcoordinate(32);
	sdep = DefaultDepth(dpy, screen);
	allocsdep = sdep;
	if (allocsdep == 24) allocsdep = 32;
	pad = XBitmapPad(dpy);
	bytesperrow = ((wid*allocsdep+pad-1) / pad) * pad / 8;
	datasize = bytesperrow * hei;

	/* initialize icon storage */
	if (gra_icontruedata == 0)
	{
		for(i=0; i<MAXICONS; i++) buildupmain[i] = buildupalt[i] = 0;
		gra_icontruedata = (char *)emalloc(datasize, us_tool->cluster);
		if (gra_icontruedata == 0) return(0);
		gra_iconrowdata = (char *)emalloc(bytesperrow, us_tool->cluster);
		if (gra_iconrowdata == 0) return(0);
	}

	/* choose an icon to build */
	which++;
	if (which >= MAXICONS) which = 0;

	if (buildup[which] == 0)
	{
		win = XtWindow(widget);
		visual = DefaultVisual(dpy, screen);
		buildup[which] = XCreatePixmap(dpy, win, wid, hei, sdep);
		XtSetArg(arg[0], XtNbackground, &gcv.background);
		XtSetArg(arg[1], XtNforeground, &gcv.foreground);
		XtGetValues(widget, arg, 2);
		foreground = gcv.foreground;
		background = gcv.background;
		if (sdep > 16)
		{
			bgr = background & 0xFF;
			bgg = (background >> 8) & 0xFF;
			bgb = (background >> 16) & 0xFF;
		}
		gcv.fill_style = FillStippled;
		pgc = XtGetGC(widget, GCForeground | GCBackground, &gcv);
		ximage = XCreateImage(dpy, visual, sdep, ZPixmap, 0,
			gra_icontruedata, wid, hei, pad, 0);
	}
	lastouty = 0;
	tdptr = gra_icontruedata;
	for(y=0; y<32; y++)
	{
		lastoutx = 0;
		for(i=0; i<bytesperrow; i++) gra_iconrowdata[i] = foreground;
		for(x=0; x<32; x++)
		{
			byte = data[y*4+(x>>3)];
			bit = byte & (0200 >> (x&7));
			if (x == 31) outx = wid-1; else
				outx = gra_scaledialogcoordinate(x);

			if (bit == 0)
			{
				for(i=lastoutx; i<=outx; i++)
				{
					switch (sdep)
					{
						case 1:
							gra_iconrowdata[i>>3] |= (0200 >> (i&7));
							break;
						case 8:
							gra_iconrowdata[i] = background;
							break;
						case 16:
							((short *)gra_iconrowdata)[i] = background;
							break;
						case 24:
						case 32:
							setptr = &((char *)gra_iconrowdata)[i*4];
#ifdef BYTES_SWAPPED
							*setptr++ = 0;
							*setptr++ = bgb;
							*setptr++ = bgg;
							*setptr++ = bgr;
#else
							*setptr++ = bgb;
							*setptr++ = bgg;
							*setptr++ = bgr;
							*setptr++ = 0;
#endif
							break;
					}
				}
			}
			lastoutx = outx+1;
		}
		if (y == 31) outy = hei-1; else
			outy = gra_scaledialogcoordinate(y);
		for(i=lastouty; i<=outy; i++)
		{
			for(j=0; j<bytesperrow; j++)
				tdptr[j] = gra_iconrowdata[j];
			tdptr += bytesperrow;
		}
		lastouty = outy+1;
	}
	XPutImage(dpy, buildup[which], pgc, ximage, 0, 0, 0, 0, wid, hei);
	return(buildup[which]);
}

/*
 * Routine to handle the focus-change events on dialogs.
 */
void gra_dialog_event_handler(Widget w, XtPointer data, XEvent *event, Boolean *cont)
{
	Arg arg[2];
	short inix, iniy;

	/* on the first focus-in, pickup the actual location of the dialog */
	if (w != gra_curdialog->window) return;
	if (event->type != FocusIn) return;
	if (gra_curdialog->inix != -1 || gra_curdialog->iniy != -1) return;
	XtSetArg(arg[0], XtNx, &inix);
	XtSetArg(arg[1], XtNy, &iniy);
	XtGetValues(gra_curdialog->window, arg, 2);
	gra_curdialog->inix = inix;   gra_curdialog->iniy = iniy;
}

/*
 * Routine to handle events specific to dialog items
 */
void gra_dialogaction(Widget w, XtPointer client_data, XmSelectionBoxCallbackStruct *call_data)
{
	INTBIG item, itemtype, value, count, selected, myselected[1], *selectedlist, i;
	char *line;
	XmPushButtonCallbackStruct *cbspb;
	XEvent *event;

	item = ((int)client_data) & 0xFFFF;
	itemtype = gra_curdialog->itemdesc->list[item].type;
	gra_dialoghit = item+1;

	if ((itemtype&ITEMTYPE) == BUTTON || (itemtype&ITEMTYPE) == DEFBUTTON)
	{
		XmProcessTraversal(gra_curdialog->window, XmTRAVERSE_HOME);
		gra_logwriteaction(DIAITEMCLICK, 0, gra_dialoghit, 0, 0);
	}
	if ((itemtype&ITEMTYPE) == RADIO)
	{
		gra_logwriteaction(DIAITEMCLICK, 0, gra_dialoghit, 0, 0);
	}
	if ((itemtype&ITEMTYPE) == CHECK)
	{
		value = XmToggleButtonGetState(w);
		XmToggleButtonSetState(w, value == 0 ? True : False, 0);
		gra_logwriteaction(DIAITEMCLICK, 0, gra_dialoghit, 0, 0);
	}
	if ((itemtype&ITEMTYPE) == ICON)
	{
		cbspb = (XmPushButtonCallbackStruct *)call_data;
		event = cbspb->event;
		gra_cursorx = event->xbutton.x * DIALOGDEN / DIALOGNUM +
			gra_curdialog->itemdesc->list[item].r.left;
		gra_cursory = event->xbutton.y * DIALOGDEN / DIALOGNUM +
			gra_curdialog->itemdesc->list[item].r.top;
		gra_logwriteaction(DIAITEMCLICK, 0, gra_dialoghit, 0, 0);
	}
	if ((itemtype&ITEMTYPE) == POPUP)
	{
		selected = DiaGetPopupEntry(gra_dialoghit);
		gra_logwriteaction(DIAPOPUPSEL, 0, gra_dialoghit, selected, 0);
	}
	if ((itemtype&ITEMTYPE) == SCROLL || (itemtype&ITEMTYPE) == SCROLLMULTI)
	{
		if ((itemtype&ITEMTYPE) == SCROLLMULTI)
		{
			selectedlist = DiaGetCurLines(gra_dialoghit);
			for(i=0; selectedlist[i] >= 0; i++) ;
			gra_logwriteaction(DIASCROLLSEL, 0, gra_dialoghit, i, selectedlist);
		} else
		{
			myselected[0] = DiaGetCurLine(gra_dialoghit);
			gra_logwriteaction(DIASCROLLSEL, 0, gra_dialoghit, 1, myselected);
		}
	}
	if ((itemtype&ITEMTYPE) == EDITTEXT)
	{
		line = XmTextGetString(w);
		if (item == gra_curdialog->editline)
		{
			/* determine which character was typed (hack!!!) */
			if (strlen(line) > strlen(gra_curdialog->lastedittext))
			{
				gra_dialoghitchar = line[strlen(line)-1];
			} else if (strlen(line) < strlen(gra_curdialog->lastedittext))
			{
				gra_dialoghitchar = BACKSPACEKEY;
			}

			/* reset to previous state */
			XtRemoveCallback(w, XmNvalueChangedCallback,
				(XtCallbackProc)gra_dialogaction, (XtPointer)item);
			XmTextSetString(w, gra_curdialog->lastedittext);
			count = XmTextGetLastPosition(w);
			XmTextSetSelection(w, count, count, 0);
			XmTextSetInsertionPosition(w, count); 
			XtAddCallback(w, XmNvalueChangedCallback,
				(XtCallbackProc)gra_dialogaction, (XtPointer)item);
		}
		gra_logwriteaction(DIAEDITTEXT, 0, gra_dialoghit, gra_dialoghitchar, line);
	}
}

/*
 * Routine to handle clicks in user-drawn items
 */
void gra_dialogdraw(Widget widget, XEvent *event, String *args, int *num_args)
{
	INTBIG i;

	/* see which dialog item this applies to */
	for(i=0; i<gra_curdialog->itemdesc->items; i++)
		if (widget == gra_curdialog->items[i]) break;
	if (i >= gra_curdialog->itemdesc->items) return;

	/* get cursor coordinates */
	gra_cursorx = event->xbutton.x * DIALOGDEN / DIALOGNUM +
		gra_curdialog->itemdesc->list[i].r.left;
	gra_cursory = event->xbutton.y * DIALOGDEN / DIALOGNUM +
		gra_curdialog->itemdesc->list[i].r.top;

	/* determine type of action */
	if (*num_args != 1) return;
	if (strcmp(args[0], "motion") == 0)
	{
		gra_inputstate = MOTION;
		if ((event->xmotion.state & (Button1Mask | Button2Mask | Button3Mask)) == 0)
			gra_inputstate |= BUTTONUP;
	} else
	{
		switch (event->xbutton.button)
		{
			case 1: gra_inputstate = ISLEFT;    break;
			case 2: gra_inputstate = ISMIDDLE;  break;
			case 3: gra_inputstate = ISRIGHT;   break;
			case 4: gra_inputstate = ISWHLFWD;  break;
			case 5: gra_inputstate = ISWHLBKW;  break;
		}
		if ((event->xbutton.state & ShiftMask) != 0)
			gra_inputstate |= SHIFTISDOWN;
		if ((event->xbutton.state & LockMask) != 0)
			gra_inputstate |= SHIFTISDOWN;
		if ((event->xbutton.state & ControlMask) != 0)
			gra_inputstate |= CONTROLISDOWN;
		if ((event->xbutton.state & (Mod1Mask|Mod4Mask)) != 0)
			gra_inputstate |= METAISDOWN;
		if (strcmp(args[0], "up") == 0)
			gra_inputstate |= BUTTONUP;
	}
	if (strcmp(args[0], "down") == 0)
	{
		gra_dialoghit = i+1;
		gra_logwriteaction(DIAITEMCLICK, 0, gra_dialoghit, 0, 0);
	}
}

/*
 * Routine to handle requests to copy a scroll list
 */
void gra_dialogcopywholelist(Widget widget, XEvent *event, String *args, int *num_args)
{
	INTBIG count, i, status, len;
	long itemid;
	XmString *strlist, copylabel;
	char *text;
	Display *dpy;
	Window win;

	dpy = XtDisplay(widget);
	win = XtWindow(widget);
	copylabel = XmStringCreateLocalized("Electric dialog scroll list");
	for(;;)
	{
		status = XmClipboardStartCopy(dpy, win, copylabel, CurrentTime, widget, NULL, &itemid);
		if (status != ClipboardLocked) break;
	}
	XmStringFree(copylabel);
	XtVaGetValues(widget, XmNitemCount, &count, XmNitems, &strlist, NULL);
	for(i=0; i<count; i++)
	{
		XmStringGetLtoR(strlist[i], XmFONTLIST_DEFAULT_TAG, &text);
		len = strlen(text);
		text[len] = '\n';
		for(;;)
		{
			status = XmClipboardCopy(dpy, win, itemid, "STRING", text, len+1, i, NULL);
			if (status != ClipboardLocked) break;
		}
		text[len] = 0;
		XtFree(text);
	}
	for(;;)
	{
		status = XmClipboardEndCopy(dpy, win, itemid);
		if (status != ClipboardLocked) break;
	}
}

/*
 * Routine to handle keystrokes in an "opaque" item
 * (replaces the text with "*")
 */
void gra_dialogopaqueaction(Widget w, XtPointer client_data,
	XmTextVerifyCallbackStruct *call_data)
{
	INTBIG i;

	if (call_data->startPos < call_data->currInsert)
	{
		call_data->endPos = strlen(gra_opaquefield);
		gra_opaquefield[call_data->startPos] = 0;
		return;
	}
	if (call_data->text->length > 1)
	{
		call_data->doit = False;
		return;
	}
	strncat(gra_opaquefield, call_data->text->ptr, call_data->text->length);
	gra_opaquefield[call_data->endPos + call_data->text->length] = 0;

	for(i=0; i<call_data->text->length; i++)
		call_data->text->ptr[i] = '*';
}

/*
 * Routine to redraw user-drawn items (calls the user's callback)
 */
void gra_dialogredraw(Widget w, XtPointer client_data, XmSelectionBoxCallbackStruct *call_data)
{
	RECTAREA ra;

	if (gra_curdialog->redrawroutine != 0)
	{
		DiaItemRect(gra_curdialog->redrawitem, &ra);
		(*gra_curdialog->redrawroutine)(&ra);
	}
}

/*
 * Routine to redraw separator lines
 */
void gra_dialogredrawsep(Widget w, XtPointer client_data, XmSelectionBoxCallbackStruct *call_data)
{
	Display *dpy;
	Window win;
	INTBIG x, y, wid, hei;
	XGCValues gcv;
	GC gc;

	gra_getdialogcoordinates(&gra_curdialog->itemdesc->list[(INTBIG)client_data].r,
		&x, &y, &wid, &hei);
	dpy = XtDisplay(w);
	win = XtWindow(w);
	gcv.foreground = BlackPixelOfScreen(XtScreen(w));
	gc = XtGetGC(w, GCForeground, &gcv);
	XSetForeground(dpy, gc, BlackPixelOfScreen(XtScreen(w)));
	XFillRectangle(dpy, win, gc, 0, 0, wid, hei);
	XtReleaseGC(w, gc);
}

#else

/****************************** ATHENA DIALOGS ******************************/

/* the four scroller arrows */
#define UPARROW      0
#define DOWNARROW    1
#define LEFTARROW    2
#define RIGHTARROW   3

#define THUMBSIZE      16		/* width of the thumb area in scroll slider */
#define MAXSCROLLS      4		/* maximum scroll items in a dialog */
#define MAXDIALOGS      2		/* maximum subdialogs on screen */
#define MAXMATCH       50
#define MINSCROLLTICKS  2		/* minimum ticks between scrollbar slider arrows */
#define MINPAGETICKS   20		/* minimum ticks between scrollbar slider page shifts */
#define DRAGBARHEIGHT  18

typedef void (*USERTYPE)(RECTAREA*);

/* the scroll arrow definition */
#define ARROWLEN     7
static INTBIG dia_arrowx[] = {4, 10, 10, 13, 7, 1, 4};
static INTBIG dia_arrowy[] = {12, 12, 8, 8, 2, 8, 8};

typedef struct
{
	INTBIG  count;
	INTBIG  current;
	char **namelist;
} POPUPDATA;

typedef struct
{
	INTBIG   scrollitem;		/* item number of SCROLL area (-1 if none) */
	RECTAREA userrect;			/* position of SCROLL area */
	INTBIG   flags;				/* state SCROLL area */
	INTBIG   vthumbpos;			/* position of vertical thumb slider  */
	INTBIG   hthumbpos;			/* position of horizontal thumb slider */
	INTBIG   horizfactor;		/* shift of horizontal text (0 to 100) */
	INTBIG   firstline;			/* line number of top line */
	INTBIG   linesinfolder;		/* number of lines displayable */
	INTBIG   whichlow;			/* first currently highlighted line */
	INTBIG   whichhigh;			/* last currently highlighted line */
	INTBIG   lineheight;		/* height of line of text */
	INTBIG   lineoffset;		/* offset to baseline for text */
	char   **scrolllist;		/* list of text lines */
	INTBIG   scrolllistsize;	/* size of line list */
	INTBIG   scrolllistlen;		/* number of valid lines/list */
} DSCROLL;

typedef struct
{
	DIALOG   *dlgresaddr;		/* address of this dialog */
	short     onscreen;			/* nonzero if displayed */
	short     defaultbutton;	/* default button */
	INTBIG    theDialog;

	/* for the scroll item */
	INTBIG     scrollcount;			/* number of scroll items */
	INTBIG     curscroll;			/* current scroll item */
	DSCROLL    scroll[MAXSCROLLS];	/* data structures for the scroll item(s) */

	/* for the current edit text item */
	INTBIG     curitem;				/* current edit item */
	INTBIG     editstart, editend;	/* start/end selected text in edit item */
	INTBIG     firstch;				/* first displayed character in edit item */
	INTBIG     opaqueitem;			/* item number of opaque edit text */

	INTBIG     usertextsize;		/* size of text in user areas */
	INTBIG     userdoubleclick;		/* nonzero to allow double-click in user areas */
} DIALOCAL;

DIALOCAL   dia_it, dia_save[MAXDIALOGS];
INTBIG     dia_active = 0;
INTBIG     dia_savepos = 0;
INTBIG     dia_slineheight;			/* height of a line of scroll text */
INTBIG     dia_slineoffset;			/* scroll text: distance up to baseline */
INTBIG     dia_lineheight;			/* height of a line of other text */
INTBIG     dia_lineoffset;			/* other text: distance up to baseline */
INTBIG     dia_curlineoffset;		/* current distance up to baseline */
INTBIG     dia_knowndialogcount = 0;/* number of resource-read dialogs */
DIALOG   **dia_knowndialogs;		/* array of resource-read dialogs */
INTBIG    *dia_knowdialognumbers;	/* array of resource-read dialog numbers */
INTBIG     dia_firstupdate;
INTBIG     dia_wasin, dia_lbase, dia_hbase, dia_offset, dia_ddata;
RECTAREA   dia_rect;				/* the current rectangle being tracked */
char      *dia_msg;
POPUPDATA *dia_pd;					/* the current popup menu */
INTBIG     dia_cnt;					/* the current entry count in the popup menu */
INTBIG     dia_sta;					/* the current start point in the popup menu */
UINTBIG    dia_lastdiatime = 0;		/* time of last scrollbar slider button action */
INTBIG     dia_xoffset, dia_yrev;
INTBIG     dia_revy;					/* for reversing Y coordinates */
POLYGON   *dia_poly = NOPOLYGON;
WINDOWPART dia_window;				/* current window in which dialog is drawn */
INTBIG     dia_lastx, dia_lasty;
GRAPHICS dia_drawgray = {LAYERA, DGRAY, SOLIDC, SOLIDC,
	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};
GRAPHICS dia_drawdim = {LAYERA, GRAY, SOLIDC, SOLIDC,
	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};
void     (*gra_diaeachdown)(INTBIG x, INTBIG y);
extern GRAPHICS us_menufigs;

/* prototypes for local routines */
static BOOLEAN Dbuttondown(INTBIG x, INTBIG y);
static BOOLEAN Dcheckdown(INTBIG x, INTBIG y);
static void    Dcorrectxy(INTBIG *x, INTBIG *y);
static void    Ddonedialogwindow(void);
static void    Ddoneedit(void);
static BOOLEAN Ddownarrow(INTBIG x, INTBIG y);
static BOOLEAN Ddownpage(INTBIG x, INTBIG y);
static void    Ddragwindow(INTBIG x, INTBIG y);
static void    Ddrawarrow(INTBIG sc, INTBIG which, INTBIG filled);
static void    Ddrawbox(INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy, GRAPHICS *which);
static void    Ddrawcircle(RECTAREA *r, INTBIG dim);
static void    Ddrawdisc(RECTAREA *r);
static void    Ddrawhorizslider(INTBIG sc);
static void    Ddrawitem(INTBIG type, RECTAREA *r, char *msg, INTBIG dim);
static void    Ddrawline(INTBIG xf, INTBIG yf, INTBIG xt, INTBIG yt);
static void    Ddrawmsg(INTBIG sc, char *msg, INTBIG which);
static void    Ddrawpolygon(INTBIG *xv, INTBIG *yv, INTBIG count, INTBIG filled);
static void    Ddrawpopupentries(void);
static void    Ddrawrectframe(RECTAREA *r, INTBIG on, INTBIG dim);
static void    Ddrawroundrectframe(RECTAREA *r, INTBIG arc, INTBIG dim);
static void    Ddrawtext(char *msg, INTBIG len, INTBIG x, INTBIG y, INTBIG dim);
static void    Ddrawvertslider(INTBIG sc);
static void    Deditbox(RECTAREA *r, INTBIG draw, INTBIG dim);
static BOOLEAN Deditdown(INTBIG x, INTBIG y);
static INTBIG  Dgetcolorindex(INTBIG r, INTBIG g, INTBIG b);
static INTBIG  Dgeteditpos(RECTAREA *r, INTBIG x, INTBIG y, char *msg);
static INTBIG  Dgettextsize(char *msg, INTBIG len);
static void    Dgetwindowextent(INTBIG *ly, INTBIG *hy);
static void    Dgrayrect(RECTAREA *r);
static void    Dhighlight(INTBIG on);
static void    Dhighlightrect(RECTAREA *r);
static BOOLEAN Dhscroll(INTBIG x, INTBIG y);
static void    Dinsertstr(char *insmsg);
static void    Dinsetrect(RECTAREA *r, INTBIG amt);
static void    Dintdrawrect(RECTAREA *r, INTBIG colorindex);
static void    Dinvertbox(INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy);
static void    Dinvertentry(INTBIG sc, INTBIG on);
static void    Dinvertrect(RECTAREA *r);
static void    Dinvertrectframe(RECTAREA *r);
static BOOLEAN Dleftarrow(INTBIG x, INTBIG y);
static BOOLEAN Dleftpage(INTBIG x, INTBIG y);
static void    Dnewdialogwindow(RECTAREA *r, char *movable);
static void    Dnewdialogwindowframe(RECTAREA *r, char *movable);
static INTBIG  Dneweditbase(void);
static void    Dputicon(INTBIG x, INTBIG y, char *data);
static void    Dredrawdialogwindow(void);
static void    Dredrawscroll(INTBIG sc);
static void    Drestorerect(INTBIG sr);
static BOOLEAN Drightarrow(INTBIG x, INTBIG y);
static BOOLEAN Drightpage(INTBIG x, INTBIG y);
static INTBIG  Dsaverect(RECTAREA *r);
static void    Dsethscroll(INTBIG sc);
static void    Dsettextsmall(INTBIG sc);
static void    Dsetvscroll(INTBIG sc);
static void    Dshiftbits(RECTAREA *sr, RECTAREA *dr);
static void    Dstuffmessage(char *msg, RECTAREA *r, INTBIG dim);
static void    Dstufftext(char *msg, RECTAREA *r);
static void    Dtextlocation(char *msg, INTBIG len, RECTAREA *r, INTBIG *wid, INTBIG *line);
static void    Dtrackcursor(INTBIG, INTBIG, BOOLEAN (*eachdown)(INTBIG, INTBIG));
static BOOLEAN Dtrackpopup(INTBIG x, INTBIG y);
static BOOLEAN Duparrow(INTBIG x, INTBIG y);
static BOOLEAN Duppage(INTBIG x, INTBIG y);
static BOOLEAN Dvscroll(INTBIG x, INTBIG y);
static INTBIG  Dwaitforaction(INTBIG *x, INTBIG *y, INTBIG *chr, INTBIG *special, UINTBIG *time, BOOLEAN *shifted);
static INTBIG  Dwhichitem(INTBIG x, INTBIG y);

/*
 * Routine to initialize a dialog described by "dialog".
 * Returns true if dialog cannot be initialized.
 */
BOOLEAN DiaInitDialog(DIALOG *dialog)
{
	INTBIG itemtype, i, j, amt;
	INTBIG sx, sy;
	RECTAREA r;
	char *save, *line;
	POPUPDATA *pd;

	/* be sure the dialog is translated */
	DiaTranslate(dialog);

	/* save the current dialog if this is a subdialog */
	if (dia_savepos > 0)
	{
		if (dia_savepos > MAXDIALOGS) return(TRUE);
		dia_save[dia_savepos-1] = dia_it;
	}
	dia_savepos++;

	/* compute size of some items to automatically scale them */
	gra_settextsize(NOWINDOWPART, TXTMENU, 0, 0, 0, 0);
	for(i=0; i<dialog->items; i++)
	{
		itemtype = dialog->list[i].type & ITEMTYPE;
		switch (itemtype)
		{
			case DEFBUTTON:
			case BUTTON:
			case CHECK:
			case RADIO:
			case MESSAGE:
				line = dialog->list[i].msg;
				screengettextsize(NOWINDOWPART, line, &sx, &sy);
				j = sx;
				amt = j - (dialog->list[i].r.right - dialog->list[i].r.left);
				if (amt > 0)
				{
					for(j=0; j<dialog->items; j++)
					{
						if (j == i) continue;
						if (dialog->list[j].r.left >= dialog->list[i].r.right)
							dialog->list[j].r.left += amt;
						if (dialog->list[j].r.right >= dialog->list[i].r.right)
							dialog->list[j].r.right += amt;
					}
					dialog->list[i].r.right += amt;
				}
				break;
		}
	}
	for(i=0; i<dialog->items; i++)
	{
		j = dialog->list[i].r.right + 9;
		if (j < dialog->windowRect.right - dialog->windowRect.left) continue;
		dialog->windowRect.right = dialog->windowRect.left + j;
	}

	/* initialize dialog data structures */
	dia_it.defaultbutton = OK;
	dia_it.dlgresaddr = dialog;
	dia_it.curitem = -1;
	dia_it.opaqueitem = -1;
	dia_it.usertextsize = 8;
	dia_it.userdoubleclick = 0;
	for(i=0; i<MAXSCROLLS; i++)
	{
		dia_it.scroll[i].scrollitem = -1;
		dia_it.scroll[i].horizfactor = 0;
		dia_it.scroll[i].scrolllistlen = 0;
		dia_it.scroll[i].scrolllistsize = 0;
	}
	dia_it.curscroll = 0;
	dia_it.scrollcount = 0;
	dia_it.onscreen = 1;
	dia_it.firstch = 0;

	/* make the window */
	Dnewdialogwindow(&dialog->windowRect, dialog->movable);
	Dnewdialogwindowframe(&dialog->windowRect, dialog->movable);

	/* find the default button */
	for(i=0; i<dialog->items; i++)
	{
		itemtype = dia_it.dlgresaddr->list[i].type;
		if ((itemtype&ITEMTYPE) == DEFBUTTON)
			dia_it.defaultbutton = i+1;
	}

	/* loop through all of the dialog entries, drawing them */
	for(i=0; i<dialog->items; i++)
	{
		/* draw the item */
		itemtype = dia_it.dlgresaddr->list[i].type;
		line = dia_it.dlgresaddr->list[i].msg;
		r = dia_it.dlgresaddr->list[i].r;

		if ((itemtype&ITEMTYPE) == EDITTEXT)
		{
			if (dia_it.curitem == -1) dia_it.curitem = i;
		}
		if ((itemtype&ITEMTYPE) == SCROLL || (itemtype&ITEMTYPE) == SCROLLMULTI)
		{
			if (dia_it.scrollcount < MAXSCROLLS)
				dia_it.scroll[dia_it.scrollcount++].scrollitem = i;
		}
		if ((itemtype&ITEMTYPE) == MESSAGE || (itemtype&ITEMTYPE) == EDITTEXT)
		{
			save = (char *)emalloc(strlen(line)+1, el_tempcluster);
			if (save == 0) return(TRUE);
			(void)strcpy(save, line);
			dia_it.dlgresaddr->list[i].data = (INTBIG)save;
		} else if ((itemtype&ITEMTYPE) == POPUP)
		{
			pd = (POPUPDATA *)emalloc(sizeof (POPUPDATA), el_tempcluster);
			if (pd == 0) return(TRUE);
			pd->count = 0;
			dia_it.dlgresaddr->list[i].data = (INTBIG)pd;
			line = (char *)pd;
		} else dia_it.dlgresaddr->list[i].data = 0;

		Ddrawitem(itemtype, &r, line, 0);

		/* highlight the default button */
		if (i == dia_it.defaultbutton-1 &&
			(itemtype == BUTTON || itemtype == DEFBUTTON)) Dhighlightrect(&r);
	}
	if (dia_it.curitem >= 0)
	{
		dia_it.editstart = 0;
		dia_it.editend = strlen(dia_it.dlgresaddr->list[dia_it.curitem].msg);
		Dhighlight(1);
	}
	dia_active++;
	dia_firstupdate = 1;
	return(FALSE);
}

/*
 * Routine to handle actions and return the next item hit.
 */
INTBIG DiaNextHit(void)
{
	INTBIG chr, itemHit;
	char ch[2];

	for(;;)
	{
		/* if interrupted, stop dialog */
		if (el_pleasestop != 0) return(CANCEL);

		/* get the next event, ignore fluff */
		chr = DiaGetNextCharacter(&itemHit);
		if (chr == -1) continue;

		/* non-character events return immediately */
		if (chr == -2) break;

		/* handle special character events */
		if (chr == ESCKEY) return(CANCEL);
		if (chr == '\n' || chr == '\r' || chr == CTRLCKEY) return(dia_it.defaultbutton);

		/* handle delete/backspace key */
		if ((chr == BACKSPACEKEY || chr == DELETEKEY) && dia_it.curitem >= 0)
		{
			if (dia_it.editstart == dia_it.editend && dia_it.editstart > 0)
				dia_it.editstart--;
			chr = 0;
		}

		ch[0] = chr;   ch[1] = 0;
		Dinsertstr(ch);
		break;
	}
	return(itemHit);
}

/*
 * Routine to parse the next input event and return the next character typed into
 * the current edit item.  If the routine returns -1, nothing has happened.  If the
 * routine returns -2, an item has been hit (and is in "itemHit").
 */
INTSML DiaGetNextCharacter(INTBIG *itemHit)
{
	RECTAREA r;
	char *msg;
	INTBIG sr, thumbval, special;
	UINTBIG time;
	static char match[MAXMATCH];
	static INTBIG matchpos = 0;
	static UINTBIG lasttime = 0;
	INTBIG i, j, chr, type, which, v, t, n, x, y, sc, oak, newfirst, ly, hy;
	BOOLEAN shifted;
	INTBIG selitems[MAXSCROLLMULTISELECT];
	DSCROLL *scr;
	POPUPDATA *pd;

	oak = Dwaitforaction(&x, &y, &chr, &special, &time, &shifted);
	if (oak == 1 || oak == 5)
	{
		/* hit in the window: find the item */
		*itemHit = Dwhichitem(x, y);
		if (*itemHit == 0) return(-1);
		type = dia_it.dlgresaddr->list[*itemHit-1].type;
		r = dia_it.dlgresaddr->list[*itemHit-1].r;

		if ((type&ITEMTYPE) == MESSAGE || (type&INACTIVE) != 0) return(-1);

		/* if the item is a popup menu, display it and track */
		if ((type&ITEMTYPE) == POPUP)
		{
			pd = (POPUPDATA *)dia_it.dlgresaddr->list[*itemHit-1].data;

			/* system can't handle popups automatically: do it by hand */
			dia_cnt = pd->count;    dia_sta = 0;
			Dgetwindowextent(&ly, &hy);
			for(;;)
			{
				i = r.top - ((pd->current-dia_sta) * dia_lineheight) + 1;
				if (i > ly) break;
				dia_sta++;
			}
			for(;;)
			{
				i = r.bottom + ((dia_cnt-pd->current-1) * dia_lineheight) + 1;
				if (i < hy) break;
				dia_cnt--;
			}
			r.top -= ((pd->current-dia_sta) * dia_lineheight) + 1;
			r.bottom += ((dia_cnt-pd->current-1) * dia_lineheight) + 1;
			r.left--;   r.right++;
			sr = Dsaverect(&r);
			dia_pd = pd;
			dia_rect = r;
			Dsettextsmall(0);
			Ddrawpopupentries();
			dia_wasin = -1;
			Dtrackcursor(x, y, Dtrackpopup);
			Drestorerect(sr);
			if (dia_wasin < 0) return(-1);
			i = dia_wasin + dia_sta;

			pd->current = i;
			Ddrawitem(POPUP, &dia_it.dlgresaddr->list[*itemHit-1].r, (char *)pd, 0);
			return(-2);
		}

		/* items under the user's control are given to the user */
		if (type == USERDRAWN)
		{
			/* double click in user selects and returns */
			if (oak == 5 && dia_it.userdoubleclick != 0)
			{
				*itemHit = dia_it.defaultbutton;
			}
			return(-2);
		}

		/* if the item is edit text, make it the current one */
		if (type == EDITTEXT)
		{
			if (dia_it.curitem != *itemHit - 1) Ddoneedit(); else Dhighlight(0);
			dia_it.curitem = *itemHit - 1;
			msg = (char *)dia_it.dlgresaddr->list[*itemHit-1].data;
			i = Dgeteditpos(&r, x, y, msg);
			if (oak == 5)
			{
				/* look for a full word about position "base" */
				for(dia_it.editstart=i-1; dia_it.editstart>=0; dia_it.editstart--)
					if (!isalnum(msg[dia_it.editstart])) break;
				dia_it.editstart++;
				for(dia_it.editend = dia_it.editstart; msg[dia_it.editend] != 0; dia_it.editend++)
					if (!isalnum(msg[dia_it.editend])) break;
				Dhighlight(1);
			} else
			{
				dia_it.editstart = dia_it.editend = i;
				Dhighlight(1);
			}
			dia_lbase = dia_it.editstart;   dia_hbase = dia_it.editend;
			dia_rect = r;
			dia_msg = msg;
			Dtrackcursor(x, y, Deditdown);
			return(-2);
		}

		/* if the item is a button, reverse it and track */
		if (type == BUTTON || type == DEFBUTTON)
		{
			r.left++;   r.right--;
			r.top++;    r.bottom--;
			Dinvertrect(&r);
			dia_rect = r;
			dia_wasin = 1;
			Dtrackcursor(x, y, Dbuttondown);
			if (dia_wasin == 0) return(-1);
			Dinvertrect(&r);
			return(-2);
		}

		/* if the item is a check, outline it and track */
		if (type == CHECK)
		{
			dia_rect = r;
			r.right = r.left + 11;   r.left++;
			r.top = (r.top + r.bottom) / 2 - 5;
			r.bottom = r.top + 10;
			Ddrawrectframe(&r, 1, 0);
			dia_wasin = 1;
			dia_ddata = dia_it.dlgresaddr->list[*itemHit-1].data;
			Dtrackcursor(x, y, Dcheckdown);
			if (dia_wasin == 0) return(-1);
			Ddrawrectframe(&r, 0, 0);
			if (dia_ddata != 0)
			{
				Ddrawline(r.left, r.top, r.right-1, r.bottom-1);
				Ddrawline(r.left, r.bottom-1, r.right-1, r.top);
			}
			return(-2);
		}

		/* if the item is a scroll area, select a line */
		if (type == SCROLL || type == SCROLLMULTI)
		{
			for(sc=0; sc<dia_it.scrollcount; sc++)
				if (dia_it.scroll[sc].scrollitem == *itemHit - 1) break;
			if (sc >= dia_it.scrollcount) return(-1);
			scr = &dia_it.scroll[dia_it.curscroll=sc];

			if (x > scr->userrect.right)
			{
				/* cursor in vertical slider */
				if (scr->scrolllistlen <= scr->linesinfolder) return(-1);
				if (y > scr->userrect.bottom-16)
				{
					/* the down arrow */
					Ddrawarrow(sc, DOWNARROW, 1);
					Dtrackcursor(x, y, Ddownarrow);
					Ddrawarrow(sc, DOWNARROW, 0);
					return(-1);
				}
				if (y < scr->userrect.top+16)
				{
					/* the up arrow */
					Ddrawarrow(sc, UPARROW, 1);
					Dtrackcursor(x, y, Duparrow);
					Ddrawarrow(sc, UPARROW, 0);
					return(-1);
				}
				if (y > scr->vthumbpos+THUMBSIZE/2 && y <= scr->userrect.bottom-16)
				{
					/* scroll down one page */
					Dtrackcursor(x, y, Ddownpage);
					return(-1);
				}
				if (y < scr->vthumbpos-THUMBSIZE/2 && y >= scr->userrect.top+16)
				{
					/* scroll up one page */
					Dtrackcursor(x, y, Duppage);
					return(-1);
				}
				if (y >= scr->vthumbpos-THUMBSIZE/2 && y <= scr->vthumbpos+THUMBSIZE/2)
				{
					/* drag slider appropriately */
					v = y;   t = scr->vthumbpos;
					dia_rect = dia_it.dlgresaddr->list[*itemHit-1].r;
					dia_rect.left = dia_rect.right - 14;
					dia_rect.right--;
					dia_rect.top = scr->vthumbpos - THUMBSIZE/2;
					dia_rect.bottom = scr->vthumbpos + THUMBSIZE/2;
					Dinvertrectframe(&dia_rect);
					dia_offset = t-v;
					Dtrackcursor(x, y, Dvscroll);
					dia_rect.top = scr->vthumbpos - THUMBSIZE/2;
					dia_rect.bottom = scr->vthumbpos + THUMBSIZE/2;
					Dinvertrectframe(&dia_rect);
					r = scr->userrect;
					r.top += 16;   r.bottom -= 16;
					thumbval = scr->vthumbpos - r.top - THUMBSIZE/2;
					thumbval *= scr->scrolllistlen - scr->linesinfolder;
					thumbval /= r.bottom - r.top - THUMBSIZE;
					i = thumbval;
					if (i < 0) i = 0;
					if (i == scr->firstline) return(-1);
					scr->firstline = i;
					Dredrawscroll(sc);
					Dinvertentry(sc, 1);
					Dsetvscroll(sc);
					return(-1);
				}
			} else if (y > scr->userrect.bottom)
			{
				/* cursor in horizontal slider */
				if (x > scr->userrect.right-16)
				{
					/* the right arrow */
					Ddrawarrow(sc, RIGHTARROW, 1);
					Dtrackcursor(x, y, Drightarrow);
					Ddrawarrow(sc, RIGHTARROW, 0);
					return(-1);
				}
				if (x < scr->userrect.left+16)
				{
					/* the left arrow */
					Ddrawarrow(sc, LEFTARROW, 1);
					Dtrackcursor(x, y, Dleftarrow);
					Ddrawarrow(sc, LEFTARROW, 0);
					return(-1);
				}
				if (x > scr->hthumbpos+THUMBSIZE/2 && x <= scr->userrect.right-16)
				{
					/* scroll right one page */
					Dtrackcursor(x, y, Drightpage);
					return(-1);
				}
				if (x < scr->hthumbpos-THUMBSIZE/2 && x >= scr->userrect.left+16)
				{
					/* scroll left one page */
					Dtrackcursor(x, y, Dleftpage);
					return(-1);
				}
				if (x >= scr->hthumbpos-THUMBSIZE/2 && x <= scr->hthumbpos+THUMBSIZE/2)
				{
					/* drag slider appropriately */
					v = x;   t = scr->hthumbpos;
					dia_rect = dia_it.dlgresaddr->list[*itemHit-1].r;
					dia_rect.top = dia_rect.bottom - 14;
					dia_rect.bottom--;
					dia_rect.left = scr->hthumbpos - THUMBSIZE/2;
					dia_rect.right = scr->hthumbpos + THUMBSIZE/2;
					Dinvertrectframe(&dia_rect);
					dia_offset = t - v;
					Dtrackcursor(x, y, Dhscroll);
					dia_rect.left = scr->hthumbpos - THUMBSIZE/2;
					dia_rect.right = scr->hthumbpos + THUMBSIZE/2;
					Dinvertrectframe(&dia_rect);
					r = scr->userrect;
					r.left += 16;   r.right -= 16;
					thumbval = scr->hthumbpos - r.left - THUMBSIZE/2;
					thumbval *= 100;
					thumbval /= r.right - r.left - THUMBSIZE;
					i = thumbval;
					if (i < 0) i = 0;
					if (i == scr->horizfactor) return(-1);
					scr->horizfactor = i;
					Dredrawscroll(sc);
					Dinvertentry(sc, 1);
					Dsethscroll(sc);
					return(-1);
				}
			} else
			{
				/* double click in scroll selects and returns */
				if (oak == 5 && scr->scrolllistlen > 0 && (scr->flags&SCDOUBLEQUIT) != 0)
				{
					*itemHit = dia_it.defaultbutton;
					return(-2);
				}

				if ((scr->flags&SCSELMOUSE) != 0)
				{
					/* cursor in list: select an entry */
					which = y - r.top;
					which /= scr->lineheight;
					which += scr->firstline;
					if (which >= scr->scrolllistlen) which = scr->scrolllistlen - 1;
					if (type == SCROLLMULTI && shifted && scr->whichlow >= 0)
					{
						if (which < scr->whichlow)
						{
							j = 0;
							for(i=which; i<= scr->whichhigh; i++)
								selitems[j++] = i;
							DiaSelectLines(*itemHit, j, selitems);
						} else if (which > scr->whichhigh)
						{
							j = 0;
							for(i=scr->whichlow; i<= which; i++)
								selitems[j++] = i;
							DiaSelectLines(*itemHit, j, selitems);
						} else
						{
							if (which - scr->whichlow < scr->whichhigh - which)
							{
								j = 0;
								for(i=which; i<= scr->whichhigh; i++)
									selitems[j++] = i;
								DiaSelectLines(*itemHit, j, selitems);
							} else
							{
								j = 0;
								for(i=scr->whichlow; i<= which; i++)
									selitems[j++] = i;
								DiaSelectLines(*itemHit, j, selitems);
							}
						}

					} else
					{
						DiaSelectLine(*itemHit, which);
					}
					if ((scr->flags&SCREPORT) == 0) return(-1);
				}
			}
		}
		return(-2);
	}

	/* get the character, return immediately if special */
	if (oak != 0) return(-1);
	if (chr == ESCKEY || chr == '\n' || chr == '\r' || chr == CTRLCKEY) return(chr);

	/* handle arrow positioning */
	if ((special&SPECIALKEYDOWN) != 0)
	{
		switch ((special&SPECIALKEY)>>SPECIALKEYSH)
		{
			case SPECIALKEYARROWL:
				if (dia_it.curitem < 0) return(-1);
				Dhighlight(0);
				dia_it.editstart--;
				if (dia_it.editstart < 0) dia_it.editstart = 0;
				dia_it.editend = dia_it.editstart;
				newfirst = Dneweditbase();
				if (dia_it.firstch != newfirst)
				{
					dia_it.firstch = newfirst;
					msg = (char *)dia_it.dlgresaddr->list[dia_it.curitem].data;
					Dstufftext(msg, &dia_it.dlgresaddr->list[dia_it.curitem].r);
				}
				Dhighlight(1);
				return(-1);
			case SPECIALKEYARROWR:
				if (dia_it.curitem < 0) return(-1);
				Dhighlight(0);
				msg = (char *)dia_it.dlgresaddr->list[dia_it.curitem].data;
				if (msg[dia_it.editend] != 0) dia_it.editend++;
				dia_it.editstart = dia_it.editend;
				newfirst = Dneweditbase();
				if (dia_it.firstch != newfirst)
				{
					dia_it.firstch = newfirst;
					Dstufftext(msg, &dia_it.dlgresaddr->list[dia_it.curitem].r);
				}
				Dhighlight(1);
				return(-1);
			case SPECIALKEYARROWD:
				if (dia_it.scrollcount <= 0) return(-1);
				scr = &dia_it.scroll[sc = dia_it.curscroll];
				if (scr->scrolllistlen <= 0) return(-1);
				if (scr->whichlow >= scr->scrolllistlen-1) return(-1);
				*itemHit = scr->scrollitem + 1;
				DiaSelectLine(*itemHit, scr->whichlow+1);
				if ((scr->flags&SCREPORT) == 0) return(-1);
				return(-2);
			case SPECIALKEYARROWU:
				if (dia_it.scrollcount <= 0) return(-1);
				scr = &dia_it.scroll[sc = dia_it.curscroll];
				if (scr->scrolllistlen <= 0) return(-1);
				if (scr->whichlow <= 0) return(-1);
				*itemHit = scr->scrollitem + 1;
				DiaSelectLine(*itemHit, scr->whichlow-1);
				if ((scr->flags&SCREPORT) == 0) return(-1);
				return(-2);
		}
	}

	/* tab to next edit text item */
	if (chr == TABKEY)
	{
		type = 0;
		for(i=dia_it.curitem+1; i<dia_it.dlgresaddr->items; i++)
		{
			type = dia_it.dlgresaddr->list[i].type;
			if (type == EDITTEXT) break;
		}
		if (type != EDITTEXT)
		{
			for(i=0; i<dia_it.curitem; i++)
			{
				type = dia_it.dlgresaddr->list[i].type;
				if (type == EDITTEXT) break;
			}
		}
		if (type != EDITTEXT) return(-1);
		Ddoneedit();
		dia_it.curitem = i;
		dia_it.editstart = 0;
		msg = (char *)dia_it.dlgresaddr->list[i].data;
		dia_it.editend = strlen(msg);
		dia_it.firstch = 0;
		Dhighlight(1);
		return(-1);
	}

	if (dia_it.curitem < 0 && dia_it.scrollcount > 0 &&
		(dia_it.scroll[dia_it.curscroll].flags&SCSELKEY) != 0)
	{
		/* use key to select line in scroll area */
		scr = &dia_it.scroll[sc=dia_it.curscroll];
		*itemHit = scr->scrollitem + 1;
		if (chr >= 'A' && chr <= 'Z') chr += 040;

		/* if it has been more than a second, reset the match string */
		if (time - lasttime > 60) matchpos = 0;
		lasttime = time;

		/* add this character to the match string */
		if (matchpos < MAXMATCH)
		{
			match[matchpos] = chr;
			matchpos++;
		}

		/* find that string */
		for(which = 0; which < scr->scrolllistlen; which++)
		{
			for(i=0; i<matchpos; i++)
			{
				n = scr->scrolllist[which][i];
				if (n >= 'A' && n <= 'Z') n += 040;
				if (match[i] != n) break;
			}
			if (i >= matchpos)
			{
				DiaSelectLine(*itemHit, which);
				break;
			}
		}
		if ((scr->flags&SCREPORT) == 0) return(-1);
		return(-2);
	}

	/* insert character into edit text */
	*itemHit = dia_it.curitem + 1;
	return(chr);
}

void DiaDoneDialog(void)
{
	INTBIG i, j, type;
	POPUPDATA *oldpd;

	/* free all the edit text and message buffers */
	for(i=0; i<dia_it.dlgresaddr->items; i++)
	{
		type = dia_it.dlgresaddr->list[i].type;
		if ((type&ITEMTYPE) == POPUP)
		{
			oldpd = (POPUPDATA *)dia_it.dlgresaddr->list[i].data;
			for(j=0; j<oldpd->count; j++) efree((char *)oldpd->namelist[j]);
			if (oldpd->count > 0) efree((char *)oldpd->namelist);
		}
		if ((type&ITEMTYPE) == MESSAGE || (type&ITEMTYPE) == EDITTEXT || (type&ITEMTYPE) == POPUP)
			efree((char *)dia_it.dlgresaddr->list[i].data);
	}

	/* free all items in the scroll area */
	for(i=0; i<dia_it.scrollcount; i++)
	{
		for(j=0; j<dia_it.scroll[i].scrolllistlen; j++)
			efree((char *)dia_it.scroll[i].scrolllist[j]);
		if (dia_it.scroll[i].scrolllistsize > 0) efree((char *)dia_it.scroll[i].scrolllist);
	}
	if (dia_it.onscreen != 0)
	{
		dia_it.onscreen = 0;
		Ddonedialogwindow();
		dia_active--;
	}

	dia_savepos--;
	if (dia_savepos <= 0) return;
	dia_it = dia_save[dia_savepos-1];
	Dredrawdialogwindow();
}

/*
 * Routine to set the text in item "item" to "msg"
 */
void DiaSetText(INTBIG item, char *msg)
{
	INTBIG type, oldcur, highlight, dim;
	INTBIG amt;
	char *save;
	RECTAREA r;

	highlight = 0;
	if (item < 0)
	{
		item = -item;
		highlight = 1;
	}
	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	type = dia_it.dlgresaddr->list[item].type;

	/* special case when renaming buttons */
	if ((type&ITEMTYPE) == BUTTON || (type&ITEMTYPE) == DEFBUTTON ||
		(type&ITEMTYPE) == CHECK || (type&ITEMTYPE) == RADIO)
	{
		r = dia_it.dlgresaddr->list[item].r;
		if ((type&INACTIVE) != 0) dim = 1; else dim = 0;
		Dintdrawrect(&r, ALLOFF);
		Ddrawitem(type, &r, msg, dim);
		if ((type&ITEMTYPE) == RADIO && dia_it.dlgresaddr->list[item].data != 0)
		{
			/* draw the circle in a selected radio button */
			r.right = r.left + 12;
			r.top = (r.top + r.bottom) / 2 - 6;
			r.bottom = r.top + 12;
			Dinsetrect(&r, 3);
			Ddrawdisc(&r);
		}
		if ((type&ITEMTYPE) == CHECK && dia_it.dlgresaddr->list[item].data != 0)
		{
			/* draw the "X" in a selected check box */
			r.right = r.left + 12;
			r.top = (r.top + r.bottom) / 2 - 6;
			r.bottom = r.top + 12;
			Ddrawline(r.left, r.top, r.right-1, r.bottom-1);
			Ddrawline(r.left, r.bottom-1, r.right-1, r.top);
		}
		return;
	}

	/* handle messages and edit text */
	oldcur = dia_it.curitem;   Ddoneedit();
	if ((type&ITEMTYPE) == MESSAGE || (type&ITEMTYPE) == EDITTEXT)
	{
		if ((type&ITEMTYPE) == MESSAGE)
			Dstuffmessage(msg, &dia_it.dlgresaddr->list[item].r, 0); else
				Dstufftext(msg, &dia_it.dlgresaddr->list[item].r);
		amt = strlen(msg)+1;
		save = (char *)emalloc(amt, el_tempcluster);
		if (save == 0) return;
		(void)strcpy(save, msg);
		efree((char *)dia_it.dlgresaddr->list[item].data);
		dia_it.dlgresaddr->list[item].data = (INTBIG)save;
		if ((type&ITEMTYPE) == EDITTEXT)
		{
			if (highlight != 0)
			{
				Ddoneedit();
				oldcur = item;
				dia_it.editstart = 0;
				dia_it.editend = strlen(msg);
				dia_it.firstch = 0;
			} else if (oldcur == item)
			{
				dia_it.editstart = dia_it.editend = strlen(msg);
			}
		}
	}
	dia_it.curitem = oldcur;
	Dhighlight(1);
}

/*
 * Routine to return the text in item "item"
 */
char *DiaGetText(INTBIG item)
{
	INTBIG type;
	POPUPDATA *pd;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return("");
	type = dia_it.dlgresaddr->list[item].type;
	if ((type&ITEMTYPE) == POPUP)
	{
		pd = (POPUPDATA *)dia_it.dlgresaddr->list[item].data;
		return(pd->namelist[pd->current]);
	}
	if ((type&ITEMTYPE) == MESSAGE || (type&ITEMTYPE) == EDITTEXT)
		return((char *)dia_it.dlgresaddr->list[item].data);
	return(0);
}

/*
 * Routine to set the value in item "item" to "value"
 */
void DiaSetControl(INTBIG item, INTBIG value)
{
	INTBIG type;
	RECTAREA r;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	type = dia_it.dlgresaddr->list[item].type;
	r = dia_it.dlgresaddr->list[item].r;
	if ((type&ITEMTYPE) == CHECK)
	{
		/* check box */
		r.right = r.left + 12;
		r.top = (r.top + r.bottom) / 2 - 6;
		r.bottom = r.top + 12;
		Dintdrawrect(&r, ALLOFF);
		Ddrawrectframe(&r, 1, 0);
		if (value != 0)
		{
			Ddrawline(r.left, r.top, r.right-1, r.bottom-1);
			Ddrawline(r.left, r.bottom-1, r.right-1, r.top);
		}
		dia_it.dlgresaddr->list[item].data = (INTBIG)value;
	} else if ((type&ITEMTYPE) == RADIO)
	{
		/* radio button */
		r.right = r.left + 12;
		r.top = (r.top + r.bottom) / 2 - 6;
		r.bottom = r.top + 12;
		Dintdrawrect(&r, ALLOFF);
		Ddrawcircle(&r, 0);
		if (value != 0)
		{
			Dinsetrect(&r, 3);
			Ddrawdisc(&r);
		}
		dia_it.dlgresaddr->list[item].data = (INTBIG)value;
	}
}

/*
 * Routine to return the value in item "item"
 */
INTBIG DiaGetControl(INTBIG item)
{
	INTBIG type;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return(0);
	type = dia_it.dlgresaddr->list[item].type;
	if ((type&ITEMTYPE) == CHECK || (type&ITEMTYPE) == RADIO)
		return(dia_it.dlgresaddr->list[item].data);
	return(0);
}

/*
 * Routine to check item "item" to make sure that there is
 * text in it.  If so, it returns true.  Otherwise it beeps and returns false.
 */
BOOLEAN DiaValidEntry(INTBIG item)
{
	char *msg;

	msg = DiaGetText(item);
	while (*msg == ' ') msg++;
	if (*msg != 0) return(TRUE);
	ttybeep(1);
	return(FALSE);
}

/*
 * Routine to dim item "item"
 */
void DiaDimItem(INTBIG item)
{
	char *msg;
	INTBIG type;
	RECTAREA r;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	if (item == dia_it.curitem) Ddoneedit();
	dia_it.dlgresaddr->list[item].type |= INACTIVE;
	type = dia_it.dlgresaddr->list[item].type;
	r = dia_it.dlgresaddr->list[item].r;
	msg = (char *)dia_it.dlgresaddr->list[item].data;
	if ((type&ITEMTYPE) == BUTTON || (type&ITEMTYPE) == DEFBUTTON ||
		(type&ITEMTYPE) == CHECK || (type&ITEMTYPE) == RADIO)
			msg = dia_it.dlgresaddr->list[item].msg;
	Ddrawitem(type, &r, msg, 1);
}

/*
 * Routine to un-dim item "item"
 */
void DiaUnDimItem(INTBIG item)
{
	char *msg;
	INTBIG type;
	RECTAREA r;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	dia_it.dlgresaddr->list[item].type &= ~INACTIVE;
	type = dia_it.dlgresaddr->list[item].type;
	r = dia_it.dlgresaddr->list[item].r;
	msg = (char *)dia_it.dlgresaddr->list[item].data;
	if ((type&ITEMTYPE) == BUTTON || (type&ITEMTYPE) == DEFBUTTON ||
		(type&ITEMTYPE) == CHECK || (type&ITEMTYPE) == RADIO)
			msg = dia_it.dlgresaddr->list[item].msg;
	Ddrawitem(type, &r, msg, 0);

	/* if undimming selected radio button, redraw disc */
	if ((type&ITEMTYPE) == RADIO && dia_it.dlgresaddr->list[item].data != 0)
	{
		r.right = r.left + 9;
		r.left += 3;
		r.top = (r.top + r.bottom) / 2 - 3;
		r.bottom = r.top + 6;
		Ddrawdisc(&r);
	}
}

/*
 * Routine to change item "item" to be a message rather
 * than editable text
 */
void DiaNoEditControl(INTBIG item)
{
	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	if (item == dia_it.curitem) Ddoneedit();
	dia_it.dlgresaddr->list[item].type = MESSAGE;
	Deditbox(&dia_it.dlgresaddr->list[item].r, 0, 0);
}

/*
 * Routine to change item "item" to be editable text rather
 * than a message
 */
void DiaEditControl(INTBIG item)
{
	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	dia_it.dlgresaddr->list[item].type = EDITTEXT;
	Deditbox(&dia_it.dlgresaddr->list[item].r, 1, 0);
}

void DiaOpaqueEdit(INTBIG item)
{
	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	if (dia_it.dlgresaddr->list[item].type != EDITTEXT) return;
	dia_it.opaqueitem = item;
}

/*
 * Routine to cause item "item" to report character hits
 */
void DiaCharacterEdit(INTBIG item)
{
}

/*
 * Routine to cause item "item" to be the default button
 */
void DiaDefaultButton(INTBIG item)
{
	INTBIG olddefault;
	INTBIG itemtype, dim;
	RECTAREA r;
	char *line;

	if (item == dia_it.defaultbutton) return;
	olddefault = dia_it.defaultbutton - 1;
	dia_it.defaultbutton = item;

	/* redraw the old item without highlighting */
	itemtype = dia_it.dlgresaddr->list[olddefault].type;
	r = dia_it.dlgresaddr->list[olddefault].r;
	Dinsetrect(&r, -4);  r.right++;
	Dintdrawrect(&r, ALLOFF);
	Dinsetrect(&r, 4);   r.right--;
	line = dia_it.dlgresaddr->list[olddefault].msg;
	if ((itemtype&INACTIVE) != 0) dim = 1; else dim = 0;
	Ddrawitem(itemtype, &r, line, dim);

	/* highlight the new default button */
	r = dia_it.dlgresaddr->list[item-1].r;
	Dhighlightrect(&r);
}

/*
 * Routine to change item "item" into a popup with "count" entries
 * in "names".
 */
void DiaSetPopup(INTBIG item, INTBIG count, char **names)
{
	POPUPDATA *pd;
	INTBIG i, type;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	type = dia_it.dlgresaddr->list[item].type;
	if ((type&ITEMTYPE) != POPUP) return;

	/* copy into a POPUPDATA structure */
	pd = (POPUPDATA *)dia_it.dlgresaddr->list[item].data;
	for(i=0; i<pd->count; i++) efree((char *)pd->namelist[i]);
	if (pd->count > 0) efree((char *)pd->namelist);
	pd->count = count;
	pd->current = 0;
	pd->namelist = (char **)emalloc(count * (sizeof (char *)), el_tempcluster);
	if (pd->namelist == 0) return;
	for(i=0; i<count; i++)
	{
		pd->namelist[i] = (char *)emalloc((strlen(names[i])+1) * (sizeof (char)),
			el_tempcluster);
		if (pd->namelist[i] == 0) return;
		(void)strcpy(pd->namelist[i], names[i]);
	}

	/* display the popup */
	Ddrawitem(POPUP, &dia_it.dlgresaddr->list[item].r, (char *)pd, 0);
}

/*
 * Routine to change popup item "item" so that the current entry is "entry".
 */
void DiaSetPopupEntry(INTBIG item, INTBIG entry)
{
	POPUPDATA *pd;
	INTBIG type;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	type = dia_it.dlgresaddr->list[item].type;
	if ((type&ITEMTYPE) != POPUP) return;
	pd = (POPUPDATA *)dia_it.dlgresaddr->list[item].data;
	if (entry < 0 || entry >= pd->count) return;
	pd->current = entry;

	Ddrawitem(POPUP, &dia_it.dlgresaddr->list[item].r, (char *)pd,
		type&INACTIVE);
}

/*
 * Routine to return the current item in popup menu item "item".
 */
INTBIG DiaGetPopupEntry(INTBIG item)
{
	POPUPDATA *pd;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return(0);
	if ((dia_it.dlgresaddr->list[item].type&ITEMTYPE) != POPUP) return(0);
	pd = (POPUPDATA *)dia_it.dlgresaddr->list[item].data;
	return(pd->current);
}

void DiaInitTextDialog(INTBIG item, BOOLEAN (*toplist)(char **), char *(*nextinlist)(void),
	void (*donelist)(void), INTBIG sortpos, INTBIG flags)
{
	DSCROLL *scr;
	INTBIG sc;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	for(sc=0; sc<dia_it.scrollcount; sc++) if (dia_it.scroll[sc].scrollitem == item) break;
	if (sc >= dia_it.scrollcount) return;
	scr = &dia_it.scroll[sc];

	/* save information about this scroll area */
	scr->flags = flags;
	if ((scr->flags&SCSMALLFONT) != 0)
	{
		scr->lineheight = dia_slineheight;
		scr->lineoffset = dia_slineoffset;
	} else
	{
		scr->lineheight = dia_lineheight;
		scr->lineoffset = dia_lineoffset;
	}

	/* compute size of actual area (not including scroll bars) */
	scr->userrect = dia_it.dlgresaddr->list[item].r;
	scr->userrect.right -= 14;
	if ((scr->flags&SCHORIZBAR) != 0) scr->userrect.bottom -= 14;
	scr->linesinfolder = (scr->userrect.bottom - scr->userrect.top) / scr->lineheight;

	/* draw sliders */
	Ddrawvertslider(sc);
	if ((scr->flags&SCHORIZBAR) != 0) Ddrawhorizslider(sc);

	/* load the text */
	scr->scrolllistlen = 0;
	DiaLoadTextDialog(item+1, toplist, nextinlist, donelist, sortpos);
}

void DiaLoadTextDialog(INTBIG item, BOOLEAN (*toplist)(char **), char *(*nextinlist)(void),
	void (*donelist)(void), INTBIG sortpos)
{
	INTBIG i, items, sc;
	char *next, **list, line[256];
	DSCROLL *scr;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	for(sc=0; sc<dia_it.scrollcount; sc++) if (dia_it.scroll[sc].scrollitem == item) break;
	if (sc >= dia_it.scrollcount) return;
	scr = &dia_it.scroll[sc];

	/* deallocate all former items in the list */
	for(i=0; i<scr->scrolllistlen; i++) efree((char *)scr->scrolllist[i]);
	scr->scrolllistlen = 0;
	scr->firstline = 0;

	/* count the number of items to be put in the text editor */
	line[0] = 0;
	next = line;
	(void)(*toplist)(&next);
	for(items=0; ; items++) if ((*nextinlist)() == 0) break;
	(*donelist)();

	/* allocate space for the strings */
	if (items > 0)
	{
		list = (char **)emalloc(items * (sizeof (char *)), el_tempcluster);
		if (list == 0) return;
	}

	/* get the list */
	line[0] = 0;
	next = line;
	(void)(*toplist)(&next);
	for(i=0; i<items; i++)
	{
		next = (*nextinlist)();
		if (next == 0) next = "???";
		list[i] = (char *)emalloc(strlen(next)+1, el_tempcluster);
		if (list[i] == 0) return;
		strcpy(list[i], next);
	}
	(*donelist)();

	/* sort the list */
	if (sortpos >= 0)
	{
		gra_dialogstringpos = sortpos;
		esort(list, items, sizeof (char *), gra_stringposascending);
	}

	/* stuff the list into the text editor */
	scr->whichlow = -1;
	Dinsetrect(&scr->userrect, 1);
	Dintdrawrect(&scr->userrect, ALLOFF);
	Dinsetrect(&scr->userrect, -1);
	for(i=0; i<items; i++) DiaStuffLine(item+1, list[i]);
	Dsetvscroll(sc);
	if (scr->scrolllistlen > 0) DiaSelectLine(item+1, 0);

	/* deallocate the list */
	if (items > 0)
	{
		for(i=0; i<items; i++) efree((char *)list[i]);
		efree((char *)(char *)list);
	}
}

/*
 * Routine to stuff line "line" at the end of the edit buffer.
 */
void DiaStuffLine(INTBIG item, char *line)
{
	char **newlist, *pt;
	INTBIG i, sc;
	DSCROLL *scr;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	for(sc=0; sc<dia_it.scrollcount; sc++) if (dia_it.scroll[sc].scrollitem == item) break;
	if (sc >= dia_it.scrollcount) return;
	scr = &dia_it.scroll[sc];

	if (scr->scrolllistlen >= scr->scrolllistsize)
	{
		newlist = (char **)emalloc((scr->scrolllistsize+10) * (sizeof (char *)),
			el_tempcluster);
		if (newlist == 0) return;
		for(i=0; i<scr->scrolllistlen; i++) newlist[i] = scr->scrolllist[i];
		if (scr->scrolllistsize != 0) efree((char *)scr->scrolllist);
		scr->scrolllist = newlist;
		scr->scrolllistsize += 10;
	}
	pt = (char *)emalloc(strlen(line)+1, el_tempcluster);
	if (pt == 0) return;
	(void)strcpy(pt, line);
	if (scr->scrolllistlen < scr->firstline+scr->linesinfolder)
		Ddrawmsg(sc, line, scr->scrolllistlen);
	scr->scrolllist[scr->scrolllistlen++] = pt;
}

/*
 * Routine to select line "line" of scroll item "item".
 */
void DiaSelectLine(INTBIG item, INTBIG l)
{
	INTBIG lines[1];

	lines[0] = l;
	DiaSelectLines(item, 1, lines);
}

/*
 * Routine to select "count" lines in "lines" of scroll item "item".
 */
void DiaSelectLines(INTBIG item, INTBIG count, INTBIG *lines)
{
	INTBIG n, sc, whichlow, whichhigh, i;
	DSCROLL *scr;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	for(sc=0; sc<dia_it.scrollcount; sc++) if (dia_it.scroll[sc].scrollitem == item) break;
	if (sc >= dia_it.scrollcount) return;
	scr = &dia_it.scroll[sc];

	if (count == 0)
	{
		Dinvertentry(sc, 0);
		scr->whichlow = -1;
		return;
	}

	whichlow = whichhigh = lines[0];
	for(i=1; i<count; i++)
	{
		if (lines[i] < whichlow) whichlow = lines[i];
		if (lines[i] > whichhigh) whichhigh = lines[i];
	}

	/* if the new line is visible, simply shift the highlighting */
	if (whichhigh-scr->firstline >= 0 && whichlow-scr->firstline < scr->linesinfolder)
	{
		Dinvertentry(sc, 0);
	} else
	{
		/* must shift the buffer */
		if (whichlow < 0) Dinvertentry(sc, 0); else
		{
			n = whichlow - scr->linesinfolder/2;
			if (n > scr->scrolllistlen-scr->linesinfolder)
				n = scr->scrolllistlen-scr->linesinfolder;
			if (n < 0) n = 0;
			scr->firstline = n;
			Dredrawscroll(sc);
		}
	}
	scr->whichlow = whichlow;
	scr->whichhigh = whichhigh;
	Dinvertentry(sc, 1);
	Dsetvscroll(sc);
}

/*
 * Returns the currently selected line in the scroll list "item".
 */
INTBIG DiaGetCurLine(INTBIG item)
{
	INTBIG sc;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return(-1);
	for(sc=0; sc<dia_it.scrollcount; sc++)
		if (dia_it.scroll[sc].scrollitem == item) return(dia_it.scroll[sc].whichlow);
	return(-1);
}

/*
 * Returns the currently selected lines in the scroll list "item".  The returned
 * array is terminated with -1.
 */
INTBIG *DiaGetCurLines(INTBIG item)
{
	static INTBIG sellist[MAXSCROLLMULTISELECT];
	INTBIG sc, i, j;

	item--;
	sellist[0] = -1;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return(sellist);
	for(sc=0; sc<dia_it.scrollcount; sc++)
	{
		if (dia_it.scroll[sc].scrollitem == item)
		{
			j = 0;
			for(i=dia_it.scroll[sc].whichlow; i <= dia_it.scroll[sc].whichhigh; i++)
				sellist[j++] = i;
			sellist[j] = -1;
		}
	}
	return(sellist);
}

char *DiaGetScrollLine(INTBIG item, INTBIG l)
{
	INTBIG sc;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return("");
	for(sc=0; sc<dia_it.scrollcount; sc++)
		if (dia_it.scroll[sc].scrollitem == item)
	{
		if (l < 0 || l >= dia_it.scroll[sc].scrolllistlen) break;
		return(dia_it.scroll[sc].scrolllist[l]);
	}
	return("");
}

void DiaSetScrollLine(INTBIG item, INTBIG l, char *msg)
{
	char *ins;
	INTBIG sc;
	DSCROLL *scr;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	for(sc=0; sc<dia_it.scrollcount; sc++) if (dia_it.scroll[sc].scrollitem == item) break;
	if (sc >= dia_it.scrollcount) return;
	scr = &dia_it.scroll[sc];

	if (l >= scr->scrolllistlen) DiaStuffLine(item+1, msg); else
	{
		ins = (char *)emalloc(strlen(msg)+1, el_tempcluster);
		if (ins == 0) return;
		(void)strcpy(ins, msg);
		efree((char *)scr->scrolllist[l]);
		scr->scrolllist[l] = ins;
	}
	Dredrawscroll(sc);
	Dinvertentry(sc, 1);
	Dsetvscroll(sc);
}

void DiaItemRect(INTBIG item, RECTAREA *rect)
{
	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	*rect = dia_it.dlgresaddr->list[item].r;
}

void DiaPercent(INTBIG item, INTBIG p)
{
	RECTAREA r;
	INTBIG type;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	type = dia_it.dlgresaddr->list[item].type;
	if ((type&ITEMTYPE) != PROGRESS) return;
	r = dia_it.dlgresaddr->list[item].r;
	if (p == 0)
	{
		Dintdrawrect(&r, ALLOFF);
		Ddrawrectframe(&r, 1, 0);
		return;
	}
	r.left++;
	r.right = r.left + (r.right-1-r.left)*p/100;
	r.top++;   r.bottom--;
	Dgrayrect(&r);
	flushscreen();
}

void DiaRedispRoutine(INTBIG item, void (*routine)(RECTAREA*))
{
	INTBIG type;

	item--;
	if (item < 0 || item >= dia_it.dlgresaddr->items) return;
	type = dia_it.dlgresaddr->list[item].type;
	if ((type&ITEMTYPE) != USERDRAWN) return;
	dia_it.dlgresaddr->list[item].data = (INTBIG)routine;
}

void DiaAllowUserDoubleClick(void)
{
	dia_it.userdoubleclick = 1;
}

void DiaDrawRect(INTBIG item, RECTAREA *rect, INTBIG r, INTBIG g, INTBIG b)
{
	INTBIG colorindex;

	colorindex = Dgetcolorindex(r, g, b);
	Dintdrawrect(rect, colorindex);
}

void DiaInvertRect(INTBIG item, RECTAREA *r)
{
	Dinvertrect(r);
}

void DiaFrameRect(INTBIG item, RECTAREA *r)
{
	Dintdrawrect(r, ALLOFF);
	Ddrawrectframe(r, 1, 0);
}

void DiaDrawLine(INTBIG item, INTBIG xf, INTBIG yf, INTBIG xt, INTBIG yt, INTBIG mode)
{
	INTBIG fx, fy, tx, ty;

	fx = xf + dia_xoffset;   fy = dia_yrev - yf;
	tx = xt + dia_xoffset;   ty = dia_yrev - yt;
	if (clipline(&fx, &fy, &tx, &ty, dia_window.uselx, dia_window.usehx,
		dia_window.usely, dia_window.usehy)) return;

	switch (mode)
	{
		case DLMODEON:
			screendrawline(&dia_window, fx, fy, tx, ty, &us_menufigs, 0);
			break;
		case DLMODEOFF:
			us_menufigs.col = 0;
			screendrawline(&dia_window, fx, fy, tx, ty, &us_menufigs, 0);
			us_menufigs.col = el_colmengly;
			break;
		case DLMODEINVERT:
			screeninvertline(&dia_window, fx, fy, tx, ty);
			break;
	}
}

void DiaFillPoly(INTBIG item, INTBIG *x, INTBIG *y, INTBIG count, INTBIG r, INTBIG g, INTBIG b)
{
	us_menufigs.col = Dgetcolorindex(r, g, b);
	Ddrawpolygon(x, y, count, 1);
	us_menufigs.col = el_colmengly;
}

void DiaPutText(INTBIG item, char *msg, INTBIG x, INTBIG y)
{
	gra_settextsize(&dia_window, dia_it.usertextsize, 0, 0, 0, 0);
	screendrawtext(&dia_window, x+dia_xoffset, dia_yrev-y-dia_slineheight,
		msg, &us_menutext);
}

void DiaSetTextSize(INTBIG size)
{
	dia_it.usertextsize = size;
}

void DiaGetTextInfo(char *msg, INTBIG *wid, INTBIG *hei)
{
	INTBIG w, h;

	gra_settextsize(&dia_window, dia_it.usertextsize, 0, 0, 0, 0);
	screengettextsize(&dia_window, msg, &w, &h);
	*wid = w;   *hei = h;
}

void DiaTrackCursor(void (*eachdown)(INTBIG x, INTBIG y))
{
	gra_diaeachdown = eachdown;
	trackcursor(FALSE, us_nullup, us_nullvoid, gra_diaeachdownhandler,
		us_nullchar, us_nullvoid, TRACKNORMAL);
}

BOOLEAN gra_diaeachdownhandler(INTBIG ox, INTBIG oy)
{
	INTBIG x, y;

	DiaGetMouse(&x, &y);
	(void)((*gra_diaeachdown)(x, y));
	return(FALSE);
}

void DiaGetMouse(INTBIG *x, INTBIG *y)
{
	INTBIG lx, ly;

	readtablet(x, y);
	lx = *x;   ly = *y;
	Dcorrectxy(&lx, &ly);
	*x = lx;   *y = ly;
}

BOOLEAN DiaNullDlogList(char **c) { return(FALSE); }

char *DiaNullDlogItem(void) { return(0); }

void DiaNullDlogDone(void) {}

/****************************** ATHENA DIALOG SUPPORT ******************************/

BOOLEAN Dbuttondown(INTBIG x, INTBIG y)
{
	INTBIG in;

	Dcorrectxy(&x, &y);
	if (x < dia_rect.left || x > dia_rect.right || y < dia_rect.top || y > dia_rect.bottom)
		in = 0; else
			in = 1;
	if (in != dia_wasin)
		Dinvertrect(&dia_rect);
	dia_wasin = in;
	return(FALSE);
}

BOOLEAN Dtrackpopup(INTBIG x, INTBIG y)
{
	INTBIG which;
	RECTAREA r;

	Dcorrectxy(&x, &y);
	if (x < dia_rect.left || x > dia_rect.right || y < dia_rect.top || y > dia_rect.bottom)
		which = -1; else
	{
		which = (y - dia_rect.top - 1) / dia_lineheight;
		if (which >= dia_cnt-dia_sta) which = dia_cnt-dia_sta-1;

	}
	if (which != dia_wasin)
	{
		r.left = dia_rect.left + 1;
		r.right = dia_rect.right - 1;
		if (dia_wasin >= 0)
		{
			r.top = dia_rect.top + (dia_wasin * dia_lineheight) + 1;
			r.bottom = r.top + dia_lineheight;
			Dinvertrect(&r);
		}

		/* special case if in the "up" arrow */
		if (which == 0 && dia_sta != 0)
		{
			dia_sta--;   dia_cnt--;
			Ddrawpopupentries();
			dia_wasin = -1;
			return(FALSE);
		}

		/* special case if in the "down" arrow */
		if (which == dia_cnt-dia_sta-1 && dia_cnt != dia_pd->count)
		{
			dia_sta++;   dia_cnt++;
			Ddrawpopupentries();
			dia_wasin = -1;
			return(FALSE);
		}

		if (which >= 0)
		{
			r.top = dia_rect.top + (which * dia_lineheight) + 1;
			r.bottom = r.top + dia_lineheight;
			Dinvertrect(&r);
		}

		dia_wasin = which;
	}
	return(FALSE);
}

BOOLEAN Dcheckdown(INTBIG x, INTBIG y)
{
	INTBIG in;
	RECTAREA r;

	Dcorrectxy(&x, &y);
	if (x < dia_rect.left || x > dia_rect.right || y < dia_rect.top || y > dia_rect.bottom)
		in = 0; else
			in = 1;
	if (in != dia_wasin)
	{
		r = dia_rect;
		r.right = r.left + 11;   r.left++;
		r.top = (r.top + r.bottom) / 2 - 5;
		r.bottom = r.top + 10;
		if (in != 0) Ddrawrectframe(&r, 1, 0); else
		{
			Ddrawrectframe(&r, 0, 0);
			if (dia_ddata != 0)
			{
				Ddrawline(r.left, r.top, r.right-1, r.bottom-1);
				Ddrawline(r.left, r.bottom-1, r.right-1, r.top);
			}
		}
	}
	dia_wasin = in;
	return(FALSE);
}

BOOLEAN Deditdown(INTBIG x, INTBIG y)
{
	INTBIG l, h, pos, wid, len, prevpos, basepos;
	char *msg;

	Dcorrectxy(&x, &y);

	/* shift the text if the cursor moves outside the field */
	if (dia_rect.bottom-dia_rect.top < dia_lineheight*2)
	{
		if (y < dia_rect.top || y > dia_rect.bottom) return(FALSE);

		/* scroll horizontally if there is only 1 line in the edit field */
		if (x < dia_rect.left && dia_it.firstch > 0)
		{
			Dhighlight(0);
			dia_it.firstch--;
			Dstufftext(dia_msg, &dia_rect);
			Dhighlight(1);
			gotosleep(10);
			return(FALSE);
		}
		if (x > dia_rect.right && dia_it.firstch < dia_it.editend-1)
		{
			Dhighlight(0);
			dia_it.firstch++;
			Dstufftext(dia_msg, &dia_rect);
			Dhighlight(1);
			gotosleep(10);
			return(FALSE);
		}
	} else
	{
		if (x < dia_rect.left || x > dia_rect.right) return(FALSE);

		/* scroll vertically if there are multiple lines in the field */
		if (y < dia_rect.top && dia_it.firstch > 0)
		{
			msg = dia_msg;
			prevpos = 0;
			basepos = 0;
			while (*msg != 0)
			{
				for(len = strlen(msg); len>0; len--)
				{
					wid = Dgettextsize(msg, len);
					if (wid < dia_rect.right-dia_rect.left-2) break;
				}
				if (len == 0) break;
				basepos += len;
				if (basepos == dia_it.firstch) break;
				prevpos += len;
				msg += len;
			}
			Dhighlight(0);
			dia_it.firstch = prevpos;
			Dstufftext(dia_msg, &dia_rect);
			Dhighlight(1);
			gotosleep(30);
			return(FALSE);
		}
		if (y > dia_rect.bottom && dia_it.firstch < dia_it.editend-1)
		{
			msg = &dia_msg[dia_it.firstch];
			for(len = strlen(msg); len>0; len--)
			{
				wid = Dgettextsize(msg, len);
				if (wid < dia_rect.right-dia_rect.left-2) break;
			}
			if (len == strlen(msg)) return(FALSE);
			Dhighlight(0);
			dia_it.firstch += len;
			Dstufftext(dia_msg, &dia_rect);
			Dhighlight(1);
			gotosleep(30);
			return(FALSE);
		}
	}

	pos = Dgeteditpos(&dia_rect, x, y, dia_msg);
	l = dia_lbase;   h = dia_hbase;
	if (pos > h) h = pos;
	if (pos < l) l = pos;
	if (l != dia_it.editstart || h != dia_it.editend)
	{
		Dhighlight(0);
		dia_it.editstart = l;   dia_it.editend = h;
		Dhighlight(1);
	}
	return(FALSE);
}

BOOLEAN Ddownarrow(INTBIG x, INTBIG y)
{
	short sc;
	DSCROLL *scr;
	UINTBIG thisdiatime;

	/* limit the speed of the button */
	thisdiatime = ticktime();
	if (thisdiatime - dia_lastdiatime < MINSCROLLTICKS) return(FALSE);
	dia_lastdiatime = thisdiatime;

	scr = &dia_it.scroll[sc=dia_it.curscroll];
	if (scr->scrolllistlen-scr->firstline <= scr->linesinfolder) return(TRUE);
	scr->firstline++;
	Dredrawscroll(sc);
	Dinvertentry(sc, 1);
	Dsetvscroll(sc);
	return(FALSE);
}

BOOLEAN Duparrow(INTBIG x, INTBIG y)
{
	INTBIG sc;
	DSCROLL *scr;
	UINTBIG thisdiatime;

	/* limit the speed of the button */
	thisdiatime = ticktime();
	if (thisdiatime - dia_lastdiatime < MINSCROLLTICKS) return(FALSE);
	dia_lastdiatime = thisdiatime;

	scr = &dia_it.scroll[sc=dia_it.curscroll];
	if (scr->firstline <= 0) return(TRUE);
	scr->firstline--;
	Dredrawscroll(sc);
	Dinvertentry(sc, 1);
	Dsetvscroll(sc);
	return(FALSE);
}

BOOLEAN Ddownpage(INTBIG x, INTBIG y)
{
	INTBIG sc;
	DSCROLL *scr;
	UINTBIG thisdiatime;

	/* limit the speed of the button */
	thisdiatime = ticktime();
	if (thisdiatime - dia_lastdiatime < MINPAGETICKS) return(FALSE);
	dia_lastdiatime = thisdiatime;

	scr = &dia_it.scroll[sc=dia_it.curscroll];
	Dcorrectxy(&x, &y);
	if (y <= scr->vthumbpos+THUMBSIZE/2 || y > scr->userrect.bottom-16) return(TRUE);
	if (scr->scrolllistlen-scr->firstline <= scr->linesinfolder) return(TRUE);
	scr->firstline += scr->linesinfolder-1;
	if (scr->scrolllistlen-scr->firstline <= scr->linesinfolder)
		scr->firstline = scr->scrolllistlen-scr->linesinfolder;
	Dredrawscroll(sc);
	Dinvertentry(sc, 1);
	Dsetvscroll(sc);
	return(FALSE);
}

BOOLEAN Duppage(INTBIG x, INTBIG y)
{
	INTBIG sc;
	DSCROLL *scr;
	UINTBIG thisdiatime;

	/* limit the speed of the button */
	thisdiatime = ticktime();
	if (thisdiatime - dia_lastdiatime < MINPAGETICKS) return(FALSE);
	dia_lastdiatime = thisdiatime;

	scr = &dia_it.scroll[sc=dia_it.curscroll];
	Dcorrectxy(&x, &y);
	if (y >= scr->vthumbpos-THUMBSIZE/2 || y < scr->userrect.top+16) return(TRUE);
	if (scr->firstline <= 0) return(TRUE);
	scr->firstline -= scr->linesinfolder-1;
	if (scr->firstline < 0) scr->firstline = 0;
	Dredrawscroll(sc);
	Dinvertentry(sc, 1);
	Dsetvscroll(sc);
	return(FALSE);
}

BOOLEAN Dvscroll(INTBIG x, INTBIG y)
{
	INTBIG l;
	DSCROLL *scr;

	scr = &dia_it.scroll[dia_it.curscroll];
	Dcorrectxy(&x, &y);
	l = scr->vthumbpos;
	scr->vthumbpos = y + dia_offset;
	if (scr->vthumbpos < scr->userrect.top+16+THUMBSIZE/2)
		scr->vthumbpos = scr->userrect.top+16+THUMBSIZE/2;
	if (scr->vthumbpos > scr->userrect.bottom-16-THUMBSIZE/2)
		scr->vthumbpos = scr->userrect.bottom-16-THUMBSIZE/2;
	if (scr->vthumbpos == l) return(FALSE);
	dia_rect.top = l - THUMBSIZE/2;
	dia_rect.bottom = l + THUMBSIZE/2;
	Dinvertrectframe(&dia_rect);
	dia_rect.top = scr->vthumbpos - THUMBSIZE/2;
	dia_rect.bottom = scr->vthumbpos + THUMBSIZE/2;
	Dinvertrectframe(&dia_rect);
	return(FALSE);
}

BOOLEAN Drightarrow(INTBIG x, INTBIG y)
{
	INTBIG sc;
	DSCROLL *scr;
	UINTBIG thisdiatime;

	/* limit the speed of the button */
	thisdiatime = ticktime();
	if (thisdiatime - dia_lastdiatime < MINSCROLLTICKS) return(FALSE);
	dia_lastdiatime = thisdiatime;

	scr = &dia_it.scroll[sc=dia_it.curscroll];
	if (scr->horizfactor >= 100) return(TRUE);
	scr->horizfactor++;
	Dredrawscroll(sc);
	Dinvertentry(sc, 1);
	Dsethscroll(sc);
	return(FALSE);
}

BOOLEAN Dleftarrow(INTBIG x, INTBIG y)
{
	INTBIG sc;
	DSCROLL *scr;
	UINTBIG thisdiatime;

	/* limit the speed of the button */
	thisdiatime = ticktime();
	if (thisdiatime - dia_lastdiatime < MINSCROLLTICKS) return(FALSE);
	dia_lastdiatime = thisdiatime;

	scr = &dia_it.scroll[sc=dia_it.curscroll];
	if (scr->horizfactor <= 0) return(TRUE);
	scr->horizfactor--;
	Dredrawscroll(sc);
	Dinvertentry(sc, 1);
	Dsethscroll(sc);
	return(FALSE);
}

BOOLEAN Drightpage(INTBIG x, INTBIG y)
{
	INTBIG sc;
	DSCROLL *scr;
	UINTBIG thisdiatime;

	/* limit the speed of the button */
	thisdiatime = ticktime();
	if (thisdiatime - dia_lastdiatime < MINPAGETICKS) return(FALSE);
	dia_lastdiatime = thisdiatime;

	scr = &dia_it.scroll[sc=dia_it.curscroll];
	Dcorrectxy(&x, &y);
	if (x <= scr->hthumbpos+THUMBSIZE/2 || x > scr->userrect.right-16) return(TRUE);
	if (scr->horizfactor >= 100) return(TRUE);
	scr->horizfactor += 10;
	if (scr->horizfactor >= 100) scr->horizfactor = 100;
	Dredrawscroll(sc);
	Dinvertentry(sc, 1);
	Dsethscroll(sc);
	return(FALSE);
}

BOOLEAN Dleftpage(INTBIG x, INTBIG y)
{
	INTBIG sc;
	DSCROLL *scr;
	UINTBIG thisdiatime;

	/* limit the speed of the button */
	thisdiatime = ticktime();
	if (thisdiatime - dia_lastdiatime < MINPAGETICKS) return(FALSE);
	dia_lastdiatime = thisdiatime;

	scr = &dia_it.scroll[sc=dia_it.curscroll];
	Dcorrectxy(&x, &y);
	if (x >= scr->hthumbpos-THUMBSIZE/2 || x < scr->userrect.left+16) return(TRUE);
	if (scr->horizfactor <= 0) return(TRUE);
	scr->horizfactor -= 10;
	if (scr->horizfactor <= 0) scr->horizfactor = 0;
	Dredrawscroll(sc);
	Dinvertentry(sc, 1);
	Dsethscroll(sc);
	return(FALSE);
}

BOOLEAN Dhscroll(INTBIG x, INTBIG y)
{
	INTBIG l;
	DSCROLL *scr;

	scr = &dia_it.scroll[dia_it.curscroll];
	Dcorrectxy(&x, &y);
	l = scr->hthumbpos;
	scr->hthumbpos = x + dia_offset;
	if (scr->hthumbpos < scr->userrect.left+16+THUMBSIZE/2)
		scr->hthumbpos = scr->userrect.left+16+THUMBSIZE/2;
	if (scr->hthumbpos > scr->userrect.right-16-THUMBSIZE/2)
		scr->hthumbpos = scr->userrect.right-16-THUMBSIZE/2;
	if (scr->hthumbpos == l) return(FALSE);
	dia_rect.left = l - THUMBSIZE/2;
	dia_rect.right = l + THUMBSIZE/2;
	Dinvertrectframe(&dia_rect);
	dia_rect.left = scr->hthumbpos - THUMBSIZE/2;
	dia_rect.right = scr->hthumbpos + THUMBSIZE/2;
	Dinvertrectframe(&dia_rect);
	return(FALSE);
}

void Dredrawscroll(INTBIG sc)
{
	RECTAREA r;
	INTBIG i;
	DSCROLL *scr;

	scr = &dia_it.scroll[sc];
	r = scr->userrect;
	r.left++;        r.right--;
	r.top++;         r.bottom--;
	Dintdrawrect(&r, ALLOFF);
	for(i=scr->firstline; i<scr->scrolllistlen; i++)
	{
		if (i-scr->firstline >= scr->linesinfolder) break;
		Ddrawmsg(sc, scr->scrolllist[i], i-scr->firstline);
	}
}

/*
 * Routine to set the vertical scroll bar
 */
void Dsetvscroll(INTBIG sc)
{
	RECTAREA r;
	INTBIG f;
	DSCROLL *scr;

	/* first redraw the border */
	Ddrawvertslider(sc);

	/* get the area of the slider without arrows */
	scr = &dia_it.scroll[sc];
	r = scr->userrect;
	r.top += 16;
	r.bottom -= 16;
	r.left = r.right;
	r.right += 13;

	/* if there is nothing to scroll, clear this area */
	if (scr->scrolllistlen <= scr->linesinfolder)
	{
		Dintdrawrect(&r, ALLOFF);
		return;
	}

	/* gray the scroll area */
	Dgrayrect(&r);

	/* compute position of vertical thumb area */
	f = scr->firstline;   f *= r.bottom - r.top-THUMBSIZE;
	f /= scr->scrolllistlen - scr->linesinfolder;
	scr->vthumbpos = r.top + THUMBSIZE/2 + f;
	if (scr->vthumbpos > r.bottom-THUMBSIZE/2) scr->vthumbpos = r.bottom-THUMBSIZE/2;

	/* draw the thumb */
	r.top = scr->vthumbpos - THUMBSIZE/2;
	r.bottom = scr->vthumbpos + THUMBSIZE/2;
	Dintdrawrect(&r, ALLOFF);
	Ddrawrectframe(&r, 1, 0);
}

/*
 * Routine to set the horizontal scroll bar
 */
void Dsethscroll(INTBIG sc)
{
	RECTAREA r;
	INTBIG f;
	DSCROLL *scr;

	/* get the area of the slider without arrows */
	scr = &dia_it.scroll[sc];
	r = scr->userrect;
	r.left += 16;
	r.right -= 16;
	r.top = r.bottom;
	r.bottom += 13;

	/* gray the scroll area */
	Dgrayrect(&r);

	/* compute position of vertical thumb area */
	f = scr->horizfactor;   f *= (INTBIG)(r.right-r.left-THUMBSIZE);   f /= 100;
	scr->hthumbpos = r.left + THUMBSIZE/2 + f;
	if (scr->hthumbpos > r.right-THUMBSIZE/2) scr->hthumbpos = r.right-THUMBSIZE/2;

	/* draw the thumb */
	r.left = scr->hthumbpos - THUMBSIZE/2;
	r.right = scr->hthumbpos + THUMBSIZE/2;
	Dintdrawrect(&r, ALLOFF);
	Ddrawrectframe(&r, 1, 0);
}

void Dinvertentry(INTBIG sc, INTBIG on)
{
	RECTAREA r;
	REGISTER INTBIG which;
	DSCROLL *scr;

	scr = &dia_it.scroll[sc];
	if (scr->whichlow < 0) return;
	for(which = scr->whichlow; which <= scr->whichhigh; which++)
	{
		if (which-scr->firstline >= 0 && which-scr->firstline < scr->linesinfolder)
		{
			r.left = scr->userrect.left+1;
			r.right = scr->userrect.right-1;
			r.top = scr->userrect.top + scr->lineheight*(which-scr->firstline)+1;
			r.bottom = r.top + scr->lineheight;
			if (r.bottom >= scr->userrect.bottom) r.bottom = scr->userrect.bottom-1;
			Dinvertrect(&r);
		}
	}
}

/*
 * routine to determine which item falls under the coordinates (x, y)
 */
INTBIG Dwhichitem(INTBIG x, INTBIG y)
{
	INTBIG i;
	RECTAREA r;

	for(i=0; i<dia_it.dlgresaddr->items; i++)
	{
		r = dia_it.dlgresaddr->list[i].r;
		if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) return(i+1);
	}
	return(0);
}

void Dinsertstr(char *insmsg)
{
	INTBIG i, j, newcurspos;
	char *oldmsg, *newmsg, *pt;

	if (dia_it.curitem < 0) return;
	Dhighlight(0);
	oldmsg = (char *)dia_it.dlgresaddr->list[dia_it.curitem].data;

	/* allocate space for the new message and fill it */
	newmsg = (char *)emalloc(strlen(oldmsg)+strlen(insmsg)+dia_it.editend-dia_it.editstart+1,
		el_tempcluster);
	if (newmsg == 0) return;
	j = 0;
	for(i=0; i<dia_it.editstart; i++) newmsg[j++] = oldmsg[i];
	for(i=0; insmsg[i] != 0; i++) newmsg[j++] = insmsg[i];
	newcurspos = j;
	for(i=dia_it.editend; oldmsg[i] != 0; i++) newmsg[j++] = oldmsg[i];
	newmsg[j] = 0;
	dia_it.editstart = dia_it.editend = newcurspos;

	/* replace the message */
	efree((char *)dia_it.dlgresaddr->list[dia_it.curitem].data);
	dia_it.dlgresaddr->list[dia_it.curitem].data = (INTBIG)newmsg;

	/* make sure the cursor is visible */
	dia_it.firstch = Dneweditbase();
	if (dia_it.opaqueitem == dia_it.curitem)
	{
		(void)allocstring(&oldmsg, newmsg, el_tempcluster);
		for(pt = oldmsg; *pt != 0; pt++)
			*pt = '*';
		Dstufftext(oldmsg, &dia_it.dlgresaddr->list[dia_it.curitem].r);
		efree(oldmsg);
	} else
	{
		Dstufftext(newmsg, &dia_it.dlgresaddr->list[dia_it.curitem].r);
	}

	/* set new highlighting */
	Dhighlight(1);
}

INTBIG Dneweditbase(void)
{
	INTBIG wid, prevpos, basepos, y, firstoff, len, newfirst;
	char *newmsg, *msg;
	RECTAREA r;

	newfirst = dia_it.firstch;
	newmsg = (char *)dia_it.dlgresaddr->list[dia_it.curitem].data;
	r = dia_it.dlgresaddr->list[dia_it.curitem].r;
	if (r.bottom-r.top < dia_lineheight*2)
	{
		/* if there is only 1 line in the edit field, shift horizontally */
		if (dia_it.editend < newfirst)
		{
			newfirst = dia_it.editend-1;
			if (newfirst < 0) newfirst = 0;
		}
		while (dia_it.editend > newfirst)
		{
			wid = Dgettextsize(&newmsg[newfirst], dia_it.editend-newfirst);
			if (wid <= r.right-r.left-2) break;
			newfirst++;
		}
	} else
	{
		/* multiple lines in the edit field, shift vertically */
		while (dia_it.editend < newfirst)
		{
			msg = newmsg;
			prevpos = 0;
			basepos = 0;
			while (*msg != 0)
			{
				for(len = strlen(msg); len>0; len--)
				{
					wid = Dgettextsize(msg, len);
					if (wid < r.right-r.left-2) break;
				}
				if (len == 0) break;
				basepos += len;
				if (basepos == newfirst) break;
				prevpos += len;
				msg += len;
			}
			newfirst = prevpos;
		}
		if (dia_it.editend > newfirst)
		{
			y = r.top + dia_lineheight;
			msg = &newmsg[newfirst];
			firstoff = 0;
			basepos = 0;
			while (*msg != 0)
			{
				for(len = strlen(msg); len>0; len--)
				{
					wid = Dgettextsize(msg, len);
					if (wid < r.right-r.left-2) break;
				}
				if (len == 0) break;
				if (dia_it.editend <= newfirst + basepos + len) break;
				if (firstoff == 0) firstoff = len;
				msg += len;
				basepos += len;
				y += dia_lineheight;
				if (y > r.bottom) { newfirst += firstoff;   break; }
			}
		}
	}
	return(newfirst);
}

void Dhighlight(INTBIG on)
{
	INTBIG i, sx, ex, sline, eline, line, dummy, es, ee;
	char *msg;
	RECTAREA r, itemrect;

	if (dia_it.curitem < 0) return;
	itemrect = dia_it.dlgresaddr->list[dia_it.curitem].r;
	msg = (char *)dia_it.dlgresaddr->list[dia_it.curitem].data;
	es = dia_it.editstart;   ee = dia_it.editend;
	if (es > ee) { i = es;   es = ee;   ee = i; }
	Dtextlocation(msg, es, &itemrect, &sx, &sline);
	Dtextlocation(msg, ee, &itemrect, &ex, &eline);
	for(i=sline; i<=eline; i++)
	{
		r.top = itemrect.top + i * dia_lineheight;
		if (r.top >= itemrect.bottom) break;
		r.bottom = r.top + dia_lineheight;
		if (r.bottom > itemrect.bottom) r.bottom = itemrect.bottom;

		r.left = itemrect.left;
		if (i == sline)
		{
			Dtextlocation(msg, es+1, &itemrect, &dummy, &line);
			if (line != sline && sline != eline) continue;
			r.left += sx;
		}
		r.right = itemrect.right;
		if (i == eline)
		{
			if (ex == 0 && sline != eline) continue;
			r.right = itemrect.left + ex + 1;
		}

		Dinvertrect(&r);
	}
}

/*
 * routine to determine where the cursor is in the edit item "r" given character
 * "len" of "msg".  The horizontal offset is placed in "wid" and the line number
 * in "line".
 */
void Dtextlocation(char *msg, INTBIG position, RECTAREA *r, INTBIG *x, INTBIG *line)
{
	INTBIG basepos, len, wid;

	/* if beyond the end, any hit is the end */
	*line = *x = 0;
	if (dia_it.firstch >= strlen(msg)) return;

	/* set initial message and offset in corner of edit box */
	msg = &msg[dia_it.firstch];
	basepos = dia_it.firstch;
	while (*msg != 0)
	{
		for(len = strlen(msg); len>0; len--)
		{
			wid = Dgettextsize(msg, len);
			if (wid < r->right-r->left-2) break;
		}
		if (len <= 0) { *x = 0;   break; }

		if (position - basepos <= len)
		{
			*x = Dgettextsize(msg, position - basepos);
			break;
		}
		msg += len;
		basepos += len;
		(*line)++;
	}
}

INTBIG Dgeteditpos(RECTAREA *r, INTBIG x, INTBIG y, char *msg)
{
	INTBIG pos, i, basepos, lastpos, len, wid;

	/* if beyond the end, any hit is the end */
	if (dia_it.firstch > strlen(msg)) return(strlen(msg));

	/* set initial message and offset in corner of edit box */
	msg = &msg[dia_it.firstch];
	basepos = dia_it.firstch;
	while (*msg != 0)
	{
		for(len = strlen(msg); len>0; len--)
		{
			wid = Dgettextsize(msg, len);
			if (wid < r->right-r->left-2) break;
		}
		if (len <= 0) break;

		if (y > r->top && y <= r->top+dia_lineheight)
		{
			lastpos = 0;
			for(i=0; i<len; i++)
			{
				pos = Dgettextsize(msg, i+1);
				if (r->left+(lastpos+pos)/2 > x) break;
				lastpos = pos;
			}
			return(basepos+i);
		}

		msg += len;
		basepos += len;
		y -= dia_lineheight;
	}
	return(basepos);
}

void Ddoneedit(void)
{
	INTBIG was;

	if (dia_it.curitem < 0) return;
	Dhighlight(0);
	was = dia_it.curitem;
	dia_it.curitem = -1;
	if (dia_it.firstch == 0) return;
	dia_it.firstch = 0;
	Dstufftext((char *)dia_it.dlgresaddr->list[was-1].data, &dia_it.dlgresaddr->list[was].r);
}

void Ddrawitem(INTBIG type, RECTAREA *r, char *msg, INTBIG dim)
{
	INTBIG j, save;
	POPUPDATA *pd;
	RECTAREA myrect;
	INTBIG xv[3], yv[3];

	Dsettextsmall(-1);
	if ((type&ITEMTYPE) == POPUP)
	{
		/* popup menu item */
		pd = (POPUPDATA *)msg;
		if (pd->count <= 0) return;
		Dstuffmessage(pd->namelist[pd->current], r, dim);
		Dinsetrect(r, -1);
		Ddrawrectframe(r, 1, 0);
		Ddrawline(r->left+3, r->bottom, r->right, r->bottom);
		Ddrawline(r->right, r->top+3, r->right, r->bottom);
		Dinsetrect(r, 1);
		xv[0] = r->right - 18;   yv[0] = r->top + 4;
		xv[1] = r->right - 12;   yv[1] = r->top + 10;
		xv[2] = r->right - 6;    yv[2] = r->top + 4;
		save = r->left;   r->left = r->right - 19;
		Dintdrawrect(r, ALLOFF);
		r->left = save;
		Ddrawpolygon(xv, yv, 3, 1);
	} else if ((type&ITEMTYPE) == BUTTON || (type&ITEMTYPE) == DEFBUTTON)
	{
		/* button */
		j = Dgettextsize(msg, strlen(msg));
		Ddrawroundrectframe(r, 12, dim);
		Ddrawtext(msg, strlen(msg), (r->left+r->right-j)/2,
			(r->top+r->bottom+dia_lineheight)/2, dim);
	} else if ((type&ITEMTYPE) == CHECK)
	{
		/* check box */
		myrect = *r;
		myrect.right = myrect.left + 12;
		myrect.top = (myrect.top + myrect.bottom) / 2 - 6;
		myrect.bottom = myrect.top + 12;
		Ddrawrectframe(&myrect, 1, dim);
		Ddrawtext(msg, strlen(msg), myrect.right+4,
			r->top+dia_lineheight, dim);
	} else if ((type&ITEMTYPE) == RADIO)
	{
		/* radio button */
		myrect = *r;
		myrect.right = myrect.left + 12;
		myrect.top = (myrect.top + myrect.bottom) / 2 - 6;
		myrect.bottom = myrect.top + 12;
		Ddrawcircle(&myrect, dim);
		Ddrawtext(msg, strlen(msg), myrect.right+4,
			myrect.top+dia_lineheight, dim);
	} else if ((type&ITEMTYPE) == MESSAGE)
	{
		/* message */
		Dstuffmessage(msg, r, dim);
	} else if ((type&ITEMTYPE) == EDITTEXT)
	{
		/* edit text */
		if (dim == 0) Dstufftext(msg, r);
		Deditbox(r, 1, dim);
	} else if ((type&ITEMTYPE) == SCROLL || (type&ITEMTYPE) == SCROLLMULTI)
	{
		/* scrollable list */
		Ddrawrectframe(r, 1, dim);
	} else if ((type&ITEMTYPE) == ICON)
	{
		/* 32x32 icon */
		Dputicon(r->left, r->top, msg);
	} else if ((type&ITEMTYPE) == DIVIDELINE)
	{
		/* dividing line */
		DiaDrawRect(0, r, 255, 255, 255);
	}
}

void Ddrawpopupentries(void)
{
	INTBIG i, y;
	INTBIG xv[3], yv[3];

	Dintdrawrect(&dia_rect, ALLOFF);
	Ddrawrectframe(&dia_rect, 1, 0);
	for(i=dia_sta; i<dia_cnt; i++)
	{
		if (i == dia_sta && dia_sta != 0)
		{
			y = dia_rect.top+(i-dia_sta+1)*dia_lineheight+1;
			xv[0] = dia_rect.left + 18;   yv[0] = y - 4;
			xv[1] = dia_rect.left + 12;   yv[1] = y - 10;
			xv[2] = dia_rect.left + 6;    yv[2] = y - 4;
			Ddrawpolygon(xv, yv, 3, 1);
			continue;
		}
		if (i == dia_cnt-1 && dia_cnt != dia_pd->count)
		{
			y = dia_rect.top+(i-dia_sta+1)*dia_lineheight+1;
			xv[0] = dia_rect.left + 18;   yv[0] = y - 10;
			xv[1] = dia_rect.left + 12;   yv[1] = y - 4;
			xv[2] = dia_rect.left + 6;    yv[2] = y - 10;
			Ddrawpolygon(xv, yv, 3, 1);
			continue;
		}
		Ddrawtext(dia_pd->namelist[i], strlen(dia_pd->namelist[i]), dia_rect.left+1,
			dia_rect.top+(i-dia_sta+1)*dia_lineheight+1, 0);
	}
}

void Dstufftext(char *msg, RECTAREA *r)
{
	INTBIG len, wid, vpos;

	Dintdrawrect(r, ALLOFF);

	/* display the message in multiple lines */
	vpos = r->top+dia_lineheight;
	if (dia_it.firstch > strlen(msg)) return;
	msg = &msg[dia_it.firstch];
	while (*msg != 0)
	{
		for(len = strlen(msg); len>0; len--)
		{
			wid = Dgettextsize(msg, len);
			if (wid < r->right-r->left-2) break;
		}
		if (len <= 0) break;
		Ddrawtext(msg, len, r->left+1, vpos, 0);
		msg += len;
		vpos += dia_lineheight;
		if (vpos > r->bottom) break;
	}
}

void Dstuffmessage(char *msg, RECTAREA *r, INTBIG dim)
{
	INTBIG len, wid, truelen, vpos, mostch, i;

	Dintdrawrect(r, ALLOFF);

	/* see how much fits */
	truelen = strlen(msg);
	for(len = truelen; len > 0; len--)
	{
		wid = Dgettextsize(msg, len);
		if (wid <= r->right-r->left-2) break;
	}
	if (len <= 0) return;

	/* if it all fits or must fit (because there is just 1 line), draw it */
	vpos = r->top+dia_lineheight;
	if (len == truelen || dia_lineheight*2 > r->bottom - r->top)
	{
		if (len != truelen)
		{
			/* single line doesn't fit: put ellipsis at end */
			(void)initinfstr();
			for(i=0; i<len-3; i++) (void)addtoinfstr(msg[i]);
			(void)addstringtoinfstr("...");
			msg = returninfstr();
		}
		Ddrawtext(msg, len, r->left+1, vpos, dim);
		return;
	}

	/* write the message in multiple lines */
	while (truelen > 0)
	{
		mostch = 0;
		for(len = truelen; len > 0; len--)
		{
			wid = Dgettextsize(msg, len);
			if (wid > r->right-r->left-2) continue;
			if (mostch == 0) mostch = len;
			if (msg[len] == 0 || msg[len] == ' ') break;
		}
		if (len <= 0) len = mostch;
		Ddrawtext(msg, len, r->left+1, vpos, dim);
		if (msg[len] == ' ') len++;
		msg += len;
		truelen -= len;
		vpos += dia_lineheight;
		if (vpos > r->bottom) break;
	}
}

void Deditbox(RECTAREA *r, INTBIG draw, INTBIG dim)
{
	Dinsetrect(r, -3);
	Ddrawrectframe(r, draw, dim);
	Dinsetrect(r, 3);
}

/*
 * routine to draw line "msg" in position "which" of scroll area "sc"
 */
void Ddrawmsg(INTBIG sc, char *msg, INTBIG which)
{
	INTBIG wid, len, firstch, offset;
	INTBIG skip;
	DSCROLL *scr;

	Dsettextsmall(sc);
	scr = &dia_it.scroll[sc];
	firstch = offset = 0;
	if (scr->horizfactor != 0)
	{
		skip = scr->userrect.right - scr->userrect.left - 6;
		skip *= (INTBIG)scr->horizfactor;   skip /= 10;
		wid = Dgettextsize(msg, strlen(msg));
		if (skip >= wid) return;
		for(firstch = 0; firstch < strlen(msg); firstch++)
		{
			wid = Dgettextsize(msg, firstch);
			if (wid >= skip) break;
		}
		offset = wid - skip;
	}

	for(len = strlen(&msg[firstch]); len>0; len--)
	{
		wid = Dgettextsize(&msg[firstch], len);
		if (wid+offset < scr->userrect.right-scr->userrect.left-6) break;
	}
	if (len <= 0) return;
	Ddrawtext(&msg[firstch], len, scr->userrect.left+2+offset,
		scr->userrect.top + scr->lineheight*(which+1), 0);
	Dsettextsmall(-1);
}

void Dinsetrect(RECTAREA *r, INTBIG amt)
{
	r->left += amt;   r->right -= amt;
	r->top += amt;    r->bottom -= amt;
}

void Ddrawvertslider(INTBIG sc)
{
	RECTAREA r;
	DSCROLL *scr;

	scr = &dia_it.scroll[sc];
	r = scr->userrect;
	r.left = r.right-1;   r.right += 14;
	Ddrawrectframe(&r, 1, 0);
	Ddrawline(r.left, r.top+15, r.right-1, r.top+15);
	Ddrawarrow(sc, UPARROW, 0);
	Ddrawline(r.left, r.bottom-16, r.right-1, r.bottom-16);
	Ddrawarrow(sc, DOWNARROW, 0);
}

void Ddrawhorizslider(INTBIG sc)
{
	RECTAREA r;
	DSCROLL *scr;

	scr = &dia_it.scroll[sc];
	r = scr->userrect;
	r.top = r.bottom-1;   r.bottom += 14;
	Ddrawrectframe(&r, 1, 0);
	Ddrawline(r.left+15, r.top, r.left+15, r.bottom-1);
	Ddrawarrow(sc, LEFTARROW, 0);
	Ddrawline(r.right-16, r.top, r.right-16, r.bottom-1);
	Ddrawarrow(sc, RIGHTARROW, 0);
	Dsethscroll(sc);
}

void Ddrawarrow(INTBIG sc, INTBIG which, INTBIG filled)
{
	INTBIG i, left, top, bottom, right;
	INTBIG xv[ARROWLEN], yv[ARROWLEN];
	DSCROLL *scr;

	scr = &dia_it.scroll[sc];
	top = scr->userrect.top;
	bottom = scr->userrect.bottom-1;
	right = scr->userrect.right-1;
	left = scr->userrect.left;
	for(i=0; i<ARROWLEN; i++)
	{
		switch (which)
		{
			case UPARROW:
				xv[i] = right+dia_arrowx[i];
				yv[i] = top+dia_arrowy[i];
				break;
			case DOWNARROW:
				xv[i] = right+dia_arrowx[i];
				yv[i] = bottom-dia_arrowy[i];
				break;
			case LEFTARROW:
				xv[i] = left+dia_arrowy[i];
				yv[i] = bottom+dia_arrowx[i];
				break;
			case RIGHTARROW:
				xv[i] = right-dia_arrowy[i];
				yv[i] = bottom+dia_arrowx[i];
				break;
		}
	}
	Ddrawpolygon(xv, yv, ARROWLEN, filled);
}

void Dredrawdialogwindow(void)
{
	INTBIG i, itemtype, dim;
	char *line;
	RECTAREA r;
	USERTYPE routine;

	if (dia_active <= 0) return;

	/* redraw the window frame */
	Dsettextsmall(-1);
	Dnewdialogwindowframe(&dia_it.dlgresaddr->windowRect, dia_it.dlgresaddr->movable);

	for(i=0; i<dia_it.dlgresaddr->items; i++)
	{
		/* draw the item */
		itemtype = dia_it.dlgresaddr->list[i].type;
		r = dia_it.dlgresaddr->list[i].r;
		if ((itemtype&ITEMTYPE) == USERDRAWN)
		{
			routine = (USERTYPE)dia_it.dlgresaddr->list[i].data;
			if (routine != 0) (*routine)(&r);
			continue;
		}
		if ((itemtype&ITEMTYPE) == MESSAGE || (itemtype&ITEMTYPE) == EDITTEXT ||
			(itemtype&ITEMTYPE) == POPUP)
				line = (char *)dia_it.dlgresaddr->list[i].data; else
					line = dia_it.dlgresaddr->list[i].msg;
		if ((itemtype&ITEMTYPE) == CHECK || (itemtype&ITEMTYPE) == RADIO)
			DiaSetControl(i+1, dia_it.dlgresaddr->list[i].data);
		if ((itemtype&INACTIVE) != 0) dim = 1; else dim = 0;
		Ddrawitem(itemtype, &r, line, dim);

		/* highlight the default button */
		if (i == dia_it.defaultbutton-1 &&
			(itemtype == BUTTON || itemtype == DEFBUTTON)) Dhighlightrect(&r);
	}

	/* turn on anything being edited */
	if (dia_it.curitem >= 0) Dhighlight(1);
	for(i=0; i<dia_it.scrollcount; i++)
	{
		Dredrawscroll(i);
		Dinvertentry(i, 1);      /* restores previous inverted entry */
		Dsetvscroll(i);
		if ((dia_it.scroll[i].flags&SCHORIZBAR) != 0)
		{
			Ddrawhorizslider(i);
			Dsethscroll(i);
		}
	}
}

/****************************** ATHENA DIALOG PRIMITIVE GRAPHICS ******************************/

/*
 * routine to prepare a dialog in location "r", draggable if "movable" is nonzero
 */
void Dnewdialogwindow(RECTAREA *r, char *movable)
{
	INTBIG swid, shei, x, y;
	UINTBIG descript[TEXTDESCRIPTSIZE];
	RECTAREA lr;

	dia_window = *el_curwindowpart;
	getwindowframesize(el_curwindowpart->frame, &swid, &shei);

	/* set drawing bounds to address the world */
	dia_window.screenlx = dia_window.screenly = dia_window.uselx = dia_window.usely = 0;
	dia_window.screenhx = dia_window.usehx = swid-1;
	dia_window.screenhy = dia_window.usehy = shei-1;
	dia_window.frame = el_curwindowpart->frame;
	computewindowscale(&dia_window);

	dia_revy = shei-1;
	setdefaultcursortype(NORMALCURSOR);
	us_menufigs.col = el_colmengly;
	us_menutext.col = el_colmentxt;

	/* set text sizes */
	TDCLEAR(descript);
	TDSETSIZE(descript, 8);
	screensettextinfo(&dia_window, NOTECHNOLOGY, descript);
	screengettextsize(&dia_window, "Xy", &x, &y);   dia_slineheight = y;

	TDSETSIZE(descript, TXTMENU);
	screensettextinfo(&dia_window, NOTECHNOLOGY, descript);
	screengettextsize(&dia_window, "Xy", &x, &y);   dia_lineheight = y;

	/* adjust for window borders */
	lr = *r;
	if (movable == 0) Dinsetrect(&lr, -5); else
	{
		Dinsetrect(&lr, -1);
		lr.top -= DRAGBARHEIGHT;
	}

	/* make sure the dialog fits in the edit window */
	getwindowframesize(el_curwindowpart->frame, &swid, &shei);
	if (lr.right >= swid) { lr.left -= lr.right-swid+1;   lr.right = swid-1; }
	if (lr.bottom >= shei) { lr.top -= lr.bottom-shei+1;   lr.bottom = shei-1; }
	if (lr.left < 0) { lr.right -= lr.left;   lr.left = 0; }
	if (lr.top < 0) { lr.bottom -= lr.top;   lr.top = 0; }
	if (lr.right >= swid) lr.right = swid-1;
	if (lr.bottom >= shei) lr.bottom = shei-1;

	/* save the display under the dialog */
	dia_it.theDialog = screensavebox(&dia_window, lr.left, lr.right, dia_revy-lr.bottom,
		dia_revy-lr.top);

	/* compute the true offsets */
	if (movable == 0)
	{
		dia_xoffset = lr.left+5;    dia_yrev = dia_revy - (lr.top+5);
	} else
	{
		dia_xoffset = lr.left+1;    dia_yrev = dia_revy - (lr.top+DRAGBARHEIGHT+1);
	}
	dia_lineoffset = 0;
	dia_curlineoffset = dia_lineoffset;
}

/*
 * routine to draw the dialog frame in location "r", draggable if "movable" is nonzero
 */
void Dnewdialogwindowframe(RECTAREA *r, char *movable)
{
	RECTAREA lr;
	INTBIG realxo, realyo, wid;
	INTBIG swid, shei;

	lr = *r;
	if (movable == 0) Dinsetrect(&lr, -5); else
	{
		Dinsetrect(&lr, -1);
		lr.top -= DRAGBARHEIGHT;
	}

	/* make sure the dialog fits in the edit window */
	getwindowframesize(el_curwindowpart->frame, &swid, &shei);
	if (lr.right >= swid) { lr.left -= lr.right-swid+1;   lr.right = swid-1; }
	if (lr.bottom >= shei) { lr.top -= lr.bottom-shei+1;   lr.bottom = shei-1; }
	if (lr.left < 0) { lr.right -= lr.left;   lr.left = 0; }
	if (lr.top < 0) { lr.bottom -= lr.top;   lr.top = 0; }
	if (lr.right >= swid) lr.right = swid-1;
	if (lr.bottom >= shei) lr.bottom = shei-1;

	/* compute the true offsets and set temporary ones */
	if (movable == 0)
	{
		realxo = lr.left+5;    realyo = dia_revy - (lr.top+5);
	} else
	{
		realxo = lr.left+1;    realyo = dia_revy - (lr.top+DRAGBARHEIGHT+1);
	}
	dia_xoffset = lr.left;    dia_yrev = dia_revy - lr.top;

	screendrawbox(&dia_window, lr.left, lr.right, dia_revy-lr.bottom,
		dia_revy-lr.top, &us_ebox);
	lr.right -= lr.left;   lr.left = 0;
	lr.bottom -= lr.top;   lr.top = 0;
	if (movable == 0)
	{
		Ddrawrectframe(&lr, 1, 0);
		Dinsetrect(&lr, 3);
		Ddrawrectframe(&lr, 1, 0);
		Dinsetrect(&lr, 1);
		Ddrawrectframe(&lr, 1, 0);
	} else
	{
		Ddrawrectframe(&lr, 1, 0);
		lr.bottom = lr.top + DRAGBARHEIGHT;
		lr.left++;   lr.right--;   lr.top++;
		Dgrayrect(&lr);
		Ddrawline(lr.left,lr.bottom, lr.right,lr.bottom);
		if (*movable != 0)
		{
			wid = Dgettextsize(movable, strlen(movable));
			lr.left = (lr.right+lr.left-wid) / 2-10;
			lr.right = lr.left + wid + 20;
			Dintdrawrect(&lr, ALLOFF);
			Ddrawtext(movable, strlen(movable), lr.left+10, lr.bottom, 0);
		}
	}

	/* set true offsets */
	dia_xoffset = realxo;    dia_yrev = realyo;
}

/*
 * Routine to return the low and high Y coordinates of the WINDOW in dialog space.
 */
void Dgetwindowextent(INTBIG *ly, INTBIG *hy)
{
	INTBIG swid, shei;

	getwindowframesize(el_curwindowpart->frame, &swid, &shei);
	*ly = -dia_it.dlgresaddr->windowRect.top;
	*hy = shei-dia_it.dlgresaddr->windowRect.top;
}

/*
 * routine to terminate the current dialog
 */
void Ddonedialogwindow(void)
{
	screenrestorebox(dia_it.theDialog, 1);
}

/*
 * routine to wait for the next action from the dialog window.  Returns:
 *  -1  Nothing happened
 *   0  Key typed, character in "chr"
 *   1  Mouse button pushed, coordinates in (x,y), modifiers in "chr"
 *   2  Mouse button released, coordinates in (x,y)
 *   4  Mouse motion while button down, coordinates in (x,y)
 *   5  Double-click of mouse button, coordinates in (x,y), modifiers in "chr"
 */
INTBIG Dwaitforaction(INTBIG *x, INTBIG *y, INTBIG *chr, INTBIG *special, UINTBIG *time, BOOLEAN *shifted)
{
	INTBIG but, xl, yl;
	INTBIG lx, ly;

	if (!ttydataready())
	{
		waitforbutton(&xl, &yl, &but);
		if (but < 0) return(-1);
		lx = xl;   ly = yl;
		Dcorrectxy(&lx, &ly);
		*x = lx;   *y = ly;

		/* see if the drag bar was hit */
		if (dia_it.dlgresaddr->movable != 0 && *y < 0)
		{
			Ddragwindow(*x, *y);
			return(-1);
		}
		*time = ticktime();
		*shifted = shiftbutton(but);
		if (doublebutton(but)) return(5);
		return(1);
	}
	*shifted = FALSE;
	*chr = getnxtchar(special);
	*time = ticktime();
	return(0);
}

/*
 * routine to track mouse movements while the button remains down, calling
 * the routine "eachdown" for each advance.  The routine is given the
 * mouse coordinates as parameters.  The initial mouse position is (fx,fy).
 */
void Dtrackcursor(INTBIG fx, INTBIG fy, BOOLEAN (*eachdown)(INTBIG, INTBIG))
{
	trackcursor(FALSE, us_nullup, us_nullvoid, eachdown, us_nullchar, us_nullvoid, TRACKNORMAL);
}

/*
 * routine to save the rectangle "r" and return a pointer that can be
 * used to restore it with "Drestorerect()".  Returns zero if this function
 * is not possible.
 */
INTBIG Dsaverect(RECTAREA *r)
{
	return(screensavebox(&dia_window, r->left+dia_xoffset, r->right+1+dia_xoffset,
		dia_yrev-r->bottom, dia_yrev-r->top));
}

/*
 * routine to restore the rectangle "sr" (a pointer that came from "Dsaverect()")
 */
void Drestorerect(INTBIG sr)
{
	screenrestorebox(sr, 1);
}

/*
 * routine to move the rectangle "sr" to "dr"
 */
void Dshiftbits(RECTAREA *sr, RECTAREA *dr)
{
	screenmovebox(&dia_window, sr->left, sr->top, sr->right-sr->left, sr->bottom-sr->top,
		dr->left, dr->top);
}

void Dcorrectxy(INTBIG *x, INTBIG *y)
{
	*x -= dia_xoffset;
	*y = dia_yrev - *y;
}

/*
 * routine to draw the 32x32 bitmap in "data" at (x,y)
 */
void Dputicon(INTBIG x, INTBIG y, char *data)
{
	INTBIG xp, yp, v;

	for(yp=0; yp<32; yp++)
	{
		for(xp=0; xp<32; xp++)
		{
			v = data[yp*4 + (xp>>3)];
			if ((v & (0200>>(xp&7))) == 0) continue;
			Ddrawbox(x+xp, x+xp, y+yp, y+yp, &us_menufigs);
		}
	}
}

/*
 * routine to draw a line from (xf,yf) to (xt,yt) in the dialog
 */
void Ddrawline(INTBIG xf, INTBIG yf, INTBIG xt, INTBIG yt)
{
	INTBIG fx, fy, tx, ty;

	fx = xf + dia_xoffset;   fy = dia_yrev - yf;
	tx = xt + dia_xoffset;   ty = dia_yrev - yt;
	if (clipline(&fx, &fy, &tx, &ty, dia_window.uselx, dia_window.usehx,
		dia_window.usely, dia_window.usehy)) return;
	screendrawline(&dia_window, fx, fy, tx, ty, &us_menufigs, 0);
}

/*
 * routine to draw a filled rectangle at "r" in the dialog.  Erases if "on" is zero
 */
void Dintdrawrect(RECTAREA *r, INTBIG colorindex)
{
	us_box.col = colorindex;
	Ddrawbox(r->left, r->right-1, r->bottom-1, r->top, &us_box);
}

void Ddrawbox(INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy, GRAPHICS *which)
{
	lx += dia_xoffset;   ly = dia_yrev - ly;
	hx += dia_xoffset;   hy = dia_yrev - hy;
	if (lx < dia_window.uselx) lx = dia_window.uselx;
	if (hx >= dia_window.usehx) hx = dia_window.usehx-1;
	if (ly < dia_window.usely) ly = dia_window.usely;
	if (hy >= dia_window.usehy) hy = dia_window.usehy-1;
	if (lx > hx || ly > hy) return;
	screendrawbox(&dia_window, lx, hx, ly, hy, which);
}

void Dinvertbox(INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy)
{
	lx += dia_xoffset;   ly = dia_yrev - ly;
	hx += dia_xoffset;   hy = dia_yrev - hy;
	if (lx < dia_window.uselx) lx = dia_window.uselx;
	if (hx >= dia_window.usehx) hx = dia_window.usehx-1;
	if (ly < dia_window.usely) ly = dia_window.usely;
	if (hy >= dia_window.usehy) hy = dia_window.usehy-1;
	if (lx >= hx || ly >= hy) return;
	screeninvertbox(&dia_window, lx, hx, ly, hy);
}

/*
 * Routine to find the closest index to color (r,g,b)
 */
INTBIG Dgetcolorindex(INTBIG r, INTBIG g, INTBIG b)
{
	REGISTER INTBIG colorvalue, dist, bestdist, i;
	REGISTER VARIABLE *varred, *vargreen, *varblue;
	INTBIG *red, *green, *blue;

	/* determine color to use */
	varred = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
	varblue = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
	if (varred == NOVARIABLE || vargreen == NOVARIABLE || varblue == NOVARIABLE) return(0);
	red = (INTBIG *)varred->addr;
	green = (INTBIG *)vargreen->addr;
	blue = (INTBIG *)varblue->addr;
	bestdist = 99999;
	for(i=0; i<255; i++)
	{
		dist = abs(red[i] - r) + abs(green[i] - g) + abs(blue[i] - b);
		if (dist > bestdist) continue;
		bestdist = dist;
		colorvalue = i;
	}
	return(colorvalue);
}

/*
 * routine to draw a gray rectangle at "r" in the dialog
 */
void Dgrayrect(RECTAREA *r)
{
	Ddrawbox(r->left, r->right, r->bottom, r->top, &dia_drawgray);
}

/*
 * routine to invert a rectangle at "r" in the dialog
 */
void Dinvertrect(RECTAREA *r)
{
	Dinvertbox(r->left, r->right, r->bottom-1, r->top);
}

/*
 * routine to draw the outline of rectangle "r" in the dialog.  Erases if "on" is zero
 */
void Ddrawrectframe(RECTAREA *r, INTBIG on, INTBIG dim)
{
	Ddrawline(r->left, r->top, r->left, r->bottom-1);
	Ddrawline(r->left, r->bottom-1, r->right, r->bottom-1);
	Ddrawline(r->right, r->bottom-1, r->right, r->top);
	Ddrawline(r->right, r->top, r->left, r->top);
}

/*
 * routine to highlight the outline of rectangle "r" in the dialog
 */
void Dhighlightrect(RECTAREA *r)
{
	Dinsetrect(r, -3);
	Ddrawrectframe(r, 1, 0);
	Dinsetrect(r, -1);
	Ddrawrectframe(r, 1, 0);
}

/*
 * routine to draw the outline of rounded rectangle "r" in the dialog
 */
void Ddrawroundrectframe(RECTAREA *r, INTBIG arc, INTBIG dim)
{
	Ddrawrectframe(r, 1, dim);
}

/*
 * routine to invert the outline of rectangle "r" in the dialog
 */
void Dinvertrectframe(RECTAREA *r)
{
	Dinvertbox(r->left, r->left+1, r->bottom-1, r->top);
	Dinvertbox(r->right-1, r->right, r->bottom-1, r->top);
	Dinvertbox(r->left+1, r->right-1, r->bottom, r->bottom-1);
	Dinvertbox(r->left+1, r->right-1, r->top+1, r->top);
}

/*
 * routine to draw the polygon in "count" points (xv,yv) and fill it if "filled" is nonzero
 */
void Ddrawpolygon(INTBIG *xv, INTBIG *yv, INTBIG count, INTBIG filled)
{
	INTBIG i, l;

	/* adjust to edit window coordinates */
	if (dia_poly == NOPOLYGON) dia_poly = allocpolygon(count, us_tool->cluster); else
		if (dia_poly->limit < count) (void)extendpolygon(dia_poly, count);
	for(i=0; i<count; i++)
	{
		dia_poly->xv[i] = xv[i] + dia_xoffset;
		dia_poly->yv[i] = dia_yrev - yv[i];
	}
	dia_poly->count = count;
	dia_poly->style = CLOSED;

	clippoly(dia_poly, dia_window.uselx, dia_window.usehx, dia_window.usely, dia_window.usehy);
	if (dia_poly->count == 0) return;

	if (filled == 0)
	{
		screendrawpolygon(&dia_window, dia_poly->xv, dia_poly->yv, dia_poly->count, &us_ebox);
		for(i=0; i<dia_poly->count; i++)
		{
			if (i == 0) l = dia_poly->count-1; else l = i-1;
			screendrawline(&dia_window, dia_poly->xv[l], dia_poly->yv[l],
				dia_poly->xv[i], dia_poly->yv[i], &us_menufigs, 0);
		}
	} else screendrawpolygon(&dia_window, dia_poly->xv, dia_poly->yv, dia_poly->count, &us_menufigs);
}

/*
 * routine to draw a circle outline in rectangle "r" in the dialog
 */
void Ddrawcircle(RECTAREA *r, INTBIG dim)
{
	GRAPHICS *which;

	if (dim == 0) which = &us_menufigs; else which = &dia_drawdim;
	if (dia_poly == NOPOLYGON) dia_poly = allocpolygon(2, us_tool->cluster); else
		if (dia_poly->limit < 2) (void)extendpolygon(dia_poly, 2);
	dia_poly->xv[0] = (r->left+r->right)/2+dia_xoffset;
	dia_poly->yv[0] = dia_yrev-(r->top+r->bottom)/2;
	dia_poly->xv[1] = dia_poly->xv[0] + (r->right-r->left)/2;
	dia_poly->yv[1] = dia_poly->yv[0];
	dia_poly->count = 2;
	dia_poly->style = CIRCLE;
	cliparc(dia_poly, dia_window.uselx, dia_window.usehx, dia_window.usely, dia_window.usehy);
	if (dia_poly->style != CIRCLE) return;

	screendrawcircle(&dia_window, dia_poly->xv[0], dia_poly->yv[0], (r->right-r->left)/2, which);
}

/*
 * routine to draw a filled circle in rectangle "r" in the dialog
 */
void Ddrawdisc(RECTAREA *r)
{
	if (dia_poly == NOPOLYGON) dia_poly = allocpolygon(2, us_tool->cluster); else
		if (dia_poly->limit < 2) (void)extendpolygon(dia_poly, 2);
	dia_poly->xv[0] = (r->left+r->right)/2+dia_xoffset;
	dia_poly->yv[0] = dia_yrev-(r->top+r->bottom)/2;
	dia_poly->xv[1] = dia_poly->xv[0] + (r->right-r->left)/2;
	dia_poly->yv[1] = dia_poly->yv[0];
	dia_poly->count = 2;
	dia_poly->style = DISC;
	cliparc(dia_poly, dia_window.uselx, dia_window.usehx, dia_window.usely, dia_window.usehy);
	if (dia_poly->style != DISC) return;

	screendrawdisc(&dia_window, dia_poly->xv[0], dia_poly->yv[0], (r->right-r->left)/2, &us_menufigs);
}

/*
 * routine to determine the width of the "len" characters at "msg"
 */
INTBIG Dgettextsize(char *msg, INTBIG len)
{
	INTBIG wid, hei;
	INTBIG need;
	static char *tempbuf;
	static INTBIG tempbufsize = 0;

	if (len == 0) return(0);
	need = len + 1;
	if (need > tempbufsize)
	{
		if (tempbufsize > 0) efree(tempbuf);
		tempbufsize = 0;
		tempbuf = (char *)emalloc(need, us_tool->cluster);
		if (tempbuf == 0) return(0);
		tempbufsize = need;
	}
	strncpy(tempbuf, msg, len);
	tempbuf[len] = 0;
	screengettextsize(&dia_window, tempbuf, &wid, &hei);
	return(wid);
}

/*
 * Routine to set the current text size to the large or small scroll text.
 */
void Dsettextsmall(INTBIG sc)
{
	INTBIG smallf;
	DSCROLL *scr;

	if (sc < 0) smallf = 0; else
	{
		scr = &dia_it.scroll[sc];
		smallf = scr->flags & SCSMALLFONT;
	}
	if (smallf != 0)
	{
		dia_curlineoffset = dia_slineoffset;
		gra_settextsize(&dia_window, 2, 0, 0, 0, 0);   /* used to be TXT8P */
	} else
	{
		dia_curlineoffset = dia_lineoffset;
		gra_settextsize(&dia_window, TXTMENU, 0, 0, 0, 0);
	}
}

/*
 * routine to draw "len" characters of "msg" at (x,y)
 */
void Ddrawtext(char *msg, INTBIG len, INTBIG x, INTBIG y, INTBIG dim)
{
	INTBIG need;
	INTBIG sx, sy;
	GRAPHICS *which;
	static char *tempbuf;
	static INTBIG tempbufsize = 0;

	if (dim == 0) which = &us_menutext; else which = &dia_drawdim;
	need = len + 1;
	if (need > tempbufsize)
	{
		if (tempbufsize > 0) efree(tempbuf);
		tempbufsize = 0;
		tempbuf = (char *)emalloc(need, us_tool->cluster);
		if (tempbuf == 0) return;
		tempbufsize = need;
	}
	strncpy(tempbuf, msg, len);
	tempbuf[len] = 0;
	screengettextsize(&dia_window, tempbuf, &sx, &sy);
	if (x+dia_xoffset >= dia_window.uselx && x+dia_xoffset+sx < dia_window.usehx &&
		dia_yrev-y >= dia_window.usely && dia_yrev-y+sy < dia_window.usehy)
	{
		screendrawtext(&dia_window, x+dia_xoffset, dia_yrev-y,
			tempbuf, which);
	}
}

BOOLEAN Ddragwin(INTBIG x, INTBIG y)
{
	INTBIG swid, shei;

	if (x == dia_lastx && y == dia_lasty) return(FALSE);
	getwindowframesize(el_curwindowpart->frame, &swid, &shei);
	if (dia_it.dlgresaddr->windowRect.left+x-dia_lastx < 0) return(FALSE);
	if (dia_it.dlgresaddr->windowRect.right+x-dia_lastx >= swid) return(FALSE);
	if (dia_it.dlgresaddr->windowRect.top-y+dia_lasty < 0) return(FALSE);
	if (dia_it.dlgresaddr->windowRect.bottom-y+dia_lasty >= shei) return(FALSE);
	Dinvertrectframe(&dia_it.dlgresaddr->windowRect);
	dia_it.dlgresaddr->windowRect.left += x-dia_lastx;
	dia_it.dlgresaddr->windowRect.right += x-dia_lastx;
	dia_it.dlgresaddr->windowRect.top -= y-dia_lasty;
	dia_it.dlgresaddr->windowRect.bottom -= y-dia_lasty;
	dia_lastx = x;   dia_lasty = y;
	Dinvertrectframe(&dia_it.dlgresaddr->windowRect);
	return(FALSE);
}

void Ddragwindow(INTBIG x, INTBIG y)
{
	INTBIG save;
	INTBIG swid, shei;
	REGISTER INTBIG saveleft, saveright, savetop, savebottom;
	RECTAREA lr, oldr;

	if (y <= -DRAGBARHEIGHT && x >= 0 &&
		x <= dia_it.dlgresaddr->windowRect.right-dia_it.dlgresaddr->windowRect.left) return;

	dia_lastx = x + dia_xoffset;
	dia_lasty = dia_yrev - y;
	dia_xoffset = 0;         dia_yrev = dia_revy;
	oldr = dia_it.dlgresaddr->windowRect;

	/* make the window frame true to size */
	Dinsetrect(&dia_it.dlgresaddr->windowRect, -1);
	dia_it.dlgresaddr->windowRect.top -= DRAGBARHEIGHT;
	Dinvertrectframe(&dia_it.dlgresaddr->windowRect);
	trackcursor(FALSE, us_nullup, us_nullvoid, Ddragwin, us_nullchar, us_nullvoid, TRACKNORMAL);
	Dinvertrectframe(&dia_it.dlgresaddr->windowRect);

	/* save the old dialog image */
	Dinsetrect(&oldr, -1);
	oldr.top -= DRAGBARHEIGHT;
	saveleft = oldr.left;
	saveright = oldr.right;
	savetop = dia_revy-oldr.bottom;
	savebottom = dia_revy-oldr.top;

	/* make sure the dialog fits in the edit window */
	getwindowframesize(el_curwindowpart->frame, &swid, &shei);
	if (saveright >= swid) { saveleft -= saveright-swid+1;    saveright = swid-1; }
	if (savebottom >= shei) { savetop -= savebottom-shei+1;   savebottom = shei-1; }
	if (saveleft < 0) { saveright -= saveleft;   saveleft = 0; }
	if (savetop < 0) { savebottom -= savetop;   savetop = 0; }
	if (saveright >= swid) saveright = swid-1;
	if (savebottom >= shei) savebottom = shei-1;

	save = screensavebox(&dia_window, saveleft, saveright, savetop, savebottom);

	/* move the saved bits to the new location */
	screenmovesavedbox(save, dia_it.dlgresaddr->windowRect.left-oldr.left,
		-(dia_it.dlgresaddr->windowRect.top-oldr.top));

	/* restore the bits underneath */
	screenrestorebox(dia_it.theDialog, 1);

	/* save the bits under the new dialog area */
	lr = dia_it.dlgresaddr->windowRect;
	saveleft = lr.left;
	saveright = lr.right;
	savetop = dia_revy-lr.bottom;
	savebottom = dia_revy-lr.top;
	if (saveright >= swid) { saveleft -= saveright-swid+1;    saveright = swid-1; }
	if (savebottom >= shei) { savetop -= savebottom-shei+1;   savebottom = shei-1; }
	if (saveleft < 0) { saveright -= saveleft;   saveleft = 0; }
	if (savetop < 0) { savebottom -= savetop;   savetop = 0; }
	if (saveright >= swid) saveright = swid-1;
	if (savebottom >= shei) savebottom = shei-1;
	dia_it.theDialog = screensavebox(&dia_window, saveleft, saveright, savetop, savebottom);

	/* restore the dialog to the new location */
	screenrestorebox(save, 1);

	/* restore the moved dialog frame to its true size */
	Dinsetrect(&dia_it.dlgresaddr->windowRect, 1);
	dia_it.dlgresaddr->windowRect.top += DRAGBARHEIGHT;

	/* compute proper drawing offsets */
	dia_xoffset = dia_it.dlgresaddr->windowRect.left;
	dia_yrev = dia_revy - dia_it.dlgresaddr->windowRect.top;
}

#endif
	
/*
 * Helper routine for "DiaLoadTextDialog" that makes strings go in ascending order.
 */
int gra_stringposascending(const void *e1, const void *e2)
{
	REGISTER char *c1, *c2;

	c1 = *((char **)e1);
	c2 = *((char **)e2);
	return(namesame(&c1[gra_dialogstringpos], &c2[gra_dialogstringpos]));
}
