ggi.src

        Copyright (c) Kapteyn Laboratorium Groningen 2002
        All Rights Reserved.

Name:         ggi.src
Creator:      terlouw
Host:         backlund
Date:         Mar 11, 2002
Contents:     Makefile.uti ggi.c ggiplot.c ggiprompter.c ggiplotprompter.c
              ggiverify.c ggiplotcoledit.c ggifilebrowser.c ggiinset.c ggihelpmenu.c
              ggiheaderbutton.c ggikeyident.c ggicolormenu.c ggitextmenu.c ggioptionsshell.c
              ggi.h ggidisplay.h ggiwindow.h ggiwidget.h ggicontext.h
              colormaps.H GgiInternal.H ggi.dc2 ggi.doc GgiActivate.dc2
              GgiAlignLabel.dc2 GgiAutoLayout.dc2 GgiButton.dc2 GgiCanvas.dc2 GgiConfigure.dc2
              GgiDeactivate.dc2 GgiDelete.dc2 GgiDisplay.dc3 GgiGauge.dc2 GgiSetHeight.dc2
              GgiHelpText.dc2 GgiLabel.dc2 GgiList.dc2 GgiListColumns.dc2 GgiListLabels.dc2
              GgiLogo.dc2 GgiMenu.dc2 GgiMenuLabels.dc2 GgiPostponeRealize.dc2 GgiRealize.dc2
              GgiIsRealized.dc2 GgiSetCleanup.dc2 GgiSetLabel.dc2 GgiSetPosition.dc2 GgiSetRange.dc2
              GgiSetResources.dc2 GgiShowDoc.dc2 GgiTextField.dc2 GgiSetWidth.dc2 GgiViewport.dc2
              GgiWindow.dc3 GgiWidget.dc3 GgiContext.dc3 GgiPlotField.dc2 GgiPlotXor.dc2
              GgiWidth.dc2 GgiHeight.dc2 ScheduleGgiPlotCursor.dc2 DescheduleGgiPlotCursor.dc2 GgiSetBitmap.dc2
              GgiShell.dc2 GgiUseShell.dc2 GgiShowShell.dc2 GgiForm.dc2 GgiDialog.dc2
              GgiHandleEvents.dc2 GgiPlotColors.dc2 GgiDial.dc2 GgiPlotFrames.dc2 GgiPlotRecord.dc2
              GgiPlotShow.dc2 GgiSetFormat.dc2 GgiSetCircular.dc2 GgiPrompter.dc2 GgiPlotPrompter.dc2
              GgiSetKeyword.dc2 GgiProgress.dc2 GgiVerify.dc2 GgiPlotMapColors.dc2 GgiPlotColorEditor.dc2
              GgiFileBrowser.dc2 GgiInset.dc2 GgiEditor.dc2 GgiEditorSetSource.dc2 GgiEditorSource.dc2
              GgiEditorSave.dc2 GgiEditorSaveAs.dc2 GgiEditorSearch.dc2 GgiHelpMenu.dc2 GgiHeaderButton.dc2
              GgiKeyIdent.dc3 GgiColorMenu.dc2 GgiTextMenu.dc2 GgiSetDisplay.dc2 ggihelp.doc
              GgiOptionsShell.dc3 GgiOptionsButton.dc2 GgiSetBtnKey.dc2

#>            Makefile.uti
SHELL  = /bin/sh

pack::
	$$gip_sys/pack.csh ggi.src Makefile.uti \
	ggi.c ggiplot.c ggiprompter.c ggiplotprompter.c \
	ggiverify.c ggiplotcoledit.c ggifilebrowser.c ggiinset.c ggihelpmenu.c \
        ggiheaderbutton.c ggikeyident.c ggicolormenu.c ggitextmenu.c \
        ggioptionsshell.c \
	ggi.h ggidisplay.h ggiwindow.h ggiwidget.h ggicontext.h \
	colormaps.H GgiInternal.H \
	ggi.dc2 ggi.doc \
	GgiActivate.dc2 GgiAlignLabel.dc2 GgiAutoLayout.dc2 \
	GgiButton.dc2 GgiCanvas.dc2 GgiConfigure.dc2 \
	GgiDeactivate.dc2 GgiDelete.dc2 GgiDisplay.dc3 \
	GgiGauge.dc2 GgiSetHeight.dc2 GgiHelpText.dc2 \
	GgiLabel.dc2 GgiList.dc2 GgiListColumns.dc2 GgiListLabels.dc2 \
	GgiLogo.dc2 \
	GgiMenu.dc2 GgiMenuLabels.dc2 GgiPostponeRealize.dc2 GgiRealize.dc2 \
	GgiIsRealized.dc2 GgiSetCleanup.dc2 GgiSetLabel.dc2 \
	GgiSetPosition.dc2 GgiSetRange.dc2 GgiSetResources.dc2 \
	GgiShowDoc.dc2 GgiTextField.dc2 GgiSetWidth.dc2 \
	GgiViewport.dc2 GgiWindow.dc3 GgiWidget.dc3 GgiContext.dc3 \
	GgiPlotField.dc2 GgiPlotXor.dc2 \
	GgiWidth.dc2 GgiHeight.dc2 \
	ScheduleGgiPlotCursor.dc2 DescheduleGgiPlotCursor.dc2 \
	GgiSetBitmap.dc2 GgiShell.dc2 GgiUseShell.dc2 GgiShowShell.dc2 \
	GgiForm.dc2 GgiDialog.dc2 GgiHandleEvents.dc2 GgiPlotColors.dc2 \
	GgiDial.dc2 GgiPlotFrames.dc2 GgiPlotRecord.dc2 GgiPlotShow.dc2 \
	GgiSetFormat.dc2 GgiSetCircular.dc2 GgiPrompter.dc2 \
	GgiPlotPrompter.dc2 GgiSetKeyword.dc2 GgiProgress.dc2 GgiVerify.dc2 \
	GgiPlotMapColors.dc2 GgiPlotColorEditor.dc2 GgiFileBrowser.dc2 \
	GgiInset.dc2 GgiEditor.dc2 GgiEditorSetSource.dc2 GgiEditorSource.dc2 \
	GgiEditorSave.dc2 GgiEditorSaveAs.dc2 GgiEditorSearch.dc2 \
	GgiHelpMenu.dc2 GgiHeaderButton.dc2 GgiKeyIdent.dc3 \
	GgiColorMenu.dc2 GgiTextMenu.dc2 GgiSetDisplay.dc2 ggihelp.doc \
	GgiOptionsShell.dc3 GgiOptionsButton.dc2 GgiSetBtnKey.dc2
#<

#>            ggi.c
/* ggi.c -X
                              COPYRIGHT (c) 1998
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw.
*/

/* ==================== default configuration values ======================= */

#define WIDTH         400         /* Window width */
#define HEIGHT        150         /* Window height (when AutoLayout==FALSE) */
#define MENUHEIGHT     35         /* Menu bar height */
#define TEXTHEIGHT    200         /* Text input area height */
#define BUTTONHEIGHT   35         /* Button bar height */
#define MINLABWIDTH    60         /* Minimum width of keyword labels */
#define APPCLASS      "XGgi"      /* Application class name */
#define APPNAME       ""          /* Application name */
#define HELPDELAY     700         /* Pop-up delay for help texts */
#define AUTOLAYOUT    TRUE        /* Automatic lay-out of elements */
#define REALIZELATER  FALSE       /* Postpone realization */
#define TEXTLENGTH    256         /* Text length for a.o. TextField elements */
#define VALLENGTH      32         /* Text length for valuator elements */
#define FMTLENGTH      16         /* Text length for valuator elements */
#define DIALSTEPS     100         /* Rheostat maximum value */
/* ------------------------------------------------------------------------- */

#define XT_TRANSLATE    1         /* use XtTranslateCoords ?  */
#define HELP_PTR_REL    1         /* help pop-ups pointer-relative ? */
#define TOPPOP          1         /* use top-level shell for pop-ups */
#define SLIDER2         1         /* use Slider2 widget rather than Scrollbar */

#include "stddef.h"
#include <sys/time.h>
#include "stdlib.h"
#include "ctype.h"
#include "gipsyc.h"
#include "stdarg.h"
#include "stdio.h"
#include "math.h"
#include "keyevents.h"
#include "srvreq.h"
#include "userfio.h"
#include "decodereal.h"
#include "setfblank.h"
#include "ggi.h"
#include "ggidisplay.h"
#include "ggiwindow.h"
#include "ggiwidget.h"
#include "ggicontext.h"

#include <X11/Intrinsic.h>      /* Intrinsics Definitions */
#include <X11/StringDefs.h>     /* Standard Name-String definitions */
#include <X11/Shell.h>
#include <X11/cursorfont.h>

#include <X11/Xaw/Box.h>        /* Athena Box Widget */
#include <X11/Xaw/Paned.h>      /* Athena Paned Widget */
#include <X11/Xaw/Viewport.h>   /* Athena Viewport Widget */
#include <X11/Xaw/Form.h>       /* Athena Form Widget */
#include <X11/Xaw/Label.h>      /* Athena Label Widget */
#include <X11/Xaw/Toggle.h>     /* Athena Toggle Widget */
#include <X11/Xaw/AsciiText.h>  /* Athena Ascii Text Widget */
#include <X11/Xaw/MenuButton.h> /* Athena Menu Button Widget */
#include <X11/Xaw/SimpleMenu.h> /* Athena Simple Menu Widget */
#include <X11/Xaw/Sme.h>        /* Athena Menu Spacer Widget */
#include <X11/Xaw/SmeBSB.h>     /* Athena Menu Entry Widget */
#include <X11/Xaw/SmeLine.h>    /* Athena Menu Separator Widget */
#include <X11/Xaw/List.h>       /* Athena List Widget */
#include <X11/Xaw/Scrollbar.h>  /* Athena Scrollbar Widget */
#include "TextField.h"          /* Robert W. McMullen's TextField widget */
#include "Slider2.h"            /* Aziz Boxwala's Slider2 widget */
#include "Rheostat.h"           /* Joe English' Rheostat widget */
#include "GgiInternal.H"        /* Visual info, etc. */

#include "eventsxt.h"

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

#define XTJUST(al) ((al)==ggiLeft?XtJustifyLeft:((al)==ggiCenter?XtJustifyCenter:XtJustifyRight))

typedef enum {TEXT,BUTTON,MENU,GAUGE,LABEL,CANVAS,SHELL,FORM,DIAL,PROGRESS,
              VWPORT,LIST,EDITOR,NCLASSES} Class;

typedef void (*DeleteProc)(ident id);
typedef void (*SetLabelProc)(ident id, char *label, int width);
typedef void (*AlignProc)(ident id, ggiAlign al);
typedef void (*SetRangeProc)(ident id, float max, float min);
typedef void (*SetBitmapProc)(ident id, int nx, int ny, char *bits);
typedef int  (*GetWidthProc)(ident id);
typedef int  (*GetHeightProc)(ident id);
typedef void (*SetFollowProc)(ident id, bool follow);
typedef void (*SetFormatProc)(ident id, char *format);
typedef void (*SetCircProc)(ident id, bool circular);

typedef struct {
   DeleteProc    delete;     /* delete method */
   KeyeventProc  keychanged; /* keyword changed method */
   SetLabelProc  setlabel;   /* descriptive label change method */
   AlignProc     align;      /* label alignment method */
   SetRangeProc  setrange;   /* set data range method */
   SetBitmapProc setbitmap;  /* set bitmap method */
   GetWidthProc  getwidth;   /* get width method */
   GetHeightProc getheight;  /* get height method */
   SetFollowProc setfollow;  /* enable/disable keyword following */
   SetFormatProc setformat;  /* set valuator's output format */
   SetCircProc   setcirc;    /* set valuator's circular mode */
} _Methods, *Methods;

static Methods methods;

typedef struct {
   Widget box;              /* container for keyword label and text widget */
   Widget key;              /* keyword label */
   Widget inp;              /* text input widget */
   int    length;           /* string length */
   int    position;         /* insertion position, -1: position not preserved */
   char   *curstr;          /* string being manipulated by text widget */
   char   *prvstr;          /* previous string contents */
} _TextInput, *TextInput;

typedef struct {
   Widget   inp;            /* toggle widget */
} _ButtonInput, *ButtonInput;

typedef struct _MenuEntry {
   struct _MenuEntry *next;
   int    num;              /* menu entry ordinal */
   char   *key;             /* user input keyword shortcut */
   Widget inp;              /* menu entry widget */
   struct _MenuInput *menu; /* parent */
} _MenuEntry, *MenuEntry;

typedef struct {
   Widget   inp;            /* list widget */
} _ListInput, *ListInput;

typedef struct {
   Widget    inp;           /* menu widget */
   Widget    shell;         /* menu pop-up shell */
   char      *name;         /* menu name in the form "menu_KEYWORD" */
   char      *btnkey;       /* keyword for reporting mouse button number */
   int       btn;           /* mouse button number clicked */
   MenuEntry entries;       /* linked list of menu entries */
} _MenuInput, *MenuInput;

typedef struct {
   Widget box;              /* container for keyword label and scroll widget */
   Widget key;              /* keyword label */
   Widget inp;              /* scrollbar widget */
   float  min;              /* minimum allowable value */
   float  max;              /* maximum allowable value */
   int    steps;            /* number of steps from min to max */
   int    expected;         /* number of expected key events */
   bool   dontfollow;       /* disable keyword following */
   bool   circular;         /* circular mode */
   char   *curstr;          /* new string contents */
   char   *prvstr;          /* previous string contents */
   char   fmt[FMTLENGTH];   /* format string */
} _GaugeInput, *GaugeInput;

typedef struct {
   Widget box;              /* container for keyword label and scroll widget */
   Widget key;              /* keyword label */
   Widget inp;              /* rheostat widget */
   float  min;              /* minimum allowable value */
   float  max;              /* maximum allowable value */
   int    expected;         /* number of expected key events */
   bool   dontfollow;       /* disable keyword following */
   bool   circular;         /* circular mode */
   char   *curstr;          /* new string contents */
   char   *prvstr;          /* previous string contents */
   char   fmt[FMTLENGTH];   /* format string */
} _DialInput, *DialInput;

typedef struct {
   Widget text;
   Widget source;
   int    mode;
   int    size;
   bool   changed;
} _Editor, *Editor;

typedef struct {
   Widget text;             /* label widget */
} _Label, *Label;

typedef struct {
   Widget gauge;
   int    length;
} _Progress, *Progress;

typedef struct {
   Widget canvas;
   Window window;
} _Canvas, *Canvas;

typedef struct {
   Widget shell;
   Widget form;
   bool   up;
   bool   shown;
   bool   dialog;
} _Shell, *Shell;

typedef struct {
   Widget form;
} _Form, *Form;

typedef struct {
   Widget vwport;
   Widget form;             /* the actual container */
   Widget hor;              /* horizontal scrollbar */
   Widget ver;              /* vertical scrollbar */
   bool   bars;             /* hor and ver initialized? */
   float  x, y;             /* current center */
   int    expected;         /* number of expected key events */
   bool   expectcb;         /* callback expected */
} _Vwport, *Vwport;

typedef struct _Element {
   struct  _Element  *next;       /* next element in global element list */ 
   struct  _Element  *container;  /* this element's container */
   struct  _Contents *contents;   /* contained elements list  */
   Class    type;             /* element type */
   Methods  methods;          /* methods struct */
   void     *implementation;  /* pointer to associated xxxInput struct */
   Widget   active;           /* the 'active' widget */
   Widget   outer;            /* the outmost widget */
   Widget   shell;            /* parent shell */
   ident    keyevent;         /* keyword handler registration ID */
   char     *keyword;         /* user input keyword */
   char     *message;         /* help pop-up text */
   char     *label;           /* element's label */
   int      insensitive;      /* disabled user interaction? */
   GgiCleanupProc cleanup;    /* post-delete cleanup function */
   void     *cleanarg;        /* cleanup function argument */
} _Element, *Element; 

typedef struct _Contents {
   struct  _Contents *next;
   Element element;           /* contained element */
} _Contents, *Contents;
   
static String def_fallback[] = {
   "*Background: #D8D8D8",
   "*Foreground: black",
   "*BorderColor: black",
   "*BeNiceToColormap: True",
   "*TextField.background: lightyellow",
   "*TextField.font: *courier*bold-r*12*",
   "*Editor.background: lightyellow",
   "*Editor*AsciiSink.background: lightyellow",
   "*SimpleMenu.background: lightyellow",
   "*SmeBSB.foreground: black",
   "*SmeBSB.font: *helvetica*bold-r*20*",
   "*List.background: lightyellow",
   "*Scrollbar.shown: 1.0",
   "*MenuButton.font: *helvetica*bold-r*20*",
   "*Label.font: *helvetica*bold-r*20*",
   "*Label.shadowWidth: 0",
   "*Toggle.font: *helvetica*bold-r*20*",
   "*helptext.background: yellow",
   "*errortext.background: white",
   "*errortext.foreground: red",
   "*helptext.font: *helvetica*bold-r*20*",
   "*gauge.background: lightyellow",
   "*gauge.indicatorColor: red",
   "*gauge.indicatorFillColor: red",
   "*progress.background: lightyellow",
   "*progress.indicatorColor: blue",
   "*progress.indicatorFillColor: blue",
   "*dial.background: lightyellow",
   "*dial.arrowColor: red",
   "*logo.foreground: #B8B8B8",
   "*logotext.background: yellow",
   "*pgplot.background: blue4",
   "*pgplot.foreground: gray",
   NULL
};
static String* fallback=def_fallback;

static String base_trans = "<Btn3Up>: pop_options()";

static Widget top=NULL;              /* top level container */
static Widget basewin=NULL, menus=NULL, texts=NULL, buttons=NULL,
              gauges=NULL, dials=NULL;
static Widget helpshell, helptext;   /* help text shell and label widget */
static Widget errorshell, errortext; /* error text shell and label widget */
static Widget docshell, doctext;
static XtAppContext context;
static XtIntervalId helptimer=0;     /* timer ID for delayed help pop-up */
static XtIntervalId errortimer=0;    /* timer ID for error text pop-down */

static int Width       =WIDTH,
           Height      =HEIGHT,
           Menuheight  =MENUHEIGHT,
           Textheight  =TEXTHEIGHT,
           Buttonheight=BUTTONHEIGHT,
           Helpdelay   =HELPDELAY;
static char Appclass[40]=APPCLASS;
static char Appname[40]=APPNAME;
static bool AutoLayout = AUTOLAYOUT;
static bool RealizeLater=REALIZELATER;
static char *DisplayName=NULL;
static int  scrno=-1;

#define mark_width 15
#define mark_height 15
static unsigned char mark_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0xfe, 0x01, 0xfe, 0x03, 0xfe, 0x07,
  0xfe, 0x1f, 0x06, 0x7f, 0xfe, 0x1f, 0xfe, 0x07, 0xfe, 0x03, 0xfe, 0x01,
  0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, };

#define thumb_width 1
#define thumb_height 1
static unsigned char thumb_bits[] = { 0xff };

#define gipsy_width 53
#define gipsy_height 60
static unsigned char gipsy_bits[] = {
  0x00, 0x51, 0x55, 0x55, 0x55, 0x15, 0x00, 0x80, 0xd6, 0xff, 0xdf, 0xff,
  0x1f, 0x00, 0xc0, 0x16, 0x67, 0x0b, 0xff, 0x3f, 0x00, 0x80, 0xd0, 0x5a,
  0xdb, 0xff, 0x7f, 0x00, 0xc0, 0xd6, 0x5a, 0xdb, 0xff, 0x3f, 0x00, 0xc0,
  0xd6, 0x58, 0xdb, 0x7b, 0x2f, 0x00, 0x80, 0x16, 0xe5, 0x38, 0x7b, 0x6f,
  0x00, 0xc0, 0x3f, 0xf0, 0xff, 0xff, 0x3f, 0x00, 0x80, 0x1f, 0xf0, 0xff,
  0xe5, 0x3f, 0x00, 0xc0, 0x1f, 0xd0, 0xdd, 0xc3, 0x3f, 0x00, 0x80, 0x2e,
  0xf0, 0x7f, 0xd1, 0x3f, 0x00, 0xc0, 0x1f, 0xf4, 0xef, 0xc1, 0x3d, 0x00,
  0x80, 0x3f, 0xf0, 0xf7, 0xe1, 0x3f, 0x00, 0xc0, 0x2e, 0xfc, 0xde, 0xf7,
  0x37, 0x00, 0x80, 0xfb, 0xff, 0xf7, 0xfa, 0x3e, 0x00, 0xa0, 0x7f, 0x5d,
  0xbe, 0xbf, 0x3b, 0x00, 0x40, 0xd5, 0x77, 0xd6, 0xeb, 0x2e, 0x00, 0x80,
  0xe0, 0x5f, 0x2b, 0xbe, 0x3b, 0x00, 0x00, 0x90, 0xb5, 0x17, 0xeb, 0x16,
  0x00, 0x00, 0xc0, 0x5b, 0x0d, 0x77, 0x3b, 0x00, 0x00, 0x00, 0x6f, 0x9b,
  0xdb, 0x16, 0x00, 0x00, 0xc0, 0xb7, 0xd5, 0x56, 0x3b, 0x00, 0x00, 0x00,
  0x8f, 0xb6, 0xb5, 0x15, 0x00, 0x00, 0xc0, 0x8f, 0xd5, 0xaa, 0x3e, 0x00,
  0x00, 0x40, 0x0f, 0xab, 0xaa, 0x2a, 0x00, 0x00, 0xa0, 0xd7, 0x45, 0x05,
  0xf4, 0x00, 0x00, 0xd0, 0x57, 0xa2, 0x03, 0xe8, 0x00, 0xa0, 0xe4, 0x53,
  0x81, 0x00, 0xd0, 0x01, 0x00, 0xf5, 0xd7, 0x00, 0x01, 0xe4, 0x03, 0x80,
  0xfe, 0x80, 0x81, 0x00, 0xa0, 0x03, 0x00, 0xbb, 0xa0, 0x43, 0x00, 0xc0,
  0x07, 0x40, 0x55, 0x80, 0x49, 0x00, 0xa8, 0x07, 0x00, 0x08, 0x80, 0x75,
  0x00, 0xd0, 0x07, 0x00, 0xa1, 0xc0, 0x53, 0x00, 0x40, 0x07, 0x00, 0x04,
  0xd0, 0x01, 0x00, 0xe8, 0x07, 0x80, 0x10, 0xe5, 0x89, 0x00, 0xd0, 0x07,
  0x00, 0x00, 0x74, 0x00, 0x01, 0xe8, 0x03, 0x00, 0x42, 0x10, 0x20, 0x00,
  0xea, 0x03, 0x00, 0x00, 0x01, 0x82, 0x4a, 0xf9, 0x01, 0x00, 0x00, 0x04,
  0x00, 0x10, 0xf6, 0x01, 0x00, 0x00, 0x10, 0x00, 0xd4, 0xff, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xf4, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x3f,
  0x00, 0x80, 0x0e, 0x80, 0x0a, 0x14, 0x03, 0x00, 0x80, 0x17, 0x8c, 0x17,
  0x5c, 0x06, 0x02, 0xe0, 0x3d, 0x86, 0x9d, 0x37, 0x86, 0x01, 0x70, 0x30,
  0x8c, 0xb9, 0x53, 0x8c, 0x01, 0x38, 0x00, 0x8e, 0x91, 0x03, 0x9c, 0x01,
  0x18, 0x00, 0x8c, 0x39, 0x07, 0xfc, 0x00, 0x18, 0x00, 0x86, 0x1f, 0x1e,
  0x70, 0x00, 0x18, 0x78, 0x8c, 0x0f, 0x3c, 0x70, 0x00, 0x18, 0xfc, 0x8c,
  0x01, 0x70, 0x20, 0x00, 0x18, 0x60, 0x86, 0x01, 0x60, 0x70, 0x00, 0x38,
  0x60, 0x8c, 0x01, 0xc0, 0x20, 0x00, 0xe0, 0x30, 0x8e, 0x81, 0x77, 0x70,
  0x00, 0xe0, 0x3f, 0x8c, 0x00, 0x3f, 0x20, 0x00, 0x80, 0x0a, 0x80, 0x00,
  0x1a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  };
static Pixmap mark, thumb, gipsy;
static bool help_armed=FALSE;
static float BLANK;

static Element ellist=NULL;             /* list of elements */
static Element container=NULL;          /* current container element */
static int deldepth=-1;                 /* recursion depth of GgiDelete */

static XVisualInfo vinfo;
static Colormap    colormap;
static Pixmap      pixmap;
static int         depth;
static _GgiInfo    info={&vinfo, 0, 0, &colormap, 0};
static FILE *eventlogfile=NULL;
static bool logging=FALSE;
static bool logintv=FALSE;

GgiInfo GgiGlobal=&info;

/* -------------------------------------------------------------------------- */
/*                                 StrDup                                     */
/* -------------------------------------------------------------------------- */
/*  String duplication function.
 */
static char *StrDup(char *text)
{
   char *result=NULL;
    
   if (text) {
      result = NNew(strlen(text)+1,char);
      strcpy(result,text);
   }
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 get_bool                                   */
/* -------------------------------------------------------------------------- */
static void get_bool(ident id, char *key, int code, void *arg)
{
   bool *value=(bool*)arg;
   *value = toflog(*value);
   (void)userflog(value, 1, 2, key, " ");
   *value = tobool(*value);
   if (eventlogfile) fflush(eventlogfile);
}

/* -------------------------------------------------------------------------- */
/*                                 handle_logfile                             */
/* -------------------------------------------------------------------------- */
static void handle_logfile(ident id, char *key, int code, void *arg)
{
   fchar name;
   char  cname[FILENAME_MAX+1];
   fint  nread;
    
   name.a = cname; name.l = FILENAME_MAX;
   nread = userftext(name, 2, key, " ");
   cname[nread] = '\0';
   if (eventlogfile) fclose(eventlogfile);
   eventlogfile = NULL;
   logging = FALSE;
   if (nread) {
      eventlogfile = fopen(cname, "w");
      if (eventlogfile) logging = TRUE;
      else              errorf(1, "Invalid event log file: %s", cname);
   }
}   

/* -------------------------------------------------------------------------- */
/*                                 writekey                                   */
/* -------------------------------------------------------------------------- */
/*  Write keyword-value pair to Hermes. If event logging is on, also write
 *  to log file.
 */
static void writekey(char *fmt, ... )
{
   char        buffer[CMDLEN];
   va_list     args;
   static bool prvlogintv=FALSE;
   static struct timeval prvtime;
   struct timeval now;
   float  elapsed;

   va_start( args, fmt );
   vsprintf( buffer, fmt, args );
   wkey_c( tofchar( buffer ) );                          /* to Hermes   */
   if (logging
       && strncmp(buffer, "GGI", 3)
       && strncmp(buffer, "_GGI", 4)) {
      if (logintv) {
         gettimeofday(&now, NULL);
         if (prvlogintv) {
            elapsed = (float)(now.tv_sec-prvtime.tv_sec) +
                      (float)(now.tv_usec-prvtime.tv_usec)/1000000.0;
            fprintf(eventlogfile, "%DELAY %.2f\n", elapsed);
         }
         prvtime = now;
      }
      prvlogintv = logintv;
      fprintf(eventlogfile, "%s\n", buffer);   /* to log file */
   }
   va_end( args );
}

/* -------------------------------------------------------------------------- */
/*                                 findvisual                                 */
/* -------------------------------------------------------------------------- */
/*  Find suitable visual.
 */
static void findvisual(Display *display, int screen,
                       XVisualInfo *result, int *deffound, int *fixed)
{
   XVisualInfo *available, template;
   long        mask=VisualScreenMask|VisualClassMask;
   int         i, nfound;
   Visual      *defvis;
   VisualID    defvisid;

   *deffound = 0;
   result->visual = NULL;
    
   defvis = XDefaultVisual(display, screen);
   defvisid = XVisualIDFromVisual(defvis);
   template.screen = screen;

   if (!*fixed) {
/*
 *  Try PseudoColor
 */
      template.class  = PseudoColor;
      available = XGetVisualInfo(display, mask, &template, &nfound);
      for (i=0; i<nfound; i++) {
         if (available[i].visualid == defvisid) {
            anyoutf(16, "Default visual");
            (*deffound)++;
            break;
         }
      }
      if (*deffound) *result = available[i];
      else if (nfound) *result = available[0];
      if (result->visual) {
         anyoutf(16, "%d-bit PseudoColor", result->depth);
         XFree(available);
         return;
      }

/*
 *  Try DirectColor
 */
      template.class  = DirectColor;
      available = XGetVisualInfo(display, mask, &template, &nfound);
      for (i=0; i<nfound; i++) {
         if (available[i].visualid == defvisid) {
            (*deffound)++;
            break;
         }
      }
      if (*deffound) *result = available[i];
      else if (nfound) *result = available[0];
      if (result->visual) {
         anyoutf(16, "%d-bit DirectColor", result->depth);
         XFree(available);
         return;
      }
   }
/*
 *  No PseudoColor or DirectColor: use default visual
 */
   mask = VisualIDMask;
   template.visualid = defvisid;
   available = XGetVisualInfo(display, mask, &template, &nfound);
   *result = available[0];
   XFree(available);
   anyoutf(16, "%d-bit default colormap", result->depth);
   (*deffound)++;
   (*fixed)++;
   return;
}

/* -------------------------------------------------------------------------- */
/*                                 cleanup                                    */
/* -------------------------------------------------------------------------- */
/*  "Timer" handler for performing a Ggi element post-delete cleanup.
 */
static void cleanup(ident id, void *arg)
{
   Element current=(Element)arg;
   current->cleanup(current->cleanarg);
   Delete(current);
   Deschedule(&id);
}

/* -------------------------------------------------------------------------- */
/*                                 pop_options                                */
/* -------------------------------------------------------------------------- */
static void pop_options(Widget curwid, XEvent *event,
                     String *params, Cardinal *num_params)
{
   wkeyf("_GGI_POP_OPTIONS=YES");
}

/* -------------------------------------------------------------------------- */
/*                                 close_window                               */
/* -------------------------------------------------------------------------- */
/*  window close action routine. Window manager close attempt is ignored
 *  and user gets audible feedback.
 */
static void close_window(Widget curwid, XEvent *event,
                     String *params, Cardinal *num_params)
{
   XBell(XtDisplay(curwid), 100);
}

/* -------------------------------------------------------------------------- */
/*                                 text_bol                                   */
/* -------------------------------------------------------------------------- */
/*  Textfield widget: beginning-of-line
 */
static void text_bol(Widget curwid, XEvent *event,
                     String *params, Cardinal *num_params)
{
   TextFieldSetInsertionPosition(curwid, 0);
}

/* -------------------------------------------------------------------------- */
/*                                 text_eol                                   */
/* -------------------------------------------------------------------------- */
/*  Textfield widget: end-of-line
 */
static void text_eol(Widget curwid, XEvent *event,
                     String *params, Cardinal *num_params)
{
   char *curstr=TextFieldGetString(curwid);
   
   TextFieldSetInsertionPosition(curwid, strlen(curstr));
   Delete(curstr);
}

/* -------------------------------------------------------------------------- */
/*                                 text_kill                                  */
/* -------------------------------------------------------------------------- */
/*  Textfield widget: kill to end-of-line
 */
static void text_kill(Widget curwid, XEvent *event,
                      String *params, Cardinal *num_params)
{
   char *curstr=TextFieldGetString(curwid);
    
   curstr[TextFieldGetInsertionPosition(curwid)] = '\0';
   TextFieldSetString(curwid, curstr);
   Delete(curstr);
}

/* -------------------------------------------------------------------------- */
/*                                 text_caret                                 */
/* -------------------------------------------------------------------------- */
/*  Textfield widget: show or hide insertion point
 */
static void text_caret(Widget curwid, XEvent *event,
                      String *params, Cardinal *num_params)
{
   if (!strcmp(params[0],"on")) {
      XtVaSetValues(curwid, XtNdisplayCaret, True, NULL);
   } else {
      XtVaSetValues(curwid, XtNdisplayCaret, False, NULL);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 textchanged                                */
/* -------------------------------------------------------------------------- */
/*  Text widget action procedure for actions that change the text buffer.
 *  It first updates any previously changed text field and then
 *  registers itself as changed.
 *  This procedure is also called directly with NULL as the curwid argument.
 */
static void textchanged(Widget curwid, XEvent *event,
                        String *params, Cardinal *num_params)
{
   static Widget changed=NULL;
   Element current;
   char    *newtext;
   
   if (changed && changed!=curwid) {
      for (current=ellist;  current; current = current->next) {
         if (current->active==changed) {
            TextInput curtext=(TextInput)current->implementation;
            newtext = TextFieldGetString(curtext->inp);
            strcpy(curtext->curstr, newtext);
            Delete(newtext);
            if (strcmp(curtext->curstr, curtext->prvstr)) {
               writekey("%s%s", current->keyword, curtext->curstr);
               strcpy(curtext->prvstr, curtext->curstr);
               /* save position for expected key event */
               curtext->position =
                  TextFieldGetInsertionPosition(curtext->inp);
            }
         }
      }
   }
   changed = curwid;
}

/* -------------------------------------------------------------------------- */
/*                                 textfocus                                  */
/* -------------------------------------------------------------------------- */
/*  Text widget action procedure for setting the keyboard focus.
 */
static void textfocus(Widget curwid, XEvent *event,
                      String *params, Cardinal *num_params)
{
   Widget shell=curwid;
   
   while (!XtIsShell(shell)) shell = XtParent(shell);
   XtSetKeyboardFocus(shell, curwid);
   textchanged(curwid, NULL, NULL, NULL);
}

/* -------------------------------------------------------------------------- */
/*                                 editfocus                                  */
/* -------------------------------------------------------------------------- */
/*  Editor widget action procedure for setting the keyboard focus.
 */
static void editfocus(Widget curwid, XEvent *event,
                      String *params, Cardinal *num_params)
{
   Widget shell=curwid;
   
   while (!XtIsShell(shell)) shell = XtParent(shell);
   XtSetKeyboardFocus(shell, curwid);
}

/* -------------------------------------------------------------------------- */
/*                                 stepfocus                                  */
/* -------------------------------------------------------------------------- */
/*  Text widget action procedure for stepping the keyboard focus.
 */
static void stepfocus(Widget curwid, XEvent *event,
                      String *params, Cardinal *num_params)
{
   Element current;
   Widget  previous=NULL, next=NULL;
   Widget  shell=curwid;
    
   while (!XtIsShell(shell)) shell = XtParent(shell);
   for (current=ellist; current->active != curwid; current=current->next) {;}

   while (1) {
      current = current->next;
      if (!current) current = ellist;
      if (current->active == curwid) break;
      if (current->type==TEXT
          && XtIsSensitive(current->active)
          && shell==current->shell) {
         if (!next) next = current->active;
         previous = current->active;
      }
   }
   switch (*params[0]) {
      case 'p': {
         if (previous) {
            textfocus(previous, NULL, NULL, NULL);
         }
         break;
      }
      case 'n': {
         if (next) {
            textfocus(next, NULL, NULL, NULL);
         }
         break;
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 text_enter                                 */
/* -------------------------------------------------------------------------- */
/*  Text widget action procedure for the Enter key.
 */
static void text_enter(Widget w, XEvent *event,
                    String *params, Cardinal *num_params)
{
   Element current;
   
   for (current=ellist;  current; current = current->next) {
      if (current->active==w) {
         TextInput curtext=(TextInput)current->implementation;
         strcpy(curtext->prvstr, "\n"); /* force different from current */
         break;
      }
   }
   textchanged(NULL, NULL, NULL, NULL);
}

/* -------------------------------------------------------------------------- */
/*                                 button_pressed                             */
/* -------------------------------------------------------------------------- */
/*  Toggle widget callback. It reads the widget's state and then
 *  calls writekey() to send the state to Hermes.
 *  It first calls textchanged() to send any changed textfields to Hermes.
 */
static void button_pressed(Widget w, XtPointer client_data, XtPointer call_data)
{
   Element current=(Element)client_data;
   Boolean state;
    
   XFlush(XtDisplay(top));
   textchanged(NULL, NULL, NULL, NULL);
   XtVaGetValues(w, XtNstate, &state, NULL);
   writekey("%s%s", current->keyword, state?"YES":"NO");
}

/* -------------------------------------------------------------------------- */
/*                                 menu_btn                                   */
/* -------------------------------------------------------------------------- */
/*  Menu button action procedure to record which mouse button was clicked.
 */
static void menu_btn(Widget w, XEvent *event,
                     String *params, Cardinal *num_params)
{
   Element current;
   
   for (current=ellist;  current; current = current->next) {
      if (current->active==w) {
         MenuInput curmenu=(MenuInput)current->implementation;
         int ib=((XButtonEvent*)event)->button;
         curmenu->btn = ib==Button1?ggiButn1:(ib==Button2?ggiButn2:ggiButn3);
         break;
      }
   }
}


/* -------------------------------------------------------------------------- */
/*                                 menu_select                                */
/* -------------------------------------------------------------------------- */
/*  MenuButton widget callback. It calls writekey() to send the button's
 *  ordinal number to Hermes.
 *  It also calls textchanged() to send any changed textfields to Hermes.
 */
static void menu_select(Widget w, XtPointer client_data, XtPointer call_data)

{
   MenuEntry entry=(MenuEntry)client_data;
   MenuInput curmenu=(MenuInput)entry->menu;

   textchanged(NULL, NULL, NULL, NULL);
   if (curmenu->btnkey) {
      writekey("%s%d", curmenu->btnkey, curmenu->btn);
   }
   writekey("%s%d",entry->key,entry->num);
}

/* -------------------------------------------------------------------------- */
/*                                 list_select                                */
/* -------------------------------------------------------------------------- */
/*  List widget callback. It calls writekey() to send the associated text
 *  to Hermes.
 *  It also calls textchanged() to send any changed textfields to Hermes.
 */
static void list_select(Widget w, XtPointer client_data, XtPointer call_data)

{
   Element current=(Element)client_data;
   XawListReturnStruct *choice=(XawListReturnStruct*)call_data;

   textchanged(NULL, NULL, NULL, NULL);
   writekey("%s%d", current->keyword, choice->list_index);
}

/* -------------------------------------------------------------------------- */
/*                                 gauge_scrolled                             */
/* -------------------------------------------------------------------------- */
/*  ScrollBar widget callback. It determines the current gauge value
 *  and calls writekey() to send it to Hermes.
 */
static void gauge_scrolled(Widget w, XtPointer clientdata, XtPointer calldata)
{
   XabSlider2CallbackStruct *slistru=(XabSlider2CallbackStruct*)calldata;
   Element current=(Element)clientdata;
   GaugeInput curgauge=(GaugeInput)current->implementation;
   float fraction=((float)(slistru->low_value))/(float)curgauge->steps;
   float minval=curgauge->min, maxval=curgauge->max;

   sprintf(curgauge->curstr, curgauge->fmt, minval + fraction*(maxval-minval));
   if (strcmp(curgauge->prvstr, curgauge->curstr)) {
      strcpy(curgauge->prvstr, curgauge->curstr);
      textchanged(NULL, NULL, NULL, NULL);
      writekey("%s%s", current->keyword, curgauge->curstr);
      curgauge->expected++;
   }
}

/* -------------------------------------------------------------------------- */
/*                                 dial_changed                               */
/* -------------------------------------------------------------------------- */
/*  Rheostat widget callback. It determines the current dial value
 *  and calls writekey() to send it to Hermes.
 */
static void dial_changed(Widget w, XtPointer clientdata, XtPointer calldata)
{
   XfwfRheostatCallbackStruct *diastru=(XfwfRheostatCallbackStruct*)calldata;
   Element current=(Element)clientdata;
   DialInput curdial=(DialInput)current->implementation;
   float fraction=((float)(diastru->value))/(float)DIALSTEPS;
   float minval=curdial->min, maxval=curdial->max;
    
   sprintf(curdial->curstr, curdial->fmt, minval + fraction*(maxval-minval));
   if (strcmp(curdial->prvstr, curdial->curstr)) {
      strcpy(curdial->prvstr, curdial->curstr);
      textchanged(NULL, NULL, NULL, NULL);
      writekey("%s%s", current->keyword, curdial->curstr);
      curdial->expected++;
   }
}

/* -------------------------------------------------------------------------- */
/*                                 editor_changed                             */
/* -------------------------------------------------------------------------- */
/*  Text widget callback. It reports any text change to the associated
 *  keyword.
 */
static void editor_changed(Widget w, XtPointer clientdata, XtPointer calldata)
{
   Element current=(Element)clientdata;
   Editor  curedit=(Editor)current->implementation;
    
   if (!curedit->changed) {
      curedit->changed = TRUE;
      if (current->keyword) wkeyf("%sYES", current->keyword);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 vwport_scrolled                            */
/* -------------------------------------------------------------------------- */
/*  Viewport scrollbar callback. It reports the new viewport center to
 *  the associated keyword.
 */
static void vwport_scrolled(Widget w, XtPointer clientdata, XtPointer calldata)
{
   Element current=(Element)clientdata;
   Vwport curvwport=(Vwport)current->implementation;
   float shown, top, cen;

   if (curvwport->expectcb) return;
   XtVaGetValues(w, XtNshown, &shown, XtNtopOfThumb, &top, NULL);    
   cen = top + shown/2;
   if (w==curvwport->hor) curvwport->x = cen; else curvwport->y = cen;
   writekey("%s%f %f", current->keyword, curvwport->x, curvwport->y);
   curvwport->expected++;
}

/* -------------------------------------------------------------------------- */
/*                                 showhelp                                   */
/* -------------------------------------------------------------------------- */
/*  Timer procedure showhelp() pops up the current help text shell.
 *  The pop-up is positioned below the active widged (this is determined
 *  by armhelp), 10 pixels to the right of the current cursor position.
 */
static void showhelp(XtPointer arg, XtIntervalId *id)
{
   Window rootret, childret;
   Dimension width;
   int rx, ry, wx, wy;
   unsigned int maskret;
    
   XtVaGetValues(helptext, XtNwidth, &width,  NULL);
   (void)XQueryPointer(XtDisplay(top),XtWindow(top), &rootret, &childret,
   &rx, &ry, &wx, &wy, &maskret);
   if ((rx+10+width) >= DisplayWidth(XtDisplay(top),0)) {
      rx = DisplayWidth(XtDisplay(top),0)-width;
   } else {
      rx +=10;
   }
#if !HELP_PTR_REL
   XtVaSetValues(helpshell, XtNx, rx, NULL);
#else
/*
 *   Pointer-relative positioning because XtTranslateCoords
 *   sometimes delivers incorrect coordinates.
 */
   XtVaSetValues(helpshell, XtNx, rx, XtNy, ry+10, NULL);
#endif
   XtPopup(helpshell, XtGrabNone);
   helptimer = 0;
}

/* -------------------------------------------------------------------------- */
/*                                 armhelp                                    */
/* -------------------------------------------------------------------------- */
/*  Action procedure to be called when pointer enters a widget's window.
 *  It searches for any help text associated with this widget and 
 *  stores this in the help text Label widget.
 *  Then it either pops up the help shell or registers timer procedure
 *  'showhelp' to do this at a later time.
 */
static void armhelp(Widget w, XEvent *event, String *params, Cardinal *npar)
{
   Element current=ellist;
      
   Dimension width, height;
   Position  x, y;

   for (current=ellist;  current; current = current->next) {
      if ((current->active==w)) {
         if (current->message) {
            XtVaGetValues(w, XtNwidth, &width, XtNheight, &height, NULL);
#if XT_TRANSLATE
            XtTranslateCoords(w, 0, (Position)(height+4), &x,&y);
#else
/*
 *   Use XTranslateCoordinates instead of XtTranslateCoords because the
 *   latter sometimes delivers incorrect coordinates.
 */
            {
               Window child_ret;
               (void)XTranslateCoordinates(XtDisplay(w), XtWindow(w),
               DefaultRootWindow(XtDisplay(w)), 0, height+4,
               (int*)&x, (int*)&y, &child_ret);
            }
#endif
            XtVaSetValues(helpshell, XtNx, x, XtNy, y, NULL);
            XtVaSetValues(helptext, XtNlabel, current->message, NULL);
            if (!Helpdelay)
               XtPopup(helpshell, XtGrabNone);
            else
               helptimer = XtAppAddTimeOut(context, Helpdelay, showhelp, NULL);
            help_armed = TRUE;
         }
         break;
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 disarmhelp                                 */
/* -------------------------------------------------------------------------- */
/*  Action procedure to be called when pointer leaves a widget's window.
 *  If there is still a timer running, then this is stopped.
 *  Otherwise the help shell is active and will be popped down.
 */
static void disarmhelp(Widget w, XEvent *event, String *params, Cardinal *npar)
{
   if (help_armed) {
      if (helptimer) {
         XtRemoveTimeOut(helptimer);
         helptimer = 0;
      } else {
         XtPopdown(helpshell);
      }
      help_armed = FALSE;
   }
}

/* -------------------------------------------------------------------------- */
/*                                 logoinfo                                   */
/* -------------------------------------------------------------------------- */
/* Action procedure to be called when pointer enters or leaves a logo
 *  widget. It pops up or down an informative text label.
 */
static void logoinfo(Widget w, XEvent *event, String *params, Cardinal *npar)
{
   static char message[]=
"This program is part of GIPSY, the\n\
\"Groningen Image Processing System\",\n\
developed at the Kapteyn Institute.";
   static bool initialized=FALSE;
   static Widget logoshell, logotext;

   Dimension width, height;
   Position  x, y;

   if (!initialized) {
      initialized = TRUE;
      logoshell = XtVaCreatePopupShell("logoshell", overrideShellWidgetClass,
                                       top,
                                       XtNvisual, vinfo.visual,
                                       XtNcolormap, colormap,
                                       XtNborderPixmap, pixmap,
                                       XtNdepth, depth,
                                       XtNallowShellResize, True, NULL);
      logotext = XtVaCreateManagedWidget("logotext", labelWidgetClass,
                                          logoshell, XtNlabel, message, NULL);
   }
   switch (*params[0]) {
      case 'e': {
         XtVaGetValues(logotext, XtNheight, &height, XtNwidth, &width,  NULL); /* text's height */
         XtTranslateCoords(w, (Position)(-width-4),(Position)(-height-4),&x,&y);
         XtVaSetValues(logoshell, XtNx, x<0?0:x, XtNy, y<0?0:y, NULL);
         XtPopup(logoshell, XtGrabNone);
         break;
      }
      case 'l': {
         XtPopdown(logoshell);
         break;
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 errordown                                  */
/* -------------------------------------------------------------------------- */
/*  Timer procedure to pop down error pop-up.
 */
static void errordown(XtPointer arg, XtIntervalId *id)
{
   XtPopdown(errorshell);
   errortimer = 0;
}

/* -------------------------------------------------------------------------- */
/*                                 showstatus                                 */
/* -------------------------------------------------------------------------- */
/*  Status message callback function receiving copy of status_c() messages.
 */
static void showstatus(char *message)
{
   XtVaSetValues(top, XtNtitle, message, NULL);
#if 0
   XtVaSetValues(top, XtNiconName, message, NULL);
#endif
}
/* -------------------------------------------------------------------------- */
/*                                 getreal                                    */
/* -------------------------------------------------------------------------- */
static float getreal(char *key)
{
   float result=0.0;
   fint fint1=1;
   fchar text;
   char  text_c[TEXTLENGTH];

   text.a = text_c; text.l = TEXTLENGTH;
   if (userftext(text, 2, key, " ")        <= 0 ) return BLANK;
   if (decodereal_c(text, &result, &fint1) <= 0 ) return BLANK;
    
   return result;
}

/* -------------------------------------------------------------------------- */
/*                              InsertElement                                 */
/* -------------------------------------------------------------------------- */
/*  Insert the element specified in the argument in the current
 *  container's element list.
 */
static void InsertElement(Element element)
{
   element->container = container;
   if (container) {
      Contents contents=New(_Contents);
      contents->element = element;
      contents->next = container->contents;
      container->contents = contents;
   }
}

/* -------------------------------------------------------------------------- */
/*                              RemoveElement                                 */
/* -------------------------------------------------------------------------- */
/*  Remove the element specified in the argument from the element's
 *  container's element list.
 */
static void RemoveElement(Element element)
{
   Element container = element->container;
   Contents current, *contp, *next;

   if (!container) return;   
   for (contp = &container->contents; *contp; contp=next) {
      current = *contp;
      next = &(*contp)->next;
      if (current->element == element) {
         *contp = current->next;
         Delete(current);
         element->container = NULL;
         break;
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                         M E T H O D S  S E C T I O N                       */
/* -------------------------------------------------------------------------- */
/*   This section contains the definitions of the different methods
 *   for every class.
 *   Every class should at least have a 'delete' method.
 *   In function initialize() the Methods structs are initialized
 *   with pointers to the methods.
 */
/* -------------------------------------------------------------------------- */
/*                                 TEXT methods                               */
/* -------------------------------------------------------------------------- */
static void TEXTdelete(ident id)
{
   Element current=(Element)id;
   TextInput curtext=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curtext->box);
   Delete(curtext->curstr);
   Delete(curtext->prvstr);
}

static void TEXTkeychanged(ident id, char *key, int code, void *arg)
{
   Element current=(Element)arg;
   TextInput curtext=(TextInput)current->implementation;
   fchar value;
   fint  nchars;

   if (code==KEYCHANGE) {
      value.a = curtext->curstr;
      value.l = curtext->length;
      nchars = userftext(value, 1, key, " ");
      if (nchars || !keystatus()) {
         curtext->curstr[nchars] = '\0';
         strcpy(curtext->prvstr, curtext->curstr);
         XtVaSetValues(current->active, XtNstring, curtext->curstr, NULL);
         if (curtext->position >= 0) {
            /* restore position if key event was expected */
            TextFieldSetInsertionPosition(curtext->inp, curtext->position);
            curtext->position = -1;  /* don't expect key event anymore */
         } else {
            XtVaSetValues(curtext->inp, XtNinsertPosition, 0, NULL);
         }
      }
   } else if (code==KEYREJECT) {
      fchar message;
      char  message_c[41];
      fint  nc=40;
      Position x, y;
      Dimension width, height;
       
      message.a = message_c; message.l = nc;
      if (errortimer) {
         XtRemoveTimeOut(errortimer);
         XtPopdown(errorshell);
      }
      *(curtext->prvstr) = '\001';  /* force previous different from current */
      nc = userftext(message, -1, "UCAMESSAGE=", " ");
      message_c[nc] = '\0';
      if (!message_c[0]) strcpy(message_c,"Bad input");
      XtVaGetValues(curtext->inp, XtNwidth, &width, XtNheight, &height, NULL);
      XtTranslateCoords(curtext->inp, (Position)7, (Position)(height+3), &x, &y);
      XtVaSetValues(errorshell, XtNx, x, XtNy, y, NULL);
      XtVaSetValues(errortext, XtNlabel, message_c, NULL);
      XtPopup(errorshell, XtGrabNone);
      errortimer = XtAppAddTimeOut(context, 3 SECONDS, errordown, NULL);
   }
}

static void TEXTsetlabel(ident id, char *label, int width)
{
   Element current=(Element)id;
   TextInput curimpl=current->implementation;
   if (label) {
      Delete(current->label);
      current->label = StrDup(label);
      XtVaSetValues(curimpl->key,XtNlabel,current->label,NULL);
   }
   if (width) XtVaSetValues(curimpl->key,XtNwidth,width,NULL);
}

static void TEXTalign(ident id, ggiAlign al)
{
   Dimension width;
   
   Element current=(Element)id;
   TextInput curimpl=current->implementation;
   XtVaGetValues(curimpl->key, XtNwidth, &width, NULL);
   XtVaSetValues(curimpl->key, XtNjustify, XTJUST(al), XtNwidth, width, NULL);
}

static int TEXTgetwidth(ident id)
{
   int result=4;                 /* initial size due to interwidget spacing */
   Dimension width;
   Element current=(Element)id;
   TextInput curimpl=current->implementation;
   
   XtVaGetValues(curimpl->key, XtNwidth, &width, NULL);
   result += width;
   XtVaGetValues(curimpl->inp, XtNwidth, &width, NULL);
   result += width;
   
   return result;
}

static int TEXTgetheight(ident id)
{
   int result=2;                 /* initial size due to interwidget spacing */
   Dimension height;
   Element current=(Element)id;
   TextInput curimpl=current->implementation;
   
   XtVaGetValues(curimpl->inp, XtNheight, &height, NULL);
   result += height;
   
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 BUTTON methods                             */
/* -------------------------------------------------------------------------- */
static void BUTTONdelete(ident id)
{
   Element current=(Element)id;
   ButtonInput curbutt=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curbutt->inp);
}

static void BUTTONkeychanged(ident id, char *key, int code, void *arg)
{
   Element current=(Element)arg;
   bool state=toflog(FALSE);
   
   (void)userflog(&state, 1, 2, key, " ");
   XtVaSetValues(current->active, XtNstate, tobool(state)?True:False, NULL);
}

static void BUTTONsetlabel(ident id, char *label, int width)
{
   Element current=(Element)id;
   ButtonInput curimpl=current->implementation;
   if (label) {
      Delete(current->label);
      current->label = StrDup(label);
      XtVaSetValues(curimpl->inp,XtNlabel,current->label,NULL);
   }
   if (width) XtVaSetValues(curimpl->inp,XtNwidth,width,NULL);
}

static void BUTTONsetbitmap(ident id, int nx, int ny, char *bits)
{
   Element current=(Element)id;
   ButtonInput curimpl=current->implementation;
   Pixmap bm;
   Display *display=XtDisplay(top);
   
   bm = XCreateBitmapFromData(display, XRootWindow(display, scrno), bits,
        nx, ny);
   XtVaSetValues(curimpl->inp, XtNbitmap, bm, NULL);
}

static void BUTTONalign(ident id, ggiAlign al)
{
   Element current=(Element)id;
   ButtonInput curimpl=current->implementation;
   XtVaSetValues(curimpl->inp, XtNjustify, XTJUST(al), NULL);
}

/* -------------------------------------------------------------------------- */
/*                                 MENU methods                               */
/* -------------------------------------------------------------------------- */
static void MENUdelete(ident id)
{
   Element current=(Element)id;
   MenuInput curmenu=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curmenu->inp);
   Delete(curmenu->name);
   if (curmenu->btnkey) Delete(curmenu->btnkey);
   while (curmenu->entries) {
      MenuEntry current=curmenu->entries;
      curmenu->entries = curmenu->entries->next;
      Delete(current);
   }
}

static void MENUkeychanged(ident id, char *key, int code, void *arg)
{
   Element current=(Element)arg;
   MenuEntry entry=((MenuInput)current->implementation)->entries;
   fint state=-1;
   
   (void)userfint(&state, 1, 2, key, " ");
   while (entry) {
      if (entry->num==state) {
         XtVaSetValues(entry->inp, XtNleftBitmap, mark, NULL);
      } else {
         XtVaSetValues(entry->inp, XtNleftBitmap, None, NULL);
      }
      entry = entry->next;
   }
}

static void MENUsetlabel(ident id, char *label, int width)
{
   Element current=(Element)id;
   MenuInput curimpl=current->implementation;
   if (label) {
      Delete(current->label);
      current->label = StrDup(label);
      XtVaSetValues(curimpl->inp,XtNlabel,current->label,NULL);
   }
   if (width) XtVaSetValues(curimpl->inp,XtNwidth,width,NULL);
}

static void MENUalign(ident id, ggiAlign al)
{
   Element current=(Element)id;
   MenuInput curimpl=current->implementation;
   XtVaSetValues(curimpl->inp, XtNjustify, XTJUST(al), NULL);
}

/* -------------------------------------------------------------------------- */
/*                                 GAUGE methods                              */
/* -------------------------------------------------------------------------- */
static void GAUGEdelete(ident id)
{
   Element current=(Element)id;
   GaugeInput curgauge=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curgauge->box);
   Delete(curgauge->curstr);
   Delete(curgauge->prvstr);
}

static void GAUGEkeychanged(ident id, char *key, int code, void *arg)
{
   Element current=(Element)arg;
   GaugeInput curgauge=(GaugeInput)current->implementation;
   float value=0.0, minval=curgauge->min, maxval=curgauge->max;
   float max=(float)curgauge->steps;
       
   if (curgauge->expected) {
      curgauge->expected--;
   } else if (!curgauge->dontfollow) {
      value = getreal(key);
      if (value==BLANK) return;
      if (curgauge->circular) {
         double   expo;
         value = modf((value-minval)/(maxval-minval), &expo);
         if (value<0.0) value = 1.0+value;
         value *= max;
      } else {
         value = max*(value-minval)/(maxval-minval);
         value = value<0.0?0.0:(value>max?max:value);
      }
      sprintf(curgauge->curstr, curgauge->fmt, value);
      strcpy(curgauge->prvstr, curgauge->curstr);
      XtVaSetValues(current->active, XtNhighValue, (int)value,
                                     XtNlowValue,  (int)value, NULL);
   }
}

static void GAUGEsetlabel(ident id, char *label, int width)
{
   Element current=(Element)id;
   GaugeInput curimpl=current->implementation;
   if (label) {
      Delete(current->label);
      current->label = StrDup(label);
      XtVaSetValues(curimpl->key,XtNlabel,current->label,NULL);
   }
   if (width) XtVaSetValues(curimpl->key,XtNwidth,width,NULL);
}

static void GAUGEsetrange(ident id, float minval, float maxval)
{
   Element current=(Element)id;
   GaugeInput curgauge=(GaugeInput)current->implementation;
   float max=(float)curgauge->steps;
   float value=0.0;
    
   curgauge->min = minval;
   curgauge->max = maxval;
   value = getreal(current->keyword);
   if (value==BLANK) return;
   value = max*(value-minval)/(maxval-minval);
   value = value<0.0?0.0:(value>max?max:value);
   XtVaSetValues(current->active, XtNhighValue, (int)value,
                                  XtNlowValue,  (int)value, NULL);
}

static void GAUGEalign(ident id, ggiAlign al)
{
   Element current=(Element)id;
   GaugeInput curimpl=current->implementation;
   XtVaSetValues(curimpl->key, XtNjustify, XTJUST(al), NULL);
}

static int GAUGEgetwidth(ident id)
{
   int result=4;                 /* initial size due to interwidget spacing */
   Dimension width;
   Element current=(Element)id;
   GaugeInput curimpl=current->implementation;
   
   XtVaGetValues(curimpl->key, XtNwidth, &width, NULL);
   result += width;
   XtVaGetValues(curimpl->inp, XtNwidth, &width, NULL);
   result += width;
   
   return result;
}

static int GAUGEgetheight(ident id)
{
   int result=2;                 /* initial size due to interwidget spacing */
   Dimension height;
   Element current=(Element)id;
   GaugeInput curimpl=current->implementation;
   
   XtVaGetValues(curimpl->inp, XtNheight, &height, NULL);
   result += height;
   
   return result;
}

static void GAUGEsetfollow(ident id, bool follow)
{
   Element current=(Element)id;
   GaugeInput curimpl=current->implementation;
   
   curimpl->dontfollow = !follow;
}

static void GAUGEsetformat(ident id, char *format)
{
   Element current=(Element)id;
   GaugeInput curimpl=current->implementation;
   
   strcpy(curimpl->fmt, format);
}

static void GAUGEsetcirc(ident id, bool circular)
{
   Element current=(Element)id;
   GaugeInput curimpl=current->implementation;
   
   curimpl->circular = circular;
}

/* -------------------------------------------------------------------------- */
/*                                 DIAL methods                               */
/* -------------------------------------------------------------------------- */
static void DIALdelete(ident id)
{
   Element current=(Element)id;
   GaugeInput curgauge=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curgauge->box);
}

static void DIALkeychanged(ident id, char *key, int code, void *arg)
{
   Element current=(Element)arg;
   DialInput curdial=(DialInput)current->implementation;
   float value=0.0, minval=curdial->min, maxval=curdial->max;
   float max=(float)DIALSTEPS;
       
   if (curdial->expected) {
      curdial->expected--;
   } else if (!curdial->dontfollow) {
      value = getreal(key);
      if (value==BLANK) return;
      if (curdial->circular) {
         double   expo;
         value = modf((value-minval)/(maxval-minval), &expo);
         if (value<0.0) value = 1.0+value;
         value *= max;
      } else {
         value = max*(value-minval)/(maxval-minval);
         value = value<0.0?0.0:(value>max?max:value);
      }
      XtVaSetValues(current->active, XtNvalue, (int)value, NULL);
   }
}

static void DIALsetlabel(ident id, char *label, int width)
{
   Element current=(Element)id;
   DialInput curimpl=current->implementation;
   if (label) {
      Delete(current->label);
      current->label = StrDup(label);
      if (curimpl->key) {
         XtVaSetValues(curimpl->key,XtNlabel,current->label,NULL);
         if (width) XtVaSetValues(curimpl->key,XtNwidth,width,NULL);
      }
   } else {
      XtDestroyWidget(curimpl->key);
      curimpl->key = NULL;
   }
}

static void DIALsetrange(ident id, float minval, float maxval)
{
   Element current=(Element)id;
   DialInput curdial=(DialInput)current->implementation;
   float max=(float)DIALSTEPS;
   float value=0.0;
    
   curdial->min = minval;
   curdial->max = maxval;
   value = getreal(current->keyword);
   if (value==BLANK) return;
   value = max*(value-minval)/(maxval-minval);
   value = value<0.0?0.0:(value>max?max:value);
   XtVaSetValues(current->active, XtNvalue, (int)value, NULL);
}

static void DIALalign(ident id, ggiAlign al)
{
   Element current=(Element)id;
   DialInput curimpl=current->implementation;
   XtVaSetValues(curimpl->key, XtNjustify, XTJUST(al), NULL);
}

static int DIALgetwidth(ident id)
{
   int result=2;                 /* initial size due to interwidget spacing */
   Dimension widthr, widthl=0;
   Element current=(Element)id;
   DialInput curimpl=current->implementation;
   
   if (curimpl->key) XtVaGetValues(curimpl->key, XtNwidth, &widthl, NULL);
   XtVaGetValues(curimpl->inp, XtNwidth, &widthr, NULL);
   result += widthl>widthr?widthl:widthr;
     
   return result;
}

static int DIALgetheight(ident id)
{
   int result=4;                 /* initial size due to interwidget spacing */
   Dimension height;
   Element current=(Element)id;
   DialInput curimpl=current->implementation;
   
   XtVaGetValues(curimpl->inp, XtNheight, &height, NULL);
   result += height;
   if (curimpl->key) {
      XtVaGetValues(curimpl->key, XtNheight, &height, NULL);
      result += height;
   } else result -= 2;
   
   return result;
}

static void DIALsetfollow(ident id, bool follow)
{
   Element current=(Element)id;
   DialInput curimpl=current->implementation;
   
   curimpl->dontfollow = !follow;
}

static void DIALsetformat(ident id, char *format)
{
   Element current=(Element)id;
   DialInput curimpl=current->implementation;
   
   strcpy(curimpl->fmt, format);
}

static void DIALsetcirc(ident id, bool circular)
{
   Element current=(Element)id;
   DialInput curimpl=current->implementation;
   
   curimpl->circular = circular;
}

/* -------------------------------------------------------------------------- */
/*                                 LABEL methods                              */
/* -------------------------------------------------------------------------- */
static void LABELdelete(ident id)
{
   Element current=(Element)id;
   Label curlab=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curlab->text);
}

static void LABELsetlabel(ident id, char *label, int width)
{
   Element current=(Element)id;
   Label curimpl=current->implementation;
   if (label) {
      Delete(current->label);
      current->label = StrDup(label);
      XtVaSetValues(curimpl->text,XtNlabel,current->label,NULL);
   }
   if (width) XtVaSetValues(curimpl->text,XtNwidth,width,NULL);
}

static void LABELsetbitmap(ident id, int nx, int ny, char *bits)
{
   Element current=(Element)id;
   Label curimpl=current->implementation;
   Pixmap bm;
   Display *display=XtDisplay(top);
   
   bm = XCreateBitmapFromData(display, XRootWindow(display, scrno), bits,
                              nx, ny);
   XtVaSetValues(curimpl->text, XtNbitmap, bm, NULL);
}

static void LABELalign(ident id, ggiAlign al)
{
   Element current=(Element)id;
   Label curimpl=current->implementation;
   XtVaSetValues(curimpl->text, XtNjustify, XTJUST(al), NULL);
}

/* -------------------------------------------------------------------------- */
/*                                 CANVAS methods                              */
/* -------------------------------------------------------------------------- */
static void CANVASdelete(ident id)
{
   Element current=(Element)id;
   Canvas curlab=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curlab->canvas);
}

/* -------------------------------------------------------------------------- */
/*                                 SHELL methods                              */
/* -------------------------------------------------------------------------- */
static void SHELLdelete(ident id)
{
   Element current=(Element)id;
   Shell curshell=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curshell->shell);
}

/* -------------------------------------------------------------------------- */
/*                                 FORM methods                               */
/* -------------------------------------------------------------------------- */
static void FORMdelete(ident id)
{
   Element current=(Element)id;
   Form curform=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curform->form);
}

/* -------------------------------------------------------------------------- */
/*                                 VWPORT methods                             */
/* -------------------------------------------------------------------------- */
static void VWPORTdelete(ident id)
{
   Element current=(Element)id;
   Vwport curvwport=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curvwport->vwport);
}

static void VWPORTkeychanged(ident id, char *key, int code, void *arg)
{
   float center[2], shown, top, prevtop;
   Element current=(Element)arg;
   Vwport curvwport=(Vwport)current->implementation;
    
   if (!curvwport->bars) {
      curvwport->hor = XtNameToWidget(curvwport->vwport, "horizontal");
      if (curvwport->hor) {
         XtAddCallback(curvwport->hor, XtNjumpProc, vwport_scrolled, current);
         XtAddCallback(curvwport->hor, XtNscrollProc, vwport_scrolled, current);
      }
      curvwport->ver = XtNameToWidget(curvwport->vwport, "vertical");
      if (curvwport->ver) {
         XtAddCallback(curvwport->ver, XtNjumpProc, vwport_scrolled, current);
         XtAddCallback(curvwport->ver, XtNscrollProc, vwport_scrolled, current);
      }
      curvwport->bars = TRUE;
   }
       
   if (curvwport->expected) {
      curvwport->expected--;
   } else {
      (void)userfreal(center, 2, 2, key, " ");
      curvwport->x = center[0];
      curvwport->y = center[1];
      curvwport->expectcb = TRUE;
      if (curvwport->hor) {
         XtVaGetValues(curvwport->hor, XtNshown, &shown, XtNtopOfThumb, &prevtop, NULL);
         top = curvwport->x - shown/2.0;
         if (fabs(prevtop-top)>0.01*shown) {
            XawScrollbarSetThumb(curvwport->hor, top, -1.0);
            XtCallCallbacks(curvwport->hor, XtNjumpProc, &top);
         }
      }
      if (curvwport->ver) {
         XtVaGetValues(curvwport->ver, XtNshown, &shown, XtNtopOfThumb, &prevtop, NULL);
         top = curvwport->y - shown/2.0;
         if (fabs(prevtop-top)>0.01*shown) {
            XawScrollbarSetThumb(curvwport->ver, top, -1.0);
            XtCallCallbacks(curvwport->ver, XtNjumpProc, &top);
         }
      }
      curvwport->expectcb = FALSE;
   }
}

/* -------------------------------------------------------------------------- */
/*                                 LIST methods                               */
/* -------------------------------------------------------------------------- */
static void LISTdelete(ident id)
{
   Element current=(Element)id;
   ListInput curlist=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curlist->inp);
}

static void LISTkeychanged(ident id, char *key, int code, void *arg)
{
   Window rootret, childret;
   int rx, ry, wx, wy;
   unsigned int maskret;
   fchar message;
   char  message_c[41];
   fint  nc=40;
    
   message.a = message_c; message.l = nc;
   if (errortimer) {
      XtRemoveTimeOut(errortimer);
      XtPopdown(errorshell);
   }
   nc = userftext(message, -1, "UCAMESSAGE=", " ");
   message_c[nc] = '\0';
   if (!message_c[0]) strcpy(message_c,"Bad choice");
   (void)XQueryPointer(XtDisplay(top),XtWindow(top), &rootret, &childret,
                       &rx, &ry, &wx, &wy, &maskret);
   
   XtVaSetValues(errorshell, XtNx, rx, XtNy, ry+10, NULL);
   XtVaSetValues(errortext, XtNlabel, message_c, NULL);
   XtPopup(errorshell, XtGrabNone);
   errortimer = XtAppAddTimeOut(context, 3 SECONDS, errordown, NULL);
}

/* -------------------------------------------------------------------------- */
/*                                 PROGRESS methods                           */
/* -------------------------------------------------------------------------- */
static void PROGRESSdelete(ident id)
{
   Element current=(Element)id;
   Progress curprog=(Progress)current->implementation;
    
   if (!deldepth) XtDestroyWidget(curprog->gauge);
}

static void PROGRESSkeychanged(ident id, char *key, int code, void *arg)
{
   Element current=(Element)arg;
   Progress curprog=(Progress)current->implementation;
   float value=0.0;
    
   value = getreal(key);
   if (value==BLANK) return;
   value = value*(float)curprog->length;
   XtVaSetValues(current->active, XtNindicatorThickness, (int)value, NULL);
}

/* -------------------------------------------------------------------------- */
/*                                 EDITOR methods                             */
/* -------------------------------------------------------------------------- */
static void EDITORdelete(ident id)
{
   Element current=(Element)id;
   Editor  curedit=current->implementation;
    
   if (!deldepth) XtDestroyWidget(curedit->text);
}

static void EDITORkeychanged(ident id, char *key, int code, void *arg)
{
   Element current=(Element)arg;
   Editor  curedit=current->implementation;
   bool    changed=toflog(FALSE);
    
   (void)userflog(&changed, 1, 2, key, " ");
   curedit->changed = tobool(changed);
}

/* ----------------------------- (end of methods) --------------------------- */

/* -------------------------------------------------------------------------- */
/*                                 initialize                                 */
/* -------------------------------------------------------------------------- */
/*  Initialize or do nothing when already initialized.
 */
static int initialize(void)
{
   static XtActionsRec actions[] = {
      {"text-bol", text_bol},
      {"text-eol", text_eol},
      {"text-kill", text_kill},
      {"text-caret", text_caret},
      {"text-enter", text_enter},
      {"armhelp", armhelp},
      {"disarmhelp", disarmhelp},
      {"textchanged", textchanged},
      {"logoinfo", logoinfo},
      {"textfocus", textfocus},
      {"stepfocus", stepfocus},
      {"closewindow", close_window},
      {"editfocus", editfocus},
      {"pop_options", pop_options},
      {"menu_btn", menu_btn}
   }; int n_actions=sizeof(actions)/sizeof(XtActionsRec);

   
   static int result=0, initialized=0;
   int owncol;
   Widget menuview, textview, buttview;
   Widget panes;
   Display *display;

   setfblank_c(&BLANK);
   if (initialized) return result;
   initialized++;
   
   if (!top) {
      Widget       topLevel;
      int          zero=0;

/*
 *  handle user-specified options
 */
      {
         fchar        ggiopt;
         char         ggioptc[10];
         int          i, nread;
         
         ggiopt.a = ggioptc;
         ggiopt.l = sizeof(ggioptc)-1;
         nread = userftext(ggiopt, 2, "GGIOPT=", " ");
         ggioptc[nread] = '\0';
         for (i=0; i<nread; i++) ggioptc[i] = toupper(ggioptc[i]);
         owncol = !strcmp(ggioptc, "OWNCOL");
         info.fixedcolors = !strcmp(ggioptc, "FIXCOL");
      }
/*
 *  initialize methods for each class
 */
      methods = NNew(NCLASSES,_Methods);

      methods[TEXT].delete       = TEXTdelete;
      methods[TEXT].keychanged   = TEXTkeychanged;
      methods[TEXT].setlabel     = TEXTsetlabel;
      methods[TEXT].align        = TEXTalign;
      methods[TEXT].getwidth     = TEXTgetwidth;
      methods[TEXT].getheight    = TEXTgetheight;

      methods[BUTTON].delete     = BUTTONdelete;
      methods[BUTTON].keychanged = BUTTONkeychanged;
      methods[BUTTON].setlabel   = BUTTONsetlabel;
      methods[BUTTON].setbitmap  = BUTTONsetbitmap;
      methods[BUTTON].align      = BUTTONalign;

      methods[MENU].delete       = MENUdelete;
      methods[MENU].keychanged   = MENUkeychanged;
      methods[MENU].setlabel     = MENUsetlabel;
      methods[MENU].align        = MENUalign;
      
      methods[LIST].delete       = LISTdelete;
      methods[LIST].keychanged   = LISTkeychanged;

      methods[GAUGE].delete      = GAUGEdelete;
      methods[GAUGE].keychanged  = GAUGEkeychanged;
      methods[GAUGE].setlabel    = GAUGEsetlabel;
      methods[GAUGE].setrange    = GAUGEsetrange;
      methods[GAUGE].align       = GAUGEalign;
      methods[GAUGE].getwidth    = GAUGEgetwidth;
      methods[GAUGE].getheight   = GAUGEgetheight;
      methods[GAUGE].setfollow   = GAUGEsetfollow;
      methods[GAUGE].setformat   = GAUGEsetformat;
      methods[GAUGE].setcirc     = GAUGEsetcirc;
   
      methods[DIAL].delete       = DIALdelete;
      methods[DIAL].keychanged   = DIALkeychanged;
      methods[DIAL].setlabel     = DIALsetlabel;
      methods[DIAL].setrange     = DIALsetrange;
      methods[DIAL].align        = DIALalign;
      methods[DIAL].getwidth     = DIALgetwidth;
      methods[DIAL].getheight    = DIALgetheight;
      methods[DIAL].setfollow    = DIALsetfollow;
      methods[DIAL].setformat    = DIALsetformat;
      methods[DIAL].setcirc      = DIALsetcirc;
   
      methods[LABEL].delete      = LABELdelete;
      methods[LABEL].setlabel    = LABELsetlabel;
      methods[LABEL].setbitmap   = LABELsetbitmap;
      methods[LABEL].align       = LABELalign;
      
      methods[CANVAS].delete     = CANVASdelete;

      methods[SHELL].delete      = SHELLdelete;

      methods[FORM].delete       = FORMdelete;

      methods[VWPORT].delete     = VWPORTdelete;
      methods[VWPORT].keychanged = VWPORTkeychanged;

      methods[PROGRESS].delete   = PROGRESSdelete;
      methods[PROGRESS].keychanged = PROGRESSkeychanged;

      methods[EDITOR].delete     = EDITORdelete;
      methods[EDITOR].keychanged = EDITORkeychanged;

      if (!*Appname) {
         int i;
         fchar taskname;
         taskname.a = Appname;
         taskname.l = 9;
         myname_c(taskname);
         char2str(taskname, Appname, 9);
         for (i=strlen(Appname)-1; i&&(Appname[i]==' ') ; i--) Appname[i]='\0';
      } 
      XtToolkitInitialize();
      context = XtCreateApplicationContext();
      XtAppSetFallbackResources(context, fallback);
      display = XtOpenDisplay(context, DisplayName, Appname, Appclass, NULL, 0,
                             &zero, NULL);
      if (!display) {
         errorf(4, "Ggi error: cannot open display");
         result = -1;
         return result;
      }
      scrno = DefaultScreen(display);
      mark = XCreateBitmapFromData(display, XRootWindow(display, scrno),
                                 (char*)mark_bits, mark_width, mark_height);
      thumb = XCreateBitmapFromData(display, XRootWindow(display, scrno),
                                 (char*)thumb_bits, thumb_width, thumb_height);
      gipsy = XCreateBitmapFromData(display, XRootWindow(display, scrno),
                                 (char*)gipsy_bits, gipsy_width, gipsy_height);

      findvisual(display, scrno, &vinfo, &info.defaultvisual,
                 &info.fixedcolors);
      depth = vinfo.depth;
      if (owncol || !info.defaultvisual) {
         colormap = XCreateColormap(display, XRootWindow(display, scrno),
                                    vinfo.visual, AllocNone);
         pixmap = XCreatePixmap(display, XRootWindow(display, scrno), 1, 1,
                                depth);
         topLevel = XtVaAppCreateShell(Appname, Appclass,
                                       applicationShellWidgetClass, display,
                                       XtNvisual, vinfo.visual,
                                       XtNcolormap, colormap,
                                       XtNborderPixmap, pixmap,
                                       XtNdepth, depth,
                                       NULL);
      } else {
         colormap = XDefaultColormap(display, scrno);
         info.defaultcolormap = 1;
         topLevel = XtAppCreateShell(Appname, Appclass,
                                     applicationShellWidgetClass,
                                     display, NULL, 0);
      }
                                    
      XtVaSetValues(topLevel, XtNiconPixmap, gipsy, NULL);
      AttachXt(&context);
      if (AutoLayout) {
         panes = XtVaCreateManagedWidget("panes", panedWidgetClass, topLevel,
                                          NULL);
         menuview = XtVaCreateManagedWidget("mview", viewportWidgetClass, panes,
                                            XtNallowHoriz, False,
                                            XtNallowVert, True,
                                            XtNmin, Menuheight,
                                            NULL);
         menus = XtVaCreateManagedWidget("menus", boxWidgetClass, menuview, 
                                         XtNwidth, Width, XtNheight, Menuheight,
                                         NULL);
         textview = XtVaCreateManagedWidget("tview", viewportWidgetClass, panes,
                                            XtNallowHoriz, False,
                                            XtNallowVert, True,
                                            XtNmin, Textheight,
                                            NULL);
         texts = XtVaCreateManagedWidget("texts", boxWidgetClass, textview, 
                                         XtNwidth, Width, XtNheight, Textheight,
                                         NULL);
         buttview = XtVaCreateManagedWidget("bview", viewportWidgetClass, panes,
                                            XtNallowHoriz, False,
                                            XtNallowVert, True,
                                            XtNmin, Buttonheight,
                                            NULL);
         buttons = XtVaCreateManagedWidget("menus", boxWidgetClass, buttview, 
                                        XtNwidth, Width, XtNheight, Buttonheight,
                                        NULL);
         gauges = texts; /* +++ for the time being? */
         dials  = texts; /* +++ for the time being? */
      } else {
         basewin = XtVaCreateManagedWidget("panes", formWidgetClass, topLevel,
                                          NULL);
                                      /* same composite widget for everything */
         dials=gauges=buttons=texts=menus=basewin;
         XtOverrideTranslations(basewin, XtParseTranslationTable(base_trans));
         if (!RealizeLater)
         (void)XtVaCreateManagedWidget("sizer", coreWidgetClass, basewin,
                                    XtNwidth, 1, XtNheight, 1,
                                    XtNleft, XtChainLeft, XtNright, XtChainLeft,
                                    XtNtop,  XtChainTop, XtNbottom, XtChainTop,
                                    XtNfromHoriz, NULL, XtNfromVert, NULL,
                                    XtNhorizDistance, Width,
                                    XtNvertDistance, Height,
                                    XtNborderWidth, 0,
                                    NULL);
      }
      XtAppAddActions(context, actions, n_actions);
      helpshell = XtVaCreatePopupShell("helpshell", overrideShellWidgetClass,
                             topLevel,
                             XtNallowShellResize, True,
                             XtNvisual, vinfo.visual,
                             XtNcolormap, colormap,
                             XtNborderPixmap, pixmap,
                             XtNdepth, depth,
                             NULL);
      helptext = XtVaCreateManagedWidget("helptext", labelWidgetClass,
                   helpshell,  NULL);
      errorshell = XtVaCreatePopupShell("errorshell", overrideShellWidgetClass,
                             topLevel,
                             XtNallowShellResize, True,
                             XtNvisual, vinfo.visual,
                             XtNcolormap, colormap,
                             XtNborderPixmap, pixmap,
                             XtNdepth, depth,
                             NULL);
      errortext = XtVaCreateManagedWidget("errortext", labelWidgetClass,
                   errorshell, NULL);
      docshell  = XtVaCreateManagedWidget("docshell", transientShellWidgetClass,
                            topLevel,
                            XtNallowShellResize, True,
                            XtNvisual, vinfo.visual,
                            XtNcolormap, colormap,
                            XtNborderPixmap, pixmap,
                            XtNdepth, depth,
                            NULL);
      doctext   = XtVaCreateManagedWidget("doctext", asciiTextWidgetClass,
                   docshell, XtNscrollHorizontal, XawtextScrollWhenNeeded,
                   XtNscrollVertical, XawtextScrollWhenNeeded,
                   XtNdisplayCaret, False, XtNtype, XawAsciiFile,
                   XtNstring, "/dev/null", NULL);
      top = topLevel;
      if (!RealizeLater) GgiRealize();
      status_cb(showstatus); /* receive copy of status messages from status_c */
   }
/*
 *  schedule and call event log file handler
 */
   if (!AutoLayout) {
      (void)ScheduleKeyevent(handle_logfile, "GGILOGFILE=", KEYCHANGE, NULL);
      handle_logfile(NULL, "GGILOGFILE=", KEYCHANGE, NULL);
      (void)ScheduleKeyevent(get_bool, "GGILOG=", KEYCHANGE, &logging);
      get_bool(NULL, "GGILOG=", KEYCHANGE, &logging);
      (void)ScheduleKeyevent(get_bool, "GGILOGDELAY=", KEYCHANGE, &logintv);
      get_bool(NULL, "GGILOGDELAY=", KEYCHANGE, &logintv);
   }
      
   return result;
}

/* ========================================================================== */
/*                                 GgiTextField                               */
/* -------------------------------------------------------------------------- */
ident GgiTextField(char *keyword, char *message, int size)
{
   Element   current=New(_Element);
   TextInput curtext=New(_TextInput);
   fchar value;
   fint  nchars;

   static String trans =
      "Ctrl<Key>A:	disarmhelp() text-bol() \n\
       Ctrl<Key>B:	disarmhelp() backward-char() \n\
       Ctrl<Key>D:	disarmhelp() textchanged() delete-next-char() \n\
       Ctrl<Key>E:	disarmhelp() text-eol() \n\
       Ctrl<Key>F:	disarmhelp() forward-char() \n\
       Ctrl<Key>H:	disarmhelp() textchanged() delete-previous-char() \n\
       Ctrl<Key>K:	disarmhelp() textchanged() text-kill() \n\
       Ctrl<Key>U:      disarmhelp() text-bol() text-kill() \n\
       <Key>Home:	disarmhelp() text-bol() \n\
       :<Key>KP_Home:	disarmhelp() text-bol() \n\
       <Key>End:	disarmhelp() text-eol() \n\
       :<Key>KP_End:	disarmhelp() text-eol() \n\
       <Key>Right:	disarmhelp() forward-char() \n\
       :<Key>KP_Right:	disarmhelp() forward-char() \n\
       <Key>Left:	disarmhelp() backward-char() \n\
       :<Key>KP_Left:	disarmhelp() backward-char() \n\
       <Key>Down:	stepfocus(previous) \n\
       :<Key>KP_Down:	stepfocus(previous) \n\
       <Key>Tab:	stepfocus(previous) \n\
       <Key>Up:		stepfocus(next) \n\
       :<Key>KP_Up:	stepfocus(next) \n\
       <Key>Delete:	disarmhelp() textchanged() delete-previous-char() \n\
       :<Key>KP_Delete: disarmhelp() textchanged() delete-previous-char() \n\
       <Key>BackSpace:	disarmhelp() textchanged() delete-previous-char() \n\
       <Key>Return:	textchanged() text-enter() \n\
       :<Key>KP_Enter:	textchanged() text-enter() \n\
       <Key>Escape:     disarmhelp() text-bol() text-kill() \n\
       ~Alt ~Ctrl<Key>: disarmhelp() textchanged() insert-char() \n\
       <EnterWindow>:	armhelp() \n\
       <LeaveWindow>:	disarmhelp() \n\
       <FocusIn>:	text-caret(on) \n\
       <FocusOut>:	text-caret(off) \n\
       <Btn1Down>:	disarmhelp() textfocus() select-start() \n\
       <Btn1Motion>:	extend-adjust() \n\
       <Btn1Up>:	extend-end(PRIMARY, CUT_BUFFER0) \n\
       <Btn2Down>:	disarmhelp() textfocus() textchanged() insert-selection(PRIMARY, CUT_BUFFER0) \n\
       <Btn3Down>:	disarmhelp() textfocus() textchanged() text-bol() \
                        text-kill() \n\
       <Btn3Down>(2):   insert-selection(PRIMARY, CUT_BUFFER0)";
       
   static XtTranslations ctrans=NULL;

   if (initialize()) return (ident)NULL;
   if (!ctrans) ctrans = XtParseTranslationTable(trans);
   current->next = ellist;
   ellist = current;
   current->type = TEXT;
   current->methods = methods+TEXT;
   current->implementation = curtext;
   current->keyword = StrDup(keyword);
   current->label   = StrDup(keyword);
   current->message = StrDup(message);
   curtext->curstr  = NNew(TEXTLENGTH+1,char);
   curtext->prvstr  = NNew(TEXTLENGTH+1,char);
   curtext->length  = TEXTLENGTH;
   curtext->box = XtVaCreateManagedWidget("textfield", boxWidgetClass, texts,
                         XtNborderWidth, 0, XtNresizable, TRUE,
                         XtNorientation, XtorientHorizontal,
                         XtNhSpace, 1, XtNvSpace, 1,
                         NULL);
   current->shell = texts;
   while(!XtIsShell(current->shell)) current->shell = XtParent(current->shell); 
   curtext->key = XtVaCreateManagedWidget(current->label, labelWidgetClass,
                         curtext->box,
                         XtNborderWidth, 0,
                         XtNjustify, XtJustifyRight,
                         NULL);
   current->outer = curtext->box;
   {
      Dimension width;
      XtVaGetValues(curtext->key, XtNwidth, &width, NULL);
      if (width<MINLABWIDTH) {
         XtVaSetValues(curtext->key, XtNwidth, MINLABWIDTH, NULL);
      }
   }
   curtext->inp = XtVaCreateManagedWidget("text", textfieldWidgetClass,
                         curtext->box,
                         XtNlength, curtext->length, /* maximum string length */
                         XtNwidth, size*7+7,  /* approx. size characters wide */
                         XtNdisplayCaret, False,
                         NULL);
   
   current->active = curtext->inp;
   curtext->position = -1;

   XtVaSetValues(current->active, XtNtranslations, ctrans, NULL);
   value.a = curtext->curstr;
   value.l = curtext->length;
   nchars = userftext(value, 2, keyword, " ");
   curtext->curstr[nchars] = '\0';
   XtVaSetValues(current->active, XtNstring, curtext->curstr, NULL);
   strcpy(curtext->prvstr, curtext->curstr);
   current->keyevent = ScheduleKeyevent(current->methods->keychanged, keyword,
                                        KEYCHANGE|KEYREJECT, current);
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiButton                                  */
/* -------------------------------------------------------------------------- */
ident GgiButton(char *keyword, char *message)
{
   Element     current=New(_Element);
   ButtonInput curbutt=New(_ButtonInput);
   bool state=toflog(FALSE);

   String trans =
        "<Btn1Down>,<Btn1Up>:   toggle() notify()\n\
         <EnterWindow>:         highlight(Always) armhelp()\n\
         <LeaveWindow>:         disarmhelp() unhighlight()";

   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = BUTTON;
   current->methods = methods+BUTTON;
   current->implementation = curbutt;
   current->keyword = StrDup(keyword);
   current->label   = StrDup(keyword);
   current->message = StrDup(message);
   current->label[strlen(current->label)-1] = '\0'; /* strip = sign */
   curbutt->inp = XtVaCreateManagedWidget(current->label, toggleWidgetClass,
                                          buttons, NULL);
   current->outer = curbutt->inp;
   XtAddCallback(curbutt->inp, XtNcallback, button_pressed, current);
   current->active = curbutt->inp;
   XtOverrideTranslations(curbutt->inp,XtParseTranslationTable(trans));
   (void)userflog(&state, 1, 2, keyword, " ");
   XtVaSetValues(current->active, XtNstate, tobool(state)?True:False, NULL);
   current->keyevent = ScheduleKeyevent(current->methods->keychanged, keyword,
                                         KEYCHANGE, current);
   InsertElement(current);
   return (ident)current;
}   

/* ========================================================================== */
/*                                 GgiMenu                                    */
/* -------------------------------------------------------------------------- */
ident GgiMenu(char *keyword, char *message, char **labels)
{
   Element   current=New(_Element);
   MenuInput curmenu=New(_MenuInput);
   Widget    menu;
   int       i=0;
   fint      state=-1;
   
   String trans =
        "<BtnDown>:      menu_btn() disarmhelp() reset() PopupMenu()\n\
         <EnterWindow>:  highlight() armhelp()\n\
         <LeaveWindow>:  disarmhelp() reset()";

   char menuname[KEYLEN+6];

   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = MENU;
   current->methods = methods+MENU;
   current->keyword = StrDup(keyword);
   current->message = StrDup(message);
   current->label   = StrDup(keyword);
   current->implementation = curmenu;
   current->label[strlen(current->label)-1] = '\0'; /* strip = sign */
   sprintf(menuname, "menu_%s", current->keyword);
   menuname[strlen(menuname)-1] = '\0';
   curmenu->name = StrDup(menuname);
   curmenu->inp = XtVaCreateManagedWidget(current->label, menuButtonWidgetClass,
                                       menus,
                                       XtNshapeStyle, XmuShapeRectangle,
                                       XtNmenuName, curmenu->name,
                                       NULL);
   current->outer = curmenu->inp;
   XtOverrideTranslations(curmenu->inp,XtParseTranslationTable(trans));
   menu = XtVaCreatePopupShell(curmenu->name, simpleMenuWidgetClass,
                                       curmenu->inp,
                                       XtNvisual, vinfo.visual,
                                       XtNcolormap, colormap,
                                       XtNborderPixmap, pixmap,
                                       XtNdepth, depth,
                             NULL);
   curmenu->shell = menu;
   (void)userfint(&state, 1, 2, keyword, " ");
   while (*labels) {
      MenuEntry entry=New(_MenuEntry);
      entry->next = curmenu->entries;
      curmenu->entries = entry;
      if (!strcmp(*labels,"_LINE")) {
         entry->inp = XtVaCreateManagedWidget(*labels, smeLineObjectClass,
                                              menu, NULL);
      } else if (!strcmp(*labels,"_SPACE")) {
         entry->inp = XtVaCreateManagedWidget(*labels, smeObjectClass,
                         menu, XtNheight, 5, NULL);
      } else if (**labels=='_') {
         entry->inp = XtVaCreateManagedWidget("menutitle", smeBSBObjectClass,
                         menu, XtNlabel,(*labels)+1, XtNsensitive, False, NULL);
      } else {
         entry->inp = XtVaCreateManagedWidget(*labels, smeBSBObjectClass,
                                              menu, XtNleftMargin, 17, NULL);
      }
      entry->menu = (struct _MenuInput *)curmenu;
      entry->num = i;
      entry->key = current->keyword;
      if (state==i) {
         XtVaSetValues(entry->inp, XtNleftBitmap, mark, NULL);
      }
      XtAddCallback(entry->inp, XtNcallback, menu_select, entry);
      labels++;
      i++;
   }
   current->active = curmenu->inp;
   current->keyevent = ScheduleKeyevent(current->methods->keychanged, keyword,
                                        KEYCHANGE, current);
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiList                                    */
/* -------------------------------------------------------------------------- */
ident GgiList(char *keyword, int defcol, char **labels)
{
   Element   current=New(_Element);
   ListInput curlist=New(_ListInput);
   
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = LIST;
   current->methods = methods+LIST;
   current->keyword = StrDup(keyword);
   current->label   = StrDup(keyword);
   current->label[strlen(current->label)-1] = '\0'; /* strip = sign */
   current->implementation = curlist;
   curlist->inp = XtVaCreateManagedWidget(current->label, listWidgetClass,
                                     texts,
                                     XtNdefaultColumns, abs(defcol),
                                     XtNforceColumns, (Boolean)(defcol<0),
                                     XtNlist, labels,
                                     XtNborderWidth, 0,
                                     NULL);
   current->outer = curlist->inp;
   XtAddCallback(curlist->inp, XtNcallback, list_select, current);
   current->active = curlist->inp;
   current->keyevent = ScheduleKeyevent(current->methods->keychanged, keyword,
                                        KEYREJECT, current);
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiGauge                                   */
/* -------------------------------------------------------------------------- */
ident GgiGauge(char *keyword, char *message, int length,
               float minval, float maxval)
{
   Element    current =New(_Element);
   GaugeInput curgauge=New(_GaugeInput);
   float      value=0.0;
   float      max;
    
   String trans =
      "<Btn2Down>:     disarmhelp() LockIncrement()\n\
       <Btn3Down>:     disarmhelp() LockIncrement()\n\
       <Btn1Down>:     disarmhelp() LockSelect()\n\
       <Btn1Motion>:   LockDrag()\n\
       <Btn1Up>:       Release()\n\
       <EnterWindow>:  armhelp()\n\
       <LeaveWindow>:  disarmhelp()";
    
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = GAUGE;
   current->methods = methods+GAUGE;
   current->implementation = curgauge;
   current->keyword = StrDup(keyword);
   current->message = StrDup(message);
   current->label   = StrDup(keyword);
   curgauge->box = XtVaCreateManagedWidget("gaugebox", boxWidgetClass, gauges,
                         XtNborderWidth, 0, XtNresizable, TRUE,
                         XtNorientation, XtorientHorizontal,
                         XtNhSpace, 1, XtNvSpace, 1, 
                         NULL);
   current->outer = curgauge->box;
   curgauge->key = XtVaCreateManagedWidget(current->label, labelWidgetClass,
                         curgauge->box,
                         XtNborderWidth, 0,
                         XtNjustify, XtJustifyRight,
                         NULL);
   curgauge->min   = minval;
   curgauge->max   = maxval;
   curgauge->steps = (length?length:Width/2)-3;
   max             = (float)curgauge->steps;
   {
      Dimension width;
      XtVaGetValues(curgauge->key, XtNwidth, &width, NULL);
      if (width<MINLABWIDTH) {
         XtVaSetValues(curgauge->key, XtNwidth, MINLABWIDTH, NULL);
      }
   }
   curgauge->inp = XtVaCreateManagedWidget("gauge", xabSlider2WidgetClass,
                         curgauge->box,
                         XtNorientation, XabHORIZONTAL,
                         XtNwidth, length?length:Width/2, XtNheight, 19,
                         XtNminimum, 0,   XtNmaximum,  curgauge->steps,
                         XtNlowValue,  0, XtNhighValue, 10,
                         XtNincrement, 1,
                         XtNprocessingDirection, XabMAX_ON_RIGHT,
                         NULL);
   current->active = curgauge->inp;
   XtVaSetValues(curgauge->inp, XtNhighValue, 0, NULL);
   XtAddCallback(curgauge->inp, XtNdragCallback, gauge_scrolled, current);
   XtAddCallback(curgauge->inp, XtNincrementCallback, gauge_scrolled, current);
   XtAddCallback(curgauge->inp, XtNlockSelectCallback, gauge_scrolled, current);
   XtAddCallback(curgauge->inp, XtNlockDragCallback, gauge_scrolled, current);
   XtAddCallback(curgauge->inp, XtNlockIncrementCallback, gauge_scrolled, current);
   XtAddCallback(curgauge->inp, XtNselectCallback, gauge_scrolled, current);
   value = getreal(keyword);
   if (value==BLANK) value = 0.0;
   curgauge->prvstr = NNew(VALLENGTH+1,char);
   curgauge->curstr = NNew(VALLENGTH+1,char);
   strcpy(curgauge->fmt, "%f");
   sprintf(curgauge->curstr, "%f", value);
   strcpy(curgauge->prvstr, curgauge->curstr);
   value = max*(value-minval)/(maxval-minval);
   value = value<0.0?0.0:(value>max?max:value);
   XtVaSetValues(current->active, XtNhighValue, (int)value,
                                  XtNlowValue,  (int)value, NULL);
   XtVaSetValues(current->active,
                 XtNtranslations, XtParseTranslationTable(trans), NULL);
   current->keyevent = ScheduleKeyevent(current->methods->keychanged, keyword,
                                         KEYCHANGE, current);
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiDial                                    */
/* -------------------------------------------------------------------------- */
ident GgiDial(char *keyword, char *message, int radius, int minang, int maxang,
              int intervals, float arrw, float innarrl, float outarrl,
              float minval, float maxval)
{
   Element    current =New(_Element);
   DialInput  curdial =New(_DialInput);
   float      value=0.0;
   float      max;
    
   String trans =
      "<Btn1Down>:     disarmhelp() set()\n\
       <Btn1Motion>:   set()\n\
       <Btn2Down>:     disarmhelp() increment(-1)\n\
       <Btn3Down>:     disarmhelp() increment(+1)\n\
       <BtnUp>:        notify()\n\
       <EnterWindow>:  armhelp()\n\
       <LeaveWindow>:  disarmhelp()";
    
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = DIAL;
   current->methods = methods+DIAL;
   current->implementation = curdial;
   current->keyword = StrDup(keyword);
   current->message = StrDup(message);
   current->label   = StrDup(keyword);
   curdial->box = XtVaCreateManagedWidget("dialbox", boxWidgetClass, gauges,
                         XtNborderWidth, 0, XtNresizable, TRUE,
                         XtNorientation, XtorientVertical,
                         XtNhSpace, 1, XtNvSpace, 1, 
                         NULL);
   current->outer = curdial->box;
   curdial->key = XtVaCreateManagedWidget(current->label, labelWidgetClass,
                         curdial->box,
                         XtNborderWidth, 0,
                         XtNjustify, XtJustifyRight,
                         NULL);
   curdial->min   = minval;
   curdial->max   = maxval;
   max            = (float)DIALSTEPS;
   {
      Dimension width;
      XtVaGetValues(curdial->key, XtNwidth, &width, NULL);
      if (width<MINLABWIDTH) {
         XtVaSetValues(curdial->key, XtNwidth, MINLABWIDTH, NULL);
      }
   }
   curdial->inp = XtVaCreateManagedWidget("dial", xfwfRheostatWidgetClass,
                         curdial->box,
                         XtNradius, radius,
                         XtNminimumAngle,     minang,
                         XtNmaximumAngle,     maxang,
                         XtNouterArrowLength, (int)(((float)radius)*outarrl),
                         XtNinnerArrowLength, (int)(((float)radius)*innarrl),
                         XtNarrowWidth,       (int)(((float)radius)*arrw),
                         XtNnumberIntervals,  intervals,
                         XtNtickGravity,      False,
                         XtNfillArrow,        True,
                         XtNminimumValue, 0,  XtNmaximumValue, DIALSTEPS,
                         NULL);
   current->active = curdial->inp;
   XtAddCallback(curdial->inp, XtNnotify, dial_changed, current);
   XtAddCallback(curdial->inp, XtNsetCallback, dial_changed, current);
   value = getreal(keyword);
   if (value==BLANK) value = 0.0;
   curdial->prvstr = NNew(VALLENGTH+1,char);
   curdial->curstr = NNew(VALLENGTH+1,char);
   strcpy(curdial->fmt, "%f");
   sprintf(curdial->curstr, "%f", value);
   strcpy(curdial->prvstr, curdial->curstr);
   value = max*(value-minval)/(maxval-minval);
   value = value<0.0?0.0:(value>max?max:value);
   XtVaSetValues(current->active, XtNvalue, (int)value, NULL);
   XtVaSetValues(current->active,
                 XtNtranslations, XtParseTranslationTable(trans), NULL);
   current->keyevent = ScheduleKeyevent(current->methods->keychanged, keyword,
                                         KEYCHANGE, current);
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiLabel                                   */
/* -------------------------------------------------------------------------- */
ident GgiLabel(char *text)
{
   Element current=New(_Element); 
   Label   currlab=New(_Label);
    
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = LABEL;
   current->methods = methods+LABEL;
   current->implementation = currlab;
   current->label = StrDup(text);
   currlab->text  = XtVaCreateManagedWidget(current->label, labelWidgetClass,
                                            texts, XtNborderWidth, 0, NULL);
   current->outer = currlab->text;
   InsertElement(current);
   return (ident)current;
}
   
/* ========================================================================== */
/*                                 GgiLogo                                    */
/* -------------------------------------------------------------------------- */
/* special label object, displays logo (default: built-in GIPSY logo)
 */
ident GgiLogo(unsigned char bits[], int width, int height)
{
   Element current=New(_Element); 
   Label   currlab=New(_Label);
   Pixmap  logo;
   String trans =
        "<EnterWindow>:  logoinfo(enter)\n\
         <LeaveWindow>:  logoinfo(leave)";

    
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = LABEL;
   current->methods = methods+LABEL;
   current->implementation = currlab;
   current->label = "logo";
   if (bits) {
      logo = XCreateBitmapFromData(XtDisplay(top), XRootWindow(XtDisplay(top), scrno),
                                   (char*)bits, width, height);
   } else {
      logo = gipsy;
   }
   currlab->text  = XtVaCreateManagedWidget(current->label, labelWidgetClass,
                                            texts, XtNborderWidth, 0,
                                            XtNcursor,
                                     XCreateFontCursor(XtDisplay(top),XC_star),
                                            XtNbitmap, logo, NULL);
   current->outer = currlab->text;
   XtVaSetValues(currlab->text,
                 XtNtranslations, XtParseTranslationTable(trans), NULL);
   InsertElement(current);
   return (ident)current;
}
   
/* ========================================================================== */
/*                                 GgiCanvas                                  */
/* -------------------------------------------------------------------------- */
ident GgiCanvas(char *name, int width, int height)
{
   Element current=New(_Element);
   Canvas  curcanv=New(_Canvas);
    
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = CANVAS;
   current->methods = methods+CANVAS;
   current->implementation = curcanv;
   curcanv->canvas = XtVaCreateManagedWidget(name, simpleWidgetClass, texts,
                                             XtNwidth, width, XtNheight, height,
                                             XtNborderWidth, 0,
                                             NULL);
   current->outer  = curcanv->canvas;
   current->active = curcanv->canvas;
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiShell                                   */
/* -------------------------------------------------------------------------- */
ident GgiShell(char *name)
{
   Element current=New(_Element);
   Shell   curshell=New(_Shell);
    
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = SHELL;
   current->methods = methods+SHELL;
   current->implementation = curshell;
#if TOPPOP
   curshell->shell = XtVaAppCreateShell(name, Appclass,
                                 applicationShellWidgetClass,
                                 XtDisplay(top), 
                                       XtNvisual, vinfo.visual,
                                       XtNcolormap, colormap,
                                       XtNborderPixmap, pixmap,
                                       XtNdepth, depth,
                                 XtNallowShellResize, True, NULL);
#else
   curshell->shell = XtVaCreatePopupShell(name, transientShellWidgetClass,
                                 top,
                                       XtNvisual, vinfo.visual,
                                       XtNcolormap, colormap,
                                       XtNborderPixmap, pixmap,
                                       XtNdepth, depth,
                                 XtNallowShellResize, True, NULL);
#endif
   curshell->form  = XtVaCreateManagedWidget(name, formWidgetClass, 
                                             curshell->shell, NULL);
   XtOverrideTranslations(curshell->shell, XtParseTranslationTable(base_trans));
   current->outer  = curshell->form;
   current->active = curshell->shell;
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiDialog                                  */
/* -------------------------------------------------------------------------- */
ident GgiDialog(char *name)
{
   Element current=New(_Element);
   Shell   curshell=New(_Shell);
    
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = SHELL;
   current->methods = methods+SHELL;
   current->implementation = curshell;
   curshell->dialog = TRUE;
   curshell->shell = XtVaCreatePopupShell(name, transientShellWidgetClass,
                                 top,
                                       XtNvisual, vinfo.visual,
                                       XtNcolormap, colormap,
                                       XtNborderPixmap, pixmap,
                                       XtNdepth, depth,
                                 XtNallowShellResize, True, NULL);
   curshell->form  = XtVaCreateManagedWidget(name, formWidgetClass, 
                                             curshell->shell, NULL);
   current->outer  = curshell->form;
   current->active = curshell->shell;
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiForm                                    */
/* -------------------------------------------------------------------------- */
ident GgiForm(char *name, int border)
{
   Element current=New(_Element);
   Form   curform=New(_Form);
    
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = FORM;
   current->methods = methods+FORM;
   current->implementation = curform;
   curform->form  = XtVaCreateManagedWidget(name, formWidgetClass, texts,
                                            XtNborderWidth, border, NULL);
   current->outer  = curform->form;
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiViewport                                */
/* -------------------------------------------------------------------------- */
ident GgiViewport(char *keyword, int width, int height)
{
   Element current=New(_Element);
   Vwport  curvwport=New(_Vwport);
    
   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = VWPORT;
   current->methods = methods+VWPORT;
   current->keyword = StrDup(keyword);
   current->implementation = curvwport;
   curvwport->vwport  = XtVaCreateManagedWidget(keyword, viewportWidgetClass,
                           texts, /*XtNwidth, width, XtNheight, height,*/
                           XtNallowHoriz, True,
                           XtNallowVert,  True,
                           XtNuseRight,   True,
                           XtNuseBottom,  True, 
                           NULL);
   if (width) XtVaSetValues(curvwport->vwport, XtNwidth, width, NULL);
   if (height) XtVaSetValues(curvwport->vwport, XtNheight, height, NULL);
   curvwport->form = XtVaCreateManagedWidget(keyword, formWidgetClass,
                           curvwport->vwport,
                           XtNborderWidth, 0, NULL);
   current->outer  = curvwport->vwport;
   current->keyevent = ScheduleKeyevent(current->methods->keychanged, keyword,
                                         KEYCHANGE, current);
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiProgress                                */
/* -------------------------------------------------------------------------- */
ident GgiProgress(char *keyword, char *message, int length)
{
   Element    current =New(_Element);
   Progress   curprog=New(_Progress);
   float      value=0.0;
    
   String trans =
    "<EnterWindow>:  armhelp()\n\
     <LeaveWindow>:  disarmhelp()";

   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = PROGRESS;
   current->methods = methods+PROGRESS;
   current->implementation = curprog;
   current->keyword = StrDup(keyword);
   current->message = StrDup(message);
   value = getreal(keyword);
   if (value==BLANK) value = 0.0;
   curprog->gauge = XtVaCreateManagedWidget("progress", xabSlider2WidgetClass,
                       texts,
                       XtNorientation, XabHORIZONTAL,
                       XtNwidth, length?length:Width/2, XtNheight, 11,
                       XtNminimum, 0,   XtNmaximum, 1000,
                       XtNlowValue,  0, XtNhighValue, 10,
                       XtNincrement, 1,
                       XtNprocessingDirection, XabMAX_ON_RIGHT,
                       NULL);
   XtVaSetValues(curprog->gauge, XtNhighValue, 0,
                 XtNindicatorThickness, (int)(value*length),
                 XtNtranslations, XtParseTranslationTable(trans),
                 NULL);
   curprog->length = length;
   current->outer = curprog->gauge;
   current->active = curprog->gauge;
   current->keyevent = ScheduleKeyevent(current->methods->keychanged, keyword,
                                         KEYCHANGE, current);
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiEditor                                  */
/* -------------------------------------------------------------------------- */
ident GgiEditor(char *keyword, int width, int height, int mode,
                char *contents, int size)
{
   Element    current=New(_Element);
   Editor     curedit=New(_Editor);
   XawTextEditType editmode;
    
   String trans = "<Btn1Down>:     editfocus() select-start()";

   if (initialize()) return (ident)NULL;
   current->next = ellist;
   ellist = current;
   current->type = EDITOR;
   current->methods = methods+EDITOR;
   current->implementation = curedit;
   current->keyword = StrDup(keyword);
   curedit->mode = mode;
   curedit->size = size;
   switch (curedit->mode) {
      case ggiReadOnly: editmode = XawtextRead; break;
      case ggiEdit:     editmode = XawtextEdit; break;
      case ggiAppend:   editmode = XawtextAppend; break;
      default:          editmode = XawtextRead; break;
   }
   curedit->text = XtVaCreateManagedWidget(
                      curedit->mode==ggiReadOnly?"Viewer":"Editor",
                      asciiTextWidgetClass, texts,
                      XtNtopMargin, 4,
                      XtNbottomMargin, 4,
                      XtNleftMargin, 4,
                      XtNrightMargin, 4,
                      XtNscrollHorizontal, XawtextScrollWhenNeeded,
                      XtNscrollVertical, XawtextScrollWhenNeeded,
                      XtNdisplayCaret, curedit->mode==ggiReadOnly?False:True,
                      XtNeditType, editmode,
                      XtNtype, size<0?XawAsciiFile:XawAsciiString,
                      XtNstring, contents,
                      XtNwidth, width, XtNheight, height,
                      size>0?XtNlength:NULL, size,
                      XtNuseStringInPlace, True, NULL);
   XtVaGetValues(curedit->text, XtNtextSource, &curedit->source, NULL);
   XtAddCallback(curedit->source, XtNcallback, editor_changed, current);
   current->outer  = curedit->text;
   current->active = curedit->text;
   XtOverrideTranslations(curedit->text, XtParseTranslationTable(trans));
   if (curedit->mode != ggiReadOnly && current->keyword) {
      current->keyevent = ScheduleKeyevent(current->methods->keychanged,
                                           keyword, KEYCHANGE, current);
   }
   InsertElement(current);
   return (ident)current;
}

/* ========================================================================== */
/*                                 GgiEditorSetSource                         */
/* -------------------------------------------------------------------------- */
ident GgiEditorSetSource(ident id, char *source, int size)
{
   Element current=(Element)id;
   Editor  curedit=(Editor)current->implementation;
    
   XtVaSetValues(current->active, XtNstring, source,
                                  size>0?XtNlength:NULL, size,
                                  NULL);
   curedit->changed = FALSE;
   if (current->keyword) wkeyf(current->keyword);
   return id;
}

/* ========================================================================== */
/*                                 GgiEditorSource                            */
/* -------------------------------------------------------------------------- */
char *GgiEditorSource(ident id)
{
   Element current=(Element)id;
   char *result;
    
   XtVaGetValues(current->active, XtNstring, &result, NULL);
   return result;
}

/* ========================================================================== */
/*                                 GgiEditorSave                              */
/* -------------------------------------------------------------------------- */
ident GgiEditorSave(ident id)
{
   Element current=(Element)id;
   Editor  curedit=(Editor)current->implementation;
    
   if (curedit->changed) {
      curedit->changed = FALSE;
      XawAsciiSave(curedit->source);
      if (current->keyword) wkeyf(current->keyword);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiEditorSaveAs                            */
/* -------------------------------------------------------------------------- */
ident GgiEditorSaveAs(ident id, char *name)
{
   Element current=(Element)id;
   Editor  curedit=(Editor)current->implementation;
    
   XawAsciiSaveAsFile(curedit->source, name);
   if (curedit->changed && current->keyword) wkeyf(current->keyword);
   curedit->changed = FALSE;

   return id;
}

/* ========================================================================== */
/*                                 GgiEditorSearch                            */
/* -------------------------------------------------------------------------- */
ident GgiEditorSearch(ident id, int direction)
{
   Element current=(Element)id;
   XEvent event;
   char *forward="forward";
   char *backward="backward";
   
   XtCallActionProc(current->active, "search", &event,
                    direction>0?&forward:&backward, 1);
   return id;
}

/* ========================================================================== */
/*                                 GgiUseShell                                */
/* -------------------------------------------------------------------------- */
ident GgiUseShell(ident id)
{
   Element current=(Element)id;
   static Element previous=NULL;
   Element result=previous;
    
   if (current && (current->type==SHELL ||
                   current->type==FORM  )) {
      dials=gauges=buttons=texts=menus = current->outer;
      previous  = current;
   } else if (current && (current->type==VWPORT)) {
      dials=gauges=buttons=texts=menus =
         ((Vwport)(current->implementation))->form;
      previous  = current;
   } else {
      dials=gauges=buttons=texts=menus = basewin;
      previous = NULL;
   }
   container = previous;                            /* set current container */
   return (ident)result;
}

/* ========================================================================== */
/*                                 GgiShowShell                               */
/* -------------------------------------------------------------------------- */
void GgiShowShell(ident id, bool show)
{
   Element current=(Element)id;
   Shell  curshell=(Shell)current->implementation;
    
   if (show && !curshell->up) {
      if (curshell->dialog) {
         Window rootret, childret;
         int rx, ry, wx, wy;
         Dimension width, height;
         unsigned int maskret;
        
         if (!curshell->shown) {
            (void)XQueryPointer(XtDisplay(top),XtWindow(top), &rootret,
                                &childret, &rx, &ry, &wx, &wy, &maskret);
            XtPopup(curshell->shell, XtGrabNone);
            XtPopdown(curshell->shell);    /* allow sizes to be determined */
            XtVaGetValues(curshell->form, XtNwidth,  &width,
                                          XtNheight, &height, NULL);
/*!!*/            if ((rx+10+width) >= DisplayWidth(XtDisplay(top),0)) {
/*!!*/               rx = DisplayWidth(XtDisplay(top),0)-width-10;
            } else {
               rx += 10;
            }
/*!!*/            if ((ry+10+height) >= DisplayHeight(XtDisplay(top),0)) {
/*!!*/               ry = DisplayHeight(XtDisplay(top),0)-height-20;
            } else {
               ry += 10;
            }
            XtVaSetValues(curshell->shell, XtNx, rx, XtNy, ry, NULL);
         }
         XtPopup(curshell->shell, XtGrabNonexclusive);
      } else {
         XtPopup(curshell->shell, XtGrabNone);
      }
      if (!curshell->shown) {
                                               /* catch window manager close */
         Atom wm_del_win = XInternAtom(XtDisplay(top),
                                       "WM_DELETE_WINDOW", False);
         XSetWMProtocols(XtDisplay(top),XtWindow(curshell->shell),
                         &wm_del_win,1);
         XtOverrideTranslations(curshell->shell,
            XtParseTranslationTable("<Message>WM_PROTOCOLS: closewindow()\n"));
      }
      curshell->shown = TRUE;
      curshell->up = TRUE;
   } else if (curshell->up && !show) {
      XtPopdown(curshell->shell);
      curshell->up = FALSE;
   }
}

/* ========================================================================== */
/*                                 GgiDeactivate                              */
/* -------------------------------------------------------------------------- */
ident GgiDeactivate(ident id)
{
   Element current=(Element)id;
    
   if (current) {
      current->insensitive = 1;
      XtSetSensitive(current->outer, False);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiActivate                                */
/* -------------------------------------------------------------------------- */
ident GgiActivate(ident id)
{
   Element current=(Element)id;
    
   if (current) {
      current->insensitive = 0;
      XtSetSensitive(current->outer, True);
   }
   return id;
}

#if 0 /* doesn't seem to be necessary after all... */
/* ========================================================================== */
/*                                 GgiFollowKey                               */
/* -------------------------------------------------------------------------- */
ident GgiFollowKey(ident id, bool follow)
{
   Element current=(Element)id;
    
   if (current && current->methods->setfollow) {
      current->methods->setfollow(id, follow);
   }
   return id;
}
#endif

/* ========================================================================== */
/*                                 GgiHelpText                                */
/* -------------------------------------------------------------------------- */
ident  GgiHelpText(ident id, char *message)
{
   Element current=(Element)id;
   
   if (current) {
      Delete(current->message);
      current->message = StrDup(message);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiSetLabel                                */
/* -------------------------------------------------------------------------- */
ident GgiSetLabel(ident id, char *label, int width)
{
   Element current=(Element)id;
    
   if (current && current->methods->setlabel) {
      XtUnmanageChild(current->outer);
      XFlush(XtDisplay(top));
      current->methods->setlabel(id, label, width);
      XtManageChild(current->outer);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiSetBitmap                               */
/* -------------------------------------------------------------------------- */
ident GgiSetBitmap(ident id, int width, int height, unsigned char *bits)
{
   Element current=(Element)id;
    
   if (current && current->methods->setbitmap) {
      XtUnmanageChild(current->outer);
      current->methods->setbitmap(id, width, height, (char*)bits);
      XtManageChild(current->outer);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiAlignLabel                              */
/* -------------------------------------------------------------------------- */
ident GgiAlignLabel(ident id, ggiAlign al)
{
   Element current=(Element)id;
   if (current && current->methods->align) {
      XtUnmanageChild(current->outer);
      current->methods->align(id, al);
      XtManageChild(current->outer);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiSetRange                                */
/* -------------------------------------------------------------------------- */
ident GgiSetRange(ident id, float min, float max)
{
   Element current=(Element)id;
   if (current && current->methods->setrange) {
      current->methods->setrange(id, min, max);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiSetFormat                               */
/* -------------------------------------------------------------------------- */
ident GgiSetFormat(ident id, char *format)
{
   Element current=(Element)id;
   if (current && current->methods->setformat) {
      current->methods->setformat(id, format);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiSetCircular                             */
/* -------------------------------------------------------------------------- */
ident GgiSetCircular(ident id, bool circular)
{
   Element current=(Element)id;
   if (current && current->methods->setcirc) {
      current->methods->setcirc(id, circular);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiSetKeyword                              */
/* -------------------------------------------------------------------------- */
ident GgiSetKeyword(ident id, char *key)
{
   Element current=(Element)id;
   if (current && current->methods->keychanged) {
      DescheduleKeyevent(&current->keyevent);
      Delete(current->keyword);
      current->keyword = StrDup(key);
      current->keyevent = ScheduleKeyevent(current->methods->keychanged, key,
                                           KEYCHANGE|KEYREJECT, current);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiSetCleanup                              */
/* -------------------------------------------------------------------------- */
ident GgiSetCleanup(ident id, GgiCleanupProc proc, void *arg)
{
   Element current=(Element)id;
   if (current) {
      current->cleanup  = proc;
      current->cleanarg = arg;
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiMenuLabels                              */
/* -------------------------------------------------------------------------- */
ident GgiMenuLabels(ident id, char **labels)
{
   Element   current=(Element)id;
   MenuInput curmenu=(MenuInput)current->implementation;
   Widget    menu=curmenu->shell;
   MenuEntry entry;
   int i=0;
    
   XtDestroyWidget(menu);
   menu = XtVaCreatePopupShell(curmenu->name, simpleMenuWidgetClass, 
                               curmenu->inp,
                               XtNvisual, vinfo.visual,
                               XtNcolormap, colormap,
                               XtNborderPixmap, pixmap,
                               XtNdepth, depth,
                               NULL);
   curmenu->shell = menu;
   while (curmenu->entries) {
      entry = curmenu->entries;
      curmenu->entries = entry->next;
      Delete(entry);
   }                                             /* old labels now removed */
    
   while (*labels) {
      entry = New(_MenuEntry);
      entry->next = curmenu->entries;
      curmenu->entries = entry;
      if (!strcmp(*labels,"_LINE")) {
         entry->inp = XtVaCreateManagedWidget(*labels, smeLineObjectClass,
                                              menu, NULL);
      } else if (!strcmp(*labels,"_SPACE")) {
         entry->inp = XtVaCreateManagedWidget(*labels, smeObjectClass,
                         menu, XtNheight, 5, NULL);
      } else if (**labels=='_') {
         entry->inp = XtVaCreateManagedWidget("menutitle", smeBSBObjectClass,
                         menu, XtNlabel,(*labels)+1, XtNsensitive, False, NULL);
      } else {
         entry->inp = XtVaCreateManagedWidget(*labels, smeBSBObjectClass,
                                              menu, XtNleftMargin, 17, NULL);
      }
      entry->menu = (struct _MenuInput *)curmenu;
      entry->num = i;
      entry->key = current->keyword;
      XtAddCallback(entry->inp, XtNcallback, menu_select, entry);
      labels++;
      i++;
   }                                           /* new labels installed */
   wkeyf(current->keyword);                    /* undefine old menu index */
    
   return id;
}

/* ========================================================================== */
/*                                 GgiSetBtnKey                               */
/* -------------------------------------------------------------------------- */
ident GgiSetBtnKey(ident id, char *key)
{
   Element current=(Element)id;
   if (current->type==MENU) {
      MenuInput curmenu=(MenuInput)current->implementation;
      if (curmenu->btnkey) Delete(curmenu->btnkey);
      if (key) curmenu->btnkey = StrDup(key);
   }
   return id;
}

/* ========================================================================== */
/*                                 GgiListLabels                              */
/* -------------------------------------------------------------------------- */
ident GgiListLabels(ident id, char **labels)
{
   Element   current=(Element)id;
   ListInput curlist=(ListInput)current->implementation;

   XtUnmanageChild(current->outer);
   XawListChange(curlist->inp, labels, 0, 0, True);
   XtManageChild(current->outer);
   wkeyf(current->keyword);                    /* undefine old list index */
    
   return id;
}

/* ========================================================================== */
/*                                 GgiListColumns                             */
/* -------------------------------------------------------------------------- */
ident GgiListColumns(ident id, int columns)
{
   Element   current=(Element)id;
   ListInput curlist=(ListInput)current->implementation;

   XtUnmanageChild(current->outer);
   XtVaSetValues(curlist->inp, XtNdefaultColumns, abs(columns),
                               XtNforceColumns, (Boolean)(columns<0),
                               NULL);
   XtManageChild(current->outer);
    
   return id;
}

/* ========================================================================== */
/*                                 GgiWidth                                   */
/* -------------------------------------------------------------------------- */
int GgiWidth(ident id)
{
   Element current=(Element)id;
   Dimension width=0;
    
   if (current) {
      if (current->methods->getwidth) {
         width = current->methods->getwidth(id);
      } else {
         XtVaGetValues(current->outer, XtNwidth, &width, NULL);
      }
   }
   return (int)width;
}
   
/* ========================================================================== */
/*                                 GgiHeight                                  */
/* -------------------------------------------------------------------------- */
int GgiHeight(ident id)
{
   Element current=(Element)id;
   Dimension height=0;
    
   if (current) {
      if (current->methods->getheight) {
         height = current->methods->getheight(id);
      } else {
         XtVaGetValues(current->outer, XtNheight, &height, NULL); 
      }
   }
   return (int)height;
}

/* ========================================================================== */
/*                                 GgiSetPosition                             */
/* -------------------------------------------------------------------------- */
ident GgiSetPosition(ident id, int xfrom, ident xid, int yfrom, ident yid)
{
   Element current=(Element)id;
   Element xref=(Element)xid;
   Element yref=(Element)yid;
   Widget  curwdg=current->outer;
   Widget  xwdg, ywdg;
 
   xwdg = xref?xref->outer:NULL;
   ywdg = yref?yref->outer:NULL;

   if (xfrom) XtVaSetValues(curwdg, XtNhorizDistance, xfrom, NULL);
   if (yfrom) XtVaSetValues(curwdg, XtNvertDistance,  yfrom, NULL);
   XtVaSetValues(curwdg, XtNleft, XtChainLeft, XtNright, XtChainLeft,
                         XtNtop,  XtChainTop, XtNbottom, XtChainTop,
                         XtNfromHoriz, xwdg, XtNfromVert, ywdg, NULL);
   return id;
}

/* ========================================================================== */
/*                                 GgiDelete                                  */
/* -------------------------------------------------------------------------- */
void GgiDelete(ident *id)   
{
   Element current, *elp, *next;
   
   if (!id || !*id) return;
   deldepth++;
   current = (Element)*id;
   while (current->contents) {
      /* first delete any contained elements */
      GgiDelete((ident*)&current->contents->element);
   }
   RemoveElement(current);           /* remove element itself from container */
   current->methods->delete((ident)current);
   if (current->keyevent) DescheduleKeyevent(&current->keyevent);
   Delete(current->implementation);
   Delete(current->keyword);
   Delete(current->message);
   Delete(current->label);
   for (elp = &ellist; *elp; elp=next) {
      /* remove from global element list and delete */
      next = &(*elp)->next;
      if (*elp == current) {
         *elp = current->next;
         if (current->cleanup) {
            (void)ScheduleTimer(cleanup, 0, current);
         } else {
            Delete(current);
         }
         break;
      }
   }
   deldepth--;
}

/* ========================================================================== */
/*                                 GgiConfigure                               */
/* -------------------------------------------------------------------------- */
void GgiConfigure(char *appclass, int width, int menuheight, int textheight,
                  int buttonheight)
{
   if (appclass) strcpy(Appclass,appclass);
   if (width)        Width = width;
   if (menuheight)   Menuheight = menuheight;
   if (textheight)   Textheight = textheight;
   if (buttonheight) Buttonheight = buttonheight;
}

/* ========================================================================== */
/*                                 GgiAutoLayout                              */
/* -------------------------------------------------------------------------- */
void GgiAutoLayout(bool a)
{
   AutoLayout = a;
}

/* ========================================================================== */
/*                                 GgiPostponeRealize                         */
/* -------------------------------------------------------------------------- */
void GgiPostponeRealize(bool later)
{
   RealizeLater = later;
}

/* ========================================================================== */
/*                                 GgiRealize                                 */
/* -------------------------------------------------------------------------- */
void GgiRealize(void)
{
   static bool realized=FALSE;
   Atom wm_del_win;
   
   if (!realized) {
      if (!AutoLayout) GgiOptionsShell("_GGI_POP_OPTIONS=");
      XtRealizeWidget(top);
                                               /* catch window manager close */
      wm_del_win  = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False);
      XSetWMProtocols(XtDisplay(top),XtWindow(top),&wm_del_win,1);
      XtOverrideTranslations(top,
            XtParseTranslationTable("<Message>WM_PROTOCOLS: closewindow()\n"));
      realized = TRUE;
   }
}

/* ========================================================================== */
/*                                 GgiSetHeight                               */
/* -------------------------------------------------------------------------- */
void GgiSetHeight(int height)
{
   Height = height;
}

/* ========================================================================== */
/*                                 GgiSetWidth                                */
/* -------------------------------------------------------------------------- */
void GgiSetWidth(int width)
{
   Width = width;
}

/* ========================================================================== */
/*                                 GgiShowDoc                                 */
/* -------------------------------------------------------------------------- */
void GgiShowDoc(char *docname)
{
   static bool active=FALSE;
   
   if (active) XtPopdown(docshell);
   active = FALSE;
   if (docname) {
      XtVaSetValues(doctext, XtNstring, docname, XtNheight, 320, XtNwidth, 620, NULL);
      XtPopup(docshell, XtGrabNone);
      active = TRUE;
   }
}

/* ========================================================================== */
/*                                 GgiDisplay                                 */
/* -------------------------------------------------------------------------- */
Display *GgiDisplay(void)
{
   return XtDisplay(top);
}

/* ========================================================================== */
/*                                 GgiWindow                                  */
/* -------------------------------------------------------------------------- */
Window GgiWindow(ident id)
{
   Element current=(Element)id;
   return XtWindow(current->outer);
}

/* ========================================================================== */
/*                                 GgiWidget                                  */
/* -------------------------------------------------------------------------- */
Widget GgiWidget(ident id)
{
   Element current=(Element)id;
   return current->active;
}

/* ========================================================================== */
/*                                 GgiContext                                 */
/* -------------------------------------------------------------------------- */
XtAppContext *GgiContext(void)
{
   return &context;
}

/* ========================================================================== */
/*                                 GgiSetResources                            */
/* -------------------------------------------------------------------------- */
void GgiSetResources(char **res)
{
   String *resources=(String*)res;
   int nr1=0, nr2=0;
    
   while (def_fallback[nr1]) nr1++;
   while (resources[nr2]) nr2++;
   fallback = NNew(nr1+nr2+1,String);
   for (nr1=0; def_fallback[nr1]; nr1++)     fallback[nr1]  = def_fallback[nr1];
   for (nr2=0; resources[nr2];  nr1++, nr2++) fallback[nr1] = resources[nr2];
}

/* ========================================================================== */
/*                                 GgiIsRealized                              */
/* -------------------------------------------------------------------------- */
bool GgiIsRealized(ident id)
{
   Element current=(Element)id;
   return (bool)XtIsRealized(current->outer);
}

/* ========================================================================== */
/*                                 GgiHandleEvents                            */
/* -------------------------------------------------------------------------- */
int GgiHandleEvents(void)
{
   int result=0;
   
   if (top) {
      while (XtAppPending(context)) {
         XtAppProcessEvent(context, XtIMAll);
         result = 1;
      }
   }
   return result;
}

/* ========================================================================== */
/*                                 GgiSetDisplay                              */
/* -------------------------------------------------------------------------- */
void GgiSetDisplay(char *displayname)
{
   if (DisplayName) {
      Delete(DisplayName);
   }
   if (displayname && *displayname) {
      DisplayName = StrDup(displayname);
   }
}
#<

#>            ggiplot.c
/* ggiplot.c -XT
                           COPYRIGHT (c) 1998, 2001
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands
*/


#define MAXWINDOWS    20
#define MAXFRAMES    256
#define DEVICE       "GGI"
#define ACTIVE_SHAPE XC_crosshair
#define QUIET_SHAPE  XC_top_left_arrow
#define MAXCOLORS    256
#define NCOLORS       16

/*
 * includes:
 */

#include "limits.h"
#include "stdlib.h"
#include "string.h"
#include "stdarg.h"
#include "stdio.h"

#include "gipsyc.h"			/* GIPSY definitions */
#include "ctype.h"
#include "ggi.h"
#include "ggidisplay.h"
#include "ggiwindow.h"
#include "ggiwidget.h"
#include "ggicontext.h"
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>     /* Standard Name-String definitions */
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <X11/Xaw/Label.h>      /* defines XtNcursor */
#include "userfio.h"
#include "string.h"
#include "error.h"
#include "addpgdrv.h"
#include "ggixor.h"
#include "GgiInternal.H"

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}
#define INT(x)		( (int) ( (x) + 0.5 ) )
#define LONGBITS (sizeof(long)*8)
#define CMMLEN ((MAXCOLORS-1)/LONGBITS+1)
#define SETCOLOR(map,color) map[color/LONGBITS] |= 1<<(color%LONGBITS)

typedef unsigned char colx;                    /* PGPLOT color index       */

typedef struct {
   ident         canvas;                       /* Ggi canvas               */
   char          *name;                        /* PGPLOT device name       */
   Window        window;                       /* canvas' window           */
   Pixmap        pixmap8;                      /* backup frame for drawing */
   Widget        widget;                       /* canvas' widget           */
   GC            gc;                           /* graphics context         */
   XImage        *image;                       /* image for raster mode    */
   bool          xormode;                      /* XOR drawing mode         */ 
   int           width, height;                /* physical dimensions      */
   int           depth;                        /* the window's depth       */
   float         xorg, xscale, yorg, yscale;   /* PGPLOT scaling info      */
   GgiCursorProc proc;                         /* cursor read-out callback */
   void          *arg;                         /* callback client data     */
   bool          picture;                      /* picture started flag     */
   bool          initialized;                  /* canvas initialized flag  */
   unsigned int  cursor;                       /* cursor type              */
   Pixmap        frames[MAXFRAMES];            /* recording frames         */
   bool          framevalid[MAXFRAMES];        /* frame validity           */
   Pixmap        backframes[MAXFRAMES];        /* backup frames            */
   int           curwinframe;                  /* frame number of window   */
   int           curbackframe;                 /* frame number of backup   */
   int           nframes;                      /* number of frames         */
   int           nbackframes;                  /* number of backup frames  */
   unsigned long *colpix[MAXCOLORS];           /* color pixel values       */
   int           ncolors;                      /* number of colors         */
   int           maxframes;                    /* maximum number of frames */
   XImage        *backimage;                   /* backup image             */
   colx          *backraster;                  /* backup raster buffer     */
   bool          backvalid;                    /* backup raster validity   */
   int           curbackraster;                /* frame number of raster   */
   long          cmapmask[CMMLEN];             /* color map mask           */
   int           colindex[MAXCOLORS];          /* color index map          */
   GC            backgc;                       /* backup frame GC          */
   int           line_width;                   /* line width               */
} _CanvasContext, *CanvasContext;

static  int             append;
static	int		NPOLY = 0;
static	int		POLY = 0;
static	XPoint		VERTEX[500];

static Display   *display;          /* one display for all windows */
static Window    window;            /* window to be used at initialization */
static Cursor    active_cursor;     /* cursor shown when callback is active */
static Cursor    quiet_cursor;      /* cursor shown when no callback active */
XtAppContext     *app_context;      /* the application context */
XtTranslations   translations;      /* plotfield core widget translations */
static XGCValues cvalues;           /* Graphics Context values */
static XGCValues backcvalues;       /* backup Graphics Context values */
static Colormap  colormap;          /* Color map */ 
static unsigned long colpix[MAXCOLORS];  /* Color pixel values (master table)*/
static int       ncolors;           /* Number of colors available */
static int       pad;               /* Scan line quantum */
static float scale_x, scale_y;      /* pixels per inch */
static int screen;                  /* screen number */
static int cindex;                  /* current color index */
static int nxerr=0;                 /* X error count from xerror() */
static char *imagedata=NULL;        /* data buffer for XImage structures */ 
static int   imagedata_size=0;      /* size of imagedata */
static colx *linebuffer=NULL;       /* line buffer for ggidriv opcode 26 */
static int   linebuffer_size=0;     /* size of linebuffer */

static CanvasContext contexts[MAXWINDOWS];
static CanvasContext focus=NULL;
static CanvasContext context=NULL;  /* open device's context */
static int nwindows=0;
static _GgiPlotInfo plotinfo;
static long changedcol[CMMLEN];
static bool truecolor;

static bool initialized=FALSE;

extern GgiInfo GgiGlobal;


/* -------------------------------------------------------------------------- */
/*                                 load_back_image                            */
/* -------------------------------------------------------------------------- */
/*  Load specified 8-bit backup image into backup raster buffer.
 */
static void load_back_image(CanvasContext current, int frameno)
{
   XImage *image=current->backimage;
   char *imagebuffer;
   colx *backbuffer= current->backraster;
   int bytespp = current->depth/8;             /* bytes per pixel in Pixmap */
   int bytesip = pad/8;                        /* bytes per pixel in XImage */
   int map_part=frameno%bytespp;
   int map_index=frameno/bytespp;
   int order=current->image->byte_order;
   int offset=((order==LSBFirst)?0:pad-1)+map_part*((order==LSBFirst)?+1:-1);
   int i, npixels=current->width*current->height;

   image->data = imagedata;
   XGetSubImage(display, current->backframes[map_index], 0, 0,
       current->width, current->height, 0xFF<<(map_part*8),
       ZPixmap, image, 0, 0);
   imagebuffer = image->data+offset;
   for (i=0; i<npixels; i++) *(backbuffer+i) = *(imagebuffer+i*bytesip);
   current->curbackraster = frameno;
}

/* -------------------------------------------------------------------------- */
/*                                 store_back_image                           */
/* -------------------------------------------------------------------------- */
/*  Store backup raster buffer in specified 8-bit backup image.
 */
static void store_back_image(CanvasContext current, int frameno)
{
   static GC gc=NULL;
   XGCValues gcvalues;
   XImage *image=current->backimage;
   char *imagebuffer;
   colx *backbuffer= current->backraster;
   int bytespp = current->depth/8;             /* bytes per pixel in Pixmap */
   int bytesip = pad/8;                        /* bytes per pixel in XImage */
   int map_part=frameno%bytespp;
   int map_index=frameno/bytespp;
   int order=current->image->byte_order;
   int offset=((order==LSBFirst)?0:pad-1)+map_part*((order==LSBFirst)?+1:-1);
   int i, npixels=current->width*current->height;

   if (!gc) {
      gc = XCreateGC(display, current->backframes[0], 0, &gcvalues);
   }
   image->data = imagedata;
   imagebuffer = image->data+offset;
   for (i=0; i<npixels; i++) *(imagebuffer+i*bytesip) = *(backbuffer+i);
   gcvalues.plane_mask = 0xFF<<(map_part*8);
   XChangeGC(display, gc, GCPlaneMask, &gcvalues);
   XPutImage(display, current->backframes[map_index], gc, image, 0, 0, 0, 0,
             current->width, current->height);
   if (frameno) current->curbackraster = frameno;
}

/* -------------------------------------------------------------------------- */
/*                                 copy_back_image                            */
/* -------------------------------------------------------------------------- */
static void copy_back_image(CanvasContext current, int source, int dest)
{
#if defined(PLANECOPY)
/*
 *  Delegate copying to X server. Less efficient for tested servers.
 */
   static GC gc=NULL;
   int bytespp = current->depth/8;             /* bytes per pixel in Pixmap */
   int source_bit=source%bytespp*8;            /* source plane */
   int source_index=source/bytespp;            /* source Pixmap index */
   int dest_bit=dest%bytespp*8;                /* destination plane */
   int dest_index=dest/bytespp;                /* destination Pixmap index */
   int bit;
   
   if (!gc) {
      XGCValues gcvalues;
      gcvalues.foreground = -1;
      gcvalues.background =  0;
      gc = XCreateGC(display, current->backframes[0],
                     GCForeground | GCBackground, &gcvalues);
   }
   for (bit=0; bit<8; bit++) {
      XSetPlaneMask(display, gc, 1<<(dest_bit+bit));
      XCopyPlane(display, current->backframes[source_index],
                          current->backframes[dest_index], gc, 
                          0, 0, current->width, current->height,
                          0, 0, 1<<(source_bit+bit));
   }
#else
   if (current->curbackraster != current->curwinframe)
      load_back_image(current, current->curwinframe);
   store_back_image(current, 0);
#endif
}

/* -------------------------------------------------------------------------- */
/*                                 prepare_backdraw                           */
/* -------------------------------------------------------------------------- */
/*  Prepare for drawing into backup pixmap. 
 */
static void prepare_backdraw(void)
{
   if (context->curwinframe != context->curbackframe) {
      copy_back_image(context, context->curwinframe, 0);
   }
   context->curwinframe  = 0;
   context->curbackframe = 0;
   context->backvalid = FALSE;
}

/* -------------------------------------------------------------------------- */
/*                                 copy_frame                                 */
/* -------------------------------------------------------------------------- */
/*  Copy one drawable in the frames array to another.
 */
static void copy_frame(CanvasContext current, int source, int dest)
{
   if (source==0) XSync(display, False);             /* source is window? */
   XCopyArea(display, current->frames[source],
                      current->frames[dest], current->gc,
                      0, 0, current->width, current->height, 0, 0);
   if (dest==0) XSync(display, False);              /* destination is window? */
}

/* -------------------------------------------------------------------------- */
/*                                 map_colors                                 */
/* -------------------------------------------------------------------------- */
/*  Translate color indices to pixel values.
 */
static void map_colors(CanvasContext current, colx *source, char *dest, int n)
{
   int i, bytespp=pad/8, order=current->image->byte_order;
   unsigned long pixel, **colpix=current->colpix;
    
   if (bytespp==1) {
      for (i=0; i<n; i++) {
         *(dest++) = *colpix[source[i]];
      }
   } else if (bytespp==4) {
      if (order==LSBFirst) {
         for (i=0; i<n; i++) {
            pixel = *colpix[source[i]];
            *(dest++) = pixel     & 0xFF;
            *(dest++) = pixel>>8  & 0xFF;
            *(dest++) = pixel>>16 & 0xFF;
            *(dest++) = pixel>>24 & 0xFF;
         }
      } else {
         for (i=0; i<n; i++) {
            pixel = *colpix[source[i]];
            *(dest++) = pixel>>24 & 0xFF;
            *(dest++) = pixel>>16 & 0xFF;
            *(dest++) = pixel>>8  & 0xFF;
            *(dest++) = pixel     & 0xFF;
         }
      }
   } else {
      if (order==LSBFirst) {
         for (i=0; i<n; i++) {
            pixel = *colpix[source[i]];
            *(dest++) = pixel     & 0xFF;
            *(dest++) = pixel>>8  & 0xFF;
         }
      } else {
         for (i=0; i<n; i++) {
            pixel = *colpix[source[i]];
            *(dest++) = pixel>>8  & 0xFF;
            *(dest++) = pixel     & 0xFF;
         }
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 apply_colormap                             */
/* -------------------------------------------------------------------------- */
/*  Apply colormap to backup raster buffer and write to specified frame.
 */
static void apply_colormap(CanvasContext current, int frame)
{
   int npix=current->width*current->height;
    
   current->image->data = imagedata;
   map_colors(current, current->backraster, current->image->data, npix);
   XPutImage(display, current->frames[frame], current->gc, current->image,
             0, 0, 0, 0, current->width, current->height);
}

/* -------------------------------------------------------------------------- */
/*                                 update_truecolor                           */
/* -------------------------------------------------------------------------- */
/*  Find all windows affected by the colormap change and rewrite them
 *  using the new colormap.
 */
static void update_truecolor(void)
{
   int w, i, j;
   CanvasContext current;
    
   for (w=0; w<nwindows; w++) {
      current = contexts[w];
      if (current->initialized) {
         for (j=0; j<CMMLEN; j++) {
            if (current->cmapmask[j] & changedcol[j]) break;
         }
         if (j<CMMLEN) {               /* this window's colormap has changed */
            if (!current->backvalid) {
               load_back_image(current, current->curwinframe);
               current->backvalid = TRUE;
            }
            apply_colormap(current, 0);
            for (i=1; i<current->nframes; i++) {
               current->framevalid[i] = FALSE;      /* invalidate all frames */
            }
         }
      }
   }
   for (j=0; j<CMMLEN; j++) changedcol[j] = 0;      /* clear changed mask    */
}

/* -------------------------------------------------------------------------- */
/*                                 findcontext                                */
/* -------------------------------------------------------------------------- */
/*  findcontext() finds a CanvasContext, given a GgiCanvas element.
 */
static CanvasContext findcontext(ident id)
{
   ident result=NULL;
   int   i;
    
   for (i=0; i<nwindows; i++) {
      CanvasContext current=contexts[i];
      if (current->canvas==id) {
         result = current;
         break;
      }
   }
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 StrDup                                     */
/* -------------------------------------------------------------------------- */
/*  String duplication function.
 */
static char *StrDup(char *text)
{
   char *result=NULL;

   if (text) {
      result = NNew(strlen(text)+1,char);
      strcpy(result,text);
   }
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 fcopy                                      */
/* -------------------------------------------------------------------------- */
/*   fcopy fills a fortran character variable with the contents of an
 *   asciiz string and returns the length of the asciiz string.
 */

static  int     fcopy( fchar d, char *s )
{
   int  l;                                      /* loop counter */
   int  l1;                                     /* length of source */
   int  l2;                                     /* length of destination */

   l1 = strlen( s );                            /* length of source */
   l2 = d.l;                                    /* length of destination */
   for (l = 0; l < l1 && l < l2; l++) {         /* copy loop */
      d.a[l] = s[l];                            /* do the copy */
   }
   while (l < l2) d.a[l++] = ' ';               /* rest is blank */
   return( l1 );                                /* return length of source */
}

/* -------------------------------------------------------------------------- */
/*                                 xerror                                     */
/* -------------------------------------------------------------------------- */
/*  Nonfatal error event handler set by GgiPlotFrames() when creating
 *  pixmaps.
 */
static int xerror(Display *display, XErrorEvent *event)
{
   nxerr++;
   return 0;
}

/* ========================== Action procedures ============================= */

/* -------------------------------------------------------------------------- */
/*                                 enter_plot                                 */
/* -------------------------------------------------------------------------- */
static void enter_plot(Widget curwid, XEvent *event,
                       String *params, Cardinal *num_params)
{
   int i;
   CanvasContext context;
    
   for (i=0; i<nwindows; i++) {
      context = contexts[i];
      if (curwid==context->widget) {
         focus = context;
         plotinfo.plotfield = focus->canvas;
         plotinfo.name      = focus->name;
         break;
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 leave_plot                                 */
/* -------------------------------------------------------------------------- */
static void leave_plot(Widget curwid, XEvent *event,
                       String *params, Cardinal *num_params)
{
   focus = NULL;
}

/* -------------------------------------------------------------------------- */
/*                                 click_plot                                 */
/* -------------------------------------------------------------------------- */
static void click_plot(Widget curwid, XEvent *event,
                       String *params, Cardinal *num_params)
{
   if (!focus) enter_plot(curwid, event, params, num_params);
   if (focus && focus->picture && focus->proc) {
      int ix = ((XButtonEvent*)event)->x;
      int iy = focus->height - ((XButtonEvent*)event)->y -1;
      int ib = ((XButtonEvent*)event)->button;
      unsigned int is = ((XButtonEvent*)event)->state;
      
      plotinfo.x = ((float)ix - focus->xorg)/focus->xscale;
      plotinfo.y = ((float)iy - focus->yorg)/focus->yscale;
      plotinfo.width  = 0.0;
      plotinfo.height = 0.0;
      plotinfo.button = ib==Button1?ggiButn1:(ib==Button2?ggiButn2:ggiButn3);
      plotinfo.state = (is&Button1Mask?ggiButn1:0)
                     | (is&Button2Mask?ggiButn2:0)
                     | (is&Button3Mask?ggiButn3:0)
                     | (is&ShiftMask  ?ggiShift:0)
                     | (is&ControlMask?ggiCtrl :0);
      plotinfo.key = '\0';
      focus->proc(focus, &plotinfo, focus->arg);
   }
}

/* -------------------------------------------------------------------------- */
/*                                  drag_plot                                 */
/* -------------------------------------------------------------------------- */
static void drag_plot(Widget curwid, XEvent *event,
                       String *params, Cardinal *num_params)
{
   if (!focus) enter_plot(curwid, event, params, num_params);
   if (focus && focus->picture && focus->proc) {
      int ix = ((XMotionEvent*)event)->x;
      int iy = focus->height - ((XMotionEvent*)event)->y -1;
      unsigned int is = ((XMotionEvent*)event)->state;
      
      plotinfo.x = ((float)ix - focus->xorg)/focus->xscale;
      plotinfo.y = ((float)iy - focus->yorg)/focus->yscale;
      plotinfo.width  = 0.0;
      plotinfo.height = 0.0;
      plotinfo.button = 0;
      plotinfo.state = (is&Button1Mask?ggiButn1:0)
                     | (is&Button2Mask?ggiButn2:0)
                     | (is&Button3Mask?ggiButn3:0)
                     | (is&ShiftMask  ?ggiShift:0)
                     | (is&ControlMask?ggiCtrl :0);
      plotinfo.key = '\0';
      focus->proc(focus, &plotinfo, focus->arg);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 read_key                                   */
/* -------------------------------------------------------------------------- */
static void read_key(Widget curwid, XEvent *event,
                       String *params, Cardinal *num_params)
{
   XKeyEvent *key_event=(XKeyEvent*)event;
   KeySym keysym;
   char outbuf[10];
   int i, nchar;
    
   if (!focus) enter_plot(curwid, event, params, num_params);
   if (focus && focus->picture && focus->proc) {
      int ix = key_event->x;
      int iy = focus->height - key_event->y;
      unsigned int is = key_event->state;
       
      plotinfo.x = ((float)ix - focus->xorg)/focus->xscale;
      plotinfo.y = ((float)iy - focus->yorg)/focus->yscale;
      plotinfo.width  = 0.0;
      plotinfo.height = 0.0;
      plotinfo.button = ggiKeybd;
      plotinfo.state = (is&Button1Mask?ggiButn1:0)
                     | (is&Button2Mask?ggiButn2:0)
                     | (is&Button3Mask?ggiButn3:0)
                     | (is&ShiftMask  ?ggiShift:0)
                     | (is&ControlMask?ggiCtrl :0);
      nchar = XLookupString(key_event, outbuf, 10, &keysym, NULL);
      if (nchar==0) {
         nchar = 1;
         switch (keysym) {
            case XK_KP_Left:
            case XK_Left:  *outbuf = ggiLeftArrow; break;
            case XK_KP_Up:
            case XK_Up:    *outbuf = ggiUpArrow; break;
            case XK_KP_Right:
            case XK_Right: *outbuf = ggiRightArrow; break;
            case XK_KP_Down:
            case XK_Down:  *outbuf = ggiDownArrow; break;
            default: nchar = 0;
         }
      }
      for (i=0; i<nchar; i++) {
         plotinfo.key = outbuf[i];
         focus->proc(focus, &plotinfo, focus->arg);
      }
   }
}

/* =================     (end of action procedures )     ==================== */

/* -------------------------------------------------------------------------- */
/*                                 initialize                                 */
/* -------------------------------------------------------------------------- */
/*  initialize() initializes a number of common objects and parameters.
 */
static void initialize(void)
{
   XColor color;
   char *colnames[]={"Black", "White", "Red", "Green", "Blue",
                     "Cyan", "Magenta", "Yellow", "Orange",
                     "MediumSpringGreen", "SpringGreen", "SlateBlue", 
                     "MediumSlateBlue", "OrangeRed", "DimGray", "LightGray"};
   int   icolor;
   ident tmp;

   static XtActionsRec actions[] = {
      {"enter_plot", enter_plot},
      {"leave_plot", leave_plot},
      {"click_plot", click_plot},
      {"drag_plot",  drag_plot},
      {"read_key",   read_key}
   }; int n_actions=5;
   
   static String trans =
      "<EnterWindow>:   enter_plot() \n\
       <LeaveWindow>:   leave_plot() \n\
       <BtnUp>:         click_plot() \n\
       <BtnDown>:       editfocus() click_plot() \n\
       <Motion>:        drag_plot() \n\
       <Key>:           read_key()";

   tmp = GgiCanvas("tmp", 1, 1);          /* force Ggi initialization */
   GgiDelete(&tmp);
   translations = XtParseTranslationTable(trans);
   display = GgiDisplay();
   ncolors = sizeof(colnames)/sizeof(char*);
   screen  = DefaultScreen(display);
   scale_x = 25.4 * (float)DisplayWidth(display,screen)
                  / (float)DisplayWidthMM(display,screen);
   scale_y = 25.4 * (float)DisplayHeight(display,screen)
                  / (float)DisplayHeightMM(display,screen);
   colormap = *(GgiGlobal->colormap);
   for (icolor=0; icolor<ncolors; icolor++) {
      (void)XParseColor(display, colormap, colnames[icolor], &color);
      XAllocColor(display, colormap, &color);
      colpix[icolor] = color.pixel;
   }
   app_context = GgiContext();
   XtAppAddActions(*app_context, actions, n_actions);
   active_cursor = XCreateFontCursor(display, ACTIVE_SHAPE);
   quiet_cursor  = XCreateFontCursor(display, QUIET_SHAPE);
   truecolor = GgiGlobal->vinfo->class==TrueColor;
   initialized = TRUE;
}

/* -------------------------------------------------------------------------- */
/*                                 ggidriv                                    */
/* -------------------------------------------------------------------------- */
/*  PGPLOT driver for Ggi plotfields.
 */
static void ggidriv( fint *idev, fint *ifunc, float *rbuf, fint *nbuf, fchar chr,
                    fint *lchr )
{
   int id=0;
    
   if ((*ifunc) && ((*idev)<0 || (*idev)>=nwindows)) {
      errorf(4, "Bad Ggi device");                          /* fatal error */
   }

   if (*ifunc) {
      context = contexts[*idev];
   }

   switch( (*ifunc) ) {
      case -1: {/* Get PGPLOT device type */
         (*lchr) = fcopy( chr, DEVICE);
         break;
      }
      case 0: {	/* Return device id */
         int l1=INT(rbuf[0]);
         while (id<nwindows && strncmp(chr.a,contexts[id]->name,l1)) id++;
         if (id==nwindows) {
            *nbuf = -1;
         } else {
            *idev = id;
         }
         break;
      }
      case 1: {	/* Return device name */
         *nbuf = 0;
         *lchr = fcopy( chr, context->name );
         break;
      }
      case 2: {	/* Return minimum range of view surface and color index */
         rbuf[0] = 0.0;
         rbuf[1] = context->width - 1;
         rbuf[2] = 0.0;
         rbuf[3] = context->height - 1;
         rbuf[4] = 0.0;
         rbuf[5] = context->ncolors - 1;
         *nbuf   = 6;
         *lchr   = 0;
         break;
      }
      case 3: {	/* Return device scale */
         rbuf[0] = scale_x;
         rbuf[1] = scale_y;
         rbuf[2] = 1.0;
         *nbuf   = 3;
         *lchr   = 0;
         break;
      }
      case 4: {	/* Return device capabilities */
         *nbuf = 0;
         *lchr = fcopy( chr, "IXNATRPNYN" );
         break;
      }
      case 5: {	/* Return default device/file name */
         *nbuf = 0;
         *lchr = fcopy( chr, context->name );
         break;
      }
      case 6: {	/* Return default size of view */
         rbuf[0] = 0;
         rbuf[1] = context->width - 1;
         rbuf[2] = 0;
         rbuf[3] = context->height - 1;
         *nbuf   = 4;
         *lchr   = 0;
         break;
      }
      case 7: {	/* Return miscellaneous defaults */
         rbuf[0] = 2.0;
         *nbuf = 1;
         *lchr = 0;
         break;
      }
      case 8: {	/* Select device */
         break;
      }
      case 9: {	/* Open workstation */
         XSetWindowAttributes set_attributes;
         XWindowAttributes    attributes;
         Widget w;

         window = GgiWindow(context->canvas);
         if (!initialized) initialize();
         if (!context->initialized) {
            context->initialized = TRUE;
            context->window = window;
            context->frames[0] = window;
            context->nframes   = 1;
            cvalues.background = *context->colpix[0];
            cvalues.foreground = *context->colpix[1];
            context->gc = XCreateGC(display, window,
                GCForeground | GCBackground, &cvalues);
            context->line_width = 1;
            XSetLineAttributes(display, context->gc, context->line_width,
                               LineSolid, CapButt, JoinRound );
            context->widget = GgiWidget(context->canvas);
            set_attributes.colormap         = colormap;
            set_attributes.background_pixel = *context->colpix[0];
            set_attributes.backing_store    = Always;
            XChangeWindowAttributes(display, window,
                                    CWBackPixel | CWColormap | CWBackingStore,
                                    &set_attributes);

            for (w=context->widget; XtParent(w); w=XtParent(w)) {;}
               
            XSetWindowColormap(display, XtWindow(w), colormap);

            XClearWindow(display, window);

            XGetWindowAttributes(display, context->window, &attributes);
            context->width = attributes.width;
            context->height = attributes.height;
            context->depth  = attributes.depth;
            XtVaSetValues(context->widget, XtNtranslations, translations,
               XtNcursor,
               context->cursor==QUIET_SHAPE?quiet_cursor:active_cursor,
               XtNlabel, " ", NULL);
            pad = attributes.depth>16?32:(attributes.depth>8?16:8);
            if (attributes.width*pad/8 > imagedata_size) {
               imagedata = realloc(imagedata, attributes.width*pad/8);
               imagedata_size = attributes.width*pad/8;
            }
            if (attributes.width> linebuffer_size) {
               linebuffer  = realloc(linebuffer, attributes.width);
               linebuffer_size = attributes.width;
            }
            context->image = XCreateImage(display, attributes.visual,
               attributes.depth, ZPixmap, 0, NULL,
               attributes.width, 1, pad, 0);
            if (truecolor) {
               if (attributes.width*attributes.height*pad/8 > imagedata_size) {
                  imagedata = realloc(imagedata,
                                      attributes.width*attributes.height*pad/8);
                  imagedata_size = attributes.width*attributes.height*pad/8;
               }
               context->image->height = attributes.height;
               
               context->pixmap8 =
                   XCreatePixmap(display, context->window,
                                 context->width, context->height,
                                 context->depth);
               context->backframes[0] = context->pixmap8;
               context->nbackframes   = 1;
               backcvalues.background = 0;
               backcvalues.foreground = 1;
               backcvalues.plane_mask = 0xFF;
               context->backgc = XCreateGC(display, context->pixmap8,
                   GCForeground | GCBackground | GCPlaneMask, &backcvalues);
               XSetLineAttributes(display, context->backgc, context->line_width,
                                  LineSolid, CapButt, JoinRound );
               backcvalues.foreground = 0;
               XChangeGC(display, context->backgc, GCForeground, &backcvalues);
               XFillRectangle(display, context->pixmap8, context->backgc, 0, 0,
                               context->width, context->height);
               backcvalues.foreground = 1;
               XChangeGC(display, context->backgc, GCForeground, &backcvalues);
               context->backraster =
                   malloc(attributes.width*attributes.height);
               context->backimage = XCreateImage(display, attributes.visual,
                   attributes.depth, ZPixmap, 0, NULL,
                   attributes.width, attributes.height, pad, 0);
               context->curwinframe  = 0;
               context->curbackframe = 0;
               context->backvalid = FALSE;
            }
         }
         rbuf[0] = 0.0;
         rbuf[1] = 1.0;
         append = INT( rbuf[2] );
         *nbuf = 2;
         XFlush(display);
         break;
      }
      case 10: { /* Close workstation */
         *nbuf = 0;
         *lchr = 0;
         XFlush(display);
         break;
      }
      case 11: { /* Begin picture */
         if (!append) {
            XClearWindow(display, context->window );
            if (truecolor) {
               prepare_backdraw();
               backcvalues.foreground = 0;
               XChangeGC(display, context->backgc, GCForeground, &backcvalues);
               XFillRectangle(display, context->pixmap8, context->backgc, 0, 0,
                               context->width, context->height);
               backcvalues.foreground = 1;
               XChangeGC(display, context->backgc, GCForeground, &backcvalues);
            }
         }
         XFlush(display);
         context->picture = TRUE;
         break;
      }
      case 12: { /* Draw line */
         int	i0, i1, j0, j1;

         i0 = INT( rbuf[0] );
         j0 = context->height - 1 - INT( rbuf[1] );
         i1 = INT( rbuf[2] );
         j1 = context->height - 1 - INT( rbuf[3] );
         XDrawLine(display, context->window, context->gc, i0, j0, i1, j1 );
         if (truecolor) {
            prepare_backdraw();
            XDrawLine(display, context->pixmap8, context->backgc,
                      i0, j0, i1, j1 );
         }
         break;
      }
      case 13: { /* Draw dot */
         int	i0, j0;
         int	w;

         i0 = INT( rbuf[0] );
         j0 = context->height - 1 - INT( rbuf[1] );
         w = context->line_width;
         if (w == 1) {
            XDrawPoint(display, context->window, context->gc, i0, j0 );
            if (truecolor) {
               prepare_backdraw();
               XDrawPoint(display, context->pixmap8, context->backgc, i0, j0 );
            }
         } else {
            XFillArc(display, context->window, context->gc, i0 - w / 2,
               j0 - w / 2, w, w, 0, 359 * 64 );
            if (truecolor) {
               prepare_backdraw();
               XFillArc(display, context->pixmap8, context->backgc, i0 - w / 2,
                  j0 - w / 2, w, w, 0, 359 * 64 );
            }
         }
         
         break;
      }
      case 14: { /* End picture */
         XFlush(display);
         break;
      }
      case 15: { /* Set color index */
         cindex = INT( rbuf[0] );
         if ( cindex < 0 || cindex>=context->ncolors) {
            cindex = 1;
         }
         cvalues.foreground =
            context->xormode?
              *context->colpix[cindex]^*context->colpix[0]:
              *context->colpix[cindex];
         XChangeGC(display, context->gc, GCForeground, &cvalues);
         if (truecolor) {
            backcvalues.foreground = cindex;
            XChangeGC(display, context->backgc, GCForeground, &backcvalues);
         }
         break;
      }
      case 16: { /* Flush buffer */
         XFlush(display);
         break;
      }
      case 17: { /* Read cursor */
         *chr.a = '\0'; /* No PGPLOT cursor available */
         break;
      }
      case 18: { /* Erase alpha screen */
         break;
      }
      case 19: { /* Set line style */
         break;
      }
      case 20: { /* Polygon fill */
         if (POLY == 0) {
            NPOLY = INT( rbuf[0] );
            POLY = NPOLY;
         } else {
            VERTEX[POLY].y = context->height - 1 - INT( rbuf[1] );
            VERTEX[POLY].x = INT( rbuf[0] );
            --POLY;
            if (POLY == 0) {
               XFillPolygon(display, context->window, context->gc,
                  &(VERTEX[1]), NPOLY, Complex, CoordModeOrigin );
               if (truecolor) {
                  prepare_backdraw();
                  XFillPolygon(display, context->pixmap8, context->backgc,
                     &(VERTEX[1]), NPOLY, Complex, CoordModeOrigin );
               }
               XFlush(display);
            }
         }
         break;
      }
      case 21: { /* Set color representions */
/*
 *  This code extends the 'official' PGPLOT function 21 in the sense
 *  that it is capable of modifiying more than one color representation
 *  in one call. Argument NBUF is used to determine how many color indices
 *  need to be modified.
 */
         int	ci;
         int    nc;
         int    ic;

         ci = INT( rbuf[0] );
         nc = ((*nbuf)-1)/3;
         if (ci>0 && (ci+nc-1)<context->ncolors) {
            XColor	thiscolor[256];
            XGCValues	values;
            
            for (ic=0; ic<nc; ic++) {
               thiscolor[ic].red   = USHRT_MAX * rbuf[ic*3+1];
               thiscolor[ic].green = USHRT_MAX * rbuf[ic*3+2];
               thiscolor[ic].blue  = USHRT_MAX * rbuf[ic*3+3];
               thiscolor[ic].flags = DoRed | DoGreen | DoBlue;
               thiscolor[ic].pixel = *context->colpix[ci];
               if (ci==cindex) {
                  XGetGCValues(display, context->gc, GCForeground, &values );
                  values.foreground = thiscolor[ic].pixel;
                  if (context->xormode) values.foreground ^= *context->colpix[0];
                  XChangeGC(display, context->gc, GCForeground, &values);
               }
               ci++;
            }
            if (truecolor) {
               ci = INT( rbuf[0] ); 
               for (ic=0; ic<nc; ic++) {
                  SETCOLOR(changedcol, context->colindex[ci]);
                  XAllocColor(display, colormap, &thiscolor[ic]);
                  *context->colpix[ci++] = thiscolor[ic].pixel;
               }
               update_truecolor();
            } else {
               XStoreColors(display, colormap, thiscolor, nc);
            }
            XFlush(display);
         }
         break;
      }
      case 22: {/* Set line width */
         int	lw;

         lw = INT( rbuf[0] );
         if (lw < 1 || lw > 40) lw = 1;
         if (lw > 1) lw = lw / 2;
         context->line_width = lw;
         XSetLineAttributes(display, context->gc, context->line_width,
                            LineSolid, CapButt, JoinRound );
         if (truecolor) {
            XSetLineAttributes(display, context->backgc, context->line_width,
                               LineSolid, CapButt, JoinRound );
         }
         break;
      }
      case 23: { /* Escape function -- here used for XOR mode */
         if (*lchr >= 3 && !strncmp(chr.a,"Ggi",3)) {
            if (!strncmp(chr.a+3,"XOR",3)) { /* XOR drawing mode */
               bool prvmode=context->xormode;
               context->xormode = rbuf[0]?TRUE:FALSE;
               if (prvmode != context->xormode) {
                  XGetGCValues(display, context->gc, GCForeground, &cvalues );
                  cvalues.foreground ^= *context->colpix[0];
                  XChangeGC(display, context->gc, GCForeground, &cvalues);
                  XSetFunction(display, context->gc,
                                        context->xormode?GXxor:GXcopy);
                  if (truecolor) {
                     XSetFunction(display, context->backgc,
                                           context->xormode?GXxor:GXcopy);
                  }
               }
               rbuf[0] = prvmode?1.0:0.0;
               *nbuf   = 1;
            }
         }
         break;
      }
      case 24: { /* Rectangle fill */
         int	i0, il, j0, jl;

         i0 = INT( rbuf[0] );
         j0 = context->height - 1 - INT( rbuf[3] );
         il = INT( rbuf[2] - rbuf[0] );
         jl = INT( rbuf[3] - rbuf[1] );
         XFillRectangle(display, context->window, context->gc, i0, j0, il, jl );
         if (truecolor) {
            prepare_backdraw();
            XFillRectangle(display, context->pixmap8, context->backgc, i0, j0,
                           il, jl );
         }
         XFlush(display);
         break;
      }
      case 25: { /* Not implemented */
         break;
      }
      case 26: { /* Line of pixels */
         int x = INT( rbuf[0] );
         int y = context->height - 1 - INT( rbuf[1] );
         int i, n = *nbuf - 2;
         int bytesip = pad/8;
         
         for (i=0; i<n; i++) linebuffer[i] = (int)(rbuf[i+2]);
         map_colors(context, linebuffer, imagedata, n);
         context->image->data = imagedata;
         XPutImage(display, context->window, context->gc, context->image,
                   0, 0, x, y, (unsigned)n, (unsigned)1);
         if (truecolor) {
            int offset = context->image->byte_order==LSBFirst?0:pad-1;
            prepare_backdraw();
            for (i=0; i<n; i++) {
               imagedata[i*bytesip+offset] = linebuffer[i];
            }
            XPutImage(display, context->pixmap8, context->backgc,
                context->image, 0, 0, x, y, (unsigned)n, (unsigned)1);
         }
         XFlush(display);
         break;
      }
      case 27: { /* Scaling information */
         context->xorg   = rbuf[0];
         context->xscale = rbuf[1];
         context->yorg   = rbuf[2];
         context->yscale = rbuf[3];
         break;
      }
      case 29: { /* Query color representation */
         XColor thiscolor;
         int ci = INT( rbuf[0] );
         thiscolor.pixel = *context->colpix[ci];
         thiscolor.flags = DoRed | DoGreen | DoBlue;
         XQueryColor(display, colormap, &thiscolor);
         *nbuf = 4;
         rbuf[1] = (float)thiscolor.red/(float)USHRT_MAX;
         rbuf[2] = (float)thiscolor.green/(float)USHRT_MAX;
         rbuf[3] = (float)thiscolor.blue/(float)USHRT_MAX;
         break;
      }
      default: {
      }
   }
}

/* ========================================================================== */
/*                                 GgiPlotField                               */
/* -------------------------------------------------------------------------- */
extern ident GgiPlotField(char *name, int width, int height)
{
   ident canvas=NULL;
   char  text[40];
   int   i;
   
   if (width) {
      if (nwindows>=MAXWINDOWS) return NULL;
      contexts[nwindows] = New(_CanvasContext);
      canvas = GgiCanvas("pgplot", width, height);
      sprintf(text,"Plot device: %s", name);
      XtVaSetValues(GgiWidget(canvas), XtNlabel, text,
                                       XtNwidth, width, XtNheight, height,
                                       NULL);
      contexts[nwindows]->canvas = canvas;
      contexts[nwindows]->name   = StrDup(name);
      contexts[nwindows]->xorg   = 0.0;
      contexts[nwindows]->xscale = 1.0;
      contexts[nwindows]->yorg   = 0.0;
      contexts[nwindows]->yscale = 1.0;
      contexts[nwindows]->cursor = QUIET_SHAPE;
      contexts[nwindows]->maxframes = MAXFRAMES;
      if (!initialized) initialize();
      for (i=0; i<ncolors; i++) {
         contexts[nwindows]->colpix[i] = &colpix[i];
         contexts[nwindows]->colindex[i] = i;
         SETCOLOR(contexts[nwindows]->cmapmask, i);
      }
      for (i=ncolors; i<MAXCOLORS; i++) {
         contexts[nwindows]->colpix[i] = &colpix[0];
         contexts[nwindows]->colindex[i] = i;
      }
      contexts[nwindows]->ncolors = ncolors;
      (void)addpgdrv(ggidriv, name, NULL, NULL, NULL, NULL, NULL);
      nwindows++;
   } else {
      int i;
      CanvasContext current;
      for (i=0; i<nwindows; i++) {
         current = contexts[i];
         if (!strcmp(name,current->name)) {
            canvas = current->canvas;
            break;
         }
      }
   }
   return canvas;
}

/* ========================================================================== */
/*                                ScheduleGgiPlotCursor                       */
/* -------------------------------------------------------------------------- */
ident ScheduleGgiPlotCursor(GgiCursorProc proc, ident plotfield, void *arg)
{
   ident result=NULL;
   int   i;
    
   for (i=0; i<nwindows; i++) {
      CanvasContext current=contexts[i];
      if (current->canvas==plotfield) {
         if (!current->proc) {
            if (current->initialized)
               XtVaSetValues(current->widget, XtNcursor, active_cursor, NULL);
            current->cursor = ACTIVE_SHAPE;
            current->proc = proc;
            current->arg  = arg;
            result = current;
         }
         break;
      }
   }
   return result;
}

/* ========================================================================== */
/*                                DescheduleGgiPlotCursor                     */
/* -------------------------------------------------------------------------- */
void  DescheduleGgiPlotCursor(ident *id)
{
   CanvasContext current=*(CanvasContext*)id;
   if (current->initialized)
      XtVaSetValues(current->widget, XtNcursor, quiet_cursor, NULL);
   current->cursor = QUIET_SHAPE;
   current->proc = NULL;
   *id = NULL;
}

/* ========================================================================== */
/*                               GgiPlotXor                                   */
/* -------------------------------------------------------------------------- */
bool GgiPlotXor(bool mode)
{
   mode = toflog(mode);
   return tobool(ggixor_c(&mode));
}

/* ========================================================================== */
/*                               GgiPlotColors                                */
/* -------------------------------------------------------------------------- */
int GgiPlotColors(ident id, int newcolors)
{
   int more, status;
   if (!initialized) initialize();
   if (newcolors>MAXCOLORS) newcolors = MAXCOLORS;
   if (newcolors>ncolors) {
      if (truecolor) {
         ncolors = newcolors;
      } else {
         more = newcolors-ncolors;
         do {
            status = XAllocColorCells( display, colormap, False,
                                       NULL, 0, colpix+ncolors, more );
            if (status) break;
            more--;
         } while (more);
         ncolors += more;
      }
   }
   if (ncolors<newcolors && GgiGlobal->defaultcolormap) {
      colormap = XCopyColormapAndFree(display, colormap);
      GgiGlobal->defaultcolormap = 0;
      ncolors = GgiPlotColors(id, newcolors);
   }
   if (id) {
      CanvasContext current=findcontext(id);
      int i;

      for (i=0; i<CMMLEN; i++) {
         current->cmapmask[i] = 0;
      }
      for (i=0; i<ncolors; i++) {
         current->colpix[i] = &colpix[i];
         current->colindex[i] = i;
         SETCOLOR(current->cmapmask, i);
      }
      current->ncolors = ncolors;
   }
   return ncolors;
}

/* ========================================================================== */
/*                               GgiPlotFrames                                */
/* -------------------------------------------------------------------------- */
int GgiPlotFrames(ident id, int newframes)
{
   CanvasContext current=findcontext(id);
   int nframes = current->nframes;
   int nbackframes = current->nbackframes;
   int bytespp = current->depth/8;
   Pixmap frame, backframe;
   newframes++;
   if (newframes > current->maxframes) newframes = current->maxframes;
   nxerr = 0;
   (void)XSetErrorHandler(xerror);
   while (newframes > nframes) {
      if (truecolor && nframes%bytespp == 0) {
         backframe = XCreatePixmap(display, current->window, current->width,
                                            current->height, current->depth);
         XSync(display, False);
         (void)GgiHandleEvents();
         if (!nxerr) {
            current->backframes[nbackframes++] = backframe;
         }
      }
      if (!nxerr) {
         frame = XCreatePixmap(display, current->window, current->width,
                                        current->height, current->depth);
         XSync(display, False);
         (void)GgiHandleEvents();
      }
      if (nxerr) {
         if (nframes>1) newframes = 7*nframes/8; /* back off ...    */
         current->maxframes = newframes;         /* don't try again */
         break;
      }
      current->frames[nframes++] = frame;
   }
   (void)XSetErrorHandler(NULL);
   if (truecolor) {
      int newbackframes = (newframes-1)/bytespp+1;
      while (newbackframes < nbackframes) {
         XFreePixmap(display, current->backframes[--nbackframes]);
      }
      current->nbackframes = nbackframes;
   }
   while (newframes < nframes) {
      XFreePixmap(display, current->frames[--nframes]);
   }
   current->nframes = nframes;
   return nframes-1;
}

/* ========================================================================== */
/*                               GgiPlotRecord                                */
/* -------------------------------------------------------------------------- */
void GgiPlotRecord(ident id, int frame)
{
   CanvasContext current=findcontext(id);
   int nframes = current->nframes;
    
   frame++;
   if (frame>0 && frame<nframes) {
      copy_frame(current, 0, frame);
      if (truecolor) {
         load_back_image(current, current->curwinframe);
         store_back_image(current, frame);
         current->curbackframe = frame;
         current->curwinframe = frame;
         current->framevalid[frame] = TRUE;
      }
   }
}

/* ========================================================================== */
/*                               GgiPlotShow                                  */
/* -------------------------------------------------------------------------- */
void GgiPlotShow(ident id, int frame)
{
   CanvasContext current=findcontext(id);
   int nframes = current->nframes;
    
   frame++;
   if (frame>0 && frame<nframes) {
      if (truecolor) {
         if (!current->framevalid[frame]) {
            load_back_image(current, frame);
            current->backvalid = TRUE;
            apply_colormap(current, frame);
            current->framevalid[frame] = TRUE;
         } else current->backvalid = FALSE;
         current->curwinframe = frame;
      }
      copy_frame(current, frame, 0);
   }
}

/* ========================================================================== */
/*                               GgiPlotMapColors                             */
/* -------------------------------------------------------------------------- */
void GgiPlotMapColors(ident id, ... )
{
   CanvasContext current=findcontext(id);
   va_list args;
   int i, ci, nc;
    
   for (i=0; i<CMMLEN; i++) {
      current->cmapmask[i] = 0;
   }
   current->ncolors = 0;
   va_start(args, id);
   for (;;) {
      ci = va_arg(args, int);
      if (ci<0) break;
      nc = va_arg(args, int);
      while (nc--) {
         SETCOLOR(current->cmapmask, ci);
         current->colindex[current->ncolors] = ci;
         current->colpix[current->ncolors++] = &colpix[ci++];
      }
   }
   va_end(args);
}
#<

#>            ggiprompter.c
/* ggiprompter.c -XT
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands


Author: J.P. Terlouw
*/

#define NAME "Question"
#define KEY_OK  "_GGIPR_OK="
#define KEY_INP "_GGIPR_INP="
#define KEY_CAN "_GGIPR_CAN="


#include "stddef.h"
#include "stdio.h"
#include "string.h"
#include "gipsyc.h"
#include "userfio.h"
#include "wkey.h"
#include "ggi.h"

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

static ident prompter=NULL;
static ident l_message;
static ident ok_id, can_id, inp_id, rej_id;
static char  currkey[KEYLEN];
static char  key_inp[KEYLEN];
static bool  delivered;
static bool  expected;


/* -------------------------------------------------------------------------- */
/*                                 handle_input                               */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for the text input.
 */
static void handle_input(ident id, char *key, int code, void *arg)
{
   if (!expected) {
      wkeyf("%sY", KEY_OK);
   } else {
      expected = FALSE;
   }
}

/* -------------------------------------------------------------------------- */
/*                                 handle_ok                                  */
/* -------------------------------------------------------------------------- */
static void handle_ok(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      if (!delivered) {
         fint nread;
         fchar text;
         char ctext[128];
         text.a = ctext; text.l = 127;

         nread = userftext(text, 2, key_inp, " ");
         if (nread) {
            ctext[nread] = '\0';
            wkeyf("%s%s", currkey, ctext);
            GgiSetLabel(l_message, " ", 0);
            delivered = TRUE;
         }
      }
      wkeyf(key);
   }
}

/* -------------------------------------------------------------------------- */
/*                                deleteprompter                              */
/* -------------------------------------------------------------------------- */
static void deleteprompter(void)
{
   GgiDelete(&prompter);
   DescheduleKeyevent(&ok_id);
   DescheduleKeyevent(&can_id);
   DescheduleKeyevent(&inp_id);
   DescheduleKeyevent(&rej_id);
   wkeyf(KEY_OK);                                         /* reset OK button */
   prompter = NULL;
}



/* -------------------------------------------------------------------------- */
/*                                 handle_cancel                              */
/* -------------------------------------------------------------------------- */
/*  keyword handler for the CANCEL button.
 */
static void handle_cancel(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      wkeyf(currkey);
      deleteprompter();
   }
}

/* -------------------------------------------------------------------------- */
/*                                 askagain                                   */
/* -------------------------------------------------------------------------- */
static void askagain(char *message)
{
   if (prompter) {
      GgiSetLabel(l_message, message, 0);
      delivered = FALSE;
   }
}

/* -------------------------------------------------------------------------- */
/*                                 handle_reject                              */
/* -------------------------------------------------------------------------- */
/*  keyword handler for reject events on the input.
 */
static void handle_reject(ident id, char *key, int code, void *arg)
{
   fchar  text;
   char  ctext[41];
   fint   nread;
    
   text.a = ctext; text.l = 40;
   nread = userftext(text, -1, "UCAMESSAGE=", " ");
   ctext[nread] = '\0';
   askagain(nread?ctext:"Bad input");
}

/* -------------------------------------------------------------------------- */
/*                                 accepted                                   */
/* -------------------------------------------------------------------------- */
static void accepted(void)
{
   if (prompter) {
      deleteprompter();
   }
}

/* -------------------------------------------------------------------------- */
/*                                 newprompter                                */
/* -------------------------------------------------------------------------- */
static void newprompter(char *key, char *message)
{
   ident prvshl;
   ident b_ok, b_cancel;
   ident frame;
   ident l_prompt, t_input;
   fint  nread;
   fchar text;
   char ctext[128];
    
   text.a = ctext; text.l=127;
   nread = userftext(text, 2, key, " ");
   ctext[nread] = '\0';
   sprintf(key_inp, "_GP_%s", key);
   if (nread) {
      wkeyf("%s%s", key_inp, ctext); /* copy key contents to input field */
      expected = TRUE;               /* suppress event handling */
   }
   strcpy(currkey, key);          /* save current key */
   delivered = FALSE;
   prompter =  GgiDialog(NAME);
   
   prvshl = GgiUseShell(prompter);
   b_ok   = GgiSetLabel(GgiButton(KEY_OK, NULL), "OK", 0);
   ok_id = ScheduleKeyevent(handle_ok, KEY_OK, KEYCHANGE, NULL);
   b_cancel = GgiSetLabel(GgiButton(KEY_CAN, NULL), "CANCEL", 0);
   can_id = ScheduleKeyevent(handle_cancel, KEY_CAN, KEYCHANGE, NULL);
   frame = GgiForm(NAME, 2);
   GgiSetPosition(frame,    0, NULL, 0, NULL);
   GgiSetPosition(b_cancel, 0, NULL, 0, frame);
   GgiSetPosition(b_ok, -GgiWidth(b_ok)-2, frame, 0, frame);
   
   (void)GgiUseShell(frame);
   l_prompt  = GgiLabel(message);
   t_input   = GgiSetLabel(GgiTextField(key_inp, NULL, 40), " ", 1);
   inp_id = ScheduleKeyevent(handle_input, key_inp, KEYCHANGE, NULL);
   rej_id = ScheduleKeyevent(handle_reject, key, KEYREJECT, NULL);
   l_message = GgiLabel(" \n ");
   GgiSetPosition(l_prompt,  0, NULL, 0, NULL);
   GgiSetPosition(t_input,   0, NULL, 0, l_prompt);
   GgiSetPosition(l_message, 0, NULL, 0, t_input);

   (void)GgiUseShell(prvshl);
   GgiShowShell(prompter, TRUE);
}

/* ========================================================================== */
/*                                 GgiPrompter                                */
/* -------------------------------------------------------------------------- */
/*
 */
void GgiPrompter(char *key, char *message)
{
   if (!strcmp("ACCEPT", key)) {
      accepted();
   } else if (!strcmp("AGAIN", key)) {
      askagain(message);
   } else if (!prompter) {
      newprompter(key, message);
   } else {
      errorf(1, "GgiPrompter: illegal directive: \"%s\"", key);
   }
}   

/* ========================================================================== */
/*                                  TEST PROGRAM                              */
/* ========================================================================== */
#if defined(TESTBED)
/*
 *  Simple sample program showing all GgiPrompter() features.
 */
#include "init.h"
#include "finis.h"
#include "cmain.h"
#include "eventmonitor.h"

static bool  confirmed=FALSE;

/*
 *  Button keyword handler for quitting.
 */
static void quit(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);                                         /* reset button    */
      finis_c();
   }
}

/*
 *  Button keyword handler creating the prompter.
 */
static void promptuser(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiPrompter("TEXT=", "Enter your text:");           /* create prompter */
      wkeyf(key);                                         /* reset button    */
      anyoutf(0, "Prompter created");
   }
}

/*
 *  Keyword handler receiving the resulting text.
 */
static void getresult(ident id, char *key, int code, void *arg)
{
   fint nread;
   fchar text;
   char ctext[128];

   text.a = ctext; text.l = 127;

   nread = userftext(text, 2, key, " ");
    
   if (nread) {
      if (*ctext == 'Z') {
         /* reject text beginning with 'Z' */
         GgiPrompter("AGAIN", "Text beginning with 'Z'\n not allowed");
         confirmed = FALSE;
         anyoutf(0, "Illegal text not accepted");
      } else if (*ctext == 'z') {
         rejectf(key, "Lower case 'z' also not allowed");
         anyoutf(0, "Illegal text rejected");
         confirmed = FALSE;
      } else {
         if (*ctext == 'A') {
            /* Accept text beginning with 'A' unconditionally */
            GgiPrompter("ACCEPT", NULL);                      /* Accept text */
            confirmed = FALSE;
            anyoutf(0, "Good text accepted unconditionally");
         } else if (!confirmed) {
            /* Other text must be confirmed first */
            GgiPrompter("AGAIN", "Please confirm");          /* Confirm text */
            confirmed = TRUE;
            anyoutf(0, "Confirmation requested for questionable text");
         } else {
            /* Text has been confirmed */
            GgiPrompter("ACCEPT", NULL);                      /* Accept text */
            confirmed = FALSE;
            anyoutf(0, "Questionable text confirmed and accepted");
         }
      }
   } else {
      /* user cancelled prompter */
      confirmed = FALSE;
      anyoutf(0, "User cancelled prompter");
   }
}


MAIN_PROGRAM_ENTRY
{
   ident button, quitbtn;
   
   init_c();
   
   GgiAutoLayout(FALSE);
   GgiPostponeRealize(TRUE); 
   
   button  = GgiButton("PROMPT=", "Pop up prompter");
   quitbtn = GgiButton("QUIT=", "Terminate program");
   GgiSetPosition(button,  0, NULL,   0, NULL);
   GgiSetPosition(quitbtn, 0, button, 0, NULL);
   GgiRealize();
   (void)ScheduleKeyevent(promptuser, "PROMPT=", KEYCHANGE, NULL);
   (void)ScheduleKeyevent(quit, "QUIT=", KEYCHANGE, NULL);
   (void)ScheduleKeyevent(getresult,  "TEXT="  , KEYCHANGE, NULL);
   eventmonitor("MONITOR=", TRUE); /* if MONITOR=Y, events are monitored */
   MainLoop();
}
#endif
#<

#>            ggiplotprompter.c
/* ggiplotprompter.c -XT
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands


Author: J.P. Terlouw
*/

#define NAME "Select Plotter"
#define KEY_OK  "_GGIPR_OK="
#define KEY_INP "_GGIPR_INP="
#define KEY_CAN "_GGIPR_CAN="


#include "stddef.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "gipsyc.h"
#include "userfio.h"
#include "wkey.h"
#include "pgplot.h"
#include "grexec.h"
#include "ggi.h"

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

static ident prompter=NULL;
static ident l_message, m_plotter;
static ident ok_id, can_id, dev_id, rej_id;
static char  currfile[KEYLEN], currdev[KEYLEN];
static char  file_inp[KEYLEN], dev_inp[KEYLEN];
static bool  delivered;
static bool  expected;

static char **plotters;

#define MAXDEVLEN 20  /* constants for grdevices */
#define MAXDEVS   20

/* -------------------------------------------------------------------------- */
/*                                 grdevices                                  */
/* -------------------------------------------------------------------------- */
/*  make list of graphics devices
 */
static char **grdevices(char kind)
{
   fint   ndevs, idev, ifunc, nbuf, lchr;
   float  rbuf;
   fchar  chr;
   int    mdevs=0;                           /* number of matching devices */
   char   capabilities[MAXDEVLEN];
   char   **devnames=(char**)calloc(MAXDEVS+1,sizeof(char**));
    
   idev = 0; ifunc = 0; nbuf  = 1; lchr  = 1;
   grexec_c(&idev, &ifunc, &rbuf, &nbuf, chr, &lchr);
   ndevs = (fint)rbuf;
   chr.l = MAXDEVLEN;
   for (idev=1; idev<=ndevs && mdevs<MAXDEVS; idev++) {
      chr.a = capabilities;
      ifunc = 4; nbuf  = 1; lchr  = 1;
      grexec_c(&idev, &ifunc, &rbuf, &nbuf, chr, &lchr);
      if (capabilities[0]==kind) {
         mdevs++;
         chr.a = (char*)calloc(MAXDEVLEN+1,1);
         ifunc = 1; nbuf  = 1; lchr  = 1;
         grexec_c(&idev, &ifunc, &rbuf, &nbuf, chr, &lchr);
         chr.a[lchr] = '\0';
         devnames[mdevs-1] = chr.a;
      }
   }
   devnames[mdevs] = NULL;
   return devnames;
}

/* -------------------------------------------------------------------------- */
/*                                 handle_dev                                 */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for device menu. Activates "PLOT"-button and sets
 *  plotter name in button.
 */
static void handle_dev(ident id, char *key, int code, void *arg)
{
   fint index;
   
   (void)userfint(&index, 1, 2, key, " ");
   GgiSetLabel(m_plotter, plotters[index], 0);
   GgiActivate((ident)arg);
}

/* -------------------------------------------------------------------------- */
/*                                 handle_ok                                  */
/* -------------------------------------------------------------------------- */
static void handle_ok(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   fint index;
   
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      if (!delivered) {
         fint nread;
         fchar text;
         char ctext[128];
         text.a = ctext; text.l = 127;

         nread = userftext(text, 2, file_inp, " ");
         ctext[nread] = '\0';
         (void)userfint(&index, 1, 2, dev_inp, " ");
         wkeyf("%s%s %s%s", currfile, ctext, currdev, plotters[index]);
         GgiSetLabel(l_message, " ", 0);
         delivered = TRUE;
      }
      wkeyf(key);
   }
}

/* -------------------------------------------------------------------------- */
/*                                deleteprompter                              */
/* -------------------------------------------------------------------------- */
static void deleteprompter(void)
{
   GgiDelete(&prompter);
   DescheduleKeyevent(&ok_id);
   DescheduleKeyevent(&can_id);
   if (dev_id) DescheduleKeyevent(&dev_id);
   DescheduleKeyevent(&rej_id);
   wkeyf(KEY_OK);                                         /* reset OK button */
   prompter = NULL;
}



/* -------------------------------------------------------------------------- */
/*                                 handle_cancel                              */
/* -------------------------------------------------------------------------- */
/*  keyword handler for the CANCEL button.
 */
static void handle_cancel(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      deleteprompter();
   }
}

/* -------------------------------------------------------------------------- */
/*                                 askagain                                   */
/* -------------------------------------------------------------------------- */
static void askagain(char *message)
{
   if (prompter) {
      GgiSetLabel(l_message, message, 0);
      delivered = FALSE;
   }
}

/* -------------------------------------------------------------------------- */
/*                                 handle_reject                              */
/* -------------------------------------------------------------------------- */
/*  keyword handler for reject events on the input.
 */
static void handle_reject(ident id, char *key, int code, void *arg)
{
   fchar  text;
   char  ctext[41];
   fint   nread;
    
   text.a = ctext; text.l = 40;
   nread = userftext(text, -1, "UCAMESSAGE=", " ");
   ctext[nread] = '\0';
   askagain(nread?ctext:"Bad input");
}

/* -------------------------------------------------------------------------- */
/*                                 accepted                                   */
/* -------------------------------------------------------------------------- */
static void accepted(void)
{
   if (prompter) {
      deleteprompter();
   }
}

/* -------------------------------------------------------------------------- */
/*                                 newprompter                                */
/* -------------------------------------------------------------------------- */
static void newprompter(char *filekey, char *devkey, char *message)
{
   ident prvshl;
   ident b_ok, b_cancel;
   ident frame;
   ident l_prompt, t_input;
   ident l_plotter;
   fint  nread;
   fchar text;
   char ctext[128];
    
   text.a = ctext; text.l=127;
   nread = userftext(text, 2, filekey, " ");
   ctext[nread] = '\0';
   sprintf(file_inp, "_GP_%s", filekey);
   sprintf(dev_inp, "_GP_%s", devkey);
   if (nread) {
      wkeyf("%s%s", file_inp, ctext); /* copy key contents to input field */
      expected = TRUE;               /* suppress event handling */
   }
   strcpy(currfile, filekey);     /* save current key */
   strcpy(currdev, devkey);       /* save current device key */
   delivered = FALSE;
   prompter =  GgiDialog(NAME);
   
   prvshl = GgiUseShell(prompter);
   b_ok   = GgiSetLabel(GgiButton(KEY_OK, NULL), "PLOT", 0);
   GgiDeactivate(b_ok);
   ok_id = ScheduleKeyevent(handle_ok, KEY_OK, KEYCHANGE, NULL);
   b_cancel = GgiSetLabel(GgiButton(KEY_CAN, NULL), "CANCEL", 0);
   can_id = ScheduleKeyevent(handle_cancel, KEY_CAN, KEYCHANGE, NULL);
   frame = GgiForm(NAME, 2);
   GgiSetPosition(frame,    0, NULL, 0, NULL);
   GgiSetPosition(b_cancel, 0, NULL, 0, frame);
   GgiSetPosition(b_ok, -GgiWidth(b_ok)-2, frame, 0, frame);
   
   (void)GgiUseShell(frame);
   l_prompt  = GgiLabel(message);
   t_input   = GgiSetLabel(GgiTextField(file_inp, NULL, 40), " ", 1);
   l_plotter = GgiLabel("Plotter: ");
   if (!plotters) plotters = grdevices('H');
   if (!plotters[0]) {
      m_plotter = GgiSetLabel(GgiLabel("error"),
                              "Error: no PostScript device\n"
                              "found in $gip_loc/grdevices", 0);
   } else {
      m_plotter = GgiSetLabel(GgiMenu(dev_inp, "Graphics device", plotters),
                              "(none)", 0);
      dev_id = ScheduleKeyevent(handle_dev, dev_inp, KEYCHANGE, b_ok);
   }
   nread = userftext(text, 2, devkey, " ");
   if (nread) {
      char **entry;
      int index=0;
      ctext[nread] = '\0';
      for (entry=plotters; *entry; entry++, index++) {
         if (!strcmp(*entry,ctext)) {
            wkeyf("%s%d", dev_inp, index);
            break;
         }
      }
   }
      
   rej_id = ScheduleKeyevent(handle_reject, filekey, KEYREJECT, NULL);
   l_message = GgiSetLabel(GgiLabel("error"), " \n ", 0);
   GgiSetPosition(l_prompt,  0, NULL,      0, NULL);
   GgiSetPosition(t_input,   0, NULL,      0, l_prompt);
   GgiSetPosition(l_plotter, 0, NULL,      8, t_input);
   GgiSetPosition(m_plotter, 0, l_plotter, 0, t_input);
   GgiSetPosition(l_message, 0, NULL,      0, m_plotter);

   (void)GgiUseShell(prvshl);
   GgiShowShell(prompter, TRUE);
}

/* ========================================================================== */
/*                                 GgiPlotPrompter                                   */
/* -------------------------------------------------------------------------- */
/*
 */
void GgiPlotPrompter(char *filekey, char *devkey, char *message)
{
   if (!strcmp("ACCEPT", filekey)) {
      accepted();
   } else if (!strcmp("AGAIN", filekey)) {
      askagain(message);
   } else if (!prompter) {
      newprompter(filekey, devkey, message);
   } else {
      errorf(1, "GgiPlotPrompter: illegal directive: \"%s\"", filekey);
   }
}   

/* ========================================================================== */
/*                                  TEST PROGRAM                              */
/* ========================================================================== */
#if defined(TESTBED)
/*
 *  Simple sample program showing all GgiPlotPrompter() features.
 */
#include "init.h"
#include "finis.h"
#include "cmain.h"
#include "eventmonitor.h"

static bool  confirmed=FALSE;

/*
 *  Button keyword handler for quitting.
 */
static void quit(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);                                         /* reset button    */
      finis_c();
   }
}

/*
 *  Button keyword handler creating the prompter.
 */
static void promptuser(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiPlotPrompter("FILE=", "DEVICE=", "Postscript filename:");  /* create prompter */
      wkeyf(key);                                         /* reset button    */
      anyoutf(0, "Prompter created");
   }
}

/*
 *  Keyword handler receiving the resulting text.
 */
static void getresult(ident id, char *key, int code, void *arg)
{
   fint nread;
   fchar text;
   char ctext[128];

   text.a = ctext; text.l = 127;

   nread = userftext(text, 2, key, " ");
    
   if (nread) {
      if (*ctext == 'Z') {
         /* reject text beginning with 'Z' */
         GgiPlotPrompter("AGAIN", NULL,
                         "Text beginning with 'Z'\n not allowed");
         confirmed = FALSE;
         anyoutf(0, "Illegal text not accepted");
      } else if (*ctext == 'z') {
         rejectf(key, "Lower case 'z' also not allowed");
         anyoutf(0, "Illegal text rejected");
         confirmed = FALSE;
      } else {
         if (*ctext == 'A') {
            /* Accept text beginning with 'A' unconditionally */
            GgiPlotPrompter("ACCEPT", NULL, NULL);             /* Accept text */
            confirmed = FALSE;
            anyoutf(0, "Good text accepted unconditionally");
         } else if (!confirmed) {
            /* Other text must be confirmed first */
            GgiPlotPrompter("AGAIN", NULL, "Please confirm"); /* Confirm text */
            confirmed = TRUE;
            anyoutf(0, "Confirmation requested for questionable text");
         } else {
            /* Text has been confirmed */
            GgiPlotPrompter("ACCEPT", NULL, NULL);             /* Accept text */
            confirmed = FALSE;
            anyoutf(0, "Questionable text confirmed and accepted");
         }
      }
   }
}


MAIN_PROGRAM_ENTRY
{
   ident button, quitbtn;
   
   init_c();
   
   GgiAutoLayout(FALSE);
   GgiPostponeRealize(TRUE); 
   
   button  = GgiButton("PROMPT=", "Pop up prompter");
   quitbtn = GgiButton("QUIT=", "Terminate program");
   GgiSetPosition(button,  0, NULL,   0, NULL);
   GgiSetPosition(quitbtn, 0, button, 0, NULL);
   GgiRealize();
   (void)ScheduleKeyevent(promptuser, "PROMPT=", KEYCHANGE, NULL);
   (void)ScheduleKeyevent(quit, "QUIT=", KEYCHANGE, NULL);
   (void)ScheduleKeyevent(getresult,  "FILE="  , KEYCHANGE, NULL);
   eventmonitor("MONITOR=", TRUE); /* if MONITOR=Y, events are monitored */
   MainLoop();
}
#endif
#<

#>            ggiverify.c
/* ggiverify.c
                              COPYRIGHT (c) 1998
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define NAME "Question"

#include "stddef.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"

static ident shell=NULL;
static ident yes_id, no_id;

/* -------------------------------------------------------------------------- */
/*                                 keyhandler                                 */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for VFY_YES= and VFY_NO=
 */
static void keyhandler(ident id, char *key, int code, void *arg)
{
   bool *result=arg;
   
   wkeyf("VFY_YES= VFY_NO="); /* reset buttons */
   *result = id==yes_id;
   DescheduleKeyevent(&yes_id);
   DescheduleKeyevent(&no_id);
   GgiDelete(&shell);
   shell = NULL;
   BreakMainLoop();
}

/* ========================================================================== */
/*                                 GgiVerify                                  */
/* -------------------------------------------------------------------------- */
bool GgiVerify(char *question, char *truelabel, char *falselabel)
{
   ident prevshell;
   ident message, yes_but, no_but;
   bool  result;
    
   if (shell) return FALSE;              /* only one instance allowed */
   shell = GgiDialog(NAME);
   prevshell = GgiUseShell(shell);
   message = GgiLabel(question);
   yes_but = GgiSetLabel(GgiButton("VFY_YES=", NULL),
             truelabel?truelabel:"YES", 0);
   no_but  = GgiSetLabel(GgiButton("VFY_NO=",  NULL),
             falselabel?falselabel:" NO ",  0);

   GgiSetPosition(message, 20, NULL, 0, NULL);
   GgiSetPosition(yes_but, 20, NULL, 20, message);
   GgiSetPosition(no_but,  40, yes_but, 20, message);
   (void)GgiUseShell(prevshell);
   yes_id = ScheduleKeyevent(keyhandler, "VFY_YES=", KEYCHANGE, &result);
   no_id  = ScheduleKeyevent(keyhandler, "VFY_NO=",  KEYCHANGE, &result);
   GgiShowShell(shell, TRUE);
   MainLoop();
   return result;
}
#<

#>            ggiplotcoledit.c
/* ggiplotcoledit.c -XT
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands


Author: J.P. Terlouw
*/

#define MAXCOLORS 256
#define MAXOBJ  10
#define MAXMAP  (13+1) /* +1 for _LINE menu element */
#define WIDTH  265

#include "stddef.h"
#include "stdlib.h"
#include "stdarg.h"
#include "stdio.h"
#include "math.h"
#include "gipsyc.h"
#include "userfio.h"
#include "pgplot.h"
#include "ggi.h"

#include <X11/Intrinsic.h>

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

typedef struct {
   float        red;
   float        green;
   float        blue;
} Color_struct;

#include "colormaps.H"

static Color_struct user[256];

static char *maps[]={"Mono",
                     "Rainbow",
                     "RonEkers",
                     "Staircase",
                     "Background",
                     "Heat",
                     "Isophot",
                     "Mousse",
                     "Random",
                     "RGB",
                     "Smooth",
                     "Renzo",
                     "_LINE",
                     "User",
                     NULL};

static char *colors[]={"Black", "Red",    "Green",   "Blue",
                                "Yellow", "Magenta", "Cyan",  "White", NULL};
static char *coldef[]={"0 0 0", "1 0 0",  "0 1 0",   "0 0 1",
                                "1 1 0",  "1 0 1",   "0 1 1", "1 1 1"};

typedef enum{        Mono,
                     Rainbow,
                     RonEkers,
                     Staircase,
                     Background,
                     Heat,
                     Isophot,
                     Mousse,
                     Random,
                     RGB,
                     Smooth,
                     Renzo,
                     _map_line,
                     User} map;

static char *scales[]={"Linear",
                       "Log",
                       "Exp",
                       "Sqrt",
                       "Square",
                       NULL};

typedef enum         { Linear,
                       Log,
                       Exp,
                       Sqrt,
                       Square} scale;

typedef struct {
   int   seqno;                           /* sequence number */
   bool  initialized;                     /* initialized flag */
   int   ncolors;                         /* number of colors */
   ident frame;                           /* container element */
   ident wedge;                           /* wedge plotfield */
   fint  pgid;                            /* PGPLOT device identifier */
   ident slope;                           /* slope valuator */
   float slopeval;                        /* current slope value */
   ident shift;                           /* shift valuator */
   float shiftval;                        /* current shift value */
   float invert;                          /* inversion factor value */
   map   mapval;                          /* current color map identifier */
   scale scaleval;                        /* current scaling */
   float v1, v2;                          /* data limits values */
   char  *units;                          /* data units string */
   Color_struct colormap[MAXCOLORS];      /* color map */
   ident blank;                           /* blank plotfield */
   fint  bl_pgid;                         /* PGPLOT device identifier */
   fint  bl_ci;                           /* blank color index */
   Color_struct bl_color;                 /* blank color */
} _Context, *Context;

float reds[MAXCOLORS];
float greens[MAXCOLORS];
float blues[MAXCOLORS];

static Context contexts[MAXOBJ];
static int number=0;
static Color_struct *colormaps[MAXMAP];
static float f0=0.0, f1=1.0;

/* -------------------------------------------------------------------------- */
/*                                 confromid                                  */
/* -------------------------------------------------------------------------- */
/*  Find Context given an ident.
 */
static Context confromid(ident id)
{
   int i;
   Context context=NULL;
   
   for (i=0; i<number; i++) {
      context = contexts[i];
      if (id==context->frame) break;
   }
   return context;
}

/* -------------------------------------------------------------------------- */
/*                                 blank                                      */
/* -------------------------------------------------------------------------- */
/*  Draw blank field
 */
static void blank(Context context)
{
   float v=0.0, w=0.1;
   float tr[6];
   fint  one=1, icilo=16, icihi=17;
   tr[0] = -0.5; tr[1] = 1.0; tr[2] = 0.0;
   tr[3] = -0.5; tr[4] = 0.0; tr[5] = 1.0;
   pgpage_c();
   pgscir_c(&icilo, &icihi);
   pgimag_c(&v, &one, &one, &one, &one, &one, &one, &v, &w, tr);
}

/* -------------------------------------------------------------------------- */
/*                                 wedge                                      */
/* -------------------------------------------------------------------------- */
/*  Draw plotfield's wedge.
 */
static void wedge(Context context)
{
   int   i;
   float imag[WIDTH];
   fint  icilo, icihi;
   float a1=0.0, a2=(float)(WIDTH-1);
   float tr[6];
   fint  idim=WIDTH, jdim=1, i1=1, i2=WIDTH, j1=1, j2=1;
   icilo = 16;
   icihi = context->ncolors+15;
   for (i=0; i<WIDTH; i++) imag[i] = (float)i;
   tr[0] = -0.5/(float)WIDTH; tr[1] = 1.0/(float)WIDTH; tr[2] = 0.0;
   tr[3] = -0.5;              tr[4] = 0.0;              tr[5] = 1.0;
   pgswin_c(&f0, &f1, &f0, &f1);
   pgscir_c(&icilo, &icihi);
   pgimag_c(imag, &idim, &jdim, &i1, &i2, &j1, &j2, &a1, &a2, tr);      
}

/* -------------------------------------------------------------------------- */
/*                                 annot                                      */
/* -------------------------------------------------------------------------- */
/*  Draw axes and labels.
 */
static void annot(Context context)
{
   float xtick = 0.0;
   fint  nxsub = 0;
   float ch=7.0;
   
   pgsch_c(&ch);
   if (context->v1 != context->v2) {
      pgswin_c(&context->v1, &context->v2, &f0, &f1);
      pgbox_c(tofchar("ABINTS"), &xtick, &nxsub,
              tofchar("BC"), &xtick, &nxsub);
      pgbox_c(tofchar("C"), &xtick, &nxsub, 
              tofchar("BC"), &xtick, &nxsub);
   } else {
      pgswin_c(&f0, &f1, &f0, &f1);
      pgbox_c(tofchar("ABITS"), &xtick, &nxsub,
              tofchar("BC"), &xtick, &nxsub);
      pgbox_c(tofchar("C"), &xtick, &nxsub, 
              tofchar("BC"), &xtick, &nxsub);
   }
   if (*context->units) {
      float disp=1.2;
      ch = 6;
      pgsch_c(&ch);
      pgmtxt_c(tofchar("R"), &disp, &f0, &f0, tofchar(context->units));
   }
}
   
/* -------------------------------------------------------------------------- */
/*                                 loadmap                                    */
/* -------------------------------------------------------------------------- */
/*  Load colormap and apply scaling.
 */
static void loadmap(Context context)
{
   int i, v;
   Color_struct *dstmap=context->colormap, *srcmap=colormaps[context->mapval];
   
   switch (context->scaleval) {
      case Log: {
         for (i=0; i<MAXCOLORS; i++) {
            v = (double)(MAXCOLORS-1) /
                   log10((double)(MAXCOLORS)) *
                   log10((double)(i+1));
            dstmap[i] = srcmap[v];
         }
         break;
      }
      case Exp: {
         for (i=0; i<MAXCOLORS; i++) {
            v = (double)(MAXCOLORS-1) /
                   (pow(10.0, (double)(MAXCOLORS-1)/100.0) -1.0) *
                   (pow(10.0, (double)(i)/100.0) -1.0);
            dstmap[i] = srcmap[v];
         }
         break;
      }
      case Sqrt: {
         for (i=0; i<MAXCOLORS; i++) {
            v = (double)(MAXCOLORS-1) /
                   sqrt((double)MAXCOLORS) *
                   sqrt((double)i);
            dstmap[i] = srcmap[v];
         }
         break;
      }
      case Square: {
         for (i=0; i<MAXCOLORS; i++) {
            v = (double)(MAXCOLORS-1) /
                    (double)(MAXCOLORS*MAXCOLORS) *
                    (double)(i*i);
            dstmap[i] = srcmap[v];
         }
         break;
      }
      case Linear:
      default:     {
         for (i=0; i<MAXCOLORS; i++) {
            dstmap[i] = srcmap[i];
         }
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 applymap                                   */
/* -------------------------------------------------------------------------- */
/*  Apply appropriately scaled and shifted colormap to current PGPLOT device.
 */
static void applymap(Color_struct *colormap,
                     float slopeval, float shiftval, int cstart, int ncolors)
{
   int i;
   float x, y;
   int   m;
   fint  ci=cstart, ncol=ncolors;
   for (i=0; i<ncolors; i++) {
      x = ((float)i/(float)(ncolors-1))-0.5;
      y = slopeval*(x-shiftval)+0.5;
      if (y>1.0) y = 1.0;
      else if (y<0.0) y = 0.0;
      m = ((float)(MAXCOLORS-1)*y+0.5);
      reds[i]   = colormap[m].red;
      greens[i] = colormap[m].green;
      blues[i]  = colormap[m].blue;
   }
   pgsncr_c(&ci, reds, greens, blues, &ncol);
}

/* -------------------------------------------------------------------------- */
/*                                 modblank                                   */
/* -------------------------------------------------------------------------- */
/*   Update blank value, reflecting changes in input field.
 */
static void modblank(Context context)
{
   fint ppgid, i16=16;
  
   pgqid_c(&ppgid);
   pgslct_c(&context->bl_pgid);
   pgscr_c(&i16, &context->bl_color.red,
                            &context->bl_color.green,
                            &context->bl_color.blue);
   pgslct_c(&ppgid);
}

/* -------------------------------------------------------------------------- */
/*                                 modmap                                     */
/* -------------------------------------------------------------------------- */
/*   Update colormap, reflecting any changes in slope and shift.
 */
static void modmap(Context context)
{
   fint ppgid;
   
   if (GgiIsRealized(context->wedge)) {
      pgqid_c(&ppgid);
      if (!context->initialized) {
         char  name[10], key[KEYLEN];
         float leftmar=0.05, rightmar=0.95, botmar=0.3, topmar=0.9;
   
         if (context->blank) {
            sprintf(name, "CE_BLANK%d", context->seqno);
            context->bl_pgid = pgopen_c(tofchar(name));
            blank(context);
            sprintf(key, "CE_BLANK%d=", context->bl_ci);
            if (!TriggerKey(key)) wkeyf("%s 0 0 0", key);
         }
         sprintf(name, "CE_WEDGE%d", context->seqno);
         context->pgid = pgopen_c(tofchar(name));
         pgsvp_c(&leftmar, &rightmar, &botmar, &topmar);
         loadmap(context);
         pgpage_c();
         wedge(context);
         annot(context);
         context->initialized = TRUE;
      } else {
         pgslct_c(&context->pgid);
      }
      applymap(context->colormap, context->invert*context->slopeval,
                        context->shiftval, 16, context->ncolors);
      if (ppgid) pgslct_c(&ppgid);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 getslope                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_SLOPEn=
 */
static void getslope(ident id, char *key, int code, void *arg)
{
   Context context=(Context)arg;
   (void)userfreal(&context->slopeval, 1, 2, key, " ");
   modmap(context);
}
   
/* -------------------------------------------------------------------------- */
/*                                 getshift                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_SHIFTn=
 */
static void getshift(ident id, char *key, int code, void *arg)
{
   Context context=(Context)arg;
   (void)userfreal(&context->shiftval, 1, 2, key, " ");
   modmap(context);
}

/* -------------------------------------------------------------------------- */
/*                                 selmap                                     */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_MAPn=
 */
static void selmap(ident id, char *key, int code, void *arg)
{
   Context context=(Context)arg;
   fint mapno;
   
   (void)userfint(&mapno, 1, 2, key, " ");
   context->mapval = mapno;
   loadmap(context);
   modmap(context);
}

/* -------------------------------------------------------------------------- */
/*                                 getblank                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_BLANKn=
 */
static void getblank(ident id, char *key, int code, void *arg)
{
   Context context=(Context)arg;
   
   (void)userfreal((float*)&context->bl_color, 3, 4, key, " ");
   modblank(context);
}

/* -------------------------------------------------------------------------- */
/*                                 getscale                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_SCALEn=
 */
static void getscale(ident id, char *key, int code, void *arg)
{
   Context context=(Context)arg;
   fint scaleno;
   
   (void)userfint(&scaleno, 1, 2, key, " ");
   context->scaleval = scaleno;
   loadmap(context);
   modmap(context);
}

/* -------------------------------------------------------------------------- */
/*                                 getusermap                                 */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_USER= for loading user color map.
 */
static void getusermap(ident id, char *key, int code, void *arg)
{
   char   name[FILENAME_MAX+1];
   fchar fname;
   int   i, nread;
   FILE  *f;
   
   fname.a = name; fname.l=FILENAME_MAX;
   nread = userftext(fname, 2, key, " ");
   if (!nread) return;
   name[nread] = '\0';
   f = fopen(name, "r");
   if (!f) {
      rejectf(key, "Cannot open");
      return;
   }
   for (i=0; i<MAXCOLORS; i++) {
      fscanf( f, "%e%e%e", &user[i].red, &user[i].green, &user[i].blue);
   }
   fclose(f);
   for (i=0; i<number; i++) {
      char mapkey[KEYLEN];
      sprintf(mapkey, "CE_MAP%d=", i+1);
      (void)TriggerKey(mapkey);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 mapfilter                                  */
/* -------------------------------------------------------------------------- */
static char *mapfilter(char *name)
{
   int  namlen=strlen(name);
   if (strcmp(name+namlen-4, ".lut")) return NULL;
   return name;
}

/* -------------------------------------------------------------------------- */
/*                                 selectmap                                  */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_BROWSE= for selecting user color map.
 */
static void selectmap(ident id, char *key, int code, void *arg)
{
   ident browser;
   char *mapkey=(char *)arg;

   bool button=toflog(FALSE);
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      browser = GgiFileBrowser(ggiIdent, mapkey);
      if (browser) {
         (void)GgiFileBrowser(ggiDelete, browser);
      } else {
         browser = GgiFileBrowser(ggiCreate, mapkey, "Select color map file",
                                             mapfilter);
         (void)GgiFileBrowser(ggiNowait, browser);
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 reset                                      */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_RESETn=
 */
static void reset(ident id, char *key, int code, void *arg)
{
   Context context=(Context)arg;
   bool button=toflog(FALSE);
   
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      int seqno=context->seqno;
      wkeyf("%s CE_SLOPE%d=1.0 CE_SHIFT%d=0.0", key, seqno, seqno);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 invert                                     */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_INVERTn=
 */
static void invert(ident id, char *key, int code, void *arg)
{
   Context context=(Context)arg;
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   context->invert=tobool(button)?-1.0:+1.0;
   modmap(context);
}

/* -------------------------------------------------------------------------- */
/*                                 getcolor                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for CE_BLMENUn=
 */
static void getcolor(ident id, char *key, int code, void *arg)
{
   fint color=0;
    
   if (userfint(&color, 1, 2, key, " ")==1) {
      wkeyf("CE_BLANK%d=%s", ((Context)arg)->bl_ci, coldef[color]);
      wkeyf(key);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 create                                     */
/* -------------------------------------------------------------------------- */
/*  Create the colormap editor.
 */
static ident create(int ci, int nc, int bl)
{
   Context context;
   ident   curshell;
   char    key[KEYLEN];
   ident   resetbut, invertbut, userbut, mapmenu, scalemenu;
   fint    tmpfint;
   
   if (number==MAXOBJ) return NULL;
   if (!number) {
                                                          /* initialization */
      (void)ScheduleKeyevent(getusermap, "CE_USER=", KEYCHANGE, NULL);
      (void)ScheduleKeyevent(selectmap, "CE_BROWSE=", KEYCHANGE, "CE_USER=");
      (void)TriggerKey("CE_USER=");
   }
   context = New(_Context);
   contexts[number++] = context;
   context->seqno = number;
   context->ncolors = nc;
   context->invert  = 1.0;
   context->frame = GgiForm("Colors", 1);
   context->units = NNew(1,char);
   context->bl_ci = bl;
   context->slopeval = 1.0;
   context->shiftval = 0.0;
   context->invert   = 1.0;
   context->mapval   = 0;
   context->scaleval = 0;
   curshell = GgiUseShell(context->frame);
   
   sprintf(key, "CE_SLOPE%d=", number);
   context->slope = GgiGauge(key, "Colormap slope", 250, 0.0, 5.0);
   GgiSetLabel(context->slope, "SLOPE", 60);
   (void)ScheduleKeyevent(getslope, key, KEYCHANGE, context);
   
   sprintf(key, "CE_SHIFT%d=", number);
   context->shift = GgiGauge(key, "Colormap shift", 250, -0.5, 0.5);
   GgiSetLabel(context->shift, "SHIFT", 60);
   (void)ScheduleKeyevent(getshift, key, KEYCHANGE, context);
   
   userbut = GgiSetLabel(GgiButton("CE_BROWSE=", "Load user LUT file"),
                         "LOAD", 0);
 
   sprintf(key, "CE_MAP%d=", number);
   mapmenu = GgiSetLabel(GgiMenu(key, NULL, maps), "COLOR", 0);
   (void)ScheduleKeyevent(selmap, key, KEYCHANGE, context);
   
   sprintf(key, "CE_SCALE%d=", number);
   scalemenu = GgiSetLabel(GgiMenu(key, NULL, scales), "SCALE", 0);
   (void)ScheduleKeyevent(getscale, key, KEYCHANGE, context);
   
   sprintf(key, "CE_RESET%d=", number);
   resetbut = GgiSetLabel(GgiButton(key, NULL), "RESET", 0);
   (void)ScheduleKeyevent(reset, key, KEYCHANGE, context);

   sprintf(key, "CE_INVERT%d=", number);
   invertbut = GgiSetLabel(GgiButton(key, NULL), "INVERT", 0);
   (void)ScheduleKeyevent(invert, key, KEYCHANGE, context);

   sprintf(key, "CE_WEDGE%d", number);
   context->wedge = GgiPlotField(key, 315, 65);
   GgiPlotMapColors(context->wedge, 0, 16, ci, nc, -1);
   
   GgiSetPosition(context->wedge, 0, NULL,    0, NULL);
   GgiSetPosition(context->slope, 0, NULL,    0, context->wedge);
   GgiSetPosition(context->shift, 0, NULL,    0, context->slope);

   GgiSetPosition(scalemenu, -GgiWidth(scalemenu)-1, context->shift,
                                                  0, context->shift);
   GgiSetPosition(mapmenu, -GgiWidth(mapmenu)-GgiWidth(scalemenu)-4, scalemenu,
                                                  0, context->shift);
   GgiSetPosition(userbut, -GgiWidth(userbut)-GgiWidth(mapmenu)-4, mapmenu,
                                                  0, context->shift);

   GgiSetPosition(resetbut,  0, NULL,     0, context->shift);
   GgiSetPosition(invertbut, 0, resetbut, 0, context->shift);

   if (bl>15) {
      ident bltext, blmenu;
      
      sprintf(key, "CE_BLANK%d=", bl);   /* keyword linked with color index! */
      bltext = GgiTextField(key,
                        "Red, green, blue values for BLANK [0,1]", 10);
      (void)GgiSetLabel(bltext, " ", 1);
      (void)ScheduleKeyevent(getblank, key, KEYCHANGE, context);
      sprintf(key, "CE_BLMENU%d=", bl);      
      blmenu = GgiSetLabel(GgiMenu(key, NULL, colors), "BLANK color", 0);
      (void)ScheduleKeyevent(getcolor, key, KEYCHANGE, context);
      sprintf(key, "CE_BLANK%d", number);
      context->blank = GgiPlotField(key, 50, 23);
      GgiPlotMapColors(context->blank, 0, 16, bl, 1, -1);
      GgiSetPosition(blmenu,         0, NULL,   15, resetbut);
      GgiSetPosition(bltext,         0, blmenu, 15, resetbut);
      GgiSetPosition(context->blank, 5, bltext, 15, resetbut);
   }

   (void)GgiUseShell(curshell);
   
   colormaps[Mono]       = mono;
   colormaps[Rainbow]    = rainbow;
   colormaps[RonEkers]   = ronekers;
   colormaps[Staircase]  = staircase;
   colormaps[Background] = background;
   colormaps[Heat]       = heat;
   colormaps[Isophot]    = isophot;
   colormaps[Mousse]     = mousse;
   colormaps[Random]     = ranlut;
   colormaps[RGB]        = rgb;
   colormaps[Smooth]     = smooth;
   colormaps[Renzo]      = renzo;
   colormaps[User]       = user;

   sprintf(key, "CE_SLOPE%d=", context->seqno);
   if (!TriggerKey(key)) wkeyf("%s 1", key);         /* attempt to initialize */
   sprintf(key, "CE_SHIFT%d=", context->seqno);
   (void)userfreal(&context->shiftval, 1, 2, key, " ");
   sprintf(key, "CE_INVERT%d=", context->seqno);
   {
      bool button=toflog(FALSE);
      (void)userflog(&button, 1, 2, key, " ");
      context->invert = tobool(button)?-1.0:+1.0;
   }
   sprintf(key, "CE_MAP%d=", context->seqno);
   if (userfint(&tmpfint, 1, 2, key, " ")==1) context->mapval = tmpfint;
   sprintf(key, "CE_SCALE%d=", context->seqno);
   if (userfint(&tmpfint, 1, 2, key, " ")==1) context->scaleval = tmpfint;
   return context->frame;
}

/* -------------------------------------------------------------------------- */
/*                                 modcol                                     */
/* -------------------------------------------------------------------------- */
/*  Modify the color indices to be operated upon.
 */
static void modcol(ident id, int ci, int nc)
{
   /* ... */
}

/* -------------------------------------------------------------------------- */
/*                                 modval                                     */
/* -------------------------------------------------------------------------- */
/*  Modify the data limits.
 */
static void modval(ident id, float v1, float v2)
{
   Context context=confromid(id);
   fint ppgid;
    
   context->v1 = v1;
   context->v2 = v2;
   pgqid_c(&ppgid);
   if (context->initialized) {
      pgslct_c(&context->pgid);
      pgpage_c();
      wedge(context);
      annot(context);
   }
   if (ppgid) pgslct_c(&ppgid);
}

/* -------------------------------------------------------------------------- */
/*                                 modunt                                     */
/* -------------------------------------------------------------------------- */
/*  Modify the data units.
 */
static void modunt(ident id, char *units)
{
   Context context=confromid(id);
   fint ppgid;
   
   Delete(context->units);
   context->units =  NNew(strlen(units)+1,char);
   strcpy(context->units, units);
   pgqid_c(&ppgid);
   if (context->initialized) {
      pgslct_c(&context->pgid);
      pgpage_c();
      wedge(context);  
      annot(context);
   }
   if (ppgid) pgslct_c(&ppgid);
}

/* -------------------------------------------------------------------------- */
/*                                 apply                                      */
/* -------------------------------------------------------------------------- */
/*  Apply the colormap to the currently open PGPLOT device, e.g. for
 *  hardcopies.
 */
static void apply(ident id)
{
   int i;
   Context context;
   fint c1, c2;
   
   for (i=0; i<number; i++) {
      context = contexts[i];
      if (id==context->frame) break;
   }
   if (context->initialized) {
      pgqcir_c(&c1, &c2);
      applymap(context->colormap, context->invert*context->slopeval,
               context->shiftval, (int)c1, (int)(c2-c1+1));
      if (context->blank) {
         pgqbci_c(&c1);
         if (c1>15) {
            pgscr_c(&c1, &context->bl_color.red,
                         &context->bl_color.green,
                         &context->bl_color.blue);
         }
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 getmap                                     */
/* -------------------------------------------------------------------------- */
/*  Obtain the current color map for use in the application.
 */
static void getmap(ident id, int nc, float *reds, float *greens, float *blues,
                                     float *bred, float *bgreen, float *bblue)
{
   int i, m;
   float x, y, slopeval, shiftval;
   Color_struct *colormap;
   Context context;
    
   for (i=0; i<number; i++) {
      context = contexts[i];
      if (id==context->frame) break;
   }
   if (context->initialized) {
      slopeval = context->invert*context->slopeval;
      shiftval = context->shiftval;
      colormap = context->colormap;
      if (context->blank) {
         *bred   = context->bl_color.red;
         *bgreen = context->bl_color.green;
         *bblue  = context->bl_color.blue;
      } else {
         *bred = *bgreen = *bblue = -1.0;          /* blank not defined */
      }
      for (i=0; i<nc; i++) {
         x = ((float)i/(float)(nc-1))-0.5;
         y = slopeval*(x-shiftval)+0.5;
         if (y>1.0) y = 1.0; else if (y<0.0) y = 0.0;
         m = ((float)(MAXCOLORS-1)*y+0.5);
         reds[i]   = colormap[m].red;
         greens[i] = colormap[m].green;
         blues[i]  = colormap[m].blue;
      }
   }
}

/* ========================================================================== */
/*                                 GgiPlotColorEditor                         */
/* -------------------------------------------------------------------------- */
/*
 */
ident GgiPlotColorEditor(int opcode, ... )
{
   va_list args;
   ident result=NULL;
   
   va_start( args, opcode);
   switch (opcode) {
      case ggiCreate: {
         int ci, nc, bl;
         ci = va_arg(args, int);
         nc = va_arg(args, int);
         bl = va_arg(args, int);
         result = create(ci, nc, bl);
         break;
      }
      case ggiColind: {
         int ci, nc;
         result = va_arg(args, ident);
         ci = va_arg(args, int);
         nc = va_arg(args, int);
         modcol(result, ci, nc);
         break;
      }
      case ggiLimits: {
         float v1, v2;
         result = va_arg(args, void*);
         v1 = va_arg(args, double);
         v2 = va_arg(args, double);
         modval(result, v1, v2);
         break;
      }
      case ggiUnits: {
         char *units;
         result = va_arg(args, ident);
         units = va_arg(args, char*);
         modunt(result, units);
         break;
      }
      case ggiApply: {
         result = va_arg(args, ident);
         apply(result);
         break;
      }
      case ggiGetMap: {
         int msize;
         float *reds, *greens, *blues;
         float *bred, *bgreen, *bblue;
         result = va_arg(args, ident);
         msize  = va_arg(args, int);
         reds   = va_arg(args, float*);
         greens = va_arg(args, float*);
         blues  = va_arg(args, float*);
         bred   = va_arg(args, float*);
         bgreen = va_arg(args, float*);
         bblue  = va_arg(args, float*);
         getmap(result, msize, reds, greens, blues, bred, bgreen, bblue);
         break;
      }
      default: {
      }
   }
   va_end(args);
   return result;
}

#if defined(TESTBED)  /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
#include "init.h"
#include "finis.h"
#include "cmain.h"

static void limits(ident id, char *key, int code, void *arg)
{
   float values[2];
    
   (void)userfreal(values, 2, 2, key, " ");
   (void)GgiPlotColorEditor(ggiLimits, (ident)arg, values[0], values[1]);
}

static void units(ident id, char *key, int code, void *arg)
{
   fchar text;
   char  ctext[10];
   fint  nread;
    
   text.l = 9; text.a = ctext;
   nread = userftext(text, 2, key, " ");
   ctext[nread] = '\0';
   (void)GgiPlotColorEditor(ggiUnits, (ident)arg, ctext);
}

static void quit(ident id, char *key, int code, void *arg)
{
   float reds[20], greens[20], blues[20], bred, bgreen, bblue;
   ident cm=(ident)arg;
   bool button=toflog(FALSE);
   int i;

   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiPlotColorEditor(ggiGetMap, cm, 20, reds,  greens,  blues,
                                      &bred, &bgreen, &bblue);
      anyoutf(0, "Color Map:");
      for (i=0; i<20; i++) {
         anyoutf(0, "%f %f %f", reds[i], greens[i], blues[i]);
      }
      anyoutf(0, "Blank: %f %f %f", bred, bgreen, bblue);
      wkeyf(key);
      finis_c();
   }
}

MAIN_PROGRAM_ENTRY
{
   ident cm1, cm2, p;
   
   init_c();
   GgiAutoLayout(FALSE);
   GgiPostponeRealize(TRUE);
   p = GgiButton("QUIT=", NULL);
   (void)GgiPlotColors(NULL, 16+130);
   cm1 = GgiPlotColorEditor(ggiCreate, 16, 64, 16+130-2);
   cm2 = GgiPlotColorEditor(ggiCreate, 16+64, 64, 16+130-2);
   GgiSetPosition(cm1, 0, NULL, 0, NULL);
   GgiSetPosition(cm2, 0, cm1,  0, NULL);
   GgiSetPosition(p,   0, NULL, 0, cm1);
   GgiRealize();

   (void)ScheduleKeyevent(limits, "LIM1=", KEYCHANGE, cm1);
   (void)ScheduleKeyevent(limits, "LIM2=", KEYCHANGE, cm2);
   (void)ScheduleKeyevent(units, "UNT1=", KEYCHANGE, cm1);
   (void)ScheduleKeyevent(units, "UNT2=", KEYCHANGE, cm2);
   (void)ScheduleKeyevent(quit, "QUIT=", KEYCHANGE, cm1);
   
   MainLoop();
}
#endif
#<

#>            ggifilebrowser.c
/* ggifilebrowser.c -XT
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands


Author: J.P. Terlouw
*/

#define NAME     "File selector"
#define MAXINST  3             /* maximum number of instances */
#define DIRINC  100            /* directory array size increment */
#define FILEINC 500            /* file array size increment */
#define MARGIN   80

#include "stddef.h"
#include "stdlib.h"
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
#include <sys/file.h>
#include <sys/stat.h>
#if defined(__bsd__)
#include <sys/dir.h>
#else
#include <dirent.h>
#endif

#include "gipsyc.h"
#include "userfio.h" 
#include "ggi.h"  

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

typedef struct {
   char *name;                     /* file name */
   char *detail;                   /* detailed file information */
} _File, *File;

typedef struct {
   int   seqno;                    /* sequence number */
   int   keyseq;                   /* file keyword sequence number */
   char  key[KEYLEN];              /* output keyword */
   char  dirkey[KEYLEN];           /* directory keyword */
   char  dirxkey[KEYLEN];          /* directory index keyword */
   char  filekey[KEYLEN];          /* file keyword */
   char  filexkey[KEYLEN];         /* file index keyword */
   char  clkey[KEYLEN];            /* CLOSE keyword */
   char  okkey[KEYLEN];            /* OK keyword */
   char  curdir[FILENAME_MAX];     /* currently displayed directory */
   GgiNameconverterProc converter; /* file name converter */
   GgiNameconverterProc getdetail; /* detailed file information converter */
   int   immediate;                /* write output immediately */
   int   closeonok;                /* close after OK */
   int   maxdirs;                  /* current maximum number of directories */
   int   ndirs;                    /* present number of directories */
   char  **dirs;                   /* pointers to directory names */
   int   maxfiles;                 /* current maximum number of files */
   int   nfiles;                   /* present number of files */
   File  files;                    /* pointers to file info structs */
   bool  detailmode;               /* display detailed file information? */
   bool  detailed;                 /* detailed information present flag */
   char  **filenames;              /* pointers to file names */
   ident shell;                    /* pop-up shell */
   ident frame;                    /* outer frame */
   ident dlist;                    /* directory list element */
   ident flist;                    /* file list element */
   ident okbutt;                   /* OK button */
   ident dir_cb;                   /* directory keyword callback */
   ident dirx_cb;                  /* directory index keyword callback */
   ident file_cb;                  /* file keyword callback */
   ident filex_cb;                 /* file index keyword callback */
   ident close_cb;                 /* CLOSE keyword callback */
   ident okay_cb;                  /* OK keyword callback */
   ident reject_cb;                /* output keyword reject callback */
   ident detail_cb;                /* detail button callback */
} _Instance, *Instance;

static char basedir[FILENAME_MAX];
static Instance instances[MAXINST];
static Instance current;
static int ninstances=0;

/* -------------------------------------------------------------------------- */
/*                                 StrDup                                     */
/* -------------------------------------------------------------------------- *//*  String duplication function.
 */
static char *StrDup(char *text)
{
   char *result=NULL;

   if (text) {
      result = NNew(strlen(text)+1,char);
      strcpy(result,text);
   }
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 nullconverter                              */
/* -------------------------------------------------------------------------- */
/*  Default no-op filename converter.
 */
static char *nullconverter(char *name)
{
   return name;
}

/* -------------------------------------------------------------------------- */
/*                                 compdir                                    */
/* -------------------------------------------------------------------------- */
/*  Directory name comparison function for qsort()
 */
static int compdir(const void *s1, const void *s2)
{
   return strcmp(*(char**)s1, *(char**)s2);
}

/* -------------------------------------------------------------------------- */
/*                                 compfil                                    */
/* -------------------------------------------------------------------------- */
/*  File entry comparison function for qsort()
 */
static int compfil(const void *s1, const void *s2)
{
   return strcmp(((File)s1)->name, ((File)s2)->name);
}

/* -------------------------------------------------------------------------- */
/*                                 thisdir                                    */
/* -------------------------------------------------------------------------- */
/*  Obtain current directory.
 */
static void thisdir(char *buffer)
{
#if     defined(__sysv__)
   (void)getcwd(buffer, FILENAME_MAX+1);
#else
   (void)getwd(buffer);
#endif
}

/* -------------------------------------------------------------------------- */
/*                                 clearnames                                 */
/* -------------------------------------------------------------------------- */
static void clearnames(void)
{
   int i;
   for (i=0; i<current->nfiles; i++) {
      Delete(current->files[i].name);
      Delete(current->files[i].detail);
   }
   current->nfiles = 0;
   for (i=0; i<current->ndirs; i++) free(current->dirs[i]);
   current->ndirs = 0;
}

/* -------------------------------------------------------------------------- */
/*                                 makelist                                   */
/* -------------------------------------------------------------------------- */
/*  Make list for GgiList element.
 */
static void makelist(void)
{
   int i;
   for (i=0; i<current->nfiles; i++) {
      current->filenames[i] = current->detailed?
         current->files[i].detail:current->files[i].name;
   }
   current->filenames[i] = NULL;
}

/* -------------------------------------------------------------------------- */
/*                                 scanner                                    */
/* -------------------------------------------------------------------------- */
static void scanner(void)
{
   struct stat buf;
#if defined(__sysv__)
   struct dirent *entry;
#else
   struct direct *entry;
#endif
   DIR *dir=opendir(".");
   char *name;
    
   if (dir) {
      clearnames();
      while((entry=readdir(dir))) {
         if (!stat(entry->d_name, &buf)) {
            if (S_ISDIR(buf.st_mode)) {
               if (current->ndirs==current->maxdirs) {
                  current->maxdirs += DIRINC;
                  current->dirs = realloc(current->dirs,
                                     (current->maxdirs+1)*sizeof(char*));
               }
               current->dirs[current->ndirs++] = StrDup(entry->d_name);
            } else if ((name = current->converter(entry->d_name))) {
               if (current->nfiles==current->maxfiles) {
                  current->maxfiles += FILEINC;
                  current->files = realloc(current->files,
                                      (current->maxfiles+1)*sizeof(_File));
                  current->filenames = realloc(current->filenames,
                                      (current->maxfiles+1)*sizeof(char*));
               }
               current->files[current->nfiles].name = StrDup(name);
               if (current->getdetail && current->detailmode) {
                  current->files[current->nfiles].detail =
                     StrDup(current->getdetail(name));
               } else {
                  current->files[current->nfiles].detail = NULL;
               }
               current->nfiles++;
            }
         }
      }
      current->detailed = current->getdetail && current->detailmode;
      closedir(dir);
      current->dirs[current->ndirs]        = NULL;
      current->files[current->nfiles].name = NULL;
      qsort(current->dirs,  current->ndirs,  sizeof(char*),  compdir);
      qsort(current->files, current->nfiles, sizeof(_File),  compfil);
      makelist();
   } else {
      rejectf(current->dirkey, "Cannot read directory");
   }
}

/* -------------------------------------------------------------------------- */
/*                                 relayerr                                   */
/* -------------------------------------------------------------------------- */
/*  Propagate reject from destination keyword to internal keyword.
 */
static void relayerr(ident id, char *key, int code, void *arg)
{
   fchar message;
   char cmessage[80];
   fint nread;

   message.a = cmessage; message.l = 80;
   nread = userftext(message, -1, "UCAMESSAGE=", " ");
   cmessage[nread] = '\0';
   rejectf((char*)arg, "%s", cmessage);
}  

/* -------------------------------------------------------------------------- */
/*                                 switchdir                                  */
/* -------------------------------------------------------------------------- */
static int switchdir(char *newdir)
{
   int result=-1;
   
   thisdir(basedir);
   (void)chdir(current->curdir);
   if (!chdir(newdir)) {
      thisdir(current->curdir);
      scanner();
      GgiListLabels(current->dlist, current->dirs);
      GgiListLabels(current->flist, current->filenames);
      result = 0;
   }
   (void)chdir(basedir);
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 enterdir                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for directory text field.
 */
static void enterdir(ident id, char *key, int code, void *arg)
{
   char dirname[FILENAME_MAX+1];
   fchar fdirname;
   fint  nread;
    
   fdirname.a = dirname; fdirname.l = FILENAME_MAX;
   current = (Instance)arg;
   nread = userftext(fdirname, 2, key, " ");
   if (nread>0) {
      dirname[nread] = '\0';
      if (switchdir(dirname)) rejectf(key, "Cannot open directory");
   }
}

/* -------------------------------------------------------------------------- */
/*                                 selectdir                                  */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for directory selection from list.
 */
static void selectdir(ident id, char *key, int code, void *arg)
{
   fint nread, ix;
   
   current = (Instance)arg;
   nread = userfint(&ix, 1, 2, key, " ");
   if (nread>0) {
      if  (!switchdir(current->dirs[ix])) {
         SuppressKeyevent(enterdir, current->dirkey);
         wkeyf("%s%s", current->dirkey, current->curdir);
      } else {
         rejectf(key, "Cannot open directory");
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 delete                                     */
/* -------------------------------------------------------------------------- */
/*  Delete file browser.
 */
static void delete(ident instance)
{
   int i;
    
   for (i=0; i<MAXINST; i++) {
      if (instance && instance==instances[i]) break;
   }
   if (i==MAXINST) return;                 /* must have been deleted already */
   current = (Instance)instance;
   clearnames();
   Delete(current->dirs);
   Delete(current->files);
   Delete(current->filenames);
   DescheduleKeyevent(&current->dir_cb);
   DescheduleKeyevent(&current->dirx_cb);
   DescheduleKeyevent(&current->file_cb);
   DescheduleKeyevent(&current->filex_cb);
   DescheduleKeyevent(&current->close_cb);
   DescheduleKeyevent(&current->okay_cb);
   DescheduleKeyevent(&current->reject_cb);
   if (current->detail_cb) DescheduleKeyevent(&current->detail_cb);
   GgiDelete(&current->shell);
   instances[current->seqno] = NULL;
   Delete(current);
   ninstances--;
}

/* -------------------------------------------------------------------------- */
/*                                 enterfile                                  */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for file text field.
 */
static void enterfile(ident id, char *key, int code, void *arg)
{
   char filename[FILENAME_MAX+1];
   fchar ffilename;
   fint  nread;
    
   ffilename.a = filename; ffilename.l = FILENAME_MAX;
   current = (Instance)arg;
   nread = userftext(ffilename, 2, key, " ");
   if (nread>0) {
      filename[nread] = '\0';
      wkeyf("%s%s", current->key, filename);
      if (current->closeonok) delete(current);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 selectfile                                 */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for file selection from list.
 */
static void selectfile(ident id, char *key, int code, void *arg)
{
   fint nread, ix;
    
   current = (Instance)arg;
   nread = userfint(&ix, 1, 2, key, " ");
   if (nread>0) {
      if (!current->immediate) SuppressKeyevent(enterfile, current->filekey);
      if (*current->curdir) {
         wkeyf("%s%s/%s", current->filekey,
                          current->curdir, current->files[ix].name);
      } else {
         wkeyf("%s%s", current->filekey, current->files[ix].name);
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 okay                                       */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for OK button.
 */
static void okay(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
 
   current = (Instance)arg;
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      (void)TriggerKey(current->filekey);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 closeproc                                  */
/* -------------------------------------------------------------------------- */
/*  keyword handler causing the element to be deleted.
 */
static void closeproc(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      delete(arg);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 toggle_info                                */
/* -------------------------------------------------------------------------- */
static void toggle_info(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   
   current = (Instance)arg;
   (void)userflog(&button, 1, 2, key, " ");
   current->detailmode = tobool(button);
#if 0
   (void)TriggerKey(current->dirkey);
#else
#if 1
   if (!*current->curdir) {
      scanner();
      GgiListLabels(current->flist, current->filenames);
   } else {
      switchdir(current->curdir);
   }
#else
   if (current->detailmode) {
      if (current->detailed) {
         makelist();
      } else {
         if (!*current->curdir) {
            scanner();
            GgiListLabels(current->flist, current->filenames);
         } else {
            switchdir(current->curdir);
         }
      }
   } else {
      makelist();
   }
#endif
#endif
   GgiListColumns(current->flist, current->detailmode?-1:3);
}

/* -------------------------------------------------------------------------- */
/*                                 details                                    */
/* -------------------------------------------------------------------------- */
/*  Register file info converter.
 */
static void details(ident instance, GgiNameconverterProc proc)
{
   ident button, prev;
   char key[KEYLEN];
   
   current = (Instance)instance;
   current->getdetail = proc;
   sprintf(key, "_GGIFB_%d_DET=", current->keyseq);
   prev = GgiUseShell(current->shell);
   button = GgiSetLabel(GgiButton(key, "Show detailed information"),
                        "DETAILS", 0);
   GgiSetPosition(button, -GgiWidth(button), current->frame, 0, NULL);
   (void)GgiUseShell(prev);
   current->detail_cb = ScheduleKeyevent(toggle_info, key, KEYCHANGE, current);
   (void)TriggerKey(key);
}

/* -------------------------------------------------------------------------- */
/*                                 create                                     */
/* -------------------------------------------------------------------------- */
static ident create(char *key, char *message, GgiNameconverterProc converter)
{
   int i;
   ident prev, label, frame, dirtext, filetext, dport, fport, clbutt;
   
   if (ninstances>=MAXINST) return NULL;
    
   ninstances++;
   current = New(_Instance);
   for (i=0; i<MAXINST; i++) if (!instances[i]) break;
   instances[i] = current;
   current->seqno   = i;
   current->keyseq  = GgiKeyIdent(key);
   
   strcpy(current->key, key);
   sprintf(current->dirkey,   "_GGIFB_%d_DIR=",   current->keyseq);
   sprintf(current->dirxkey,  "_GGIFB_%d_DIRX=",  current->keyseq);
   sprintf(current->filekey,  "_GGIFB_%d_FILE=",  current->keyseq);
   sprintf(current->filexkey, "_GGIFB_%d_FILEX=", current->keyseq);
   sprintf(current->clkey,    "_GGIFB_%d_CLOSE=", current->keyseq);
   sprintf(current->okkey,    "_GGIFB_%d_OK=",    current->keyseq);
   current->converter = converter?converter:nullconverter;
   current->dirs      = New(char*);
   current->filenames = New(char*);
   current->files     = New(_File);
   current->shell = GgiShell(NAME);
   prev = GgiUseShell(current->shell);
   label = GgiLabel(message);
   GgiSetPosition(label, 0, NULL, 0, NULL);
   frame  = GgiForm(NAME, 2);
   current->frame = frame;
   GgiSetPosition(frame, 0, NULL, 0, label);
   clbutt = GgiSetLabel(GgiButton(current->clkey, NULL), "CLOSE", 0);
   GgiSetPosition(clbutt, 0, NULL, 0, frame);
   current->okbutt = GgiSetLabel(GgiButton(current->okkey, NULL), "OK", 0);
   GgiSetPosition(current->okbutt, -GgiWidth(current->okbutt), frame, 0, frame);
   GgiUseShell(frame);
   dirtext = GgiSetLabel(GgiTextField(current->dirkey, NULL, 50),
                         "Directory:", MARGIN);
   GgiSetPosition(dirtext, 0, NULL, 0, NULL);
   dport = GgiViewport("List", 120, 200);
   GgiSetPosition(dport, 0, NULL,  0, dirtext);
   fport = GgiViewport("List", 350, 200);
   GgiSetPosition(fport, 0, dport, 0, dirtext);
   filetext = GgiSetLabel(GgiTextField(current->filekey, NULL, 50),
                          "Name:", MARGIN);
   GgiSetPosition(filetext, 0, NULL, 0, dport);
   scanner();
   GgiUseShell(dport);
   current->dlist = GgiList(current->dirxkey, -1, current->dirs);
   GgiUseShell(fport);
   current->flist = GgiList(current->filexkey, 3, current->filenames);

   current->dir_cb =
      ScheduleKeyevent(enterdir, current->dirkey, KEYCHANGE, current);
   current->dirx_cb = 
      ScheduleKeyevent(selectdir, current->dirxkey, KEYCHANGE, current);
   current->file_cb =
      ScheduleKeyevent(enterfile, current->filekey, KEYCHANGE, current);
   current->filex_cb =
      ScheduleKeyevent(selectfile, current->filexkey, KEYCHANGE, current);
   current->okay_cb =
      ScheduleKeyevent(okay, current->okkey, KEYCHANGE, current);
   current->close_cb =
      ScheduleKeyevent(closeproc, current->clkey, KEYCHANGE, current);
   current->reject_cb =
      ScheduleKeyevent(relayerr, key, KEYREJECT, current->filekey);

    
   GgiShowShell(current->shell, TRUE);
   (void)GgiUseShell(prev);
   (void)TriggerKey(current->dirkey);
   return (ident)current;
}

/* -------------------------------------------------------------------------- */
/*                                 findid                                     */
/* -------------------------------------------------------------------------- */
/*  find ident, given the filename keyword.
 */
static ident findid(char *key)
{
   int i;
   ident result=NULL;
 
   for (i=0; i<MAXINST; i++) {
      Instance current=instances[i];
      if (current && !strcmp(key, current->key)) {
         result = (ident)current;
         break;
      }
   }
   return result;
}

/* ========================================================================== */
/*                                 GgiFileBrowser                             */
/* -------------------------------------------------------------------------- */
ident GgiFileBrowser(int opcode, ...)
{
   va_list args;
   ident result=NULL;
   
   va_start( args, opcode);
   switch (opcode) {
      case ggiCreate: {
         char *key, *message;
         GgiNameconverterProc converter;
         key       = va_arg(args, char*);
         message   = va_arg(args, char*);
         converter = va_arg(args, GgiNameconverterProc);
         result = create(key, message, converter);
         break;
      }
      case ggiDelete: {
         ident current;
         current = va_arg(args, ident);
         delete(current);
         break;
      }
      case ggiIdent: {
         char *key; 
         key    = va_arg(args, char*);
         result = findid(key);
         break;
      }
      case ggiNowait: {
         Instance current;
         result = va_arg(args, ident);
         current = (Instance)result;
         current->immediate = 1;
         GgiDelete(&current->okbutt);
         break;
      }
      case ggiCloseOnOK: {
         Instance current;
         result = va_arg(args, ident);
         current = (Instance)result;
         current->closeonok = 1;
         break;
      }
      case ggiFileInfo: {
         Instance current;
         GgiNameconverterProc converter;
         result = va_arg(args, ident);
         current = (Instance)result;
         converter = va_arg(args, GgiNameconverterProc);
         details(current, converter);
         break;
      }
#if defined(TESTBED)
      case 999: {
         Instance current;
         int      columns;
         result = va_arg(args, ident);
         current = (Instance)result;
         columns = va_arg(args, int);
         GgiListColumns(current->flist, columns);
         break;
      }
#endif
      default: {
      }
   }
   va_end(args);
   return result;
}

#if defined(TESTBED)  /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
#include "init.h"
#include "finis.h"
#include "cmain.h"
      
       
static ident tbbrowser;
 
static void quit(ident id, char *key, int code, void *arg)
{
   bool finished=toflog(FALSE);

   (void)userflog(&finished, 1, 2, key, " ");
   if (tobool(finished)) {
      wkeyf(key); 
      finis_c();
   }   
}
 
static char *tbconvert(char *name)
{
   int  namlen=strlen(name);
   static char result[FILENAME_MAX];
   
   if (namlen<7) return NULL;
   if (strcmp(name+namlen-6, ".descr")) return NULL;
   strcpy(result, name);
   result[namlen-6] = '\0';
   return result;
}
 
static char *dtconvert(char *name)
{
   static char result[FILENAME_MAX];
   static int count=0;   

   sprintf(result, "%s is file number: %3d", name, count++);
   return result;
}
 
static void tbcreate(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);  

   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
#if 1
      tbbrowser = GgiFileBrowser(ggiCreate, "FILENAME=", "Choose file", tbconvert);
      (void)GgiFileBrowser(ggiFileInfo, tbbrowser, dtconvert);
#else
      tbbrowser = GgiFileBrowser(ggiCreate, "FILENAME=", "Choose file", NULL);
#endif
      (void)GgiFileBrowser(ggiCloseOnOK, tbbrowser);
   }
}
   
static void tbdelete(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
       
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      (void)GgiFileBrowser(ggiDelete, tbbrowser);
   }
}
   
static void tbdeletex(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
       
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      (void)GgiFileBrowser(ggiDelete, GgiFileBrowser(ggiIdent, "FILENAME="));
   }
}
   
static void inset(ident id, char *key, int code, void *arg)
{
   static int count=0;
       
   if (!(count%5)) {
      rejectf(key, "Illegal filename");
   }
   count++;
}

static void changecol(ident id, char *key, int code, void *arg)
{
   fint ncol=0;
   
   (void)userfint(&ncol, 1, 2, key, " ");
   if (ncol) {
      ident browser=GgiFileBrowser(ggiIdent, "FILENAME=");
      if (browser) GgiFileBrowser(999, browser, (int)ncol);
   }
}

MAIN_PROGRAM_ENTRY
{
   ident ib, db, xb, ct, qb;
   
   init_c();
   GgiAutoLayout(FALSE);
   GgiPostponeRealize(TRUE);
   ib = GgiButton("CREATE=", NULL);
   (void)ScheduleKeyevent(tbcreate, "CREATE=", KEYCHANGE, NULL);
   db = GgiButton("DELETE=", NULL);
   (void)ScheduleKeyevent(tbdelete, "DELETE=", KEYCHANGE, NULL);
   xb = GgiButton("DELETEX=", NULL);
   (void)ScheduleKeyevent(tbdeletex, "DELETEX=", KEYCHANGE, NULL);
   ct = GgiTextField("NC=", NULL, 3);
   (void)ScheduleKeyevent(changecol, "NC=", KEYCHANGE, NULL);
   qb = GgiButton("QUIT=", NULL);
   (void)ScheduleKeyevent(quit, "QUIT=", KEYCHANGE, NULL);
   GgiSetPosition(ib, 0, NULL, 0, NULL);   
   GgiSetPosition(db, 0, ib,   0, NULL);
   GgiSetPosition(xb, 0, db,   0, NULL);
   GgiSetPosition(ct, 0, xb,   0, NULL);
   GgiSetPosition(qb, 0, ct,   0, NULL);
   GgiRealize();

   (void)ScheduleKeyevent(inset, "FILENAME=", KEYCHANGE, NULL);
   eventmonitor("MONITOR=", TRUE);
   MainLoop();
}
#endif
#<

#>            ggiinset.c
/* ggiinset.c -XT
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands


Author: J.P. Terlouw
*/

#define NAME     "Input set"
#define MAXINST  5             /* Maximum number of instances */
#define LABWID  50

#include "stddef.h"
#include "stdlib.h"
#include "gipsyc.h"
#include "stdarg.h"
#include "stdio.h"  
#include "string.h"
#include "userfio.h"
#include "gdsparams.h"
#include "axprops.h"
#include "diskinfo.h"
#include "ggi.h"

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

typedef struct {
   char   *name;             /* axis name                             */
   char   *subkey;           /* subset button keyword                 */
   char   *boxkey;           /* box button keyword                    */
   int    low, high;         /* axis limits                           */
   ident  axlabel;           /* label showing axis name               */
   ident  subbut;            /* button selecting subset mode          */
   ident  boxbut;            /* button seletecting box mode           */
   ident  subtxt;            /* subset/box specification text field   */
   ident  rnlabel;           /* label showing axis range              */
   ident  but_id;            /* button callback                       */
   ident  sub_id;            /* subset text callback                  */ 
   ident  togsub_id;         /* subset button callback for toggling   */
   ident  togbox_id;         /* box button callback for toggling      */
} _Axis, *Axis;

typedef struct {
   int   initialized;        /* initialization flag                   */
   int   seqno;              /* sequence number for identification    */
   int   keyseq;             /* INSET keyword sequence number         */
   char *name;               /* set name keyword                      */
   char *inset;              /* inset keyword                         */
   char *box;                /* box keyword                           */
   char *inset_p;            /* private inset keyword                 */
   char *box_p;              /* private box keyword                   */
   ident shell;              /* pop-up shell element                  */
   ident axbox;              /* "axis bars" container                 */
   ident name_id;            /* name keyword callback                 */
   ident browse_id;          /* BROWSE keyword callback               */
   ident can_id;             /* CANCEL keyword callback               */
   ident ok_id;              /* OK keyword callback                   */
   ident rejins_id;          /* inset keyword reject callback         */
   ident rejbox_id;          /* box keyword reject callback           */
   int   naxes;              /* number of axes                        */
   int   ndims;              /* default dimensionality                */
   int   closeonok;          /* close when OK button is pressed       */
   Axis  axes;               /* pointer to array of _Axis structs     */
} _Instance, *Instance;

static Instance instances[MAXINST];
static Instance current;
static int ninstances=0;

static char *helplabels_nobox[]= {
   "About this element",
   "Set/subset",
   " - List format",
   NULL
};

static char *helpdocs_nobox[]= {
   "ggihelp.doc#ggiinset_nobox",
   "input.doc#set",
   "input.doc#list"
};

static char *helplabels_box[]= {
   "About this element",
   "Set/subset",
   " - List format",
   "Box",
   NULL
};

static char *helpdocs_box[]= {
   "ggihelp.doc#ggiinset_box",
   "input.doc#set",
   "input.doc#list",
   "input.doc#box"
};

/* -------------------------------------------------------------------------- */
/*                                 StrDup                                     */
/* -------------------------------------------------------------------------- */
/*  String duplication function.
 */
static char *StrDup(char *text)
{
   char *result=NULL;

   if (text) {  
      result = NNew(strlen(text)+1,char);
      strcpy(result,text);
   }
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 mkkey                                      */
/* -------------------------------------------------------------------------- */
/*  transform argument string to internal keyword for current instance.
 */
static char *mkkey(char *name)
{
   static char result[KEYLEN];

   sprintf(result, "_GGIIS_%d_%s=", current->keyseq, name);
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 keycpy                                     */
/* -------------------------------------------------------------------------- */
/*  copy source keyword contents to destination keyword.
 */
static void keycpy(char *dst, char *src)
{
   char ctext[201];
   fchar text;
   fint  nread;
    
   text.a = ctext, text.l = 200;
   nread = userftext(text, 2, src, " ");
   ctext[nread] = '\0';
   wkeyf("%s%s", dst, ctext);
}

/* -------------------------------------------------------------------------- */
/*                                 remove_axes                                */
/* -------------------------------------------------------------------------- */
/*  remove all axis-related objects from element.
 */
static void remove_axes(void)
{
   int i;
    
   for (i=0; i<current->naxes; i++) {
      Axis curax = current->axes+current->naxes-i-1;
      Delete(curax->name);
      Delete(curax->subkey);
      Delete(curax->boxkey);
      DescheduleKeyevent(&curax->but_id);
      DescheduleKeyevent(&curax->sub_id);
      DescheduleKeyevent(&curax->togsub_id);
      DescheduleKeyevent(&curax->togbox_id);
      GgiDelete(&curax->rnlabel);
      GgiDelete(&curax->subtxt);
      if (current->box) GgiDelete(&curax->boxbut);
      GgiDelete(&curax->subbut);
      GgiDelete(&curax->axlabel);
   }
   Delete(current->axes);
   current->naxes = 0;
}

/* -------------------------------------------------------------------------- */
/*                                 delete                                     */
/* -------------------------------------------------------------------------- */
/*  delete element.
 */
static void delete(ident instance)
{
   int i;
   ident browser;
   
   for (i=0; i<MAXINST; i++) {
      if (instance && instance==instances[i]) break;
   }
   if (i==MAXINST) return;                 /* must have been deleted already */
   current = (Instance)instance;
   browser = GgiFileBrowser(ggiIdent, current->name);
   if (browser) (void)GgiFileBrowser(ggiDelete, browser);
   remove_axes();
   DescheduleKeyevent(&current->name_id);
   DescheduleKeyevent(&current->browse_id);
   DescheduleKeyevent(&current->can_id);
   DescheduleKeyevent(&current->ok_id);
   if (current->box) DescheduleKeyevent(&current->rejins_id);
   Delete(current->name);
   Delete(current->inset);
   Delete(current->box);
   Delete(current->inset_p);
   Delete(current->box_p);
   GgiDelete(&current->shell);
   instances[current->seqno] = NULL;
   Delete(current);
   ninstances--;
}

/* -------------------------------------------------------------------------- */
/*                                 canproc                                    */
/* -------------------------------------------------------------------------- */
/*  keyword handler causing the element to be deleted.
 */
static void canproc(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      delete(arg);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 okproc                                     */
/* -------------------------------------------------------------------------- */
/*  keyword handler causing the internal subset and box keywords to be
 *  copied to the corresponding destination keywords.
 */
static void okproc(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   current = (Instance)arg;
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      keycpy(current->inset, current->inset_p);
      if (current->box) keycpy(current->box, current->box_p);
      if (current->closeonok) delete(current);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 setnames                                   */
/* -------------------------------------------------------------------------- */
static char *setnames(char *name)
{
   int  namlen=strlen(name);
   static char result[FILENAME_MAX];
   
   if (namlen<7) return NULL;
   if (strcmp(name+namlen-6, ".descr")) return NULL;
   strcpy(result, name);
   result[namlen-6] = '\0';
   return result;   
}


/* -------------------------------------------------------------------------- */
/*                                 setinfo                                    */
/* -------------------------------------------------------------------------- */
static char *setinfo(char *name)
{
   return diskinfo(name, 12, 35, 12 );
}

/* -------------------------------------------------------------------------- */
/*                                 browseproc                                 */
/* -------------------------------------------------------------------------- */
static void browseproc(ident id, char *key, int code, void *arg)
{
   ident browser;
   
   bool button=toflog(FALSE);
   current = (Instance)arg;
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      browser = GgiFileBrowser(ggiIdent, current->name);
      if (browser) {
         (void)GgiFileBrowser(ggiDelete, browser);
      } else {
         browser = GgiFileBrowser(ggiCreate, current->name, "Select input set",
                                  setnames);
         (void)GgiFileBrowser(ggiNowait, browser);
         (void)GgiFileBrowser(ggiFileInfo, browser, setinfo);
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 axproc                                     */
/* -------------------------------------------------------------------------- */
/*  handler for axis-related keywords. Updates the internal subset and box
 *  keywords.
 */
static void axproc(ident id, char *key, int code, void *arg)
{
   char  istxt[200], tmpbf[200], lotxt[200], hitxt[200];
   char  curkey[KEYLEN];
   fchar fistxt, ftmpbf;
   fint  nread;
   int   i;
    
   current = (Instance)arg;
   fistxt.a = istxt; fistxt.l = 199;
   ftmpbf.a = tmpbf; ftmpbf.l = 199;
   nread = userftext(fistxt, 2, current->name, " ");
   istxt[nread] = '\0';
   lotxt[0] = hitxt[0] = '\0';
    
   for (i=0; i<current->naxes; i++) {
      bool sub=toflog(FALSE);

      sprintf(curkey, "S%d", i);
      (void)userflog(&sub, 1, 2, mkkey(curkey), " ");
      if (tobool(sub)) {                            /* subset button selected */
         sprintf(curkey, "T%d", i);
         strcat(istxt, " ");
         strcat(istxt, current->axes[i].name);
         nread = userftext(ftmpbf, 2, mkkey(curkey), " ");
         tmpbf[nread] = '\0';
         strcat(istxt, " ");
         strcat(istxt, tmpbf);
         if (id==current->axes[i].but_id) {
            /* from button */
            if (!current->box) {
               GgiActivate(current->axes[i].subtxt);
            }
            GgiSetLabel(current->axes[i].subtxt, "List:", LABWID);
            GgiHelpText(current->axes[i].subtxt,
              "List of subsets\ne.g. 1 2 3 or 1:3\n(default: all subsets)");
         }
      } else {                                      /* no subset: box         */
         fint limits[2];
         sprintf(curkey, "T%d", i);
         if (current->box) {
            limits[0] = current->axes[i].low;
            limits[1] = current->axes[i].high;
            (void)userfint(limits, 2, 6, mkkey(curkey), " ");
            sprintf(tmpbf, "%d ", limits[0]);
            strcat(lotxt, tmpbf);
            sprintf(tmpbf, "%d ", limits[1]);
            strcat(hitxt, tmpbf);
            if (id==current->axes[i].but_id) {
               /* from button */
               GgiSetLabel(current->axes[i].subtxt, "Range:", LABWID);
               GgiHelpText(current->axes[i].subtxt,
                 "Coordinate range\ne.g. -30 30\n(default: everything)");
            }
         } else if (id==current->axes[i].but_id) {
            SuppressKeyevent(axproc, mkkey(curkey));
            wkeyf(mkkey(curkey));
            GgiDeactivate(current->axes[i].subtxt);
            GgiSetLabel(current->axes[i].subtxt, "N/A ", LABWID);
         }
      }
   }
   if (current->initialized) {
      wkeyf("%s%s", current->inset_p, istxt);
      if (current->box) wkeyf("%s%s %s", current->box_p, lotxt, hitxt);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 toggle                                     */
/* -------------------------------------------------------------------------- */
/*  keyword handler for toggling between subset- and box mode.
 */
static void toggle(ident id, char *key, int code, void *arg)
{
   bool state;
    
   (void)userflog(&state, 1, 2, key, " ");
   SuppressKeyevent(toggle, (char*)arg);
   wkeyf(tobool(state)?"%sNO":"%sYES", (char*)arg);
}
   
/* -------------------------------------------------------------------------- */
/*                                 nameproc                                   */
/* -------------------------------------------------------------------------- */
/*  keyword handler for set name. Updates axis information.
 */
static void nameproc(ident id, char *key, int code, void *arg)
{
   char  setnamec[200];
   fchar setname;
   atype axis[GDS_MAXDIM];
   fint  naxes;
   char  errmes[80];
    
   current = (Instance)arg;
   setname.a = setnamec; setname.l = 200;
   if (userftext(setname, 2, key, " ") > 0) {
      remove_axes();
      naxes = axprops(setname, axis, errmes);
      if (!naxes) {
         rejectf(key, errmes);
      } else if (current->ndims != GDS_MAXDIM && naxes<current->ndims) {
         rejectf(key, "Too few axes:\n %d required, %d present",
                       current->ndims, naxes);
      } else {
         ident prev, ref=NULL;
         int   i;
          
         prev = GgiUseShell(current->axbox);
         current->axes  = NNew(naxes, _Axis);
         current->naxes = naxes;
         for (i=0; i<naxes; i++) {
            Axis curax=current->axes+i;
            curax->low   = axis[i].blo;
            curax->high  = axis[i].bhi;
            curax->name  = StrDup(axis[i].name);
            {
               char  buffer[20];
               
               curax->axlabel = GgiAlignLabel(GgiLabel(" "), ggiLeft);
               GgiSetLabel(curax->axlabel, curax->name, 110);
               GgiSetPosition(curax->axlabel, 0, NULL,           0, ref);

               sprintf(buffer, "S%d", i);
               curax->subbut = GgiButton(mkkey(buffer), NULL);
               curax->subkey = StrDup(mkkey(buffer));
               (void)GgiSetLabel(curax->subbut, "SUB", 0);
               curax->but_id =
                  ScheduleKeyevent(axproc, mkkey(buffer), KEYCHANGE, current);
               GgiSetPosition(curax->subbut,  0, curax->axlabel, 0, ref);

               sprintf(buffer, "B%d", i);
               curax->boxkey = StrDup(mkkey(buffer));
               if (current->box) {
                  curax->boxbut = GgiButton(mkkey(buffer), NULL);
                  (void)GgiSetLabel(curax->boxbut, "BOX", 0);
                  GgiSetPosition(curax->boxbut, 1, curax->subbut,  0, ref);
               }

               curax->togbox_id =
                  ScheduleKeyevent(toggle, curax->boxkey, KEYCHANGE,
                                   curax->subkey);
               curax->togsub_id =
                  ScheduleKeyevent(toggle, curax->subkey, KEYCHANGE,
                                   curax->boxkey);
               if (!TriggerKey(curax->boxkey))
                  wkeyf(current->ndims>i?"%sYES":"%sNO", curax->boxkey);

               sprintf(buffer, "T%d", i);
               curax->subtxt = GgiTextField(mkkey(buffer), NULL, 10);
               (void)GgiSetLabel(curax->subtxt, " ", LABWID);
               curax->sub_id =
                  ScheduleKeyevent(axproc, mkkey(buffer), KEYCHANGE, current);
               GgiSetPosition(curax->subtxt, 5,
                        current->box?curax->boxbut:curax->subbut,  0, ref);

               sprintf(buffer, "[ %d -> %d ]", axis[i].blo, axis[i].bhi);
               curax->rnlabel = GgiLabel(buffer);
               GgiSetPosition(curax->rnlabel, 0, curax->subtxt,  0, ref);
               
               ref = curax->axlabel;
            }
         }
         (void)GgiUseShell(prev);
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 relayerr                                   */
/* -------------------------------------------------------------------------- */
/*  propagate reject from destination keyword to internal keyword.
 */
static void relayerr(ident id, char *key, int code, void *arg)
{
   fchar message;
   char cmessage[80];
   fint nread;
    
   message.a = cmessage; message.l = 80;
   nread = userftext(message, -1, "UCAMESSAGE=", " ");
   cmessage[nread] = '\0';
   rejectf((char*)arg, "%s", cmessage);
}



/* -------------------------------------------------------------------------- */
/*                                 create                                     */
/* -------------------------------------------------------------------------- */
/*  create set input pop-up element.
 */
static ident create(char *namekey, char *setkey, char *boxkey, char *message)
{
   ident prev, label, helpme, frame;
   ident header, browse, canbut, okbut;
   ident name, inset, box;
   int   i;
   char  helpkey[KEYLEN];

   if (ninstances>=MAXINST) return NULL;
   
   ninstances++;
   current = New(_Instance);
   for (i=0; i<MAXINST; i++) if (!instances[i]) break;
   instances[i] = current;
   current->seqno   = i;
   current->keyseq  = GgiKeyIdent(namekey);
   current->name    = StrDup(namekey);
   current->inset   = StrDup(setkey);
   current->box     = StrDup(boxkey);
   current->inset_p = StrDup(mkkey("I"));
   current->box_p   = StrDup(mkkey("B"));
   current->ndims   = GDS_MAXDIM;
   
   current->shell = GgiShell(NAME);

   prev   = GgiUseShell(current->shell);
   label  = GgiLabel(message);
   sprintf(helpkey, "_GGIIS_%d_HELP=", current->keyseq);
   if (boxkey) {
      helpme = GgiHelpMenu(helpkey, "HELP", NULL,
                           helplabels_box, helpdocs_box);
   } else {
      helpme = GgiHelpMenu(helpkey, "HELP", NULL,
                           helplabels_nobox, helpdocs_nobox);
   }
   frame  = GgiForm(NAME, 2);
   canbut = GgiSetLabel(GgiButton(mkkey("CANCEL"), NULL), "CLOSE", 0);
   current->can_id =
      ScheduleKeyevent(canproc, mkkey("CANCEL"), KEYCHANGE, current);
   okbut  = GgiSetLabel(GgiButton(mkkey("OK"), NULL), "OK", 0);
   current->ok_id =
      ScheduleKeyevent(okproc, mkkey("OK"), KEYCHANGE, current);
   GgiSetPosition(label,   0,               NULL,  0, NULL);
   GgiSetPosition(helpme,  -GgiWidth(helpme), frame,  0, NULL);
   GgiSetPosition(frame,   0,               NULL,  7, label);
   GgiSetPosition(canbut,  0,               NULL,  0, frame);
   GgiSetPosition(okbut,  -GgiWidth(okbut), frame, 0, frame);
    
   (void)GgiUseShell(frame);
   name  = GgiSetLabel(GgiTextField(namekey, "Set name", 40), "Set name", 70);
   current->name_id =
      ScheduleKeyevent(nameproc, namekey, KEYCHANGE, current);
   header = GgiHeaderButton(mkkey("HDR"), "HEADER",
                            "Show set header", namekey);
   browse = GgiSetLabel(GgiButton(mkkey("BROWSE"), NULL), "BROWSE", 0);
   current->browse_id =
      ScheduleKeyevent(browseproc, mkkey("BROWSE"), KEYCHANGE, current);
   current->axbox  = GgiViewport(NAME, 500, 100);
   inset = GgiSetLabel(GgiTextField(mkkey("I"),
              "Generic set/subset specification", 60), "Set/subset", 70);
   keycpy(current->inset_p, current->inset);
   current->rejins_id =
      ScheduleKeyevent(relayerr, setkey, KEYREJECT, current->inset_p);
   if (current->box) {
      box   = GgiSetLabel(GgiTextField(mkkey("B"),
                 "Generic box specification", 60), "BOX", 70);
      keycpy(current->box_p, current->box);
      current->rejbox_id =
         ScheduleKeyevent(relayerr, boxkey, KEYREJECT, current->box_p);
   }
   GgiSetPosition(name,            0, NULL, 0, NULL);
   GgiSetPosition(browse, -GgiWidth(browse), current->axbox, 0, NULL);
   GgiSetPosition(header, 0, name, 0, NULL);
   GgiSetPosition(current->axbox,  0, NULL, 0, name);
   GgiSetPosition(inset,           0, NULL, 0, current->axbox);
   if (current->box) GgiSetPosition(box,             0, NULL, 0, inset);
   (void)GgiUseShell(prev);
   
   (void)TriggerKey(namekey);      /* initialize axes if name already present */
   GgiShowShell(current->shell, TRUE);
   GgiHandleEvents();
   current->initialized++;
   return (ident)current;
}

/* -------------------------------------------------------------------------- */
/*                                 findid                                     */
/* -------------------------------------------------------------------------- */
/*  find ident, given the set name keyword.
 */
static ident findid(char *name)
{
   int i;
   ident result=NULL;
    
   for (i=0; i<MAXINST; i++) {
      Instance current=instances[i];
      if (current && !strcmp(name, current->name)) {
         result = (ident)current;
         break;
      }
   }
   return result;
}

/* ========================================================================== */
/*                                 GgiInset                                   */
/* -------------------------------------------------------------------------- */
/*
 */
ident GgiInset(int opcode, ...)
{
   va_list args;
   ident result=NULL;  
    
   va_start( args, opcode);
   switch (opcode) {
      case ggiCreate: {
         char *namekey, *insetkey, *boxkey, *message;
         namekey  = va_arg(args, char*);
         insetkey = va_arg(args, char*);
         boxkey   = va_arg(args, char*);
         message  = va_arg(args, char*);
         result = create(namekey, insetkey, boxkey, message?message:" ");
         break;
      }
      case ggiDelete: {
         ident current;
         current = va_arg(args, ident);
         delete(current);
         break;
      }
      case ggiIdent: {
         char *name;
         name = va_arg(args, char*);
         result = findid(name);
         break;
      }
      case ggiNdims: {
         Instance current;
         int      ndims;
         current = va_arg(args, Instance);
         ndims   = va_arg(args, int);
         current->ndims = ndims;
         result = (ident)current;
         break;
      }
      case ggiCloseOnOK: {
         Instance current;
         current = va_arg(args, Instance);
         current->closeonok = 1;
         result = (ident)current;
         break;
      }
      default: {
      }
   }
   va_end(args);
   return result;
}

#if defined(TESTBED)  /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
#include "init.h"
#include "finis.h"
#include "cmain.h"


static ident tbinsbox;

static void quit(ident id, char *key, int code, void *arg)
{
   bool finished=toflog(FALSE);
    
   (void)userflog(&finished, 1, 2, key, " ");
   if (tobool(finished)) {
      wkeyf(key);
#if 0
      /* ggikeyident.c must be compiled with -DDEBUG */
      dumpkeyidents();
#endif
      finis_c();
   }
}

static void tbcreate(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
#if 0
      tbinsbox = GgiInset(ggiCreate, "SETNAME=", "INSET=", "BOX=",
                                     "Set to be specified for this "
                                     "test of GgiInset");
#else
      tbinsbox = GgiInset(ggiCreate, "SETNAME=", "INSET=", NULL,
                                     "Set to be specified for this\n"
                                     "test of GgiInset");
#endif
      (void)GgiInset(ggiNdims, tbinsbox, 2);
      (void)GgiInset(ggiCloseOnOK, tbinsbox);
   }
}

static void tbdelete(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      (void)GgiInset(ggiDelete, tbinsbox);
   }
}

static void tbdeletex(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      (void)GgiInset(ggiDelete, GgiInset(ggiIdent, "SETNAME="));
   }
}

static void inset(ident id, char *key, int code, void *arg)
{
   static int count=0;
    
   if (!(count%5)) {
      rejectf(key, "Illegal inset or box");
   }
   count++;
}

MAIN_PROGRAM_ENTRY
{
   ident ib, db, xb, qb;

   init_c();
   GgiAutoLayout(FALSE);
   GgiPostponeRealize(TRUE);
   ib = GgiButton("CREATE=", NULL);
   (void)ScheduleKeyevent(tbcreate, "CREATE=", KEYCHANGE, NULL);
   db = GgiButton("DELETE=", NULL);
   (void)ScheduleKeyevent(tbdelete, "DELETE=", KEYCHANGE, NULL);
   xb = GgiButton("DELETEX=", NULL);
   (void)ScheduleKeyevent(tbdeletex, "DELETEX=", KEYCHANGE, NULL);
   qb = GgiButton("QUIT=", NULL);
   (void)ScheduleKeyevent(quit, "QUIT=", KEYCHANGE, NULL);
   GgiSetPosition(ib, 0, NULL, 0, NULL);
   GgiSetPosition(db, 0, ib,   0, NULL);
   GgiSetPosition(xb, 0, db,   0, NULL);
   GgiSetPosition(qb, 0, xb,   0, NULL);
   GgiRealize();

   (void)ScheduleKeyevent(inset, "INSET=", KEYCHANGE, NULL);
   (void)ScheduleKeyevent(inset, "BOX=", KEYCHANGE, NULL);
   eventmonitor("MONITOR=", TRUE);
   MainLoop();
}
#endif
#<

#>            ggihelpmenu.c
/* ggihelpmenu.c -XT
                              COPYRIGHT (c) 2000
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#include "stddef.h"
#include "string.h"
#include <sys/file.h>
#ifndef	O_CREAT
#include <fcntl.h>
#endif
#ifdef	__sysv__
#include <unistd.h>
#endif
#ifndef	L_XTND
#define	L_XTND	SEEK_END
#endif
#include "stdlib.h"
#include "stdio.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"

#define W 660
#define H 330
#define MAXSIZE 100000

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

typedef struct {
   bool   open;                /* open state */
   char   **documents;         /* list of documents */
   ident  shell;               /* GgiShell container element */
   ident  editor;              /* Ggi text editor */
   int    choice;              /* current menu selection */
   char   *contents;           /* help text */
   ident  sforw_cb;            /* forward-search callback */
   ident  sback_cb;            /* backward-search callback */
   ident  close_cb;            /* close callback */
   ident  menu_cb;             /* menu callback */
} _HelpMenu, *HelpMenu;

static int instance=0;
static char *paths[]={".",NULL,NULL,NULL,NULL};
static int  npaths;

#define left_width 15
#define left_height 15
static unsigned char left_bits[] = {
 0x3f,0x7e,0x21,0x42,0x21,0x42,0x21,0x42,0x21,0x42,0xe1,0x43,0x61,0x43,0x61,
 0x43,0x61,0x43,0x61,0x43,0x7f,0x7f,0xfe,0x3f,0x2e,0x3a,0x28,0x0a,0x38,0x0e
 };
#define right_width 15
#define right_height 15
static unsigned char right_bits[] = {
 0x38,0x0e,0x28,0x0a,0x2e,0x3a,0xfe,0x3f,0x7f,0x7f,0x61,0x43,0x61,0x43,0x61,
 0x43,0x61,0x43,0xe1,0x43,0x21,0x42,0x21,0x42,0x21,0x42,0x21,0x42,0x3f,0x7e
 };

/* -------------------------------------------------------------------------- */
/*                                 StrDup                                     */
/* -------------------------------------------------------------------------- */
/*  String duplication function. 
 */
static char *StrDup(char *text)
{
   char *result=NULL;

   if (text) {
      result = NNew(strlen(text)+1,char);
      strcpy(result,text);
   }
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 filecontents                               */
/* -------------------------------------------------------------------------- */
static char *filecontents(char *path, int *fsize)
{
   int fd = open(path,O_RDONLY,NULL);
   char *result;
   char *contents;
   int  size, nread=0;
   char message[256];

   if (fd<0) {
      *fsize = -1;
      return NULL;               /* error: could not open */
   }
   size = lseek(fd,0,L_XTND);    /* determine file length */
   if (size>MAXSIZE) {
      sprintf(message, "%s:\n"
                       "    document size of %d bytes exceeds\n"
                       "    maximum size of %d bytes.", path, size, MAXSIZE);
      result = StrDup(message);
      *fsize = strlen(message);
   } else if (size<=0) {
      sprintf(message, "%s:\n"
                       "    zero size or no access to document", path);
      result = StrDup(message);
      *fsize = strlen(message);
   } else {
      (void)lseek(fd,0,L_SET);           /* go back to beginning of file */
      contents = NNew(size+1, char);     /* allocate space + trailer byte */
      while (nread<size) nread += read(fd,contents+nread,size-nread);
      contents[nread] = 0;
      result = contents;
      *fsize = size;
   }
   close(fd);
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 cleanup                                    */
/* -------------------------------------------------------------------------- */
static void cleanup(void *arg)
{
   HelpMenu current=(HelpMenu)arg;
    
   (void)DescheduleKeyevent(&current->sforw_cb);
   (void)DescheduleKeyevent(&current->sback_cb);
   (void)DescheduleKeyevent(&current->close_cb);
   (void)DescheduleKeyevent(&current->menu_cb);
   GgiDelete(&current->shell);
   Delete(current->contents);
   Delete(current);
}

/* -------------------------------------------------------------------------- */
/*                                 handle_menu                                */
/* -------------------------------------------------------------------------- */
static void handle_menu(ident id, char *key, int code, void *arg)
{
   HelpMenu current=(HelpMenu)arg;
   fint choice=-1;
   int  i, size;
   char filename[256];
   char secstart[80];
   char secend[80];
   char document[256];
   bool secmode=FALSE;
    
   (void)userfint(&choice, 1, 2, key, " ");
   if (choice>=0) {
      if (!current->open) {
         GgiShowShell(current->shell, TRUE);
         current->open = TRUE;
      }
      if (choice != current->choice) {
         current->choice = choice;
         Delete(current->contents);
         strcpy(document, current->documents[choice]);
         for (i=0; document[i]; i++) {
            if (document[i]=='#') {
               document[i] = '\0';
               secmode = TRUE;
               sprintf(secstart, "\n#begin section %s", document+i+1);
               sprintf(secend, "\n#end section %s", document+i+1);
               break;
            }
         } 
         for (i=0; i<npaths && !current->contents; i++) {
            sprintf(filename, "%s/%s", paths[i], document);
            current->contents = filecontents(filename, &size);
         }
         if (current->contents) {
            char *showntext=current->contents;
            if (secmode) {
               showntext = strstr(showntext, secstart);
               if (showntext) {
                  showntext = showntext+strlen(secstart)+1;
                  size = strstr(showntext, secend)-showntext;
                  if (size>0) showntext[size] = '\0';
               }
            }
            if (showntext && size>0) {
               (void)GgiEditorSetSource(current->editor, showntext, size);
            } else {
               (void)GgiEditorSetSource(current->editor, "Section error", 13);
            }
         } else {
            (void)GgiEditorSetSource(current->editor, "Document error", 14);
         }
      }
#if 0
      wkeyf(key);
#endif
   }
}

/* -------------------------------------------------------------------------- */
/*                                 search_back                                */
/* -------------------------------------------------------------------------- */
static void search_back(ident id, char *key, int code, void *arg)
{
   HelpMenu current=(HelpMenu)arg;
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (button) {
      wkeyf(key);
      GgiEditorSearch(current->editor, -1);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 search_forw                                */
/* -------------------------------------------------------------------------- */
static void search_forw(ident id, char *key, int code, void *arg)
{
   HelpMenu current=(HelpMenu)arg;
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (button) {
      wkeyf(key);
      GgiEditorSearch(current->editor, 1);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 close_window                               */
/* -------------------------------------------------------------------------- */
static void close_window(ident id, char *key, int code, void *arg)
{
   HelpMenu current=(HelpMenu)arg;
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (button) {
      wkeyf(key);
      if (current->open) {
         GgiShowShell(current->shell, FALSE);
         current->open = FALSE;
      }
   }
}

/* ========================================================================== */
/*                                 GgiHelpMenu                                */
/* -------------------------------------------------------------------------- */
ident GgiHelpMenu(char *key, char *buttontext, char *message,
                  char** labels, char **documents)
{
   ident menu, prev, ownmenu, closebut, search, back, forw;
   HelpMenu current=New(_HelpMenu);
   char keybuf[KEYLEN];

   if (!paths[1]) {
      /* initialization */
      paths[1] = StrDup(getenv("gip_tsk"));
      paths[2] = StrDup(getenv("gip_doc"));
      paths[3] = StrDup(getenv("gip_sub"));
      npaths = 4;
   }
   current->choice = -1;
   menu = GgiSetLabel(GgiMenu(key, message, labels), buttontext, 0);
   current->documents = documents;
   prev = GgiUseShell(NULL);
   current->shell = GgiShell(message?message:"Help");
   (void)GgiUseShell(current->shell);
   ownmenu = GgiSetLabel(GgiMenu(key, NULL, labels), buttontext, 0);
   search = GgiLabel(" ");
   sprintf(keybuf, "_GGIHM_%d_BACK=", GgiKeyIdent(key));
   back  = GgiSetBitmap(GgiButton(keybuf, "Search backward"),
                        left_width, left_height, left_bits);
   current->sback_cb = ScheduleKeyevent(search_back,keybuf,KEYCHANGE, current);
   sprintf(keybuf, "_GGIHM_%d_FORW=", GgiKeyIdent(key));
   forw  = GgiSetBitmap(GgiButton(keybuf, "Search forward"),
                        right_width, right_height, right_bits);
   current->sforw_cb = ScheduleKeyevent(search_forw,keybuf,KEYCHANGE, current);
   current->editor = GgiEditor(NULL, W, H, ggiReadOnly, " ", 1);
   sprintf(keybuf, "_GGIHM_%d_CLOSE=", GgiKeyIdent(key));
   closebut = GgiSetLabel(GgiButton(keybuf, NULL), "CLOSE", 0);
   current->close_cb = ScheduleKeyevent(close_window,keybuf,KEYCHANGE, current);
   current->menu_cb = ScheduleKeyevent(handle_menu, key, KEYCHANGE, current);
   (void)GgiSetCleanup(menu, cleanup, current);

   GgiSetPosition(search,          0, NULL,                   0, NULL);
   GgiSetPosition(back,            0, search,                 0, NULL);
   GgiSetPosition(forw,            0, back,                   0, NULL);
   GgiSetPosition(ownmenu,         W-GgiWidth(ownmenu), NULL, 0, NULL);
   GgiSetPosition(current->editor, 0, NULL,                   0, back);
   GgiSetPosition(closebut,        0, NULL,          0, current->editor);
   
   (void)GgiUseShell(prev);
   instance++;
   return menu;  
}

#if defined(TESTBED)  /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
#include "init.h"
#include "finis.h"
#include "cmain.h"

static char *labels[]={"GUI", "Hermes", "_LINE", "ROTMAS 1", "ROTMAS 2",
                       "ROTMAS 3", "ROTMAS 4", NULL};
static char *documents[]={"ggi.doc", "hermes.dc1", NULL,
                         "rotmas.dc1#1", "rotmas.dc1#2", "rotmas.dc1#3",
                         "rotmas.dc1#4", NULL};

MAIN_PROGRAM_ENTRY
{
   ident helper1, helper2;
    
   init_c();
   GgiAutoLayout(FALSE);
   GgiPostponeRealize(TRUE);
    
   helper1 = GgiHelpMenu("HELP1=", "Help1", "Help one", labels, documents);
   helper2 = GgiHelpMenu("HELP2=", "Help2", "Hulp twee", labels, documents);
   GgiSetPosition(helper1, 0, NULL,    0, NULL);
   GgiSetPosition(helper2, 0, helper1, 0, NULL);
   GgiRealize(); 
   MainLoop();
}
#endif
#<

#>            ggiheaderbutton.c
/* ggiheaderbutton.c -XT
                              COPYRIGHT (c) 2000
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#include "stddef.h"
#include "string.h"
#include "stdlib.h"
#include "stdio.h"
#include "gipsyc.h"
#include "subst.h"
#include "deputy.h"
#include "userfio.h"
#include "ggi.h"

#define WIDTH  660
#define HEIGHT 440

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

typedef struct {
   bool   open;               /* open state */
   char   *setkey;            /* set keyword */
   ident  shell;              /* GgiShell container element */
   ident  editor;             /* Ggi text editor */
   ident  pop_cb;             /* open/close buttons callback */
   ident  set_cb;             /* set name callback */
   ident  mode_cb;            /* output mode callback */
} _HeaderButton, *HeaderButton;

static char listfile[80];

static char *helplabels[]= {
   "About this element",
   "Output mode",
   NULL
};

static char *helpdocs[]= {
   "ggihelp.doc#ggiheaderbutton",
   "header.dc1#mode"
};

/* -------------------------------------------------------------------------- */
/*                                 StrDup                                     */
/* -------------------------------------------------------------------------- */
/*  String duplication function. 
 */
static char *StrDup(char *text)
{
   char *result=NULL;

   if (text) {
      result = NNew(strlen(text)+1,char);
      strcpy(result,text);
   }
   return result;
}

/* -------------------------------------------------------------------------- */
/*                                 cleanup                                    */
/* -------------------------------------------------------------------------- */
/*  Post-delete cleanup function.
 */
static void cleanup(void *arg)
{
   HeaderButton current=(HeaderButton)arg;
    
   DescheduleKeyevent(&current->pop_cb);
   DescheduleKeyevent(&current->mode_cb);
   if  (current->set_cb) DescheduleKeyevent(&current->set_cb);
   GgiDelete(&current->shell);
   Delete(current->setkey);
   Delete(current);
}

/* -------------------------------------------------------------------------- */
/*                                 get_head                                   */
/* -------------------------------------------------------------------------- */
/*  Setname keyword handler.
 */
static void get_head(ident id, char *key, int code, void *arg)
{
   HeaderButton current=(HeaderButton)arg;
   fint err;
   char buffer[80];

   sprintf(buffer, "MODE=_GGIHB_MODE=FILENAME=_GGIHB_FILENAME=INSET=%s", key);
   subst_c(tofchar(buffer), &err);
   deputy_c(tofchar("header"), &err);
   if (err==1) (void)GgiEditorSetSource(current->editor, listfile, -1);
   remove(listfile);
}

/* -------------------------------------------------------------------------- */
/*                                 change_mode                                */
/* -------------------------------------------------------------------------- */
/*  Mode keyword handler.
 */
static void change_mode(ident id, char *key, int code, void *arg)
{
   HeaderButton current=(HeaderButton)arg;
    
   if (current->open) get_head(NULL, current->setkey, KEYCHANGE, current);
}

/* -------------------------------------------------------------------------- */
/*                                 pop_shell                                  */
/* -------------------------------------------------------------------------- */
/*  Open/close keyword handler.
 */
static void pop_shell(ident id, char *key, int code, void *arg)
{
   HeaderButton current=(HeaderButton)arg;
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (button) {
      wkeyf(key);
      current->open = !current->open;
      GgiShowShell(current->shell, current->open);
      if (current->open) {
         current->set_cb = ScheduleKeyevent(get_head, current->setkey,
                                                      KEYCHANGE, current);
         (void)TriggerKey(current->setkey);
      } else {
         DescheduleKeyevent(&current->set_cb);
      }
   }
}

/* ========================================================================== */
/*                                 GgiHeaderButton                            */
/* -------------------------------------------------------------------------- */
ident GgiHeaderButton(char *key, char *buttontext, char *message, char *setkey)
{
   static bool initialized=FALSE;
   ident button, prev, modetext, helpmenu, closebut;
   char  helpkey[KEYLEN];
   HeaderButton current=New(_HeaderButton);

   if (!initialized) {
      sprintf(listfile, "/tmp/ggi_head_list_%d", getpid());
      if (!TriggerKey("_GGIHB_MODE=")) wkeyf("_GGIHB_MODE=AFH");
      wkeyf("_GGIHB_FILENAME=%s", listfile);
      initialized = TRUE;
   }
   current->setkey = StrDup(setkey);
   button = GgiSetLabel(GgiButton(key, message), buttontext, 0);
   prev = GgiUseShell(NULL);
   current->shell = GgiShell("Set Header");
   (void)GgiUseShell(current->shell);
   modetext = GgiSetLabel(GgiTextField("_GGIHB_MODE=",
                          "Output mode - see HELP", 5),  "MODE:", 0);
   current->mode_cb = ScheduleKeyevent(change_mode, "_GGIHB_MODE=",
                                                           KEYCHANGE, current);
   sprintf(helpkey, "_GGIHB_%d_HELP=", GgiKeyIdent(setkey)); 
   helpmenu = GgiHelpMenu(helpkey, "HELP", NULL, helplabels, helpdocs);
   current->editor = GgiEditor(NULL,WIDTH,HEIGHT, ggiReadOnly, "/dev/null", -1);
   closebut = GgiSetLabel(GgiButton(key, NULL), "CLOSE", 0);
   current->pop_cb = ScheduleKeyevent(pop_shell, key, KEYCHANGE, current);
   (void)GgiSetCleanup(button, cleanup, current);

   GgiSetPosition(modetext,        0, NULL, 0, NULL);
   GgiSetPosition(helpmenu, WIDTH-GgiWidth(helpmenu),  NULL, 0, NULL);
   GgiSetPosition(current->editor, 0, NULL, 0, helpmenu);
   GgiSetPosition(closebut,        0, NULL, 0, current->editor);
   
   (void)GgiUseShell(prev);
   return button;  
}

#if defined(TESTBED)  /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
#include "init.h"
#include "finis.h"
#include "cmain.h"


MAIN_PROGRAM_ENTRY
{
   ident settext, buthead;
    
   init_c();
   GgiAutoLayout(FALSE);
   GgiPostponeRealize(TRUE);
    
   settext = GgiTextField("SETNAAM=", NULL, 40);
   buthead = GgiHeaderButton("KOP=", "HEADER", "Header Task", "SETNAAM=");
   GgiSetPosition(settext, 0, NULL,    0, NULL);
   GgiSetPosition(buthead, 0, settext, 0, NULL);
   GgiRealize(); 
   MainLoop();
}
#endif
#<

#>            ggikeyident.c
/* ggikeyident.c
                              COPYRIGHT (c) 2000
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define TABSIZ 311                               /* Hash table size */
#define MPY   1021                               /* Multiplier      */

#include "stddef.h"
#include "stdlib.h"
#include "gipsyc.h"
#include "string.h"

static char *table[TABSIZ], **ovftab=NULL;
static int ovfsiz=0;

int GgiKeyIdent(char *key)
{
   int hash=(int)*key, index;
   char *c=key;

   while (*c) {
      if (*c == '=') break;
      hash = ((MPY*hash)^(int)*(c++))%TABSIZ;
   }

   index = hash;
   for (;;) {
      if (!table[index]) {
         table[index] = (char *)malloc(strlen(key)+1);
         strcpy(table[index], key);
         return index;
      }
      if (!strcmp(table[index], key)) {
         return index;
      }
      index = (index+1)%TABSIZ;
      if (index==hash) break;
   }
   for (index=0; index<ovfsiz; index++) {
      if (!strcmp(ovftab[index], key)) {
         return index+TABSIZ;
      }
   }
   ovftab = realloc(ovftab, (index+1)*sizeof(char*));
   ovfsiz++;
   ovftab[index] = (char *)malloc(strlen(key)+1);
   strcpy(ovftab[index], key);
   return index+TABSIZ;
}

#if defined(DEBUG)
#include "userfio.h"
void dumpkeyidents(void)
{
   int i;
    
   anyoutf(0, " === Dump KeyIdent table ===");
   anyoutf(0, "Hash table size: %d", TABSIZ);
   anyoutf(0, "--------------------");
   for (i=0; i<TABSIZ; i++) {
      if (table[i]) anyoutf(0, "%3d %s", i, table[i]);
   }
   if (ovfsiz) {
      anyoutf(0, "Overflow table size %d", ovfsiz);
      anyoutf(0, "-----------------------");
      for (i=0; i<ovfsiz; i++) {
         if (ovftab[i]) anyoutf(0, "%3d %s", i+TABSIZ, ovftab[i]);
      }
   } else {
      anyoutf(0, "No overflow table");
   }
   anyoutf(0, " === end of KeyIdent table ===");
}
#endif

#if defined(TESTBED)
#include "stdio.h"
void main(void)
{
   char key[20];
   for (;;) {
      scanf("%s", key);
      printf("%3d %s\n", GgiKeyIdent(key), key);
   }
}
#endif
#<

#>            ggicolormenu.c
/* ggicolormenu.c
                              COPYRIGHT (c) 2000
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define  BTNWID 100  /* menu button width */
#define  MANY    50  /* some reasonably large number */

#include "stddef.h"
#include "stdlib.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"

#define New(type) ((type *)calloc(1,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

static char *colnums[]={
   "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
   "10", "11", "12", "13", "14", "15", NULL
};

static char *colnames[]={
   " 0: Background",
   " 1: Foreground",
   " 2: Red",
   " 3: Green",
   " 4: Blue",
   " 5: Cyan",
   " 6: Magenta",
   " 7: Yellow",
   " 8: Orange",
   " 9: Greenyellow",
   "10: Greencyan",
   "11: Bluecyan",
   "12: Bluemagenta",
   "13: Redmagenta",
   "14: Darkgray",
   "15: Lightgray",
   NULL
};

/* -------------------------------------------------------------------------- */
/*                                 cleanup                                    */
/* -------------------------------------------------------------------------- */
/*  Post-delete cleanup function.
 */
static void cleanup(void *arg)
{
   ident *textcb=(ident*)arg;

   DescheduleKeyevent(textcb);
   Delete(textcb);
}

/* -------------------------------------------------------------------------- */
/*                                 col_text                                   */
/* -------------------------------------------------------------------------- */
/* Keyword handler for setting labels in the color menus.
 */
static void col_text(ident id, char *key, int code, void *arg)
{
   fint index;
   fint multi[MANY]; /* some reasonably large number */
   
   if (userfint(multi, MANY, 2, key, " ")>1)
      GgiSetLabel((ident)arg, "- multiple -", BTNWID);
   else if (userfint(&index, 1, 2, key, " ")==1)
      GgiSetLabel((ident)arg, colnames[index]+4, BTNWID);
      /* color number (width 4) skipped */
   else
      GgiSetLabel((ident)arg, "- none -", BTNWID);
}

/* ========================================================================== */
/*                                 GgiColorMenu                               */
/* -------------------------------------------------------------------------- */
ident GgiColorMenu(char *keyword, char *message)
{
   ident menu;
   ident *textcb=New(ident);
   menu = GgiTextMenu(keyword, message, colnums);
   GgiMenuLabels(menu, colnames);
   *textcb = ScheduleKeyevent(col_text, keyword, KEYCHANGE, menu);
   (void)GgiSetCleanup(menu, cleanup, textcb);
   col_text(NULL, keyword, KEYCHANGE, menu);
   return menu;
}
#<

#>            ggitextmenu.c
/* ggitextmenu.c -XT
                              COPYRIGHT (c) 2000
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#include "stddef.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"

#define New(type) ((type *)calloc(1,sizeof(type)))
#define NNew(n,type) ((type *)calloc(n,sizeof(type)))
#define Delete(x)  {free(x); x=NULL;}

typedef struct {
   char   *keyword;        /* destination keyword */
   char   **labels;        /* original set of label texts */
   ident  trans;           /* translator callback */
} _TextMenu, *TextMenu;

/* -------------------------------------------------------------------------- */
/*                                 StrDup                                     */
/* -------------------------------------------------------------------------- */
/*  String duplication function.
 */
static char *StrDup(char *text)
{
   char *result=NULL;
   
   if (text) {
      result = NNew(strlen(text)+1,char);
      strcpy(result,text);
   }
   return result;
} 

/* -------------------------------------------------------------------------- */
/*                                 cleanup                                    */
/* -------------------------------------------------------------------------- */
/*  Post-delete cleanup function.
 */
static void cleanup(void *arg)
{
   TextMenu current=(TextMenu)arg;
    
   DescheduleKeyevent(&current->trans);
   Delete(current->keyword)
   Delete(current);
}

/* -------------------------------------------------------------------------- */
/*                                 translate                                  */
/* -------------------------------------------------------------------------- */
/* Keyword handler for setting labels in the color menus.
 */
static void translate(ident id, char *key, int code, void *arg)
{
   fint index;
   TextMenu current=(TextMenu)arg;

   if (userfint(&index, 1, 2, key, " ")==1) {
      wkeyf("%s%s", current->keyword, current->labels[index]);
   }
}

/* ========================================================================== */
/*                                 GgiTextMenu                                */
/* -------------------------------------------------------------------------- */
ident GgiTextMenu(char *keyword, char *message, char **labels)
{
   ident menu;
   char  menukey[KEYLEN], butlab[KEYLEN];
   TextMenu current=New(_TextMenu);
    
   current->keyword  = StrDup(keyword);
   current->labels   = labels;
   strcpy(butlab, keyword);
   butlab[strlen(butlab)-1] = '\0';
   sprintf(menukey, "_GGITM_%d_SEL=", GgiKeyIdent(keyword));
   menu = GgiSetLabel(GgiMenu(menukey, message, labels), butlab, 0);
   current->trans = ScheduleKeyevent(translate, menukey, KEYCHANGE, current);
   (void)GgiSetCleanup(menu, cleanup, current);
   return menu;
}

#if defined(TESTBED)  /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
#include "init.h"
#include "finis.h"
#include "cmain.h"

static char *labels[]={
   "1",
   "2",
   "4",
   "8",
   NULL
};

static char *newlabels[]={
   "One",
   "Two",
   "Four",
   "Eight",
   NULL
};

MAIN_PROGRAM_ENTRY
{
   init_c();
   (void)GgiMenuLabels(GgiTextMenu("GETAL=", "Kies een getal", labels),newlabels);
   eventmonitor("MONITOR=", TRUE);
   MainLoop();
}   
#endif
#<

#>            ggioptionsshell.c
/*
                              COPYRIGHT (c) 2001
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/



#include "stddef.h"
#include "string.h"
#include "gipsyc.h"
#include "stdio.h"
#include "myname.h"
#include "nelc.h"
#include "userfio.h"
#include "eventmonitor.h"
#include "ggi.h"

static char optionskey[KEYLEN];
static ident b_dolog, l_message;
static char  cname[FILENAME_MAX+1]="";
static fint  playstat;

/* -------------------------------------------------------------------------- */
/*                                 testfile                                   */
/* -------------------------------------------------------------------------- */
static int testfile(char* name, char *access)
{
   FILE *f=fopen(name,access);
   if (f) {
      fclose(f);
      return 1;
   } else return 0;
}
#define readable(fn) testfile(fn, "r")
#define writable(fn) testfile(fn, "w")

/* -------------------------------------------------------------------------- */
/*                                 popshell                                   */
/* -------------------------------------------------------------------------- */
static void popshell(ident id, char *key, int code, void *arg)
{
   static bool state=FALSE;
   bool button=toflog(FALSE);
   
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);                                /* reset button              */
      GgiShowShell((ident)arg, !state);          /* pop up/down               */
      state = !state;                            /* update state              */
   }
}

/* -------------------------------------------------------------------------- */
/*                                 getname                                    */
/* -------------------------------------------------------------------------- */
static void getname(ident id, char *key, int code, void *arg)
{
   fchar name;
   fint  nread;
   
   name.a = cname; name.l = FILENAME_MAX;
   nread = userftext(name, 2, key, " ");
   cname[nread] = '\0';
   if (nread) {
      if (readable(cname)) {
         if (!GgiVerify("File exists. Overwrite?", "YES", "NO")) {
            wkeyf("GGILOG=NO");
            return;
         }
      }
      if (!writable(cname)) {
         rejectf(key, "Cannot write to file");
         wkeyf("GGILOG=NO");
         return;
      }
      wkeyf("GGILOGFILE=%s GGILOG=YES", cname);
      GgiActivate(b_dolog);
   } else {
      wkeyf("GGILOGFILE= GGILOG=NO");
      GgiDeactivate(b_dolog);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 showmessage                                */
/* -------------------------------------------------------------------------- */
static void showmessage(void)
{
   char *message;
   
   switch (playstat) {
      case  0: message = "Event Player:  running"; break;
      case  1: message = "Event Player:  finished"; break;
      case -1: message = "Event Player:  insufficient resources"; break;
      case -2: message = "Event Player:  cannot run"; break;
      case -3: message = "Event Player:  fatal error"; break;
      case -4: message = "Event Player:  crashed"; break;
      case -5: message = "Event Player:  user abort"; break;
      default: message = "Event Player:  unknown error";
   }
   GgiSetLabel(l_message, message, 0);
}

/* -------------------------------------------------------------------------- */
/*                                 player_exit                                */
/* -------------------------------------------------------------------------- */
static void player_exit(ident id, char *key, int code, void *arg)
{
   (void)userfint(&playstat, 1, 2, key, " ");
   showmessage();
}
   

/* -------------------------------------------------------------------------- */
/*                                 player                                     */
/* -------------------------------------------------------------------------- */
static void player(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   fchar taskname;
   char ctaskname[TSKLEN+1];
   char playname[TSKLEN+1];
   char *prefix="PLAY_";
   int  tsklen, prelen;

   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      taskname.a = ctaskname; taskname.l = TSKLEN;
      myname_c(taskname);
      tsklen = nelc_c(taskname);
      ctaskname[tsklen] = '\0';
      strcpy(playname, ctaskname);
      prelen = strlen(prefix);
      tsklen = (prelen+tsklen)>TSKLEN?(TSKLEN-prelen):tsklen;
      playname[tsklen] = '\0';
      wkeyf("GGILOG=");                                    /* stop recording */
      playstat = xeqcontf("_GGIPLAYFATE=", "%s%s(player) TASK=%s FILENAME=%s",
                                          prefix, playname, ctaskname, cname);
      showmessage();
   }
}

/* -------------------------------------------------------------------------- */
/*                                 monitor                                    */
/* -------------------------------------------------------------------------- */
static void monitor(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   (void)eventmonitor(NULL, tobool(button));
}

/* ========================================================================== */
/*                                 GgiOptionsShell                            */
/* -------------------------------------------------------------------------- */
void GgiOptionsShell(char *key)
{
   static ident shell;
   ident prev, frame, close;

   shell = GgiShell("Ggi Options");
   prev  = GgiUseShell(shell);
   frame = GgiForm("Ggi Options", 1);
   close = GgiSetLabel(GgiButton(key, "Close window"), "CLOSE", 0);

   GgiSetPosition(frame, 0, NULL, 0, NULL);
   GgiSetPosition(close, 0, NULL, 0, frame);
    
   {
      ident l_record, t_logfile, b_logdel, b_player, l_monitor, b_monitor;
      GgiUseShell(frame);
      l_record = GgiLabel("-------- Event recording and playback ----------");
      t_logfile = GgiTextField("_GGI_LOGFILE=",
                  "Name of file for recording Ggi events", 40);
      GgiSetLabel(t_logfile, "Event File: ", 0);
      ScheduleKeyevent(getname, "_GGI_LOGFILE=", KEYCHANGE, NULL);
      b_dolog = GgiButton("GGILOG=", "Event recording on/off");
      GgiDeactivate(b_dolog);
      GgiSetLabel(b_dolog, "RECORD", 0);
      b_logdel = GgiButton("GGILOGDELAY=", "Time interval recording on/off");
      GgiSetLabel(b_logdel, "DELAYS", 0);
      b_player = GgiButton("_GGIPLAYER=", "Start event player");
      GgiSetLabel(b_player, "START", 0);
      l_message = GgiLabel("Event Player:  not yet started");
      ScheduleKeyevent(player, "_GGIPLAYER=", KEYCHANGE, NULL);
      ScheduleKeyevent(player_exit, "_GGIPLAYFATE=", KEYCHANGE, NULL);
      l_monitor = GgiLabel("--------------- Event monitoring --------------");
      b_monitor = GgiButton("_GGI_MONITOR=", "Show events in log file on/off");
      ScheduleKeyevent(monitor, "_GGI_MONITOR=", KEYCHANGE, shell);
      GgiSetLabel(b_monitor, "MONITOR", 0);
      GgiSetPosition(l_record, 80, NULL,      0, NULL);
      GgiSetPosition(t_logfile, 0, NULL,      0, l_record);
      GgiSetPosition(b_dolog,   0, t_logfile, 0, l_record);
      GgiSetPosition(b_logdel,  0, b_dolog,   0, l_record);
      GgiSetPosition(b_player,  0, t_logfile, 0, b_dolog);
      GgiSetPosition(l_message, 0, NULL,      0, b_dolog);
      GgiSetPosition(l_monitor,80, NULL,     20, b_player);
      GgiSetPosition(b_monitor, 0, NULL,      0, l_monitor);
   }
   (void)GgiUseShell(prev);
   ScheduleKeyevent(popshell, key, KEYCHANGE, shell);
   strcpy(optionskey, key);
}

/* ========================================================================== */
/*                                 GgiOptionsButton                           */
/* -------------------------------------------------------------------------- */
ident GgiOptionsButton(void)
{
   ident result;

   result = GgiButton(optionskey, "GUI Options Pop-up window");
   GgiSetLabel(result, "GUI", 0);
   return result;
}
#<

#>            ggi.h
#if !defined(_ggi_h_)
#define _ggi_h_
#include "keyevents.h"

#define ggiButn1         1                        /* mouse button 1     */
#define ggiButn2         2                        /* mouse button 2     */
#define ggiButn3         4                        /* mouse button 3     */
#define ggiShift         8                        /* shift key          */
#define ggiCtrl         16                        /* ctrl key           */
#define ggiKeybd        32                        /* other keyboard key */
#define ggiLeftArrow  0xD1                        /* left arrow         */
#define ggiUpArrow    0xD2                        /* up arrow           */
#define ggiRightArrow 0xD3                        /* right arrow        */
#define ggiDownArrow  0xD4                        /* down arrow         */

#define ggiCreate     1
#define ggiColind     2
#define ggiLimits     3
#define ggiUnits      4
#define ggiApply      5
#define ggiDelete     6
#define ggiIdent      7
#define ggiNdims      8
#define ggiNowait     9
#define ggiCloseOnOK 10
#define ggiFileInfo  11
#define ggiGetMap    12

#define ggiReadOnly 0
#define ggiEdit     1
#define ggiAppend   2

#define ggiImmediateKeyevent(x) wkey_cb(x?GgiHandleEvents:NULL)

typedef enum {ggiLeft, ggiCenter, ggiRight} ggiAlign;   /* alignments */

typedef struct {
   ident plotfield;       /* Ggi element id */
   char  *name;           /* PGPLOT device name */
   float x, y;            /* cursor position in world coordinates */
   float width, height;   /* size of box (may be negative) */
   int   button;          /* button or key which caused the event */
   int   state;           /* mask of all active buttons and modifiers */
   char  key;             /* keyboard key */
} _GgiPlotInfo, *GgiPlotInfo;

typedef void (*GgiCursorProc)(ident, GgiPlotInfo, void*);
typedef char *(*GgiNameconverterProc)(char*);
typedef void (*GgiCleanupProc)(void*);

ident GgiTextField(char *keyword, char *message, int size);
ident GgiButton(char *keyword, char *message);
ident GgiMenu(char *keyword, char *message, char **labels);
ident GgiMenuLabels(ident menu, char **labels);
ident GgiList(char *keyword, int defcol, char **labels);
ident GgiListLabels(ident id, char **labels);
ident GgiListColumns(ident id, int columns);
ident GgiEditor(char *keyword, int width, int height, int mode,
                char *contents, int size);
ident GgiEditorSetSource(ident id, char *source, int size);
char *GgiEditorSource(ident id);
ident GgiEditorSave(ident id);
ident GgiEditorSaveAs(ident id, char *name);
ident GgiEditorSearch(ident id, int direction);
ident GgiGauge(char *keyword, char *message, int length, float minval,
               float maxval);
ident GgiDial(char *keyword, char *message, int radius, int minang, int maxang,
              int intervals, float arrw, float inarrl, float outarrl,
              float minval, float maxval);
ident GgiLabel(char *text);
ident GgiLogo(unsigned char bits[], int width, int height);
ident GgiCanvas(char *name, int width, int height);
ident GgiProgress(char *keyword, char *message, int length);
ident GgiPlotField(char *name, int width, int height);
int   GgiPlotColors(ident id, int newcolors);
ident GgiShell(char *name);
ident GgiDialog(char *name);
ident GgiForm(char *name, int border);
ident GgiViewport(char *name, int xsize, int ysize);
ident GgiUseShell(ident id);
void  GgiShowShell(ident id, bool show);
bool  GgiPlotXor(bool mode);
int   GgiPlotFrames(ident id, int newframes);
void  GgiPlotRecord(ident id, int frame);
void  GgiPlotShow(ident id, int frame);
void  GgiPlotMapColors(ident id, ... );
ident ScheduleGgiPlotCursor(GgiCursorProc, ident plotfield, void *arg);
void  DescheduleGgiPlotCursor(ident *id);
ident GgiActivate(ident id);
ident GgiDeactivate(ident id);
ident GgiFollowKey(ident id, bool follow);
ident GgiHelpText(ident id, char *message);
ident GgiSetLabel(ident id, char *label, int width);
ident GgiSetBitmap(ident id, int width, int height, unsigned char *bits);
ident GgiSetRange(ident id, float min, float max);
ident GgiSetFormat(ident id, char *format);
ident GgiSetCircular(ident id, bool circular);
ident GgiSetKeyword(ident id, char *key);
ident GgiSetCleanup(ident id, GgiCleanupProc proc, void *arg);
ident GgiAlignLabel(ident id, ggiAlign al);
ident GgiSetPosition(ident id, int xfrom, ident xid, int yfrom, ident yid);
void  GgiDelete(ident *id);
int   GgiWidth(ident id);
int   GgiHeight(ident id);
bool  GgiIsRealized(ident id);
void  GgiConfigure(char *appclass, int width, int menuheight, int textheight,
                   int buttonheight);
void  GgiAutoLayout(bool a);
void  GgiPostponeRealize(bool later);
void  GgiRealize(void);
void  GgiSetHeight(int height);
void  GgiSetWidth(int width);
void  GgiReLayout(void);
void  GgiShowDoc(char *docname);
void  GgiSetResources(char **resources);
int   GgiHandleEvents(void);
void  GgiPrompter(char *key, char *message);
void  GgiPlotPrompter(char *filekey, char *devkey, char *message);
ident GgiPlotColorEditor(int opcode, ... );
ident GgiInset(int opcode, ... );
ident GgiFileBrowser(int opcode, ... );
bool  GgiVerify(char *question, char *truelabel, char *falselabel);
ident GgiHelpMenu(char *key, char *buttontext, char *message,
                  char** labels, char **documents);
ident GgiTextMenu(char *keyword, char *message, char **labels);
ident GgiColorMenu(char *keyword, char *message);
ident GgiHeaderButton(char *key, char *buttontext, char *message, char *setkey);
int   GgiKeyIdent(char *key);
void  GgiSetDisplay(char *displayname);
void  GgiOptionsShell(char *key);
ident GgiOptionsButton(void);
ident GgiSetBtnKey(ident id, char *key);
#endif
#<

#>            ggidisplay.h
#if !defined(_ggidisplay_h_)
#define _ggidisplay_h_
#include <X11/Xlib.h>
Display *GgiDisplay(void);
#endif
#<

#>            ggiwindow.h
#if !defined(_ggiwindow_h_)
#define _ggiwindow_h_
#include <X11/Xlib.h>
Window GgiWindow(ident id);
#endif
#<

#>            ggiwidget.h
#if !defined(_ggiwidget_h_)
#define _ggiwidget_h_
#include <X11/Intrinsic.h>
Widget GgiWidget(ident id);
#endif
#<

#>            ggicontext.h
#if !defined(_ggicontext_h_)
#define _ggicontext_h_
#include <X11/Intrinsic.h>
XtAppContext *GgiContext(void);
#endif
#<

#>            colormaps.H
static  Color_struct    mono[] = {
   { 0.00000, 0.00000, 0.00000 },
   { 0.00392, 0.00392, 0.00392 },
   { 0.00784, 0.00784, 0.00784 },
   { 0.01176, 0.01176, 0.01176 },
   { 0.01569, 0.01569, 0.01569 },
   { 0.01961, 0.01961, 0.01961 },
   { 0.02353, 0.02353, 0.02353 },
   { 0.02745, 0.02745, 0.02745 },
   { 0.03137, 0.03137, 0.03137 },
   { 0.03529, 0.03529, 0.03529 },
   { 0.03922, 0.03922, 0.03922 },
   { 0.04314, 0.04314, 0.04314 },
   { 0.04706, 0.04706, 0.04706 },
   { 0.05098, 0.05098, 0.05098 },
   { 0.05490, 0.05490, 0.05490 },
   { 0.05882, 0.05882, 0.05882 },
   { 0.06275, 0.06275, 0.06275 },
   { 0.06667, 0.06667, 0.06667 },
   { 0.07059, 0.07059, 0.07059 },
   { 0.07451, 0.07451, 0.07451 },
   { 0.07843, 0.07843, 0.07843 },
   { 0.08235, 0.08235, 0.08235 },
   { 0.08627, 0.08627, 0.08627 },
   { 0.09020, 0.09020, 0.09020 },
   { 0.09412, 0.09412, 0.09412 },
   { 0.09804, 0.09804, 0.09804 },
   { 0.10196, 0.10196, 0.10196 },
   { 0.10588, 0.10588, 0.10588 },
   { 0.10980, 0.10980, 0.10980 },
   { 0.11373, 0.11373, 0.11373 },
   { 0.11765, 0.11765, 0.11765 },
   { 0.12157, 0.12157, 0.12157 },
   { 0.12549, 0.12549, 0.12549 },
   { 0.12941, 0.12941, 0.12941 },
   { 0.13333, 0.13333, 0.13333 },
   { 0.13725, 0.13725, 0.13725 },
   { 0.14118, 0.14118, 0.14118 },
   { 0.14510, 0.14510, 0.14510 },
   { 0.14902, 0.14902, 0.14902 },
   { 0.15294, 0.15294, 0.15294 },
   { 0.15686, 0.15686, 0.15686 },
   { 0.16078, 0.16078, 0.16078 },
   { 0.16471, 0.16471, 0.16471 },
   { 0.16863, 0.16863, 0.16863 },
   { 0.17255, 0.17255, 0.17255 },
   { 0.17647, 0.17647, 0.17647 },
   { 0.18039, 0.18039, 0.18039 },
   { 0.18431, 0.18431, 0.18431 },
   { 0.18824, 0.18824, 0.18824 },
   { 0.19216, 0.19216, 0.19216 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.20000, 0.20000, 0.20000 },
   { 0.20392, 0.20392, 0.20392 },
   { 0.20784, 0.20784, 0.20784 },
   { 0.21176, 0.21176, 0.21176 },
   { 0.21569, 0.21569, 0.21569 },
   { 0.21961, 0.21961, 0.21961 },
   { 0.22353, 0.22353, 0.22353 },
   { 0.22745, 0.22745, 0.22745 },
   { 0.23137, 0.23137, 0.23137 },
   { 0.23529, 0.23529, 0.23529 },
   { 0.23922, 0.23922, 0.23922 },
   { 0.24314, 0.24314, 0.24314 },
   { 0.24706, 0.24706, 0.24706 },
   { 0.25098, 0.25098, 0.25098 },
   { 0.25490, 0.25490, 0.25490 },
   { 0.25882, 0.25882, 0.25882 },
   { 0.26275, 0.26275, 0.26275 },
   { 0.26667, 0.26667, 0.26667 },
   { 0.27059, 0.27059, 0.27059 },
   { 0.27451, 0.27451, 0.27451 },
   { 0.27843, 0.27843, 0.27843 },
   { 0.28235, 0.28235, 0.28235 },
   { 0.28627, 0.28627, 0.28627 },
   { 0.29020, 0.29020, 0.29020 },
   { 0.29412, 0.29412, 0.29412 },
   { 0.29804, 0.29804, 0.29804 },
   { 0.30196, 0.30196, 0.30196 },
   { 0.30588, 0.30588, 0.30588 },
   { 0.30980, 0.30980, 0.30980 },
   { 0.31373, 0.31373, 0.31373 },
   { 0.31765, 0.31765, 0.31765 },
   { 0.32157, 0.32157, 0.32157 },
   { 0.32549, 0.32549, 0.32549 },
   { 0.32941, 0.32941, 0.32941 },
   { 0.33333, 0.33333, 0.33333 },
   { 0.33725, 0.33725, 0.33725 },
   { 0.34118, 0.34118, 0.34118 },
   { 0.34510, 0.34510, 0.34510 },
   { 0.34902, 0.34902, 0.34902 },
   { 0.35294, 0.35294, 0.35294 },
   { 0.35686, 0.35686, 0.35686 },
   { 0.36078, 0.36078, 0.36078 },
   { 0.36471, 0.36471, 0.36471 },
   { 0.36863, 0.36863, 0.36863 },
   { 0.37255, 0.37255, 0.37255 },
   { 0.37647, 0.37647, 0.37647 },
   { 0.38039, 0.38039, 0.38039 },
   { 0.38431, 0.38431, 0.38431 },
   { 0.38824, 0.38824, 0.38824 },
   { 0.39216, 0.39216, 0.39216 },
   { 0.39608, 0.39608, 0.39608 },
   { 0.40000, 0.40000, 0.40000 },
   { 0.40392, 0.40392, 0.40392 },
   { 0.40784, 0.40784, 0.40784 },
   { 0.41176, 0.41176, 0.41176 },
   { 0.41569, 0.41569, 0.41569 },
   { 0.41961, 0.41961, 0.41961 },
   { 0.42353, 0.42353, 0.42353 },
   { 0.42745, 0.42745, 0.42745 },
   { 0.43137, 0.43137, 0.43137 },
   { 0.43529, 0.43529, 0.43529 },
   { 0.43922, 0.43922, 0.43922 },
   { 0.44314, 0.44314, 0.44314 },
   { 0.44706, 0.44706, 0.44706 },
   { 0.45098, 0.45098, 0.45098 },
   { 0.45490, 0.45490, 0.45490 },
   { 0.45882, 0.45882, 0.45882 },
   { 0.46275, 0.46275, 0.46275 },
   { 0.46667, 0.46667, 0.46667 },
   { 0.47059, 0.47059, 0.47059 },
   { 0.47451, 0.47451, 0.47451 },
   { 0.47843, 0.47843, 0.47843 },
   { 0.48235, 0.48235, 0.48235 },
   { 0.48627, 0.48627, 0.48627 },
   { 0.49020, 0.49020, 0.49020 },
   { 0.49412, 0.49412, 0.49412 },
   { 0.49804, 0.49804, 0.49804 },
   { 0.50196, 0.50196, 0.50196 },
   { 0.50588, 0.50588, 0.50588 },
   { 0.50980, 0.50980, 0.50980 },
   { 0.51373, 0.51373, 0.51373 },
   { 0.51765, 0.51765, 0.51765 },
   { 0.52157, 0.52157, 0.52157 },
   { 0.52549, 0.52549, 0.52549 },
   { 0.52941, 0.52941, 0.52941 },
   { 0.53333, 0.53333, 0.53333 },
   { 0.53725, 0.53725, 0.53725 },
   { 0.54118, 0.54118, 0.54118 },
   { 0.54510, 0.54510, 0.54510 },
   { 0.54902, 0.54902, 0.54902 },
   { 0.55294, 0.55294, 0.55294 },
   { 0.55686, 0.55686, 0.55686 },
   { 0.56078, 0.56078, 0.56078 },
   { 0.56471, 0.56471, 0.56471 },
   { 0.56863, 0.56863, 0.56863 },
   { 0.57255, 0.57255, 0.57255 },
   { 0.57647, 0.57647, 0.57647 },
   { 0.58039, 0.58039, 0.58039 },
   { 0.58431, 0.58431, 0.58431 },
   { 0.58824, 0.58824, 0.58824 },
   { 0.59608, 0.59608, 0.59608 },
   { 0.60000, 0.60000, 0.60000 },
   { 0.59608, 0.59608, 0.59608 },
   { 0.60392, 0.60392, 0.60392 },
   { 0.60784, 0.60784, 0.60784 },
   { 0.61176, 0.61176, 0.61176 },
   { 0.61569, 0.61569, 0.61569 },
   { 0.61961, 0.61961, 0.61961 },
   { 0.62353, 0.62353, 0.62353 },
   { 0.62745, 0.62745, 0.62745 },
   { 0.63137, 0.63137, 0.63137 },
   { 0.63529, 0.63529, 0.63529 },
   { 0.63922, 0.63922, 0.63922 },
   { 0.64314, 0.64314, 0.64314 },
   { 0.64706, 0.64706, 0.64706 },
   { 0.65098, 0.65098, 0.65098 },
   { 0.65490, 0.65490, 0.65490 },
   { 0.65882, 0.65882, 0.65882 },
   { 0.66275, 0.66275, 0.66275 },
   { 0.66667, 0.66667, 0.66667 },
   { 0.67059, 0.67059, 0.67059 },
   { 0.67451, 0.67451, 0.67451 },
   { 0.67843, 0.67843, 0.67843 },
   { 0.68235, 0.68235, 0.68235 },
   { 0.68627, 0.68627, 0.68627 },
   { 0.69020, 0.69020, 0.69020 },
   { 0.69412, 0.69412, 0.69412 },
   { 0.69804, 0.69804, 0.69804 },
   { 0.70196, 0.70196, 0.70196 },
   { 0.70588, 0.70588, 0.70588 },
   { 0.70980, 0.70980, 0.70980 },
   { 0.71373, 0.71373, 0.71373 },
   { 0.71765, 0.71765, 0.71765 },
   { 0.72157, 0.72157, 0.72157 },
   { 0.72549, 0.72549, 0.72549 },
   { 0.72941, 0.72941, 0.72941 },
   { 0.73333, 0.73333, 0.73333 },
   { 0.73725, 0.73725, 0.73725 },
   { 0.74118, 0.74118, 0.74118 },
   { 0.74510, 0.74510, 0.74510 },
   { 0.74902, 0.74902, 0.74902 },
   { 0.75294, 0.75294, 0.75294 },
   { 0.75686, 0.75686, 0.75686 },
   { 0.76078, 0.76078, 0.76078 },
   { 0.76471, 0.76471, 0.76471 },
   { 0.76863, 0.76863, 0.76863 },
   { 0.77255, 0.77255, 0.77255 },
   { 0.77647, 0.77647, 0.77647 },
   { 0.78039, 0.78039, 0.78039 },
   { 0.78431, 0.78431, 0.78431 },
   { 0.78824, 0.78824, 0.78824 },
   { 0.79216, 0.79216, 0.79216 },
   { 0.79608, 0.79608, 0.79608 },
   { 0.80000, 0.80000, 0.80000 },
   { 0.80392, 0.80392, 0.80392 },
   { 0.80784, 0.80784, 0.80784 },
   { 0.81176, 0.81176, 0.81176 },
   { 0.81569, 0.81569, 0.81569 },
   { 0.81961, 0.81961, 0.81961 },
   { 0.82353, 0.82353, 0.82353 },
   { 0.82745, 0.82745, 0.82745 },
   { 0.83137, 0.83137, 0.83137 },
   { 0.83529, 0.83529, 0.83529 },
   { 0.83922, 0.83922, 0.83922 },
   { 0.84314, 0.84314, 0.84314 },
   { 0.84706, 0.84706, 0.84706 },
   { 0.85098, 0.85098, 0.85098 },
   { 0.85490, 0.85490, 0.85490 },
   { 0.85882, 0.85882, 0.85882 },
   { 0.86275, 0.86275, 0.86275 },
   { 0.86667, 0.86667, 0.86667 },
   { 0.87059, 0.87059, 0.87059 },
   { 0.87451, 0.87451, 0.87451 },
   { 0.87843, 0.87843, 0.87843 },
   { 0.88235, 0.88235, 0.88235 },
   { 0.88627, 0.88627, 0.88627 },
   { 0.89020, 0.89020, 0.89020 },
   { 0.89412, 0.89412, 0.89412 },
   { 0.89804, 0.89804, 0.89804 },
   { 0.90196, 0.90196, 0.90196 },
   { 0.90588, 0.90588, 0.90588 },
   { 0.90980, 0.90980, 0.90980 },
   { 0.91373, 0.91373, 0.91373 },
   { 0.91765, 0.91765, 0.91765 },
   { 0.92157, 0.92157, 0.92157 },
   { 0.92549, 0.92549, 0.92549 },
   { 0.92941, 0.92941, 0.92941 },
   { 0.93333, 0.93333, 0.93333 },
   { 0.93725, 0.93725, 0.93725 },
   { 0.94118, 0.94118, 0.94118 },
   { 0.94510, 0.94510, 0.94510 },
   { 0.94902, 0.94902, 0.94902 },
   { 0.95294, 0.95294, 0.95294 },
   { 0.95686, 0.95686, 0.95686 },
   { 0.96078, 0.96078, 0.96078 },
   { 0.96471, 0.96471, 0.96471 },
   { 0.96863, 0.96863, 0.96863 },
   { 0.97255, 0.97255, 0.97255 },
   { 0.97647, 0.97647, 0.97647 },
   { 0.98039, 0.98039, 0.98039 },
   { 0.98431, 0.98431, 0.98431 },
   { 0.98824, 0.98824, 0.98824 },
   { 0.99216, 0.99216, 0.99216 },
   { 0.99608, 0.99608, 0.99608 },
   { 1.00000, 1.00000, 1.00000 }
};

static	Color_struct	rainbow[] = {
   { 0.00000, 0.00000, 0.01176 },
   { 0.00000, 0.00000, 0.02745 },
   { 0.00000, 0.00000, 0.04314 },
   { 0.00000, 0.00000, 0.05882 },
   { 0.00000, 0.00000, 0.07451 },
   { 0.00000, 0.00000, 0.09020 },
   { 0.00000, 0.00000, 0.10588 },
   { 0.00000, 0.00000, 0.12157 },
   { 0.00000, 0.00000, 0.13725 },
   { 0.00000, 0.00000, 0.15294 },
   { 0.00000, 0.00000, 0.16863 },
   { 0.00000, 0.00000, 0.18431 },
   { 0.00000, 0.00000, 0.20000 },
   { 0.00000, 0.00000, 0.21176 },
   { 0.00000, 0.00000, 0.22745 },
   { 0.00000, 0.00000, 0.24314 },
   { 0.00000, 0.00000, 0.25882 },
   { 0.00000, 0.00000, 0.27451 },
   { 0.00000, 0.00000, 0.29020 },
   { 0.00000, 0.00000, 0.30588 },
   { 0.00000, 0.00000, 0.32157 },
   { 0.00000, 0.00000, 0.33725 },
   { 0.00000, 0.00000, 0.35294 },
   { 0.00000, 0.00000, 0.36863 },
   { 0.00000, 0.00000, 0.38431 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.41176 },
   { 0.00000, 0.00000, 0.42745 },
   { 0.00000, 0.00000, 0.44314 },
   { 0.00000, 0.00000, 0.45882 },
   { 0.00000, 0.00000, 0.47451 },
   { 0.00000, 0.00000, 0.49020 },
   { 0.00000, 0.00000, 0.50588 },
   { 0.00000, 0.00000, 0.52157 },
   { 0.00000, 0.00000, 0.53725 },
   { 0.00000, 0.00000, 0.55294 },
   { 0.00000, 0.00000, 0.56863 },
   { 0.00000, 0.00000, 0.58431 },
   { 0.00000, 0.00000, 0.60000 },
   { 0.00000, 0.00000, 0.61176 },
   { 0.00000, 0.00000, 0.62745 },
   { 0.00000, 0.00000, 0.64314 },
   { 0.00000, 0.00000, 0.65882 },
   { 0.00000, 0.00000, 0.67451 },
   { 0.00000, 0.00000, 0.69020 },
   { 0.00000, 0.00000, 0.70588 },
   { 0.00000, 0.00000, 0.72157 },
   { 0.00000, 0.00000, 0.73725 },
   { 0.00000, 0.00000, 0.75294 },
   { 0.00000, 0.00000, 0.76863 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.80000 },
   { 0.00000, 0.00000, 0.81176 },
   { 0.00000, 0.00000, 0.82745 },
   { 0.00000, 0.00000, 0.84314 },
   { 0.00000, 0.00000, 0.85882 },
   { 0.00000, 0.00000, 0.87451 },
   { 0.00000, 0.00000, 0.89020 },
   { 0.00000, 0.00000, 0.90588 },
   { 0.00000, 0.00000, 0.92157 },
   { 0.00000, 0.00000, 0.93725 },
   { 0.00000, 0.00000, 0.95294 },
   { 0.00000, 0.00000, 0.96863 },
   { 0.00000, 0.00000, 0.98431 },
   { 0.00000, 0.00000, 1.00000 },
   { 0.00000, 0.03529, 1.00000 },
   { 0.00000, 0.07059, 1.00000 },
   { 0.00000, 0.10980, 1.00000 },
   { 0.00000, 0.14510, 1.00000 },
   { 0.00000, 0.18039, 1.00000 },
   { 0.00000, 0.21961, 1.00000 },
   { 0.00000, 0.25490, 1.00000 },
   { 0.00000, 0.29412, 1.00000 },
   { 0.00000, 0.32941, 1.00000 },
   { 0.00000, 0.36471, 1.00000 },
   { 0.00000, 0.40392, 1.00000 },
   { 0.00000, 0.43922, 1.00000 },
   { 0.00000, 0.47843, 1.00000 },
   { 0.00000, 0.50196, 1.00000 },
   { 0.00000, 0.52549, 1.00000 },
   { 0.00000, 0.54902, 1.00000 },
   { 0.00000, 0.57255, 1.00000 },
   { 0.00000, 0.59608, 1.00000 },
   { 0.00000, 0.61961, 1.00000 },
   { 0.00000, 0.64314, 1.00000 },
   { 0.00000, 0.66667, 1.00000 },
   { 0.00000, 0.69020, 1.00000 },
   { 0.00000, 0.71373, 1.00000 },
   { 0.00000, 0.73725, 1.00000 },
   { 0.00000, 0.76078, 1.00000 },
   { 0.00000, 0.78431, 1.00000 },
   { 0.00000, 0.80000, 1.00000 },
   { 0.00000, 0.81569, 1.00000 },
   { 0.00000, 0.83137, 1.00000 },
   { 0.00000, 0.84706, 1.00000 },
   { 0.00000, 0.86667, 1.00000 },
   { 0.00000, 0.88235, 1.00000 },
   { 0.00000, 0.89804, 1.00000 },
   { 0.00000, 0.91373, 1.00000 },
   { 0.00000, 0.93333, 1.00000 },
   { 0.00000, 0.94902, 1.00000 },
   { 0.00000, 0.96471, 1.00000 },
   { 0.00000, 0.98039, 1.00000 },
   { 0.00000, 1.00000, 1.00000 },
   { 0.00000, 1.00000, 0.97647 },
   { 0.00000, 1.00000, 0.95294 },
   { 0.00000, 1.00000, 0.92941 },
   { 0.00000, 1.00000, 0.90588 },
   { 0.00000, 1.00000, 0.88627 },
   { 0.00000, 1.00000, 0.86275 },
   { 0.00000, 1.00000, 0.83922 },
   { 0.00000, 1.00000, 0.81569 },
   { 0.00000, 1.00000, 0.79608 },
   { 0.00000, 1.00000, 0.77255 },
   { 0.00000, 1.00000, 0.74902 },
   { 0.00000, 1.00000, 0.72549 },
   { 0.00000, 1.00000, 0.70588 },
   { 0.00000, 1.00000, 0.65098 },
   { 0.00000, 1.00000, 0.59608 },
   { 0.00000, 1.00000, 0.54118 },
   { 0.00000, 1.00000, 0.48627 },
   { 0.00000, 1.00000, 0.43137 },
   { 0.00000, 1.00000, 0.37647 },
   { 0.00000, 1.00000, 0.32549 },
   { 0.00000, 1.00000, 0.27059 },
   { 0.00000, 1.00000, 0.21569 },
   { 0.00000, 1.00000, 0.16078 },
   { 0.00000, 1.00000, 0.10588 },
   { 0.00000, 1.00000, 0.05098 },
   { 0.00000, 1.00000, 0.00000 },
   { 0.05098, 1.00000, 0.00000 },
   { 0.10588, 1.00000, 0.00000 },
   { 0.16078, 1.00000, 0.00000 },
   { 0.21569, 1.00000, 0.00000 },
   { 0.27059, 1.00000, 0.00000 },
   { 0.32549, 1.00000, 0.00000 },
   { 0.37647, 1.00000, 0.00000 },
   { 0.43137, 1.00000, 0.00000 },
   { 0.48627, 1.00000, 0.00000 },
   { 0.54118, 1.00000, 0.00000 },
   { 0.59608, 1.00000, 0.00000 },
   { 0.65098, 1.00000, 0.00000 },
   { 0.70588, 1.00000, 0.00000 },
   { 0.72549, 1.00000, 0.00000 },
   { 0.74902, 1.00000, 0.00000 },
   { 0.77255, 1.00000, 0.00000 },
   { 0.79608, 1.00000, 0.00000 },
   { 0.81569, 1.00000, 0.00000 },
   { 0.83922, 1.00000, 0.00000 },
   { 0.86275, 1.00000, 0.00000 },
   { 0.88627, 1.00000, 0.00000 },
   { 0.90588, 1.00000, 0.00000 },
   { 0.92941, 1.00000, 0.00000 },
   { 0.95294, 1.00000, 0.00000 },
   { 0.97647, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 0.99608, 0.97647, 0.00000 },
   { 0.99608, 0.95686, 0.00000 },
   { 0.99608, 0.93333, 0.00000 },
   { 0.99608, 0.91373, 0.00000 },
   { 0.99216, 0.89412, 0.00000 },
   { 0.99216, 0.87059, 0.00000 },
   { 0.99216, 0.85098, 0.00000 },
   { 0.99216, 0.82745, 0.00000 },
   { 0.98824, 0.80784, 0.00000 },
   { 0.98824, 0.78824, 0.00000 },
   { 0.98824, 0.76471, 0.00000 },
   { 0.98824, 0.74510, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.70588, 0.00000 },
   { 0.98824, 0.68627, 0.00000 },
   { 0.98824, 0.66667, 0.00000 },
   { 0.98824, 0.64706, 0.00000 },
   { 0.99216, 0.62745, 0.00000 },
   { 0.99216, 0.60784, 0.00000 },
   { 0.99216, 0.58824, 0.00000 },
   { 0.99216, 0.56863, 0.00000 },
   { 0.99608, 0.54902, 0.00000 },
   { 0.99608, 0.52941, 0.00000 },
   { 0.99608, 0.50980, 0.00000 },
   { 0.99608, 0.49020, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.43137, 0.00000 },
   { 1.00000, 0.39608, 0.00000 },
   { 1.00000, 0.36078, 0.00000 },
   { 1.00000, 0.32549, 0.00000 },
   { 1.00000, 0.28627, 0.00000 },
   { 1.00000, 0.25098, 0.00000 },
   { 1.00000, 0.21569, 0.00000 },
   { 1.00000, 0.18039, 0.00000 },
   { 1.00000, 0.14118, 0.00000 },
   { 1.00000, 0.10588, 0.00000 },
   { 1.00000, 0.07059, 0.00000 },
   { 1.00000, 0.03529, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.05098 },
   { 1.00000, 0.00000, 0.10588 },
   { 1.00000, 0.00000, 0.16078 },
   { 1.00000, 0.00000, 0.21569 },
   { 1.00000, 0.00000, 0.27059 },
   { 1.00000, 0.00000, 0.32549 },
   { 1.00000, 0.00000, 0.37647 },
   { 1.00000, 0.00000, 0.43137 },
   { 1.00000, 0.00000, 0.48627 },
   { 1.00000, 0.00000, 0.54118 },
   { 1.00000, 0.00000, 0.59608 },
   { 1.00000, 0.00000, 0.65098 },
   { 1.00000, 0.00000, 0.70588 },
   { 1.00000, 0.00000, 0.72549 },
   { 1.00000, 0.00000, 0.74902 },
   { 1.00000, 0.00000, 0.77255 },
   { 1.00000, 0.00000, 0.79608 },
   { 1.00000, 0.00000, 0.81569 },
   { 1.00000, 0.00000, 0.83922 },
   { 1.00000, 0.00000, 0.86275 },
   { 1.00000, 0.00000, 0.88627 },
   { 1.00000, 0.00000, 0.90588 },
   { 1.00000, 0.00000, 0.92941 },
   { 1.00000, 0.00000, 0.95294 },
   { 1.00000, 0.00000, 0.97647 },
   { 1.00000, 0.00000, 1.00000 },
   { 1.00000, 0.03529, 1.00000 },
   { 1.00000, 0.07059, 1.00000 },
   { 1.00000, 0.10588, 1.00000 },
   { 1.00000, 0.14118, 1.00000 },
   { 1.00000, 0.18039, 1.00000 },
   { 1.00000, 0.21569, 1.00000 },
   { 1.00000, 0.25098, 1.00000 },
   { 1.00000, 0.28627, 1.00000 },
   { 1.00000, 0.32549, 1.00000 },
   { 1.00000, 0.36078, 1.00000 },
   { 1.00000, 0.39608, 1.00000 },
   { 1.00000, 0.43137, 1.00000 },
   { 1.00000, 0.47059, 1.00000 },
   { 1.00000, 0.48627, 1.00000 },
   { 1.00000, 0.50588, 1.00000 },
   { 1.00000, 0.52157, 1.00000 },
   { 1.00000, 0.54118, 1.00000 },
   { 1.00000, 0.56078, 1.00000 },
   { 1.00000, 0.57647, 1.00000 },
   { 1.00000, 0.59608, 1.00000 },
   { 1.00000, 0.61176, 1.00000 },
   { 1.00000, 0.63137, 1.00000 },
   { 1.00000, 0.65098, 1.00000 },
   { 1.00000, 0.66667, 1.00000 },
   { 1.00000, 0.68627, 1.00000 },
   { 1.00000, 0.70588, 1.00000 },
   { 1.00000, 0.74510, 1.00000 },
   { 1.00000, 0.78824, 1.00000 },
   { 1.00000, 0.83137, 1.00000 },
   { 1.00000, 0.87059, 1.00000 },
   { 1.00000, 0.91373, 1.00000 },
   { 1.00000, 0.95686, 1.00000 },
   { 1.00000, 1.00000, 1.00000 }
};

static  Color_struct    ronekers[] = {
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.19608, 0.19608, 0.19608 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.47451, 0.00000, 0.60784 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.00000, 0.00000, 0.78431 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.37255, 0.65490, 0.92549 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.56863, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 0.00000, 0.96471, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.69412, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 }
};

static	Color_struct staircase[] = {
   { 0.00392, 0.00392, 0.31373 },
   { 0.00784, 0.00784, 0.31373 },
   { 0.01176, 0.01176, 0.31373 },
   { 0.01569, 0.01569, 0.31373 },
   { 0.01961, 0.01961, 0.31373 },
   { 0.02353, 0.02353, 0.31373 },
   { 0.02745, 0.02745, 0.31373 },
   { 0.03137, 0.03137, 0.31373 },
   { 0.03529, 0.03529, 0.31373 },
   { 0.03922, 0.03922, 0.31373 },
   { 0.04314, 0.04314, 0.31373 },
   { 0.04706, 0.04706, 0.31373 },
   { 0.05098, 0.05098, 0.31373 },
   { 0.05490, 0.05490, 0.31373 },
   { 0.05882, 0.05882, 0.31373 },
   { 0.06275, 0.06275, 0.31373 },
   { 0.06667, 0.06667, 0.47059 },
   { 0.07059, 0.07059, 0.47059 },
   { 0.07451, 0.07451, 0.47059 },
   { 0.07843, 0.07843, 0.47059 },
   { 0.08235, 0.08235, 0.47059 },
   { 0.08627, 0.08627, 0.47059 },
   { 0.09020, 0.09020, 0.47059 },
   { 0.09412, 0.09412, 0.47059 },
   { 0.09804, 0.09804, 0.47059 },
   { 0.10196, 0.10196, 0.47059 },
   { 0.10588, 0.10588, 0.47059 },
   { 0.10980, 0.10980, 0.47059 },
   { 0.11373, 0.11373, 0.47059 },
   { 0.11765, 0.11765, 0.47059 },
   { 0.12157, 0.12157, 0.47059 },
   { 0.12549, 0.12549, 0.47059 },
   { 0.12941, 0.12941, 0.62745 },
   { 0.13333, 0.13333, 0.62745 },
   { 0.13725, 0.13725, 0.62745 },
   { 0.14118, 0.14118, 0.62745 },
   { 0.14510, 0.14510, 0.62745 },
   { 0.14902, 0.14902, 0.62745 },
   { 0.15294, 0.15294, 0.62745 },
   { 0.15686, 0.15686, 0.62745 },
   { 0.16078, 0.16078, 0.62745 },
   { 0.16471, 0.16471, 0.62745 },
   { 0.16863, 0.16863, 0.62745 },
   { 0.17255, 0.17255, 0.62745 },
   { 0.17647, 0.17647, 0.62745 },
   { 0.18039, 0.18039, 0.62745 },
   { 0.18431, 0.18431, 0.62745 },
   { 0.18824, 0.18824, 0.62745 },
   { 0.19216, 0.19216, 0.78431 },
   { 0.19608, 0.19608, 0.78431 },
   { 0.20000, 0.20000, 0.78431 },
   { 0.20392, 0.20392, 0.78431 },
   { 0.20784, 0.20784, 0.78431 },
   { 0.21176, 0.21176, 0.78431 },
   { 0.21569, 0.21569, 0.78431 },
   { 0.21961, 0.21961, 0.78431 },
   { 0.22353, 0.22353, 0.78431 },
   { 0.22745, 0.22745, 0.78431 },
   { 0.23137, 0.23137, 0.78431 },
   { 0.23529, 0.23529, 0.78431 },
   { 0.23922, 0.23922, 0.78431 },
   { 0.24314, 0.24314, 0.78431 },
   { 0.24706, 0.24706, 0.78431 },
   { 0.25098, 0.25098, 0.78431 },
   { 0.25490, 0.25490, 0.94118 },
   { 0.25882, 0.25882, 0.94118 },
   { 0.26275, 0.26275, 0.94118 },
   { 0.26667, 0.26667, 0.94118 },
   { 0.27059, 0.27059, 0.94118 },
   { 0.27451, 0.27451, 0.94118 },
   { 0.27843, 0.27843, 0.94118 },
   { 0.28235, 0.28235, 0.94118 },
   { 0.28627, 0.28627, 0.94118 },
   { 0.29020, 0.29020, 0.94118 },
   { 0.29412, 0.29412, 0.94118 },
   { 0.29804, 0.29804, 0.94118 },
   { 0.30196, 0.30196, 0.94118 },
   { 0.30588, 0.30588, 0.94118 },
   { 0.30980, 0.30980, 0.94118 },
   { 0.31373, 0.31373, 0.94118 },
   { 0.31765, 0.31765, 0.95294 },
   { 0.32157, 0.32157, 0.96471 },
   { 0.32549, 0.32549, 0.97647 },
   { 0.32941, 0.32941, 0.98824 },
   { 0.33333, 0.33333, 1.00000 },
   { 0.00392, 0.31373, 0.00392 },
   { 0.00784, 0.31373, 0.00784 },
   { 0.01176, 0.31373, 0.01176 },
   { 0.01569, 0.31373, 0.01569 },
   { 0.01961, 0.31373, 0.01961 },
   { 0.02353, 0.31373, 0.02353 },
   { 0.02745, 0.31373, 0.02745 },
   { 0.03137, 0.31373, 0.03137 },
   { 0.03529, 0.31373, 0.03529 },
   { 0.03922, 0.31373, 0.03922 },
   { 0.04314, 0.31373, 0.04314 },
   { 0.04706, 0.31373, 0.04706 },
   { 0.05098, 0.31373, 0.05098 },
   { 0.05490, 0.31373, 0.05490 },
   { 0.05882, 0.31373, 0.05882 },
   { 0.06275, 0.31373, 0.06275 },
   { 0.06667, 0.47059, 0.06667 },
   { 0.07059, 0.47059, 0.07059 },
   { 0.07451, 0.47059, 0.07451 },
   { 0.07843, 0.47059, 0.07843 },
   { 0.08235, 0.47059, 0.08235 },
   { 0.08627, 0.47059, 0.08627 },
   { 0.09020, 0.47059, 0.09020 },
   { 0.09412, 0.47059, 0.09412 },
   { 0.09804, 0.47059, 0.09804 },
   { 0.10196, 0.47059, 0.10196 },
   { 0.10588, 0.47059, 0.10588 },
   { 0.10980, 0.47059, 0.10980 },
   { 0.11373, 0.47059, 0.11373 },
   { 0.11765, 0.47059, 0.11765 },
   { 0.12157, 0.47059, 0.12157 },
   { 0.12549, 0.47059, 0.12549 },
   { 0.12941, 0.62745, 0.12941 },
   { 0.13333, 0.62745, 0.13333 },
   { 0.13725, 0.62745, 0.13725 },
   { 0.14118, 0.62745, 0.14118 },
   { 0.14510, 0.62745, 0.14510 },
   { 0.14902, 0.62745, 0.14902 },
   { 0.15294, 0.62745, 0.15294 },
   { 0.15686, 0.62745, 0.15686 },
   { 0.16078, 0.62745, 0.16078 },
   { 0.16471, 0.62745, 0.16471 },
   { 0.16863, 0.62745, 0.16863 },
   { 0.17255, 0.62745, 0.17255 },
   { 0.17647, 0.62745, 0.17647 },
   { 0.18039, 0.62745, 0.18039 },
   { 0.18431, 0.62745, 0.18431 },
   { 0.18824, 0.62745, 0.18824 },
   { 0.19216, 0.78431, 0.19216 },
   { 0.19608, 0.78431, 0.19608 },
   { 0.20000, 0.78431, 0.20000 },
   { 0.20392, 0.78431, 0.20392 },
   { 0.20784, 0.78431, 0.20784 },
   { 0.21176, 0.78431, 0.21176 },
   { 0.21569, 0.78431, 0.21569 },
   { 0.21961, 0.78431, 0.21961 },
   { 0.22353, 0.78431, 0.22353 },
   { 0.22745, 0.78431, 0.22745 },
   { 0.23137, 0.78431, 0.23137 },
   { 0.23529, 0.78431, 0.23529 },
   { 0.23922, 0.78431, 0.23922 },
   { 0.24314, 0.78431, 0.24314 },
   { 0.24706, 0.78431, 0.24706 },
   { 0.25098, 0.78431, 0.25098 },
   { 0.25490, 0.94118, 0.25490 },
   { 0.25882, 0.94118, 0.25882 },
   { 0.26275, 0.94118, 0.26275 },
   { 0.26667, 0.94118, 0.26667 },
   { 0.27059, 0.94118, 0.27059 },
   { 0.27451, 0.94118, 0.27451 },
   { 0.27843, 0.94118, 0.27843 },
   { 0.28235, 0.94118, 0.28235 },
   { 0.28627, 0.94118, 0.28627 },
   { 0.29020, 0.94118, 0.29020 },
   { 0.29412, 0.94118, 0.29412 },
   { 0.29804, 0.94118, 0.29804 },
   { 0.30196, 0.94118, 0.30196 },
   { 0.30588, 0.94118, 0.30588 },
   { 0.30980, 0.94118, 0.30980 },
   { 0.31373, 0.94118, 0.31373 },
   { 0.31765, 0.95294, 0.31765 },
   { 0.32157, 0.96471, 0.32157 },
   { 0.32549, 0.97647, 0.32549 },
   { 0.32941, 0.98824, 0.32941 },
   { 0.33333, 1.00000, 0.33333 },
   { 0.31373, 0.00392, 0.00392 },
   { 0.31373, 0.00784, 0.00784 },
   { 0.31373, 0.01176, 0.01176 },
   { 0.31373, 0.01569, 0.01569 },
   { 0.31373, 0.01961, 0.01961 },
   { 0.31373, 0.02353, 0.02353 },
   { 0.31373, 0.02745, 0.02745 },
   { 0.31373, 0.03137, 0.03137 },
   { 0.31373, 0.03529, 0.03529 },
   { 0.31373, 0.03922, 0.03922 },
   { 0.31373, 0.04314, 0.04314 },
   { 0.31373, 0.04706, 0.04706 },
   { 0.31373, 0.05098, 0.05098 },
   { 0.31373, 0.05490, 0.05490 },
   { 0.31373, 0.05882, 0.05882 },
   { 0.31373, 0.06275, 0.06275 },
   { 0.47059, 0.06667, 0.06667 },
   { 0.47059, 0.07059, 0.07059 },
   { 0.47059, 0.07451, 0.07451 },
   { 0.47059, 0.07843, 0.07843 },
   { 0.47059, 0.08235, 0.08235 },
   { 0.47059, 0.08627, 0.08627 },
   { 0.47059, 0.09020, 0.09020 },
   { 0.47059, 0.09412, 0.09412 },
   { 0.47059, 0.09804, 0.09804 },
   { 0.47059, 0.10196, 0.10196 },
   { 0.47059, 0.10588, 0.10588 },
   { 0.47059, 0.10980, 0.10980 },
   { 0.47059, 0.11373, 0.11373 },
   { 0.47059, 0.11765, 0.11765 },
   { 0.47059, 0.12157, 0.12157 },
   { 0.47059, 0.12549, 0.12549 },
   { 0.62745, 0.12941, 0.12941 },
   { 0.62745, 0.13333, 0.13333 },
   { 0.62745, 0.13725, 0.13725 },
   { 0.62745, 0.14118, 0.14118 },
   { 0.62745, 0.14510, 0.14510 },
   { 0.62745, 0.14902, 0.14902 },
   { 0.62745, 0.15294, 0.15294 },
   { 0.62745, 0.15686, 0.15686 },
   { 0.62745, 0.16078, 0.16078 },
   { 0.62745, 0.16471, 0.16471 },
   { 0.62745, 0.16863, 0.16863 },
   { 0.62745, 0.17255, 0.17255 },
   { 0.62745, 0.17647, 0.17647 },
   { 0.62745, 0.18039, 0.18039 },
   { 0.62745, 0.18431, 0.18431 },
   { 0.62745, 0.18824, 0.18824 },
   { 0.78431, 0.19216, 0.19216 },
   { 0.78431, 0.19608, 0.19608 },
   { 0.78431, 0.20000, 0.20000 },
   { 0.78431, 0.20392, 0.20392 },
   { 0.78431, 0.20784, 0.20784 },
   { 0.78431, 0.21176, 0.21176 },
   { 0.78431, 0.21569, 0.21569 },
   { 0.78431, 0.21961, 0.21961 },
   { 0.78431, 0.22353, 0.22353 },
   { 0.78431, 0.22745, 0.22745 },
   { 0.78431, 0.23137, 0.23137 },
   { 0.78431, 0.23529, 0.23529 },
   { 0.78431, 0.23922, 0.23922 },
   { 0.78431, 0.24314, 0.24314 },
   { 0.78431, 0.24706, 0.24706 },
   { 0.78431, 0.25098, 0.25098 },
   { 0.94118, 0.25490, 0.25490 },
   { 0.94118, 0.25882, 0.25882 },
   { 0.94118, 0.26275, 0.26275 },
   { 0.94118, 0.26667, 0.26667 },
   { 0.94118, 0.27059, 0.27059 },
   { 0.94118, 0.27451, 0.27451 },
   { 0.94118, 0.27843, 0.27843 },
   { 0.94118, 0.28235, 0.28235 },
   { 0.94118, 0.28627, 0.28627 },
   { 0.94118, 0.29020, 0.29020 },
   { 0.94118, 0.29412, 0.29412 },
   { 0.94118, 0.29804, 0.29804 },
   { 0.94118, 0.30196, 0.30196 },
   { 0.94118, 0.30588, 0.30588 },
   { 0.94118, 0.30980, 0.30980 },
   { 0.94118, 0.31373, 0.31373 },
   { 0.94902, 0.39216, 0.39216 },
   { 0.96078, 0.52941, 0.52941 },
   { 0.97255, 0.66667, 0.66667 },
   { 0.98431, 0.80392, 0.80392 },
   { 0.99216, 0.80000, 0.80000 },
   { 1.00000, 1.00000, 1.00000 }
};

static	Color_struct	background[] = {
   { 0.00000, 0.00000, 0.00000 },
   { 0.01587, 0.01587, 0.01587 },
   { 0.03174, 0.03174, 0.03174 },
   { 0.04761, 0.04761, 0.04761 },
   { 0.06348, 0.06348, 0.06348 },
   { 0.07935, 0.07935, 0.07935 },
   { 0.09522, 0.09522, 0.09522 },
   { 0.11109, 0.11109, 0.11109 },
   { 0.12696, 0.12696, 0.12696 },
   { 0.14283, 0.14283, 0.14283 },
   { 0.15870, 0.15870, 0.15870 },
   { 0.17457, 0.17457, 0.17457 },
   { 0.19044, 0.19044, 0.19044 },
   { 0.20631, 0.20631, 0.20631 },
   { 0.22218, 0.22218, 0.22218 },
   { 0.23805, 0.23805, 0.23805 },
   { 0.25392, 0.25392, 0.25392 },
   { 0.26979, 0.26979, 0.26979 },
   { 0.28566, 0.28566, 0.28566 },
   { 0.30153, 0.30153, 0.30153 },
   { 0.31740, 0.31740, 0.31740 },
   { 0.33327, 0.33327, 0.33327 },
   { 0.34914, 0.34914, 0.34914 },
   { 0.36501, 0.36501, 0.36501 },
   { 0.38088, 0.38088, 0.38088 },
   { 0.39675, 0.39675, 0.39675 },
   { 0.41262, 0.41262, 0.41262 },
   { 0.42849, 0.42849, 0.42849 },
   { 0.44436, 0.44436, 0.44436 },
   { 0.46023, 0.46023, 0.46023 },
   { 0.47610, 0.47610, 0.47610 },
   { 0.49197, 0.49197, 0.49197 },
   { 0.50784, 0.50784, 0.50784 },
   { 0.52371, 0.52371, 0.52371 },
   { 0.53958, 0.53958, 0.53958 },
   { 0.55545, 0.55545, 0.55545 },
   { 0.57132, 0.57132, 0.57132 },
   { 0.58719, 0.58719, 0.58719 },
   { 0.60306, 0.60306, 0.60306 },
   { 0.61893, 0.61893, 0.61893 },
   { 0.63480, 0.63480, 0.63480 },
   { 0.65067, 0.65067, 0.65067 },
   { 0.66654, 0.66654, 0.66654 },
   { 0.68241, 0.68241, 0.68241 },
   { 0.69828, 0.69828, 0.69828 },
   { 0.71415, 0.71415, 0.71415 },
   { 0.73002, 0.73002, 0.73002 },
   { 0.74589, 0.74589, 0.74589 },
   { 0.76176, 0.76176, 0.76176 },
   { 0.77763, 0.77763, 0.77763 },
   { 0.79350, 0.79350, 0.79350 },
   { 0.80937, 0.80937, 0.80937 },
   { 0.82524, 0.82524, 0.82524 },
   { 0.84111, 0.84111, 0.84111 },
   { 0.85698, 0.85698, 0.85698 },
   { 0.87285, 0.87285, 0.87285 },
   { 0.88872, 0.88872, 0.88872 },
   { 0.90459, 0.90459, 0.90459 },
   { 0.92046, 0.92046, 0.92046 },
   { 0.93633, 0.93633, 0.93633 },
   { 0.95220, 0.95220, 0.95220 },
   { 0.96807, 0.96807, 0.96807 },
   { 0.98394, 0.98394, 0.98394 },
   { 0.99981, 0.99981, 0.99981 },
   { 0.00000, 0.00000, 0.99981 },
   { 0.00000, 0.01587, 0.98394 },
   { 0.00000, 0.03174, 0.96807 },
   { 0.00000, 0.04761, 0.95220 },
   { 0.00000, 0.06348, 0.93633 },
   { 0.00000, 0.07935, 0.92046 },
   { 0.00000, 0.09522, 0.90459 },
   { 0.00000, 0.11109, 0.88872 },
   { 0.00000, 0.12696, 0.87285 },
   { 0.00000, 0.14283, 0.85698 },
   { 0.00000, 0.15870, 0.84111 },
   { 0.00000, 0.17457, 0.82524 },
   { 0.00000, 0.19044, 0.80937 },
   { 0.00000, 0.20631, 0.79350 },
   { 0.00000, 0.22218, 0.77763 },
   { 0.00000, 0.23805, 0.76176 },
   { 0.00000, 0.25392, 0.74589 },
   { 0.00000, 0.26979, 0.73002 },
   { 0.00000, 0.28566, 0.71415 },
   { 0.00000, 0.30153, 0.69828 },
   { 0.00000, 0.31740, 0.68241 },
   { 0.00000, 0.33327, 0.66654 },
   { 0.00000, 0.34914, 0.65067 },
   { 0.00000, 0.36501, 0.63480 },
   { 0.00000, 0.38088, 0.61893 },
   { 0.00000, 0.39675, 0.60306 },
   { 0.00000, 0.41262, 0.58719 },
   { 0.00000, 0.42849, 0.57132 },
   { 0.00000, 0.44436, 0.55545 },
   { 0.00000, 0.46023, 0.53958 },
   { 0.00000, 0.47610, 0.52371 },
   { 0.00000, 0.49197, 0.50784 },
   { 0.00000, 0.50784, 0.49197 },
   { 0.00000, 0.52371, 0.47610 },
   { 0.00000, 0.53958, 0.46023 },
   { 0.00000, 0.55545, 0.44436 },
   { 0.00000, 0.57132, 0.42849 },
   { 0.00000, 0.58719, 0.41262 },
   { 0.00000, 0.60306, 0.39675 },
   { 0.00000, 0.61893, 0.38088 },
   { 0.00000, 0.63480, 0.36501 },
   { 0.00000, 0.65067, 0.34914 },
   { 0.00000, 0.66654, 0.33327 },
   { 0.00000, 0.68241, 0.31740 },
   { 0.00000, 0.69828, 0.30153 },
   { 0.00000, 0.71415, 0.28566 },
   { 0.00000, 0.73002, 0.26979 },
   { 0.00000, 0.74589, 0.25392 },
   { 0.00000, 0.76176, 0.23805 },
   { 0.00000, 0.77763, 0.22218 },
   { 0.00000, 0.79350, 0.20631 },
   { 0.00000, 0.80937, 0.19044 },
   { 0.00000, 0.82524, 0.17457 },
   { 0.00000, 0.84111, 0.15870 },
   { 0.00000, 0.85698, 0.14283 },
   { 0.00000, 0.87285, 0.12696 },
   { 0.00000, 0.88872, 0.11109 },
   { 0.00000, 0.90459, 0.09522 },
   { 0.00000, 0.92046, 0.07935 },
   { 0.00000, 0.93633, 0.06348 },
   { 0.00000, 0.95220, 0.04761 },
   { 0.00000, 0.96807, 0.03174 },
   { 0.00000, 0.98394, 0.01587 },
   { 0.00000, 0.99981, 0.00000 },
   { 0.00000, 1.00000, 0.00000 },
   { 0.01587, 1.00000, 0.00000 },
   { 0.03174, 1.00000, 0.00000 },
   { 0.04761, 1.00000, 0.00000 },
   { 0.06348, 1.00000, 0.00000 },
   { 0.07935, 1.00000, 0.00000 },
   { 0.09522, 1.00000, 0.00000 },
   { 0.11109, 1.00000, 0.00000 },
   { 0.12696, 1.00000, 0.00000 },
   { 0.14283, 1.00000, 0.00000 },
   { 0.15870, 1.00000, 0.00000 },
   { 0.17457, 1.00000, 0.00000 },
   { 0.19044, 1.00000, 0.00000 },
   { 0.20631, 1.00000, 0.00000 },
   { 0.22218, 1.00000, 0.00000 },
   { 0.23805, 1.00000, 0.00000 },
   { 0.25392, 1.00000, 0.00000 },
   { 0.26979, 1.00000, 0.00000 },
   { 0.28566, 1.00000, 0.00000 },
   { 0.30153, 1.00000, 0.00000 },
   { 0.31740, 1.00000, 0.00000 },
   { 0.33327, 1.00000, 0.00000 },
   { 0.34914, 1.00000, 0.00000 },
   { 0.36501, 1.00000, 0.00000 },
   { 0.38088, 1.00000, 0.00000 },
   { 0.39675, 1.00000, 0.00000 },
   { 0.41262, 1.00000, 0.00000 },
   { 0.42849, 1.00000, 0.00000 },
   { 0.44436, 1.00000, 0.00000 },
   { 0.46023, 1.00000, 0.00000 },
   { 0.47610, 1.00000, 0.00000 },
   { 0.49197, 1.00000, 0.00000 },
   { 0.50784, 1.00000, 0.00000 },
   { 0.52371, 1.00000, 0.00000 },
   { 0.53958, 1.00000, 0.00000 },
   { 0.55545, 1.00000, 0.00000 },
   { 0.57132, 1.00000, 0.00000 },
   { 0.58719, 1.00000, 0.00000 },
   { 0.60306, 1.00000, 0.00000 },
   { 0.61893, 1.00000, 0.00000 },
   { 0.63480, 1.00000, 0.00000 },
   { 0.65067, 1.00000, 0.00000 },
   { 0.66654, 1.00000, 0.00000 },
   { 0.68241, 1.00000, 0.00000 },
   { 0.69828, 1.00000, 0.00000 },
   { 0.71415, 1.00000, 0.00000 },
   { 0.73002, 1.00000, 0.00000 },
   { 0.74589, 1.00000, 0.00000 },
   { 0.76176, 1.00000, 0.00000 },
   { 0.77763, 1.00000, 0.00000 },
   { 0.79350, 1.00000, 0.00000 },
   { 0.80937, 1.00000, 0.00000 },
   { 0.82524, 1.00000, 0.00000 },
   { 0.84111, 1.00000, 0.00000 },
   { 0.85698, 1.00000, 0.00000 },
   { 0.87285, 1.00000, 0.00000 },
   { 0.88872, 1.00000, 0.00000 },
   { 0.90459, 1.00000, 0.00000 },
   { 0.92046, 1.00000, 0.00000 },
   { 0.93633, 1.00000, 0.00000 },
   { 0.95220, 1.00000, 0.00000 },
   { 0.96807, 1.00000, 0.00000 },
   { 0.98394, 1.00000, 0.00000 },
   { 0.99981, 1.00000, 0.00000 },
   { 1.00000, 0.99981, 0.00000 },
   { 1.00000, 0.98394, 0.00000 },
   { 1.00000, 0.96807, 0.00000 },
   { 1.00000, 0.95220, 0.00000 },
   { 1.00000, 0.93633, 0.00000 },
   { 1.00000, 0.92046, 0.00000 },
   { 1.00000, 0.90459, 0.00000 },
   { 1.00000, 0.88872, 0.00000 },
   { 1.00000, 0.87285, 0.00000 },
   { 1.00000, 0.85698, 0.00000 },
   { 1.00000, 0.84111, 0.00000 },
   { 1.00000, 0.82524, 0.00000 },
   { 1.00000, 0.80937, 0.00000 },
   { 1.00000, 0.79350, 0.00000 },
   { 1.00000, 0.77763, 0.00000 },
   { 1.00000, 0.76176, 0.00000 },
   { 1.00000, 0.74589, 0.00000 },
   { 1.00000, 0.73002, 0.00000 },
   { 1.00000, 0.71415, 0.00000 },
   { 1.00000, 0.69828, 0.00000 },
   { 1.00000, 0.68241, 0.00000 },
   { 1.00000, 0.66654, 0.00000 },
   { 1.00000, 0.65067, 0.00000 },
   { 1.00000, 0.63480, 0.00000 },
   { 1.00000, 0.61893, 0.00000 },
   { 1.00000, 0.60306, 0.00000 },
   { 1.00000, 0.58719, 0.00000 },
   { 1.00000, 0.57132, 0.00000 },
   { 1.00000, 0.55545, 0.00000 },
   { 1.00000, 0.53958, 0.00000 },
   { 1.00000, 0.52371, 0.00000 },
   { 1.00000, 0.50784, 0.00000 },
   { 1.00000, 0.49197, 0.00000 },
   { 1.00000, 0.47610, 0.00000 },
   { 1.00000, 0.46023, 0.00000 },
   { 1.00000, 0.44436, 0.00000 },
   { 1.00000, 0.42849, 0.00000 },
   { 1.00000, 0.41262, 0.00000 },
   { 1.00000, 0.39675, 0.00000 },
   { 1.00000, 0.38088, 0.00000 },
   { 1.00000, 0.36501, 0.00000 },
   { 1.00000, 0.34914, 0.00000 },
   { 1.00000, 0.33327, 0.00000 },
   { 1.00000, 0.31740, 0.00000 },
   { 1.00000, 0.30153, 0.00000 },
   { 1.00000, 0.28566, 0.00000 },
   { 1.00000, 0.26979, 0.00000 },
   { 1.00000, 0.25392, 0.00000 },
   { 1.00000, 0.23805, 0.00000 },
   { 1.00000, 0.22218, 0.00000 },
   { 1.00000, 0.20631, 0.00000 },
   { 1.00000, 0.19044, 0.00000 },
   { 1.00000, 0.17457, 0.00000 },
   { 1.00000, 0.15870, 0.00000 },
   { 1.00000, 0.14283, 0.00000 },
   { 1.00000, 0.12696, 0.00000 },
   { 1.00000, 0.11109, 0.00000 },
   { 1.00000, 0.09522, 0.00000 },
   { 1.00000, 0.07935, 0.00000 },
   { 1.00000, 0.06348, 0.00000 },
   { 1.00000, 0.04761, 0.00000 },
   { 1.00000, 0.03174, 0.00000 },
   { 1.00000, 0.01587, 0.00000 },
   { 1.00000, 0.00000, 0.00000 }
};

static	Color_struct	heat[] = {
   { 0.00000, 0.00000, 0.00000 },
   { 0.01176, 0.00392, 0.00000 },
   { 0.02353, 0.00784, 0.00000 },
   { 0.03529, 0.01176, 0.00000 },
   { 0.04706, 0.01569, 0.00000 },
   { 0.05882, 0.01961, 0.00000 },
   { 0.07059, 0.02353, 0.00000 },
   { 0.08235, 0.02745, 0.00000 },
   { 0.09412, 0.03137, 0.00000 },
   { 0.10588, 0.03529, 0.00000 },
   { 0.11765, 0.03922, 0.00000 },
   { 0.12941, 0.04314, 0.00000 },
   { 0.14118, 0.04706, 0.00000 },
   { 0.15294, 0.05098, 0.00000 },
   { 0.16471, 0.05490, 0.00000 },
   { 0.17647, 0.05882, 0.00000 },
   { 0.18824, 0.06275, 0.00000 },
   { 0.20000, 0.06667, 0.00000 },
   { 0.21176, 0.07059, 0.00000 },
   { 0.22353, 0.07451, 0.00000 },
   { 0.23529, 0.07843, 0.00000 },
   { 0.24706, 0.08235, 0.00000 },
   { 0.25882, 0.08627, 0.00000 },
   { 0.27059, 0.09020, 0.00000 },
   { 0.28235, 0.09412, 0.00000 },
   { 0.29412, 0.09804, 0.00000 },
   { 0.30588, 0.10196, 0.00000 },
   { 0.31765, 0.10588, 0.00000 },
   { 0.32941, 0.10980, 0.00000 },
   { 0.34118, 0.11373, 0.00000 },
   { 0.35294, 0.11765, 0.00000 },
   { 0.36471, 0.12157, 0.00000 },
   { 0.37647, 0.12549, 0.00000 },
   { 0.38824, 0.12941, 0.00000 },
   { 0.40000, 0.13333, 0.00000 },
   { 0.41176, 0.13725, 0.00000 },
   { 0.42353, 0.14118, 0.00000 },
   { 0.43529, 0.14510, 0.00000 },
   { 0.44706, 0.14902, 0.00000 },
   { 0.45882, 0.15294, 0.00000 },
   { 0.47059, 0.15686, 0.00000 },
   { 0.48235, 0.16078, 0.00000 },
   { 0.49412, 0.16471, 0.00000 },
   { 0.50588, 0.16863, 0.00000 },
   { 0.51765, 0.17255, 0.00000 },
   { 0.52941, 0.17647, 0.00000 },
   { 0.54118, 0.18039, 0.00000 },
   { 0.55294, 0.18431, 0.00000 },
   { 0.56471, 0.18824, 0.00000 },
   { 0.57647, 0.19216, 0.00000 },
   { 0.58824, 0.19608, 0.00000 },
   { 0.60000, 0.20000, 0.00000 },
   { 0.61176, 0.20392, 0.00000 },
   { 0.62353, 0.20784, 0.00000 },
   { 0.63529, 0.21176, 0.00000 },
   { 0.64706, 0.21569, 0.00000 },
   { 0.65882, 0.21961, 0.00000 },
   { 0.67059, 0.22353, 0.00000 },
   { 0.68235, 0.22745, 0.00000 },
   { 0.69412, 0.23137, 0.00000 },
   { 0.70588, 0.23529, 0.00000 },
   { 0.71765, 0.23922, 0.00000 },
   { 0.72941, 0.24314, 0.00000 },
   { 0.74118, 0.24706, 0.00000 },
   { 0.75294, 0.25098, 0.00000 },
   { 0.76471, 0.25490, 0.00000 },
   { 0.77647, 0.25882, 0.00000 },
   { 0.78824, 0.26275, 0.00000 },
   { 0.80000, 0.26667, 0.00000 },
   { 0.81176, 0.27059, 0.00000 },
   { 0.82353, 0.27451, 0.00000 },
   { 0.83529, 0.27843, 0.00000 },
   { 0.84706, 0.28235, 0.00000 },
   { 0.85882, 0.28627, 0.00000 },
   { 0.87059, 0.29020, 0.00000 },
   { 0.88235, 0.29412, 0.00000 },
   { 0.89412, 0.29804, 0.00000 },
   { 0.90588, 0.30196, 0.00000 },
   { 0.91765, 0.30588, 0.00000 },
   { 0.92941, 0.30980, 0.00000 },
   { 0.94118, 0.31373, 0.00000 },
   { 0.95294, 0.31765, 0.00000 },
   { 0.96471, 0.32157, 0.00000 },
   { 0.97647, 0.32549, 0.00000 },
   { 0.98824, 0.32941, 0.00000 },
   { 1.00000, 0.33333, 0.00000 },
   { 1.00000, 0.33725, 0.00000 },
   { 1.00000, 0.34118, 0.00000 },
   { 1.00000, 0.34510, 0.00000 },
   { 1.00000, 0.34902, 0.00000 },
   { 1.00000, 0.35294, 0.00000 },
   { 1.00000, 0.35686, 0.00000 },
   { 1.00000, 0.36078, 0.00000 },
   { 1.00000, 0.36471, 0.00000 },
   { 1.00000, 0.36863, 0.00000 },
   { 1.00000, 0.37255, 0.00000 },
   { 1.00000, 0.37647, 0.00000 },
   { 1.00000, 0.38039, 0.00000 },
   { 1.00000, 0.38431, 0.00000 },
   { 1.00000, 0.38824, 0.00000 },
   { 1.00000, 0.39216, 0.00000 },
   { 1.00000, 0.39608, 0.00000 },
   { 1.00000, 0.40000, 0.00000 },
   { 1.00000, 0.40392, 0.00000 },
   { 1.00000, 0.40784, 0.00000 },
   { 1.00000, 0.41176, 0.00000 },
   { 1.00000, 0.41569, 0.00000 },
   { 1.00000, 0.41961, 0.00000 },
   { 1.00000, 0.42353, 0.00000 },
   { 1.00000, 0.42745, 0.00000 },
   { 1.00000, 0.43137, 0.00000 },
   { 1.00000, 0.43529, 0.00000 },
   { 1.00000, 0.43922, 0.00000 },
   { 1.00000, 0.44314, 0.00000 },
   { 1.00000, 0.44706, 0.00000 },
   { 1.00000, 0.45098, 0.00000 },
   { 1.00000, 0.45490, 0.00000 },
   { 1.00000, 0.45882, 0.00000 },
   { 1.00000, 0.46275, 0.00000 },
   { 1.00000, 0.46667, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47451, 0.00000 },
   { 1.00000, 0.47843, 0.00000 },
   { 1.00000, 0.48235, 0.00000 },
   { 1.00000, 0.48627, 0.00000 },
   { 1.00000, 0.49020, 0.00000 },
   { 1.00000, 0.49412, 0.00000 },
   { 1.00000, 0.49804, 0.00000 },
   { 1.00000, 0.50196, 0.00000 },
   { 1.00000, 0.50588, 0.00000 },
   { 1.00000, 0.50980, 0.00000 },
   { 1.00000, 0.51373, 0.00000 },
   { 1.00000, 0.51765, 0.00000 },
   { 1.00000, 0.52157, 0.00000 },
   { 1.00000, 0.52549, 0.00000 },
   { 1.00000, 0.52941, 0.00000 },
   { 1.00000, 0.53333, 0.00000 },
   { 1.00000, 0.53725, 0.00000 },
   { 1.00000, 0.54118, 0.00000 },
   { 1.00000, 0.54510, 0.00000 },
   { 1.00000, 0.54902, 0.00000 },
   { 1.00000, 0.55294, 0.00000 },
   { 1.00000, 0.55686, 0.00000 },
   { 1.00000, 0.56078, 0.00000 },
   { 1.00000, 0.56471, 0.00000 },
   { 1.00000, 0.56863, 0.00000 },
   { 1.00000, 0.57255, 0.00000 },
   { 1.00000, 0.57647, 0.00000 },
   { 1.00000, 0.58039, 0.00000 },
   { 1.00000, 0.58431, 0.00000 },
   { 1.00000, 0.58824, 0.00000 },
   { 1.00000, 0.59216, 0.00000 },
   { 1.00000, 0.59608, 0.00000 },
   { 1.00000, 0.60000, 0.00000 },
   { 1.00000, 0.60392, 0.00000 },
   { 1.00000, 0.60784, 0.00000 },
   { 1.00000, 0.61176, 0.00000 },
   { 1.00000, 0.61569, 0.00000 },
   { 1.00000, 0.61961, 0.00000 },
   { 1.00000, 0.62353, 0.00000 },
   { 1.00000, 0.62745, 0.00000 },
   { 1.00000, 0.63137, 0.00000 },
   { 1.00000, 0.63529, 0.00000 },
   { 1.00000, 0.63922, 0.00000 },
   { 1.00000, 0.64314, 0.00000 },
   { 1.00000, 0.64706, 0.00000 },
   { 1.00000, 0.65098, 0.01176 },
   { 1.00000, 0.65490, 0.02353 },
   { 1.00000, 0.65882, 0.03529 },
   { 1.00000, 0.66275, 0.04706 },
   { 1.00000, 0.66667, 0.05882 },
   { 1.00000, 0.67059, 0.07059 },
   { 1.00000, 0.67451, 0.08235 },
   { 1.00000, 0.67843, 0.09412 },
   { 1.00000, 0.68235, 0.10588 },
   { 1.00000, 0.68627, 0.11765 },
   { 1.00000, 0.69020, 0.12941 },
   { 1.00000, 0.69412, 0.14118 },
   { 1.00000, 0.69804, 0.15294 },
   { 1.00000, 0.70196, 0.16471 },
   { 1.00000, 0.70588, 0.17647 },
   { 1.00000, 0.70980, 0.18824 },
   { 1.00000, 0.71373, 0.20000 },
   { 1.00000, 0.71765, 0.21176 },
   { 1.00000, 0.72157, 0.22353 },
   { 1.00000, 0.72549, 0.23529 },
   { 1.00000, 0.72941, 0.24706 },
   { 1.00000, 0.73333, 0.25882 },
   { 1.00000, 0.73725, 0.27059 },
   { 1.00000, 0.74118, 0.28235 },
   { 1.00000, 0.74510, 0.29412 },
   { 1.00000, 0.74902, 0.30588 },
   { 1.00000, 0.75294, 0.31765 },
   { 1.00000, 0.75686, 0.32941 },
   { 1.00000, 0.76078, 0.34118 },
   { 1.00000, 0.76471, 0.35294 },
   { 1.00000, 0.76863, 0.36471 },
   { 1.00000, 0.77255, 0.37647 },
   { 1.00000, 0.77647, 0.38824 },
   { 1.00000, 0.78039, 0.40000 },
   { 1.00000, 0.78431, 0.41176 },
   { 1.00000, 0.78824, 0.42353 },
   { 1.00000, 0.79216, 0.43529 },
   { 1.00000, 0.79608, 0.44706 },
   { 1.00000, 0.80000, 0.45882 },
   { 1.00000, 0.80392, 0.47059 },
   { 1.00000, 0.80784, 0.48235 },
   { 1.00000, 0.81176, 0.49412 },
   { 1.00000, 0.81569, 0.50588 },
   { 1.00000, 0.81961, 0.51765 },
   { 1.00000, 0.82353, 0.52941 },
   { 1.00000, 0.82745, 0.54118 },
   { 1.00000, 0.83137, 0.55294 },
   { 1.00000, 0.83529, 0.56471 },
   { 1.00000, 0.83922, 0.57647 },
   { 1.00000, 0.84314, 0.58824 },
   { 1.00000, 0.84706, 0.60000 },
   { 1.00000, 0.85098, 0.61176 },
   { 1.00000, 0.85490, 0.62353 },
   { 1.00000, 0.85882, 0.63529 },
   { 1.00000, 0.86275, 0.64706 },
   { 1.00000, 0.86667, 0.65882 },
   { 1.00000, 0.87059, 0.67059 },
   { 1.00000, 0.87451, 0.68235 },
   { 1.00000, 0.87843, 0.69412 },
   { 1.00000, 0.88235, 0.70588 },
   { 1.00000, 0.88627, 0.71765 },
   { 1.00000, 0.89020, 0.72941 },
   { 1.00000, 0.89412, 0.74118 },
   { 1.00000, 0.89804, 0.75294 },
   { 1.00000, 0.90196, 0.76471 },
   { 1.00000, 0.90588, 0.77647 },
   { 1.00000, 0.90980, 0.78824 },
   { 1.00000, 0.91373, 0.80000 },
   { 1.00000, 0.91765, 0.81176 },
   { 1.00000, 0.92157, 0.82353 },
   { 1.00000, 0.92549, 0.83529 },
   { 1.00000, 0.92941, 0.84706 },
   { 1.00000, 0.93333, 0.85882 },
   { 1.00000, 0.93725, 0.87059 },
   { 1.00000, 0.94118, 0.88235 },
   { 1.00000, 0.94510, 0.89412 },
   { 1.00000, 0.94902, 0.90588 },
   { 1.00000, 0.95294, 0.91765 },
   { 1.00000, 0.95686, 0.92941 },
   { 1.00000, 0.96078, 0.94118 },
   { 1.00000, 0.96471, 0.95294 },
   { 1.00000, 0.96863, 0.96471 },
   { 1.00000, 0.97255, 0.97647 },
   { 1.00000, 0.97647, 0.98824 },
   { 1.00000, 0.98039, 1.00000 },
   { 1.00000, 0.98431, 1.00000 },
   { 1.00000, 0.98824, 1.00000 },
   { 1.00000, 0.99216, 1.00000 },
   { 1.00000, 0.99608, 1.00000 },
   { 1.00000, 1.00000, 1.00000 }
};

static	Color_struct	isophot[] = {
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.03922 },
   { 0.00000, 0.00000, 0.07843 },
   { 0.00000, 0.00000, 0.11765 },
   { 0.00000, 0.00000, 0.15686 },
   { 0.00000, 0.00000, 0.19608 },
   { 0.00000, 0.00000, 0.23529 },
   { 0.00000, 0.00000, 0.27843 },
   { 0.00000, 0.00000, 0.31765 },
   { 0.00000, 0.00000, 0.35686 },
   { 0.00000, 0.00000, 0.39608 },
   { 0.00000, 0.00000, 0.43529 },
   { 0.00000, 0.00000, 0.47451 },
   { 0.00000, 0.00000, 0.51765 },
   { 0.00000, 0.00000, 0.55686 },
   { 0.00000, 0.00000, 0.59608 },
   { 0.00000, 0.00000, 0.63529 },
   { 0.00000, 0.00000, 0.67451 },
   { 0.00000, 0.00000, 0.71765 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 0.00000, 0.00000, 0.87843 },
   { 0.00000, 0.00000, 0.91765 },
   { 0.00000, 0.00000, 0.95686 },
   { 0.00000, 0.00000, 1.00000 },
   { 0.00000, 0.03137, 1.00000 },
   { 0.00000, 0.06275, 1.00000 },
   { 0.00000, 0.09412, 1.00000 },
   { 0.00000, 0.12549, 1.00000 },
   { 0.00000, 0.15686, 1.00000 },
   { 0.00000, 0.18824, 1.00000 },
   { 0.00000, 0.21961, 1.00000 },
   { 0.00000, 0.25490, 1.00000 },
   { 0.00000, 0.28627, 1.00000 },
   { 0.00000, 0.31765, 1.00000 },
   { 0.00000, 0.34902, 1.00000 },
   { 0.00000, 0.38039, 1.00000 },
   { 0.00000, 0.41176, 1.00000 },
   { 0.00000, 0.44314, 1.00000 },
   { 0.00000, 0.47843, 1.00000 },
   { 0.00000, 0.49804, 1.00000 },
   { 0.00000, 0.51765, 1.00000 },
   { 0.00000, 0.53725, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 0.00000, 0.61961, 1.00000 },
   { 0.00000, 0.63922, 1.00000 },
   { 0.00000, 0.65882, 1.00000 },
   { 0.00000, 0.67843, 1.00000 },
   { 0.00000, 0.70196, 1.00000 },
   { 0.00000, 0.72157, 1.00000 },
   { 0.00000, 0.74118, 1.00000 },
   { 0.00000, 0.76078, 1.00000 },
   { 0.00000, 0.78431, 1.00000 },
   { 0.00000, 0.79608, 1.00000 },
   { 0.00000, 0.81176, 1.00000 },
   { 0.00000, 0.82353, 1.00000 },
   { 0.00000, 0.83922, 1.00000 },
   { 0.00000, 0.85490, 1.00000 },
   { 0.00000, 0.86667, 1.00000 },
   { 0.00000, 0.88235, 1.00000 },
   { 0.00000, 0.89412, 1.00000 },
   { 0.00000, 0.90980, 1.00000 },
   { 0.00000, 0.92549, 1.00000 },
   { 0.00000, 0.93725, 1.00000 },
   { 0.00000, 0.95294, 1.00000 },
   { 0.00000, 0.96863, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 0.00000, 1.00000, 0.96078 },
   { 0.00000, 1.00000, 0.94118 },
   { 0.00000, 1.00000, 0.92157 },
   { 0.00000, 1.00000, 0.90196 },
   { 0.00000, 1.00000, 0.88235 },
   { 0.00000, 1.00000, 0.86275 },
   { 0.00000, 1.00000, 0.84314 },
   { 0.00000, 1.00000, 0.82353 },
   { 0.00000, 1.00000, 0.80392 },
   { 0.00000, 1.00000, 0.78431 },
   { 0.00000, 1.00000, 0.76471 },
   { 0.00000, 1.00000, 0.74510 },
   { 0.00000, 1.00000, 0.72549 },
   { 0.00000, 1.00000, 0.70588 },
   { 0.00000, 1.00000, 0.65490 },
   { 0.00000, 1.00000, 0.60784 },
   { 0.00000, 1.00000, 0.56078 },
   { 0.00000, 1.00000, 0.51373 },
   { 0.00000, 1.00000, 0.46667 },
   { 0.00000, 1.00000, 0.41961 },
   { 0.00000, 1.00000, 0.37255 },
   { 0.00000, 1.00000, 0.32549 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 0.00000, 1.00000, 0.13725 },
   { 0.00000, 1.00000, 0.09020 },
   { 0.00000, 1.00000, 0.04314 },
   { 0.00000, 1.00000, 0.00000 },
   { 0.04706, 1.00000, 0.00000 },
   { 0.09412, 1.00000, 0.00000 },
   { 0.14118, 1.00000, 0.00000 },
   { 0.18824, 1.00000, 0.00000 },
   { 0.23529, 1.00000, 0.00000 },
   { 0.28235, 1.00000, 0.00000 },
   { 0.32941, 1.00000, 0.00000 },
   { 0.37647, 1.00000, 0.00000 },
   { 0.42353, 1.00000, 0.00000 },
   { 0.47059, 1.00000, 0.00000 },
   { 0.51765, 1.00000, 0.00000 },
   { 0.56471, 1.00000, 0.00000 },
   { 0.61176, 1.00000, 0.00000 },
   { 0.65882, 1.00000, 0.00000 },
   { 0.70588, 1.00000, 0.00000 },
   { 0.72549, 1.00000, 0.00000 },
   { 0.74510, 1.00000, 0.00000 },
   { 0.76471, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 0.84314, 1.00000, 0.00000 },
   { 0.86275, 1.00000, 0.00000 },
   { 0.88235, 1.00000, 0.00000 },
   { 0.90196, 1.00000, 0.00000 },
   { 0.92157, 1.00000, 0.00000 },
   { 0.94118, 1.00000, 0.00000 },
   { 0.96078, 1.00000, 0.00000 },
   { 0.98039, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 0.99608, 0.98039, 0.00000 },
   { 0.99608, 0.96078, 0.00000 },
   { 0.99608, 0.94118, 0.00000 },
   { 0.99608, 0.92549, 0.00000 },
   { 0.99216, 0.90588, 0.00000 },
   { 0.99216, 0.88627, 0.00000 },
   { 0.99216, 0.87059, 0.00000 },
   { 0.99216, 0.85098, 0.00000 },
   { 0.98824, 0.83137, 0.00000 },
   { 0.98824, 0.81569, 0.00000 },
   { 0.98824, 0.79608, 0.00000 },
   { 0.98824, 0.77647, 0.00000 },
   { 0.98824, 0.76078, 0.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 0.98824, 0.69020, 0.00000 },
   { 0.98824, 0.67059, 0.00000 },
   { 0.98824, 0.65490, 0.00000 },
   { 0.98824, 0.63922, 0.00000 },
   { 0.98824, 0.61961, 0.00000 },
   { 0.99216, 0.60392, 0.00000 },
   { 0.99216, 0.58824, 0.00000 },
   { 0.99216, 0.56863, 0.00000 },
   { 0.99216, 0.55294, 0.00000 },
   { 0.99608, 0.53725, 0.00000 },
   { 0.99608, 0.51765, 0.00000 },
   { 0.99608, 0.50196, 0.00000 },
   { 0.99608, 0.48627, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.43529, 0.00000 },
   { 1.00000, 0.40392, 0.00000 },
   { 1.00000, 0.37255, 0.00000 },
   { 1.00000, 0.34118, 0.00000 },
   { 1.00000, 0.30980, 0.00000 },
   { 1.00000, 0.27843, 0.00000 },
   { 1.00000, 0.24706, 0.00000 },
   { 1.00000, 0.21569, 0.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 0.09020, 0.00000 },
   { 1.00000, 0.05882, 0.00000 },
   { 1.00000, 0.02745, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.04706 },
   { 1.00000, 0.00000, 0.09412 },
   { 1.00000, 0.00000, 0.14118 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 0.00000, 0.32941 },
   { 1.00000, 0.00000, 0.37647 },
   { 1.00000, 0.00000, 0.42353 },
   { 1.00000, 0.00000, 0.47059 },
   { 1.00000, 0.00000, 0.51765 },
   { 1.00000, 0.00000, 0.56471 },
   { 1.00000, 0.00000, 0.61176 },
   { 1.00000, 0.00000, 0.65882 },
   { 1.00000, 0.00000, 0.70588 },
   { 1.00000, 0.00000, 0.72549 },
   { 1.00000, 0.00000, 0.74902 },
   { 1.00000, 0.00000, 0.77255 },
   { 1.00000, 0.00000, 0.79608 },
   { 1.00000, 0.00000, 0.81569 },
   { 1.00000, 0.00000, 0.83922 },
   { 1.00000, 0.00000, 0.86275 },
   { 1.00000, 0.00000, 0.88627 },
   { 1.00000, 0.00000, 0.90588 },
   { 1.00000, 0.00000, 0.92941 },
   { 1.00000, 0.00000, 0.95294 },
   { 1.00000, 0.00000, 0.97647 },
   { 1.00000, 0.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 0.14118, 1.00000 },
   { 1.00000, 0.17647, 1.00000 },
   { 1.00000, 0.21176, 1.00000 },
   { 1.00000, 0.25098, 1.00000 },
   { 1.00000, 0.28627, 1.00000 },
   { 1.00000, 0.32157, 1.00000 },
   { 1.00000, 0.36078, 1.00000 },
   { 1.00000, 0.39608, 1.00000 },
   { 1.00000, 0.43137, 1.00000 },
   { 1.00000, 0.47059, 1.00000 },
   { 1.00000, 0.48627, 1.00000 },
   { 1.00000, 0.50588, 1.00000 },
   { 1.00000, 0.52157, 1.00000 },
   { 1.00000, 0.54118, 1.00000 },
   { 1.00000, 0.56078, 1.00000 },
   { 1.00000, 0.57647, 1.00000 },
   { 1.00000, 0.59608, 1.00000 },
   { 1.00000, 0.61176, 1.00000 },
   { 1.00000, 0.63137, 1.00000 },
   { 1.00000, 0.65098, 1.00000 },
   { 1.00000, 0.66667, 1.00000 },
   { 1.00000, 0.68627, 1.00000 },
   { 1.00000, 0.70588, 1.00000 },
   { 1.00000, 0.74510, 1.00000 },
   { 1.00000, 0.78824, 1.00000 },
   { 1.00000, 0.83137, 1.00000 },
   { 1.00000, 0.87059, 1.00000 },
   { 1.00000, 0.91373, 1.00000 },
   { 1.00000, 0.95686, 1.00000 },
   { 1.00000, 1.00000, 1.00000 }
};

static	Color_struct	mousse[] = {
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.06667 },
   { 0.00000, 0.00000, 0.06667 },
   { 0.00000, 0.00000, 0.06667 },
   { 0.00000, 0.00000, 0.06667 },
   { 0.00000, 0.00000, 0.06667 },
   { 0.00000, 0.00000, 0.06667 },
   { 0.00000, 0.00000, 0.06667 },
   { 0.00000, 0.00000, 0.06667 },
   { 0.00000, 0.00000, 0.13333 },
   { 0.00000, 0.00000, 0.13333 },
   { 0.00000, 0.00000, 0.13333 },
   { 0.00000, 0.00000, 0.13333 },
   { 0.00000, 0.00000, 0.13333 },
   { 0.00000, 0.00000, 0.13333 },
   { 0.00000, 0.00000, 0.13333 },
   { 0.00000, 0.00000, 0.13333 },
   { 0.00000, 0.00000, 0.20000 },
   { 0.00000, 0.00000, 0.20000 },
   { 0.00000, 0.00000, 0.20000 },
   { 0.00000, 0.00000, 0.20000 },
   { 0.00000, 0.00000, 0.20000 },
   { 0.00000, 0.00000, 0.20000 },
   { 0.00000, 0.00000, 0.20000 },
   { 0.00000, 0.00000, 0.20000 },
   { 0.00000, 0.00000, 0.26667 },
   { 0.00000, 0.00000, 0.26667 },
   { 0.00000, 0.00000, 0.26667 },
   { 0.00000, 0.00000, 0.26667 },
   { 0.00000, 0.00000, 0.26667 },
   { 0.00000, 0.00000, 0.26667 },
   { 0.00000, 0.00000, 0.26667 },
   { 0.00000, 0.00000, 0.26667 },
   { 0.00000, 0.00000, 0.33333 },
   { 0.00000, 0.00000, 0.33333 },
   { 0.00000, 0.00000, 0.33333 },
   { 0.00000, 0.00000, 0.33333 },
   { 0.00000, 0.00000, 0.33333 },
   { 0.00000, 0.00000, 0.33333 },
   { 0.00000, 0.00000, 0.33333 },
   { 0.00000, 0.00000, 0.33333 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.46667 },
   { 0.00000, 0.00000, 0.46667 },
   { 0.00000, 0.00000, 0.46667 },
   { 0.00000, 0.00000, 0.46667 },
   { 0.00000, 0.00000, 0.46667 },
   { 0.00000, 0.00000, 0.46667 },
   { 0.00000, 0.00000, 0.46667 },
   { 0.00000, 0.00000, 0.46667 },
   { 0.00000, 0.00000, 0.53333 },
   { 0.00000, 0.00000, 0.53333 },
   { 0.00000, 0.00000, 0.53333 },
   { 0.00000, 0.00000, 0.53333 },
   { 0.00000, 0.00000, 0.53333 },
   { 0.00000, 0.00000, 0.53333 },
   { 0.00000, 0.00000, 0.53333 },
   { 0.00000, 0.00000, 0.53333 },
   { 0.06667, 0.00000, 0.53333 },
   { 0.06667, 0.00000, 0.53333 },
   { 0.06667, 0.00000, 0.53333 },
   { 0.06667, 0.00000, 0.53333 },
   { 0.06667, 0.00000, 0.53333 },
   { 0.06667, 0.00000, 0.53333 },
   { 0.06667, 0.00000, 0.53333 },
   { 0.06667, 0.00000, 0.53333 },
   { 0.13333, 0.00000, 0.53333 },
   { 0.13333, 0.00000, 0.53333 },
   { 0.13333, 0.00000, 0.53333 },
   { 0.13333, 0.00000, 0.53333 },
   { 0.13333, 0.00000, 0.53333 },
   { 0.13333, 0.00000, 0.53333 },
   { 0.13333, 0.00000, 0.53333 },
   { 0.13333, 0.00000, 0.53333 },
   { 0.20000, 0.00000, 0.53333 },
   { 0.20000, 0.00000, 0.53333 },
   { 0.20000, 0.00000, 0.53333 },
   { 0.20000, 0.00000, 0.53333 },
   { 0.20000, 0.00000, 0.53333 },
   { 0.20000, 0.00000, 0.53333 },
   { 0.20000, 0.00000, 0.53333 },
   { 0.20000, 0.00000, 0.53333 },
   { 0.26667, 0.00000, 0.53333 },
   { 0.26667, 0.00000, 0.53333 },
   { 0.26667, 0.00000, 0.53333 },
   { 0.26667, 0.00000, 0.53333 },
   { 0.26667, 0.00000, 0.53333 },
   { 0.26667, 0.00000, 0.53333 },
   { 0.26667, 0.00000, 0.53333 },
   { 0.26667, 0.00000, 0.53333 },
   { 0.33333, 0.00000, 0.53333 },
   { 0.33333, 0.00000, 0.53333 },
   { 0.33333, 0.00000, 0.53333 },
   { 0.33333, 0.00000, 0.53333 },
   { 0.33333, 0.00000, 0.53333 },
   { 0.33333, 0.00000, 0.53333 },
   { 0.33333, 0.00000, 0.53333 },
   { 0.33333, 0.00000, 0.53333 },
   { 0.40000, 0.00000, 0.53333 },
   { 0.40000, 0.00000, 0.53333 },
   { 0.40000, 0.00000, 0.53333 },
   { 0.40000, 0.00000, 0.53333 },
   { 0.40000, 0.00000, 0.53333 },
   { 0.40000, 0.00000, 0.53333 },
   { 0.40000, 0.00000, 0.53333 },
   { 0.40000, 0.00000, 0.53333 },
   { 0.46667, 0.00000, 0.53333 },
   { 0.46667, 0.00000, 0.53333 },
   { 0.46667, 0.00000, 0.53333 },
   { 0.46667, 0.00000, 0.53333 },
   { 0.46667, 0.00000, 0.53333 },
   { 0.46667, 0.00000, 0.53333 },
   { 0.46667, 0.00000, 0.53333 },
   { 0.46667, 0.00000, 0.53333 },
   { 0.53333, 0.00000, 0.53333 },
   { 0.53333, 0.00000, 0.53333 },
   { 0.53333, 0.00000, 0.53333 },
   { 0.53333, 0.00000, 0.53333 },
   { 0.53333, 0.00000, 0.46667 },
   { 0.53333, 0.00000, 0.46667 },
   { 0.53333, 0.00000, 0.46667 },
   { 0.53333, 0.00000, 0.46667 },
   { 0.60000, 0.00000, 0.40000 },
   { 0.60000, 0.00000, 0.40000 },
   { 0.60000, 0.00000, 0.40000 },
   { 0.60000, 0.00000, 0.40000 },
   { 0.60000, 0.00000, 0.33333 },
   { 0.60000, 0.00000, 0.33333 },
   { 0.60000, 0.00000, 0.33333 },
   { 0.60000, 0.00000, 0.33333 },
   { 0.66667, 0.00000, 0.26667 },
   { 0.66667, 0.00000, 0.26667 },
   { 0.66667, 0.00000, 0.26667 },
   { 0.66667, 0.00000, 0.26667 },
   { 0.66667, 0.00000, 0.20000 },
   { 0.66667, 0.00000, 0.20000 },
   { 0.66667, 0.00000, 0.20000 },
   { 0.66667, 0.00000, 0.20000 },
   { 0.73333, 0.00000, 0.13333 },
   { 0.73333, 0.00000, 0.13333 },
   { 0.73333, 0.00000, 0.13333 },
   { 0.73333, 0.00000, 0.13333 },
   { 0.73333, 0.00000, 0.06667 },
   { 0.73333, 0.00000, 0.06667 },
   { 0.73333, 0.00000, 0.06667 },
   { 0.73333, 0.00000, 0.06667 },
   { 0.80000, 0.00000, 0.00000 },
   { 0.80000, 0.00000, 0.00000 },
   { 0.80000, 0.00000, 0.00000 },
   { 0.80000, 0.00000, 0.00000 },
   { 0.80000, 0.00000, 0.00000 },
   { 0.80000, 0.00000, 0.00000 },
   { 0.80000, 0.00000, 0.00000 },
   { 0.80000, 0.00000, 0.00000 },
   { 0.86667, 0.00000, 0.00000 },
   { 0.86667, 0.00000, 0.00000 },
   { 0.86667, 0.00000, 0.00000 },
   { 0.86667, 0.00000, 0.00000 },
   { 0.86667, 0.00000, 0.00000 },
   { 0.86667, 0.00000, 0.00000 },
   { 0.86667, 0.00000, 0.00000 },
   { 0.86667, 0.00000, 0.00000 },
   { 0.93333, 0.00000, 0.00000 },
   { 0.93333, 0.00000, 0.00000 },
   { 0.93333, 0.00000, 0.00000 },
   { 0.93333, 0.00000, 0.00000 },
   { 0.93333, 0.00000, 0.00000 },
   { 0.93333, 0.00000, 0.00000 },
   { 0.93333, 0.00000, 0.00000 },
   { 0.93333, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.06667, 0.00000 },
   { 1.00000, 0.06667, 0.00000 },
   { 1.00000, 0.13333, 0.00000 },
   { 1.00000, 0.13333, 0.00000 },
   { 1.00000, 0.20000, 0.00000 },
   { 1.00000, 0.20000, 0.00000 },
   { 1.00000, 0.26667, 0.00000 },
   { 1.00000, 0.26667, 0.00000 },
   { 1.00000, 0.33333, 0.00000 },
   { 1.00000, 0.33333, 0.00000 },
   { 1.00000, 0.40000, 0.00000 },
   { 1.00000, 0.40000, 0.00000 },
   { 1.00000, 0.46667, 0.00000 },
   { 1.00000, 0.46667, 0.00000 },
   { 1.00000, 0.53333, 0.00000 },
   { 1.00000, 0.53333, 0.00000 },
   { 1.00000, 0.60000, 0.00000 },
   { 1.00000, 0.60000, 0.00000 },
   { 1.00000, 0.66667, 0.00000 },
   { 1.00000, 0.66667, 0.00000 },
   { 1.00000, 0.73333, 0.00000 },
   { 1.00000, 0.73333, 0.00000 },
   { 1.00000, 0.80000, 0.00000 },
   { 1.00000, 0.80000, 0.00000 },
   { 1.00000, 0.86667, 0.00000 },
   { 1.00000, 0.86667, 0.00000 },
   { 1.00000, 0.93333, 0.00000 },
   { 1.00000, 0.93333, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.00000 },
   { 1.00000, 1.00000, 0.06667 },
   { 1.00000, 1.00000, 0.06667 },
   { 1.00000, 1.00000, 0.13333 },
   { 1.00000, 1.00000, 0.13333 },
   { 1.00000, 1.00000, 0.20000 },
   { 1.00000, 1.00000, 0.20000 },
   { 1.00000, 1.00000, 0.26667 },
   { 1.00000, 1.00000, 0.26667 },
   { 1.00000, 1.00000, 0.33333 },
   { 1.00000, 1.00000, 0.33333 },
   { 1.00000, 1.00000, 0.40000 },
   { 1.00000, 1.00000, 0.40000 },
   { 1.00000, 1.00000, 0.46667 },
   { 1.00000, 1.00000, 0.46667 },
   { 1.00000, 1.00000, 0.53333 },
   { 1.00000, 1.00000, 0.53333 },
   { 1.00000, 1.00000, 0.60000 },
   { 1.00000, 1.00000, 0.60000 },
   { 1.00000, 1.00000, 0.66667 },
   { 1.00000, 1.00000, 0.66667 },
   { 1.00000, 1.00000, 0.73333 },
   { 1.00000, 1.00000, 0.73333 },
   { 1.00000, 1.00000, 0.80000 },
   { 1.00000, 1.00000, 0.80000 },
   { 1.00000, 1.00000, 0.86667 },
   { 1.00000, 1.00000, 1.00000 }
};

static	Color_struct	ranlut[] = {
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.47059, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.62745, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 0.78431, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 1.00000, 0.00392 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00392, 0.86275, 0.47059 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.78431, 0.62745 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.70588, 0.78431 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.00000, 0.62745, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.47059, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.23529, 0.00392, 1.00000 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.47059, 0.00392, 0.78431 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.62745, 0.00392, 0.62745 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.78431, 0.00392, 0.47059 },
   { 0.90196, 0.11765, 0.23529 },
   { 0.90196, 0.11765, 0.23529 },
   { 0.90196, 0.11765, 0.23529 },
   { 0.90196, 0.11765, 0.23529 },
   { 0.90196, 0.11765, 0.23529 },
   { 0.90196, 0.11765, 0.23529 },
   { 0.90196, 0.11765, 0.23529 },
   { 0.90196, 0.11765, 0.23529 },
   { 0.90196, 0.11765, 0.23529 },
   { 0.90196, 0.11765, 0.23529 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.23529, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 1.00000, 0.47059, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.98039, 0.47059 },
   { 0.98039, 0.98039, 0.47059 },
   { 0.98039, 0.98039, 0.47059 },
   { 0.98039, 0.98039, 0.47059 },
   { 0.98039, 0.98039, 0.47059 },
   { 0.98039, 0.98039, 0.47059 },
   { 0.98039, 0.98039, 0.47059 },
   { 0.98039, 0.98039, 0.47059 },
   { 0.98039, 0.98039, 0.47059 },
   { 0.98039, 0.98039, 0.47059 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 },
   { 1.00000, 1.00000, 1.00000 }
};

static	Color_struct	rgb[] = {
   { 0.00000, 0.00000, 0.00000 },
   { 0.01176, 0.00000, 0.00000 },
   { 0.02745, 0.00000, 0.00000 },
   { 0.04314, 0.00000, 0.00000 },
   { 0.05882, 0.00000, 0.00000 },
   { 0.07451, 0.00000, 0.00000 },
   { 0.08627, 0.00000, 0.00000 },
   { 0.10196, 0.00000, 0.00000 },
   { 0.11765, 0.00000, 0.00000 },
   { 0.13333, 0.00000, 0.00000 },
   { 0.14902, 0.00000, 0.00000 },
   { 0.16078, 0.00000, 0.00000 },
   { 0.17647, 0.00000, 0.00000 },
   { 0.19216, 0.00000, 0.00000 },
   { 0.20784, 0.00000, 0.00000 },
   { 0.22353, 0.00000, 0.00000 },
   { 0.23529, 0.00000, 0.00000 },
   { 0.25098, 0.00000, 0.00000 },
   { 0.26667, 0.00000, 0.00000 },
   { 0.28235, 0.00000, 0.00000 },
   { 0.29804, 0.00000, 0.00000 },
   { 0.30980, 0.00000, 0.00000 },
   { 0.32549, 0.00000, 0.00000 },
   { 0.34118, 0.00000, 0.00000 },
   { 0.35686, 0.00000, 0.00000 },
   { 0.37255, 0.00000, 0.00000 },
   { 0.38431, 0.00000, 0.00000 },
   { 0.40000, 0.00000, 0.00000 },
   { 0.41569, 0.00000, 0.00000 },
   { 0.43137, 0.00000, 0.00000 },
   { 0.44706, 0.00000, 0.00000 },
   { 0.45882, 0.00000, 0.00000 },
   { 0.47451, 0.00000, 0.00000 },
   { 0.49020, 0.00000, 0.00000 },
   { 0.50588, 0.00000, 0.00000 },
   { 0.52157, 0.00000, 0.00000 },
   { 0.53725, 0.00000, 0.00000 },
   { 0.54902, 0.00000, 0.00000 },
   { 0.56471, 0.00000, 0.00000 },
   { 0.58039, 0.00000, 0.00000 },
   { 0.59608, 0.00000, 0.00000 },
   { 0.61176, 0.00000, 0.00000 },
   { 0.62353, 0.00000, 0.00000 },
   { 0.63922, 0.00000, 0.00000 },
   { 0.65490, 0.00000, 0.00000 },
   { 0.67059, 0.00000, 0.00000 },
   { 0.68627, 0.00000, 0.00000 },
   { 0.69804, 0.00000, 0.00000 },
   { 0.71373, 0.00000, 0.00000 },
   { 0.72941, 0.00000, 0.00000 },
   { 0.74510, 0.00000, 0.00000 },
   { 0.76078, 0.00000, 0.00000 },
   { 0.77255, 0.00000, 0.00000 },
   { 0.78824, 0.00000, 0.00000 },
   { 0.80392, 0.00000, 0.00000 },
   { 0.81961, 0.00000, 0.00000 },
   { 0.83529, 0.00000, 0.00000 },
   { 0.84706, 0.00000, 0.00000 },
   { 0.86275, 0.00000, 0.00000 },
   { 0.87843, 0.00000, 0.00000 },
   { 0.89412, 0.00000, 0.00000 },
   { 0.90980, 0.00000, 0.00000 },
   { 0.92157, 0.00000, 0.00000 },
   { 0.93725, 0.00000, 0.00000 },
   { 0.95294, 0.00000, 0.00000 },
   { 0.96863, 0.01176, 0.00000 },
   { 0.98431, 0.02745, 0.00000 },
   { 1.00000, 0.04314, 0.00000 },
   { 0.98431, 0.05882, 0.00000 },
   { 0.96863, 0.07451, 0.00000 },
   { 0.95294, 0.09020, 0.00000 },
   { 0.93725, 0.10588, 0.00000 },
   { 0.92157, 0.12157, 0.00000 },
   { 0.90196, 0.13725, 0.00000 },
   { 0.88627, 0.15294, 0.00000 },
   { 0.87059, 0.16863, 0.00000 },
   { 0.85490, 0.18431, 0.00000 },
   { 0.83922, 0.20000, 0.00000 },
   { 0.82353, 0.21569, 0.00000 },
   { 0.80392, 0.23137, 0.00000 },
   { 0.78824, 0.24706, 0.00000 },
   { 0.77255, 0.26275, 0.00000 },
   { 0.75686, 0.27843, 0.00000 },
   { 0.74118, 0.29412, 0.00000 },
   { 0.72157, 0.30980, 0.00000 },
   { 0.70588, 0.32549, 0.00000 },
   { 0.69020, 0.34118, 0.00000 },
   { 0.67451, 0.35686, 0.00000 },
   { 0.65882, 0.37255, 0.00000 },
   { 0.64314, 0.38824, 0.00000 },
   { 0.62353, 0.40392, 0.00000 },
   { 0.60784, 0.41961, 0.00000 },
   { 0.59216, 0.43529, 0.00000 },
   { 0.57647, 0.45098, 0.00000 },
   { 0.56078, 0.46667, 0.00000 },
   { 0.54118, 0.48235, 0.00000 },
   { 0.52549, 0.49804, 0.00000 },
   { 0.50980, 0.51373, 0.00000 },
   { 0.49412, 0.52941, 0.00000 },
   { 0.47843, 0.54510, 0.00000 },
   { 0.46275, 0.56078, 0.00000 },
   { 0.44314, 0.57647, 0.00000 },
   { 0.42745, 0.59216, 0.00000 },
   { 0.41176, 0.60784, 0.00000 },
   { 0.39608, 0.62353, 0.00000 },
   { 0.38039, 0.63922, 0.00000 },
   { 0.36078, 0.65490, 0.00000 },
   { 0.34510, 0.67059, 0.00000 },
   { 0.32941, 0.68627, 0.00000 },
   { 0.31373, 0.70196, 0.00000 },
   { 0.29804, 0.71765, 0.00000 },
   { 0.28235, 0.73333, 0.00000 },
   { 0.26275, 0.74902, 0.00000 },
   { 0.24706, 0.76471, 0.00000 },
   { 0.23137, 0.78039, 0.00000 },
   { 0.21569, 0.79608, 0.00000 },
   { 0.20000, 0.81176, 0.00000 },
   { 0.18039, 0.82745, 0.00000 },
   { 0.16471, 0.84314, 0.00000 },
   { 0.14902, 0.85882, 0.00000 },
   { 0.13333, 0.87451, 0.00000 },
   { 0.11765, 0.89020, 0.00000 },
   { 0.10196, 0.90588, 0.00000 },
   { 0.08235, 0.92157, 0.00000 },
   { 0.06667, 0.93725, 0.00000 },
   { 0.05098, 0.95294, 0.00000 },
   { 0.03529, 0.96863, 0.00000 },
   { 0.01961, 0.98431, 0.01176 },
   { 0.00000, 1.00000, 0.02745 },
   { 0.00000, 0.98431, 0.04314 },
   { 0.00000, 0.96863, 0.05882 },
   { 0.00000, 0.95294, 0.07451 },
   { 0.00000, 0.93725, 0.09020 },
   { 0.00000, 0.92157, 0.10588 },
   { 0.00000, 0.90588, 0.11765 },
   { 0.00000, 0.89020, 0.13333 },
   { 0.00000, 0.87451, 0.14902 },
   { 0.00000, 0.85882, 0.16471 },
   { 0.00000, 0.84314, 0.18039 },
   { 0.00000, 0.82745, 0.19608 },
   { 0.00000, 0.81176, 0.21176 },
   { 0.00000, 0.79608, 0.22353 },
   { 0.00000, 0.78039, 0.23922 },
   { 0.00000, 0.76471, 0.25490 },
   { 0.00000, 0.74902, 0.27059 },
   { 0.00000, 0.73333, 0.28627 },
   { 0.00000, 0.71765, 0.30196 },
   { 0.00000, 0.70196, 0.31765 },
   { 0.00000, 0.68627, 0.33333 },
   { 0.00000, 0.66667, 0.34510 },
   { 0.00000, 0.65098, 0.36078 },
   { 0.00000, 0.63529, 0.37647 },
   { 0.00000, 0.61961, 0.39216 },
   { 0.00000, 0.60392, 0.40784 },
   { 0.00000, 0.58824, 0.42353 },
   { 0.00000, 0.57255, 0.43922 },
   { 0.00000, 0.55686, 0.45098 },
   { 0.00000, 0.54118, 0.46667 },
   { 0.00000, 0.52549, 0.48235 },
   { 0.00000, 0.50980, 0.49804 },
   { 0.00000, 0.49412, 0.51373 },
   { 0.00000, 0.47843, 0.52941 },
   { 0.00000, 0.46275, 0.54510 },
   { 0.00000, 0.44706, 0.55686 },
   { 0.00000, 0.43137, 0.57255 },
   { 0.00000, 0.41569, 0.58824 },
   { 0.00000, 0.40000, 0.60392 },
   { 0.00000, 0.38431, 0.61961 },
   { 0.00000, 0.36863, 0.63529 },
   { 0.00000, 0.35294, 0.65098 },
   { 0.00000, 0.33333, 0.66667 },
   { 0.00000, 0.31765, 0.67843 },
   { 0.00000, 0.30196, 0.69412 },
   { 0.00000, 0.28627, 0.70980 },
   { 0.00000, 0.27059, 0.72549 },
   { 0.00000, 0.25490, 0.74118 },
   { 0.00000, 0.23922, 0.75686 },
   { 0.00000, 0.22353, 0.77255 },
   { 0.00000, 0.20784, 0.78431 },
   { 0.00000, 0.19216, 0.80000 },
   { 0.00000, 0.17647, 0.81569 },
   { 0.00000, 0.16078, 0.83137 },
   { 0.00000, 0.14510, 0.84706 },
   { 0.00000, 0.12941, 0.86275 },
   { 0.00000, 0.11373, 0.87843 },
   { 0.00000, 0.09804, 0.89020 },
   { 0.00000, 0.08235, 0.90588 },
   { 0.00000, 0.06667, 0.92157 },
   { 0.00000, 0.05098, 0.93725 },
   { 0.00000, 0.03529, 0.95294 },
   { 0.00000, 0.01961, 0.96863 },
   { 0.00000, 0.00000, 0.98431 },
   { 0.00000, 0.00000, 1.00000 },
   { 0.00000, 0.00000, 0.98431 },
   { 0.00000, 0.00000, 0.96863 },
   { 0.00000, 0.00000, 0.95294 },
   { 0.00000, 0.00000, 0.93725 },
   { 0.00000, 0.00000, 0.92157 },
   { 0.00000, 0.00000, 0.90588 },
   { 0.00000, 0.00000, 0.89020 },
   { 0.00000, 0.00000, 0.87451 },
   { 0.00000, 0.00000, 0.85882 },
   { 0.00000, 0.00000, 0.84314 },
   { 0.00000, 0.00000, 0.82745 },
   { 0.00000, 0.00000, 0.81176 },
   { 0.00000, 0.00000, 0.79608 },
   { 0.00000, 0.00000, 0.78039 },
   { 0.00000, 0.00000, 0.76471 },
   { 0.00000, 0.00000, 0.74902 },
   { 0.00000, 0.00000, 0.73333 },
   { 0.00000, 0.00000, 0.71765 },
   { 0.00000, 0.00000, 0.70196 },
   { 0.00000, 0.00000, 0.68627 },
   { 0.00000, 0.00000, 0.66667 },
   { 0.00000, 0.00000, 0.65098 },
   { 0.00000, 0.00000, 0.63529 },
   { 0.00000, 0.00000, 0.61961 },
   { 0.00000, 0.00000, 0.60392 },
   { 0.00000, 0.00000, 0.58824 },
   { 0.00000, 0.00000, 0.57255 },
   { 0.00000, 0.00000, 0.55686 },
   { 0.00000, 0.00000, 0.54118 },
   { 0.00000, 0.00000, 0.52549 },
   { 0.00000, 0.00000, 0.50980 },
   { 0.00000, 0.00000, 0.49412 },
   { 0.00000, 0.00000, 0.47843 },
   { 0.00000, 0.00000, 0.46275 },
   { 0.00000, 0.00000, 0.44706 },
   { 0.00000, 0.00000, 0.43137 },
   { 0.00000, 0.00000, 0.41569 },
   { 0.00000, 0.00000, 0.40000 },
   { 0.00000, 0.00000, 0.38431 },
   { 0.00000, 0.00000, 0.36863 },
   { 0.00000, 0.00000, 0.35294 },
   { 0.00000, 0.00000, 0.33333 },
   { 0.00000, 0.00000, 0.31765 },
   { 0.00000, 0.00000, 0.30196 },
   { 0.00000, 0.00000, 0.28627 },
   { 0.00000, 0.00000, 0.27059 },
   { 0.00000, 0.00000, 0.25490 },
   { 0.00000, 0.00000, 0.23922 },
   { 0.00000, 0.00000, 0.22353 },
   { 0.00000, 0.00000, 0.20784 },
   { 0.00000, 0.00000, 0.19216 },
   { 0.00000, 0.00000, 0.17647 },
   { 0.00000, 0.00000, 0.16078 },
   { 0.00000, 0.00000, 0.14510 },
   { 0.00000, 0.00000, 0.12941 },
   { 0.00000, 0.00000, 0.11373 },
   { 0.00000, 0.00000, 0.09804 },
   { 0.00000, 0.00000, 0.08235 },
   { 0.00000, 0.00000, 0.06667 },
   { 0.00000, 0.00000, 0.05098 },
   { 0.00000, 0.00000, 0.03529 },
   { 0.00000, 0.00000, 0.01961 },
   { 0.00000, 0.00000, 0.00000 }
};

static	Color_struct	smooth[] = {
   { 0.00000, 0.00000, 1.00000 },
   { 0.01569, 0.00000, 0.98431 },
   { 0.03529, 0.00000, 0.96471 },
   { 0.05098, 0.00000, 0.94902 },
   { 0.06667, 0.00000, 0.93333 },
   { 0.08627, 0.00000, 0.91373 },
   { 0.10196, 0.00000, 0.89804 },
   { 0.11765, 0.00000, 0.88235 },
   { 0.13725, 0.00000, 0.86275 },
   { 0.15294, 0.00000, 0.84706 },
   { 0.16863, 0.00000, 0.83137 },
   { 0.18824, 0.00000, 0.81176 },
   { 0.20392, 0.00000, 0.79608 },
   { 0.21961, 0.00000, 0.78039 },
   { 0.23922, 0.00000, 0.76078 },
   { 0.25490, 0.00000, 0.74510 },
   { 0.27059, 0.00000, 0.72941 },
   { 0.28627, 0.00000, 0.71373 },
   { 0.30588, 0.00000, 0.69412 },
   { 0.32157, 0.00000, 0.67843 },
   { 0.33725, 0.00000, 0.66275 },
   { 0.35686, 0.00000, 0.64314 },
   { 0.37255, 0.00000, 0.62745 },
   { 0.38824, 0.00000, 0.61176 },
   { 0.40784, 0.00000, 0.59216 },
   { 0.42353, 0.00000, 0.57647 },
   { 0.43922, 0.00000, 0.56078 },
   { 0.45882, 0.00000, 0.54118 },
   { 0.47451, 0.00000, 0.52549 },
   { 0.49020, 0.00000, 0.50980 },
   { 0.50980, 0.00000, 0.49020 },
   { 0.52549, 0.00000, 0.47451 },
   { 0.54118, 0.00000, 0.45882 },
   { 0.56078, 0.00000, 0.43922 },
   { 0.57647, 0.00000, 0.42353 },
   { 0.59216, 0.00000, 0.40784 },
   { 0.61176, 0.00000, 0.38824 },
   { 0.62745, 0.00000, 0.37255 },
   { 0.64314, 0.00000, 0.35686 },
   { 0.66275, 0.00000, 0.33725 },
   { 0.67843, 0.00000, 0.32157 },
   { 0.69412, 0.00000, 0.30588 },
   { 0.71373, 0.00000, 0.28627 },
   { 0.72941, 0.00000, 0.27059 },
   { 0.74510, 0.00000, 0.25490 },
   { 0.76078, 0.00000, 0.23922 },
   { 0.78039, 0.00000, 0.21961 },
   { 0.79608, 0.00000, 0.20392 },
   { 0.81176, 0.00000, 0.18824 },
   { 0.83137, 0.00000, 0.16863 },
   { 0.84706, 0.00000, 0.15294 },
   { 0.86275, 0.00000, 0.13725 },
   { 0.88235, 0.00000, 0.11765 },
   { 0.89804, 0.00000, 0.10196 },
   { 0.91373, 0.00000, 0.08627 },
   { 0.93333, 0.00000, 0.06667 },
   { 0.94902, 0.00000, 0.05098 },
   { 0.96471, 0.00000, 0.03529 },
   { 0.98431, 0.00000, 0.01569 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.00000, 0.00000 },
   { 1.00000, 0.01176, 0.00000 },
   { 1.00000, 0.01961, 0.00000 },
   { 1.00000, 0.03137, 0.00000 },
   { 1.00000, 0.03922, 0.00000 },
   { 1.00000, 0.05098, 0.00000 },
   { 1.00000, 0.05882, 0.00000 },
   { 1.00000, 0.07059, 0.00000 },
   { 1.00000, 0.08235, 0.00000 },
   { 1.00000, 0.09020, 0.00000 },
   { 1.00000, 0.10196, 0.00000 },
   { 1.00000, 0.10980, 0.00000 },
   { 1.00000, 0.12157, 0.00000 },
   { 1.00000, 0.12941, 0.00000 },
   { 1.00000, 0.14118, 0.00000 },
   { 0.99608, 0.15294, 0.00000 },
   { 0.99608, 0.16078, 0.00000 },
   { 0.99608, 0.17255, 0.00000 },
   { 0.99608, 0.18039, 0.00000 },
   { 0.99608, 0.19216, 0.00000 },
   { 0.99608, 0.20392, 0.00000 },
   { 0.99608, 0.21176, 0.00000 },
   { 0.99608, 0.22353, 0.00000 },
   { 0.99608, 0.23137, 0.00000 },
   { 0.99608, 0.24314, 0.00000 },
   { 0.99608, 0.25098, 0.00000 },
   { 0.99608, 0.26275, 0.00000 },
   { 0.99608, 0.27451, 0.00000 },
   { 0.99608, 0.28235, 0.00000 },
   { 0.99608, 0.29412, 0.00000 },
   { 0.99608, 0.30196, 0.00000 },
   { 0.99608, 0.31373, 0.00000 },
   { 0.99608, 0.32157, 0.00000 },
   { 0.99608, 0.33333, 0.00000 },
   { 0.99608, 0.34510, 0.00000 },
   { 0.99608, 0.35294, 0.00000 },
   { 0.99608, 0.36471, 0.00000 },
   { 0.99608, 0.37255, 0.00000 },
   { 0.99608, 0.38431, 0.00000 },
   { 0.99608, 0.39216, 0.00000 },
   { 0.99608, 0.40392, 0.00000 },
   { 0.99608, 0.41569, 0.00000 },
   { 0.99608, 0.42353, 0.00000 },
   { 0.99608, 0.43529, 0.00000 },
   { 0.99608, 0.44314, 0.00000 },
   { 0.99216, 0.45490, 0.00000 },
   { 0.99216, 0.46667, 0.00000 },
   { 0.99216, 0.47451, 0.00000 },
   { 0.99216, 0.48627, 0.00000 },
   { 0.99216, 0.49412, 0.00000 },
   { 0.99216, 0.50588, 0.00000 },
   { 0.99216, 0.51373, 0.00000 },
   { 0.99216, 0.52549, 0.00000 },
   { 0.99216, 0.53725, 0.00000 },
   { 0.99216, 0.54510, 0.00000 },
   { 0.99216, 0.55686, 0.00000 },
   { 0.99216, 0.56471, 0.00000 },
   { 0.99216, 0.57647, 0.00000 },
   { 0.99216, 0.58431, 0.00000 },
   { 0.99216, 0.59608, 0.00000 },
   { 0.99216, 0.60000, 0.00000 },
   { 0.99216, 0.60784, 0.00000 },
   { 0.99216, 0.61176, 0.00000 },
   { 0.99216, 0.61569, 0.00000 },
   { 0.99216, 0.61961, 0.00000 },
   { 0.99216, 0.62745, 0.00000 },
   { 0.99216, 0.63137, 0.00000 },
   { 0.99216, 0.63529, 0.00000 },
   { 0.99216, 0.64314, 0.00000 },
   { 0.98824, 0.64706, 0.00000 },
   { 0.98824, 0.65098, 0.00000 },
   { 0.98824, 0.65882, 0.00000 },
   { 0.98824, 0.66275, 0.00000 },
   { 0.98824, 0.66667, 0.00000 },
   { 0.98824, 0.67451, 0.00000 },
   { 0.98824, 0.67843, 0.00000 },
   { 0.98824, 0.68235, 0.00000 },
   { 0.98824, 0.68627, 0.00000 },
   { 0.98824, 0.69412, 0.00000 },
   { 0.98824, 0.69804, 0.00000 },
   { 0.98824, 0.70196, 0.00000 },
   { 0.98824, 0.70980, 0.00000 },
   { 0.98824, 0.71373, 0.00000 },
   { 0.98824, 0.71765, 0.00000 },
   { 0.98824, 0.72549, 0.00000 },
   { 0.98824, 0.72941, 0.00000 },
   { 0.98824, 0.73333, 0.00000 },
   { 0.98824, 0.73725, 0.00000 },
   { 0.98824, 0.74510, 0.00000 },
   { 0.98824, 0.74902, 0.00000 },
   { 0.98431, 0.75294, 0.00000 },
   { 0.98431, 0.76078, 0.00000 },
   { 0.98431, 0.76471, 0.00000 },
   { 0.98431, 0.76863, 0.00000 },
   { 0.98431, 0.77255, 0.00000 },
   { 0.98431, 0.78039, 0.00000 },
   { 0.98431, 0.78431, 0.00000 },
   { 0.98431, 0.78824, 0.00000 },
   { 0.98431, 0.79608, 0.00000 },
   { 0.98431, 0.80000, 0.00000 },
   { 0.98431, 0.80392, 0.00000 },
   { 0.98431, 0.81176, 0.00000 },
   { 0.98431, 0.81569, 0.00000 },
   { 0.98431, 0.81961, 0.00000 },
   { 0.98431, 0.82745, 0.00000 },
   { 0.98431, 0.83137, 0.00000 },
   { 0.98431, 0.83529, 0.00000 },
   { 0.98431, 0.83922, 0.00000 },
   { 0.98431, 0.84706, 0.00000 },
   { 0.98431, 0.85098, 0.00000 },
   { 0.98039, 0.85490, 0.00000 },
   { 0.98039, 0.86275, 0.00000 },
   { 0.98039, 0.86667, 0.00000 },
   { 0.98039, 0.87059, 0.00000 },
   { 0.98039, 0.87843, 0.00000 },
   { 0.98039, 0.88235, 0.00000 },
   { 0.98039, 0.88627, 0.00000 },
   { 0.98039, 0.89020, 0.00000 },
   { 0.98039, 0.89804, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.98039, 0.90196, 0.00000 },
   { 0.96471, 0.88627, 0.00000 },
   { 0.94902, 0.87059, 0.00000 },
   { 0.92941, 0.85490, 0.00000 },
   { 0.91373, 0.83922, 0.00000 },
   { 0.89804, 0.82745, 0.00000 },
   { 0.88235, 0.81176, 0.00000 },
   { 0.86275, 0.79608, 0.00000 },
   { 0.84706, 0.78039, 0.00000 },
   { 0.83137, 0.76471, 0.00000 },
   { 0.81569, 0.74902, 0.00000 },
   { 0.79608, 0.73333, 0.00000 },
   { 0.78039, 0.71765, 0.00000 },
   { 0.76471, 0.70196, 0.00000 },
   { 0.74902, 0.68627, 0.00000 },
   { 0.72941, 0.67451, 0.00000 },
   { 0.71373, 0.65882, 0.00000 },
   { 0.69804, 0.64314, 0.00000 },
   { 0.68235, 0.62745, 0.00000 },
   { 0.66275, 0.61176, 0.00000 },
   { 0.64706, 0.59608, 0.00000 },
   { 0.63137, 0.58039, 0.00000 },
   { 0.61569, 0.56471, 0.00000 },
   { 0.60000, 0.54902, 0.00000 },
   { 0.58039, 0.53333, 0.00000 },
   { 0.56471, 0.52157, 0.00000 },
   { 0.54902, 0.50588, 0.00000 },
   { 0.53333, 0.49020, 0.00000 },
   { 0.51373, 0.47451, 0.00000 },
   { 0.49804, 0.45882, 0.00000 },
   { 0.48235, 0.44314, 0.00000 },
   { 0.46667, 0.42745, 0.00000 },
   { 0.44706, 0.41176, 0.00000 },
   { 0.43137, 0.39608, 0.00000 },
   { 0.41569, 0.38039, 0.00000 },
   { 0.40000, 0.36863, 0.00000 },
   { 0.38039, 0.35294, 0.00000 },
   { 0.36471, 0.33725, 0.00000 },
   { 0.34902, 0.32157, 0.00000 },
   { 0.33333, 0.30588, 0.00000 },
   { 0.31765, 0.29020, 0.00000 },
   { 0.29804, 0.27451, 0.00000 },
   { 0.28235, 0.25882, 0.00000 },
   { 0.26667, 0.24314, 0.00000 },
   { 0.25098, 0.22745, 0.00000 },
   { 0.23137, 0.21569, 0.00000 },
   { 0.21569, 0.20000, 0.00000 },
   { 0.20000, 0.18431, 0.00000 },
   { 0.18431, 0.16863, 0.00000 },
   { 0.16471, 0.15294, 0.00000 },
   { 0.14902, 0.13725, 0.00000 },
   { 0.13333, 0.12157, 0.00000 },
   { 0.11765, 0.10588, 0.00000 },
   { 0.09804, 0.09020, 0.00000 },
   { 0.08235, 0.07451, 0.00000 },
   { 0.06667, 0.06275, 0.00000 },
   { 0.05098, 0.04706, 0.00000 },
   { 0.03137, 0.03137, 0.00000 },
   { 0.01569, 0.01569, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 },
   { 0.00000, 0.00000, 0.00000 }
};

static  Color_struct    renzo[] = {
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.19608, 0.19608, 0.19608 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.47451, 0.00000, 0.60784 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.00000, 0.00000, 0.78431 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.37255, 0.65490, 0.92549 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.56863, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {0.00000, 0.96471, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 1.00000, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.69412, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {1.00000, 0.00000, 0.00000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.80000, 0.10000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.60000, 0.20000, 0.10000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 },
   {0.39000, 0.22000, 0.11000 }
};
#<

#>            GgiInternal.H
#if !defined(_ggiinternal_h_)
#define _ggiinternal_h_
typedef struct _GgiInfo {
   XVisualInfo *vinfo;
   int         defaultvisual;
   int         fixedcolors;
   Colormap    *colormap;
   int         defaultcolormap;
} _GgiInfo, *GgiInfo;
#endif
#<

#>            ggi.dc2
Document:     ggi

Purpose:      Describes Ggi, the GIPSY graphical user interface

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Description: Ggi (``GIPSY graphical interface'') is a collection of
             routines which allows the programmer to construct a graphical
             user interface for a task. It is based on the X Toolkit
             and the almost universally available Athena widgets.
             It makes use of GIPSY's event-handling facilities. 

             The following routines are available:

             1. routines which define some global setting:
              --------------------------------------------
                GgiAutoLayout
                   sets Ggi's layout policy, either automatic or explicit.
                GgiPostponeRealize
                   causes Ggi to postpone realization of elements on the screen
                   until this is explicitly requested.
                GgiRealize
                   realize created elements.
                GgiSetHeight
                   set the application's main window height.
                GgiSetWidth
                   set the application's main window width.
                GgiConfigure
                   for automatic layout policy, set a number of dimensions.
                GgiSetResources
                   set the application's fallback resources.
                void GgiSetDisplay
                   override the default display name.
                   
             2. routines which create a user interface element:
             --------------------------------------------------
                a. simple elements
                ------------------
                GgiTextField
                   create a text input field.
                GgiEditor
                   create a text editor element.
                GgiButton
                   create a push-down button.
                GgiMenu
                   create a roll-down menu.
                GgiList
                   create a list-selector element.
                GgiGauge
                   create a bar-shaped analog valuator.
                GgiDial
                   create a dial-shaped analog valuator.
                GgiLabel
                   create a text output field.
                GgiLogo
                   create the GIPSY logo.
                GgiProgress
                   create a "progress bar".
                GgiPlotField
                   create a PGPLOT-compatible plot area.
                GgiCanvas
                   create an element without built-in behaviour.
                GgiShell
                   create a secondary window containing other elements.
                GgiDialog
                   like GgiShell, but interaction limited to this window.
                GgiForm
                   create a container element for other elements.
                GgiViewport
                   create a scrollable container element.

                b. elements constructed from other elements
                -------------------------------------------
                GgiPrompter
                   prompter element implementing a simple dialog.
                GgiPlotPrompter
                   prompter element for selecting a plot device.
                GgiVerify
                   question pop-up window waiting for user action.
                GgiPlotColorEditor
                   colormap editing element for plot fields.
                GgiFileBrowser
                   directory browser and file selector.
                GgiInset
                   set, subset and box input element.
                GgiHelpMenu
                   configurable menu with associated help text pop-up.
                GgiColorMenu
                   menu for selecting standard PGPLOT colors
                GgiTextMenu
                   menu for selecting text items
                GgiHeaderButton
                   button with associated GDS set header text pop-up.

              3. routines which manipulate a user interface element:
              ------------------------------------------------------
                GgiActivate
                   make the element sensitive to user interaction.
                GgiDeactivate
                   make the element insensitive to user interaction.
                GgiHelpText
                   set or modify the element's help text ('tip').
                GgiSetLabel
                   set or modify the element's descriptive label.
                GgiSetBitmap
                   set or modify a bitmap in the element's label.
                GgiAlignLabel 
                   modify the label's alignment (left, center, right).
                GgiSetPosition
                   set or modify the element's position.
                GgiSetRange
                   modify the range of an analog valuator element.
                GgiSetFormat
                   modifies an analog valuator's output format.
                GgiSetCircular
                   cause an analog valuator to wrap around outside its range.
                GgiSetKeyword
                   modifies an element's associated user input keyword
                GgiMenuLabels
                   modify the entry labels of a menu element.
                GgiListLabels
                   modify the entry labels of a list element.
                GgiDelete
                   delete the element.
                GgiUseShell
                   specify secondary window in which to create elements.
                GgiShowShell
                   make secondary window visible or invisible.
                GgiPlotXor
                   set non-destructive plotting mode.
                GgiPlotColors
                   obtain colors for the plotfields master table.
                GgiPlotMapColors
                    map plotfield colors to colors from the master table.
                GgiPlotFrames
                   create off-screen pixmaps associated with plot field.
                GgiPlotRecord
                   record current plot in specified pixmap.
                GgiPlotShow
                   copy specified pixmap to current plot window.
                GgiPlotColorEditor
                   modify parameters in colormap editing element.
                ScheduleGgiPlotCursor
                   register function receiving coordinates from a plot field.
                DescheduleGgiPlotCursor
                   de-register coordinate function.

              4. miscellaneous routines:
              --------------------------
                GgiWidth
                   return the element's width in pixels.
                GgiHeight
                   return the element's height in pixels.
                GgiShowDoc
                   pop up a document.
                GgiDisplay
                   return the X11 display identification.
                GgiWindow
                   return the element's X11 window identification.
                GgiWidget
                   return the element's active widget.
                GgiContext
                   return the Xt application context.
                GgiIsRealized
                   test whether element is realized.
                GgiHandleEvents
                   explicitly process outstanding events.
                GgiKeyIdent
                   return a user input keyword's identification number.

             All 'active' elements are associated with a user input keyword
             through which user inputs are transferred to the
             application. Once the user interface has been constructed,
             the task normally only communicates with the user through
             keywords. This communication is bi-directional: e.g. when
             the user makes a change in a text input field,
             this is notified to the application code,
             but when the application changes the keyword value by
             calling wkey_c(), the text input field is also updated.
             This also works for analog valuators, buttons, etc.
              
             It is possible to have more than one element associated
             with a particular keyword, e.g. both a valuator and a text
             field. In this case, when one element is manipulated, the
             other element(s) will follow it.

             Ggi must be initialized before scheduling any event handlers.
             Creating any element implicitly initializes Ggi.

Updates:     Jan 14, 1998: JPT, Document created.
             Feb 24, 1998: JPT, GgiWidget and GgiContext added.
             Feb 26, 1998: JPT, Plot field added.
             Mar  4, 1998: JPT, GgiPlotXor added. Improved help pop-up.
             Mar  9, 1998: JPT, GgiMenuLabels, GgiWidth and GgiHeight added.
             Mar 16, 1998: JPT, Bug in plot initialization fixed.
             Mar 23, 1998: JPT, Bug fixed in GgiMenuLabels, plotcursor improved.
             Mar 25, 1998: JPT, GgiSetBitmap added.
             Mar 31, 1998: JPT, Shell element added.
             Apr 17, 1998: JPT, Use Slider2 widget for gauge.
             May 13, 1998: JPT, Form element added.
             Jun  8, 1998: JPT, Small improvements.
             Jun 22, 1998: JPT, Small improvements.
             Jun 24, 1998: JPT, GgiDialog added.
             Jul  2, 1998: JPT, Improved XOR plot mode; special menu entries.
             Aug 26, 1998: JPT, Small improvements.
             Sep 11, 1998: JPT, Improved plot driver, added GgiHandleEvents.
             Nov  9, 1998: JPT, Plot driver modified for PGPLOT 5.2.0.
             Feb  8, 1999: JPT, Updated document for new features.
             Feb 19, 1999: JPT, Textfields now always generate event on RETURN.
             Mar 31, 1999: JPT, Recursive deletion. GgiPrompter added.
             Apr 27, 1999: JPT, GgiSetKeyword added.
             May  3, 1999: JPT, Fixed bug in SHELLdelete method
             May 28, 1999: JPT, GgiPlotPrompter added.
             Jun 23, 1999: JPT, Merge resource strings.
             Jul  1, 1999: JPT, GgiProgress added.
             Aug  4, 1999: JPT, GgiVerify added.
             Sep 20, 1999: JPT, GgiPlotMapColors, GgiPlotColorEditor
                                and GgiIsRealized added.
             Nov  2, 1999: JPT, GgiViewport, GgiList and GgiListLabels added.
             Nov 25, 1999: JPT, GgiFileBrowser and GgiInset added.
             Feb 17, 2000: JPT, GgiHelpMenu added.
             Feb 18, 2000: JPT, Prevent window manager close.
             Apr 18, 2000: JPT, Changed plotcursor and other small improvements.
             Jun 23, 2000: JPT, Canvas now Simple widget instead of Label.
             Jul 12, 2000: JPT, Documented GgiHelpMenu, GgiHeaderButton and
                                GgiKeyIdent.
             Aug 21, 2000: JPT, Added GgiColorMenu and GgiTextMenu.
             Sep  5, 2000: JPT, Added ggiGetMap to GgiPlotColorEditor.
             Jan 10, 2001: JPT, Check for pseudocolor visual;
                                added GgiSetDisplay.
             Feb  1, 2001: JPT, Support for non-default visuals.
             Mar 12, 2001: JPT, Support for TrueColor visuals.
             Mar 15, 2001: JPT, Improved keyboard mapping for plots.
             Mar 22, 2001: JPT, Added arrow keys for plots.
             Apr 24, 2001: JPT, Logging feature added.
             May  9, 2001: JPT, General options pop-up.
             Oct  9, 2001: JPT, Keyword interaction for GgiViewport.
             Nov 12, 2001: JPT, Fixed visual bug in GgiMenuLabels.
             Nov 23, 2001: JPT, Allow multiple X server screens
#<

#>            ggi.doc
Document:     ggi

------------------------------------------------------------------------
Ggi, GIPSY's graphical user interface, makes use of the X Toolkit. Most
elements come from the Athena widget set. With a few exceptions, all
elements are coupled to the Hermes keyword mechanism. This implies that
all rules for user input also apply to input within Ggi.

Below the interaction with the most important elements is explained.


TEXT INPUT FIELDS
=================
In a text input field the user can type information. It has limited
Emacs-like editing capabilities. The following mouse and keyboard
interactions are defined:

CTRL-B or Left Arrow  :  move one character left

CTRL-F or Right Arrow :  move one character right

CTRL-A                :  move to beginning of text

CTRL-E                :  move to end of text

CTRL-H, Backspace
or Delete             :  delete previous character

CTRL-D                :  delete next character

CTRL-K                :  delete from cursor to end

CTRL-U, Esc or      
Right Button click    :  delete all

Left Button drag      :  select text for "mouse buffer"

Middle Button click   :  paste "mouse buffer" into input field

Enter                 :  accept input (i.e. send it to the program)

Left Button click     :  move to input field clicked in

Tab or Down Arrow     :  move to next input field

Up Arrow              :  move to previous input field


ANALOG VALUATORS
================
There are two kinds of analog valuators: gauges and dials. A gauge is a
horizontal rectangle containing a kind of handle which can be
manipulated with the mouse. The position of the handle determines which
value is sent to the program. A dial is a circle or circle sector
containing an arrow which also can be manipulated with the mouse.
In this case the rotation angle determines the value. 

GAUGE interactions
------------------
Left Mouse Button     :  move handle to position of the mouse. Both clicking
                         and dragging are possible

Middle or Right click :  move handle a small amount towards mouse position

DIAL interactions
-----------------
Left Mouse Button     :  move arrow to position of the mouse. Both clicking
                         and dragging are possible

Middle Button click   :  move arrow small amount clockwise

Right  Button click   :  move arrow small amount anti-clockwise


BUTTONS and MENUS
=================
These elements don't need much explanation. A button can be activated by
clicking in it with the mouse. A menu requires a two-stage interaction.
First press the left mouse button in the menu button and hold it down.
Now the actual menu pops up. Then move the mouse pointer to the desired
menu entry and release the mouse button.

NOTE: on some systems menu entries cannot be selected when the Caps Lock
      key is on.


PLOT FIELDS
===========
Depending on the program, a plot field may be either output-only or
cursor interaction may be possible. For output-only plot fields, the
cursor has an arrow shape. When cursor interaction is possible, a
crosshair cursor is used. The possible interactions are completely
determined by the program.


POP-UP WINDOWS
==============
Pop-up windows are windows, separate from the "main" window, which can
be brought to the screen when they are needed. The way in which this is
done, is determined by the program, but it is usually the result of a
button-press or menu selection. Closing such a window is also under
program control, usually as the result of pressing some kind of
"close"-button.


HELP WINDOWS
============
A help window is a pop-up window used to show a documentation file. If
a document does not fit in the window, scrollbars are added which allow
the user to scroll through the document. Interaction with help windows
depends on which version of the Athena widget set is used. The
following interactions may or may not work:

CTRL-S                :  search forward (case sensitive)

CTRL-R                :  search backwards (case sensitive)

CTRL-V or Page Down   :  go to next page

Page Up               :  go to previous page

CTRL-Z or CTRL-N      :  scroll or move one line forward

CTRL-P                :  move one line backwards


WINDOW BACKGROUND
=================
Clicking in any window's background with the right mouse button
activates a special pop-up window containing GUI elements common to
every application.  Currently there are elements for recording events in
a file (which can be played back by the task PLAYER), for starting
PLAYER, and for logging events in the log file. Please note that PLAYER
is an independent task, not a pop-up window of the originating task.
It is started under the name of the originating task prefixed by "PLAY_".


Keywords
========

GGIOPT=

     When a Ggi application is started, the keyword GGIOPT= can be used to
     influence its behaviour with respect to the colormap. Two values
     are possible:
     
     OWNCOL causes the application to use its own colormap. This can be useful
            for applications displaying images with a large number of colors.
            The default behaviour is that Ggi will try to obtain the necessary
            colors from the default colormap and if this is not possible, it
            will switch to a newly created colormap. In the latter case
            standard colors are often displayed incorrectly.
     
     FIXCOL always use the default colormap. This is only useful when the
            the default colormap is fixed, i.e. of the TrueColor class.


GGILOGFILE=

     Name of file in which changes in keywords related to Ggi elements are
     to be logged. This file can then later be used as input to the event
     player program PLAYER. GGILOGFILE= can be changed at any time:
     when the task is started, but also for a running task. Also accessible
     via the window background pop-up. See above.


GGILOG=

     YES or NO. Switches logging on or off. Note that logging is implicitly
     on whenever a log file is specified. Can be changed at any time.
     Also accessible via the window background pop-up.


GGILOGDELAY=

     YES or NO. If YES, also the time intervals between events are logged.
     Can be changed at any time. Also accessible via the window background
     pop-up.


Configuration file
==================
There is a limited possibility to configure Ggi. This is done using an
application defaults file, which must have the name XGgi and reside in
a directory pointed to by the environment variable XAPPLRESDIR.
An indication of what can be configured can be found in Ggi's source
code, $gip_sub/ggi.src. Search for "def_fallback".

For instance if you prefer a white background color and a blue foreground,
create a file XGgi with the following contents:

   *Background: white
   *Foreground: blue

Then set the environment variable to the current directory:

   setenv XAPPLRESDIR `pwd`

Now you must start GIPSY again after which the new configuration will be
used. Please note that Ggi does not always adapt to the chosen configuration.
E.g. if you choose a different font size, the layout may not be optimal. 
------------------------------------------------------------------------


Purpose:      Describe GIPSY's Graphical User Interface

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Updates:      Nov 18, 1999: JPT, Document created.
              Aug 30, 2000: JPT, Document text updated.
              Feb  7, 2001: JPT, Explain GGIOPT= keyword
              Apr 24, 2001: JPT, GILOGFILE=, GGILOG= and GGILOGDELAY= added.
              May  9, 2001: JPT, Window background pop-up.
              Jun 18, 2001: JPT, Added PLAYER to window background pop-up.
#<

#>            GgiActivate.dc2
Function:     GgiActivate

Purpose:      Make Ggi element sensitive to user interaction

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident element, result;

              result = GgiActivate(element);

                 element  -  the element to be operated upon.
                 result   -  receives a copy of the argument.

Description:  GgiActivate makes an element sensitive to user interaction,
              reversing the effect of GgiDeactivate.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiAlignLabel.dc2
Function:     GgiAlignLabel

Purpose:      Modify the alignment of a Ggi element's label

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident     element, result;
              ggiAlign  alignment;

              result = GgiAlignLabel(element, alignment);

                 element   -  the element to be operated upon.
                 alignment -  the kind of alignment: ggiLeft, ggiCenter
                              or ggiRight.
                 result    -  receives a copy the argument.

Description:  This function operates on all elements containing a label,
              including elements created by GgiLabel.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiAutoLayout.dc2
Function:     GgiAutoLayout

Purpose:      Modify Ggi's automatic layout operation default

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              bool auto;

              GgiAutoLayout(auto);

                 auto  -  if TRUE, Ggi will position elements automatically;
                          if FALSE, elements must be positioned explicitly.

Description:  GgiAutoLayout must be called before any element is created.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiButton.dc2
Function:     GgiButton

Purpose:      Create a Ggi button element.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident button;
              char *keyword, *message;

              button = GgiButton(keyword, message);

                 button     -  'handle' by which element can be referenced.
                 keyword    -  user input keyword.
                 message    -  help text which pops up when the pointer stays
                               on the button for some time.
 
Description:  GgiButton creates a button.
              This is a toggle button, when it has been pushed, it stays
              depressed until it is pushed again, or the associated keyword
              is set to 'NO' by calling wkey_c().
              The associated keyword is set to 'YES' or 'NO' depending on
              the button's state. It can be read by userlog_c().
               
              The default button's label text is the supplied keyword
              without the equals sign.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiCanvas.dc2
Function:     GgiCanvas

Purpose:      Create a Ggi element without built-in behaviour.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident canvas;
              char *name;
              int  width, height;

              canvas = GgiCanvas(name, width, height);

                 canvas  -  'handle' by which element can be referenced.
                 name    -  widget's name.
                 width   -  width in pixels.
                 height  -  height in pixels.

Description:  GgiCanvas creates an element without built-in behaviour.
              The element's window can be manipulated using routines from Xlib.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiConfigure.dc2
Function:     GgiConfigure

Purpose:      Modify default Ggi configuration parameters

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              char *appclass;
              int  width, menuheight, remheight, buttheight;

              GgiConfigure(appclass, width, menuheight, remheight, buttheight);

                 appclass   -  application class name
                 width      -  task's window width in pixels
                 menuheight -  height in pixels of menu pane
                 remheight  -  height of pane with non-menu, non-button elements
                 buttheight -  height of button pane

Description:  GgiConfigure modifies the application class name and a number
              of dimensions. The menu height and button height dimensions
              are only used when automatic layout of elements is in effect.

              When any argument is given as NULL or zero, the internal default
              remains in effect.

              GgiConfigure must be called before any element is created.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiDeactivate.dc2
Function:     GgiDeactivate

Purpose:      Make Ggi element insensitive to user interaction

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident element, result;

              result = GgiDeactivate(element);

                 element  -  the element to be operated upon.
                 result   -  receives a copy of the argument.

Description:  GgiDeactivate makes an element insensitive to user interaction.
              This prevents the user to manipulate the element, e.g., when
              such manipulation would be irrelevant or unwantend in the
              present context.
              If a container element (form or shell) is deactivated, all
              elements contained by it will be made insensitive.

Updates:      Jan 13, 1998: JPT, Document created.
              May 13, 1998: JPT, Container element interaction added.
#<

#>            GgiDelete.dc2
Function:     GgiDelete

Purpose:      Delete a Ggi element.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident element;
 
              GgiDelete(&element);

                 element  -  element to be deleted. Note that the argument
                             is a pointer to element.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiDisplay.dc3
Function:     GgiDisplay

Purpose:      Obtain the X Window display used by Ggi

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggidisplay.h"
              Display *result;

              result = GgiDisplay();

                 result  -  the X Window display used by Ggi

Description:  GgiDisplay() obtains the X Window display used by Ggi.
              This information can be useful when elements must be manipulated
              outside Ggi, which may be the case for canvas elements.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiGauge.dc2
Function:     GgiGauge

Purpose:      Create an analog valuator Ggi element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident gauge;
              char *keyword, *message;
              int   length;
              float left, right;

              gauge = GgiGauge(keyword, message, length, left, right);

                 gauge    -  'handle' by which element can be referenced.
                 keyword  -  user input keyword.
                 message  -  help text which pops up when the pointer stays
                             in the input area for some time.
                 length   -  gauge's length in pixels.
                 left     -  value corresponding to leftmost position.
                 right    -  value corresponding to rightmost position.

Description:  GgiGauge creates an analog valuator element.
              On the screen is consists of a label field and the actual
              gauge implemented as a scrollbar. By default the supplied keyword
              is taken as the label's value.

              When it is manipulated, it writes its value to the keyword by
              calling wkey_c(). This can be read by any number reading
              routine e.g. userreal_c().

Related documents:
              GgiSetCircular.dc2, GgiSetFormat.dc2, GgiSetRange.dc2
              
Updates:      Jan 13, 1998: JPT, Document created.
              Jan 28, 1999: JPT, GgiSetFormat and GgiSetCircular added.
              Oct  2, 2000: JPT, Fixed bug in keychanged method.
#<

#>            GgiSetHeight.dc2
Function:     GgiSetHeight

Purpose:      Modify default Ggi window height.

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              int height;

              GgiSetHeight(height);

                 heigth  -  task's window heigth in pixels

Description:  GgiSetHeight must be called before any element is created.
              The supplied height will not be used when automatic layout is
              in effect or when element realization is postponed.

Updates:      Jan 13, 1998: JPT, Document created.
              Mar  9, 1998: JPT, renamed from GgiHeight
#<

#>            GgiHelpText.dc2
Function:     GgiHelpText

Purpose:      Modify a Ggi element's help text.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident element, result;
              char  *message;

              result = GgiHelpText(element, message);

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiLabel.dc2
Function:     GgiLabel

Purpose:      Create a text output Ggi element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident label;
              char *text;

              label = GgiLabel(text);

                 label  -  'handle' by which element can be referenced.
                 text   -  initially displayed text.

Description:  GgiLabel creates a text output element.
              If necessary, the displayed text can later be changed
              by calling GgiSetLabel().

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiList.dc2
Function:     GgiList

Purpose:      Create a Ggi list element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident list;
              char *keyword, **labels;
              int  defcol;

              list = GgiList(keyword, defcol, labels);

                 list     - 'handle' by which element can be referenced.
                 keyword  -  user input keyword.
                 defcol   -  default number of columns (see below).
                 labels   -  a NULL-pointer terminated list of pointers to
                             the list entry texts.

Description:  GgiList creates a list element. A list element consists of
              a rectangular area containing a list of strings formatted
              in rows and columns. The 'defcol' argument determines the
              number of columns. When >0, it is hint which may or may not
              be used. When <0, the number of columns is fixed to the
              absolute value of the argument.

              When a list entry is selected, it is highlighted and the
              corresponding index is written to the keyword. (The first
              element corresponds with index zero.)

Related document: GgiListLabels.dc2

Updates:      Nov  2, 1999: JPT, Document created.
              Jul 19, 2000: JPT, Implemented fixed number of columns.
#<

#>            GgiListColumns.dc2
Function:     GgiListColumns

Purpose:      Replace a list Ggi element's entry labels

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident list, result;
              int   columns;

              result = GgiListColumns(list, columns);

                 result   -  copy of the 'list' argument.
                 list     -  list element. Must have been created with GgiList.
                 columns  -  number of columns.

Description:  GgiListColumns changes the number of columns used to display
              the list. Positive numbers are hints, negative numbers force
              the number of columns to the absolute value of the argument.

Updates:      Jul 19, 2000: JPT, Document created.
#<

#>            GgiListLabels.dc2
Function:     GgiListLabels

Purpose:      Replace a list Ggi element's entry labels

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident list, result;
              char  **labels;

              result = GgiListLabels(list, labels);

                 result   -  copy of the 'list' argument.
                 list     -  list element. Must have been created with GgiList.
                 labels   -  a NULL-pointer terminated list of pointers to
                             the list entry texts.

Description:  GgiListLabels replaces a list element's entry labels.
              Because the current value of the keyword associated with the
              list may not be valid anymore, wkey_c() is called in order
              to undefine it.

Updates:      Nov  2, 1999: JPT, Document created.
#<

#>            GgiLogo.dc2
Function:     GgiLogo

Purpose:      Create a logo Ggi element.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident logo;

              logo = GgiLogo(NULL, 0, 0);

                 logo  -  'handle' by which element can be referenced.

Description:  GgiLogo creates a logo element.
              This causes the display of the GIPSY logo, which serves
              as a modest commercial. When the pointer is on this logo,
              an informational text is popped up.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiMenu.dc2
Function:     GgiMenu

Purpose:      Create a menu Ggi element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident menu;
              char *keyword, *message, **labels;

              menu = GgiMenu(keyword, message, labels);

                 menu     - 'handle' by which element can be referenced.
                 keyword  -  user input keyword.
                 message  -  help text which pops up when the pointer stays
                             in the input area for some time.
                 labels   -  a NULL-pointer terminated list of pointers to
                             the menu entry texts.

Description:  GgiMenu creates a menu element.
              When a menu entry is selected, the element writes an integer
              index by calling wkey_c(). (The first element corresponds
              with index zero.) This can be read by userint_c().

              The default menu label is the supplied keyword without the
              equals sign.

              The following menu entry texts have a special meaning.
              These 'entries' are normally counted, but they cannot
              be selected.

                _LINE     separator line
                _SPACE    5 pixels high blank separator
                _text     text separator ('text' is any text)

Related document: GgiMenuLabels.dc2

Updates:      Jan 13, 1998: JPT, Document created.
              Jul  2, 1998: JPT, Special entries implemented.
#<

#>            GgiMenuLabels.dc2
Function:     GgiMenuLabels

Purpose:      Replace a menu Ggi element's entry labels

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident menu, result;
              char  **labels;

              result = GgiMenuLabels(menu, labels);

                 result   -  copy of the 'menu' argument.
                 menu     -  menu element. Must have been created with GgiMenu.
                 labels   -  a NULL-pointer terminated list of pointers to
                             the menu entry texts.

Description:  GgiMenuLabels replaces a menu element's entry labels.
              Because the current value of the keyword associated with the
              menu may not be valid anymore, wkey_c() is called in order
              to undefine it.

Updates:      Mar 9, 1998: JPT, Document created.
#<

#>            GgiPostponeRealize.dc2
Function:     GgiPostponeRealize

Purpose:      Specify postponed Ggi element realization

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              bool later;

              GgiPostponeRealize(later);

                 later  -  if TRUE, Ggi will realize elements which will be
                           created at a later time when GgiRealize() is called.
                           if FALSE, elements will be realized immediately.

Description:  GgiPostponeRealize() must be called before any element is created.
              Postponing the realization of elements can have the following
              advantages:
              - window build-up will be faster;
              - all created elements will fit exactly in the task's main window.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiRealize.dc2
Function:     GgiRealize

Purpose:      Realize Ggi elements of which realization was postponed.

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"

              GgiRealize();

Description:  GgiRealize realizes the elements of which realization was
              postponed by GgiPostponeRealize().

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiIsRealized.dc2
Function:     GgiIsRealized

Purpose:      Check whether element has been realized

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident  element;
              bool result;

              result = GgiIsRealized(element);


Description:  GgiIsRealized() returns TRUE if the specified element has been
              realized, FALSE if not. The main purpose of this function is
              to check the status of plot fields (and canvasses) in pop-up
              shells. These can only be accessed if they have been
              realized, which is not done before the shell is popped up for
              the first time. Attempting to access an unrealized plot field
              or canvas causes the program to crash.
              
Updates:      Sep 17, 1999: JPT, Document created.
#<

#>            GgiSetCleanup.dc2
Function:     GgiSetCleanup

Purpose:      Set a Ggi element's post-delete cleanup method.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident element, result;
              void  *arg;
              GgiCleanupProc proc;

              result = GgiSetCleanup(element, proc, arg);

                 element  -  the element to be operated upon.
                 proc     -  pointer to cleanup function.
                 arg      -  argument to cleanup function.
                 result   -  copy of 'element'.

Description:  This function registers a callback function to be called
              when an element is deleted. Function prototype:

                 void proc(void*)

              A cleanup callback can be useful in cases when there are
              objects associated with the element wich must be dealt with
              when the element is deleted. Examples of such objects are
              dynamically allocated memory, other Ggi elements, keyword
              handlers, etc.

Updates:      Jul  5, 2000: JPT, Document created.
#<

#>            GgiSetLabel.dc2
Function:     GgiSetLabel

Purpose:      Modify a Ggi element's label.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident element, result;
              char  *label;
              int   width;

              result = GgiSetLabel(element, label, width);

                 element  -  the element to be operated upon.
                 label    -  the new label text'
                 width    -  optional new with in pixels; if zero, no change.
                 result   -  receives a copy the argument.

Description:  This function operates on all element types containing a label,
              including elements created by GgiLabel.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiSetPosition.dc2
Function:     GgiSetPosition

Purpose:      Position a Ggi element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident element, result, xid, yid;
              int   xfrom, yfrom;
 
              result = GgiSetPosition(element, xfrom, xid, yfrom, yid);

                 element  -  the element to be positioned.
                 xfrom    -  horizontal number of pixels from right edge of xid.
                 xid      -  other element, or NULL.
                 yfrom    -  vertical umber of pixels from bottom edge of yid.
                 yid      -  other element, or NULL.

                 If xid==NULL, the element is positioned relative to
                 the left edge of the task's main window; if yid==NULL,
                 it is positioned relative to the top edge.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiSetRange.dc2
Function:     GgiSetRange

Purpose:      Modify the range of a Ggi gauge or dial element (analog valuator).

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident valtor, result;
              float lowval, highval;

              result = GgiSetRange(gauge, lowval, highval);

                 valtor  - the gauge or dial element to be operated upon.
                 lowval  - the value corresponding with the lowest position.
                 highval - the value corresponding with the highest position.
                 result  - receives a copy the argument element.

                 lowval may be larger than highval.

Updates:      Jan 13, 1998: JPT, Document created.
              Jan 14, 1999: JPT, Modified for dial element.
#<

#>            GgiSetResources.dc2
Function:     GgiSetResources

Purpose:      Modify default fallback resources for Ggi

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              char **resources;

              GgiSetResources(resources);

                 resources  -  fallback resources as defined by the Xt,
                               the X Toolkit: a NULL-terminated array
                               of character pointers.

Description:  GgiSetResources allows the programmer to deviate from the
              default fallback resources defined by Ggi. Examples of things
              that can be modified are colours and fonts. A practical way
              of doing this is to inspect the resource string in this source
              file (ggi.c) and then define any new or deviating strings.

              There is a limited possiblity for setting resources of
              individual elements. This can only be done for elements
              created by GgiLabel(), GgiButton() or GgiMenu(). The name
              to be used in the resource specification is the text string
              used when GgiLabel was called, or the keyword without the
              equals sign for GgiButton and GgiMenu.

              GgiSetResources must be called before any element is created,
              i.e. in the initialization phase of the program.

Updates:      Jan 13, 1998: JPT, Document created.
              Jun 23, 1999: JPT, Specified resources now are merged.
              Dec 14, 2001: JPT, Fixed documentation w.r.t. argument.
#<

#>            GgiShowDoc.dc2
Function:     GgiShowDoc

Purpose:      Ggi document display routine

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              char *docname;

              GgiShowDoc(docname);

                 docname  -  document file name

Description:  Within Ggi there is only one document pop-up window available.
              If this window is not displayed, calling GgiShowDoc() pops
              up a text window with the file's contents.
              Otherwise the already present window is popped down.

Warning:      This routine is obsolete and should not be used in new software.
              GgiHelpMenu() should be used instead.

Updates:      Jan 13, 1998: JPT, Document created.
              Feb 17, 2000: JPT, Declared obsolete.
#<

#>            GgiTextField.dc2
Function:     GgiTextField

Purpose:      Create a text input Ggi element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident textfield;
              char *keyword, *message;
              int size;

              textfield = GgiTextField(keyword, message, size);

                textfield  -  'handle' by which element can be referenced.
                keyword    -  user input keyword.
                message    -  help text which pops up when the pointer stays
                              in the input area for some time.
                size       -  approximate size of the input field in characters.
                              ( actually: pixel_size = (size+1)*7 )

Description:  GgiTextField creates a text input element.
              On the screen is consists of a label field and right to this
              the actual text input field. The supplied keyword is taken as
              the label's default text.

              When typed into, it writes its contents to the keyword by
              calling wkey_c(); This can be read by any appropriate user
              input routine. If there is an error in the input which causes
              it to be rejected, an error message is displayed below the
              input field.

              The keyboard focus of multiple text input fields can be
              traversed by pressing the TAB key or the up and down arrows.
              The traversal order is the same as the order in which the
              elements were created.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiSetWidth.dc2
Function:     GgiSetWidth

Purpose:      Modify default Ggi window width.

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              int width;

              GgiSetWidth(width);

                 width  -  task's window width in pixels

Description:  GgiSetWidth must be called before any element is created.
              The supplied width will not be used when element
              realization is postponed.

Updates:      Jan 13, 1998: JPT, Document created.
              Mar  9, 1998: JPT, renamed from GgiWidth
#<

#>            GgiViewport.dc2
Function:     GgiViewport

Purpose:      Create a Ggi viewport element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident viewport;
              char *name;
              int  width, height;

              viewport = GgiViewport(keyword, width, height);

                 viewport  -  'handle' by which the element can be referenced.
                 keyword   -  the viewport's user input keyword.
                 width     -  the viewport's horizontal size.
                 height    -  the viewport's vertical size.

Description:  GgiViewport creates a viewport element. A viewport element can
              be used as a container for any number of other Ggi elements.
              These elements are positioned relative to the viewport's left
              and top edge or relative to other elements within the same
              viewport. If the contents of a viewport do not fit in any
              dimension, scrollbars will be added to it so that the user
              can move to any part of the contents.

              If width or height is specified as zero, that dimension of the
              viewport will be just large enough to enclose its initial
              contents.

              The keyword can be used to control or inquire which part of the
              viewport's contents is shown. It represents the center of the
              displayed part in normalized coordinates from (0,0) to (1,1).
              (0,0) is the upper left and (1,1) the lower right corner.
              E.g. if the keyword would be VIEWPOS=, then specifying
              VIEWPOS= 0.5 0.5 would cause the central part of the contents to
              be displayed. After the keyword has been modified at least once,
              manipulating the viewport's scrollbars will cause the keyword
              to be updated. The keyword can then be read to obtain the current
              display position.

              If a viewport element is  deleted, all elements contained by it
              will be deleted automatically.

Related document:
              GgiUseShell.dc2

Updates:      Nov  2, 1999: JPT, Document created.
              Oct  9, 2001: JPT, Implemented keyword.
#<

#>            GgiWindow.dc3
Function:     GgiWindow

Purpose:      Obtain the X Window used by a Ggi element

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              #include "ggiwindow.h"
              ident  element;
              Window result;

              result = GgiWindow(element);

                 result  -  the outer X Window used by the element.

Description:  GgiWindow() obtains the X Window used by a Ggi element.
              This information can be useful when elements must be manipulated
              outside Ggi, which may be the case for canvas elements.

Updates:      Jan 13, 1998: JPT, Document created.
#<

#>            GgiWidget.dc3
Function:     GgiWidget

Purpose:      Obtain the active widget of a Ggi element

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              #include "ggiwidget.h"
              ident  element;
              Widget result;

              result = GgiWidget(element);

                 result  -  the active widget of the element

Description:  GgiWidget() obtains the active widget of the element.
              This information can be useful when the widget must be
              manipulated outside Ggi, which may be the case for
              canvas elements.

Updates:      Feb 23, 1998: JPT, Document created.
#<

#>            GgiContext.dc3
Function:     GgiContext

Purpose:      Obtain the Ggi application context

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              #include "ggicontext.h"
              ident  element;
              XtAppContext *result;

              result = GgiContext();

                 result  -  pointer to the Ggi application context

Description:  GgiWidget() obtains the application context of a Ggi application.
              This information can be useful when the widget must be
              manipulated outside Ggi, which may be the case for
              canvas elements.

Updates:      Feb 23, 1998: JPT, Document created.
#<

#>            GgiPlotField.dc2
Function:     GgiPlotField

Purpose:      Create Ggi element implementing a PGPLOT window

Category:     USER-INTERFACE, GRAPHICS

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident plotfield;
              char *devicename;
              int  width, height;

              plotfield = GgiPlotField(devicename, width, height);

                plotfield   -  'handle' by which element can be referenced.
                devicename  -  device name to be used in later PGBEG call.
                width       -  horizontal size of window. If zero, the
                               existing element corresponding with
                               'devicename' is returned.
                height      -  vertical size of window.

Description:  GgiPlotField creates a plot window which can be used with
              the PGPLOT routines. Currently the maximum number of such
              windows is 100.
              
              All plot windows must have been created before any PGPLOT
              routine is called. Windows created later will be inoperative.

              The PGPLOT capabilities are identical to those of the X11
              plot server (plserver), except that there is no standard
              cursor capability because this is incompatible with the
              event-driven paradigm supported by Ggi.
              Cursor interaction can be obtained using a callback mechanism
              implemented by the function ScheduleGgiPlotCursor().

              Initially 16 standard colors are available. If more colors
              are needed, GgiPlotColors() can be called to adjust the
              number of colors.

Related documents:
              GgiPlotColors.dc2, GgiPlotXor.dc2, ScheduleGgiPlotCursor.dc2,
              DescheduleGgiPlotCursor.dc2.

Updates:      Feb 25, 1998: JPT, Document created.
              Mar  1, 1998: JPT, Return existing element for zero width.
              Jan 14, 1999: JPT, Document modified for GgiPlotColors.
#<

#>            GgiPlotXor.dc2
Function:     GgiPlotXor

Purpose:      Obtain and set XOR plotting mode.

Category:     USER-INTERFACE, GRAPHICS

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              bool oldmode, newmode;

              oldmode = GgiPlotXor(newmode);

                oldmode     -  current drawing mode.
                newmode     -  new drawing mode.

Description:  GgiPlotXor both obtains the current plotting mode and sets
              a new mode. XOR mode can be used to simulate non-destructive
              plotting.
              Using this mode, any part of a plot can be erased by simply
              drawing it again using the same colour as the first time.

Updates:      Mar  2, 1998: JPT, Document created.
              Jul  2, 1998: JPT, XOR mode maintained per plot window.
#<

#>            GgiWidth.dc2
Function:     GgiWidth

Purpose:      Obtain width of en element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident element;
              int width;

              width = GgiWidth(element);

                 width   - element's width in pixels

Updates:      Mar  9, 1998: JPT, Document created.
#<

#>            GgiHeight.dc2
Function:     GgiHeight

Purpose:      Obtain height of en element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident element;
              int height;

              height = GgiHeight(element);

                 height   - element's height in pixels

Updates:      Mar  9, 1998: JPT, Document created.
#<

#>            ScheduleGgiPlotCursor.dc2
Function:     ScheduleGgiPlotCursor

Purpose:      Register a callback routine receiving cursor events.

Category:     USER-INTERFACE, GRAPHICS

File:         ggi.src

Author:       J.P. Terlouw   

Use:
              #include "ggi.h"
              ident plotfield, result;
              GgiCursorProc proc;
              void *arg;

              result = ScheduleGgiPlotCursor(proc, plotfield, arg);

                result     -  registration identification (NULL when failed)
                proc       -  pointer to function.
                plotfield  -  plotfield created by GgiPlotField.
                arg        -  'client' data.

Description:  ScheduleGgiPlotCursor registers a function to be called
              when a cursor event occurs in the specified plot field.
              Such an event can be a mouse click, a pointer move or a
              key press.
               
              The prototype of 'proc' is:
                  void proc(ident id, GgiPlotInfo info, void *arg);

              Arguments:
                  id   - the registration identification.
                  info - pointer to struct described below.
                  arg  - the 'arg' argument from the registration call.

              The layout of the info struct is defined in ggi.h
              Relevant elements are:

                ident plotfield  -  Ggi element id
                char  *name      -  PGPLOT device name
                float x, y       -  cursor position in world coordinates
                int   button     -  identification of event-causing button
                int   state      -  mask of all active buttons and modifiers
                char  key        -  keyboard character
                
              Buttons and modifiers can be any of the following:
              
                ggiButn1  -  mouse button 1
                ggiButn2  -  mouse button 2
                ggiButn3  -  mouse button 3
                ggiShift  -  shift key
                ggiCtrl   -  control key
                ggiKeybd  -  other keyboard key
                
              Element 'state' contains the bit-wise OR of all buttons and
              modifiers depressed *before* the event occurred.
              If e.g. (only) mouse button 1 is depressed, 'button' will have
              the value ggiButn1 and 'state' will be zero. When button 1
              subsequently released, the callback function will be called
              again, but now with both 'button' and 'state' having the
              value ggiButn1.
              If instead of releasing the button, the mouse is moved,
              'state' will have the value ggiButn1 and 'button' will be zero.
              If an ASCII or an arrow key is typed, element 'button'
              will have the value ggiKeybd and element 'key' will contain the
              ASCII value or arrow code. The following symbolic constants
              have been defined for arrows: ggiLeftArrow, ggiUpArrow,
              ggiRightArrow and ggiDownArrow.


Updates:      Feb 25, 1998: JPT, Document created.
              Apr 13, 2000: JPT, Function is now also called for mouse moves
                                 without any button depressed.
              Apr 28, 2000: JPT, Implemented keyboard interaction.
              Mar 22, 2001: JPT, Arrow keys added.
#<

#>            DescheduleGgiPlotCursor.dc2
Function:     DescheduleGgiPlotCursor

Purpose:      De-register a function registered previously by
              ScheduleGgiPlotCursor.

Category:     USER-INTERFACE, GRAPHICS

File:         ggi.src

Author:       J.P. Terlouw

Use:          DescheduleGgiPlotCursor(&id);
              ident id  -  registration identification code.

Description:  The function associated with 'id' is descheduled and 'id' is
              set to zero. (Note that the argument is a pointer to 'id'.)

Related Doc:  ScheduleGgiPlotCursor.dc2

Updates:      Feb 25, 1998: JPT, Document created.
#<

#>            GgiSetBitmap.dc2
Function:     GgiSetBitmap

Purpose:      Specify a bitmap for a Ggi element's label.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident element, result;
              int   width, height;
              char  *bits;

              result = GgiSetBitmap(element, width, heigth, bits);

                 element  -  the element to be operated upon.
                 width    -  bitmap's width in pixels.
                 height   -  bitmap's height in pixels.
                 bits     -  the bitmap's bits.
                 result   -  receives a copy the argument.

Description:  This function currently operates on elements created by GgiLabel
              or GgiButton. As necessary it will be extended to other elements
              containing a label.

Updates:      Mar 25, 1998: JPT, Document created.
              Nov 25, 1999: JPT, Support added for GgiButton element.
#<

#>            GgiShell.dc2
Function:     GgiShell

Purpose:      Create a Ggi shell element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident shell;
              char *name;

              shell = GgiShell(name);

                 shell   -  'handle' by which element can be referenced.
                 name    -  the shell's name

Description:  GgiShell creates a shell element. A shell element is a more
              or less independent window which can contain any number of
              'ordinary' Ggi elements. It can be made visible or invisible
              (popped up or down) independent from the main window.
              If a shell element is  deleted, all elements contained by it
              will be deleted automatically.

Related documents:
              GgiUseShell.dc2, GgiShowShell.dc2, GgiDialog.dc2

Updates:      Mar 31, 1998: JPT, Document created.
              Mar 24, 1999: JPT, Recursive deletion implemented.
#<

#>            GgiUseShell.dc2
Function:     GgiUseShell

Purpose:      Use shell, form or viewport as container for new elements

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident shell, result;
              char *name;

              result = GgiUseShell(shell);

                 result  -  the container element previously used
                 shell   -  shell, form or viewport element or NULL if new
                            elements should be created in the main window again.

Description:  GgiUseShell causes new elements to be placed in the specified
              container element. To revert to the main window, the argument NULL
              can be used. If a container element is to be deleted, all elements
              contained by it must be deleted first.

Related documents:
              GgiShell.dc2, GgiForm.dc2, GgiShowShell.dc2

Updates:      Mar 31, 1998: JPT, Document created.
              May 13, 1998: JPT, Form element added
              Nov  2, 1999: JPT, Viewport element added.
#<

#>            GgiShowShell.dc2
Function:     GgiShowShell

Purpose:      Make shell element visible or invisible

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident shell;
              bool  show;

              GgiShowShell(shell, show);

                 shell   -  shell element.
                 show    -  TRUE: make element visible; FALSE: invisible.

Description:  GgiShowShell causes a shell element to be made visible or
              invisible (popped up or down). If the shell element is a
              dialog shell, interaction is limited to that shell until
              it is popped down.

Related documents:
              GgiShell.dc2, GgiUseShell.dc2

Updates:      Mar 31, 1998: JPT, Document created.
              Jun 24, 1998: JPT, Mention dialog shells.
#<

#>            GgiForm.dc2
Function:     GgiForm

Purpose:      Create a Ggi form element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident form;
              char *name;
              int border;

              form = GgiForm(name, border);

                 form    -  'handle' by which element can be referenced.
                 name    -  the form's name.
                 border  -  the form's border width.

Description:  GgiForm creates a form element. A form element can be used as
              a container for any number of 'ordinary' Ggi elements.
              These elements are positioned relative to the form's left and
              top edge or relative to other elements within the same form.
              If a form element is  deleted, all elements contained by it
              will be deleted automatically.

Related document:
              GgiUseShell.dc2

Updates:      May 13, 1998: JPT, Document created.
              Mar 24, 1999: JPT, Recursive deletion implemented.
#<

#>            GgiDialog.dc2
Function:     GgiDialog

Purpose:      Create a Ggi dialog shell element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident shell;
              char *name;

              shell = GgiDialog(name);

                 shell   -  'handle' by which element can be referenced.
                 name    -  the shell's name

Description:  GgiDialog creates a dialog shell element.
              A dialog shell element is a more or less independent window
              which can contain any number of 'ordinary' Ggi elements.
              It can be made visible or invisible (popped up or down)
              independent from the main window. Other than 'ordinary' shell
              elements created by GgiShell, interaction is limited to
              a popped-up dialog shell.
              If a dialog element is  deleted, all elements contained by it
              will be deleted automatically.

Related documents:
              GgiShell.dc2, GgiUseShell.dc2, GgiShowShell.dc2

Updates:      Jun 24, 1998: JPT, Document created.
              Mar 24, 1999: JPT, Recursive deletion implemented.

#<

#>            GgiHandleEvents.dc2
Function:     GgiHandleEvents

Purpose:      Explicitly process outstanding events

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              int result;

              result = GgiHandleEvents();

                 result - non-zero if one or more events were processed,
                          otherwise zero.

Description:  GgiHandleEvents() processes all events queued at the time of
              the call. It returns as soon as the event queue is empty.

              This function can be useful for checking user actions during
              a lengthy computation, e.g. to modify its parameters or abort
              it.

              Note that the use of this routine can introduce recursion.
              E.g. if it is called from a keyword handler and any of the
              processed events changes the keyword associated with this
              handler, the latter is called recursively.

Updates:      Sep 11, 1998: JPT, Document created.
#<

#>            GgiPlotColors.dc2
Function:     GgiPlotColors

Purpose:      Increase number of colours available to plotfield  elements.

Category:     USER-INTERFACE, GRAPHICS

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident plotter;
              int requested, obtained;

              obtained = GgiPlotColors(plotter, requested);

                 plotter    -  the plotfield element or NULL;
                 requested  -  the desired number of colors;
                 obtained   -  the number of colors that could be obtained.


Description:  GgiPlotColors' internal working is different for PseudoColor,
              DirectColor and TrueColor visuals. This is transparent to
              the calling program.

              In PseudoColor and DirectColor, GgiPlotColors() normally
              attempts to allocate colors from the standard X colormap.
              If this fails or if this has been requested by the user,
              a private colormap is created. In TrueColor, an internal
              color map is maintained. In the latter case colormap
              manipulation is emulated by repainting the window with
              the changed palette.

              The allocated colors are stored in a master table from which
              individual plotfields can use the colors they need. This is
              done by calling GgiPlotMapColors(). If the argument 'plotter'
              is not NULL, the complete master table is made available
              to the specified plotfield. If it is NULL, at least one
              plotfield must have been created before, otherwise no colors
              can be allocated and zero (0) will be returned.
              Initially color indices 0..15 in the master color table have been
              assigned a set of standard color representations. Indices
              16 and above are initially undefined and must be defined by  
              calling PGSCR.


Relates doc.: GgiPlotMapColors.dc2

Updates:      Jan 14, 1999: JPT, Document created.
              Sep 20, 1999: JPT, Implemented individual plotfield mappings.
              Mar 12, 2001: JPT, TrueColor support implemented.
#<

#>            GgiDial.dc2
Function:     GgiDial

Purpose:      Create a dial analog valuator Ggi element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident dial;
              char  *keyword, *message;
              int   radius, min_angle, max_angle, intervals;
              float arrow_width, inner_length, outer_length, low_val, high_val;

              dial = GgiDial( keyword,
                              message,
                              radius,
                              min_angle,
                              max_angle,
                              intervals,
                              arrow_width,
                              inner_length,
                              outer_length,
                              low_val,
                              high_val )

               dial         -  'handle' by which element can be referenced.
               keyword      -  user input keyword.
               message      -  help text which pops up when the pointer stays
                               in the dial for some time.
               radius       -  dial's radius in pixels.
               min_angle    -  the dial's minimum angle, measured in degrees
                               in clockwise direction from the lowest
                               (6 o'clock) position.
               max_angle    -  the dial's maximum angle.
               intervals    -  the number if tick-intervals around the dial.
               arrow_width  -  arrow width expressed as a fraction of radius.
               inner_length -  'inner' arrow length (fraction of radius).
               outer_length -  'outer' arrow length (fraction of radius).
               low_val      -  output value corresponding with min_angle.
               high_val     -  output value corresponding with max_angle.


Description:  GgiDial creates an analog valuator element based on the
              rheostat widget.
              On the screen is consists of a label field and the actual
              dial. By default the supplied keyword is taken as the
              label's value. 

              When it is manipulated, it writes its value to the keyword by
              calling wkey_c(). This can be read by any number reading
              routine e.g. userreal_c().

              The use of the layout parameters is illustrated in the html
              document $gip_htm/GgiDial/index.html .

Related documents: 
              GgiSetCircular.dc2, GgiSetFormat.dc2, GgiSetRange.dc2

Updates:      Jan 14, 1999: JPT, Document created.
              Jan 28, 1999: JPT, GgiSetFormat and GgiSetCircular added.

#<

#>            GgiPlotFrames.dc2
Function:     GgiPlotFrames

Purpose:      Obtain a number of pixmaps e.g. for movie loops.

Category:     USER-INTERFACE, GRAPHICS

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident plotter;
              int   requested, obtained; 

              obtained = GgiPlotFrames(plotter, requested);

                plotter     -  the associated plotfield element.
                requested   -  the desired number of frames.
                obtained    -  the number of frames actually obtained.

Description:  GgiPlotFrames tries to create a number of X11 pixmaps
              ("frames") of the same dimensions as the plotfield element.
              These can then later be used to store and retrieve plots.
              in a fast way, e.g. for implementing a movie loop.
              The pixmaps can only be created after the plotfield element
              has been opened at least once. It need not be open anymore
              when GgiPlotFrames is called.

Related documents:
              GgiPlotRecord.dc2, GgiPlotShow.dc2.

Updates:      Jan 25, 1999: JPT, Document created.
              Mar  8, 2000: JPT, Document changed.
#<

#>            GgiPlotRecord.dc2
Function:     GgiPlotRecord

Purpose:      Store a plot window in a pixmap ("frame").

Category:     USER-INTERFACE, GRAPHICS

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident plotter;
              int   frame

              GgiPlotRecord(plotter, frame);

                plotter   -  the plotfield element.
                frame     -  the frame number, starting with 0 (zero).

Description:  GgiPlotRecord records the current plotwindow in the specified
              frame.

Related documents:
              GgiPlotFrames.dc2, GgiPlotShow.dc2.

Updates:      Jan 25, 1999: JPT, Document created.
#<

#>            GgiPlotShow.dc2
Function:     GgiPlotShow

Purpose:      Recall a plot from a pixmap ("frame").

Category:     USER-INTERFACE, GRAPHICS

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident plotter;
              int   frame

              GgiPlotShow(plotter, frame);

                plotter   -  the plotfield element.
                frame     -  the frame number, starting with 0 (zero).

Description:  GgiPlotShow recalls the specified frame and displays it
              in the plot window.

Related documents:
              GgiPlotFrames.dc2, GgiPlotRecord.dc2.

Updates:      Jan 25, 1999: JPT, Document created.
#<

#>            GgiSetFormat.dc2
Function:     GgiSetFormat

Purpose:      Modify a Ggi valuator element's output format.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident element, result;
              char  *format;

              result = GgiSetFormat(element, format);

                 element  -  the element to be operated upon.
                 format   -  the new format.

Description:  This function operates on all valuator element types,
              currently GgiGauge and GgiDial. It modifies the format
              used to write the valuator's value to the associated keyword.
              When a valuator is created, its format is set to "%f".

Updates:      Jan 25, 1999: JPT, Document created.
#<

#>            GgiSetCircular.dc2
Function:     GgiSetCircular

Purpose:      Set a Ggi valuator element's circular mode.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident element, result;
              bool  mode;

              result = GgiSetCircular(element, mode);

                 element  -  the element to be operated upon.
                 mode     -  the new mode.

Description:  This function operates on all valuator element types,
              currently GgiGauge and GgiDial. It sets or clears an element's
              "circular mode", which affects the way keyword values
              outside an element's range are displayed.

              If circular mode is set, such values are mapped to an
              appropriate value within the range. This can be useful
              for angle values.

              If circular mode is clear, such values are mapped to
              one of the limit positions of the element.

              Note that the actual keyword value is not affected,
              only the way in which it is displayed.

Updates:      Jan 28, 1999: JPT, Document created.
#<

#>            GgiPrompter.dc2
Function:     GgiPrompter

Purpose:      Create a Ggi prompter element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              char *keydir, *message;

              GgiPrompter(keydir, message);

                 keydir  -  user input keyword or directive
                 message -  message associated with call

Description:  GgiPrompter creates a prompter. A prompter is a pop-up window
              in which a simple dialog can take place. It consists of a
              one-line prompt text, a type-in field, a text area for
              extra explanation and a cancel- and ok-button.

              As long as a prompter is active, interaction with other
              Ggi elements is not possible. This also implies than only
              one prompter can be present at any time.

              When using a prompter, a number of phases can be identified:

              1. create the prompter. In this case the arguments to
                 GgiPrompter() are the user input keyword and a prompt
                 message.
              2. reject the given input or ask confirmation. Here argument
                 keydir has the value "AGAIN" and message can be a one- or
                 two-line message with an explanation. This phase is
                 optional.
              3. accept the input with keydir value "ACCEPT". This causes
                 the prompter to be removed, after which interaction
                 with other Ggi elements is possible again.

              Apart from using the "AGAIN"-direcitive, input can also be
              rejected by Hermes or by calling reject_c() from the program.
              The prompter will then display the message associated with this
              rejection.

              When active, the prompter writes any value typed in the input
              field to the keyword specified when the prompter was created.
              When the user presses the cancel-button, an empty value is
              written to the keyword.

Keywords:     The following keywords are reserved for internal use:
              all keywords starting with "_GGIPR" and with "_GP".

See also:     Example program in the TESTBED section of ggiprompter.c.

Updates:      Mar 31, 1999: JPT, Document created.
              Mar 10, 2000: JPT, Internal keyword names standardized.

#<

#>            GgiPlotPrompter.dc2
Function:     GgiPlotPrompter

Purpose:      Create a Ggi plot prompter element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              char *filekeydir, *devkey, *message;

              GgiPlotPrompter(filekeydir, devkey, message);

                 filekeydir  -  user input keyword for a plot file name.
                 devkey      -  user input keyword for plot device name.
                 message     -  message associated with call.

Description:  GgiPlotPrompter creates a plot prompter. A plot prompter is a
              pop-up window in which a dialog takes place for specifying a
              PGPLOT device name for hardcopy plots and  an optional file name.

              It consists of:
              - a one-line prompt text;
              - a type-in field, where a filename can specified
                (this file is usually a PostScript file);
              - a menu from which any available hardcopy PGPLOT device
                can be selected;
              - a text area for extra explanation and error messages;
              - a "CANCEL" button;
              - a "PLOT"-button.

              As long as the prompter is active, interaction with other
              Ggi elements is not possible. This also implies than only
              one prompter can be present at any time.

              When using a prompter, a number of phases can be identified:

              1. create the prompter. In this case the arguments to
                 GgiPlotPrompter() are the file name- and device keywords
                 and a prompt message.
              2. reject the given input or ask confirmation. Here argument
                 filekeydir has the value "AGAIN" and message can be a one- or
                 two-line message with an explanation. This phase is
                 optional.
              3. accept the input with filekeydir value "ACCEPT". This causes
                 the prompter to be removed, after which interaction
                 with other Ggi elements is possible again.

              Apart from using the "AGAIN"-directive, input can also be
              rejected by Hermes or by calling reject_c() from the program.
              The prompter will then display the message associated with this
              rejection.

              Before the "PLOT"-button can be pressed, the user must first
              select a PGPLOT device name. This name is then written to
              the 'devkey' keyword.
              When the "PLOT"-button is pressed, the prompter writes any
              value typed in the input field to the 'filekeydir' keyword.
              When the user presses the "CANCEL"-button, an empty value is
              written to this keyword.

Keywords:     The following keywords are reserved for internal use:
              all keywords starting with "_GGIPR" and with "_GP".

See also:     Example program in the TESTBED section of ggiprompter.c.

Updates:      May 28, 1999: JPT, Document created.
              Mar 10, 2000: JPT, Internal keyword names standardized.
#<

#>            GgiSetKeyword.dc2
Function:     GgiSetKeyword

Purpose:      Modify a Ggi input element's user input keyword.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident element, result;
              char  *keyword;

              result = GgiSetKeyword(element, keyword);

                 element  -  the element to be operated upon.
                 keyword  -  the new keyword.
                 result   -  receives a copy of the first argument.

Description:  This function operates on all input element types.
              It modifies the keyword which is assiociated with the element.
              The displayed value does not immediately reflect the
              keyword's value. To achieve that, TriggerKey() can be called
              with the new keyword as argument.

See also:     TriggerKey.dc2

Updates:      Apr 27, 1999: JPT, Document created.
#<

#>            GgiProgress.dc2
Function:     GgiProgress

Purpose:      Create a Ggi progress bar

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident progress;
              char *keyword, *message;
              int   length;

              progress = GgiProgress(keyword, message, length);

                 progress -  'handle' by which element can be referenced.
                 keyword  -  user input keyword.
                 message  -  help text which pops up when the pointer stays
                             in the input area for some time.
                 length   -  element's length in pixels.

Description:  GgiProgress creates a progress bar.
              On the screen is consists of a rectangular bar which reflects
              the value of the associated user input keyword.
              This keyword is in fact not meant to be set by the user,
              but it should be set by the program uing WKEY. The value
              should be in the range 0.0 - 1.0.
              
Updates:      Jul  1, 1999: JPT, Document created.

#<

#>            GgiVerify.dc2
Function:     GgiVerify

Purpose:      Ask the user a simple question

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              bool result;
              char *message, *truelabel, *falselabel;

              result = GgiVerify(message, truelabel, falselabel);

                 message      -  the text of the question.
                 truelabel    -  text for "TRUE"-button
                 falseelabel  -  text for "FALSE"-button

Description:  GgiVerify creates a pop-up window with the question text
              and two push-down buttons. Pressing the first button will
              cause the function to return TRUE; pressing the second
              button will give FALSE. If NULL is specified for either
              truelabel or falseelabel, the text in the respective
              button will be "YES" or "NO".
              Because this function only returns after one of the buttons
              has been pressed, the application need not register any
              handlers to process the user actions.

Keywords:     The following keywords are reserved for internal use:
              "VFY_YES=" and "VFY_NO=".

Updates:      Aug  4, 1999: JPT, Document created.
#<

#>            GgiPlotMapColors.dc2
Function:     GgiPlotMapColors

Purpose:      Map plotfield colors to colors from the master table

Category:     USER-INTERFACE, GRAPHICS

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident plotter;

              GgiPlotMapColors(plotter, ...);

                 plotter -  the plotfield element.
                  ...    -  any number of pairs ci, nc :
                            int ci -  color index in master color table.
                                      -1 indicates end of arguments.
                            int nc -  number of colors to be used.


Description:  GgiPlotMapColors() builds a plotfield-specific color table
              from one or more spans from the master color table.
              This mechanism can be used to either separate or share
              colors in different plotfields.
              Initially color indices 0..15 in the master color table have been
              assigned a set of standard color representations. Indices
              16 and above are initially undefined and must be defined by
              calling PGSCR.

Example:      GgiPlotMapColors(p1, 0, 16, 16 16, -1);
              GgiPlotMapColors(p2, 0, 16, 32 16, -1);

              After this call, plotfields p1 and p2 share cells 0..15 from
              the master color table, and color indices 16..31 are different
              for p1 and p2.
              So if color index 3 is modified by calling PGSCR for
              plotfield p1, this change will also be visible in plotfield p2.
              But if color index 20 is modified, only p1 is affected.

Related doc.: GgiPlotColors.dc2

Updates:      Sep  6, 1999: JPT, Document created.
#<

#>            GgiPlotColorEditor.dc2
Function:     GgiPlotColorEditor

Purpose:      Create/manipulate Ggi colormap editing element for plot fields.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident editor;
              int   opcode;

              editor = GgiPlotColorEditor(opcode, ...);

              ... is a variable argument list of which the elements
                  depend on the value of opcode.

              The following variants exist:

              - create a colormap editor:

                  editor = GgiPlotColorEditor(ggiCreate, ci, nc);

                           int ci - starting color index                  
                           int nc - number of color indices
                           int bl - BLANK color

              - specify data limits:

                  editor = GgiPlotColorEditor(ggiLimits, id, v1, v2); 

                           ident id - existing colormap editor
                           float v1 - data value corresponding with lowest
                                      color index
                           float v2 - data value corresponding with highest
                                      color index

              - specify data units string:

                  editor = GgiPlotColorEditor(ggiUnits, id, units);

                           ident id    - existing colormap editor
                           char *units - zero-terminated character string

              - apply current settings to currently selected PGPLOT device:

                  editor = GgiPlotColorEditor(ggiApply, id);

                           ident id - existing colormap editor

              - obtain the current settings to be used in the application:

                  editor = GgiPlotColorEditor(ggiGetMap, id, nc,
                                              reds, greens, blues,
                                              bred, bgreen, bblue);

                            ident id - existing colormap editor
                            int   nc - number of colors to be obtained
                            float reds[nc], greens[nc], blues[nc]
                                     - arrays receiving the colormap components
                            float *bred, *bgreen, *bblue
                                     - variables receiving the blank color,
                                       or -1.0 if not available

Description:  GgiPlotColorEditor creates or manipulates a colormap editor.
              Such an editor operates on the part of the master color table
              specified by the arguments when the element is created.
              It consists of a color wedge, sliders which can manipulate
              the colormap's slope and shift, menus for choosing the type
              of color map and the scaling method, buttons for inverting
              and resetting the map and a button whichs pops up a file
              browser for selecting a user-defined color lookup table file.
              If argument "bl" is larger than 15, three extra elements
              are added:  a menu and an input field to specify a color,
              and a small plotfield showing this color.
              "bl" is assumed to be the color index for BLANK data values.
              It should be specified by calling PGSBCI in the plot for
              which the color map is being manipulated.

              When GgiPlotColorEditor is called with opcode ggiLimits and
              different v1 and v2, these values are used to annotate the
              wedge. Opcode ggiUnits can be used to display a text next to
              the wedge, e.g. specifying the data units.

              Opcode ggiApply can be used to apply the current settings to
              the currently selected PGPLOT device. This can be useful when
              for instance a hardcopy of a plot is being generated in which
              the current color setting is to be used. The color index range
              to be used must be specified before by calling PGSCIR.

              Opcode ggiGetMap is used to make the current setting available
              to the application. This can be useful when the application
              needs to transfer this information to an other program.

Keywords:     The state of the element is controlled by a number of
              user input keywords of the form "CE_<attr><n>=", where
              <attr> is the name of an attribute like "SLOPE" or "SHIFT".
              and <n> it the instance sequence number, starting with 1.
              The application may read and write these keywords.
              
Related documents:
              GgiPlotField.dc2, GgiPlotColors.dc2, GgiPlotMapColors.dc2


Updates:      Sep 17, 1999: JPT, Document created.
              Oct  4, 1999: JPT, BLANK specification implemented.
              Jul 12, 2000: JPT, Explained keywords.
              Sep  5, 2000: JPT, Implemented ggiGetMap.
              Dec  8, 2001: JPT, Added colormap "Renzo"
              Mar  7, 2002: JPT, Added file browser for user LUTs.
#<

#>            GgiFileBrowser.dc2
Function:     GgiFileBrowser

Purpose:      Ggi element for browsing directories and selecting files

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident element;
              int   opcode;

              element = GgiFileBrowser(opcode, ...);


              ... is a variable argument list of which the elements
                  depend on the value of opcode.

              The following variants exist:

              - create the element:

                  element = GgiFileBrowser(ggiCreate, key, message, converter);

                            char *key    - keyword receiving file name
                            char message - explanatory message
                            GgiNameconverterProc converter
                                         - converter function pointer or NULL.
                                           prototype: char *function(char *)


              - delete the element:

                  (void)GgiFileBrowser(ggiDelete, element);

                            ident element - element to be deleted


              - find existing element, given file name keyword:

                  element = GgiFileBrowser(ggiIdent, name);

                            char *name - user input keyword for file name
                          

              - set immediate selection; no OK button:

                  element = GgiFileBrowser(ggiNowait, element);

                            ident element - element to be modified

              - set immediate close after selection:

                  element = GgiFileBrowser(ggiCloseOnOK, element);

                            ident element - element to be modified

              - specify name converter function for detailed information:

                  element = GgiFileBrowser(ggiFileInfo, element, proc);

                            ident element             - element to be modified
                            GgiNameconverterProc proc - name converter

Description:  GgiFileBrowser creates or manipulates a pop-up window for
              browsing directories and selecting files. It consists of an
              input/output field for the current directory, two panes,
              an input/output field for resulting file name a close-button
              and an optional ok-button. The left pane lists all directories
              in the current directory and the right page lists the file
              names, possibly filtered by the optional converter function.
              The browser allows the user to specify a directory either by
              typing in the directory field or by selecting it in the left
              pane. Likewise, a name can be typed in the file name field
              or selected in the right pane. If an ok-button is present,
              this must be pressed first before the name becomes effective,
              i.e. before it is written to the output keyword.

              The function specified through the argument 'converter' enables
              the programmer to select and/or modify the file names found
              in the current directory. If this function returns NULL for
              a particular argument, no reference to this file will be
              included in the list. Modifying a file name can be useful
              for chopping off the filename 'extension'. The following
              example converter function selects GDS sets and chops off
              the .descr extension:

                  static char *setnames(char *name)
                  {
                     int  namlen=strlen(name);
                     static char result[FILENAME_MAX];
                  
                     if (namlen<7) return NULL;
                     if (strcmp(name+namlen-6, ".descr")) return NULL;
                     strcpy(result, name);
                     result[namlen-6] = '\0';
                     return result;  
                  }

              If a converter for detailed information has been specified,
              the element will receive an extra button labelled "DETAILS".
              When this button is depressed, the right pane will display
              the information generated by the converter. This function
              is called with the possibly already filtered and modified name.
              The result of a selection from the list is not affected by
              the format of the list entries.

Keywords:     All keywords beginning with "_GGIFB" are reserved for internal
              use by GgiFileBrowser.

Updates:      Nov 25, 1999: JPT, Document created.
              Dec  9, 1999: JPT, Improved internal keyword scheme.
              Mar 30, 2000: JPT, Immediate close implemented.
              Jul 11, 2000: JPT, Use keyword identifiers.
              Jul 21, 2000: JPT, Detailed information converter implemented.
#<

#>            GgiInset.dc2
Function:     GgiInset

Purpose:      Ggi element for specifying set, subset and box information.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident element;
              int   opcode;

              element = GgiInset(opcode, ...);


              ... is a variable argument list of which the elements
                  depend on the value of opcode.

              The following variants exist:

              - create the element:

                  element = GgiInset(ggiCreate, name, inset, box, message);

                            char *name   - user input keyword for set name
                            char *inset  - keyword for set, subset
                            char *box    - keyword for box or NULL
                            char message - explanatory message


              - delete the element:

                  (void)GgiInset(ggiDelete, element);

                            ident element - element to be deleted


              - find existing element, given set name keyword:

                  element = GgiInset(ggiIdent, name);

                            char *name - user input keyword for set name
                          

              - set dimensionality:

                  element = GgiInset(ggiNdims, element, ndim);

                            ident element - element to be modified
                            int ndim      - initial dimensionality

              - set immediate close after pressing OK button:

                  element = GgiInset(ggiCloseOnOK, element);

                            ident element - element to be modified 


Description:  GgiInset creates or manipulates a pop-up window for specifying
              set, subset and box information. This window initially consists
              of an explanatory text at the top, an input field for the set
              name, input/output fields for set, subset and box, a browse
              button, close and ok-buttons and an empty area.
                    When the user enters a set name, the empty area will
              be filled with as many lines as there are axes in the set.
              Each line consists of a text with the axis name, two buttons
              for selecting subset- or box mode, an input field and a text
              showing the range of the axis. If subset mode is selected,
              the corresponding axis name is incorporated in the set, subset
              specification, together with the contents of the input field.
              In box mode, the contents of the input fields is used for
              the box field. When the user is satisfied with the set, subset
              and box specifications, the ok-button can be pressed which
              causes the 'inset' and 'box' keywords to be written.
                    If 'box' is specified as NULL, there will be no box
              input/output field and box mode buttons. In this case only set
              and subsets can be specified.
                    In case the dimensionality has been set with opcode
              ggiNdims, the first 'ndim' axes are preset as box axes and
              the remaining axes as subset axes. If the set has fewer than
              'ndim' axes, an error message is displayed.
                    The browse button causes a file browser to be popped up
               from which a set name can be selected.

Keywords:     All keywords beginning with "_GGIIS" are reserved for internal
              use by GgiInset.
              
Updates:      Nov 25, 1999: JPT, Document created.
              Dec  9, 1999: JPT, Improved internal keyword scheme.
              Mar 30, 2000: JPT, Immediate close implemented.
              Jul 11, 2000: JPT, Header button added.
              Oct  2, 2000: JPT, Missing "close-on-ok" implemented.
#<

#>            GgiEditor.dc2
Function:     GgiEditor

Purpose:      Create a Ggi text editor

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident editor;
              char *keyword, *contents;
              int width, height, mode, size;

              editor = GgiEditor(keyword, width, height, mode, contents, size);

                editor   -  'handle' by which element can be referenced.
                keyword  -  user input keyword.
                width    -  element's width in pixels
                height   -  element's height in pixels
                mode     -  edit mode: one of ggiReadOnly, ggiEdit or ggiAppend
                contents -  depending on 'size': text to be edited or filename
                size     -  size of the text string, 0 or -1 (see below)

Description:  GgiEditor creates a text editor element.
              On the screen is consists of a rectangular area in which the
              text appears and, if necessary, one or two scrollbars.

              The associated keyword is set to 'YES' whenever the user has
              changed the text.

              The argument 'mode' determines whether the user can change the
              text and what changes are allowed. ggiReadOnly prevents the user
              from modifying the text, ggiEdit allows the user to edit the
              whole text and ggiAppend lets the user only add to the end.

              If the argument 'size' is zero or positive, the 'contents'
              argument contains the text to be edited. If it is -1,
              'contents' is the name of a file containing the text.
              If 'size' is zero, the editor element uses an internal copy of
              'contents' in which changes can be made. The original text is
              not modified. If it is larger than zero, changes are immediately
              reflected in the supplied string.

Related docs: GgiEditorSource.dc2, GgiEditorSave.dc2, GgiEditorSearch.dc2

Updates:      Feb 11, 2000: JPT, Document created.
#<

#>            GgiEditorSetSource.dc2
Function:     GgiEditorSetSource

Purpose:      Replace a Ggi text editor's text source

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident editor, result;
              char *source;
              int size;

              result = GgiEditorSetSource(editor, source, size);

                 result   -  copy of the 'editor' argument.
                 editor   -  editor element.
                 source   -  the new text source.
                 size     -  the size of the new text source.

Description:  GgiEditorSetSource replaces an editor element's text source.
              If the editor has been created with a size>0, the 'size' argument
              must reflect the size of the new text. Otherwise it should be
              0 or -1.
              The keyword associated with the editor is reset.

Updates:      Feb 11, 2000: JPT, Document created.
#<

#>            GgiEditorSource.dc2
Function:     GgiEditorSource

Purpose:      Obtain a Ggi text editor's text source

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident editor;
              char *result;

              result = GgiEditorSource(editor);

                 result   -  the editor's text source.
                 editor   -  editor element.

Description:  GgiEditorSource obtains an editor element's text source.
              Depending on how the editor was created, this string
              is either a file name or the text itself.

Updates:      Feb 14, 2000: JPT, Document created.
#<

#>            GgiEditorSave.dc2
Function:     GgiEditorSave

Purpose:      Save a Ggi text editor's contents to file.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident editor, result;

              result = GgiEditorSave(editor);

                 result   -  copy of the 'editor' argument.
                 editor   -  editor element.

Description:  GgiEditorSave saves an editor element's contents to file.
              The editor must have been created with a file source, i.e. with
              a 'size' of -1.
              The keyword associated with the editor is reset.

Updates:      Feb 11, 2000: JPT, Document created.
#<

#>            GgiEditorSaveAs.dc2
Function:     GgiEditorSaveAs

Purpose:      Save a Ggi text editor's contents to a specified file.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident editor, result;
              char *name;

              result = GgiEditorSaveAs(editor, name);

                 result   -  copy of the 'editor' argument.
                 editor   -  editor element.
                 name     -  name of the file to save to.

Description:  GgiEditorSaveAs saves an editor element's contents to file.
              The keyword associated with the editor is reset.

Updates:      Feb 14, 2000: JPT, Document created.
#<

#>            GgiEditorSearch.dc2
Function:     GgiEditorSearch

Purpose:      Pop up a search window for a text editor

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident editor, result;
              int direction;

              result = GgiEditorSearch(editor, direction);

                 result    -  copy of the 'editor' argument.
                 editor    -  editor element.
                 direction -  > 0 search forward; <= 0 search backward.

Updates:      Feb 11, 2000: JPT, Document created.
#<

#>            GgiHelpMenu.dc2
Function:     GgiHelpMenu

Purpose:      Create a help menu Ggi element

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident menu;
              char *keyword, *label, *message, **labels, **documents;

              menu = GgiHelpMenu(keyword, label, message, labels, documents);

                 menu      - 'handle' by which element can be referenced.
                 keyword   -  user input keyword.
                 label     -  label text for menu button.
                 message   -  help text for menu button and pop-up window.
                 labels    -  a NULL-pointer terminated list of pointers to
                              the menu entry texts.
                 documents -  a NULL-pointer terminated list of pointers to
                              document specifications.

Description:  GgiHelpMenu creates a menu element which interfaces with
              a pop-up containing a text editor element (read-only) and
              some control elements.

              When a menu entry is selected, the element opens the
              corresponding document in the text editor.

              The following menu entry texts have a special meaning.
              These 'entries' cannot be selected, but they must have
              a (NULL) counterpart in 'documents'.

                _LINE     separator line
                _SPACE    5 pixels high blank separator
                _text     text separator ('text' is any text)

              The document specifications are in the form of a filename
              optionally followed by a hash sign (#) and a number or text.
              If only a file name is specified, the whole file contents are
              displayed. If the name is followed by #n, only a section of
              the file is shown. In this case sections in the file must
              be delimited by lines of the form:
  
              #begin section n
                  and
              #end section n

              The element attempts to find the file in a number of directories,
              starting in the current directory and then in $gip_tsk/,
              $gip_doc/ and $gip_sub/.

Keywords:     All keywords beginning with "_GGIHM" are reserved for internal
              use by GgiHelpMenu.

Updates:      Feb 11, 2000: JPT, Document created.
              Jul 11, 2000: JPT, Use keyword identifiers.
#<

#>            GgiHeaderButton.dc2
Function:     GgiHeaderButton

Purpose:      Create a Ggi button which causes a set header to be displayed

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident button;
              char *keyword, *label, *message, *setkey;

              button = GgiHeaderButton(keyword, label, message, setkey)

                 button    - 'handle' by which element can be referenced.
                 keyword   -  user input keyword associated with the button.
                 label     -  label text for the button.
                 message   -  help text for the button.
                 setkey    -  user input keyword to obtain the setname from.

Description:  GgiHeaderButton creates a button element which interfaces with
              a pop-up containing a text editor element (read-only) and
              some control elements.

              When the button is pressed, a pop-up containing a text editor
              is popped up. (Or popped down if it is already on the screen.)
              Then the contents of 'setkey' are read and passed to the task
              HEADER which obtains and formats the set header. This is then
              displayed in the text editor.

Updates:      Jul 10, 2000: JPT, Document created.
#<

#>            GgiKeyIdent.dc3
Function:     GgiKeyIdent

Purpose:      Obtain identification number for a keyword.

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"

              int  result;
              char *keyword; 

              result = GgiKeyIdent(keyword);

                 result  -  identification number

Description:  GgiKeyIdent() returns an identification number given a
              text string. It is used for generating unique keywords
              on the basis of other keywords when combining the keywords
              themselves would result in too long a keyword.

              The function makes use of a moderately large hash table,
              which makes it likeley, but not certain, that in different
              runs the same identification number will be generated.
              If it is necessary that identification numbers for certain
              keywords will be the same, the application should present
              these keywords to GgiKeyIdent() before any other calls.

Updates:      Jul 11, 2000: JPT, Document created.
#<

#>            GgiColorMenu.dc2
Function:     GgiColorMenu

Purpose:      Create Ggi menu element for selecting standard PGPLOT colors.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident menu;
              char *keyword, *message, **labels;

              menu = GgiColorMenu(keyword, message);

                 menu     - 'handle' by which element can be referenced.
                 keyword  -  user input keyword.
                 message  -  help text which pops up when the pointer stays
                             in the input area for some time.

Description:  GgiColorMenu creates a menu element with 15 predefined label
              texts with color names.

              When a menu entry is selected, the element writes the integer
              color index corresponding with the the color name to the
              supplied keyword.

              The label text of the menu button is set to the currently
              selected color.

Updates:      Aug 18, 2000: JPT, Document created.
#<

#>            GgiTextMenu.dc2
Function:     GgiTextMenu

Purpose:      Create Ggi menu element returning label text instead of index.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident menu;
              char *keyword, *message, **labels;

              menu = GgiTextMenu(keyword, message, labels);

                 menu     - 'handle' by which element can be referenced.
                 keyword  -  user input keyword.
                 message  -  help text which pops up when the pointer stays
                             in the input area for some time.
                 labels   -  a NULL-pointer terminated list of pointers to
                             the menu entry texts.

Description:  GgiTextMenu creates a text menu element.

              When a menu entry is selected, the element writes the label
              text of the selected entry to the supplied keyword.
               
              The label texts pointed to bu the argument 'labels' mus remain
              valid as long as the element is used, i.e. it should be 
              declared static or have been created on the heap.
              
              As with other menus, the label texts can be modified later
              with GgiMenuLabels, but this does not affect the values which
              will be written to the keyword. The number of new labels must
              not exceed the original number.

Example:      Suppose you need a menu to specify a line width in a plot program.
              This can be created as follows:

                 static char *labels[] = {"1", "2", "4", NULL};
                 menu = GgiTextMenu("WIDTH=", "Line width", labels);

              In this menu the label texts are the values which will be
              assigned to the keyword WIDTH=. These values can subsequently
              be read with e.g. userfint(). If you don't like numbers as
              label texts, you can modify these texts, e.g.:

                 char *newlabels[] = {"Thin", "Normal", "Wide", NULL};
                 (void)GgiMenuLabels(menu, newlabels);

Updates:      Aug 18, 2000: JPT, Document created.
#<

#>            GgiSetDisplay.dc2
Function:     GgiSetDisplay

Purpose:      Override Ggi's default display name

Category:     USER-INTERFACE, SYSTEM

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              char *name

              GgiSetDisplay(name);

                 name  -  specifies the display, e.g. "kapteyn:0.0"

Description:  GgiSetDisplay must be called before any element is created.
              If GgiSetDisplay() is not called, or if it is called with
              a NULL or empty string argument, the default display from
              the DISPLAY environment variable is used.

Updates:      Jan 10, 2001: JPT, Document created.
#<

#>            ggihelp.doc
Document:     ggihelp

Purpose:      Contains help sections used in some Ggi help menus.

Category:     DOCUMENTATION

File:         ggi.src

Author:       J.P. Terlouw

Updates:      Jul 3, 2000: JPT, Document created.

#begin section ggiinset_box
Purpose:
--------
        This element allows you to "compose" a set/subset specification
        and a "box". A box is a coordinate range of any dimensionality.

Procedure:
----------
        First choose a set name. You can type a set name in the top input
        field or you can have the file browser do that for you. The file
        browser can be activated by pressing the "BROWSE" button. 

        When a set has been selected, the panel below is filled with as many
        lines as there are axes in the set. 

        Each line consists of a text with the axis name, two buttons labelled
        "SUB" and "BOX", an input field and a text showing the
        range of the axis. The two buttons are mutually exclusive.

        If "SUB" is selected, you can use the input field to enter a list of
        subsets along the axis. This information is then incorporated in
        the set/subset specification

        If "BOX" is selected, you can use the input field to enter a range in
        coordinates along the axis. This range is then applied to the contents
        of the "BOX" input field below.

        When you are satistfied with your set/subset and box specification,
        press the "OK" button to send them to the application.
        
Alternative:
------------
        Experienced users can either bypass the above procedure or modify its
        results by typing directly in the "Set/subset" and "BOX" input fields.
        These changes are not reflected in the fields above.

#end section ggiinset_box

#begin section ggiinset_nobox
Purpose:
--------
        This element allows you to "compose" a set/subset specification.

Procedure:
----------
        First choose a set name. You can type a set name in the top input
        field or you can have the file browser do that for you. The file
        browser can be activated by pressing the "BROWSE" button. 
        
        When a set has been selected, the panel below is filled with as many
        lines as there are axes in the set. 

        Each line consists of a text with the axis name, a button labelled
        "SUB", an input field and a text showing the range of the axis. 

        If "SUB" is selected, you can use the input field to enter a list of
        subsets along the axis. This information is then incorporated in
        the set/subset specification

        When you are satistfied with your set/subset specification,
        press the "OK" button to send it to the application.

Alternative:
------------
        Experienced users can either bypass the above procedure or modify its
        result by typing directly in the "Set/subset" input field. 
        These changes are not reflected in the fields above.
#end section ggiinset_nobox

#begin section ggiheaderbutton

   Set Header
   ==========

   This element allows you to inspect header information of the currently
   selected set. This information is generated by the task HEADER.

   You can control the output mode by typing in the "MODE" input
   field. For the possibilities select the "Output mode" entry.
#end section ggiheaderbutton

#end
#<

#>            GgiOptionsShell.dc3
Function:     GgiOptionsShell

Purpose:      Private to Ggi: pop-up shell for general options.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              char *key;
              
              GgiOptionsShell(key);

Description:  GgiOptionsShell() creates the pop-up shell. Will be popped up
              or down whenever key gets the value YES.

Updates:      May  8, 2001: JPT, Document created.

#<

#>            GgiOptionsButton.dc2
Function:     GgiOptionsButton

Purpose:      Create a button controlling the Ggi options shell

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:          
              #include "ggi.h"
              ident button;

              button = GgiOptionsButton();

Description:  GgiOptionsButton creates a button by which Ggi's general
              options pop-up shell can be popped up and down.
              The options shell currently contains controls for recording
              and monitoring events.

              By default the button's label and help text are "GUI" and
              "GUI Options Pop-up window", but these can be changed
              afterwards.

              This button's functionality is also implicitly available in
              every Ggi application which can be activated by clicking the
              right mouse button in any window's background.

Updates:      May  8, 2001: JPT, Document created.
#<

#>            GgiSetBtnKey.dc2
Function:     GgiSetBtnKey

Purpose:      Specify keyword through which mouse button is reported.

Category:     USER-INTERFACE

File:         ggi.src

Author:       J.P. Terlouw

Use:
              #include "ggi.h"
              ident element, result;
              char  *keyword;

              result = GgiSetBtnKey(element, keyword);

                 element  -  the element to be operated upon.
                 keyword  -  the keyword for reporting the mouse button.
                 result   -  receives a copy of the first argument.

Description:  This function currently only operates on the menu element type.
              After the call, the identity of the mouse button used to
              select a menu entry will be written to the specified keyword.
              The button identity is an integer with one of the values
              ggiButn1, ggiButn2 or ggiButn3.

Updates:      Nov  5, 2001: JPT, Document created.
#<
