/*
 * Electric(tm) VLSI Design Systems
 *
 * File: graphqt.cpp
 * Qt Window System interface
 * Written by: Dmitry Nadezhin, Instutute for Design Problems in Microelectronics, Russian Academy of Sciences
 *
 * Copyright (c) 2001 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 "graphqt.h"

#include <qaccel.h>
#include <qdir.h>
#include <qfile.h>
#include <qfiledialog.h>
#include <qfileinfo.h>
#include <qfontdatabase.h>
#include <qfontdialog.h>
#include <qhbox.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qlineedit.h>
#include <qmenubar.h>
#include <qmultilineedit.h>
#include <qpainter.h>
#include <qpixmap.h>
#include <qpopupmenu.h>
#include <qstatusbar.h>
#include <qtimer.h>
#include <qvbox.h>

#include "database.h"
#include "egraphics.h"
#include "eio.h"
#include "usrtrack.h"
#include "graphqtdlg.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>

#undef KeyPress

#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

#ifdef ONUNIX
#define X11PAINT
#endif

//#define OLDARGS

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

#define WMLEFTBORDER    8						/* size of left border for windows */
#define WMTOPBORDER    30						/* size of top border for windows */
#define SBARWIDTH      15						/* size of text-edit scroll bars */
#define PALETTEWIDTH  110

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

#endif

EApplication        *gra = 0;
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 INTSML        gra_windowframeindex = 0;
static INTSML        gra_windownumber;			/* number of the window that was created */
static WINDOWFRAME  *gra_deletethisframe = 0;	/* window that is to be deleted */
#ifdef OLDARGS
static int           gra_argc;					/* "argc" for display */
static char        **gra_argv;					/* "argv" for display */
#endif
static char          gra_localstring[256];
extern GRAPHICS      us_box, us_ebox, us_menutext;

static INTBIG        gra_windowbeingdeleted = 0;

int        gra_xerrors(Display *dpy, XErrorEvent *err);
void       gra_xterrors(char *msg);
void       gra_adjustwindowframeon(Display *dpy, INTBIG screen, INTSML how);
INTSML     gra_buildwindow(QWidget *desktop, WINDOWFRAME *wf, BOOLEAN floating);
void       gra_reloadmap(void);
void       gra_getwindowattributes(WINDOWFRAME *wf, XWindowAttributes *xwa);
void       gra_getwindowinteriorsize(WINDOWFRAME *wf, INTBIG *wid, INTBIG *hei);
void       gra_intsignalfunc(void);
void       gra_removewindowextent(RECTAREA *r, QWidget *wnd);
void       gra_handlequeuedframedeletion(void);
static INTSML        gra_graphicshas(INTSML want);
static char        **gra_eprinterlist(void);

/****** the messages window ******/
static INTSML        gra_messages_typingin;		/* nonzero if typing into messages window */

/****** the status bar ******/
#define MAXSTATUSLINES  1						/* number of status lines */

static STATUSFIELD *gra_statusfields[100];
static char *gra_statusfieldtext[100];
static INTSML gra_indicatorcount = 0;

/****** the menus ******/
static INTSML      gra_pulldownmenucount_ = 0;	/* number of top-level pulldown menus */
static POPUPMENU  *gra_pulldowns_[50];			/* the current top-level pulldown menus */

/****** events ******/
typedef RETSIGTYPE (*SIGNALCAST)(int);

static INTSML        gra_doublethresh;			/* threshold of double-click */
static INTBIG        gra_cursorx, gra_cursory;	/* current position of mouse */
static INTSML        gra_lstcurx, gra_lstcury;	/* former position of mouse */
static INTBIG        gra_cureventtime = 0;		/* current time counter (for repeating) */
static XEvent        gra_samplemotionevent;		/* fake event for repeating */
static Qt::ButtonState   gra_lastbuttonstate = Qt::NoButton; /* last button state (for getbuckybits) */

#define TIME_SLICE_DELAY        50              /* time ( in ms ) to postpone timeslice */

static INTSML gra_stop_dowork = 0;
RETSIGTYPE gra_sigill_trap(void);
RETSIGTYPE gra_sigfpe_trap(void);
RETSIGTYPE gra_sigbus_trap(void);
RETSIGTYPE gra_sigsegv_trap(void);
static void gra_setcurrentwindowframe(WINDOWFRAME *wf);

static INTSML gra_nxtchar;
static INTBIG gra_nxtcharspecial;
static BOOLEAN gra_nxtcharhandler(INTSML chr, INTBIG special);
static BOOLEAN gra_nullbuttonhandler(INTBIG x, INTBIG y, INTBIG but);

typedef enum
{
  S_STARTUP, S_USER, S_TOOL, S_TRACK, S_MODAL, S_CHECKINT
} GRAPHSTATE;

static char *stateNames[] =
{
  "STARTUP", "USER", "TOOL", "TRACK", "MODAL", "CHECKINT"
};

GRAPHSTATE gra_state;

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

struct {
	char  *name;				/* button name */
	INTSML 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 */
};

/* SWAPPED means that the bytes are MSB first */
/* was: #ifndef i386 */
#ifdef sparc
#  define SWAPPED 1
#endif

/* 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);

/****** 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_printerlist;
static INTBIG        gra_printerlistcount = 0;
static INTBIG        gra_printerlisttotal = 0;


int        gra_fileselectall(const struct dirent *a);
INTSML     gra_addfiletolist(char *file);
void       gra_initfilelist(void);
INTSML     gra_addprinter(char *buf);

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

int main(int argc, char *argv[])
{
#ifdef ETRACE
#if 1
	us_tracefile = fopen("trace.txt", "w");
#else
	us_tracefile = stderr;
#endif
#endif

	/* 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 */
	gra_state = S_STARTUP;
#ifdef ETRACE
	etrace(GTRACE, "  calling osprimaryosinit...\n");
#endif
	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 */
#ifdef ETRACE
	etrace(GTRACE, "  calling ossecondaryinit...\n");
#endif
	ossecondaryinit(argc, argv);

	gra_state = S_USER;
#ifdef ETRACE
	etrace(GTRACE, "  gra_state=USER\n");
#endif
	db_setcurrenttool(us_tool);
	/* first see if there is any pending input */
	el_pleasestop = 0;

#ifdef ETRACE
	etrace(GTRACE, "  main loop...\n");
#endif
	gra->exec();
	return(0);
}

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

	etrace(GTRACE, "{ graphicsoptions: args=");
	for (i = 0; i < *argc; i++) etrace(GTRACE_CONT, " <%s>", argv[i]);
	etrace(GTRACE_CONT, "\n");
#endif

#ifdef OLDARGS
	/* save command arguments */
	gra_argc = *argc;
	gra_argv = argv;
#else
	int int_argc = *argc;
	gra = new EApplication(int_argc, argv);
	*argc = int_argc;
#endif

	/* 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
#ifdef ETRACE
	etrace(GTRACE, "} graphicsoptions: us_erasech=%#o us_killch=%#o\n", us_erasech, us_killch);
#endif
}

/*
 * Routine to initialize the display device.
 */
BOOLEAN initgraphics(BOOLEAN messages)
{
#ifdef ETRACE
	etrace(GTRACE, "{ initgraphics: messages=%d\n");
#endif 

#ifdef OLDARGS
	gra = new EApplication(gra_argc, gra_argv);
#endif

	/* make the messages window */
	gra->messMainWin = new MessagesMainWindow();
	gra->messMainWin->show();

#ifdef ETRACE
	etrace(GTRACE, "} initgraphics: el_colcursor=%#o el_maplength=%d\n", el_colcursor, el_maplength);
#endif 
	return(FALSE);
}

EApplication::EApplication( int &argc, char **argv)
  : QApplication (argc, argv)
{
    REGISTER INTBIG i, j;
    INTBIG bitmask, depth;
    int namecount;
    char *ptr, **fontlist;
    char *geomSpec;

    /* initialize X and the toolkit */

	/* get switch settings */
	geomSpec = NULL;
	for(i=1; i < argc; i++)
	{
		if (argv[i][0] == '-')
		{
			switch (argv[i][1])
			{
				case 'g':       /* Geometry */
					if (++i >= argc) continue;
					geomSpec = argv[i];
					continue;
			}
		}
	}

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

	/* get the information about the edit display */
	depth = DefaultDepth(dpy(), screen());
	if (depth == 1)
		fprintf(stderr, _("Cannot run on 1-bit deep displays\n"));
	el_colcursor = CURSOR;	/* also done in usr.c later */
	el_maplength = 256;

	/*resetpaletteparameters();*/
	gra_palettewidth = DisplayWidth(dpy(), screen()) - WMLEFTBORDER;
	gra_paletteheight = DisplayHeight(dpy(), screen()) - WMTOPBORDER;
	gra_palettetop = 0;

	/* Determine initial position and size of edit window */
	XSizeHints   *gra_editxsh = XAllocSizeHints();
	if (geomSpec == NULL) geomSpec = XGetDefault(dpy(), "Electric", "geometry");
	if (geomSpec != NULL)
	{
		/* use defaults from database */
		bitmask = XWMGeometry(dpy(), screen(), 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(dpy(), screen()) - 2;
		gra_editxsh->width = DisplayWidth(dpy(), screen()) - PALETTEWIDTH - 8;
		gra_editxsh->height = gra_editxsh->height / 4 * 3;
		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;

	/* get fonts */
	fixedfont.setFamily( QString::null );
	fixedfont.setStyleHint( QFont::TypeWriter );
#if QT_VERSION >= 300
	fixedfont.setStyleStrategy( QFont::NoAntialias );
#else
	{
	    char *e = egetenv ("QT_XFT");
	    if ( e && (*e == '1' ||
		       *e == 'y' || *e == 'Y' ||
		       *e == 't' || *e == 'T' )) 
	    {
	        ttyputmsg(_("Electric may have problems drawing text with QT_XFT='%s' environment setting"), e);
	    }
	}
#endif
	fixedfont.setFixedPitch( TRUE );

	/* initialize font cache */
	QFontDatabase fdb;
	facelist = fdb.families();
	EPainter::init( facelist );

	char **localfacelist;
	screengetfacelist(&localfacelist);
	ptr = getenv("ELECTRIC_TRUETYPE_FONT");
	if (ptr != NULL)
	{
		for(i=0; i<facelist.count(); i++)
		{
			if (namesame(ptr, localfacelist[i]) == 0)
			{
				defface = localfacelist[i];
				break;
			}
		}
		if (i >= facelist.count())
		{
			printf(_("Warning: environment variable 'ELECTRIC_TRUETYPE_FONT' is '%s' which is not a known font\n"),
				ptr);
			printf(_("Choices are:\n"));
			for(i=0; i<facelist.count(); i++)
			{
				printf("  %s\n", localfacelist[i]);
			}
		}
	}
	if (defface.isEmpty()) defface = font().family();

	/* on machines with reverse byte ordering, fix the icon and the cursors */
#ifdef 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
	programicon = QBitmap (PROGICONSIZE, PROGICONSIZE, (uchar*)gra_icon, TRUE);

	/* create the cursors */
	QBitmap bitm;
	bitm = QBitmap( 16, 16, (uchar*)gra_realcursordata, TRUE );
	realcursor = QCursor( bitm, bitm, 8, 8 );
	bitm = QBitmap( 16, 16, (uchar*)gra_nomousecursordata, TRUE );
	nomousecursor = QCursor( bitm, bitm, 8, 8 );
	bitm = QBitmap( 16, 16, (uchar*)gra_drawcursordata, TRUE );
	drawcursor = QCursor( bitm, bitm, 0, 16 );
	bitm = QBitmap( 16, 16, (uchar*)gra_nullcursordata, TRUE );
	nullcursor = QCursor( bitm, bitm, 8, 8 );
	bitm = QBitmap( 16, 16, (uchar*)gra_menucursordata, TRUE );
	menucursor = QCursor( bitm, bitm, 16, 8 );
	bitm = QBitmap( 16, 16, (uchar*)gra_handcursordata, TRUE );
	handcursor = QCursor( bitm, bitm, 0, 10 );
	bitm = QBitmap( 16, 16, (uchar*)gra_techcursordata, TRUE );
	techcursor = QCursor( bitm, bitm, 8, 0 );
	bitm = QBitmap( 16, 16, (uchar*)gra_ibeamcursordata, TRUE );
	ibeamcursor = QCursor( bitm, bitm, 8, 8 );
	bitm = QBitmap( 16, 16, (uchar*)gra_lrcursordata, TRUE );
	lrcursor = QCursor( bitm, bitm, 8, 8 );
	bitm = QBitmap( 16, 16, (uchar*)gra_udcursordata, TRUE );
	udcursor = QCursor( bitm, bitm, 8, 8 );

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

	/* initialize the mouse */
	us_cursorstate = -1;
	us_normalcursor = NORMALCURSOR;
#ifdef ETRACE
	etrace(GTRACE, "  initgraphics: us_cursorstate=%d us_normalcursor=%d\n",
	       us_cursorstate, us_normalcursor);
#endif 
	setdefaultcursortype(us_normalcursor);
}

int EApplication::screen() const
{
    return DefaultScreen( dpy () );
}

MessagesMainWindow::MessagesMainWindow()
  : QMainWindow(0, "MessageMainWindow"), stopped(FALSE)
{
    QVBox *l = new QVBox( this );
    setCentralWidget( l );

    scroll = new QMultiLineEdit( l );
    scroll->setReadOnly( TRUE );
    scroll->setWordWrap( QMultiLineEdit::NoWrap );
#if QT_VERSION >= 300
    scroll->viewport()->setCursor( IbeamCursor );
#else
    scroll->setCursor( IbeamCursor );
#endif

    input = new QHBox( l );
    prompt = new QLabel( input );
    line = new QLineEdit( input );
    input->hide();

    connect(line, SIGNAL(returnPressed()), SLOT(lineEntered()));
    line->installEventFilter( this );
    
    /* Determine initial position and size of messages window */
    int screenmw = QApplication::desktop()->width();
    int screenmh = QApplication::desktop()->height();
    int width = screenmh / 3 * 2;
    int height = screenmh / 4 - 80;
    int x = (screenmw - width) / 2;
    int y = screenmh - height - 52;
    setGeometry(x, y, width, height);

#ifdef EPROGRAMNAME
    char *title = EPROGRAMNAME;
#else
    char *title = "Electric";
#endif
    setIcon( gra->programicon );
    (void)sprintf(gra_localstring, _("%s Messages"), title);
    setCaption( gra_localstring );
    (void)strcpy(gra_localstring, _("Messages"));
    setIconText( gra_localstring );

    gra_messages_typingin = 0;
}

void MessagesMainWindow::closeEvent( QCloseEvent *e )
{
#ifdef ETRACE
    etrace(GTRACE, "{ MessagesMainWindow::closeEvent %s\n", stateNames[gra_state]);
#endif
    ttyputerr(_("Cannot delete the messages window"));
    e->ignore();
#ifdef ETRACE
    etrace(GTRACE, "{ MessagesMainWindow::closeEvent\n");
#endif
}

void MessagesMainWindow::keyPressEvent( QKeyEvent *e )
{
#ifdef ETRACE
    etrace(GTRACE, "{ MessagesMainWidnow::keyPressEvent %s accepted=%d\n", stateNames[gra_state], e->isAccepted());
#endif
    gra_setcurrentwindowframe(NOWINDOWFRAME);
    INTBIG special;
    int state = gra->translatekey(e, &special);

    if (state != 0 || special != 0)
    {
        us_state |= DIDINPUT;
	INTSML cmd = state;
#ifdef ETRACE
	etrace(GTRACE, "  MessagesMainWindow::keyPressEvent: %s state=%#o cmd=%#o special=%#o DIDINPUT%d,%d\n",
	       stateNames[gra_state], state, cmd, special, gra_cursorx, gra_cursory );
#endif

	switch (gra_state) {
	case S_USER:
#ifdef ETRACE
	  etrace(GTRACE, "{ us_oncommand: cmd=%#o special=%#o\n", cmd, special);
#endif
	  us_oncommand( cmd, special );
	  gra->toolTimeSlice();
#ifdef ETRACE
	  etrace(GTRACE, "} us_oncommand\n");
#endif
	  break;
	}
	e->accept();
    }
    else QMainWindow::keyPressEvent( e );
#ifdef ETRACE
    etrace(GTRACE, "} MessagesMainWindow::keyPressEvent accepted=%d\n", e->isAccepted());
#endif
}

void MessagesMainWindow::lineEntered()
{
    line->releaseKeyboard();
    gra->exit_loop();
}

bool MessagesMainWindow::eventFilter( QObject *watched, QEvent *e )
{
    if (watched == line && e->type() == QEvent::KeyPress) {
        QKeyEvent *k = (QKeyEvent *) e;
	if ((k->state() & ControlButton) && k->key() == Key_D &&
	    line->text().isEmpty()) {
	    stopped = TRUE;
	    lineEntered();
	    return TRUE;
	}
    }
    return QMainWindow::eventFilter( watched, e );
}

#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;

#ifdef ETRACE
	etrace(GTRACE, "{ setupenvironment: us_tool->USER_machine = UNIX\n");
#endif

	/* 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);
#ifdef ETRACE
		etrace(GTRACE, "} setupenvironment: el_libdir = %s\n", el_libdir);
#endif
		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);
#ifdef ETRACE
		etrace(GTRACE, "} setupenvironment: el_libdir = %s\n", el_libdir);
#endif
		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);
#ifdef ETRACE
		etrace(GTRACE, "} setupenvironment: el_libdir = %s\n", el_libdir);
#endif
		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);
#ifdef ETRACE
		etrace(GTRACE, "} setupenvironment: el_libdir = %s\n", el_libdir);
#endif
		return;
	}

	/* look for "~/.cadrc" */
	(void)initinfstr();
	(void)addstringtoinfstr(truepath("~/.cadrc"));
	pt = returninfstr();
	if (fileexistence(pt) == 1)
	{
		(void)allocstring(&el_libdir, pt, db_cluster);
#ifdef ETRACE
		etrace(GTRACE, "} setupenvironment: el_libdir = %s\n", el_libdir);
#endif
		return;
	}

	ttyputerr(_("Warning: cannot find '.cadrc' startup file"));
	(void)allocstring(&el_libdir, ".", db_cluster);
#ifdef ETRACE
	etrace(GTRACE, "} setupenvironment: el_libdir = %s\n", el_libdir);
#endif
}

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);
#ifdef ETRACE
	etrace(GTRACE, "{}setlibdir: el_libdir = %s\n", el_libdir);
#endif
}

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

EApplication::~EApplication()
{
	REGISTER INTBIG i;
	REGISTER INTBIG j;

	/* deallocate font cache */
	EPainter::term();

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

void termgraphics(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{}termgraphics\n");
#endif
    delete gra;
    gra = 0;
}

void exitprogram(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{}exitprogram\n");
#endif
	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;

#ifdef ETRACE
	etrace(GTRACE, "{ newwindowframe: floating=%d", floating);
	if (r != NULL) etrace(GTRACE_CONT, " r=%d,%d %d,%d", r->top, r->left, r->bottom, r->right);
	etrace(GTRACE_CONT, "\n");
#endif

	/* allocate one */
	wf = (WINDOWFRAME *)emalloc((sizeof (WINDOWFRAME)), us_tool->cluster);
	if (wf == 0) return(NOWINDOWFRAME);
	wf->numvar = 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(QApplication::desktop(), wf, floating) != 0)
	{
		efree((char *)wf);
		el_firstwindowframe = oldlisthead;
#ifdef ETRACE
		etrace(GTRACE, "} newwindowframe: failed\n");
#endif
		return(NOWINDOWFRAME);
	}

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

#ifdef ETRACE
	etrace(GTRACE, "} newwindowframe: wf=%d swid=%d shei=%d\n",
	       wf->windindex, wf->swid, wf->shei);
#endif
	return(wf);
}

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

#ifdef ETRACE
	etrace(GTRACE, "{}killwindowframe: owf=%d gra_windowbeingdeleted=%d\n",
	       owf->windindex, gra_windowbeingdeleted);
#endif

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

	/* kill the actual window, that will remove the frame, too */
	GraphicsMainWindow *qwin = (GraphicsMainWindow*)owf->qt;
	delete qwin;

	/* 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;

	efree((char *)owf);
}

void movedisplay(void)
{
#if QT_VERSION >= 300
	WINDOWFRAME *wf = getwindowframe(1);
	if (wf == NOWINDOWFRAME) return;
	QWidget *newDesktop = QApplication::desktop();
	GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
	QDesktopWidget *desktop = QApplication::desktop();
	newDesktop = desktop->screen( (desktop->screenNumber(qwin) + 1) % desktop->numScreens() );
	qwin->deleteLater();
	wf->qt = NULL;
	if (gra_buildwindow(newDesktop, wf, wf->floating) != 0)
	  ttyputerr(_("Problem moving the display"));

	/* redisplay */
	us_drawmenu(1, wf);
	us_redostatus(wf);
	us_endbatch();
#else
	ttyputmsg(_("(Display move works only with Qt version 3\n"));
#endif
}

WINDOWFRAME *getwindowframe(BOOLEAN canfloat)
{
#ifdef ETRACE
	etrace(GTRACE, "{}getwindowframe: canfloat=%d el_curwindowframe=%#x windindex=%d\n",
	       canfloat, el_curwindowframe,
	       el_curwindowframe != NOWINDOWFRAME ? el_curwindowframe->windindex : -1);
#endif
	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)
{
    QRect geom = gra->messMainWin->geometry();
    *top = geom.top();
    *left = geom.left();
    *bottom = geom.bottom();
    *right = geom.right();
#ifdef ETRACE
	etrace(GTRACE, "{}getmessagesframeinfo: top=%d left=%d bottom=%d right=%d\n",
	       *top, *left, *bottom, *right);
#endif
}

/*
 * Routine to set the size and position of the messages window.
 */
void setmessagesframeinfo(INTBIG top, INTBIG left, INTBIG bottom, INTBIG right)
{
#ifdef ETRACE
	etrace(GTRACE, "{}setmessagesframeinfo: top=%d left=%d bottom=%d right=%d\n",
	       top, left, bottom, right);
#endif
    QRect geom( QPoint( left, top ) , QPoint( right, bottom ) );
    gra->messMainWin->setGeometry( geom );
}

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

#ifdef ETRACE
	etrace(GTRACE, "{ sizewindowframe: windindex=%d wid=%d hei=%d\n",
	       wf->windindex, wid, hei);
#endif
	gra_getwindowattributes(wf, &xwa);
	if (wid == xwa.width && hei == xwa.height)
	{
#ifdef ETRACE
		etrace(GTRACE, "} sizewindowframe: nothing to do\n");
#endif
		return;
	}
	if (wf == lastwf && wid == lastwid && hei == lasthei)
	{
		repeat++;
		if (repeat > 2)
		{
#ifdef ETRACE
			etrace(GTRACE, "} sizewindowframe: failed repeat=%d\n", repeat);
#endif
			return;
		}
	} else
	{
		lastwf = wf;   lastwid = wid;   lasthei = hei;   repeat = 1;
	}
	gra_internalchange = ticktime();
	GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
	qwin->resize(wid, hei);
#ifdef ETRACE
	etrace(GTRACE, "} sizewindowframe: done\n");
#endif
}

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

#ifdef ETRACE
	etrace(GTRACE, "{}movewindowframe: windindex=%d left=%d top=%d\n",
	       wf->windindex, left, top);
#endif
	gra_getwindowattributes(wf, &xwa);
#if 0
	if (wf->floating != 0) top += gra_palettetop;
#endif
	if (left == xwa.x && top == xwa.y) return;
	gra_internalchange = ticktime();
	GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
	qwin->move(left, top);
}

void gra_getwindowattributes(WINDOWFRAME *wf, XWindowAttributes *xwa)
{
    GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
    xwa->x = qwin->x();
    xwa->y = qwin->y();
    xwa->width = qwin->width();
    xwa->height = qwin->height();
}

void gra_getwindowinteriorsize(WINDOWFRAME *wf, INTBIG *wid, INTBIG *hei)
{
	GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
	*wid = qwin->draw->width();
	*hei = qwin->draw->height();
}

/*
 * Routine to close the messages window if it is in front.  Returns true if the
 * window was closed.
 */
BOOLEAN closefrontmostmessages(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{}closefrontmostmessages:\n");
#endif

	return(FALSE);
}

/*
 * Routine to bring window "wf" to the front.
 */
void bringwindowtofront(WINDOWFRAME *wf)
{
#ifdef ETRACE
	etrace(GTRACE, "{}bringwindowtofront: windindex\n", wf->windindex);
#endif

	GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
	XRaiseWindow(gra->dpy(), qwin->winId());
	XSetInputFocus(gra->dpy(), qwin->winId(), RevertToNone, CurrentTime);
}

/*
 * Routine to organize the windows according to "how" (0: tile horizontally,
 * 1: tile vertically, 2: cascade).
 */
void adjustwindowframe(INTBIG how)
{
#ifdef ETRACE
	etrace(GTRACE, "{ adjustwindowframe: how=%d\n", how);
#endif

	gra_adjustwindowframeon(gra->dpy(), gra->screen(), how);

#ifdef ETRACE
	etrace(GTRACE, "} adjustwindowframe:\n");
#endif
}

void gra_adjustwindowframeon(Display *dpy, INTBIG screen, INTSML 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->floating == 0) 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, (GraphicsMainWindow*)compmenu->qt);
	}
	if (gra->messMainWin != 0)
	{
		/* remove messages menu from area of tiling */
		gra_removewindowextent(&r, gra->messMainWin);
	}

	/* rearrange the windows */
	child = 0;
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->floating != 0) 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);
		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, QWidget *wnd)
{
    QRect er = wnd->frameGeometry();

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

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

#ifdef ETRACE
	etrace(GTRACE, "{}getpaletteparameters: wid=%d hei=%d palettewidth=%d\n",
	       *wid, *hei, *palettewidth);
#endif
}

/*
 * 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)
{
#ifdef ETRACE
	etrace(GTRACE, "{}resetpaletteparameters:\n");
#endif

	gra_palettewidth = DisplayWidth(gra->dpy(), gra->screen()) - WMLEFTBORDER;
	gra_paletteheight = DisplayHeight(gra->dpy(), gra->screen()) - WMTOPBORDER;
	gra_palettetop = 0;
}

GraphicsDraw::GraphicsDraw( QWidget *parent )
  : QWidget( parent, "draw", WRepaintNoErase )
{
    setMouseTracking( TRUE );
}

bool GraphicsDraw::event( QEvent * e )
{
    if ( e->type() == QEvent::AccelOverride ) {
	QKeyEvent* ke = (QKeyEvent*) e;
	if (!(ke->state() & AltButton)) ke->accept();
    }
    return QWidget::event( e );
}

void GraphicsDraw::focusInEvent( QFocusEvent *e )
{
#ifdef ETRACE
    etrace(GTRACE, "{ GraphicsDraw::focusInEvent %s\n", stateNames[gra_state]);
#endif
    QWidget::focusInEvent( e );
#ifdef ETRACE
    etrace(GTRACE, "} GraphicsDraw::focusInEvent\n");
#endif
}

void GraphicsDraw::focusOutEvent( QFocusEvent *e )
{
#ifdef ETRACE
    etrace(GTRACE, "{ GraphicsDraw::focusOutEvent %s\n", stateNames[gra_state]);
#endif
    QWidget::focusOutEvent( e );
#ifdef ETRACE
    etrace(GTRACE, "} GraphicsDraw::focusOutEvent\n");
#endif
}

void GraphicsDraw::keyPressEvent( QKeyEvent *e )
{
#ifdef ETRACE
    etrace(GTRACE, "{ GraphicsDraw::keyPressEvent %s accepted=%d\n", stateNames[gra_state], e->isAccepted());
#endif
    gra_setcurrentwindowframe(wf);
    INTBIG special;
    int state = gra->translatekey(e, &special);

    if (state != 0 || special != 0)
    {
        us_state |= DIDINPUT;
	INTBIG cmd = state;
	keepCursorPos( mapFromGlobal( QCursor::pos() ) );
#ifdef ETRACE
	etrace(GTRACE, "  GraphicsDraw::keyPressEvent: %s state=%#o cmd=%#o special=%#o DIDINPUT%d,%d\n",
	       stateNames[gra_state], state, cmd, special, gra_cursorx, gra_cursory );
#endif

	switch (gra_state) {
	case S_USER:
#ifdef ETRACE
	  etrace(GTRACE, "{ us_oncommand: cmd=%#o\n", cmd);
#endif
	  us_oncommand( cmd, special );
	  gra->toolTimeSlice();
#ifdef ETRACE
	  etrace(GTRACE, "} us_oncommand\n");
#endif
	  break;
	case S_TRACK: {
#ifdef ETRACE
	  etrace(GTRACE, "  trackcursor { eachchar x=%d y=%d cmd=%#o\n",
		 gra_cursorx, gra_cursory, cmd);
#endif
	  int keepon = (*gra->eachchar)(gra_cursorx, gra_cursory, cmd);
	  flushscreen();
#ifdef ETRACE
	  etrace(GTRACE, "  trackcursor } eachchar: keepon=%d el_pleasestop=%d\n",
		 keepon, el_pleasestop);
#endif
	  if (keepon || el_pleasestop) gra->exit_loop();
	}
	break;
	case S_MODAL: {
#ifdef ETRACE
	  etrace(GTRACE, "  modalloop { charhandler cmd=%#o special=%#o\n", cmd, special);
#endif
	  BOOLEAN keepon = (*gra->charhandler)(cmd, special);
	  flushscreen();
#ifdef ETRACE
	  etrace(GTRACE, "  modalloop } charhandler: keepon=%d el_pleasestop=%d\n",
		 keepon, el_pleasestop);
#endif
	  if (keepon || el_pleasestop) gra->exit_loop();
	}
	break;
	}
	e->accept();
    }
    else QWidget::keyPressEvent( e );
#ifdef ETRACE
    etrace(GTRACE, "} GraphicsDraw::keyPressEvent accepted=%d\n", e->isAccepted());
#endif
}

GraphicsMainWindow::GraphicsMainWindow( QWidget *parent, BOOLEAN floating )
  : QMainWindow( parent, "GraphicsMainWindow")
{
    pulldownmenus = 0;
    pulldowns = 0;
    pulldownmenucount = 0;
    if (!floating) {
        for (int i = 0; i < gra_indicatorcount; i++) {
            statusItems[i] = new QLabel( statusBar() );
	    STATUSFIELD *sf = gra_statusfields[i];
	    statusBar()->addWidget( statusItems[i], sf->endper - sf->startper );
	    statusItems[i]->setText( gra_statusfieldtext[i] );
	}
    }
}

GraphicsMainWindow::~GraphicsMainWindow()
{
    if (pulldownmenus != 0) efree((char*)pulldownmenus);
    if (pulldowns != 0) efree((char*)pulldowns);
}

void GraphicsMainWindow::closeEvent( QCloseEvent *e )
{
#ifdef ETRACE
    etrace(GTRACE, "{ GraphicsMainWindow::closeEvent %s\n", stateNames[gra_state]);
#endif
    /* queue this frame for deletion */
    gra_deletethisframe = wf;
    e->ignore();
    gra->toolTimeSlice();
#ifdef ETRACE
    etrace(GTRACE, "} GraphicsMainWindow::closeEvent\n");
#endif
}

INTSML gra_buildwindow(QWidget *desktop, WINDOWFRAME *wf, BOOLEAN floating)
{
	wf->floating = floating;
	int x, y, width, height;

	/* determine window position */

	int screenw = desktop->width();
	int screenh = desktop->height();
	if (!floating)
	{
		x = gra_editposx;
		y = gra_editposy;
		width = gra_editsizex;
		height = gra_editsizey;
		x += (gra_windownumber%5) * 40;
		y += (gra_windownumber%5) * 40;
		if (x + width > screenw)
			width = screenw - x;
		if (y + height > screenh)
			height = screenh - y;
		gra_windownumber++;
	} else
	{
		x = 0;
		y = 1 + gra_palettetop;
		width = PALETTEWIDTH;
		height = screenh - 50;
		gra_internalchange = ticktime();
	}

	/*
	 * make top-level graphics widget
	 */
	GraphicsMainWindow *qwin = new GraphicsMainWindow( desktop, floating );
	wf->qt = qwin;
	qwin->wf = wf;

	/* load window manager information */
	qwin->setIcon( gra->programicon );
	if (!floating) (void)strcpy(gra_localstring, _("Electric")); else
		(void)strcpy(gra_localstring, _("Components"));
	qwin->setCaption( gra_localstring );
	qwin->setIconText( gra_localstring );

	if (!floating) qwin->pulldownmenuload();
	qwin->draw = new GraphicsDraw(qwin);
	qwin->draw->wf = wf;
	qwin->setCentralWidget( qwin->draw );
	qwin->setFocusProxy( qwin->draw );
	qwin->setGeometry( x, y, width, height);

	/* get window size and depth after creation */
	wf->swid = wf->shei = -1;
	qwin->show();

	/* reload cursor */
	us_cursorstate = -1;
	setdefaultcursortype(us_normalcursor);

	/* fill the color map  */
	us_getcolormap(el_curtech, COLORSDEFAULT, FALSE);
	gra_reloadmap();

	return(0);
}

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

/*
 * return nonzero if the capabilities in "want" are present
 */
BOOLEAN graphicshas(INTBIG want)
{
	BOOLEAN has = gra_graphicshas(want);
#ifdef ETRACE
	etrace(GTRACE, "{}graphicshas: want=%#o has=%d\n", want, has);
#endif
	return(has);
}

static INTSML        gra_graphicshas(INTSML want)
{
	if ((want & CANHAVENOWINDOWS) != 0) return(0);
	return(1);
}


/*
 * internal routine to beep the status display.  If beeps are turned off, no
 * sound is made (unless "force" is nonzero)
 */
void ttybeep(INTBIG force)
{
#ifdef ETRACE
	etrace(GTRACE, "{}ttybeep force=%d\n", force);
#endif
	if ((us_tool->toolstate & TERMBEEP) != 0 || force != 0)
	{
		XBell(gra->dpy(), 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);
#ifdef ETRACE
	etrace(GTRACE, "{ error: line=<%s> us_logplay=NULL\n", line);
#endif

	/* print to stderr if graphics not initiaized */
	if (gra == 0) {
		fprintf(stderr, "Fatal Error: %s\n", line);
#ifdef ETRACE
		etrace(GTRACE, "} error stderr\n");
#endif
		return;
	}

	/* 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)
		{
#ifdef ETRACE
			etrace(GTRACE, "  error: saving libraries\n");
#endif
			/* 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)
				{
#ifdef ETRACE
					etrace(GTRACE, "} error: saving libraries failed\n");
#endif
					return;
				}
			}
		}
	}
	DiaDoneDialog();
#ifdef ETRACE
	etrace(GTRACE, "} error\n");
#endif
}

/*
 * Routine to get the environment variable "name" and return its value.
 */
char *egetenv(char *name)
{
	char *value = getenv(name);
#ifdef ETRACE
	etrace(GTRACE, "{}getenv: name=%s value=<%s>\n", name, value != NULL ? value : "NULL");
#endif
	return(value);
}

/*
 * 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)
{
	INTBIG process = fork();
#ifdef ETRACE
	etrace(GTRACE, "{}efork: process=%d\n", process);
#endif
	return(process);
}

/*
 * Routine to run the string "command" in a shell.
 * Returns nonzero if the command cannot be run.
 */
INTBIG esystem(char *command)
{
#ifdef ETRACE
	etrace(GTRACE, "{}esystem: command=<%s>\n", command);
#endif
	system(command);
	return(0);
}

/*
 * Routine to execute the program "program" with the arguments "args"
 */
void eexec(char *program, char *args[])
{
#ifdef ETRACE
	char **arg;

	etrace(GTRACE, "{}exec: program=<%s> args=", program);
	for (arg = args; *arg != NULL; arg++)
	{
		etrace(GTRACE_CONT, " <%s>", *arg);
	}
	etrace(GTRACE_CONT, "\n");
#endif
	execvp(program, args);
}

/*
 * routine to kill process "process".
 */
INTBIG ekill(INTBIG process)
{
	INTBIG result = kill(process, SIGKILL);
#ifdef ETRACE
	etrace(GTRACE, "{}ekill: process=%d result=%d\n", process, result);
#endif
	return(result);
}

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

#ifdef ETRACE
	etrace(GTRACE, "{ ewait: process=%d\n", process);
#endif
	for(;;)
	{
		pid = wait((int *)0);
		if (pid == process)
		{
#ifdef ETRACE
			etrace(GTRACE, "} ewait\n");
#endif
			return;
		}
		if (pid == -1)
		{
			perror("Waiting");
#ifdef ETRACE
			etrace(GTRACE, "} ewait: failed\n");
#endif
			return;
		}
	}
}

/*
 * Routine to determine the list of printers and return it.
 * The list terminates with a zero.
 */
char **eprinterlist(void)
{
	char **list;
#ifdef ETRACE
	char **p;

	etrace(GTRACE, "{ eprinterlist:\n");
#endif
	list = gra_eprinterlist();
#ifdef ETRACE
	etrace(GTRACE, "} eprinterlist:");
	for (p = list; *p != NULL; p++) etrace(GTRACE_CONT, " <%s>", *p);
	etrace(GTRACE_CONT, "\n");
#endif
	return(list);
}

static char        **gra_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) != 0)
				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) != 0)
				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);
}

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

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

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

	/* 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(1);
		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(0);
}

/******************** 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)
{
	INTSML i, j;
	REGISTER WINDOWFRAME *wf;

#ifdef ETRACE
	etrace(GTRACE, "{}ttyfreestatusfield: %s\n", sf->label);
#endif
	for(i=0; i<gra_indicatorcount; i++)
	{
		if (gra_statusfields[i] != sf) continue;

		/* remove the field */
		efree(gra_statusfieldtext[i]);
		for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
		{
			if (wf->floating) continue;
			GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
			QLabel *statusItem = qwin->statusItems[i];
			qwin->statusBar()->removeWidget( statusItem );
			delete statusItem;
			for(j=i+1; j<gra_indicatorcount; j++)
			{
				qwin->statusItems[j-1] = qwin->statusItems[j];
				qwin->statusItems[j] = 0;
			}
		}

		for(j=i+1; j<gra_indicatorcount; j++)
		{
			gra_statusfields[j-1] = gra_statusfields[j];
			gra_statusfieldtext[j-1] = gra_statusfieldtext[j];
		}
		gra_indicatorcount--;
	}

	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)
{
	INTSML len, i, j, pos, newpos;
	REGISTER WINDOWFRAME *wf;

#ifdef ETRACE_
	etrace(GTRACE, "{%cttysetstatusfield: wwf=%d field=%s message=<%s> cancrop=%d\n",
	       field != 0 ? ' ' : '}',
	       wwf != NOWINDOWFRAME ? wwf->windindex : -1,
	       field != 0 ? field->label : "<NULL>",
	       message != NULL ? message : "<NULL>",
	       cancrop);
#endif

	/* figure out which indicator this is */
	if (field == 0) return;

	/* if this is a title bar setting, do it now */
	if (field->line == 0)
	{
		if (wwf == NOWINDOWFRAME) return;
			/* 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;
		GraphicsMainWindow *qwin = (GraphicsMainWindow*)wwf->qt;
		qwin->setCaption( gra_localstring );
		return;
	}

	/* ignore null fields */
	if (*field->label == 0) return;

	/* 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;

	/* see if this indicator is in the list */
	for(i=0; i<gra_indicatorcount; i++)
	{
		if (gra_statusfields[i]->line != field->line ||
			gra_statusfields[i]->startper != field->startper ||
			gra_statusfields[i]->endper != field->endper) continue;

		/* load the string */
		for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
		{
			if (wf->floating) continue;
			if (wwf != NOWINDOWFRAME && wwf != wf) continue;
			GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
			QLabel *statusItem = qwin->statusItems[i];
			/* load the string */
			(void)reallocstring(&gra_statusfieldtext[i], gra_localstring, db_cluster);
			statusItem->setText( gra_localstring );
		}
		return;
	}

	/* not found: find where this field fits in the order */
	(void)allocstring(&gra_statusfieldtext[i], gra_localstring, db_cluster);
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->floating) continue;
		GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
		QLabel *statusItem = new QLabel( qwin->statusBar() );
		statusItem->setText( gra_localstring );
		qwin->statusItems[gra_indicatorcount] = statusItem;
		statusItem->show(); /* Is it necessary ? */
		qwin->statusBar()->addWidget( statusItem, field->endper - field->startper  );
	}
	gra_statusfields[gra_indicatorcount++] = field;
#ifdef ETRACE_
	etrace(GTRACE, "} ttysetstatusfield\n");
#endif
}

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

/*
 * Routine to put the string "s" into the messages window.
 * Pops up the messages window if "important" is true.
 */
void putmessagesstring(char *s, BOOLEAN important)
{
#ifdef ETRACE
	etrace(GTRACE, "{}putmessagesstring: <%s> important=%d\n", s, important);
#endif

	/* print to stderr if messages are not initiaized */
	if (gra == 0 || gra->messMainWin == 0 || gra->messMainWin->scroll == 0) {
		fprintf(stderr, "%s\n", s);
		return;
	}

	/* make sure the window isn't iconified or obscured */
	if (important)
	{
		gra->messMainWin->raise();
	}

	QMultiLineEdit *scroll = gra->messMainWin->scroll;
	scroll->insertLine( s, scroll->numLines() - 1);
	scroll->setCursorPosition( scroll->numLines() - 1, 0 );
}

/*
 * Routine to return the name of the key that ends a session from the messages window.
 */
char *getmessageseofkey(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{}getmessageseofkey: ^D\n");
#endif
	return("^D");
}

char *getmessagesstring(char *prompt)
{
#ifdef ETRACE
	etrace(GTRACE, "{ getmessagesstring: prompt=<%s>\n", prompt);
#endif

	/* change state */
	GRAPHSTATE savedstate = gra_state;
	gra_state = S_MODAL;
#ifdef ETRACE
	etrace(GTRACE, "  gra_state=MODAL\n");
#endif

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

	QMultiLineEdit *scroll = gra->messMainWin->scroll;
	QHBox *input = gra->messMainWin->input;
	QLabel *label = gra->messMainWin->prompt;
	QLineEdit *line = gra->messMainWin->line;
	label->setText(prompt);
	line->clear();
	gra->messMainWin->stopped = FALSE;
	input->show();
	gra->messMainWin->raise();
	line->grabKeyboard();
	gra->enter_loop();
	input->hide();

	/* restore cursor */
	setnormalcursor(oldnormalcursor);
	gra_state = savedstate;

	if (gra->messMainWin->stopped) {
#ifdef ETRACE
		etrace(GTRACE, "} getmessagesstring: stopped gra_state=%s\n",
		       stateNames[gra_state]);
#endif
		return(0);
	}
	scroll->insertLine( label->text() + line->text(), scroll->numLines() - 1);
	scroll->setCursorPosition( scroll->numLines() - 1, 0 );
	
	char *str = EApplication::localize( line->text() );

#ifdef ETRACE
	etrace(GTRACE, "} getmessagesstring: <%s> gra_state=%s\n",
	       str, stateNames[gra_state]);
#endif
	return(str); /* ToDo */
#if 0
	XmTextPosition tp, starttp;
	char ch[2], *block;
	INTBIG c, total;

	/* 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 = 1;
	for(;;)
	{
		c = getnxtchar();
		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 = 0;

	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);
#endif
}

/*
 * routine to select fonts in the messages window
 */
void setmessagesfont(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{ setmessagesfont\n");
#endif
	bool ok;
	QFont font = gra->messMainWin->scroll->font();
	font = QFontDialog::getFont( &ok, font, gra->messMainWin );
	if (ok)
	{
		gra->messMainWin->scroll->setFont( font );
	}
#ifdef ETRACE
	etrace(GTRACE, "} setmessagesfont\n");
#endif
}

/*
 * routine to cut text from the messages window if it is current.  Returns true
 * if sucessful.
 */
BOOLEAN cutfrommessages(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{}cutrommessages: result=0\n");
#endif
	return(FALSE);
}

/*
 * routine to copy text from the messages window if it is current.  Returns true
 * if sucessful.
 */
BOOLEAN copyfrommessages(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{}copyfrommessages: result=0\n");
#endif
	return(FALSE);
}

/*
 * routine to paste text to the messages window if it is current.  Returns true
 * if sucessful.
 */
BOOLEAN pastetomessages(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{}pastetomessages: result=0\n");
#endif
	return(FALSE);
}

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

	/* get cut buffer */
	string = XFetchBuffer(gra->dpy(), &returned, 0);
	(void)initinfstr();
	if (*string != 0)
	{
		(void)addstringtoinfstr(string);
		XFree(string);
	}
	string = returninfstr();
#ifdef ETRACE
	etrace(GTRACE, "{}getcutbuffer: <%s>\n", string);
#endif
	return(string);
}
        
/*
 * routine to set the contents of the system cut buffer to "msg"
 */
void setcutbuffer(char *msg)
{
#ifdef ETRACE
	etrace(GTRACE, "{}setcutbuffer: msg=<%s>\n", msg);
#endif
	XStoreBuffer(gra->dpy(), msg, strlen(msg), 0);
}

/*
 * Routine to clear all text from the messages window.
 */
void clearmessageswindow(void)
{
	gra->messMainWin->scroll->clear();
#ifdef ETRACE
	etrace(GTRACE, "{}clearmessageswindow\n");
#endif
}

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

void flushscreen(void)
{
	REGISTER WINDOWFRAME *wf;

	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		/* nothing to do if no changes have been made to offscreen buffer */
		GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
		EPainter *p = &qwin->draw->painter;
		if (!p->dirty()) continue;
		QRect r = p->copyRect();
		p->clearDirty();
		qwin->draw->repaint( r, FALSE );
	}
#if 0
	XFlush(gra->dpy());
#endif
}

/*
 * Routine to change the default cursor (to indicate modes).
 */
void setnormalcursor(INTBIG curs)
{
#ifdef ETRACE
	etrace(GTRACE, "{ setnormalcursor: curs=%d\n", curs);
#endif
	us_normalcursor = curs;
	setdefaultcursortype(us_normalcursor);
#ifdef ETRACE
	etrace(GTRACE, "} setnormalcursor\n", curs);
#endif
}

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)
{
	REGISTER WINDOWFRAME *wf;
	REGISTER INTSML i;
	REGISTER INTBIG r, g, b;

#ifdef ETRACE
	etrace(GTRACE, "{ colormapload: low=%d high=%d\n", low, high);
#endif
	/* 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)
	{
		GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;	
		GraphicsDraw *draw = qwin->draw;
#if QT_VERSION >= 300
		int screen = gra->desktop()->screenNumber( draw );
#else
		int screen = draw->x11Screen();
#endif

		for(i=low; i<=high; i++)
		{
			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];
			}

#ifdef X11PAINT
			/* for 16bpp, crop values to 5 bits */
			/* if this is color-mapped, get closest entry in that map */
			Visual *visual = DefaultVisual(gra->dpy(), screen);
			if (VisualClass(visual) == PseudoColor ||
			    VisualClass(visual) == StaticColor)
			{
		        	Colormap colmap = DefaultColormap( gra->dpy(), screen);
				XColor xc;
				xc.flags = DoRed | DoGreen | DoBlue;
				xc.red   = r << 8;
				xc.green = g << 8;
				xc.blue  = b << 8;
				XAllocColor(gra->dpy(), colmap, &xc);
				draw->colorvalue[i] = xc.pixel;
			} else
			{
				INTBIG depth = DefaultDepth(gra->dpy(), screen);
				if (depth == 16)
				{
					draw->colorvalue[i] = ((r & 0xF8) << 8) |
					  ((g & 0xF8) << 3) | ((b & 0xF8) >> 3);
				} else
				{
					draw->colorvalue[i] = (b << 16) | (g << 8) | r;
				}
			}
#endif
		}

		draw->painter.updateAll();
	}

	flushscreen();

#ifdef ONUNIX
	/* set the cursor color */
	if (low == 0 || (low <= el_colcursor && high >= el_colcursor))
	{
		XColor        gra_xfc, gra_xbc;			/* cursor color structure  */
		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(gra->dpy(), &gra_xfc, &gra_xbc);
	}
#endif

#ifdef ETRACE
	etrace(GTRACE, "} colormapload\n");
#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.handle(), fg, bg);
	XRecolorCursor(dpy, gra->nomousecursor.handle(), fg, bg);
	XRecolorCursor(dpy, gra->drawcursor.handle(), fg, bg);
	XRecolorCursor(dpy, gra->nullcursor.handle(), fg, bg);
	XRecolorCursor(dpy, gra->menucursor.handle(), fg, bg);
	XRecolorCursor(dpy, gra->handcursor.handle(), fg, bg);
	XRecolorCursor(dpy, gra->techcursor.handle(), fg, bg);
	XRecolorCursor(dpy, gra->ibeamcursor.handle(), fg, bg);
	XRecolorCursor(dpy, gra->lrcursor.handle(), fg, bg);
	XRecolorCursor(dpy, gra->udcursor.handle(), fg, bg);
}

/*
 * 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)
	{
		GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
		switch (state)
		{
			case NORMALCURSOR:
				qwin->setCursor( gra->realcursor );
				break;
			case WANTTTYCURSOR:
				qwin->setCursor( gra->nomousecursor );
				break;
			case PENCURSOR:
				qwin->setCursor( gra->drawcursor );
				break;
			case NULLCURSOR:
				qwin->setCursor( gra->nullcursor );
				break;
			case MENUCURSOR:
				qwin->setCursor( gra->menucursor );
				break;
			case HANDCURSOR:
				qwin->setCursor( gra->handcursor );
				break;
			case TECHCURSOR:
				qwin->setCursor( gra->techcursor );
				break;
			case IBEAMCURSOR:
				qwin->setCursor( gra->ibeamcursor );
				break;
			case LRCURSOR:
				qwin->setCursor( gra->lrcursor );
				break;
			case UDCURSOR:
				qwin->setCursor( gra->udcursor );
				break;
		}
	}

#ifdef ETRACE
	etrace(GTRACE, "  setdefaultcursortype: oldcursor=%d newcursor=%d\n",
	       us_cursorstate, state);
#endif
	us_cursorstate = state;
}

/******************** 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_butonname" */
	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 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)
{
	/* This function is not used on Qt */
#ifdef ETRACE
	etrace(GTRACE, "{} waitforbutton: but=-1\n");
#endif
	*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)
{
#ifdef ETRACE
	etrace(GTRACE, "{ modalloop: cursor=%d %s\n",
	       cursor, stateNames[gra_state]);
#endif
	/* change state */
	GRAPHSTATE savedstate = gra_state;
	gra_state = S_MODAL;
#ifdef ETRACE
	etrace(GTRACE, "  gra_state=MODAL\n");
#endif
	gra->charhandler = charhandler;
	gra->buttonhandler = buttonhandler;

	/* set cursor */
	INTBIG oldnormalcursor = us_normalcursor;
	setnormalcursor(cursor);
#if 1
	flushscreen();
#endif

	qApp->enter_loop();

	/* restore cursor */
	setnormalcursor(oldnormalcursor);
	gra_state = savedstate;
#ifdef ETRACE
	etrace(GTRACE, "  gra_state=%s\n", stateNames[gra_state]);
#endif
	gra->charhandler = 0;
	gra->buttonhandler = 0;
#ifdef ETRACE
	etrace(GTRACE, "} modalloop\n");
#endif
}

/*
 * 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)
{
#ifdef ETRACE
	etrace(GTRACE, "{ trackcursor: waitforpush=%d purpose=%d %s\n",
	       waitforpush, purpose, stateNames[gra_state]);
#endif
	/* change state */
	GRAPHSTATE savedstate = gra_state;
	gra_state = S_TRACK;
#ifdef ETRACE
	etrace(GTRACE, "  gra_state=TRACK\n");
#endif
	gra->waitforpush = waitforpush;
	gra->whileup = whileup;
	gra->whendown = whendown;
	gra->eachdown = eachdown;
	gra->eachchar = eachchar;

	/* change the cursor to an appropriate icon */
	INTSML oldnormalcursor = 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);

	flushscreen();

	BOOLEAN keepon = FALSE;
	if (!waitforpush) {
#ifdef ETRACE
	        etrace(GTRACE, "  trackcursor { whendown\n");
#endif
		(*whendown)();
#ifdef ETRACE
		etrace(GTRACE, "  trackcursor } whendown\n");
		etrace(GTRACE, "  trackcursor { eachdown x=%d y=%d\n", gra_cursorx, gra_cursory);
#endif
		keepon = (*eachdown)(gra_cursorx, gra_cursory);
#ifdef ETRACE
		etrace(GTRACE, "  trackcursor } eachdown: keepon=%d el_pleasestp=%d\n",
		       keepon, el_pleasestop);
#endif
	}

	/* enter modal loop */
	if (!keepon && !el_pleasestop) qApp->enter_loop();

	/* inform the user that all is done */
#ifdef ETRACE
	etrace(GTRACE, "  trackcursor { done\n");
#endif
	(*done)();
#ifdef ETRACE
	etrace(GTRACE, "  trackcursor } done\n");
#endif
	flushscreen();

	/* restore the state of the world */
	us_normalcursor = oldnormalcursor;
	setdefaultcursortype(us_normalcursor);
	gra_state = savedstate;
#ifdef ETRACE
	etrace(GTRACE, "  gra_state=%s\n", stateNames[gra_state]);
#endif
	gra->whileup = 0;
	gra->whendown = 0;
	gra->eachdown = 0;
	gra->eachchar = 0;
#ifdef ETRACE
	etrace(GTRACE, "} trackcursor\n");
#endif
}

/*
 * 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;
#ifdef ETRACE
	etrace(GTRACE, "{}readtablet: x=%d y=%d\n", *x, *y);
#endif
}

/*
 * routine to turn off the cursor tracking if it is on
 */
void stoptablet(void)
{
	/* This function is not used on Qt */
#ifdef ETRACE
	etrace(GTRACE, "{ stoptablet\n");
#endif
	if (us_cursorstate != IBEAMCURSOR) setdefaultcursortype(us_normalcursor);
#ifdef ETRACE
	etrace(GTRACE, "} stoptablet\n");
#endif
}

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

/*
 * routine to get the next character from the keyboard
 */
INTSML getnxtchar(INTBIG *special)
{
	/* This function is not used on Qt */
#ifdef ETRACE
	etrace(GTRACE, "{ getnxtchar\n");
#endif
	modalloop(gra_nxtcharhandler, gra_nullbuttonhandler, WANTTTYCURSOR);
#ifdef ETRACE
	etrace(GTRACE, "} getnxtchar: char=%#o special=%#o\n",
	       gra_nxtchar, gra_nxtcharspecial);
#endif
	*special = gra_nxtcharspecial;
	return(gra_nxtchar);
}

static BOOLEAN gra_nxtcharhandler(INTSML chr, INTBIG special)
{
	gra_nxtchar = chr;
	gra_nxtcharspecial = special;
	return(TRUE);
}

static BOOLEAN gra_nullbuttonhandler(INTBIG x, INTBIG y, INTBIG but)
{
	return(FALSE);
}

/* not required with X11 */
void checkforinterrupt(void)
{
#ifdef ETRACE
	// etrace(GTRACE, "{ checkforinterrupt: %s\n", stateNames[gra_state]);
#endif
#if 0
	GRAPHSTATE savedstate = gra_state;
	gra_state = S_CHECKINT;

	if (gra->hasPendingEvents())
		gra->processEvents();
	setdefaultcursortype(NULLCURSOR);

	gra_state = savedstate;
#endif
#ifdef ETRACE
	// etrace(GTRACE, "} checkforinterrupt\n");
#endif
}

/*
 * Routine to return which "bucky bits" are held down (shift, control, etc.)
 */
/*
 * Routine to return which "bucky bits" are held down (shift, control, etc.)
 */
INTBIG getbuckybits(void)
{
	REGISTER INTBIG bits;

	bits = 0;
	if (gra_lastbuttonstate & Qt::ControlButton) bits |= CONTROLDOWN|ACCELERATORDOWN;
	if (gra_lastbuttonstate & Qt::ShiftButton) bits |= SHIFTDOWN;
	if (gra_lastbuttonstate & Qt::AltButton) bits |= OPTALTMETDOWN;
	return(bits);
}

/*
 * routine to tell whether data is waiting at the terminal.  Returns true
 * if data is ready.
 */
BOOLEAN ttydataready(void)
{
	/* This function is not used on Qt */
#ifdef ETRACE
	etrace(GTRACE, "{}ttydataready: result=0\n");
#endif
	return(FALSE);
}

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

char *EApplication::localize (QString qstr)
{
    static QCString str;

    str = qstr.local8Bit();
    const char *s = str;
    return((char*)s);
}

char *EApplication::localizeFilePath (QString filePath, bool addSeparator )
{
    static QCString filePathCS;

    if ( addSeparator && !filePath.isEmpty() && filePath.right(1) != QString::fromLatin1("/") )
	filePath += '/';
    filePath = QDir::convertSeparators( filePath );
    filePathCS = QFile::encodeName( filePath );
    const char *s = filePathCS;
    return((char*)s);
}

/*
 * 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, ""));
}

/*
 * 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 mactype;
	BOOLEAN binary;
	char *extension, *winfilter, *shortname, *longname, temp[300];
	extern COMCOMP us_yesnop;

#ifdef ETRACE
	etrace(GTRACE, "{ fileselect: msg=%s filetype=%d defofile\n", msg, filetype, defofile);
#endif

	QString fileName;

	QWidget *parent =
	  el_curwindowframe != NOWINDOWFRAME ?
	  (QWidget*)el_curwindowframe->qt :
	  gra->messMainWin;
	describefiletype(filetype, &extension, &winfilter, &mactype, &binary, &shortname, &longname);
	sprintf(temp, "%s (%s)", msg, winfilter);
	QString filter = QString::fromLocal8Bit( temp );
	if ((filetype&FILETYPEWRITE) == 0)
	{
		/* input file selection: display the file input dialog box */
		sprintf(temp, _("%s Selection"), msg);
		QString title = QString::fromLocal8Bit( temp );

		fileName = QFileDialog::getOpenFileName(
			QString::null, filter, parent, "FileSelection", title);
	} else
	{
		/* output file selection: display the file input dialog box */
		sprintf(temp, _("%s Selection"), msg);
		QString title = QString::fromLocal8Bit( temp );
		QString startWith = QFile::decodeName( defofile );

		fileName = QFileDialog::getSaveFileName(
			startWith, filter, parent, "File Creation", title);

		/* give warning if creating file that already exists */
		if (!fileName.isEmpty() && QFile::exists( fileName ))
		{
			char fullmsg[300], *pars[5];
			const char *s = EApplication::localize( fileName );
			sprintf(fullmsg, _("File '%s' exists.  Overwrite? "), s);
			INTSML count = ttygetparam(fullmsg, &us_yesnop, 2, pars);
			if (count > 0 && namesamen(pars[0], "no", strlen(pars[0])) == 0)
				fileName = QString::null;
		}
	}
	if (!fileName.isEmpty()) {
		QDir::setCurrent( QFileInfo(fileName).dirPath() );
	}
	strcpy(gra_curpath, EApplication::localizeFilePath( fileName, FALSE) );

#ifdef ETRACE
	etrace(GTRACE, "} fileselect: %s\n", gra_curpath);
#endif
	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 nonzero on error.
 */
INTSML gra_addfiletolist(char *file)
{
	addtostringarray(gra_fileliststringarray, file);
	return(0);
}

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) != 0) 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 INTSML gothome = 0;
	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 == 0)
	{
		/* get name of user */
		tmp = getpwuid(getuid());
		if (tmp == 0) return(line);
		(void)strcpy(home, tmp->pw_dir);
		gothome++;
	}

	/* 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)
{
	char *s = EApplication::localizeFilePath(QDir::currentDirPath(), TRUE);
	return(s);
}

/*
 * Routine to return the home directory (returns 0 if it doesn't exist)
 */
char *hashomedir(void)
{
	char *s = EApplication::localizeFilePath(QDir::homeDirPath(), TRUE);
	return(s);
}

/*
 * 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);
#else
#  ifdef HAVE_GETTIMEOFDAY
	struct timeval tp;
	struct timezone tzp;

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

	return(theTime++);
#  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 ***************************/

void EApplication::toolTimeSlice()
{
    INTSML cmd, but, x, y;

#ifdef ETRACE
    etrace(GTRACE, "{ toolTimeSlice %s %d\n", stateNames[gra_state], gra_stop_dowork);
#endif
    if (gra_state == S_USER) {
        Q_ASSERT( gra_stop_dowork == 0 );
        gra_stop_dowork++;

	/* announce end of broadcast of changes */
	db_endbatch();

	gra_state = S_TOOL;
#if 1
	do {
		tooltimeslice();
	} while((us_state&LANGLOOP) != 0);
#else
	tooltimeslice();
#endif

	gra_state = S_USER;
	db_setcurrenttool(us_tool);
	flushscreen();
	/* first see if there is any pending input */
	el_pleasestop = 0;
	if (us_cursorstate != IBEAMCURSOR) setdefaultcursortype(us_normalcursor);
	gra_handlequeuedframedeletion();
        gra_stop_dowork--;
    }
#ifdef ETRACE
    etrace(GTRACE, "} toolTimeSlice\n");
#endif
}

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);
	}
}

void GraphicsDraw::keepCursorPos( QPoint pos )
{
    gra_cursorx = pos.x();
    gra_cursory = wf->revy - pos.y();
    if (gra_cursory < 0) gra_cursory = 0;

    gra_setcurrentwindowframe(wf);
    us_state &= GOTXY;
}

void GraphicsDraw::buttonPressed( QPoint pos, INTSML but )
{
    keepCursorPos( pos );

    us_state |= DIDINPUT;
    if (gra_state == S_USER) {
        us_ontablet(gra_cursorx, gra_cursory, but);
    } else if (gra_state == S_MODAL && gra->buttonhandler != 0) {
#ifdef ETRACE
        etrace(GTRACE, "  modalloop { buttonhandler: x=%d y=%d but=%d\n", gra_cursorx, gra_cursory, but);
#endif
	BOOLEAN keepon = gra->buttonhandler(gra_cursorx, gra_cursory, but);
#ifdef ETRACE
	etrace(GTRACE, "  modalloop } buttonhandler: keepon=%d el_pleasestop=%d\n",
	       keepon, el_pleasestop);
#endif
	if (keepon || el_pleasestop) gra->exit_loop();
    }
    gra->toolTimeSlice();
}

void GraphicsDraw::mouseDoubleClickEvent( QMouseEvent *e )
{
#ifdef ETRACE
    etrace(GTRACE, "{ GraphicsDraw::mouseDoubleClickEvent %s %d %d,%d %#o\n",
	   stateNames[gra_state], wf->windindex, e->x(), e->y(), e->state());
#endif
    gra_lastbuttonstate = e->stateAfter();
    if (gra_state == S_USER || gra_state == S_MODAL) {
        INTSML but = 0;
	switch (e->button()) {
	case LeftButton: but = 0; break;
	case RightButton: but = 2; break;
	case MidButton: but = 1; break;
	}
	but += REALBUTS*8;
	buttonPressed( e->pos(), but );
    }
    else QWidget::mouseDoubleClickEvent( e );

#ifdef ETRACE
    etrace(GTRACE, "} GraphicsDraw::mouseDoubleClickEvent\n");
#endif
}

void GraphicsDraw::mouseMoveEvent( QMouseEvent *e )
{
	INTSML setcursor, inmenu;
	char *str;
	REGISTER INTBIG x, y;
	REGISTER VARIABLE *var;
	INTBIG lx, hx, ly, hy;
	REGISTER WINDOWPART *win;
	COMMANDBINDING commandbinding;
	static INTSML overrodestatus = 0;

#ifdef ETRACE
	//	etrace(GTRACE, "{ GraphicsDraw::mouseMoveEvent %s %d %d,%d %#o\n",
	//	       stateNames[gra_state], wf->windindex, e->x(), e->y(), e->state());
#endif
	gra_lastbuttonstate = e->stateAfter();
	QWidget::mouseMoveEvent( e );

	gra_cursorx = e->x();
	gra_cursory = wf->revy - e->y();
	if (gra_cursory < 0) gra_cursory = 0;

	if ((e->state() & (LeftButton | RightButton | MidButton)) == 0 || gra->waitforpush)
	{
		/* motion while up considers any window */

		/* report the menu if over one */
		inmenu = 0;
		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 = 1;
							overrodestatus = 1;
						}
						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 = 1;
							overrodestatus = 1;
						}
					}
					us_freebindingparse(&commandbinding);
				}
			}
		}
		if (inmenu == 0 && overrodestatus != 0)
		{
			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 = 0;
		}

		setcursor = 0;
		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 = 1;
				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 = 1;
				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 = 1;
				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 = 1;
				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)
			{
				EDITOR *ed = win->editor;
				if ((ed->state&EDITORTYPE) == PACEDITOR)
				{
					if (gra_cursorx <= win->usehx - SBARWIDTH &&
						gra_cursory >= win->usely + SBARWIDTH &&
						gra_cursory < ed->revy)
					{
						setdefaultcursortype(IBEAMCURSOR);
						setcursor = 1;
						break;
					}
				}
			}
		}
		if (setcursor == 0) setdefaultcursortype(us_normalcursor);
		if (gra_state == S_TRACK) {
#ifdef ETRACE
		  etrace(GTRACE, "  trackcursor { whileup x=%d y=%d\n",
			 gra_cursorx, gra_cursory);
#endif
		  int keepon = (*gra->whileup)(gra_cursorx, gra_cursory);
#ifdef ETRACE
		  etrace(GTRACE, "  trackcursor } whileup: keepon=%d el_pleasestop=%d\n",
			 keepon, el_pleasestop);
#endif
		  if (keepon || el_pleasestop) gra->exit_loop();
		}
	} else
	{
		/* motion while button down: use last window */
	        if (gra_state == S_TRACK) {
#ifdef ETRACE
		  etrace(GTRACE, "  trackcursor { eachdown x=%d y=%d\n",
			 gra_cursorx, gra_cursory);
#endif
		  int keepon = (*gra->eachdown)(gra_cursorx, gra_cursory);
#ifdef ETRACE
		  etrace(GTRACE, "  trackcursor } eachdown: keepon=%d el_pleasestop=%d\n",
			 keepon, el_pleasestop);
#endif
		  flushscreen();
		  if (keepon || el_pleasestop) gra->exit_loop();
		}
	}

#ifdef ETRACE
	//	etrace(GTRACE, "} GraphicsDraw::mouseMoveEvent\n");
#endif
}

void GraphicsDraw::mousePressEvent( QMouseEvent *e )
{
#ifdef ETRACE
    etrace(GTRACE, "{ GraphicsDraw::mousePressEvent %s %d %d,%d %#o\n",
	   stateNames[gra_state], wf->windindex, e->x(), e->y(), e->state());
#endif
    gra_lastbuttonstate = e->stateAfter();
    if (gra_state == S_USER || gra_state == S_MODAL) {
        INTSML but = 0;
	switch (e->button()) {
	case LeftButton: but = 0; break;
	case RightButton: but = 2; break;
	case MidButton: but = 1; break;
	}
	INTSML modifier = 0;
	if (e->state() & ShiftButton) modifier |= 1;
	if (e->state() & ControlButton) modifier |= 2;
	if (e->state() & AltButton) modifier |= 4;
	switch (modifier) {
	case 1: but += REALBUTS*1; break;
	case 2: but += REALBUTS*2; break;
	case 4: but += REALBUTS*3; break;
	case 3: but += REALBUTS*4; break;
	case 5: but += REALBUTS*5; break;
	case 6: but += REALBUTS*6; break;
	case 7: but += REALBUTS*7; break;
	}
	buttonPressed( e->pos(), but );
    } else if (gra_state == S_TRACK) {

        if ((e->state() & (LeftButton | RightButton | MidButton)) == 0) {
#ifdef ETRACE
	    etrace(GTRACE, "  trackcursor { whendown\n");
#endif
	    keepCursorPos( e->pos() );
	    (*gra->whendown)();
#ifdef ETRACE
	    etrace(GTRACE, "  trackcursor } whendown\n");
	    etrace(GTRACE, "  trackcursor { eachdown x=%d y=%d\n", gra_cursorx, gra_cursory);
#endif
	    bool keepon = (*gra->eachdown)(gra_cursorx, gra_cursory);
#ifdef ETRACE
	    etrace(GTRACE, "  trackcursor } eachdown: keepon=%d el_pleasestop=%d\n", keepon, el_pleasestop);
#endif
	    gra->waitforpush = 0;
	    if (keepon || el_pleasestop) gra->exit_loop();
	}
	us_state |= DIDINPUT;
    }

#ifdef ETRACE
	etrace(GTRACE, "} GraphicsDraw::mousePressEvent\n");
#endif
}

void GraphicsDraw::mouseReleaseEvent( QMouseEvent *e )
{
#ifdef ETRACE
	etrace(GTRACE, "{ GraphicsDraw::mouseReleaseEvent %s %d %d,%d %#o\n",
	       stateNames[gra_state], wf->windindex, e->x(), e->y(), e->state());
#endif
	gra_lastbuttonstate = e->stateAfter();
	keepCursorPos( e->pos() );

	if (gra_state == S_TRACK && !gra->waitforpush) {
	  if ((e->stateAfter() & (LeftButton | RightButton | MidButton)) == 0)
	    gra->exit_loop();
	}
	us_state |= DIDINPUT;
#ifdef ETRACE
	etrace(GTRACE, "} GraphicsDraw::mouseReleaseEvent\n");
#endif
}

void GraphicsDraw::paintEvent( QPaintEvent * e)
{
    QRect r = e->rect();
#ifdef ETRACE
    etrace(GTRACE, "{ GraphicsDraw::paintEvent %d %s %d,%d %d,%d\n",
	   wf->windindex, stateNames[gra_state],
	   r.left(), r.top(), r.right(), r.bottom());
#endif
#ifdef X11PAINT
    REGISTER INTBIG x, y;
    REGISTER unsigned char *srow, *drow, *sval;

    /* get bounds of display update */
    int lx = r.left();
    int hx = r.right() + 1;
    int ly = r.top();
    int hy = r.bottom() + 1;
    if (lx < 0) lx = 0;
    if (hx > wf->swid) hx = wf->swid;
    if (ly < 0) ly = 0;
    if (hy > wf->shei) hy = wf->shei;

    /* copy from offscreen buffer to Ximage while doing color mapping */
    XImage *xi = XGetImage( gra->dpy(), pixmap.handle(), lx, ly, hx-lx, hy-ly, 0xff, ZPixmap );
    uchar *row;
    switch (pixmap.depth())
    {
	    case 8:
		    for(y=ly; y<hy; y++)
		    {
			    row = ((uchar*)(xi->data)) + xi->bytes_per_line*(y-ly);
			    for(x=lx; x<hx; x++)
			    {
			        *row++ = colorvalue[*row];
			    }
		    }
		    break;

	    case 16:
		    for(y=ly; y<hy; y++)
		    {
			    row = ((uchar*)(xi->data)) + xi->bytes_per_line*(y-ly);
			    for(x=lx; x<hx; x++)
			    {
			        sval = (uchar*)&colorvalue[*row];
				*row++ = sval[0];
				*row++ = sval[1];
			    }
		    }
		    break;

	    case 24:
	    case 32:
		    for(y=ly; y<hy; y++)
		    {
			    row = ((uchar*)(xi->data)) + xi->bytes_per_line*(y-ly);
			    for(x=lx; x<hx; x++)
			    {
			        sval = (uchar*)&colorvalue[*row];
#ifdef SWAPPED
				*row++ = sval[0];
				*row++ = sval[1];
				*row++ = sval[2];
#else
				*row++ = sval[2];
				*row++ = sval[1];
				*row++ = sval[0];
#endif
				*row++ = sval[3];
			    }
 		    }
		    break;
    }

    /* copy XImage to the screen */
    GC gc = XCreateGC(gra->dpy(), winId(), 0, NULL);
    XPutImage(gra->dpy(), winId(), gc, xi, 0, 0, lx, ly, hx-lx, hy-ly);
    XFreeGC(gra->dpy(), gc);
    XDestroyImage( xi );
#else /* X11PAINT */
    QPainter p( this );
    p.drawImage( r.topLeft(), image, r );
#endif /* not X11PAINT */
#ifdef ETRACE
    etrace(GTRACE, "} GraphicsDraw::paintEvent\n");
#endif
}


void GraphicsDraw::resizeEvent( QResizeEvent *e )
{
    static INTSML inrepaint = 0;

#ifdef ETRACE
    etrace(GTRACE, "{ GraphicsDraw::resizeEvent %s OldSize=%d,%d NewSize=%d,%d\n",
	   stateNames[gra_state],
	   e->oldSize().width(),
	   e->oldSize().height(),
	   e->size().width(),
	   e->size().height());
#endif
    if (width() != wf->swid || height() != wf->shei && inrepaint == 0) {
	inrepaint = 1;

#ifdef ETRACE
	etrace(GTRACE, "{ inrepaint: wf=%d\n", wf->windindex);
#endif
	recalcSize();

	if (wf->floating)
	{
		us_startbatch(NOTOOL, FALSE);
		us_drawmenu(1, wf);
		us_endbatch();
	} else
	{
		/* mark start of redisplay */
		us_startbatch(NOTOOL, FALSE);

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

		/* rewrite status area */
		us_drawmenu(1, wf);

		/* restore highlighting */
		us_pophighlight(FALSE);

		/* finish drawing */
		us_endbatch();

		us_redostatus(wf);

		/* describe this change */
		setactivity(_("Window Resize"));
	}
	inrepaint = 0;
#ifdef ETRACE
	etrace(GTRACE, "} inrepaint\n");
#endif
    }
    QWidget::resizeEvent( e );
#ifdef ETRACE
    etrace(GTRACE, "} GraphicsDraw::resizeEvent\n");
#endif
#if 0
			wf = gra_getcurrentwindowframe(w);
			if (wf == NOWINDOWFRAME) return;

			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->shei)
			{
				/* window changed size */
				gra_addeventtoqueue(WINDOWSIZE, 0, wf->windindex,
					((xwa.width & 0xFFFF) << 16) | (xwa.height & 0xFFFF));
				gra_repaint(wf, 1);
			}

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

#endif
}

void GraphicsDraw::wheelEvent( QWheelEvent * e)
{
#ifdef ETRACE
    etrace(GTRACE, "{ GraphicsDraw::wheelEvent %s %d %d,%d %#o\n",
	   stateNames[gra_state], wf->windindex, e->x(), e->y(), e->state());
#endif
    if (gra_state == S_USER || gra_state == S_MODAL) {
        gra_setcurrentwindowframe(wf);
	INTSML but = e->delta() > 0 ? 4 : 5;
	INTSML modifier = 0;
	if (e->state() & ShiftButton) modifier |= 1;
	if (e->state() & ControlButton) modifier |= 2;
	if (e->state() & AltButton) modifier |= 4;
	switch (modifier) {
	case 1: but += REALBUTS*1; break;
	case 2: but += REALBUTS*2; break;
	case 4: but += REALBUTS*3; break;
	case 3: but += REALBUTS*4; break;
	case 5: but += REALBUTS*5; break;
	case 6: but += REALBUTS*6; break;
	case 7: but += REALBUTS*7; break;
	}
	buttonPressed( e->pos(), but );
    } else QWidget::wheelEvent( e );

#ifdef ETRACE
	etrace(GTRACE, "} GraphicsDraw::wheelEvent\n");
#endif
}

/*
 * routine to load the global "el_curwindowframe" and "el_topwindowpart".
 */
static void gra_setcurrentwindowframe(WINDOWFRAME *wf)
{
	WINDOWPART *w;

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

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

	/* 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);
					break;
				}
			}
		}
	}
}

int EApplication::translatekey(QKeyEvent *e, INTBIG *special)
{
	int state;
	char *par[1];

#ifdef ETRACE
	etrace(GTRACE, "  translatekey: ascii=%#o key=%#o state=%x accepted=%d\n",
	       e->ascii(), e->key(), e->state(), e->isAccepted());
#endif
	gra_lastbuttonstate = e->stateAfter();
	*special = 0;
#if 1
	if (e->ascii() >= ' ' && e->ascii() < 0177 && (e->state() & ControlButton) == 0) return(e->ascii());
#endif
	if (e->key() > 0 && e->key() < 0200) state = e->key(); else
	{
		switch (e->key())
		{
			case Key_BackSpace:state = BACKSPACEKEY;   break;
			case Key_Tab:      state = 011;            break;
			  //case XK_Linefeed:  state = 012;            break;
			case Key_Return:   state = 015;            break;
			case Key_Escape:   state = ESCKEY;         break;
			case Key_Delete:   state = 0177;           break;
			case Key_Enter:    state = 015;            break;

			case Key_End:
				/* shift to end of messages window */
#if 0
				tp = XmTextGetLastPosition(gra_messageswidget);
				XmTextSetSelection(gra_messageswidget, tp, tp, 0);
				XmTextSetInsertionPosition(gra_messageswidget, tp);
				XmTextShowPosition(gra_messageswidget, tp);
#endif
				e->accept();
#ifdef ETRACE
				etrace(GTRACE, "  EApplication::translatekey } special\n");
#endif
				return(0);
			case Key_F14:
				us_undo(0, par);
				e->accept();
#ifdef ETRACE
				etrace(GTRACE, "  EApplication::translatekey } special\n");
#endif
				return(0);
			case Key_F16:
				par[0] = "copy";
				us_text(1, par);
				e->accept();
#ifdef ETRACE
				etrace(GTRACE, "  EApplication::translatekey } special\n");
#endif
				return(0);
			case Key_F18:
				par[0] = "paste";
				us_text(1, par);
				e->accept();
#ifdef ETRACE
				etrace(GTRACE, "  EApplication::translatekey } special\n");
#endif
				return(0);
			case Key_F20:
				par[0] = "cut";
				us_text(1, par);
				e->accept();
#ifdef ETRACE
				etrace(GTRACE, "  EApplication::translatekey } special\n");
#endif
				return(0);

			case Key_Left:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYARROWL<<SPECIALKEYSH);
				if ((e->state() & ShiftButton) != 0) *special |= SHIFTDOWN;
				break;
			case Key_Right:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYARROWR<<SPECIALKEYSH);
				if ((e->state() & ShiftButton) != 0) *special |= SHIFTDOWN;
				break;
			case Key_Up:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYARROWU<<SPECIALKEYSH);
				if ((e->state() & ShiftButton) != 0) *special |= SHIFTDOWN;
				break;
			case Key_Down:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYARROWD<<SPECIALKEYSH);
				if ((e->state() & ShiftButton) != 0) *special |= SHIFTDOWN;
				break;

			case Key_F1:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF1<<SPECIALKEYSH);
				break;
			case Key_F2:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF2<<SPECIALKEYSH);
				break;
			case Key_F3:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF3<<SPECIALKEYSH);
				break;
			case Key_F4:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF4<<SPECIALKEYSH);
				break;
			case Key_F5:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF5<<SPECIALKEYSH);
				break;
			case Key_F6:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF6<<SPECIALKEYSH);
				break;
			case Key_F7:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF7<<SPECIALKEYSH);
				break;
			case Key_F8:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF8<<SPECIALKEYSH);
				break;
			case Key_F9:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF9<<SPECIALKEYSH);
				break;
			case Key_F10:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF10<<SPECIALKEYSH);
				break;
			case Key_F11:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF11<<SPECIALKEYSH);
				break;
			case Key_F12:
				state = 0;
				*special = SPECIALKEYDOWN|(SPECIALKEYF12<<SPECIALKEYSH);
				break;

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

	if ((e->state() & ControlButton) != 0)
	{
		if (gra_messages_typingin == 0)
		{
			*special |= ACCELERATORDOWN;
			state = tolower(state);
		} else
		{
			if (state >= 'a' && state <= 'z') state -= 'a' - 1; else
				if (state >= 'A' && state <= 'Z') state -= 'A' - 1;
		}
	}
	return(state);
}

void GraphicsDraw::recalcSize()
{
	INTSML i;

	/* allocate pixmap for the offscreen buffer */
	if (painter.isActive()) painter.end();
	pixmap.resize( width(), height() );
	painter.begin( &pixmap );
	pixmap.fill( color0 );

#ifdef ETRACE
	etrace(GTRACE, "  gra_recalcsize: wf=%d swid=%d shei=%d\n",
	       wf->windindex, width(), height());
#endif
	wf->swid = width();
	wf->shei = 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 */

/*
 * routine to create a session logging file
 */
void logstartrecord(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{}logstartrecord\n");
#endif
}

/*
 * 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)
{
#ifdef ETRACE
	etrace(GTRACE, "{}logplayback: file=%s result=1\n", file);
#endif
	return(TRUE);
}

/*
 * routine to terminate session logging
 */
void logfinishrecord(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{}logfinishrecord\n");
#endif
}

/****************************** 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)
{
	INTBIG i, j, submenuindex, submenus;
	POPUPMENUITEM *mi, *submi;
	POPUPMENU *menu, **subpmlist;
	REGISTER WINDOWFRAME *wf;
	REGISTER USERCOM *uc;
	INTBIG menuresult;

	menu = *menuptr;
	wf = el_curwindowframe;
	QMainWindow *qwin = wf != NOWINDOWFRAME ? (QMainWindow*)wf->qt : gra->messMainWin;
	QPopupMenu qmenu( qwin );
	if (header)
	{
		header = 2;
		qmenu.insertItem( QString::fromLocal8Bit( menu->header ), -1 );
		qmenu.insertSeparator();
	}

	/* 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);
	}

	/* load the menus */
	submenus = 0;
	for(j=0; j<menu->total; j++)
	{
		mi = &menu->list[j];
		mi->changed = FALSE;
		if (*mi->attribute == 0)
		{
			qmenu.insertSeparator();
		} else
		{
			uc = mi->response;
			if (uc != NOUSERCOM && uc->menu != NOPOPUPMENU)
			{
				QPopupMenu *submenu = new QPopupMenu( qwin );
				if (submenu == 0) return(-1);
				qmenu.insertItem( QString::fromLocal8Bit( mi->attribute ), submenu, j );
				subpmlist[submenus] = uc->menu;
				submenus++;
				for(i=0; i<uc->menu->total; i++)
				{
					submi = &uc->menu->list[i];
					submenu->insertItem( QString::fromLocal8Bit( submi->attribute ), (submenus<<16) | i );
				}
			} else
			{
				qmenu.insertItem( QString::fromLocal8Bit( mi->attribute ), j );
			}
		}
	}

	QPoint pos;
	if (left >= 0 && top >= 0 && el_curwindowframe != NOWINDOWFRAME)
	{
		GraphicsMainWindow *qwin = (GraphicsMainWindow*)el_curwindowframe->qt;
		pos = qwin->draw->mapToGlobal( QPoint( left, qwin->wf->revy - top ) );
	}
	else pos = QCursor::pos();
	menuresult = qmenu.exec( pos );
	if (menuresult >= 0)
	{
		submenuindex = menuresult >> 16;
		if (submenuindex != 0) *menuptr = subpmlist[submenuindex-1];
	}
	if (submenus > 0)
	{
		efree((char *)subpmlist);
	}
	if (menuresult < 0) return(-1);
	return(menuresult & 0xFFFF);
}

BOOLEAN nativemenuload(INTBIG count, char *par[])
{
	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;
		/* load menus into this window */
		GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
		qwin->pulldownmenuload();
	}
	return(FALSE);
}

void GraphicsMainWindow::pulldownmenuload()
{
	menuBar()->clear();
	for(int i=0; i<gra_pulldownmenucount_; i++)
	{
		POPUPMENU *pm = gra_pulldowns_[i];
		if (pm == NOPOPUPMENU) continue;
		INTSML menuindex = pulldownindex(pm);
		if (menuindex < 0) continue;
		QPopupMenu *menu = pulldownmenus[menuindex];
		if (menu != NULL)
			menuBar()->insertItem( QString::fromLocal8Bit( pm->header ), menu );
	}
}

void nativemenurename(POPUPMENU *pm, INTBIG pindex)
{
	REGISTER WINDOWFRAME *wf;

	/* build the pulldown menu bar */
	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
	{
		if (wf->floating) continue;
		/* load menus into this window */
		GraphicsMainWindow *qwin = (GraphicsMainWindow*)wf->qt;
		qwin->nativemenurename( pm, pindex );
	}
}

void GraphicsMainWindow::nativemenurename(POPUPMENU *pm, INTBIG pindex)
{
	for(int i=0; i<pulldownmenucount; i++)
		if (pulldowns[i] == pm)
	{
		QPopupMenu *menu = pulldownmenus[i];
		POPUPMENUITEM *mi = &pm->list[pindex];
		USERCOM *uc = mi->response;
		int id = (i << 16) | pindex;

		if (uc->active < 0)
		{
			if (*mi->attribute == 0)
			{
				/* separator */
				ttyputerr(_("Can't rename to separator"));
			} else
			{
				/* dimmed item (DIM IT!!!) */
				menu->changeItem( QString::fromLocal8Bit( mi->attribute ), id );
				menu->setItemEnabled( id, FALSE );
			}
		} else
		{
			menu->setItemEnabled( id, TRUE );
		}

		/* see if there is a check */
		int checked = -1;
		char myline[256];
		strcpy( myline, mi->attribute );
		int 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 */
		QString metakey;
		char *pt;
		for(pt = myline; *pt != 0; pt++) if (*pt == '/' || *pt == '\\') break;
		if (*pt != 0)
		{
			INTSML key;
			INTBIG special;
			(void)us_getboundkey(pt, &key, &special);
			metakey = describeboundkey(key, special);
			*pt = 0;
		}
#ifdef ETRACE
		etrace(GTRACE, "  rename menu entry '%s' / %s\n", myline, EApplication::localize( metakey ) );
#endif
		menu->changeItem( QString::fromLocal8Bit( myline ) , id );
		if (checked != -1) menu->setItemChecked( id, checked > 0 );
		menu->setAccel( QAccel::stringToKey( metakey ), id );
	}
}

/*
 * Routine to create a pulldown menu from popup menu "pm".
 * Returns an index to the table of pulldown menus (-1 on error).
 */
INTSML GraphicsMainWindow::pulldownindex(POPUPMENU *pm)
{
	REGISTER INTSML i, pindex;
	QPopupMenu **newpulldownmenus;
	POPUPMENU **newpulldowns;

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

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

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

	pulldownmenus = newpulldownmenus;
	pulldowns = newpulldowns;

	pindex = pulldownmenucount++;
	pulldownmenus[pindex] = makepdmenu(pm, pindex);
	if (pulldownmenus[pindex] == 0)
		return(-1);
	pulldowns[pindex] = pm;
	return(pindex);
}

/*
 * Routine to create pulldown menu number "value" from the popup menu in "pm" and return
 * the menu handle.
 */
QPopupMenu *GraphicsMainWindow::makepdmenu(POPUPMENU *pm, INTSML value)
{
	char *pt;

	/* build the actual menu */
	QPopupMenu *menu = new QPopupMenu( this );
	connect( menu, SIGNAL(activated(int)), SLOT(menuAction(int)));
	for(int i=0; i<pm->total; i++)
	{
		POPUPMENUITEM *mi = &pm->list[i];
		USERCOM *uc = mi->response;
		int id = value*(1 << 16) + i;
		if (uc->active < 0)
		{
			if (*mi->attribute == 0)
			{
				/* separator */
				menu->insertSeparator();
			} else
			{
				/* dimmed item (DIM IT!!!) */
				menu->insertItem( QString::fromLocal8Bit( mi->attribute ), id );
				menu->setItemEnabled( id, FALSE );
			}
			continue;
		}

		/* see if this command is another menu */
		if (uc->menu != NOPOPUPMENU)
		{
			int submenuindex = pulldownindex(uc->menu);
			QPopupMenu *submenu = pulldownmenus[submenuindex];
			if (submenu == 0) return(0);
			menu->insertItem( QString::fromLocal8Bit( mi->attribute ), submenu, id );
			continue;
		}

		/* see if there is a check */
		int checked = -1;
		char myline[256];
		strcpy( myline, mi->attribute );
		int 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 */
		QString metakey;
		for(pt = myline; *pt != 0; pt++) if (*pt == '/' || *pt == '\\') break;
		if (*pt != 0)
		{
			INTSML key;
			INTBIG special;
			(void)us_getboundkey(pt, &key, &special);
			metakey = describeboundkey(key, special);
			*pt = 0;
		}
#ifdef ETRACE
		etrace(GTRACE, "  set menu entry '%s' / %s\n", myline, EApplication::localize( metakey ) );
#endif
		menu->insertItem( QString::fromLocal8Bit( myline ) , id );
		if (checked != -1) menu->setItemChecked( id, checked > 0 );
		menu->setAccel( QAccel::stringToKey( metakey ), id );
	}
	return(menu);
}

QString GraphicsMainWindow::describeboundkey(INTSML key, INTBIG special)
{
	char buf[2];
	char *s = buf;

	if ((special&SPECIALKEYDOWN) != 0)
	{
		switch ((special&SPECIALKEY)>>SPECIALKEYSH)
		{
			case SPECIALKEYF1:  s = "F1"; break;
			case SPECIALKEYF2:  s = "F2"; break;
			case SPECIALKEYF3:  s = "F3"; break;
			case SPECIALKEYF4:  s = "F4"; break;
			case SPECIALKEYF5:  s = "F5"; break;
			case SPECIALKEYF6:  s = "F6"; break;
			case SPECIALKEYF7:  s = "F7"; break;
			case SPECIALKEYF8:  s = "F8"; break;
			case SPECIALKEYF9:  s = "F9"; break;
			case SPECIALKEYF10: s = "F10"; break;
			case SPECIALKEYF11: s = "F11"; break;
			case SPECIALKEYF12: s = "F12"; break;
			case SPECIALKEYARROWL: s = "Left"; break;
			case SPECIALKEYARROWR: s = "Right"; break;
			case SPECIALKEYARROWU: s = "Up"; break;
			case SPECIALKEYARROWD: s = "Down"; break;
                        case SPECIALEND: s = "End"; break;
                        case SPECIALCUT: s = "F20"; break;
                        case SPECIALCOPY: s = "F16"; break;
                        case SPECIALPASTE: s = "F18"; break;
                        case SPECIALUNDO: s = "F14"; break;
			default: s[0] = 0;
		}
	} else
	{
		s[0] = key;
		s[1] = 0;
	}
	char metaline[100];
	sprintf(metaline, "%s%s%s%s",
		(special&ACCELERATORDOWN ? "Ctrl+" : ""),
		(special&SHIFTDOWN ? "Shift+" : ""),
		(special&OPTALTMETDOWN ? "Alt+" : ""),
		s);
	return QString::fromLocal8Bit( metaline );
}

void GraphicsMainWindow::menuAction( int id )
{
	/* handle menu events */
	INTSML low = id >> 16;
	INTSML high = id & 0xFFFF;
	BOOLEAN verbose;
	static INTSML ntry = 0;
#ifdef ETRACE
	etrace(GTRACE, "{ menuAction: %s %d %d\n", stateNames[gra_state], low, high);
#endif
	if (gra_state != S_USER && ntry < 2)
	{
		ttyputmsg(_("menu disabled in input mode"));
		ttybeep(1);
#ifdef ETRACE
		etrace(GTRACE, "} menuAction: ignored\n");
#endif
		ntry++;
		return;	
	}
	ntry = 0;
	POPUPMENU *pm = pulldowns[low];
	if (high >= 0 && high < pm->total)
	{
		us_state |= DIDINPUT;
		us_state &= ~GOTXY;
#ifdef ETRACE
		etrace(GTRACE, "  menuAction: DIDINPUT GOTXY\n");
#endif
		setdefaultcursortype(NULLCURSOR);
		us_forceeditchanges();
		if ((us_tool->toolstate&ECHOBIND) != 0) verbose = TRUE; else
			verbose = FALSE;
		us_execute(pm->list[high].response, verbose, TRUE, TRUE);
		db_setcurrenttool(us_tool);
		setactivity(pm->list[high].attribute);
		gra->toolTimeSlice();
	}
#ifdef ETRACE
	etrace(GTRACE, "}  menuAction\n");
#endif
}
