]> granicus.if.org Git - graphviz/commitdiff
add lefty, dotty, lneato to graphviz2 tree
authorellson <devnull@localhost>
Thu, 6 Jan 2005 15:01:44 +0000 (15:01 +0000)
committerellson <devnull@localhost>
Thu, 6 Jan 2005 15:01:44 +0000 (15:01 +0000)
45 files changed:
cmd/lefty/ws/mswin32/garray.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gbutton.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gcanvas.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gcommon.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gcommon.h [new file with mode: 0644]
cmd/lefty/ws/mswin32/glabel.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gmenu.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gpcanvas.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gquery.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gscroll.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gtext.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/gview.c [new file with mode: 0644]
cmd/lefty/ws/mswin32/lefty.rc [new file with mode: 0644]
cmd/lefty/ws/mswin32/resource.h [new file with mode: 0644]
cmd/lefty/ws/none/garray.c [new file with mode: 0644]
cmd/lefty/ws/none/gbutton.c [new file with mode: 0644]
cmd/lefty/ws/none/gcanvas.c [new file with mode: 0644]
cmd/lefty/ws/none/gcommon.c [new file with mode: 0644]
cmd/lefty/ws/none/gcommon.h [new file with mode: 0644]
cmd/lefty/ws/none/glabel.c [new file with mode: 0644]
cmd/lefty/ws/none/gmenu.c [new file with mode: 0644]
cmd/lefty/ws/none/gpcanvas.c [new file with mode: 0644]
cmd/lefty/ws/none/gquery.c [new file with mode: 0644]
cmd/lefty/ws/none/gscroll.c [new file with mode: 0644]
cmd/lefty/ws/none/gtext.c [new file with mode: 0644]
cmd/lefty/ws/none/gview.c [new file with mode: 0644]
cmd/lefty/ws/x11/.cvsignore [new file with mode: 0644]
cmd/lefty/ws/x11/Makefile.am [new file with mode: 0644]
cmd/lefty/ws/x11/garray.c [new file with mode: 0644]
cmd/lefty/ws/x11/gbutton.c [new file with mode: 0644]
cmd/lefty/ws/x11/gcanvas.c [new file with mode: 0644]
cmd/lefty/ws/x11/gcommon.c [new file with mode: 0644]
cmd/lefty/ws/x11/gcommon.h [new file with mode: 0644]
cmd/lefty/ws/x11/glabel.c [new file with mode: 0644]
cmd/lefty/ws/x11/gmenu.c [new file with mode: 0644]
cmd/lefty/ws/x11/gpcanvas.c [new file with mode: 0644]
cmd/lefty/ws/x11/gquery.c [new file with mode: 0644]
cmd/lefty/ws/x11/gscroll.c [new file with mode: 0644]
cmd/lefty/ws/x11/gtext.c [new file with mode: 0644]
cmd/lefty/ws/x11/gview.c [new file with mode: 0644]
cmd/lefty/ws/x11/libfilereq/.cvsignore [new file with mode: 0644]
cmd/lefty/ws/x11/libfilereq/Dir.c [new file with mode: 0644]
cmd/lefty/ws/x11/libfilereq/Draw.c [new file with mode: 0644]
cmd/lefty/ws/x11/libfilereq/Makefile.am [new file with mode: 0644]
cmd/lefty/ws/x11/libfilereq/Path.c [new file with mode: 0644]

diff --git a/cmd/lefty/ws/mswin32/garray.c b/cmd/lefty/ws/mswin32/garray.c
new file mode 100644 (file)
index 0000000..1b797e3
--- /dev/null
@@ -0,0 +1,350 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WAU widget->u.a
+
+int GAcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    DWORD wflags;
+    int mode, ai;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    wflags = WS_CHILDWINDOW;
+    mode = G_AWVARRAY;
+    WAU->func = NULL;
+    ps.x = ps.y = MINAWSIZE;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINAWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           wflags |= WS_BORDER;
+           break;
+       case G_ATTRMODE:
+           if (Strcmp("horizontal", attrp[ai].u.t) == 0)
+               mode = G_AWHARRAY;
+           else if (Strcmp("vertical", attrp[ai].u.t) == 0)
+               mode = G_AWVARRAY;
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRLAYOUT:
+           if (Strcmp("on", attrp[ai].u.t) == 0) {
+               Gawsetmode(widget, FALSE);
+               WAU->mode = G_AWHARRAY;
+           } else if (Strcmp("off", attrp[ai].u.t) == 0) {
+               Gawsetmode(widget, TRUE);
+               WAU->mode = G_AWHARRAY;
+           } else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRRESIZECB:
+           WAU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    Gawinitialize(widget, mode);
+    Gadjustwrect(parent, &ps);
+    if (!
+       (widget->w =
+        CreateWindow("ArrayClass", "array", wflags, 0, 0, ps.x, ps.y,
+                     parent->w, (HMENU) (widget - &Gwidgets[0]),
+                     hinstance, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    ShowWindow(widget->w, SW_SHOW);
+    UpdateWindow(widget->w);
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawinsertchild(parent, widget);
+    return 0;
+}
+
+int GAsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Gwidget_t *parent;
+    PIXsize_t ps;
+    DWORD wflags1;
+    int ai;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    wflags1 = SWP_NOMOVE | SWP_NOZORDER;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINAWSIZE);
+/*            Gadjustwrect (parent, &ps);*/
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps.x, ps.y,
+                        wflags1);
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "borderwidth");
+           return -1;
+       case G_ATTRMODE:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "mode");
+           return -1;
+       case G_ATTRLAYOUT:
+           if (Strcmp("on", attrp[ai].u.t) == 0)
+               Gawsetmode(widget, FALSE);
+           else if (Strcmp("off", attrp[ai].u.t) == 0)
+               Gawsetmode(widget, TRUE);
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRRESIZECB:
+           WAU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GAgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    RECT r;
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GetWindowRect(widget->w, &r);
+           attrp[ai].u.s.x = r.right - r.left;
+           attrp[ai].u.s.y = r.bottom - r.top;
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTGETATTR, "borderwidth");
+           return -1;
+       case G_ATTRMODE:
+           attrp[ai].u.t = (WAU->mode == G_AWHARRAY) ?
+               "horizontal" : "vertical";
+           break;
+       case G_ATTRLAYOUT:
+           attrp[ai].u.t = (Gawgetmode(widget)) ? "off" : "on";
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", widget->w);
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRRESIZECB:
+           attrp[ai].u.func = WAU->func;
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GAdestroywidget(Gwidget_t * widget)
+{
+    Gwidget_t *parent;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawdeletechild(parent, widget);
+    Gawdestroy(widget);
+    DestroyWindow(widget->w);
+    return 0;
+}
+
+/* the rest of this file contains the implementation of the array widget */
+
+static void dolayout(Gwidget_t *, int);
+
+int Gaworder(Gwidget_t * widget, void *data, Gawordercb func)
+{
+    (*func) (data, &WAU->data);
+    dolayout(widget, TRUE);
+    return 0;
+}
+
+int Gawsetmode(Gwidget_t * widget, int mode)
+{
+    WAU->data.batchmode = mode;
+    dolayout(widget, TRUE);
+    return 0;
+}
+
+int Gawgetmode(Gwidget_t * widget)
+{
+    return WAU->data.batchmode;
+}
+
+void Gawdefcoordscb(int wi, Gawdata_t * dp)
+{
+    Gawcarray_t *cp;
+    int sx, sy, csx, csy, ci;
+
+    sx = dp->sx, sy = dp->sy;
+    csx = csy = 0;
+    for (ci = 0; ci < dp->cj; ci++) {
+       cp = &dp->carray[ci];
+       if (!cp->flag)
+           continue;
+       cp->ox = csx, cp->oy = csy;
+       if (dp->type == G_AWVARRAY)
+           cp->sx = sx - 2 * cp->bs, csy += cp->sy + 2 * cp->bs;
+       else
+           cp->sy = sy - 2 * cp->bs, csx += cp->sx + 2 * cp->bs;
+    }
+    if (dp->type == G_AWVARRAY)
+       dp->sy = csy;
+    else
+       dp->sx = csx;
+}
+
+void Gawinitialize(Gwidget_t * widget, int mode)
+{
+    WAU->data.type = mode;
+    if (!
+       (WAU->data.carray =
+        Marrayalloc((long) AWCARRAYINCR * AWCARRAYSIZE)))
+       panic(POS, "Gawinitialize", "cannot allocate carray");
+    WAU->data.cn = AWCARRAYINCR;
+    WAU->data.cj = 0;
+    WAU->data.batchmode = FALSE;
+    WAU->data.working = FALSE;
+}
+
+void Gawdestroy(Gwidget_t * widget)
+{
+    Marrayfree(WAU->data.carray);
+    WAU->data.cn = WAU->data.cj = 0;
+}
+
+void Gawresize(Gwidget_t * widget)
+{
+    dolayout(widget, FALSE);
+}
+
+void Gawinsertchild(Gwidget_t * parent, Gwidget_t * child)
+{
+    if (parent->u.a->data.cj == parent->u.a->data.cn) {
+       parent->u.a->data.carray = Marraygrow(parent->u.a->data.carray,
+                                             (long) (parent->u.a->data.
+                                                     cn +
+                                                     AWCARRAYINCR) *
+                                             AWCARRAYSIZE);
+       parent->u.a->data.cn += AWCARRAYINCR;
+    }
+    parent->u.a->data.carray[parent->u.a->data.cj++].w = child->w;
+    dolayout(parent, TRUE);
+}
+
+void Gawdeletechild(Gwidget_t * parent, Gwidget_t * child)
+{
+    int ci;
+
+    for (ci = 0; ci < parent->u.a->data.cj; ci++)
+       if (parent->u.a->data.carray[ci].w == child->w)
+           break;
+    if (ci < parent->u.a->data.cj) {
+       for (; ci + 1 < parent->u.a->data.cj; ci++)
+           parent->u.a->data.carray[ci].w =
+               parent->u.a->data.carray[ci + 1].w;
+       parent->u.a->data.cj--;
+       dolayout(parent, TRUE);
+    }
+}
+
+static void dolayout(Gwidget_t * widget, int flag)
+{
+    Gawdata_t *dp;
+    Gawcarray_t *cp;
+    RECT r;
+    int sx, sy, ci;
+
+    if (WAU->data.batchmode || WAU->data.working)
+       return;
+    WAU->data.working = TRUE;
+    dp = &WAU->data;
+    for (ci = 0; ci < dp->cj; ci++) {
+       cp = &dp->carray[ci];
+       GetWindowRect(cp->w, &r);
+       cp->flag = 1;
+       cp->ox = 0;
+       cp->oy = 0;
+       cp->sx = r.right - r.left;
+       cp->sy = r.bottom - r.top;
+       cp->bs = 0;
+    }
+    GetClientRect(widget->w, &r);
+    dp->sx = r.right - r.left, dp->sy = r.bottom - r.top;
+    if (WAU->func)
+       (*WAU->func) (widget - &Gwidgets[0], dp);
+    else
+       Gawdefcoordscb(widget - &Gwidgets[0], dp);
+    if ((sx = dp->sx) < MINAWSIZE)
+       sx = MINAWSIZE;
+    if ((sy = dp->sy) < MINAWSIZE)
+       sy = MINAWSIZE;
+    if (flag && (r.right - r.left != sx || r.bottom - r.top != sy)) {
+       sx -= (r.right - r.left);
+       sy -= (r.bottom - r.top);
+       GetWindowRect(widget->w, &r);
+       sx += (r.right - r.left);
+       sy += (r.bottom - r.top);
+       SetWindowPos(widget->w, (HWND) NULL, 0, 0, sx, sy,
+                    SWP_NOMOVE | SWP_NOZORDER);
+    }
+    for (ci = 0; ci < dp->cj; ci++) {
+       cp = &dp->carray[ci];
+       SetWindowPos(cp->w, (HWND) NULL, cp->ox, cp->oy, cp->sx, cp->sy,
+                    SWP_NOZORDER);
+    }
+    WAU->data.working = FALSE;
+}
diff --git a/cmd/lefty/ws/mswin32/gbutton.c b/cmd/lefty/ws/mswin32/gbutton.c
new file mode 100644 (file)
index 0000000..79760e4
--- /dev/null
@@ -0,0 +1,168 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WBU widget->u.b
+
+int GBcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    DWORD wflags;
+    char *s;
+    int ai;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    wflags = WS_CHILDWINDOW | BS_PUSHBUTTON;
+    WBU->func = NULL;
+    ps.x = ps.y = MINBWSIZE;
+    s = "button";
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINBWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           wflags |= WS_BORDER;
+           break;
+       case G_ATTRTEXT:
+           s = attrp[ai].u.t;
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRBUTTONCB:
+           WBU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    Gadjustwrect(parent, &ps);
+    if (!(widget->w = CreateWindow("BUTTON", s, wflags, 0, 0, ps.x, ps.y,
+                                  parent->w,
+                                  (HMENU) (widget - &Gwidgets[0]),
+                                  hinstance, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    ShowWindow(widget->w, SW_SHOW);
+    UpdateWindow(widget->w);
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawinsertchild(parent, widget);
+    return 0;
+}
+
+int GBsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Gwidget_t *parent;
+    PIXsize_t ps;
+    DWORD wflags1;
+    int ai;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    wflags1 = SWP_NOMOVE | SWP_NOZORDER;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINBWSIZE);
+           Gadjustwrect(parent, &ps);
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps.x, ps.y,
+                        wflags1);
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "borderwidth");
+           return -1;
+       case G_ATTRTEXT:
+           SetWindowText(widget->w, attrp[ai].u.t);
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRBUTTONCB:
+           WBU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GBgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    RECT r;
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GetWindowRect(widget->w, &r);
+           attrp[ai].u.s.x = r.right - r.left;
+           attrp[ai].u.s.y = r.bottom - r.top;
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTGETATTR, "borderwidth");
+           return -1;
+       case G_ATTRTEXT:
+           GetWindowText(widget->w, &Gbufp[0], Gbufn);
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", widget->w);
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRBUTTONCB:
+           attrp[ai].u.func = WBU->func;
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GBdestroywidget(Gwidget_t * widget)
+{
+    Gwidget_t *parent;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawdeletechild(parent, widget);
+    DestroyWindow(widget->w);
+    return 0;
+}
diff --git a/cmd/lefty/ws/mswin32/gcanvas.c b/cmd/lefty/ws/mswin32/gcanvas.c
new file mode 100644 (file)
index 0000000..e925119
--- /dev/null
@@ -0,0 +1,1389 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WCU widget->u.c
+#define WINDOW widget->u.c->window
+#define GC widget->u.c->gc
+#define ISVISIBLE(r) ( \
+    (r.o.x <= WCU->clip.c.x) && (r.c.x >= WCU->clip.o.x) && \
+    (r.o.y <= WCU->clip.c.y) && (r.c.y >= WCU->clip.o.y) \
+)
+
+#define max(a, b) (((a) >= (b)) ? (a) : (b))
+#define min(a, b) (((a) <= (b)) ? (a) : (b))
+
+static long gstyles[5] = {
+    /* G_SOLID */ PS_SOLID,
+    /* G_DASHED */ PS_DASH,
+    /* G_DOTTED */ PS_DOT,
+    /* G_LONGDASHED */ PS_DASH,
+    /* G_SHORTDASHED */ PS_DASH,
+};
+
+static char grays[][4] = {
+    {0x00, 0x00, 0x00, 0x00,},
+    {0x08, 0x00, 0x00, 0x00,},
+    {0x08, 0x00, 0x02, 0x00,},
+    {0x0A, 0x00, 0x02, 0x00,},
+    {0x0A, 0x00, 0x0A, 0x00,},
+    {0x0A, 0x04, 0x0A, 0x00,},
+    {0x0A, 0x04, 0x0A, 0x01,},
+    {0x0A, 0x05, 0x0A, 0x01,},
+    {0x0A, 0x05, 0x0A, 0x05,},
+    {0x0E, 0x05, 0x0A, 0x05,},
+    {0x0E, 0x05, 0x0B, 0x05,},
+    {0x0F, 0x05, 0x0B, 0x05,},
+    {0x0F, 0x05, 0x0F, 0x05,},
+    {0x0F, 0x0D, 0x0F, 0x05,},
+    {0x0F, 0x0D, 0x0F, 0x07,},
+    {0x0F, 0x0F, 0x0F, 0x07,},
+    {0x0F, 0x0F, 0x0F, 0x0F,},
+};
+
+static int curcursori = -1;
+
+static void bezier(PIXpoint_t, PIXpoint_t, PIXpoint_t, PIXpoint_t);
+static HFONT findfont(char *, int);
+static int scalebitmap(Gwidget_t *, Gbitmap_t *, Gsize_t, int, int);
+static void setgattr(Gwidget_t *, Ggattr_t *);
+
+static PIXrect_t rdrawtopix(Gwidget_t *, Grect_t);
+static PIXpoint_t pdrawtopix(Gwidget_t *, Gpoint_t);
+static PIXsize_t sdrawtopix(Gwidget_t *, Gsize_t);
+static Gsize_t spixtodraw(Gwidget_t *, PIXsize_t);
+static Grect_t rpixtodraw(Gwidget_t *, PIXrect_t);
+static PIXrect_t rdrawtobpix(Gbitmap_t *, Grect_t);
+static PIXpoint_t pdrawtobpix(Gbitmap_t *, Gpoint_t);
+
+int GCcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    LOGPALETTE pal[2];         /* the 2 here is to provide enough space
+                                  for palPalEntry[0] and [1] */
+    HBRUSH brush;
+    HPEN pen;
+    HBITMAP bmap;
+    HCURSOR cursor;
+    DWORD wflags;
+    int color, ai, i;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    wflags = WS_CHILDWINDOW;
+    WCU->func = NULL;
+    WCU->needredraw = FALSE;
+    WCU->buttonsdown = 0;
+    WCU->bstate[0] = WCU->bstate[1] = WCU->bstate[2] = 0;
+    ps.x = ps.y = MINCWSIZE;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINCWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           wflags |= WS_BORDER;
+           break;
+       case G_ATTRCURSOR:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRCOLOR:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRVIEWPORT:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRWINDOW:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WCU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    Gadjustwrect(parent, &ps);
+    WCU->wrect.o.x = 0.0, WCU->wrect.o.y = 0.0;
+    WCU->wrect.c.x = 1.0, WCU->wrect.c.y = 1.0;
+    WCU->vsize.x = ps.x, WCU->vsize.y = ps.y;
+    if (!(widget->w = CreateWindow("CanvasClass", "canvas", wflags, 0, 0,
+                                  ps.x, ps.y, parent->w,
+                                  (HMENU) (widget - &Gwidgets[0]),
+                                  hinstance, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    ShowWindow(widget->w, SW_SHOW);
+    UpdateWindow(widget->w);
+    SetCursor(LoadCursor((HINSTANCE) NULL, IDC_ARROW));
+    GC = GetDC(widget->w);
+    WCU->ncolor = 2;
+    pal[0].palVersion = 0x300; /* HA HA HA */
+    pal[0].palNumEntries = 2;
+    pal[0].palPalEntry[0].peRed = 255;
+    pal[0].palPalEntry[0].peGreen = 255;
+    pal[0].palPalEntry[0].peBlue = 255;
+    pal[0].palPalEntry[0].peFlags = 0;
+    pal[0].palPalEntry[1].peRed = 0;
+    pal[0].palPalEntry[1].peGreen = 0;
+    pal[0].palPalEntry[1].peBlue = 0;
+    pal[0].palPalEntry[1].peFlags = 0;
+    WCU->cmap = CreatePalette(&pal[0]);
+    WCU->colors[0].color = pal[0].palPalEntry[0];
+    for (i = 1; i < G_MAXCOLORS; i++)
+       WCU->colors[i].color = pal[0].palPalEntry[1];
+    SelectPalette(GC, WCU->cmap, FALSE);
+    RealizePalette(GC);
+    WCU->colors[0].inuse = TRUE;
+    WCU->colors[1].inuse = TRUE;
+    for (i = 2; i < G_MAXCOLORS; i++)
+       WCU->colors[i].inuse = FALSE;
+    WCU->gattr.color = 1;
+    brush = CreateSolidBrush(PALETTEINDEX(1));
+    SelectObject(GC, brush);
+    pen = CreatePen(PS_SOLID, 1, PALETTEINDEX(1));
+    SelectObject(GC, pen);
+    SetTextColor(GC, PALETTEINDEX(1));
+    SetBkMode(GC, TRANSPARENT);
+    WCU->gattr.width = 0;
+    WCU->gattr.mode = G_SRC;
+    WCU->gattr.fill = 0;
+    WCU->gattr.style = 0;
+    WCU->defgattr = WCU->gattr;
+    WCU->font = NULL;
+    if (Gdepth == 1) {
+       for (i = 0; i < 17; i++) {
+           if (!(bmap = CreateBitmap(4, 4, 1, 1, &grays[i][0])))
+               continue;
+           WCU->grays[i] = CreatePatternBrush(bmap);
+       }
+    }
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRCURSOR:
+           if (Strcmp(attrp[ai].u.t, "watch") == 0) {
+               curcursori = 1;
+               cursor = LoadCursor((HINSTANCE) NULL, IDC_WAIT);
+           } else if (Strcmp(attrp[ai].u.t, "default") == 0) {
+               curcursori = -1;
+               cursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
+           } else {
+               Gerr(POS, G_ERRNOSUCHCURSOR, attrp[ai].u.t);
+               return -1;
+           }
+           SetCursor(cursor);
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           WCU->colors[color].color.peRed = attrp[ai].u.c.r;
+           WCU->colors[color].color.peGreen = attrp[ai].u.c.g;
+           WCU->colors[color].color.peBlue = attrp[ai].u.c.b;
+           WCU->colors[color].color.peFlags = 0;
+           if (color >= WCU->ncolor)
+               ResizePalette(WCU->cmap, color + 1), WCU->ncolor =
+                   color + 1;
+           SetPaletteEntries(WCU->cmap, (int) color, 1,
+                             &WCU->colors[color].color);
+           RealizePalette(GC);
+           WCU->colors[color].inuse = TRUE;
+           if (color == WCU->gattr.color)
+               WCU->gattr.color = -1;
+           break;
+       case G_ATTRVIEWPORT:
+           if (attrp[ai].u.s.x == 0)
+               attrp[ai].u.s.x = 1;
+           if (attrp[ai].u.s.y == 0)
+               attrp[ai].u.s.y = 1;
+           WCU->vsize.x = (int) (attrp[ai].u.s.x + 0.5);
+           WCU->vsize.y = (int) (attrp[ai].u.s.y + 0.5);
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, WCU->vsize.x,
+                        WCU->vsize.y,
+                        SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
+           break;
+       case G_ATTRWINDOW:
+           if (attrp[ai].u.r.o.x == attrp[ai].u.r.c.x)
+               attrp[ai].u.r.c.x = attrp[ai].u.r.o.x + 1;
+           if (attrp[ai].u.r.o.y == attrp[ai].u.r.c.y)
+               attrp[ai].u.r.c.y = attrp[ai].u.r.o.y + 1;
+           WCU->wrect = attrp[ai].u.r;
+           break;
+       }
+    }
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawinsertchild(parent, widget);
+    Gadjustclip(widget);
+    return 0;
+}
+
+int GCsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    HCURSOR cursor;
+    Gwidget_t *parent;
+    PIXsize_t ps;
+    DWORD wflags1;
+    int ai, color;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    wflags1 = SWP_NOMOVE | SWP_NOZORDER;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINCWSIZE);
+           Gadjustwrect(parent, &ps);
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps.x, ps.y,
+                        wflags1);
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "borderwidth");
+           return -1;
+       case G_ATTRCURSOR:
+           if (Strcmp(attrp[ai].u.t, "watch") == 0) {
+               curcursori = 1;
+               cursor = LoadCursor((HINSTANCE) NULL, IDC_WAIT);
+           } else if (Strcmp(attrp[ai].u.t, "default") == 0) {
+               curcursori = -1;
+               cursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
+           } else {
+               Gerr(POS, G_ERRNOSUCHCURSOR, attrp[ai].u.t);
+               return -1;
+           }
+           SetCursor(cursor);
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           WCU->colors[color].color.peRed = attrp[ai].u.c.r;
+           WCU->colors[color].color.peGreen = attrp[ai].u.c.g;
+           WCU->colors[color].color.peBlue = attrp[ai].u.c.b;
+           WCU->colors[color].color.peFlags = 0;
+           if (color >= WCU->ncolor)
+               ResizePalette(WCU->cmap, color + 1), WCU->ncolor =
+                   color + 1;
+           SetPaletteEntries(WCU->cmap, (int) color, 1,
+                             &WCU->colors[color].color);
+           RealizePalette(GC);
+           WCU->colors[color].inuse = TRUE;
+           if (color == WCU->gattr.color)
+               WCU->gattr.color = -1;
+           break;
+       case G_ATTRVIEWPORT:
+           if (attrp[ai].u.s.x == 0)
+               attrp[ai].u.s.x = 1;
+           if (attrp[ai].u.s.y == 0)
+               attrp[ai].u.s.y = 1;
+           WCU->vsize.x = (int) (attrp[ai].u.s.x + 0.5);
+           WCU->vsize.y = (int) (attrp[ai].u.s.y + 0.5);
+           ps.x = WCU->vsize.x, ps.y = WCU->vsize.y;
+           Gadjustwrect(&Gwidgets[widget->pwi], &ps);
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps.x,
+                        ps.y, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
+           Gadjustclip(widget);
+           break;
+       case G_ATTRWINDOW:
+           if (attrp[ai].u.r.o.x == attrp[ai].u.r.c.x)
+               attrp[ai].u.r.c.x = attrp[ai].u.r.o.x + 1;
+           if (attrp[ai].u.r.o.y == attrp[ai].u.r.c.y)
+               attrp[ai].u.r.c.y = attrp[ai].u.r.o.y + 1;
+           WCU->wrect = attrp[ai].u.r;
+           Gadjustclip(widget);
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WCU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GCgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PALETTEENTRY *cp;
+    RECT r;
+    int color, ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GetWindowRect(widget->w, &r);
+           attrp[ai].u.s.x = r.right - r.left;
+           attrp[ai].u.s.y = r.bottom - r.top;
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTGETATTR, "borderwidth");
+           return -1;
+       case G_ATTRCURSOR:
+           attrp[ai].u.t = (curcursori == -1) ? "default" : "watch";
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           if (WCU->colors[color].inuse) {
+               cp = &WCU->colors[color].color;
+               attrp[ai].u.c.r = cp->peRed;
+               attrp[ai].u.c.g = cp->peGreen;
+               attrp[ai].u.c.b = cp->peBlue;
+           } else {
+               attrp[ai].u.c.r = -1;
+               attrp[ai].u.c.g = -1;
+               attrp[ai].u.c.b = -1;
+           }
+           break;
+       case G_ATTRVIEWPORT:
+           attrp[ai].u.s = WCU->vsize;
+           break;
+       case G_ATTRWINDOW:
+           attrp[ai].u.r = WCU->wrect;
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", widget->w);
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTREVENTCB:
+           attrp[ai].u.func = WCU->func;
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GCdestroywidget(Gwidget_t * widget)
+{
+    Gwidget_t *parent;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawdeletechild(parent, widget);
+    DestroyWindow(widget->w);
+    return 0;
+}
+
+int GCcanvasclear(Gwidget_t * widget)
+{
+    Ggattr_t attr;
+    RECT r;
+    HBRUSH brush, pbrush;
+
+    attr.flags = 0;
+    setgattr(widget, &attr);
+    brush = CreateSolidBrush(PALETTEINDEX(0));
+    pbrush = SelectObject(GC, brush);
+    GetClientRect(widget->w, &r);
+    Rectangle(GC, r.left, r.top, r.right, r.bottom);
+    SelectObject(GC, pbrush);
+    DeleteObject(brush);
+    WCU->needredraw = FALSE;
+    return 0;
+}
+
+int GCsetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    setgattr(widget, ap);
+    WCU->defgattr = WCU->gattr;
+    return 0;
+}
+
+int GCgetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    if ((ap->flags & G_GATTRCOLOR))
+       ap->color = WCU->gattr.color;
+    if ((ap->flags & G_GATTRWIDTH))
+       ap->width = WCU->gattr.width;
+    if ((ap->flags & G_GATTRMODE))
+       ap->mode = WCU->gattr.mode;
+    if ((ap->flags & G_GATTRFILL))
+       ap->fill = WCU->gattr.fill;
+    if ((ap->flags & G_GATTRSTYLE))
+       ap->style = WCU->gattr.style;
+    return 0;
+}
+
+int GCarrow(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    PIXpoint_t pp1, pp2, pa, pb, pd;
+    Grect_t gr;
+    double tangent, l;
+
+    if (gp1.x < gp2.x)
+       gr.o.x = gp1.x, gr.c.x = gp2.x;
+    else
+       gr.o.x = gp2.x, gr.c.x = gp1.x;
+    if (gp1.y < gp2.y)
+       gr.o.y = gp1.y, gr.c.y = gp2.y;
+    else
+       gr.o.y = gp2.y, gr.c.y = gp1.y;
+    if (!ISVISIBLE(gr))
+       return 1;
+    pp1 = pdrawtopix(widget, gp1), pp2 = pdrawtopix(widget, gp2);
+    pd.x = pp1.x - pp2.x, pd.y = pp1.y - pp2.y;
+    if (pd.x == 0 && pd.y == 0)
+       return 0;
+    tangent = atan2((double) pd.y, (double) pd.x);
+    if ((l = sqrt((double) (pd.x * pd.x + pd.y * pd.y))) > 30)
+       l = 30;
+    pa.x = l * cos(tangent + M_PI / 7) + pp2.x;
+    pa.y = l * sin(tangent + M_PI / 7) + pp2.y;
+    pb.x = l * cos(tangent - M_PI / 7) + pp2.x;
+    pb.y = l * sin(tangent - M_PI / 7) + pp2.y;
+    setgattr(widget, ap);
+    MoveToEx(GC, pp1.x, pp1.y, NULL), LineTo(GC, pp2.x, pp2.y);
+    MoveToEx(GC, pa.x, pa.y, NULL), LineTo(GC, pp2.x, pp2.y);
+    MoveToEx(GC, pb.x, pb.y, NULL), LineTo(GC, pp2.x, pp2.y);
+    return 0;
+}
+
+int GCline(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    PIXpoint_t pp1, pp2;
+    Grect_t gr;
+
+    if (gp1.x < gp2.x)
+       gr.o.x = gp1.x, gr.c.x = gp2.x;
+    else
+       gr.o.x = gp2.x, gr.c.x = gp1.x;
+    if (gp1.y < gp2.y)
+       gr.o.y = gp1.y, gr.c.y = gp2.y;
+    else
+       gr.o.y = gp2.y, gr.c.y = gp1.y;
+    if (!ISVISIBLE(gr))
+       return 1;
+    pp1 = pdrawtopix(widget, gp1), pp2 = pdrawtopix(widget, gp2);
+    setgattr(widget, ap);
+    MoveToEx(GC, pp1.x, pp1.y, NULL);
+    LineTo(GC, pp2.x, pp2.y);
+    return 0;
+}
+
+int GCbox(Gwidget_t * widget, Grect_t gr, Ggattr_t * ap)
+{
+    PIXrect_t pr;
+    Grect_t gr2;
+
+    if (gr.o.x <= gr.c.x)
+       gr2.o.x = gr.o.x, gr2.c.x = gr.c.x;
+    else
+       gr2.o.x = gr.c.x, gr2.c.x = gr.o.x;
+    if (gr.o.y <= gr.c.y)
+       gr2.o.y = gr.o.y, gr2.c.y = gr.c.y;
+    else
+       gr2.o.y = gr.c.y, gr2.c.y = gr.o.y;
+    if (!ISVISIBLE(gr2))
+       return 1;
+    pr = rdrawtopix(widget, gr);
+    setgattr(widget, ap);
+    if (WCU->gattr.fill)
+       Rectangle(GC, pr.o.x, pr.o.y, pr.c.x, pr.c.y);
+    else {
+       Gppp[0].x = pr.o.x, Gppp[0].y = pr.o.y;
+       Gppp[1].x = pr.c.x, Gppp[1].y = pr.o.y;
+       Gppp[2].x = pr.c.x, Gppp[2].y = pr.c.y;
+       Gppp[3].x = pr.o.x, Gppp[3].y = pr.c.y;
+       Gppp[4].x = pr.o.x, Gppp[4].y = pr.o.y;
+       Polyline(GC, Gppp, 5);
+    }
+    return 0;
+}
+
+int GCpolygon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    Grect_t gr;
+    int n, i;
+
+    if (gpn == 0)
+       return 0;
+    gr.o = gpp[0], gr.c = gpp[0];
+    for (i = 1; i < gpn; i++) {
+       gr.o.x = min(gr.o.x, gpp[i].x);
+       gr.o.y = min(gr.o.y, gpp[i].y);
+       gr.c.x = max(gr.c.x, gpp[i].x);
+       gr.c.y = max(gr.c.y, gpp[i].y);
+    }
+    if (!ISVISIBLE(gr))
+       return 1;
+    if (gpn + 1 > Gppn) {
+       n = (((gpn + 1) + PPINCR - 1) / PPINCR) * PPINCR;
+       Gppp = Marraygrow(Gppp, (long) n * PPSIZE);
+       Gppn = n;
+    }
+    for (i = 0; i < gpn; i++)
+       Gppp[i] = pdrawtopix(widget, gpp[i]);
+    setgattr(widget, ap);
+    if (WCU->gattr.fill) {
+       if (Gppp[gpn - 1].x != Gppp[0].x || Gppp[gpn - 1].y != Gppp[0].y)
+           Gppp[gpn] = Gppp[0], gpn++;
+       Polygon(GC, Gppp, (int) gpn);
+    } else
+       Polyline(GC, Gppp, (int) gpn);
+    return 0;
+}
+
+int GCsplinegon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    PIXpoint_t p0, p1, p2, p3;
+    Grect_t gr;
+    int n, i;
+
+    if (gpn == 0)
+       return 0;
+    gr.o = gpp[0], gr.c = gpp[0];
+    for (i = 1; i < gpn; i++) {
+       gr.o.x = min(gr.o.x, gpp[i].x);
+       gr.o.y = min(gr.o.y, gpp[i].y);
+       gr.c.x = max(gr.c.x, gpp[i].x);
+       gr.c.y = max(gr.c.y, gpp[i].y);
+    }
+    if (!ISVISIBLE(gr))
+       return 1;
+    Gppi = 1;
+    if (Gppi >= Gppn) {
+       n = (((Gppi + 1) + PPINCR - 1) / PPINCR) * PPINCR;
+       Gppp = Marraygrow(Gppp, (long) n * PPSIZE);
+       Gppn = n;
+    }
+    Gppp[0] = p3 = pdrawtopix(widget, gpp[0]);
+    for (i = 1; i < gpn; i += 3) {
+       p0 = p3;
+       p1 = pdrawtopix(widget, gpp[i]);
+       p2 = pdrawtopix(widget, gpp[i + 1]);
+       p3 = pdrawtopix(widget, gpp[i + 2]);
+       bezier(p0, p1, p2, p3);
+    }
+    setgattr(widget, ap);
+    if (WCU->gattr.fill) {
+       if (Gppp[Gppi - 1].x != Gppp[0].x || Gppp[Gppi - 1].y != Gppp[0].y)
+           Gppp[Gppi] = Gppp[0], Gppi++;
+       Polygon(GC, Gppp, (int) Gppi);
+    } else
+       Polyline(GC, Gppp, (int) Gppi);
+    return 0;
+}
+
+static void bezier(PIXpoint_t p0, PIXpoint_t p1,
+                  PIXpoint_t p2, PIXpoint_t p3)
+{
+    Gpoint_t gp0, gp1, gp2;
+    Gsize_t s;
+    PIXpoint_t p;
+    double t;
+    int n, i, steps;
+
+    if ((s.x = p3.x - p0.x) < 0)
+       s.x = -s.x;
+    if ((s.y = p3.y - p0.y) < 0)
+       s.y = -s.y;
+    if (s.x > s.y)
+       steps = s.x / 5 + 1;
+    else
+       steps = s.y / 5 + 1;
+    for (i = 0; i <= steps; i++) {
+       t = i / (double) steps;
+       gp0.x = p0.x + t * (p1.x - p0.x);
+       gp0.y = p0.y + t * (p1.y - p0.y);
+       gp1.x = p1.x + t * (p2.x - p1.x);
+       gp1.y = p1.y + t * (p2.y - p1.y);
+       gp2.x = p2.x + t * (p3.x - p2.x);
+       gp2.y = p2.y + t * (p3.y - p2.y);
+       gp0.x = gp0.x + t * (gp1.x - gp0.x);
+       gp0.y = gp0.y + t * (gp1.y - gp0.y);
+       gp1.x = gp1.x + t * (gp2.x - gp1.x);
+       gp1.y = gp1.y + t * (gp2.y - gp1.y);
+       p.x = gp0.x + t * (gp1.x - gp0.x) + 0.5;
+       p.y = gp0.y + t * (gp1.y - gp0.y) + 0.5;
+       if (Gppi >= Gppn) {
+           n = (((Gppi + 1) + PPINCR - 1) / PPINCR) * PPINCR;
+           Gppp = Marraygrow(Gppp, (long) n * PPSIZE);
+           Gppn = n;
+       }
+       Gppp[Gppi++] = p;
+    }
+}
+
+int GCarc(Gwidget_t * widget, Gpoint_t gc, Gsize_t gs, double ang1,
+         double ang2, Ggattr_t * ap)
+{
+    PIXpoint_t pc;
+    PIXsize_t ps;
+    Grect_t gr;
+    double a1, a2;
+
+    gr.o.x = gc.x - gs.x, gr.o.y = gc.y - gs.y;
+    gr.c.x = gc.x + gs.x, gr.c.y = gc.y + gs.y;
+    if (!ISVISIBLE(gr))
+       return 1;
+    pc = pdrawtopix(widget, gc), ps = sdrawtopix(widget, gs);
+    setgattr(widget, ap);
+    a1 = ang1 * M_PI / 180, a2 = ang2 * M_PI / 180;
+    if (WCU->gattr.fill)
+       Chord(GC, pc.x - ps.x, pc.y - ps.y, pc.x + ps.x, pc.y + ps.y,
+             (int) (cos(a1) * ps.x), (int) (sin(a1) * ps.x),
+             (int) (cos(a2) * ps.x), (int) (sin(a2) * ps.x));
+    else
+       Arc(GC, pc.x - ps.x, pc.y - ps.y, pc.x + ps.x, pc.y + ps.y,
+           (int) (cos(a1) * ps.x), (int) (sin(a1) * ps.x),
+           (int) (cos(a2) * ps.x), (int) (sin(a2) * ps.x));
+    return 0;
+}
+
+#define YSCALE ((WCU->vsize.y) / (WCU->wrect.c.y - WCU->wrect.o.y))
+
+int GCtext(Gwidget_t * widget, Gtextline_t * tlp, int n, Gpoint_t go,
+          char *fn, double fs, char *justs, Ggattr_t * ap)
+{
+    Gsize_t gs;
+    PIXpoint_t po;
+    PIXsize_t ps;
+    PIXrect_t pr;
+    Grect_t gr;
+    HFONT font;
+    SIZE size;
+    RECT r;
+    int x, y, w, h, i;
+
+    po = pdrawtopix(widget, go);
+    gs.x = 0, gs.y = fs;
+    ps = sdrawtopix(widget, gs);
+    if (!(font = findfont(fn, ps.y))) {
+       Rectangle(GC, po.x, po.y, po.x + 1, po.y + 1);
+       return 0;
+    }
+    setgattr(widget, ap);
+    SETFONT(font);
+    for (w = h = 0, i = 0; i < n; i++) {
+       if (tlp[i].n)
+           GetTextExtentPoint32(GC, tlp[i].p, (int) tlp[i].n, &size);
+       else
+           GetTextExtentPoint32(GC, "M", (int) 1, &size);
+       tlp[i].w = size.cx, tlp[i].h = size.cy;
+       w = max(w, size.cx), h += size.cy;
+    }
+    switch (justs[0]) {
+    case 'l':
+       po.x += w / 2;
+       break;
+    case 'r':
+       po.x -= w / 2;
+       break;
+    }
+    switch (justs[1]) {
+    case 'd':
+       po.y -= h;
+       break;
+    case 'c':
+       po.y -= h / 2;
+       break;
+    }
+    pr.o.x = po.x - w / 2, pr.o.y = po.y;
+    pr.c.x = po.x + w / 2, pr.c.y = po.y + h;
+    gr = rpixtodraw(widget, pr);
+    if (!ISVISIBLE(gr))
+       return 1;
+    for (i = 0; i < n; i++) {
+       switch (tlp[i].j) {
+       case 'l':
+           x = po.x - w / 2;
+           break;
+       case 'n':
+           x = po.x - tlp[i].w / 2;
+           break;
+       case 'r':
+           x = po.x - (tlp[i].w - w / 2);
+           break;
+       }
+       y = po.y + i * tlp[i].h;
+       r.left = x, r.top = y;
+       r.right = x + tlp[i].w, r.bottom = y + tlp[i].h;
+       DrawText(GC, tlp[i].p, (int) tlp[i].n, &r, DT_LEFT | DT_TOP);
+    }
+    return 0;
+}
+
+int GCgettextsize(Gwidget_t * widget, Gtextline_t * tlp, int n, char *fn,
+                 double fs, Gsize_t * gsp)
+{
+    Gsize_t gs;
+    PIXsize_t ps;
+    HFONT font;
+    int i;
+    SIZE size;
+
+    gs.x = 0, gs.y = fs;
+    ps = sdrawtopix(widget, gs);
+    if (!(font = findfont(fn, ps.y))) {
+       gsp->x = 1, gsp->y = 1;
+       return 0;
+    }
+    SETFONT(font);
+    for (ps.x = ps.y = 0, i = 0; i < n; i++) {
+       GetTextExtentPoint32(GC, tlp[i].p, (int) tlp[i].n, &size);
+       ps.x = max(ps.x, size.cx), ps.y += size.cy;
+    }
+    *gsp = spixtodraw(widget, ps);
+    return 0;
+}
+
+static HFONT findfont(char *name, int size)
+{
+    HFONT font;
+    int fi;
+
+    if (name[0] == '\000')
+       return Gfontp[0].font;
+
+    sprintf(&Gbufp[0], name, size);
+    for (fi = 0; fi < Gfontn; fi++)
+       if (Strcmp(&Gbufp[0], Gfontp[fi].name) == 0
+           && Gfontp[fi].size == size)
+           return Gfontp[fi].font;
+    font = CreateFont((int) size,
+                     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &Gbufp[0]);
+    if (!font)
+       font = Gfontp[0].font;
+
+    Gfontp = Marraygrow(Gfontp, (long) (Gfontn + 1) * FONTSIZE);
+    Gfontp[Gfontn].name = strdup(&Gbufp[0]);
+    Gfontp[Gfontn].size = size;
+    Gfontp[Gfontn].font = font;
+    Gfontn++;
+    return font;
+}
+
+int GCcreatebitmap(Gwidget_t * widget, Gbitmap_t * bitmap, Gsize_t s)
+{
+    if (!widget) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    if (!(bitmap->u.bmap.orig = CreateBitmap((int) s.x, (int) s.y, 1,
+                                            Gdepth, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    bitmap->u.bmap.scaled = 0;
+    bitmap->scale.x = bitmap->scale.y = 1;
+    bitmap->ctype = widget->type;
+    bitmap->canvas = widget - &Gwidgets[0];
+    bitmap->size = s;
+    return 0;
+}
+
+int GCdestroybitmap(Gbitmap_t * bitmap)
+{
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    DeleteObject(bitmap->u.bmap.orig);
+    if (bitmap->u.bmap.scaled)
+       DeleteObject(bitmap->u.bmap.scaled);
+    return 0;
+}
+
+int GCreadbitmap(Gwidget_t * widget, Gbitmap_t * bitmap, FILE * fp)
+{
+    Gsize_t s;
+    HDC gc;
+    char bufp[2048];
+    unsigned int rgb[3];
+    char *s1, *s2;
+    char c;
+    int bufn, bufi, step, x, y, k;
+
+    if (!widget) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    step = 0;
+    while (step < 3) {
+      l1:
+       if (!fgets(bufp, 2048, fp)) {
+           Gerr(POS, G_ERRCANNOTREADBITMAP);
+           return -1;
+       }
+       s1 = &bufp[0];
+      l2:
+       for (; *s1 && isspace(*s1); s1++);
+       if (!*s1 || *s1 == '#')
+           goto l1;
+       switch (step) {
+       case 0:
+           if (strncmp(s1, "P6", 2) != 0) {
+               Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           step++, s1 += 2;
+           goto l2;
+       case 1:
+           for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++);
+           c = *s2, *s2 = 0;
+           if (s2 == s1 || (s.x = atoi(s1)) <= 0) {
+               *s2 = c, Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           *s2 = c, step++, s1 = s2;
+           goto l2;
+       case 2:
+           for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++);
+           c = *s2, *s2 = 0;
+           if (s2 == s1 || (s.y = atoi(s1)) <= 0) {
+               *s2 = c, Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           *s2 = c, step++, s1 = s2;
+           goto l2;
+       }
+    }
+    if (!(bitmap->u.bmap.orig = CreateBitmap((int) s.x, (int) s.y, 1,
+                                            Gdepth, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    gc = CreateCompatibleDC(GC);
+    SelectObject(gc, bitmap->u.bmap.orig);
+    bitmap->u.bmap.scaled = 0;
+    bitmap->scale.x = bitmap->scale.y = 1;
+    bitmap->ctype = widget->type;
+    bitmap->canvas = widget - &Gwidgets[0];
+    bitmap->size = s;
+    bufi = bufn = 0;
+    bufp[bufi] = 0;
+    for (y = 0; y < s.y; y++) {
+       for (x = 0; x < s.x; x++) {
+           for (k = 0; k < 3; k++) {
+               if (bufi == bufn) {
+                   if ((bufn = fread(bufp, 1, 2047, fp)) == 0) {
+                       if (ferror(fp))
+                           bufn = -1;
+                       DeleteDC(gc);
+                       DeleteObject(bitmap->u.bmap.orig);
+                       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+                       return -1;
+                   }
+                   bufi = 0;
+               }
+               rgb[k] = (unsigned char) bufp[bufi++];
+           }
+           SetPixel(gc, x, y, RGB(rgb[0], rgb[1], rgb[2]));
+       }
+    }
+    DeleteDC(gc);
+    return 0;
+}
+
+int GCwritebitmap(Gbitmap_t * bitmap, FILE * fp)
+{
+    Gwidget_t *widget;
+    HDC gc;
+    COLORREF color;
+    char bufp[2048];
+    int bufi, x, y, w, h;
+
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    if (bitmap->canvas < 0 || bitmap->canvas >= Gwidgetn ||
+       !Gwidgets[bitmap->canvas].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, bitmap->canvas);
+       return -1;
+    }
+    widget = &Gwidgets[bitmap->canvas];
+    if (widget->type != G_CANVASWIDGET && widget->type != G_PCANVASWIDGET) {
+       Gerr(POS, G_ERRNOTACANVAS, bitmap->canvas);
+       return -1;
+    }
+    gc = CreateCompatibleDC(GC);
+    SelectObject(gc, bitmap->u.bmap.orig);
+    fprintf(fp, "P6\n%d %d 255\n", (int) bitmap->size.x,
+           (int) bitmap->size.y);
+    bufi = 0;
+    w = bitmap->size.x;
+    h = bitmap->size.y;
+    for (y = 0; y < h; y++) {
+       for (x = 0; x < w; x++) {
+           color = GetPixel(gc, x, y);
+           bufp[bufi++] = GetRValue(color);
+           bufp[bufi++] = GetGValue(color);
+           bufp[bufi++] = GetBValue(color);
+           if (bufi + 3 >= 2048) {
+               fwrite(bufp, 1, bufi, fp);
+               bufi = 0;
+           }
+       }
+    }
+    if (bufi > 0)
+       fwrite(bufp, 1, bufi, fp);
+    DeleteDC(gc);
+    return 0;
+}
+
+int GCbitblt(Gwidget_t * widget, Gpoint_t gp, Grect_t gr,
+            Gbitmap_t * bitmap, char *mode, Ggattr_t * ap)
+{
+    PIXrect_t pr, r;
+    PIXpoint_t pp;
+    PIXsize_t s;
+    Gsize_t scale;
+    Gxy_t p;
+    HBITMAP pix;
+    HDC gc;
+    double tvx, tvy, twx, twy;
+
+    if (gr.o.x > gr.c.x)
+       p.x = gr.o.x, gr.o.x = gr.c.x, gr.c.x = p.x;
+    if (gr.o.y > gr.c.y)
+       p.y = gr.o.y, gr.o.y = gr.c.y, gr.c.y = p.y;
+    if (strcmp(mode, "b2c") == 0) {
+       if (!ISVISIBLE(gr))
+           return 1;
+       tvx = WCU->vsize.x, tvy = WCU->vsize.y;
+       twx = WCU->wrect.c.x - WCU->wrect.o.x;
+       twy = WCU->wrect.c.y - WCU->wrect.o.y;
+       scale.x = tvx / twx, scale.y = tvy / twy;
+       if (scale.x == 1 && scale.y == 1)
+           pix = bitmap->u.bmap.orig;
+       else {
+           if (scale.x != bitmap->scale.x || scale.y != bitmap->scale.y)
+               scalebitmap(widget, bitmap, scale, TRUE, 1);
+           pix = bitmap->u.bmap.scaled;
+       }
+       pr = rdrawtopix(widget, gr);
+       pp = pdrawtobpix(bitmap, gp);
+       s.x = pr.c.x - pr.o.x + 1, s.y = pr.c.y - pr.o.y + 1;
+       r.o.x = pp.x, r.o.y = pp.y - s.y + 1;
+       r.c.x = r.o.x + s.x - 1, r.c.y = r.o.y + s.y - 1;
+       if (r.o.x < 0)
+           pr.o.x -= r.o.x, r.o.x = 0;
+       if (r.o.y < 0)
+           pr.o.y -= r.o.y, r.o.y = 0;
+       if (r.c.x >= bitmap->size.x * scale.x) {
+           pr.c.x -= (r.c.x + 1 - bitmap->size.x * scale.x);
+           r.c.x = bitmap->size.x * scale.x - 1;
+       }
+       if (r.c.y >= bitmap->size.y * scale.y) {
+           pr.c.y -= (r.c.y + 1 - bitmap->size.y * scale.y);
+           r.c.y = bitmap->size.y * scale.y - 1;
+       }
+       if (pr.o.x < 0)
+           r.o.x -= pr.o.x, pr.o.x = 0;
+       if (pr.o.y < 0)
+           r.o.y -= pr.o.y, pr.o.y = 0;
+       setgattr(widget, ap);
+       gc = CreateCompatibleDC(GC);
+       SelectObject(gc, pix);
+       BitBlt(GC, pr.o.x, pr.o.y, r.c.x - r.o.x + 1, r.c.y - r.o.y + 1,
+              gc, r.o.x, r.o.y, (WCU->gattr.mode == G_SRC) ?
+              SRCCOPY : SRCINVERT);
+       DeleteDC(gc);
+    } else if (strcmp(mode, "c2b") == 0) {
+       tvx = WCU->vsize.x, tvy = WCU->vsize.y;
+       twx = WCU->wrect.c.x - WCU->wrect.o.x;
+       twy = WCU->wrect.c.y - WCU->wrect.o.y;
+       scale.x = tvx / twx, scale.y = tvy / twy;
+       if (scale.x == 1 && scale.y == 1)
+           pix = bitmap->u.bmap.orig;
+       else {
+           if (scale.x != bitmap->scale.x || scale.y != bitmap->scale.y)
+               scalebitmap(widget, bitmap, scale, FALSE, 1);
+           pix = bitmap->u.bmap.scaled;
+       }
+       pr = rdrawtobpix(bitmap, gr);
+       pp = pdrawtopix(widget, gp);
+       s.x = pr.c.x - pr.o.x + 1, s.y = pr.c.y - pr.o.y + 1;
+       r.o.x = pp.x, r.o.y = pp.y - s.y + 1;
+       r.c.x = r.o.x + s.x - 1, r.c.y = r.o.y + s.y - 1;
+       if (pr.o.x < 0)
+           r.o.x -= pr.o.x, pr.o.x = 0;
+       if (pr.o.y < 0)
+           r.o.y -= pr.o.y, pr.o.y = 0;
+       if (pr.c.x >= bitmap->size.x * scale.x) {
+           r.c.x -= (pr.c.x + 1 - bitmap->size.x * scale.x);
+           pr.c.x = bitmap->size.x * scale.x - 1;
+       }
+       if (pr.c.y >= bitmap->size.y * scale.y) {
+           r.c.y -= (pr.c.y + 1 - bitmap->size.y * scale.y);
+           pr.c.y = bitmap->size.y * scale.y - 1;
+       }
+       if (r.o.x < 0)
+           pr.o.x -= r.o.x, r.o.x = 0;
+       if (r.o.y < 0)
+           pr.o.y -= r.o.y, r.o.y = 0;
+       setgattr(widget, ap);
+       gc = CreateCompatibleDC(GC);
+       SelectObject(gc, pix);
+       BitBlt(gc, pr.o.x, pr.o.y, r.c.x - r.o.x + 1, r.c.y - r.o.y + 1,
+              GC, r.o.x, r.o.y, (WCU->gattr.mode == G_SRC) ?
+              SRCCOPY : SRCINVERT);
+       if (pix != bitmap->u.bmap.orig)
+           scalebitmap(widget, bitmap, scale, TRUE, -1);
+       DeleteDC(gc);
+    }
+    return 0;
+}
+
+static int scalebitmap(Gwidget_t * widget, Gbitmap_t * bitmap,
+                      Gsize_t scale, int copybits, int dir)
+{
+    Gsize_t nsize, o2n;
+    HBITMAP opix, spix;
+    COLORREF color;
+    HDC gc1, gc2;
+    int x, y, x2, y2, xp, yp;
+    double prod, rgb[3], xr2, yr2, xl2, yl2, xf2, yf2, xr, yr, xl, yl;
+
+    if (!copybits) {
+       if (dir == 1) {
+           nsize.x = (int) (bitmap->size.x * scale.x);
+           nsize.y = (int) (bitmap->size.y * scale.y);
+           if (!(spix = CreateBitmap((int) nsize.x, (int) nsize.y, 1,
+                                     Gdepth, NULL))) {
+               Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+               return -1;
+           }
+           if (bitmap->u.bmap.scaled)
+               DeleteObject(bitmap->u.bmap.scaled);
+           bitmap->u.bmap.scaled = spix;
+           bitmap->scale = scale;
+       }
+       return 0;
+    }
+    if (dir == 1) {
+       nsize.x = (int) (bitmap->size.x * scale.x);
+       nsize.y = (int) (bitmap->size.y * scale.y);
+       o2n.x = 1 / scale.x, o2n.y = 1 / scale.y;
+       if (!(spix = CreateBitmap((int) nsize.x, (int) nsize.y, 1,
+                                 Gdepth, NULL))) {
+           Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+           return -1;
+       }
+       opix = bitmap->u.bmap.orig;
+    } else {
+       nsize.x = (int) bitmap->size.x;
+       nsize.y = (int) bitmap->size.y;
+       o2n.x = scale.x, o2n.y = scale.y;
+       spix = bitmap->u.bmap.orig;
+       opix = bitmap->u.bmap.scaled;
+    }
+    gc1 = CreateCompatibleDC(GC);
+    SelectObject(gc1, opix);
+    gc2 = CreateCompatibleDC(GC);
+    SelectObject(gc2, spix);
+    prod = o2n.x * o2n.y;
+    y = 0;
+    yr = o2n.y;
+    yl = 0;
+    for (yp = 0; yp < nsize.y; yp++) {
+       x = 0;
+       xr = o2n.x;
+       xl = 0;
+       for (xp = 0; xp < nsize.x; xp++) {
+           y2 = y;
+           yr2 = yr;
+           yl2 = yl;
+           rgb[0] = rgb[1] = rgb[2] = 0;
+           do {
+               x2 = x;
+               xr2 = xr;
+               xl2 = xl;
+               yf2 = (yl2 + yr2 > 1) ? 1 - yl2 : yr2, yr2 -= yf2;
+               do {
+                   xf2 = (xl2 + xr2 > 1) ? 1 - xl2 : xr2, xr2 -= xf2;
+                   color = GetPixel(gc1, x2, y2);
+                   rgb[0] += (GetRValue(color) * xf2 * yf2 / prod);
+                   rgb[1] += (GetGValue(color) * xf2 * yf2 / prod);
+                   rgb[2] += (GetBValue(color) * xf2 * yf2 / prod);
+                   xl2 += xf2;
+                   if (xl2 >= 1)
+                       x2++, xl2 -= 1;
+               } while (xr2 > 0);
+               xr2 = o2n.x;
+               yl2 += yf2;
+               if (yl2 >= 1)
+                   y2++, yl2 -= 1;
+           } while (yr2 > 0);
+           yr2 = o2n.y;
+           SetPixel(gc2, xp, yp, RGB(rgb[0], rgb[1], rgb[2]));
+           x = x2;
+           xr = xr2;
+           xl = xl2;
+       }
+       y = y2;
+       yr = yr2;
+       yl = yl2;
+    }
+    DeleteDC(gc1);
+    DeleteDC(gc2);
+    if (dir == 1) {
+       if (bitmap->u.bmap.scaled)
+           DeleteObject(bitmap->u.bmap.scaled);
+       bitmap->u.bmap.scaled = spix;
+       bitmap->scale = scale;
+    }
+    return 0;
+}
+
+int GCgetmousecoords(Gwidget_t * widget, Gpoint_t * gpp, int *count)
+{
+    PIXpoint_t pp;
+    POINT p;
+    int n1, n2, n3;
+
+    GetCursorPos(&p);
+    ScreenToClient(widget->w, &p);
+    pp.x = p.x, pp.y = p.y;
+    *gpp = ppixtodraw(widget, pp);
+    n1 = GetAsyncKeyState(VK_LBUTTON);
+    n2 = GetAsyncKeyState(VK_MBUTTON);
+    n3 = GetAsyncKeyState(VK_RBUTTON);
+    *count = (n1 < 0 ? 1 : 0) + (n2 < 0 ? 1 : 0) + (n3 < 0 ? 1 : 0);
+    return 0;
+}
+
+static void setgattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    HBRUSH brush, pbrush;
+    HPEN pen, ppen;
+    PALETTEENTRY *colorp;
+    long color, mode, style, width, flag, pati;
+    double intens;
+
+    if (!(ap->flags & G_GATTRCOLOR))
+       ap->color = WCU->defgattr.color;
+    if (!(ap->flags & G_GATTRWIDTH))
+       ap->width = WCU->defgattr.width;
+    if (!(ap->flags & G_GATTRMODE))
+       ap->mode = WCU->defgattr.mode;
+    if (!(ap->flags & G_GATTRFILL))
+       ap->fill = WCU->defgattr.fill;
+    if (!(ap->flags & G_GATTRSTYLE))
+       ap->style = WCU->defgattr.style;
+    flag = FALSE;
+    mode = ap->mode;
+    if (mode != WCU->gattr.mode) {
+       WCU->gattr.mode = mode;
+       SetROP2(GC, (int) mode);
+    }
+    WCU->gattr.fill = ap->fill;
+    color = ap->color;
+    if (color >= G_MAXCOLORS || !(WCU->colors[color].inuse))
+       color = 1;
+    if (color != WCU->gattr.color)
+       WCU->gattr.color = color, flag = TRUE;
+    width = ap->width;
+    if (width != WCU->gattr.width)
+       WCU->gattr.width = width, flag = TRUE;
+    style = ap->style;
+    if (style != WCU->gattr.style)
+       WCU->gattr.style = style, style = TRUE;
+
+    if (!flag)
+       return;
+    WCU->gattr.color = color;
+    if (Gdepth == 1) {
+       colorp = &WCU->colors[color].color;
+       intens = (0.3 * colorp->peBlue + 0.59 * colorp->peRed +
+                 0.11 * colorp->peGreen) / 255.0;
+       pati =
+           (intens <= 0.0625) ? 16 : -16.0 * (log(intens) / 2.7725887222);
+       brush = WCU->grays[pati];
+    } else
+       brush = CreateSolidBrush(PALETTEINDEX(WCU->gattr.color));
+    pbrush = SelectObject(GC, brush);
+    if (Gdepth != 1)
+       DeleteObject(pbrush);
+    pen = CreatePen((int) gstyles[WCU->gattr.style], WCU->gattr.width,
+                   PALETTEINDEX(WCU->gattr.color));
+    ppen = SelectObject(GC, pen);
+    DeleteObject(ppen);
+    SetTextColor(GC, PALETTEINDEX(WCU->gattr.color));
+}
+
+static PIXrect_t rdrawtopix(Gwidget_t * widget, Grect_t gr)
+{
+    PIXrect_t pr;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    pr.o.x = tvx * (gr.o.x - WCU->wrect.o.x) / twx + 0.5;
+    pr.o.y = tvy * (1.0 - (gr.c.y - WCU->wrect.o.y) / twy) + 0.5;
+    pr.c.x = tvx * (gr.c.x - WCU->wrect.o.x) / twx + 0.5;
+    pr.c.y = tvy * (1.0 - (gr.o.y - WCU->wrect.o.y) / twy) + 0.5;
+    return pr;
+}
+
+static PIXpoint_t pdrawtopix(Gwidget_t * widget, Gpoint_t gp)
+{
+    PIXpoint_t pp;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    pp.x = tvx * (gp.x - WCU->wrect.o.x) / twx + 0.5;
+    pp.y = tvy * (1.0 - (gp.y - WCU->wrect.o.y) / twy) + 0.5;
+    return pp;
+}
+
+static PIXsize_t sdrawtopix(Gwidget_t * widget, Gsize_t gs)
+{
+    PIXsize_t ps;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    ps.x = tvx * (gs.x - 1) / twx + 1.5;
+    ps.y = tvy * (gs.y - 1) / twy + 1.5;
+    return ps;
+}
+
+Gpoint_t ppixtodraw(Gwidget_t * widget, PIXpoint_t pp)
+{
+    Gpoint_t gp;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    gp.x = (pp.x / tvx) * twx + WCU->wrect.o.x;
+    gp.y = (1.0 - pp.y / tvy) * twy + WCU->wrect.o.y;
+    return gp;
+}
+
+static Gsize_t spixtodraw(Gwidget_t * widget, PIXsize_t ps)
+{
+    Gsize_t gs;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    gs.x = ((ps.x - 1) / tvx) * twx + 1;
+    gs.y = ((ps.y - 1) / tvy) * twy + 1;
+    return gs;
+}
+
+static Grect_t rpixtodraw(Gwidget_t * widget, PIXrect_t pr)
+{
+    Grect_t gr;
+    double tvx, tvy, twx, twy, n;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    gr.o.x = (pr.o.x / tvx) * twx + WCU->wrect.o.x;
+    gr.o.y = (1.0 - pr.c.y / tvy) * twy + WCU->wrect.o.y;
+    gr.c.x = (pr.c.x / tvx) * twx + WCU->wrect.o.x;
+    gr.c.y = (1.0 - pr.o.y / tvy) * twy + WCU->wrect.o.y;
+    if (gr.o.x > gr.c.x)
+       n = gr.o.x, gr.o.x = gr.c.x, gr.c.x = n;
+    if (gr.o.y > gr.c.y)
+       n = gr.o.y, gr.o.y = gr.c.y, gr.c.y = n;
+    return gr;
+}
+
+static PIXrect_t rdrawtobpix(Gbitmap_t * bitmap, Grect_t gr)
+{
+    PIXrect_t pr;
+    double tvy;
+
+    tvy = (int) ((bitmap->size.y - 1) * bitmap->scale.y);
+    pr.o.x = gr.o.x + 0.5;
+    pr.o.y = tvy - gr.c.y + 0.5;
+    pr.c.x = gr.c.x + 0.5;
+    pr.c.y = tvy - gr.o.y + 0.5;
+    return pr;
+}
+
+static PIXpoint_t pdrawtobpix(Gbitmap_t * bitmap, Gpoint_t gp)
+{
+    PIXpoint_t pp;
+    double tvy;
+
+    tvy = (int) ((bitmap->size.y - 1) * bitmap->scale.y);
+    pp.x = gp.x + 0.5;
+    pp.y = tvy - gp.y + 0.5;
+    return pp;
+}
+
+void Gadjustclip(Gwidget_t * widget)
+{
+    Gwidget_t *parent;
+    PIXrect_t pr;
+    RECT r1, r2, r3;
+
+    parent = &Gwidgets[widget->pwi];
+    GetWindowRect(widget->w, &r1);
+    GetClientRect(parent->w, &r2);
+    GetWindowRect(parent->w, &r3);
+    pr.o.x = max(0, -(r1.left - r3.left));
+    pr.o.y = max(0, -(r1.top - r3.top));
+    pr.c.x = min(r1.right - r1.left, pr.o.x + r2.right - r2.left);
+    pr.c.y = min(r1.bottom - r1.top, pr.o.y + r2.bottom - r2.top);
+    pr.c.x = max(pr.o.x, pr.c.x);
+    pr.c.y = max(pr.o.y, pr.c.y);
+    WCU->clip = rpixtodraw(widget, pr);
+}
diff --git a/cmd/lefty/ws/mswin32/gcommon.c b/cmd/lefty/ws/mswin32/gcommon.c
new file mode 100644 (file)
index 0000000..0018bec
--- /dev/null
@@ -0,0 +1,762 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WCU widget->u.c
+#define WVU widget->u.v
+
+FILE *Gxfp;
+int Gxfd;
+int Gpopdownflag;
+int Gdepth;
+int Gnocallbacks;
+int menuselected;
+int menupoped;
+
+char *Gbufp = NULL;
+int Gbufn = 0, Gbufi = 0;
+
+PIXpoint_t *Gppp;
+int Gppn, Gppi;
+
+Gfont_t *Gfontp;
+int Gfontn;
+
+static HFONT deffont;
+static int twobmouse;
+static HWND palettechanged;
+
+LRESULT CALLBACK LeftyWndProc(HWND, UINT, WPARAM, LPARAM);
+LRESULT CALLBACK ArrayWndProc(HWND, UINT, WPARAM, LPARAM);
+LRESULT CALLBACK CanvasWndProc(HWND, UINT, WPARAM, LPARAM);
+LRESULT CALLBACK LabelWndProc(HWND, UINT, WPARAM, LPARAM);
+LRESULT CALLBACK ScrollWndProc(HWND, UINT, WPARAM, LPARAM);
+
+static void processcommand(Gwidget_t *, WPARAM, LPARAM);
+static void handleresize(Gwidget_t *);
+
+int Ginitgraphics(void)
+{
+    WNDCLASS wc;
+    HDC hdc;
+    ATOM rtn;
+
+    if (!hprevinstance) {
+       wc.style = NULL;
+       wc.lpfnWndProc = LeftyWndProc;
+       wc.cbClsExtra = 0;
+       wc.cbWndExtra = 0;
+       wc.hInstance = hinstance;
+       wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
+       wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
+       wc.hbrBackground = GetStockObject(WHITE_BRUSH);
+       wc.lpszMenuName = 0;
+       wc.lpszClassName = "LeftyClass";
+       if (!(rtn = RegisterClass(&wc)))
+           panic(POS, "GXinit", "register class rtn = %d", (int) rtn);
+
+       wc.style = NULL;
+       wc.lpfnWndProc = ArrayWndProc;
+       wc.cbClsExtra = 0;
+       wc.cbWndExtra = 0;
+       wc.hInstance = hinstance;
+       wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
+       wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
+       wc.hbrBackground = GetStockObject(WHITE_BRUSH);
+       wc.lpszMenuName = 0;
+       wc.lpszClassName = "ArrayClass";
+       if (!(rtn = RegisterClass(&wc)))
+           panic(POS, "GXinit", "register class rtn = %d", (int) rtn);
+
+       wc.style = CS_OWNDC;
+       wc.lpfnWndProc = CanvasWndProc;
+       wc.cbClsExtra = 0;
+       wc.cbWndExtra = 0;
+       wc.hInstance = hinstance;
+       wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
+       wc.hCursor = NULL;
+       wc.hbrBackground = GetStockObject(WHITE_BRUSH);
+       wc.lpszMenuName = 0;
+       wc.lpszClassName = "CanvasClass";
+       if (!(rtn = RegisterClass(&wc)))
+           panic(POS, "GXinit", "register class rtn = %d", (int) rtn);
+
+       wc.style = NULL;
+       wc.lpfnWndProc = ScrollWndProc;
+       wc.cbClsExtra = 0;
+       wc.cbWndExtra = 0;
+       wc.hInstance = hinstance;
+       wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
+       wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
+       wc.hbrBackground = GetStockObject(WHITE_BRUSH);
+       wc.lpszMenuName = 0;
+       wc.lpszClassName = "ScrollClass";
+       if (!(rtn = RegisterClass(&wc)))
+           panic(POS, "GXinit", "register class rtn = %d", (int) rtn);
+
+       wc.style = NULL;
+       wc.lpfnWndProc = LabelWndProc;
+       wc.cbClsExtra = 0;
+       wc.cbWndExtra = 0;
+       wc.hInstance = hinstance;
+       wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
+       wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
+       wc.hbrBackground = GetStockObject(WHITE_BRUSH);
+       wc.lpszMenuName = 0;
+       wc.lpszClassName = "LabelClass";
+       if (!(rtn = RegisterClass(&wc)))
+           panic(POS, "GXinit", "register class rtn = %d", (int) rtn);
+    }
+    if (getenv("LEFTY3BMOUSE"))
+       twobmouse = FALSE;
+    else
+       twobmouse = TRUE;
+    hdc = GetDC((HWND) NULL);
+    Gdepth = GetDeviceCaps(hdc, BITSPIXEL);
+    deffont = GetStockObject(SYSTEM_FONT);
+#ifndef FEATURE_MS
+    if (!(Gxfp = fopen("/dev/windows", "r")))
+       panic(POS, "GXinit", "cannot open windows device");
+    Gxfd = fileno(Gxfp);
+#endif
+    Gpopdownflag = FALSE;
+    Gbufp = Marrayalloc((long) BUFINCR * BUFSIZE);
+    Gbufn = BUFINCR;
+    Gppp = Marrayalloc((long) PPINCR * PPSIZE);
+    Gppn = PPINCR;
+    Gfontp = Marrayalloc((long) FONTSIZE);
+    Gfontn = 1;
+    Gfontp[0].name = strdup("default");
+    if (!Gdefaultfont)
+       Gfontp[0].font = deffont;
+    else if (Gdefaultfont[0] != '\000')
+       Gfontp[0].font = CreateFont(0, 0, 0, 0, 0,
+                                   0, 0, 0, 0, 0, 0, 0, 0, Gdefaultfont);
+    else
+       Gfontp[0].font = NULL;
+    ReleaseDC((HWND) NULL, hdc);
+    Gnocallbacks = FALSE;
+    return 0;
+}
+
+int Gtermgraphics(void)
+{
+    int fi;
+
+    for (fi = 0; fi < Gfontn; fi++)
+       free(Gfontp[fi].name);
+    Marrayfree(Gfontp), Gfontp = NULL, Gfontn = 0;
+    Marrayfree(Gppp), Gppp = NULL, Gppn = 0;
+    Marrayfree(Gbufp), Gbufp = NULL, Gbufn = 0;
+    return 0;
+}
+
+int Gsync(void)
+{
+    return 0;
+}
+
+int Gresetbstate(int wi)
+{
+    Gcw_t *cw;
+    int bn;
+
+    cw = Gwidgets[wi].u.c;
+    bn = cw->bstate[0] + cw->bstate[1] + cw->bstate[2];
+    cw->bstate[0] = cw->bstate[1] = cw->bstate[2] = 0;
+    cw->buttonsdown -= bn;
+    Gbuttonsdown -= bn;
+    return 0;
+}
+
+int Gprocessevents(int waitflag, Geventmode_t mode)
+{
+    MSG msg;
+    int rtn;
+
+    rtn = 0;
+    switch (waitflag) {
+    case TRUE:
+       if (!GetMessage(&msg, (HWND) NULL, (UINT) NULL, (UINT) NULL))
+           exit(msg.wParam);
+       TranslateMessage(&msg);
+       DispatchMessage(&msg);
+       if (mode == G_ONEEVENT)
+           return 1;
+       rtn = 1;
+       /* FALL THROUGH */
+    case FALSE:
+       while (PeekMessage(&msg, (HWND) 0, (UINT) 0, (UINT) 0, PM_REMOVE)) {
+           if (msg.message == WM_QUIT)
+               exit(msg.wParam);
+           TranslateMessage(&msg);
+           DispatchMessage(&msg);
+           if (mode == G_ONEEVENT)
+               return 1;
+           rtn = 1;
+       }
+       break;
+    }
+    return rtn;
+}
+
+LRESULT CALLBACK LeftyWndProc(HWND hwnd,
+                             UINT message, WPARAM wparam, LPARAM lparam)
+{
+    Gwidget_t *widget;
+    WINDOWPOS *wpos;
+    Gevent_t gev;
+
+    widget = findwidget(hwnd, G_VIEWWIDGET);
+    switch (message) {
+    case WM_WINDOWPOSCHANGED:
+       if (Gnocallbacks || !widget)
+           return (DefWindowProc(hwnd, message, wparam, lparam));
+       wpos = (WINDOWPOS *) lparam;
+       if (!(wpos->flags & SWP_NOSIZE))
+           handleresize(widget);
+       break;
+    case WM_COMMAND:
+       if (Gnocallbacks || !widget)
+           return (DefWindowProc(hwnd, message, wparam, lparam));
+       processcommand(widget, wparam, lparam);
+       break;
+    case WM_CLOSE:
+       if (!widget)
+           exit(0);
+       if (WVU->closing)
+           DestroyWindow(hwnd);
+       if (Gnocallbacks)
+           exit(0);
+       gev.type = 0, gev.code = 0, gev.data = 0;
+       gev.wi = widget - &Gwidgets[0];
+       if (WVU->func)
+           (*WVU->func) (&gev);
+       else
+           exit(0);
+       break;
+    case WM_PALETTECHANGED:
+       palettechanged = (HWND) wparam;
+       break;
+    default:
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    }
+    return 0;
+}
+
+LRESULT CALLBACK ArrayWndProc(HWND hwnd,
+                             UINT message, WPARAM wparam, LPARAM lparam)
+{
+    Gwidget_t *widget;
+    WINDOWPOS *wpos;
+
+    if (Gnocallbacks || !(widget = findwidget(hwnd, G_ARRAYWIDGET)))
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    switch (message) {
+    case WM_WINDOWPOSCHANGED:
+       wpos = (WINDOWPOS *) lparam;
+       if (!(wpos->flags & SWP_NOSIZE))
+           handleresize(widget);
+       break;
+    case WM_COMMAND:
+       processcommand(widget, wparam, lparam);
+       break;
+    default:
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    }
+    return 0;
+}
+
+LRESULT CALLBACK CanvasWndProc(HWND hwnd,
+                              UINT message, WPARAM wparam, LPARAM lparam)
+{
+    Gwidget_t *widget;
+    WINDOWPOS *wpos;
+    PIXpoint_t pp;
+    Gevent_t gev;
+    POINT p;
+    int wi, bn;
+
+    if (Gnocallbacks || !(widget = findwidget(hwnd, G_CANVASWIDGET)))
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    Gpopdownflag = FALSE;
+    switch (message) {
+    case WM_PAINT:
+       if (palettechanged != hwnd)
+           RealizePalette(widget->u.c->gc);
+       Gneedredraw = widget->u.c->needredraw = TRUE;
+       Gadjustclip(widget);
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    case WM_WINDOWPOSCHANGED:
+       wpos = (WINDOWPOS *) lparam;
+       if (!(wpos->flags & SWP_NOSIZE))
+           handleresize(widget);
+       return 0;
+    case WM_MOUSEACTIVATE:
+       SetFocus(widget->w);
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    case WM_COMMAND:
+       processcommand(widget, wparam, lparam);
+       return 0;
+    case WM_CHAR:
+       gev.type = G_KEYBD;
+       gev.code = G_DOWN;      /* I don't know how to get up events so I make */
+       Gpopdownflag = TRUE;    /* the code after this switch send the up event */
+       gev.data = wparam;
+       GetCursorPos(&p);
+       ScreenToClient(widget->w, &p);
+       pp.x = p.x, pp.y = p.y;
+       gev.p = ppixtodraw(widget, pp);
+       /* continues after the end of this switch */
+       break;
+    case WM_LBUTTONDOWN:
+    case WM_LBUTTONUP:
+    case WM_MBUTTONDOWN:
+    case WM_MBUTTONUP:
+    case WM_RBUTTONDOWN:
+    case WM_RBUTTONUP:
+       gev.type = G_MOUSE;
+       if (twobmouse) {
+           if (message == WM_LBUTTONDOWN && (wparam & MK_CONTROL))
+               message = WM_MBUTTONDOWN;
+           if (message == WM_LBUTTONUP && (wparam & MK_CONTROL))
+               message = WM_MBUTTONUP;
+       }
+       switch (message) {
+       case WM_LBUTTONDOWN:
+           gev.code = G_DOWN, gev.data = G_LEFT;
+           break;
+       case WM_LBUTTONUP:
+           gev.code = G_UP, gev.data = G_LEFT;
+           break;
+       case WM_MBUTTONDOWN:
+           gev.code = G_DOWN, gev.data = G_MIDDLE;
+           break;
+       case WM_MBUTTONUP:
+           gev.code = G_UP, gev.data = G_MIDDLE;
+           break;
+       case WM_RBUTTONDOWN:
+           gev.code = G_DOWN, gev.data = G_RIGHT;
+           break;
+       case WM_RBUTTONUP:
+           gev.code = G_UP, gev.data = G_RIGHT;
+           break;
+       }
+       pp.x = LOWORD(lparam), pp.y = HIWORD(lparam);
+       gev.p = ppixtodraw(widget, pp);
+       bn = WCU->bstate[gev.data];
+       WCU->bstate[gev.data] = (gev.code == G_DOWN) ? 1 : 0;
+       bn = WCU->bstate[gev.data] - bn;
+       widget->u.c->buttonsdown += bn;
+       Gbuttonsdown += bn;
+       /* continues after the end of this switch */
+       break;
+    default:
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    }
+    wi = gev.wi = widget - &Gwidgets[0];
+    if (widget->u.c->func)
+       (*widget->u.c->func) (&gev);
+    if (Gpopdownflag) {
+       Gpopdownflag = FALSE;
+       if (gev.code == G_DOWN) {
+           gev.code = G_UP;
+           widget = &Gwidgets[wi];
+           WCU->bstate[gev.data] = 0;
+           widget->u.c->buttonsdown--;
+           Gbuttonsdown--;
+           if (widget->inuse && widget->u.c->func)
+               (*widget->u.c->func) (&gev);
+       }
+    }
+    return 0;
+}
+
+LRESULT CALLBACK LabelWndProc(HWND hwnd,
+                             UINT message, WPARAM wparam, LPARAM lparam)
+{
+    Gwidget_t *widget;
+    PAINTSTRUCT paintstruct;
+    WINDOWPOS *wpos;
+    Gevent_t gev;
+    RECT r;
+    HDC hdc;
+    int wi;
+
+    if (Gnocallbacks || !(widget = findwidget(hwnd, G_LABELWIDGET)))
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    switch (message) {
+    case WM_PAINT:
+       hdc = BeginPaint(widget->w, &paintstruct);
+       GetWindowText(widget->w, &Gbufp[0], Gbufn);
+       GetClientRect(widget->w, &r);
+       DrawText(hdc, (LPCSTR) & Gbufp[0], strlen(Gbufp), &r, DT_LEFT);
+       EndPaint(widget->w, &paintstruct);
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    case WM_WINDOWPOSCHANGED:
+       wpos = (WINDOWPOS *) lparam;
+       if (!(wpos->flags & SWP_NOSIZE))
+           handleresize(widget);
+       return 0;
+    case WM_COMMAND:
+       processcommand(widget, wparam, lparam);
+       return 0;
+    case WM_KEYDOWN:
+    case WM_KEYUP:
+       gev.type = G_KEYBD;
+       gev.code = (message == WM_KEYDOWN) ? G_DOWN : G_UP;
+       gev.data = wparam;
+       /* continues after the end of this switch */
+       break;
+    case WM_LBUTTONDOWN:
+    case WM_LBUTTONUP:
+    case WM_MBUTTONDOWN:
+    case WM_MBUTTONUP:
+    case WM_RBUTTONDOWN:
+    case WM_RBUTTONUP:
+       gev.type = G_MOUSE;
+       if (wparam & MK_CONTROL) {
+           if (message == WM_LBUTTONDOWN)
+               message = WM_MBUTTONDOWN;
+           else if (message == WM_LBUTTONUP)
+               message = WM_MBUTTONUP;
+       }
+       switch (message) {
+       case WM_LBUTTONDOWN:
+           gev.code = G_DOWN, gev.data = G_LEFT;
+           break;
+       case WM_LBUTTONUP:
+           gev.code = G_UP, gev.data = G_LEFT;
+           break;
+       case WM_MBUTTONDOWN:
+           gev.code = G_DOWN, gev.data = G_MIDDLE;
+           break;
+       case WM_MBUTTONUP:
+           gev.code = G_UP, gev.data = G_MIDDLE;
+           break;
+       case WM_RBUTTONDOWN:
+           gev.code = G_DOWN, gev.data = G_RIGHT;
+           break;
+       case WM_RBUTTONUP:
+           gev.code = G_UP, gev.data = G_RIGHT;
+           break;
+       }
+       /* continues after the end of this switch */
+       break;
+    default:
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    }
+    wi = gev.wi = widget - &Gwidgets[0];
+    if (widget->u.l->func)
+       (*widget->u.l->func) (&gev);
+    if (Gpopdownflag) {
+       Gpopdownflag = FALSE;
+       if (gev.type == G_MOUSE && gev.code == G_DOWN) {
+           gev.code = G_UP;
+           widget = &Gwidgets[wi];
+           if (widget->inuse && widget->u.l->func)
+               (*widget->u.l->func) (&gev);
+       }
+    }
+    return 0;
+}
+
+LRESULT CALLBACK ScrollWndProc(HWND hwnd,
+                              UINT message, WPARAM wparam, LPARAM lparam)
+{
+    Gwidget_t *widget, *child;
+    WINDOWPOS *wpos;
+    PIXpoint_t po;
+    RECT r;
+    int dummy, dx, dy, wi;
+
+    if (Gnocallbacks || !(widget = findwidget(hwnd, G_SCROLLWIDGET)))
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    switch (message) {
+    case WM_WINDOWPOSCHANGED:
+       wpos = (WINDOWPOS *) lparam;
+       if (!(wpos->flags & SWP_NOSIZE))
+           handleresize(widget);
+       break;
+    case WM_HSCROLL:
+    case WM_VSCROLL:
+       for (wi = 0; wi < Gwidgetn; wi++) {
+           child = &Gwidgets[wi];
+           if (child->inuse && child->pwi == widget - &Gwidgets[0])
+               break;
+       }
+       if (wi == Gwidgetn)
+           return (DefWindowProc(hwnd, message, wparam, lparam));
+       GetClientRect(widget->w, &r);
+       GetScrollRange(widget->w, SB_HORZ, &dummy, &dx);
+       GetScrollRange(widget->w, SB_VERT, &dummy, &dy);
+       po.x = GetScrollPos(widget->w, SB_HORZ);
+       po.y = GetScrollPos(widget->w, SB_VERT);
+       switch (message) {
+       case WM_HSCROLL:
+           switch (LOWORD(wparam)) {
+           case SB_BOTTOM:
+               po.x = dx;
+               break;
+           case SB_LINEDOWN:
+               po.x += 10;
+               break;
+           case SB_LINEUP:
+               po.x -= 10;
+               break;
+           case SB_PAGEDOWN:
+               po.x += (r.right - r.left);
+               break;
+           case SB_PAGEUP:
+               po.x -= (r.right - r.left);
+               break;
+           case SB_THUMBPOSITION:
+               po.x = HIWORD(wparam);
+               break;
+           case SB_THUMBTRACK:
+               po.x = HIWORD(wparam);
+               break;
+           case SB_TOP:
+               po.x = 0;
+               break;
+           }
+           po.x = min(po.x, dx);
+           po.x = max(po.x, 0);
+           SetScrollPos(widget->w, SB_HORZ, po.x, TRUE);
+           SetWindowPos(child->w, (HWND) NULL, -po.x, -po.y, 0, 0,
+                        SWP_NOSIZE | SWP_NOZORDER);
+           break;
+       case WM_VSCROLL:
+           switch (LOWORD(wparam)) {
+           case SB_BOTTOM:
+               po.y = dy;
+               break;
+           case SB_LINEDOWN:
+               po.y += 10;
+               break;
+           case SB_LINEUP:
+               po.y -= 10;
+               break;
+           case SB_PAGEDOWN:
+               po.y += (r.bottom - r.top);
+               break;
+           case SB_PAGEUP:
+               po.y -= (r.bottom - r.top);
+               break;
+           case SB_THUMBPOSITION:
+               po.y = HIWORD(wparam);
+               break;
+           case SB_THUMBTRACK:
+               po.y = HIWORD(wparam);
+               break;
+           case SB_TOP:
+               po.y = 0;
+               break;
+           }
+           po.y = min(po.y, dy);
+           po.y = max(po.y, 0);
+           SetScrollPos(widget->w, SB_VERT, po.y, TRUE);
+           SetWindowPos(child->w, (HWND) NULL, -po.x, -po.y, 0, 0,
+                        SWP_NOSIZE | SWP_NOZORDER);
+           break;
+       }
+       break;
+    default:
+       return (DefWindowProc(hwnd, message, wparam, lparam));
+    }
+    return 0;
+}
+
+static void processcommand(Gwidget_t * widget, WPARAM wparam,
+                          LPARAM lparam)
+{
+    Gwidget_t *child;
+    WORD l;
+    int n;
+
+    if (lparam == 0) {         /* it's a menu */
+       if (LOWORD(wparam) != 999)
+           menuselected = LOWORD(wparam);
+       menupoped = FALSE;
+       return;
+    }
+    if (!(LOWORD(wparam) > 0 && LOWORD(wparam) < Gwidgetn))
+       return;
+    child = &Gwidgets[LOWORD(wparam)];
+    if (!child->inuse)
+       return;
+
+    switch (child->type) {
+    case G_TEXTWIDGET:
+       if (HIWORD(wparam) == EN_CHANGE) {      /* it's a text widget message */
+           if ((n = SendMessage(child->w, EM_GETLINECOUNT, 0, 0L)) < 2)
+               return;
+           *((WORD *) & Gbufp[0]) = Gbufn - 1;
+           l = SendMessage(child->w, EM_GETLINE, n - 1,
+                           (LPARAM) (LPSTR) & Gbufp[0]);
+           if (l != 0)
+               return;         /* no carriage return yet */
+           *((WORD *) & Gbufp[0]) = Gbufn - 1;
+           l = SendMessage(child->w, EM_GETLINE, n - 2,
+                           (LPARAM) (LPSTR) & Gbufp[0]);
+           Gbufp[l] = 0;
+           if (l > 0 && child->u.t->func)
+               (*child->u.t->func) (child - &Gwidgets[0], &Gbufp[0]);
+       }
+       break;
+    case G_BUTTONWIDGET:
+       if (child->u.b->func)
+           (*child->u.b->func) (child - &Gwidgets[0], child->udata);
+       break;
+    }
+}
+
+void Gadjustwrect(Gwidget_t * parent, PIXsize_t * psp)
+{
+    RECT r;
+
+    GetClientRect(parent->w, &r);
+    switch (parent->type) {
+    case G_ARRAYWIDGET:
+       if (parent->u.a->data.type == G_AWHARRAY)
+           psp->y = r.bottom - r.top;
+       else
+           psp->x = r.right - r.left;
+       break;
+    case G_SCROLLWIDGET:
+       psp->x = max(psp->x, r.right - r.left);
+       psp->y = max(psp->y, r.bottom - r.top);
+       break;
+    case G_VIEWWIDGET:
+    case G_QUERYWIDGET:
+       psp->x = r.right - r.left;
+       psp->y = r.bottom - r.top;
+       break;
+    }
+}
+
+static void handleresize(Gwidget_t * widget)
+{
+    Gwidget_t *parent, *child;
+    PIXsize_t ps1, ps2;
+    PIXpoint_t po;
+    DWORD wflags1, wflags2;
+    RECT r;
+    int dx, dy, wi, i;
+
+    wflags1 = SWP_NOMOVE | SWP_NOZORDER;
+    wflags2 = SWP_NOSIZE | SWP_NOZORDER;
+    GetWindowRect(widget->w, &r);
+    ps1.x = r.right - r.left, ps1.y = r.bottom - r.top;
+    ps2 = ps1;
+    /* first, take care of parent */
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    if (!parent)
+       goto handlechildren;
+    switch (parent->type) {
+    case G_VIEWWIDGET:
+       Gadjustwrect(parent, &ps1);
+       if (ps1.x != ps2.x || ps1.y != ps2.y) {
+           Gnocallbacks = TRUE;
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps1.x, ps1.y,
+                        wflags1);
+           Gnocallbacks = FALSE;
+       }
+       break;
+    case G_ARRAYWIDGET:
+       Gnocallbacks = TRUE;
+       Gawresize(parent);
+       Gnocallbacks = FALSE;
+       break;
+    case G_SCROLLWIDGET:
+       Gnocallbacks = TRUE;
+       for (i = 0; i < 2; i++) {
+           Gadjustwrect(parent, &ps1);
+           if (ps1.x > ps2.x || ps1.y > ps2.y)
+               SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps1.x, ps1.y,
+                            wflags1);
+           GetClientRect(parent->w, &r);
+           ps2.x = r.right - r.left, ps2.y = r.bottom - r.top;
+           dx = max(0, ps1.x - ps2.x);
+           dy = max(0, ps1.y - ps2.y);
+           SetScrollRange(parent->w, SB_HORZ, 0, dx, TRUE);
+           SetScrollRange(parent->w, SB_VERT, 0, dy, TRUE);
+           po.x = GetScrollPos(parent->w, SB_HORZ);
+           po.y = GetScrollPos(parent->w, SB_VERT);
+           po.x = min(po.x, dx);
+           po.x = max(po.x, 0);
+           SetScrollPos(parent->w, SB_HORZ, po.x, TRUE);
+           po.y = min(po.y, dy);
+           po.y = max(po.y, 0);
+           SetScrollPos(parent->w, SB_VERT, po.y, TRUE);
+           SetWindowPos(widget->w, (HWND) NULL, -po.x, -po.y, 0, 0,
+                        wflags2);
+           ps2 = ps1;
+       }
+       Gnocallbacks = FALSE;
+       break;
+    }
+
+  handlechildren:
+    for (wi = 0; wi < Gwidgetn; wi++) {
+       child = &Gwidgets[wi];
+       if (child->inuse && child->pwi == widget - &Gwidgets[0])
+           break;
+    }
+    if (wi == Gwidgetn)
+       return;
+    GetWindowRect(child->w, &r);
+    ps1.x = r.right - r.left, ps1.y = r.bottom - r.top;
+    ps2 = ps1;
+    switch (widget->type) {
+    case G_VIEWWIDGET:
+       Gadjustwrect(widget, &ps1);
+       if (ps1.x != ps2.x || ps1.y != ps2.y)
+           SetWindowPos(child->w, (HWND) NULL, 0, 0, ps1.x, ps1.y,
+                        wflags1);
+       break;
+    case G_ARRAYWIDGET:
+       Gawresize(widget);
+       break;
+    case G_SCROLLWIDGET:
+       Gadjustwrect(widget, &ps1);
+       if (ps1.x > ps2.x || ps1.y > ps2.y)
+           SetWindowPos(child->w, (HWND) NULL, 0, 0, ps1.x, ps1.y,
+                        wflags1);
+       GetClientRect(widget->w, &r);
+       ps2.x = r.right - r.left, ps2.y = r.bottom - r.top;
+       dx = max(0, ps1.x - ps2.x);
+       dy = max(0, ps1.y - ps2.y);
+       SetScrollRange(widget->w, SB_HORZ, 0, dx, TRUE);
+       SetScrollRange(widget->w, SB_VERT, 0, dy, TRUE);
+       po.x = GetScrollPos(widget->w, SB_HORZ);
+       po.y = GetScrollPos(widget->w, SB_VERT);
+       po.x = min(po.x, dx);
+       po.x = max(po.x, 0);
+       SetScrollPos(widget->w, SB_HORZ, po.x, TRUE);
+       po.y = min(po.y, dy);
+       po.y = max(po.y, 0);
+       SetScrollPos(widget->w, SB_VERT, po.y, TRUE);
+       SetWindowPos(child->w, (HWND) NULL, -po.x, -po.y, 0, 0, wflags2);
+       break;
+    }
+}
diff --git a/cmd/lefty/ws/mswin32/gcommon.h b/cmd/lefty/ws/mswin32/gcommon.h
new file mode 100644 (file)
index 0000000..5e3461c
--- /dev/null
@@ -0,0 +1,179 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _GCOMMON_H
+#define _GCOMMON_H
+    extern HANDLE hinstance, hprevinstance;
+
+/* point and rect structures */
+    typedef POINT PIXxy_t;
+    typedef PIXxy_t PIXpoint_t;
+    typedef PIXxy_t PIXsize_t;
+    typedef struct PIXrect_t {
+       PIXxy_t o, c;
+    } PIXrect_t;
+
+    extern int Gpopdownflag;
+    extern int Gdepth;
+    extern int Gnocallbacks;
+    extern int menuselected;
+    extern int menupoped;
+
+    extern char *Gbufp;
+    extern int Gbufn, Gbufi;
+#define BUFINCR 1024
+#define BUFSIZE sizeof (char)
+
+    extern PIXpoint_t *Gppp;
+    extern int Gppn, Gppi;
+#define PPINCR 100
+#define PPSIZE sizeof (PIXpoint_t)
+
+    typedef struct Gfont_t {
+       char *name;
+       int size;
+       HFONT font;
+    } Gfont_t;
+    extern Gfont_t *Gfontp;
+    extern int Gfontn;
+#define FONTSIZE sizeof (Gfont_t)
+#define SETFONT(font) { \
+    if (font != WCU->font) { \
+        WCU->font = font; \
+        SelectObject (GC, font); \
+    } \
+}
+
+#define GETSIZE(sin, sout, smin) \
+    sout.x = (sin.x > smin) ? sin.x + 0.5 : smin, \
+    sout.y = (sin.y > smin) ? sin.y + 0.5 : smin
+#define GETORIGIN(oin, oout) \
+    oout.x = oin.x + 0.5, oout.y = oin.y + 0.5
+
+    int Ginitgraphics(void);
+    int Gtermgraphics(void);
+    int Gsync(void);
+
+    int GAcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GAsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GAgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GAdestroywidget(Gwidget_t *);
+
+    int GBcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GBsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GBgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GBdestroywidget(Gwidget_t *);
+
+    int GCcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GCsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GCgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GCdestroywidget(Gwidget_t *);
+    int GCcanvasclear(Gwidget_t *);
+    int GCsetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GCgetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GCarrow(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GCline(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GCbox(Gwidget_t *, Grect_t, Ggattr_t *);
+    int GCpolygon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GCsplinegon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GCarc(Gwidget_t *, Gpoint_t, Gsize_t, double, double, Ggattr_t *);
+    int GCtext(Gwidget_t *, Gtextline_t *, int, Gpoint_t,
+              char *, double, char *, Ggattr_t *);
+    int GCgettextsize(Gwidget_t *, Gtextline_t *, int, char *, double,
+                     Gsize_t *);
+    int GCcreatebitmap(Gwidget_t *, Gbitmap_t *, Gsize_t);
+    int GCdestroybitmap(Gbitmap_t *);
+    int GCreadbitmap(Gwidget_t *, Gbitmap_t *, FILE *);
+    int GCwritebitmap(Gbitmap_t *, FILE *);
+    int GCbitblt(Gwidget_t *, Gpoint_t, Grect_t, Gbitmap_t *, char *,
+                Ggattr_t *);
+    int GCgetmousecoords(Gwidget_t *, Gpoint_t *, int *);
+
+    int GLcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GLsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GLgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GLdestroywidget(Gwidget_t *);
+
+    int GMcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GMsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GMgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GMdestroywidget(Gwidget_t *);
+    int GMmenuaddentries(Gwidget_t *, int, char **);
+    int GMmenudisplay(Gwidget_t *, Gwidget_t *);
+
+    int GPcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GPsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GPgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GPdestroywidget(Gwidget_t *);
+    int GPcanvasclear(Gwidget_t *);
+    int GPsetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GPgetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GParrow(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GPline(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GPbox(Gwidget_t *, Grect_t, Ggattr_t *);
+    int GPpolygon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GPsplinegon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GParc(Gwidget_t *, Gpoint_t, Gsize_t, double, double, Ggattr_t *);
+    int GPtext(Gwidget_t *, Gtextline_t *, int, Gpoint_t,
+              char *, double, char *, Ggattr_t *);
+    int GPcreatebitmap(Gwidget_t *, Gbitmap_t *, Gsize_t);
+    int GPdestroybitmap(Gbitmap_t *);
+    int GPreadbitmap(Gwidget_t *, Gbitmap_t *, FILE *);
+    int GPwritebitmap(Gbitmap_t *, FILE *);
+    int GPbitblt(Gwidget_t *, Gpoint_t, Grect_t, Gbitmap_t *, char *,
+                Ggattr_t *);
+
+    int GQcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GQsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GQgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GQdestroywidget(Gwidget_t *);
+    int GQqueryask(Gwidget_t *, char *, char *, char *, int);
+
+    int GScreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GSsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GSgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GSdestroywidget(Gwidget_t *);
+
+    int GTcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GTsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GTgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GTdestroywidget(Gwidget_t *);
+
+    int GVcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GVsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GVgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GVdestroywidget(Gwidget_t *);
+
+    void Gawinitialize(Gwidget_t *, int);
+    void Gawdestroy(Gwidget_t *);
+    void Gawresize(Gwidget_t *);
+    void Gawinsertchild(Gwidget_t *, Gwidget_t *);
+    void Gawdeletechild(Gwidget_t *, Gwidget_t *);
+
+    void Gadjustwrect(Gwidget_t *, PIXsize_t *);
+    void Gadjustclip(Gwidget_t *);
+    Gpoint_t ppixtodraw(Gwidget_t *, PIXpoint_t);
+#endif                         /* _GCOMMON_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/ws/mswin32/glabel.c b/cmd/lefty/ws/mswin32/glabel.c
new file mode 100644 (file)
index 0000000..6031d0e
--- /dev/null
@@ -0,0 +1,174 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WLU widget->u.l
+
+int GLcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    DWORD wflags;
+    char *s;
+    int ai;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    wflags = WS_CHILDWINDOW;
+    WLU->func = NULL;
+    ps.x = ps.y = MINLWSIZE;
+    s = "";
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINLWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           wflags |= WS_BORDER;
+           break;
+       case G_ATTRTEXT:
+           s = attrp[ai].u.t;
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WLU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    Gadjustwrect(parent, &ps);
+    if (!
+       (widget->w =
+        CreateWindow("LabelClass", s, wflags, 0, 0, ps.x, ps.y, parent->w,
+                     (HMENU) (widget - &Gwidgets[0]), hinstance, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    ShowWindow(widget->w, SW_SHOW);
+    UpdateWindow(widget->w);
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawinsertchild(parent, widget);
+    return 0;
+}
+
+int GLsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Gwidget_t *parent;
+    PIXsize_t ps;
+    RECT r;
+    DWORD wflags1;
+    int ai;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    wflags1 = SWP_NOMOVE | SWP_NOZORDER;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINLWSIZE);
+           Gadjustwrect(parent, &ps);
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps.x, ps.y,
+                        wflags1);
+           r.top = r.left = 0;
+           r.bottom = ps.y, r.right = ps.x;
+           InvalidateRect(widget->w, NULL, FALSE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "borderwidth");
+           return -1;
+       case G_ATTRTEXT:
+           SetWindowText(widget->w, attrp[ai].u.t);
+           GetClientRect(widget->w, &r);
+           InvalidateRect(widget->w, &r, TRUE);
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           attrp[ai].u.func = WLU->func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GLgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    RECT r;
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GetWindowRect(widget->w, &r);
+           attrp[ai].u.s.x = r.right - r.left;
+           attrp[ai].u.s.y = r.bottom - r.top;
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTGETATTR, "borderwidth");
+           return -1;
+       case G_ATTRTEXT:
+           GetWindowText(widget->w, &Gbufp[0], Gbufn);
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", widget->w);
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTREVENTCB:
+           attrp[ai].u.func = WLU->func;
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GLdestroywidget(Gwidget_t * widget)
+{
+    Gwidget_t *parent;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawdeletechild(parent, widget);
+    DestroyWindow(widget->w);
+    return 0;
+}
diff --git a/cmd/lefty/ws/mswin32/gmenu.c b/cmd/lefty/ws/mswin32/gmenu.c
new file mode 100644 (file)
index 0000000..f6800da
--- /dev/null
@@ -0,0 +1,121 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WMU widget->u.m
+
+int GMcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    if (!(widget->w = CreatePopupMenu())) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    WMU->count = 0;
+    return 0;
+}
+
+int GMsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GMgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GMdestroywidget(Gwidget_t * widget)
+{
+    DestroyMenu(widget->w);
+    return 0;
+}
+
+int GMmenuaddentries(Gwidget_t * widget, int en, char **ep)
+{
+    int ei;
+
+    for (ei = 0; ei < en; ei++)
+       AppendMenu(widget->w, MF_ENABLED | MF_STRING, WMU->count++,
+                  ep[ei]);
+    return 0;
+}
+
+int GMmenudisplay(Gwidget_t * parent, Gwidget_t * widget)
+{
+    MSG msg;
+    POINT p;
+    UINT flag;
+
+    /* FIXME not only right button */
+    menupoped = TRUE;
+    menuselected = -1;
+    GetCursorPos(&p);
+    if (GetAsyncKeyState(VK_LBUTTON) < 0)
+       flag = TPM_LEFTALIGN | TPM_LEFTBUTTON;
+    else
+       flag = TPM_LEFTALIGN | TPM_RIGHTBUTTON;
+    TrackPopupMenu(widget->w, flag, p.x, p.y, 0, parent->w, NULL);
+    PostMessage(parent->w, WM_COMMAND, 999, 0);
+    if (!GetMessage(&msg, parent->w, WM_COMMAND, WM_COMMAND))
+       panic(POS, "GMmenudisplay", "exit code in GetMessage");
+    TranslateMessage(&msg);
+    DispatchMessage(&msg);
+    Gpopdownflag = TRUE;
+    return menuselected;
+}
diff --git a/cmd/lefty/ws/mswin32/gpcanvas.c b/cmd/lefty/ws/mswin32/gpcanvas.c
new file mode 100644 (file)
index 0000000..1ee11ea
--- /dev/null
@@ -0,0 +1,1226 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WPU widget->u.p
+#define WINDOW widget->u.p->window
+#define GC widget->u.p->gc
+
+static long gstyles[5] = {
+    /* G_SOLID */ PS_SOLID,
+    /* G_DASHED */ PS_DASH,
+    /* G_DOTTED */ PS_DOT,
+    /* G_LONGDASHED */ PS_DASH,
+    /* G_SHORTDASHED */ PS_DASH,
+};
+
+static char grays[][4] = {
+    {0x00, 0x00, 0x00, 0x00,},
+    {0x08, 0x00, 0x00, 0x00,},
+    {0x08, 0x00, 0x02, 0x00,},
+    {0x0A, 0x00, 0x02, 0x00,},
+    {0x0A, 0x00, 0x0A, 0x00,},
+    {0x0A, 0x04, 0x0A, 0x00,},
+    {0x0A, 0x04, 0x0A, 0x01,},
+    {0x0A, 0x05, 0x0A, 0x01,},
+    {0x0A, 0x05, 0x0A, 0x05,},
+    {0x0E, 0x05, 0x0A, 0x05,},
+    {0x0E, 0x05, 0x0B, 0x05,},
+    {0x0F, 0x05, 0x0B, 0x05,},
+    {0x0F, 0x05, 0x0F, 0x05,},
+    {0x0F, 0x0D, 0x0F, 0x05,},
+    {0x0F, 0x0D, 0x0F, 0x07,},
+    {0x0F, 0x0F, 0x0F, 0x07,},
+    {0x0F, 0x0F, 0x0F, 0x0F,},
+};
+
+char *Gpscanvasname = "out.emf";
+
+static void bezier(PIXpoint_t, PIXpoint_t, PIXpoint_t, PIXpoint_t);
+static HFONT findfont(char *, int);
+static int scalebitmap(Gwidget_t *, Gbitmap_t *, Gsize_t, int, int);
+static void setgattr(Gwidget_t *, Ggattr_t *);
+
+static PIXrect_t rdrawtopix(Gwidget_t *, Grect_t);
+static PIXpoint_t pdrawtopix(Gwidget_t *, Gpoint_t);
+static PIXsize_t sdrawtopix(Gwidget_t *, Gsize_t);
+static PIXrect_t rdrawtobpix(Gbitmap_t *, Grect_t);
+static PIXpoint_t pdrawtobpix(Gbitmap_t *, Gpoint_t);
+
+int GPcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXpoint_t po;
+    PIXsize_t ps;
+    PRINTDLG pd;
+    DOCINFO di;
+    DEVMODE *dmp;
+    LOGPALETTE pal[2];         /* the 2 here is to provide enough space
+                                  for palPalEntry[0] and [1] */
+    HBRUSH brush;
+    HPEN pen;
+    HBITMAP bmap;
+    char *s, *s1;
+    int color, lflag, ai, dpix, dpiy, i;
+
+    s = Gpscanvasname;
+    lflag = FALSE;
+    po.x = po.y = 0;
+    ps.x = ps.y = MINPWSIZE;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           GETORIGIN(attrp[ai].u.p, po);
+           break;
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINPWSIZE);
+           break;
+       case G_ATTRNAME:
+           if (attrp[ai].u.t && attrp[ai].u.t[0])
+               s = attrp[ai].u.t;
+           break;
+       case G_ATTRMODE:
+           if (Strcmp("landscape", attrp[ai].u.t) == 0)
+               lflag = TRUE;
+           else if (Strcmp("portrait", attrp[ai].u.t) == 0)
+               lflag = FALSE;
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRCOLOR:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRVIEWPORT:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRWINDOW:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    s1 = s + strlen(s) - 3;
+    if (s1 > s && strncmp(s1, "emf", 3) == 0) {
+       WPU->mode = 1;
+       ps.x *= 8.235, ps.y *= 8.235;
+       if (!
+           (GC =
+            CreateEnhMetaFile(NULL, s, NULL, "LEFTY\\0GRAPH\\0\\0"))) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+    } else {                   /* open the printer device */
+       WPU->mode = 2;
+       di.cbSize = sizeof(DOCINFO);
+       di.lpszDocName = NULL;
+       di.lpszOutput = NULL;
+       di.lpszDatatype = "LEFTY";
+       di.fwType = 0;
+       pd.lStructSize = sizeof(pd);
+       pd.hwndOwner = NULL;
+       pd.hDevMode = NULL;
+       pd.hDevNames = NULL;
+       pd.hDC = NULL;
+       pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
+       pd.nFromPage = 0;
+       pd.nToPage = 0;
+       pd.nMinPage = 0;
+       pd.nMaxPage = 0;
+       pd.nCopies = 1;
+       pd.hInstance = NULL;
+       pd.lCustData = NULL;
+       pd.lpfnPrintHook = NULL;
+       pd.lpfnSetupHook = NULL;
+       pd.lpPrintTemplateName = NULL;
+       pd.lpSetupTemplateName = NULL;
+       pd.hPrintTemplate = NULL;
+       pd.hSetupTemplate = NULL;
+       if (!PrintDlg(&pd)) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+       if (lflag && pd.hDevMode) {
+           dmp = (DEVMODE *) GlobalLock(pd.hDevMode);
+           dmp->dmOrientation = DMORIENT_LANDSCAPE;
+           GlobalUnlock(pd.hDevMode);
+           pd.Flags = PD_RETURNDC;
+           if (!PrintDlg(&pd)) {
+               Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+               return -1;
+           }
+       }
+       GC = pd.hDC;
+       dpix = GetDeviceCaps(GC, LOGPIXELSX);
+       if (dpix != 300)
+           ps.x = ps.x * (double) dpix / 300.0;
+       dpiy = GetDeviceCaps(GC, LOGPIXELSY);
+       if (dpiy != 300)
+           ps.y = ps.y * (double) dpiy / 300.0;
+       if (StartDoc(GC, &di) <= 0 || StartPage(GC) <= 0) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+    }
+    WPU->wrect.o.x = 0.0, WPU->wrect.o.y = 0.0;
+    WPU->wrect.c.x = 1.0, WPU->wrect.c.y = 1.0;
+    WPU->vsize.x = ps.x, WPU->vsize.y = ps.y;
+    WPU->ncolor = 2;
+    pal[0].palVersion = 0x300; /* HA HA HA */
+    pal[0].palNumEntries = 2;
+    pal[0].palPalEntry[0].peRed = 255;
+    pal[0].palPalEntry[0].peGreen = 255;
+    pal[0].palPalEntry[0].peBlue = 255;
+    pal[0].palPalEntry[0].peFlags = 0;
+    pal[0].palPalEntry[1].peRed = 0;
+    pal[0].palPalEntry[1].peGreen = 0;
+    pal[0].palPalEntry[1].peBlue = 0;
+    pal[0].palPalEntry[1].peFlags = 0;
+    WPU->cmap = CreatePalette(&pal[0]);
+    WPU->colors[0].color = pal[0].palPalEntry[0];
+    for (i = 1; i < G_MAXCOLORS; i++)
+       WPU->colors[i].color = pal[0].palPalEntry[1];
+    SelectPalette(GC, WPU->cmap, FALSE);
+    RealizePalette(GC);
+    WPU->colors[0].inuse = TRUE;
+    WPU->colors[1].inuse = TRUE;
+    for (i = 2; i < G_MAXCOLORS; i++)
+       WPU->colors[i].inuse = FALSE;
+    WPU->gattr.color = 1;
+    brush = CreateSolidBrush(PALETTEINDEX(1));
+    SelectObject(GC, brush);
+    pen = CreatePen(PS_SOLID, 1, PALETTEINDEX(1));
+    SelectObject(GC, pen);
+    SetTextColor(GC, PALETTEINDEX(1));
+    SetBkMode(GC, TRANSPARENT);
+    WPU->gattr.width = 0;
+    WPU->gattr.mode = G_SRC;
+    WPU->gattr.fill = 0;
+    WPU->gattr.style = 0;
+    WPU->defgattr = WPU->gattr;
+    WPU->font = NULL;
+    if (Gdepth == 1) {
+       for (i = 0; i < 17; i++) {
+           if (!(bmap = CreateBitmap(4, 4, 1, 1, &grays[i][0])))
+               continue;
+           WPU->grays[i] = CreatePatternBrush(bmap);
+       }
+    }
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           WPU->colors[color].color.peRed = attrp[ai].u.c.r;
+           WPU->colors[color].color.peGreen = attrp[ai].u.c.g;
+           WPU->colors[color].color.peBlue = attrp[ai].u.c.b;
+           WPU->colors[color].color.peFlags = 0;
+           if (color >= WPU->ncolor)
+               ResizePalette(WPU->cmap, color + 1), WPU->ncolor =
+                   color + 1;
+           SetPaletteEntries(WPU->cmap, (int) color, 1,
+                             &WPU->colors[color].color);
+           RealizePalette(GC);
+           WPU->colors[color].inuse = TRUE;
+           if (color == WPU->gattr.color)
+               WPU->gattr.color = -1;
+           break;
+       case G_ATTRVIEWPORT:
+           if (attrp[ai].u.s.x == 0)
+               attrp[ai].u.s.x = 1;
+           if (attrp[ai].u.s.y == 0)
+               attrp[ai].u.s.y = 1;
+           WPU->vsize.x = attrp[ai].u.s.x + 0.5;
+           WPU->vsize.y = attrp[ai].u.s.y + 0.5;
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, WPU->vsize.x,
+                        WPU->vsize.y,
+                        SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
+           break;
+       case G_ATTRWINDOW:
+           if (attrp[ai].u.r.o.x == attrp[ai].u.r.c.x)
+               attrp[ai].u.r.c.x = attrp[ai].u.r.o.x + 1;
+           if (attrp[ai].u.r.o.y == attrp[ai].u.r.c.y)
+               attrp[ai].u.r.c.y = attrp[ai].u.r.o.y + 1;
+           WPU->wrect = attrp[ai].u.r;
+           break;
+       }
+    }
+    return 0;
+}
+
+int GPsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    int color, ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           break;
+       case G_ATTRSIZE:
+           break;
+       case G_ATTRNAME:
+           break;
+       case G_ATTRMODE:
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           WPU->colors[color].color.peRed = attrp[ai].u.c.r;
+           WPU->colors[color].color.peGreen = attrp[ai].u.c.g;
+           WPU->colors[color].color.peBlue = attrp[ai].u.c.b;
+           WPU->colors[color].color.peFlags = 0;
+           if (color >= WPU->ncolor)
+               ResizePalette(WPU->cmap, color + 1), WPU->ncolor =
+                   color + 1;
+           SetPaletteEntries(WPU->cmap, (int) color, 1,
+                             &WPU->colors[color].color);
+           RealizePalette(GC);
+           WPU->colors[color].inuse = TRUE;
+           if (color == WPU->gattr.color)
+               WPU->gattr.color = -1;
+           break;
+       case G_ATTRVIEWPORT:
+           if (attrp[ai].u.s.x == 0)
+               attrp[ai].u.s.x = 1;
+           if (attrp[ai].u.s.y == 0)
+               attrp[ai].u.s.y = 1;
+           WPU->vsize.x = attrp[ai].u.s.x + 0.5;
+           WPU->vsize.y = attrp[ai].u.s.y + 0.5;
+           ps.x = WPU->vsize.x, ps.y = WPU->vsize.y;
+           break;
+       case G_ATTRWINDOW:
+           if (attrp[ai].u.r.o.x == attrp[ai].u.r.c.x)
+               attrp[ai].u.r.c.x = attrp[ai].u.r.o.x + 1;
+           if (attrp[ai].u.r.o.y == attrp[ai].u.r.c.y)
+               attrp[ai].u.r.c.y = attrp[ai].u.r.o.y + 1;
+           WPU->wrect = attrp[ai].u.r;
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GPgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PALETTEENTRY *cp;
+    int color, ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           break;
+       case G_ATTRSIZE:
+           break;
+       case G_ATTRNAME:
+           break;
+       case G_ATTRMODE:
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           if (WPU->colors[color].inuse) {
+               cp = &WPU->colors[color].color;
+               attrp[ai].u.c.r = cp->peRed;
+               attrp[ai].u.c.g = cp->peGreen;
+               attrp[ai].u.c.b = cp->peBlue;
+           } else {
+               attrp[ai].u.c.r = -1;
+               attrp[ai].u.c.g = -1;
+               attrp[ai].u.c.b = -1;
+           }
+           break;
+       case G_ATTRVIEWPORT:
+           attrp[ai].u.s = WPU->vsize;
+           break;
+       case G_ATTRWINDOW:
+           attrp[ai].u.r = WPU->wrect;
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTGETATTR, "windowid");
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GPdestroywidget(Gwidget_t * widget)
+{
+    HENHMETAFILE mfile;
+
+    if (WPU->mode == 1) {
+       mfile = CloseEnhMetaFile(GC);
+       OpenClipboard(NULL);
+       EmptyClipboard();
+       SetClipboardData(CF_ENHMETAFILE, mfile);
+       CloseClipboard();
+       DeleteMetaFile(mfile);
+    } else {
+       EndPage(GC);
+       EndDoc(GC);
+    }
+    return 0;
+}
+
+int GPcanvasclear(Gwidget_t * widget)
+{
+    HBRUSH brush, pbrush;
+
+    /* FIXME: drain repaint messages */
+    brush = CreateSolidBrush(PALETTEINDEX(0));
+    pbrush = SelectObject(GC, brush);
+    Rectangle(GC, 0, 0, (int) WPU->vsize.x, (int) WPU->vsize.y);
+    SelectObject(GC, pbrush);
+    return 0;
+}
+
+int GPsetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    setgattr(widget, ap);
+    WPU->defgattr = WPU->gattr;
+    return 0;
+}
+
+int GPgetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    if ((ap->flags & G_GATTRCOLOR))
+       ap->color = WPU->gattr.color;
+    if ((ap->flags & G_GATTRWIDTH))
+       ap->width = WPU->gattr.width;
+    if ((ap->flags & G_GATTRMODE))
+       ap->mode = WPU->gattr.mode;
+    if ((ap->flags & G_GATTRFILL))
+       ap->fill = WPU->gattr.fill;
+    if ((ap->flags & G_GATTRSTYLE))
+       ap->style = WPU->gattr.style;
+    return 0;
+}
+
+int GParrow(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    PIXpoint_t pp1, pp2, pa, pb, pd;
+    double tangent, l;
+
+    pp1 = pdrawtopix(widget, gp1), pp2 = pdrawtopix(widget, gp2);
+    pd.x = pp1.x - pp2.x, pd.y = pp1.y - pp2.y;
+    if (pd.x == 0 && pd.y == 0)
+       return 0;
+    tangent = atan2((double) pd.y, (double) pd.x);
+    if ((l = sqrt((double) (pd.x * pd.x + pd.y * pd.y))) > 30)
+       l = 30;
+    pa.x = l * cos(tangent + M_PI / 7) + pp2.x;
+    pa.y = l * sin(tangent + M_PI / 7) + pp2.y;
+    pb.x = l * cos(tangent - M_PI / 7) + pp2.x;
+    pb.y = l * sin(tangent - M_PI / 7) + pp2.y;
+    setgattr(widget, ap);
+    MoveToEx(GC, pp1.x, pp1.y, NULL), LineTo(GC, pp2.x, pp2.y);
+    MoveToEx(GC, pa.x, pa.y, NULL), LineTo(GC, pp2.x, pp2.y);
+    MoveToEx(GC, pb.x, pb.y, NULL), LineTo(GC, pp2.x, pp2.y);
+    return 0;
+}
+
+int GPline(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    PIXpoint_t pp1, pp2;
+
+    pp1 = pdrawtopix(widget, gp1), pp2 = pdrawtopix(widget, gp2);
+    setgattr(widget, ap);
+    MoveToEx(GC, pp1.x, pp1.y, NULL);
+    LineTo(GC, pp2.x, pp2.y);
+    return 0;
+}
+
+int GPbox(Gwidget_t * widget, Grect_t gr, Ggattr_t * ap)
+{
+    PIXrect_t pr;
+    Grect_t gr2;
+
+    if (gr.o.x <= gr.c.x)
+       gr2.o.x = gr.o.x, gr2.c.x = gr.c.x;
+    else
+       gr2.o.x = gr.c.x, gr2.c.x = gr.o.x;
+    if (gr.o.y <= gr.c.y)
+       gr2.o.y = gr.o.y, gr2.c.y = gr.c.y;
+    else
+       gr2.o.y = gr.c.y, gr2.c.y = gr.o.y;
+    pr = rdrawtopix(widget, gr);
+    setgattr(widget, ap);
+    if (WPU->gattr.fill)
+       Rectangle(GC, pr.o.x, pr.o.y, pr.c.x, pr.c.y);
+    else {
+       Gppp[0].x = pr.o.x, Gppp[0].y = pr.o.y;
+       Gppp[1].x = pr.c.x, Gppp[1].y = pr.o.y;
+       Gppp[2].x = pr.c.x, Gppp[2].y = pr.c.y;
+       Gppp[3].x = pr.o.x, Gppp[3].y = pr.c.y;
+       Gppp[4].x = pr.o.x, Gppp[4].y = pr.o.y;
+       Polyline(GC, Gppp, 5);
+    }
+    return 0;
+}
+
+int GPpolygon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    int n, i;
+
+    if (gpn == 0)
+       return 0;
+    if (gpn + 1 > Gppn) {
+       n = (((gpn + 1) + PPINCR - 1) / PPINCR) * PPINCR;
+       Gppp = Marraygrow(Gppp, (long) n * PPSIZE);
+       Gppn = n;
+    }
+    for (i = 0; i < gpn; i++)
+       Gppp[i] = pdrawtopix(widget, gpp[i]);
+    setgattr(widget, ap);
+    if (WPU->gattr.fill) {
+       if (Gppp[gpn - 1].x != Gppp[0].x || Gppp[gpn - 1].y != Gppp[0].y)
+           Gppp[gpn] = Gppp[0], gpn++;
+       Polygon(GC, Gppp, (int) gpn);
+    } else
+       Polyline(GC, Gppp, (int) gpn);
+    return 0;
+}
+
+int GPsplinegon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    PIXpoint_t p0, p1, p2, p3;
+    int n, i;
+
+    if (gpn == 0)
+       return 0;
+    Gppi = 1;
+    if (Gppi >= Gppn) {
+       n = (((Gppi + 1) + PPINCR - 1) / PPINCR) * PPINCR;
+       Gppp = Marraygrow(Gppp, (long) n * PPSIZE);
+       Gppn = n;
+    }
+    Gppp[0] = p3 = pdrawtopix(widget, gpp[0]);
+    for (i = 1; i < gpn; i += 3) {
+       p0 = p3;
+       p1 = pdrawtopix(widget, gpp[i]);
+       p2 = pdrawtopix(widget, gpp[i + 1]);
+       p3 = pdrawtopix(widget, gpp[i + 2]);
+       bezier(p0, p1, p2, p3);
+    }
+    setgattr(widget, ap);
+    if (WPU->gattr.fill) {
+       if (Gppp[Gppi - 1].x != Gppp[0].x || Gppp[Gppi - 1].y != Gppp[0].y)
+           Gppp[Gppi] = Gppp[0], Gppi++;
+       Polygon(GC, Gppp, (int) Gppi);
+    } else
+       Polyline(GC, Gppp, (int) Gppi);
+    return 0;
+}
+
+static void bezier(PIXpoint_t p0, PIXpoint_t p1,
+                  PIXpoint_t p2, PIXpoint_t p3)
+{
+    Gpoint_t gp0, gp1, gp2;
+    Gsize_t s;
+    PIXpoint_t p;
+    double t;
+    int n, i, steps;
+
+    if ((s.x = p3.x - p0.x) < 0)
+       s.x = -s.x;
+    if ((s.y = p3.y - p0.y) < 0)
+       s.y = -s.y;
+    if (s.x > s.y)
+       steps = s.x / 5 + 1;
+    else
+       steps = s.y / 5 + 1;
+    for (i = 0; i <= steps; i++) {
+       t = i / (double) steps;
+       gp0.x = p0.x + t * (p1.x - p0.x);
+       gp0.y = p0.y + t * (p1.y - p0.y);
+       gp1.x = p1.x + t * (p2.x - p1.x);
+       gp1.y = p1.y + t * (p2.y - p1.y);
+       gp2.x = p2.x + t * (p3.x - p2.x);
+       gp2.y = p2.y + t * (p3.y - p2.y);
+       gp0.x = gp0.x + t * (gp1.x - gp0.x);
+       gp0.y = gp0.y + t * (gp1.y - gp0.y);
+       gp1.x = gp1.x + t * (gp2.x - gp1.x);
+       gp1.y = gp1.y + t * (gp2.y - gp1.y);
+       p.x = gp0.x + t * (gp1.x - gp0.x) + 0.5;
+       p.y = gp0.y + t * (gp1.y - gp0.y) + 0.5;
+       if (Gppi >= Gppn) {
+           n = (((Gppi + 1) + PPINCR - 1) / PPINCR) * PPINCR;
+           Gppp = Marraygrow(Gppp, (long) n * PPSIZE);
+           Gppn = n;
+       }
+       Gppp[Gppi++] = p;
+    }
+}
+
+int GParc(Gwidget_t * widget, Gpoint_t gc, Gsize_t gs,
+         double ang1, double ang2, Ggattr_t * ap)
+{
+    PIXpoint_t pc;
+    PIXsize_t ps;
+    double a1, a2;
+
+    pc = pdrawtopix(widget, gc), ps = sdrawtopix(widget, gs);
+    setgattr(widget, ap);
+    a1 = ang1 * M_PI / 180, a2 = ang2 * M_PI / 180;
+    if (WPU->gattr.fill)
+       Chord(GC, pc.x - ps.x, pc.y - ps.y, pc.x + ps.x, pc.y + ps.y,
+             (int) (cos(a1) * ps.x), (int) (sin(a1) * ps.x),
+             (int) (cos(a2) * ps.x), (int) (sin(a2) * ps.x));
+    else
+       Arc(GC, pc.x - ps.x, pc.y - ps.y, pc.x + ps.x, pc.y + ps.y,
+           (int) (cos(a1) * ps.x), (int) (sin(a1) * ps.x),
+           (int) (cos(a2) * ps.x), (int) (sin(a2) * ps.x));
+    return 0;
+}
+
+int GPtext(Gwidget_t * widget, Gtextline_t * tlp, int n, Gpoint_t go,
+          char *fn, double fs, char *justs, Ggattr_t * ap)
+{
+    Gsize_t gs;
+    PIXpoint_t po;
+    PIXsize_t ps;
+    PIXrect_t pr;
+    HFONT font;
+    SIZE size;
+    RECT r;
+    int x, y, w, h, i;
+
+    po = pdrawtopix(widget, go);
+    gs.x = 0, gs.y = fs;
+    ps = sdrawtopix(widget, gs);
+    if (!(font = findfont(fn, ps.y))) {
+       Rectangle(GC, po.x, po.y, po.x + 1, po.y + 1);
+       return 0;
+    }
+    setgattr(widget, ap);
+    if (font != WPU->font) {
+       WPU->font = font;
+       SelectObject(GC, font);
+    }
+    for (w = h = 0, i = 0; i < n; i++) {
+       if (tlp[i].n)
+           GetTextExtentPoint32(GC, tlp[i].p, (int) tlp[i].n, &size);
+       else
+           GetTextExtentPoint32(GC, "M", (int) 1, &size);
+       tlp[i].w = size.cx, tlp[i].h = size.cy;
+       w = max(w, size.cx), h += size.cy;
+    }
+    switch (justs[0]) {
+    case 'l':
+       po.x += w / 2;
+       break;
+    case 'r':
+       po.x -= w / 2;
+       break;
+    }
+    switch (justs[1]) {
+    case 'd':
+       po.y -= h;
+       break;
+    case 'c':
+       po.y -= h / 2;
+       break;
+    }
+    pr.o.x = po.x - w / 2, pr.o.y = po.y;
+    pr.c.x = po.x + w / 2, pr.c.y = po.y + h;
+    for (i = 0; i < n; i++) {
+       switch (tlp[i].j) {
+       case 'l':
+           x = po.x - w / 2;
+           break;
+       case 'n':
+           x = po.x - tlp[i].w / 2;
+           break;
+       case 'r':
+           x = po.x - (tlp[i].w - w / 2);
+           break;
+       }
+       y = po.y + i * tlp[i].h;
+       r.left = x, r.top = y;
+       r.right = x + tlp[i].w, r.bottom = y + tlp[i].h;
+       DrawText(GC, tlp[i].p, (int) tlp[i].n, &r, DT_LEFT | DT_TOP);
+    }
+    return 0;
+}
+
+static HFONT findfont(char *name, int size)
+{
+    HFONT font;
+    int fi;
+
+    if (name[0] == '\000')
+       return Gfontp[0].font;
+
+    sprintf(&Gbufp[0], name, size);
+    for (fi = 0; fi < Gfontn; fi++)
+       if (Strcmp(&Gbufp[0], Gfontp[fi].name) == 0
+           && Gfontp[fi].size == size)
+           return Gfontp[fi].font;
+    font = CreateFont((int) size,
+                     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &Gbufp[0]);
+    if (!font)
+       font = Gfontp[0].font;
+
+    Gfontp = Marraygrow(Gfontp, (long) (Gfontn + 1) * FONTSIZE);
+    Gfontp[Gfontn].name = strdup(&Gbufp[0]);
+    Gfontp[Gfontn].size = size;
+    Gfontp[Gfontn].font = font;
+    Gfontn++;
+    return font;
+}
+
+int GPcreatebitmap(Gwidget_t * widget, Gbitmap_t * bitmap, Gsize_t s)
+{
+    if (!widget) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    if (!(bitmap->u.bmap.orig = CreateBitmap((int) s.x, (int) s.y,
+                                            1, Gdepth, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    bitmap->u.bmap.scaled = 0;
+    bitmap->scale.x = bitmap->scale.y = 1;
+    bitmap->ctype = widget->type;
+    bitmap->canvas = widget - &Gwidgets[0];
+    bitmap->size = s;
+    return 0;
+}
+
+int GPdestroybitmap(Gbitmap_t * bitmap)
+{
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    DeleteObject(bitmap->u.bmap.orig);
+    return 0;
+}
+
+int GPreadbitmap(Gwidget_t * widget, Gbitmap_t * bitmap, FILE * fp)
+{
+    Gsize_t s;
+    HDC gc;
+    char bufp[2048];
+    unsigned int rgb[3];
+    char *s1, *s2;
+    char c;
+    int bufn, bufi, step, x, y, k;
+
+    if (!widget) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    step = 0;
+    while (step < 3) {
+      l1:
+       if (!fgets(bufp, 2048, fp)) {
+           Gerr(POS, G_ERRCANNOTREADBITMAP);
+           return -1;
+       }
+       s1 = &bufp[0];
+      l2:
+       for (; *s1 && isspace(*s1); s1++);
+       if (!*s1 || *s1 == '#')
+           goto l1;
+       switch (step) {
+       case 0:
+           if (strncmp(s1, "P6", 2) != 0) {
+               Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           step++, s1 += 2;
+           goto l2;
+       case 1:
+           for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++);
+           c = *s2, *s2 = 0;
+           if (s2 == s1 || (s.x = atoi(s1)) <= 0) {
+               *s2 = c, Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           *s2 = c, step++, s1 = s2;
+           goto l2;
+       case 2:
+           for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++);
+           c = *s2, *s2 = 0;
+           if (s2 == s1 || (s.y = atoi(s1)) <= 0) {
+               *s2 = c, Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           *s2 = c, step++, s1 = s2;
+           goto l2;
+       }
+    }
+    if (!(bitmap->u.bmap.orig = CreateBitmap((int) s.x, (int) s.y,
+                                            1, Gdepth, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    gc = CreateCompatibleDC(GC);
+    SelectObject(gc, bitmap->u.bmap.orig);
+    bitmap->u.bmap.scaled = 0;
+    bitmap->scale.x = bitmap->scale.y = 1;
+    bitmap->ctype = widget->type;
+    bitmap->canvas = widget - &Gwidgets[0];
+    bitmap->size = s;
+    bufi = bufn = 0;
+    bufp[bufi] = 0;
+    for (y = 0; y < s.y; y++) {
+       for (x = 0; x < s.x; x++) {
+           for (k = 0; k < 3; k++) {
+               if (bufi == bufn) {
+                   if ((bufn = fread(bufp, 1, 2047, fp)) == 0) {
+                       if (ferror(fp))
+                           bufn = -1;
+                       DeleteDC(gc);
+                       DeleteObject(bitmap->u.bmap.orig);
+                       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+                       return -1;
+                   }
+                   bufi = 0;
+               }
+               rgb[k] = (unsigned char) bufp[bufi++];
+           }
+           SetPixel(gc, x, y, RGB(rgb[0], rgb[1], rgb[2]));
+       }
+    }
+    DeleteDC(gc);
+    return 0;
+}
+
+int GPwritebitmap(Gbitmap_t * bitmap, FILE * fp)
+{
+    Gwidget_t *widget;
+    HDC gc;
+    COLORREF color;
+    char bufp[2048];
+    int bufi, x, y, w, h;
+
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    if (bitmap->canvas < 0 || bitmap->canvas >= Gwidgetn ||
+       !Gwidgets[bitmap->canvas].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, bitmap->canvas);
+       return -1;
+    }
+    widget = &Gwidgets[bitmap->canvas];
+    if (widget->type != G_CANVASWIDGET && widget->type != G_PCANVASWIDGET) {
+       Gerr(POS, G_ERRNOTACANVAS, bitmap->canvas);
+       return -1;
+    }
+    gc = CreateCompatibleDC(GC);
+    SelectObject(gc, bitmap->u.bmap.orig);
+    fprintf(fp, "P6\n%d %d 255\n", (int) bitmap->size.x,
+           (int) bitmap->size.y);
+    bufi = 0;
+    w = bitmap->size.x;
+    h = bitmap->size.y;
+    for (y = 0; y < h; y++) {
+       for (x = 0; x < w; x++) {
+           color = GetPixel(gc, x, y);
+           bufp[bufi++] = GetRValue(color);
+           bufp[bufi++] = GetGValue(color);
+           bufp[bufi++] = GetBValue(color);
+           if (bufi + 3 >= 2048) {
+               fwrite(bufp, 1, bufi, fp);
+               bufi = 0;
+           }
+       }
+    }
+    if (bufi > 0)
+       fwrite(bufp, 1, bufi, fp);
+    DeleteDC(gc);
+    return 0;
+}
+
+int GPbitblt(Gwidget_t * widget, Gpoint_t gp, Grect_t gr,
+            Gbitmap_t * bitmap, char *mode, Ggattr_t * ap)
+{
+    PIXrect_t pr, r;
+    PIXpoint_t pp;
+    PIXsize_t s;
+    Gsize_t scale;
+    Gxy_t p;
+    HBITMAP pix;
+    HDC gc;
+    double tvx, tvy, twx, twy;
+
+    if (gr.o.x > gr.c.x)
+       p.x = gr.o.x, gr.o.x = gr.c.x, gr.c.x = p.x;
+    if (gr.o.y > gr.c.y)
+       p.y = gr.o.y, gr.o.y = gr.c.y, gr.c.y = p.y;
+    if (strcmp(mode, "b2c") == 0) {
+       tvx = WPU->vsize.x, tvy = WPU->vsize.y;
+       twx = WPU->wrect.c.x - WPU->wrect.o.x;
+       twy = WPU->wrect.c.y - WPU->wrect.o.y;
+       scale.x = tvx / twx, scale.y = tvy / twy;
+       if (scale.x == 1 && scale.y == 1) {
+           pix = bitmap->u.bmap.orig;
+           bitmap->scale = scale;
+       } else {
+           if (scale.x != bitmap->scale.x || scale.y != bitmap->scale.y)
+               scalebitmap(widget, bitmap, scale, TRUE, 1);
+           pix = bitmap->u.bmap.scaled;
+       }
+       pr = rdrawtopix(widget, gr);
+       pp = pdrawtobpix(bitmap, gp);
+       s.x = pr.c.x - pr.o.x + 1, s.y = pr.c.y - pr.o.y + 1;
+       r.o.x = pp.x, r.o.y = pp.y - s.y + 1;
+       r.c.x = r.o.x + s.x - 1, r.c.y = r.o.y + s.y - 1;
+       if (r.o.x < 0)
+           pr.o.x -= r.o.x, r.o.x = 0;
+       if (r.o.y < 0)
+           pr.o.y -= r.o.y, r.o.y = 0;
+       if (r.c.x >= bitmap->size.x * scale.x) {
+           pr.c.x -= (r.c.x + 1 - bitmap->size.x * scale.x);
+           r.c.x = bitmap->size.x * scale.x - 1;
+       }
+       if (r.c.y >= bitmap->size.y * scale.y) {
+           pr.c.y -= (r.c.y + 1 - bitmap->size.y * scale.y);
+           r.c.y = bitmap->size.y * scale.y - 1;
+       }
+       if (pr.o.x < 0)
+           r.o.x -= pr.o.x, pr.o.x = 0;
+       if (pr.o.y < 0)
+           r.o.y -= pr.o.y, pr.o.y = 0;
+       setgattr(widget, ap);
+       gc = CreateCompatibleDC(GC);
+       SelectObject(gc, pix);
+       BitBlt(GC, pr.o.x, pr.o.y, r.c.x - r.o.x + 1, r.c.y - r.o.y + 1,
+              gc, r.o.x, r.o.y, (WPU->gattr.mode == G_SRC) ?
+              SRCCOPY : SRCINVERT);
+       DeleteDC(gc);
+    } else if (strcmp(mode, "c2b") == 0) {
+       tvx = WPU->vsize.x, tvy = WPU->vsize.y;
+       twx = WPU->wrect.c.x - WPU->wrect.o.x;
+       twy = WPU->wrect.c.y - WPU->wrect.o.y;
+       scale.x = tvx / twx, scale.y = tvy / twy;
+       if (scale.x == 1 && scale.y == 1) {
+           pix = bitmap->u.bmap.orig;
+           bitmap->scale = scale;
+       } else {
+           if (scale.x != bitmap->scale.x || scale.y != bitmap->scale.y)
+               scalebitmap(widget, bitmap, scale, FALSE, 1);
+           pix = bitmap->u.bmap.scaled;
+       }
+       pr = rdrawtobpix(bitmap, gr);
+       pp = pdrawtopix(widget, gp);
+       s.x = pr.c.x - pr.o.x + 1, s.y = pr.c.y - pr.o.y + 1;
+       r.o.x = pp.x, r.o.y = pp.y - s.y + 1;
+       r.c.x = r.o.x + s.x - 1, r.c.y = r.o.y + s.y - 1;
+       if (pr.o.x < 0)
+           r.o.x -= pr.o.x, pr.o.x = 0;
+       if (pr.o.y < 0)
+           r.o.y -= pr.o.y, pr.o.y = 0;
+       if (pr.c.x >= bitmap->size.x * scale.x) {
+           r.c.x -= (pr.c.x + 1 - bitmap->size.x * scale.x);
+           pr.c.x = bitmap->size.x * scale.x - 1;
+       }
+       if (pr.c.y >= bitmap->size.y * scale.y) {
+           r.c.y -= (pr.c.y + 1 - bitmap->size.y * scale.y);
+           pr.c.y = bitmap->size.y * scale.y - 1;
+       }
+       if (r.o.x < 0)
+           pr.o.x -= r.o.x, r.o.x = 0;
+       if (r.o.y < 0)
+           pr.o.y -= r.o.y, r.o.y = 0;
+       setgattr(widget, ap);
+       gc = CreateCompatibleDC(GC);
+       SelectObject(gc, pix);
+       BitBlt(gc, pr.o.x, pr.o.y, r.c.x - r.o.x + 1, r.c.y - r.o.y + 1,
+              GC, r.o.x, r.o.y, (WPU->gattr.mode == G_SRC) ?
+              SRCCOPY : SRCINVERT);
+       if (pix != bitmap->u.bmap.orig)
+           scalebitmap(widget, bitmap, scale, TRUE, -1);
+       DeleteDC(gc);
+    }
+    return 0;
+}
+
+static int scalebitmap(Gwidget_t * widget, Gbitmap_t * bitmap,
+                      Gsize_t scale, int copybits, int dir)
+{
+    Gsize_t nsize, o2n;
+    HBITMAP opix, spix;
+    COLORREF color;
+    HDC gc1, gc2;
+    int x, y, x2, y2, xp, yp;
+    double prod, rgb[3], xr2, yr2, xl2, yl2, xf2, yf2, xr, yr, xl, yl;
+
+    if (!copybits) {
+       if (dir == 1) {
+           nsize.x = (int) (bitmap->size.x * scale.x);
+           nsize.y = (int) (bitmap->size.y * scale.y);
+           if (!(spix = CreateBitmap((int) nsize.x, (int) nsize.y, 1,
+                                     Gdepth, NULL))) {
+               Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+               return -1;
+           }
+           if (bitmap->u.bmap.scaled)
+               DeleteObject(bitmap->u.bmap.scaled);
+           bitmap->u.bmap.scaled = spix;
+           bitmap->scale = scale;
+       }
+       return 0;
+    }
+    if (dir == 1) {
+       nsize.x = (int) (bitmap->size.x * scale.x);
+       nsize.y = (int) (bitmap->size.y * scale.y);
+       o2n.x = 1 / scale.x, o2n.y = 1 / scale.y;
+       if (!(spix = CreateBitmap((int) nsize.x, (int) nsize.y, 1,
+                                 Gdepth, NULL))) {
+           Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+           return -1;
+       }
+       opix = bitmap->u.bmap.orig;
+    } else {
+       nsize.x = (int) bitmap->size.x;
+       nsize.y = (int) bitmap->size.y;
+       o2n.x = scale.x, o2n.y = scale.y;
+       spix = bitmap->u.bmap.orig;
+       opix = bitmap->u.bmap.scaled;
+    }
+    gc1 = CreateCompatibleDC(GC);
+    SelectObject(gc1, opix);
+    gc2 = CreateCompatibleDC(GC);
+    SelectObject(gc2, spix);
+    prod = o2n.x * o2n.y;
+    y = 0;
+    yr = o2n.y;
+    yl = 0;
+    for (yp = 0; yp < nsize.y; yp++) {
+       x = 0;
+       xr = o2n.x;
+       xl = 0;
+       for (xp = 0; xp < nsize.x; xp++) {
+           y2 = y;
+           yr2 = yr;
+           yl2 = yl;
+           rgb[0] = rgb[1] = rgb[2] = 0;
+           do {
+               x2 = x;
+               xr2 = xr;
+               xl2 = xl;
+               yf2 = (yl2 + yr2 > 1) ? 1 - yl2 : yr2, yr2 -= yf2;
+               do {
+                   xf2 = (xl2 + xr2 > 1) ? 1 - xl2 : xr2, xr2 -= xf2;
+                   color = GetPixel(gc1, x2, y2);
+                   rgb[0] += (GetRValue(color) * xf2 * yf2 / prod);
+                   rgb[1] += (GetGValue(color) * xf2 * yf2 / prod);
+                   rgb[2] += (GetBValue(color) * xf2 * yf2 / prod);
+                   xl2 += xf2;
+                   if (xl2 >= 1)
+                       x2++, xl2 -= 1;
+               } while (xr2 > 0);
+               xr2 = o2n.x;
+               yl2 += yf2;
+               if (yl2 >= 1)
+                   y2++, yl2 -= 1;
+           } while (yr2 > 0);
+           yr2 = o2n.y;
+           SetPixel(gc2, xp, yp, RGB(rgb[0], rgb[1], rgb[2]));
+           x = x2;
+           xr = xr2;
+           xl = xl2;
+       }
+       y = y2;
+       yr = yr2;
+       yl = yl2;
+    }
+    DeleteDC(gc1);
+    DeleteDC(gc2);
+    if (dir == 1) {
+       if (bitmap->u.bmap.scaled)
+           DeleteObject(bitmap->u.bmap.scaled);
+       bitmap->u.bmap.scaled = spix;
+       bitmap->scale = scale;
+    }
+    return 0;
+}
+
+static void setgattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    HBRUSH brush, pbrush;
+    HPEN pen, ppen;
+    PALETTEENTRY *colorp;
+    long color, mode, style, width, flag, pati;
+    double intens;
+
+    if (!(ap->flags & G_GATTRCOLOR))
+       ap->color = WPU->defgattr.color;
+    if (!(ap->flags & G_GATTRWIDTH))
+       ap->width = WPU->defgattr.width;
+    if (!(ap->flags & G_GATTRMODE))
+       ap->mode = WPU->defgattr.mode;
+    if (!(ap->flags & G_GATTRFILL))
+       ap->fill = WPU->defgattr.fill;
+    if (!(ap->flags & G_GATTRSTYLE))
+       ap->style = WPU->defgattr.style;
+    flag = FALSE;
+    mode = ap->mode;
+    if (mode != WPU->gattr.mode) {
+       WPU->gattr.mode = mode;
+       SetROP2(GC, (int) mode);
+    }
+    WPU->gattr.fill = ap->fill;
+    color = ap->color;
+    if (color >= G_MAXCOLORS || !(WPU->colors[color].inuse))
+       color = 1;
+    if (color != WPU->gattr.color)
+       WPU->gattr.color = color, flag = TRUE;
+    width = ap->width;
+    if (width != WPU->gattr.width)
+       WPU->gattr.width = width, flag = TRUE;
+    style = ap->style;
+    if (style != WPU->gattr.style)
+       WPU->gattr.style = style, style = TRUE;
+
+    if (!flag)
+       return;
+    WPU->gattr.color = color;
+    if (Gdepth == 1) {
+       colorp = &WPU->colors[color].color;
+       intens = (0.3 * colorp->peBlue + 0.59 * colorp->peRed +
+                 0.11 * colorp->peGreen) / 255.0;
+       pati =
+           (intens <= 0.0625) ? 16 : -16.0 * (log(intens) / 2.7725887222);
+       brush = WPU->grays[pati];
+    } else
+       brush = CreateSolidBrush(PALETTEINDEX(WPU->gattr.color));
+    pbrush = SelectObject(GC, brush);
+    if (Gdepth != 1)
+       DeleteObject(pbrush);
+    pen = CreatePen((int) gstyles[WPU->gattr.style], WPU->gattr.width,
+                   PALETTEINDEX(WPU->gattr.color));
+    ppen = SelectObject(GC, pen);
+    DeleteObject(ppen);
+    SetTextColor(GC, PALETTEINDEX(WPU->gattr.color));
+}
+
+static PIXrect_t rdrawtopix(Gwidget_t * widget, Grect_t gr)
+{
+    PIXrect_t pr;
+    double tvx, tvy, twx, twy;
+
+    tvx = WPU->vsize.x - 1, tvy = WPU->vsize.y - 1;
+    twx = WPU->wrect.c.x - WPU->wrect.o.x;
+    twy = WPU->wrect.c.y - WPU->wrect.o.y;
+    pr.o.x = tvx * (gr.o.x - WPU->wrect.o.x) / twx + 0.5;
+    pr.o.y = tvy * (1.0 - (gr.c.y - WPU->wrect.o.y) / twy) + 0.5;
+    pr.c.x = tvx * (gr.c.x - WPU->wrect.o.x) / twx + 0.5;
+    pr.c.y = tvy * (1.0 - (gr.o.y - WPU->wrect.o.y) / twy) + 0.5;
+    return pr;
+}
+
+static PIXpoint_t pdrawtopix(Gwidget_t * widget, Gpoint_t gp)
+{
+    PIXpoint_t pp;
+    double tvx, tvy, twx, twy;
+
+    tvx = WPU->vsize.x - 1, tvy = WPU->vsize.y - 1;
+    twx = WPU->wrect.c.x - WPU->wrect.o.x;
+    twy = WPU->wrect.c.y - WPU->wrect.o.y;
+    pp.x = tvx * (gp.x - WPU->wrect.o.x) / twx + 0.5;
+    pp.y = tvy * (1.0 - (gp.y - WPU->wrect.o.y) / twy) + 0.5;
+    return pp;
+}
+
+static PIXsize_t sdrawtopix(Gwidget_t * widget, Gsize_t gs)
+{
+    PIXsize_t ps;
+    double tvx, tvy, twx, twy;
+
+    tvx = WPU->vsize.x - 1, tvy = WPU->vsize.y - 1;
+    twx = WPU->wrect.c.x - WPU->wrect.o.x;
+    twy = WPU->wrect.c.y - WPU->wrect.o.y;
+    ps.x = tvx * (gs.x - 1) / twx + 1.5;
+    ps.y = tvy * (gs.y - 1) / twy + 1.5;
+    return ps;
+}
+
+static PIXrect_t rdrawtobpix(Gbitmap_t * bitmap, Grect_t gr)
+{
+    PIXrect_t pr;
+    double tvy;
+
+    tvy = (int) ((bitmap->size.y - 1) * bitmap->scale.y);
+    pr.o.x = gr.o.x + 0.5;
+    pr.o.y = tvy - gr.c.y + 0.5;
+    pr.c.x = gr.c.x + 0.5;
+    pr.c.y = tvy - gr.o.y + 0.5;
+    return pr;
+}
+
+static PIXpoint_t pdrawtobpix(Gbitmap_t * bitmap, Gpoint_t gp)
+{
+    PIXpoint_t pp;
+    double tvy;
+
+    tvy = (int) ((bitmap->size.y - 1) * bitmap->scale.y);
+    pp.x = gp.x + 0.5;
+    pp.y = tvy - gp.y + 0.5;
+    return pp;
+}
diff --git a/cmd/lefty/ws/mswin32/gquery.c b/cmd/lefty/ws/mswin32/gquery.c
new file mode 100644 (file)
index 0000000..0b7c9fb
--- /dev/null
@@ -0,0 +1,262 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+#include "resource.h"
+
+#define WQU widget->u.q
+
+BOOL CALLBACK stringproc(HWND, UINT, WPARAM, LPARAM);
+BOOL CALLBACK choiceproc(HWND, UINT, WPARAM, LPARAM);
+
+int GQcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    DWORD wflags;
+    int ai;
+
+    WQU->mode = G_QWSTRING;
+    wflags = WS_OVERLAPPEDWINDOW;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRMODE:
+           if (Strcmp("string", attrp[ai].u.t) == 0)
+               WQU->mode = G_QWSTRING;
+           else if (Strcmp("file", attrp[ai].u.t) == 0)
+               WQU->mode = G_QWFILE;
+           else if (Strcmp("choice", attrp[ai].u.t) == 0)
+               WQU->mode = G_QWCHOICE;
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    switch (WQU->mode) {
+    case G_QWSTRING:
+       widget->w = 0;
+       break;
+    case G_QWFILE:
+       widget->w = 0;
+       break;
+    case G_QWCHOICE:
+       widget->w = 0;
+       break;
+    }
+    return 0;
+}
+
+int GQsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRMODE:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "mode");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GQgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRMODE:
+           switch (WQU->mode) {
+           case G_QWSTRING:
+               attrp[ai].u.t = "string";
+               break;
+           case G_QWFILE:
+               attrp[ai].u.t = "file";
+               break;
+           case G_QWCHOICE:
+               attrp[ai].u.t = "choice";
+               break;
+           }
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GQdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
+
+static Gwidget_t *choicew;
+static char *buttons[20];
+static char *stringp;
+static int stringn;
+
+int GQqueryask(Gwidget_t * widget, char *prompt, char *args,
+              char *responsep, int responsen)
+{
+    OPENFILENAME ofn;
+    char buf[256];
+    char *s1, *s2;
+    char c;
+    int i;
+
+    switch (WQU->mode) {
+    case G_QWSTRING:
+       stringp = responsep;
+       stringn = responsen;
+       buttons[0] = prompt;
+       buttons[1] = args;
+       DialogBox(hinstance, (LPCSTR) "STRINGDIALOG", (HWND) NULL,
+                 stringproc);
+       Gpopdownflag = TRUE;
+       if (stringp)
+           return 0;
+       return -1;
+       break;
+    case G_QWFILE:
+       strcpy(buf, args);
+       ofn.lStructSize = sizeof(OPENFILENAME);
+       ofn.hwndOwner = (HWND) NULL;
+       ofn.lpstrFilter = "All Files (*.*)\0*.*\0";
+       ofn.lpstrCustomFilter = NULL;
+       ofn.nMaxCustFilter = 0;
+       ofn.nFilterIndex = 1;
+       ofn.lpstrFile = buf;
+       ofn.nMaxFile = 256;
+       ofn.lpstrFileTitle = NULL;
+       ofn.nMaxFileTitle = 0;
+       ofn.lpstrInitialDir = NULL;
+       ofn.lpstrTitle = prompt;
+       ofn.Flags = 0;
+       ofn.lpstrDefExt = NULL;
+       if (!GetOpenFileName(&ofn)) {
+           Gpopdownflag = TRUE;
+           return -1;
+       }
+       strncpy(responsep, buf, responsen);
+       Gpopdownflag = TRUE;
+       break;
+    case G_QWCHOICE:
+       if (!args)
+           return -1;
+       WQU->button = 0;
+       choicew = widget;
+       buttons[0] = prompt;
+       for (s1 = args, i = 1; *s1; i++) {
+           buttons[i] = s1;
+           s2 = s1;
+           while (*s2 && *s2 != '|')
+               s2++;
+           c = *s2, *s2 = 0;
+           s1 = s2;
+           if (c)
+               s1++;
+       }
+       buttons[i] = NULL;
+       DialogBox(hinstance, "CHOICEDIALOG", (HWND) NULL, choiceproc);
+       if (WQU->button > 0)
+           strncpy(responsep, buttons[WQU->button], responsen);
+       for (s2 = args; s2 < s1; s2++)
+           if (!*s2)
+               *s2 = '|';
+       Gpopdownflag = TRUE;
+       if (WQU->button > 0)
+           return 0;
+       return -1;
+       break;
+    }
+    if (responsep[0] && responsep[strlen(responsep) - 1] == '\n')
+       responsep[strlen(responsep) - 1] = 0;
+    return 0;
+}
+
+BOOL CALLBACK stringproc(HWND hdlg, UINT message,
+                        WPARAM wparam, LPARAM lparam)
+{
+    switch (message) {
+    case WM_INITDIALOG:
+       SetDlgItemText(hdlg, IDC_STATIC1, (LPCSTR) buttons[0]);
+       if (buttons[1])
+           SetDlgItemText(hdlg, IDC_EDIT1, (LPCSTR) buttons[1]);
+       return TRUE;
+       break;
+    case WM_COMMAND:
+       switch (wparam) {
+       case IDOK:
+           GetDlgItemText(hdlg, IDC_EDIT1, stringp, stringn);
+           EndDialog(hdlg, TRUE);
+           return TRUE;
+       case IDCANCEL:
+           stringp = NULL;
+           EndDialog(hdlg, TRUE);
+           return TRUE;
+           break;
+       }
+    }
+    return FALSE;
+}
+
+BOOL CALLBACK choiceproc(HWND hdlg, UINT message,
+                        WPARAM wparam, LPARAM lparam)
+{
+    int sel, i;
+
+    switch (message) {
+    case WM_INITDIALOG:
+       SetDlgItemText(hdlg, IDC_STATIC1, (LPCSTR) buttons[0]);
+       for (i = 1; buttons[i]; i++)
+           SendDlgItemMessage(hdlg, IDC_LIST1, LB_ADDSTRING,
+                              0, (LPARAM) buttons[i]);
+       return TRUE;
+       break;
+    case WM_COMMAND:
+       if ((sel = (int) SendDlgItemMessage(hdlg, IDC_LIST1,
+                                           LB_GETCURSEL, 0, 0)) >= 0) {
+           choicew->u.q->button = sel + 1;
+           EndDialog(hdlg, TRUE);
+       }
+       return TRUE;
+       break;
+    }
+    return FALSE;
+}
diff --git a/cmd/lefty/ws/mswin32/gscroll.c b/cmd/lefty/ws/mswin32/gscroll.c
new file mode 100644 (file)
index 0000000..1f197e8
--- /dev/null
@@ -0,0 +1,210 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+int GScreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    DWORD wflags;
+    int ai;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    wflags = WS_CHILDWINDOW | WS_HSCROLL | WS_VSCROLL;
+    ps.x = ps.y = MINSWSIZE;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINSWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           wflags |= WS_BORDER;
+           break;
+       case G_ATTRCHILDCENTER:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "childcenter");
+           return -1;
+       case G_ATTRMODE:
+           if (Strcmp("forcebars", attrp[ai].u.t) != 0) {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    Gadjustwrect(parent, &ps);
+    if (!(widget->w = CreateWindow("ScrollClass", "scroll", wflags, 0, 0,
+                                  ps.x, ps.y, parent->w,
+                                  (HMENU) (widget - &Gwidgets[0]),
+                                  hinstance, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    ShowWindow(widget->w, SW_SHOW);
+    UpdateWindow(widget->w);
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawinsertchild(parent, widget);
+    return 0;
+}
+
+int GSsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Gwidget_t *parent, *child;
+    PIXpoint_t po;
+    PIXsize_t ps, pps, cps;
+    RECT r;
+    DWORD wflags1, wflags2;
+    int ai, wi;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    wflags1 = SWP_NOMOVE | SWP_NOZORDER;
+    wflags2 = SWP_NOSIZE | SWP_NOZORDER;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINSWSIZE);
+           Gadjustwrect(parent, &ps);
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps.x, ps.y,
+                        wflags1);
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "borderwidth");
+           return -1;
+       case G_ATTRCHILDCENTER:
+           for (wi = 0; wi < Gwidgetn; wi++) {
+               child = &Gwidgets[wi];
+               if (child->inuse && child->pwi == widget - &Gwidgets[0])
+                   break;
+           }
+           if (wi == Gwidgetn)
+               return 0;
+           GETORIGIN(attrp[ai].u.p, po);
+           GetClientRect(widget->w, &r);
+           pps.x = r.right - r.left, pps.y = r.bottom - r.top;
+           po.x -= pps.x / 2, po.y -= pps.y / 2;
+           GetWindowRect(child->w, &r);
+           cps.x = r.right - r.left, cps.y = r.bottom - r.top;
+           if (po.x < 0)
+               po.x = 0;
+           if (po.y < 0)
+               po.y = 0;
+           if (po.x > cps.x - pps.x)
+               po.x = cps.x - pps.x;
+           if (po.y > cps.y - pps.y)
+               po.y = cps.y - pps.y;
+           SetWindowPos(child->w, (HWND) NULL, -po.x, -po.y, 0, 0,
+                        wflags2);
+           SetScrollPos(widget->w, SB_HORZ, po.x, TRUE);
+           SetScrollPos(widget->w, SB_VERT, po.y, TRUE);
+           break;
+       case G_ATTRMODE:
+           if (Strcmp("forcebars", attrp[ai].u.t) != 0) {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GSgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Gwidget_t *child;
+    RECT r;
+    int width, height, ai, wi;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GetWindowRect(widget->w, &r);
+           attrp[ai].u.s.x = r.right - r.left;
+           attrp[ai].u.s.y = r.bottom - r.top;
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTGETATTR, "borderwidth");
+           return -1;
+       case G_ATTRCHILDCENTER:
+           for (wi = 0; wi < Gwidgetn; wi++) {
+               child = &Gwidgets[wi];
+               if (child->inuse && child->pwi == widget - &Gwidgets[0])
+                   break;
+           }
+           if (wi == Gwidgetn) {
+               Gerr(POS, G_ERRNOCHILDWIDGET);
+               return -1;
+           }
+           GetWindowRect(widget->w, &r);
+           width = r.right - r.left;
+           height = r.bottom - r.top;
+           GetWindowRect(widget->w, &r);
+           attrp[ai].u.p.x = width / 2 - r.left;
+           attrp[ai].u.p.y = height / 2 - r.top;
+           break;
+       case G_ATTRMODE:
+           attrp[ai].u.t = "forcebars";
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", widget->w);
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GSdestroywidget(Gwidget_t * widget)
+{
+    Gwidget_t *parent;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawdeletechild(parent, widget);
+    DestroyWindow(widget->w);
+    return 0;
+}
diff --git a/cmd/lefty/ws/mswin32/gtext.c b/cmd/lefty/ws/mswin32/gtext.c
new file mode 100644 (file)
index 0000000..13eed9c
--- /dev/null
@@ -0,0 +1,219 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WTU widget->u.t
+
+int GTcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    DWORD wflags;
+    char *s;
+    int ai;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    wflags = WS_CHILDWINDOW;
+    WTU->func = NULL;
+    ps.x = ps.y = MINTWSIZE;
+    s = "";
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINTWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           wflags |= WS_BORDER;
+           break;
+       case G_ATTRTEXT:
+           s = attrp[ai].u.t;
+           break;
+       case G_ATTRAPPENDTEXT:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "appendtext");
+           return -1;
+       case G_ATTRMODE:
+           if (Strcmp("oneline", attrp[ai].u.t) != 0 &&
+               Strcmp("input", attrp[ai].u.t) != 0 &&
+               Strcmp("output", attrp[ai].u.t) != 0) {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRNEWLINECB:
+           WTU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    Gadjustwrect(parent, &ps);
+    wflags |= (ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL);
+    if (ps.y > 40)
+       wflags |= (WS_HSCROLL | WS_VSCROLL);
+    if (!(widget->w = CreateWindow("EDIT", s, wflags, 0, 0, ps.x, ps.y,
+                                  parent->w,
+                                  (HMENU) (widget - &Gwidgets[0]),
+                                  hinstance, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    ShowWindow(widget->w, SW_SHOW);
+    UpdateWindow(widget->w);
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawinsertchild(parent, widget);
+    return 0;
+}
+
+int GTsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Gwidget_t *parent;
+    PIXsize_t ps;
+    DWORD wflags1;
+    int ai;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    wflags1 = SWP_NOMOVE | SWP_NOZORDER;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINTWSIZE);
+           Gadjustwrect(parent, &ps);
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps.x, ps.y,
+                        wflags1);
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "borderwidth");
+           return -1;
+       case G_ATTRTEXT:
+           Gnocallbacks = TRUE;
+           SendMessage(widget->w, WM_SETTEXT, 0, "");
+           SendMessage(widget->w, EM_SETSEL, -1, 32760);
+           SendMessage(widget->w, EM_REPLACESEL, 0,
+                       (LPARAM) attrp[ai].u.t);
+           SendMessage(widget->w, EM_SETSEL, -1, 32760);
+           /* SendMessage (widget->w, EM_REPLACESEL, 0, (LPARAM) "\r\n"); */
+           /* SendMessage (widget->w, EM_SETSEL, -1, 32760); */
+           Gnocallbacks = FALSE;
+           break;
+       case G_ATTRAPPENDTEXT:
+           Gnocallbacks = TRUE;
+           SendMessage(widget->w, EM_SETSEL, -1, 32760);
+           SendMessage(widget->w, EM_REPLACESEL, 0,
+                       (LPARAM) attrp[ai].u.t);
+           SendMessage(widget->w, EM_SETSEL, -1, 32760);
+           SendMessage(widget->w, EM_REPLACESEL, 0, (LPARAM) "\r\n");
+           SendMessage(widget->w, EM_SETSEL, -1, 32760);
+           Gnocallbacks = FALSE;
+           break;
+       case G_ATTRMODE:
+           if (Strcmp("oneline", attrp[ai].u.t) != 0 &&
+               Strcmp("input", attrp[ai].u.t) != 0 &&
+               Strcmp("output", attrp[ai].u.t) != 0) {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GTgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    RECT r;
+    int rtn, ai, i, j;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GetWindowRect(widget->w, &r);
+           attrp[ai].u.s.x = r.right - r.left;
+           attrp[ai].u.s.y = r.bottom - r.top;
+           break;
+       case G_ATTRBORDERWIDTH:
+           Gerr(POS, G_ERRCANNOTGETATTR, "borderwidth");
+           return -1;
+       case G_ATTRTEXT:
+           if ((rtn = GetWindowTextLength(widget->w)) + 1 > Gbufn) {
+               Gbufp = Marraygrow(Gbufp, (long) (rtn + 1) * BUFSIZE);
+               Gbufn = rtn + 1;
+           }
+           GetWindowText(widget->w, &Gbufp[0], Gbufn - 1);
+           for (i = 0, j = 0; Gbufp[i]; i++)
+               if (Gbufp[i] != '\r')
+                   Gbufp[j++] = Gbufp[i];
+           Gbufp[j++] = 0;
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRAPPENDTEXT:
+           Gerr(POS, G_ERRCANNOTGETATTR, "appendtext");
+           return -1;
+       case G_ATTRMODE:
+           attrp[ai].u.t = "oneline";
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", widget->w);
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRNEWLINECB:
+           attrp[ai].u.func = WTU->func;
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GTdestroywidget(Gwidget_t * widget)
+{
+    Gwidget_t *parent;
+
+    parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
+    if (parent && parent->type == G_ARRAYWIDGET)
+       Gawdeletechild(parent, widget);
+    DestroyWindow(widget->w);
+    return 0;
+}
diff --git a/cmd/lefty/ws/mswin32/gview.c b/cmd/lefty/ws/mswin32/gview.c
new file mode 100644 (file)
index 0000000..7050805
--- /dev/null
@@ -0,0 +1,180 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WVU widget->u.v
+
+int GVcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXpoint_t po;
+    PIXsize_t ps;
+    DWORD wflags;
+    char *s;
+    int ai;
+
+    WVU->func = NULL;
+    WVU->closing = FALSE;
+    wflags = WS_OVERLAPPEDWINDOW;
+    s = "LEFTY";
+    po.x = po.y = CW_USEDEFAULT;
+    ps.x = ps.y = MINVWSIZE;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           GETORIGIN(attrp[ai].u.p, po);
+           break;
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINVWSIZE);
+           break;
+       case G_ATTRNAME:
+           s = attrp[ai].u.t;
+           break;
+       case G_ATTRZORDER:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "zorder");
+           return -1;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WVU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    if (!(widget->w = CreateWindow("LeftyClass", s, wflags, po.x, po.y,
+                                  ps.x, ps.y, (HWND) 0, (HMENU) 0,
+                                  hinstance, NULL))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    ShowWindow(widget->w, SW_SHOW);
+    UpdateWindow(widget->w);
+    return 0;
+}
+
+int GVsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PIXpoint_t po;
+    PIXsize_t ps;
+    DWORD wflags1, wflags2, wflags3, wflags4;
+    int ai;
+
+    wflags1 = SWP_NOMOVE | SWP_NOZORDER;
+    wflags2 = SWP_NOSIZE | SWP_NOZORDER;
+    wflags3 = SWP_NOSIZE | SWP_NOMOVE;
+    wflags4 = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           GETORIGIN(attrp[ai].u.p, po);
+           SetWindowPos(widget->w, (HWND) NULL, po.x, po.y, 0, 0,
+                        wflags2);
+           break;
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINVWSIZE);
+           SetWindowPos(widget->w, (HWND) NULL, 0, 0, ps.x, ps.y,
+                        wflags1);
+           break;
+       case G_ATTRNAME:
+           SetWindowText(widget->w, attrp[ai].u.t);
+           return -1;
+       case G_ATTRZORDER:
+           if (Strcmp(attrp[ai].u.t, "top") == 0)
+               SetWindowPos(widget->w, (HWND) HWND_TOP, 0, 0, 0, 0,
+                            wflags3);
+           else if (Strcmp(attrp[ai].u.t, "bottom") == 0)
+               SetWindowPos(widget->w, (HWND) HWND_BOTTOM,
+                            0, 0, 0, 0, wflags4);
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WVU->func = attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GVgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    RECT r;
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           GetWindowRect(widget->w, &r);
+           attrp[ai].u.p.x = r.left, attrp[ai].u.p.y = r.top;
+           break;
+       case G_ATTRSIZE:
+           GetWindowRect(widget->w, &r);
+           attrp[ai].u.s.x = r.right - r.left;
+           attrp[ai].u.s.y = r.bottom - r.top;
+           break;
+       case G_ATTRNAME:
+           Gerr(POS, G_ERRNOTIMPLEMENTED);
+           return -1;
+       case G_ATTRZORDER:
+           Gerr(POS, G_ERRNOTIMPLEMENTED);
+           return -1;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", widget->w);
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTREVENTCB:
+           attrp[ai].u.func = WVU->func;
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GVdestroywidget(Gwidget_t * widget)
+{
+    WVU->closing = TRUE;
+    DestroyWindow(widget->w);
+    return 0;
+}
diff --git a/cmd/lefty/ws/mswin32/lefty.rc b/cmd/lefty/ws/mswin32/lefty.rc
new file mode 100644 (file)
index 0000000..7de1ae6
--- /dev/null
@@ -0,0 +1,94 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+CHOICEDIALOG DIALOG MOVEABLE IMPURE  0, 0, 186, 92
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog"
+FONT 8, "MS Sans Serif"
+BEGIN
+    LISTBOX         IDC_LIST1,40,21,105,71,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | 
+                    WS_TABSTOP
+    CTEXT           "Static",IDC_STATIC1,8,3,171,12
+END
+
+STRINGDIALOG DIALOG DISCARDABLE  0, 0, 186, 75
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,114,56,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,20,57,50,14
+    EDITTEXT        IDC_EDIT1,19,31,147,18,ES_AUTOHSCROLL
+    CTEXT           "Static",IDC_STATIC1,15,5,155,19
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+    "#include ""windows.h""\r\n"
+    "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/cmd/lefty/ws/mswin32/resource.h b/cmd/lefty/ws/mswin32/resource.h
new file mode 100644 (file)
index 0000000..3c99554
--- /dev/null
@@ -0,0 +1,44 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by lefty.rc
+//
+#define IDD_DIALOG1                     101
+#define IDC_LIST1                       1000
+#define IDC_EDIT1                       1002
+#define IDC_STATIC1                     1005
+#define IDC_STATIC                      -1
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        104
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1006
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/ws/none/garray.c b/cmd/lefty/ws/none/garray.c
new file mode 100644 (file)
index 0000000..847938e
--- /dev/null
@@ -0,0 +1,62 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+int GAcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+}
+
+int GAsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GAgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GAdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
+
+int Gaworder(Gwidget_t * widget, void *data, Gawordercb func)
+{
+    return 0;
+}
+
+int Gawsetmode(Gwidget_t * widget, int mode)
+{
+    return 0;
+}
+
+int Gawgetmode(Gwidget_t * widget)
+{
+    return 0;
+}
+
+void Gawdefcoordscb(int wi, Gawdata_t * dp)
+{
+}
diff --git a/cmd/lefty/ws/none/gbutton.c b/cmd/lefty/ws/none/gbutton.c
new file mode 100644 (file)
index 0000000..4940001
--- /dev/null
@@ -0,0 +1,43 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+int GBcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+    return 0;
+}
+
+int GBsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GBgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GBdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
diff --git a/cmd/lefty/ws/none/gcanvas.c b/cmd/lefty/ws/none/gcanvas.c
new file mode 100644 (file)
index 0000000..5b75654
--- /dev/null
@@ -0,0 +1,137 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+int GCcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+}
+
+int GCsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GCgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GCdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
+
+int GCcanvasclear(Gwidget_t * widget)
+{
+    return 0;
+}
+
+int GCsetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCgetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCarrow(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCline(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCbox(Gwidget_t * widget, Grect_t gr, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCpolygon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCsplinegon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCarc(Gwidget_t * widget, Gpoint_t gc, Gsize_t gs, double ang1,
+         double ang2, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCtext(Gwidget_t * widget, Gtextline_t * tlp, int n, Gpoint_t go,
+          char *fn, double fs, char *justs, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCgettextsize(Gwidget_t * widget, Gtextline_t * tlp, int n, char *fn,
+                 double fs, Gsize_t * gsp)
+{
+    return -1;
+}
+
+int GCcreatebitmap(Gwidget_t * widget, Gbitmap_t * bitmap, Gsize_t s)
+{
+    return 0;
+}
+
+int GCdestroybitmap(Gbitmap_t * bitmap)
+{
+    return 0;
+}
+
+#define COMPDIFF(a, b) (((a) > (b)) ? (a) - (b) : (b) - (a))
+#define CDIFF(a, b) (COMPDIFF (a.red, b[0]) + COMPDIFF (a.green, b[1]) + \
+        COMPDIFF (a.blue, b[2]))
+#define CMINMAXDIFF 20000
+
+int GCreadbitmap(Gwidget_t * widget, Gbitmap_t * bitmap, FILE * fp)
+{
+    return 0;
+}
+
+int GCwritebitmap(Gbitmap_t * bitmap, FILE * fp)
+{
+    return 0;
+}
+
+int GCbitblt(Gwidget_t * widget, Gpoint_t gp, Grect_t gr,
+            Gbitmap_t * bitmap, char *mode, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GCgetmousecoords(Gwidget_t * widget, Gpoint_t * gpp, int *count)
+{
+    return 0;
+}
diff --git a/cmd/lefty/ws/none/gcommon.c b/cmd/lefty/ws/none/gcommon.c
new file mode 100644 (file)
index 0000000..d0b6d31
--- /dev/null
@@ -0,0 +1,54 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+int Gxfd;
+Widget Groot;
+Display *Gdisplay;
+int Gpopdownflag;
+int Gscreenn;
+int Gdepth;
+
+int Ginitgraphics(void)
+{
+    return 0;
+}
+
+int Gtermgraphics(void)
+{
+    return 0;
+}
+
+int Gsync(void)
+{
+    return 0;
+}
+
+int Gresetbstate(int wi)
+{
+    return 0;
+}
+
+int Gprocessevents(int waitflag, Geventmode_t mode)
+{
+    return 0;
+}
diff --git a/cmd/lefty/ws/none/gcommon.h b/cmd/lefty/ws/none/gcommon.h
new file mode 100644 (file)
index 0000000..584ad22
--- /dev/null
@@ -0,0 +1,138 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _GCOMMON_H
+#define _GCOMMON_H
+
+    typedef struct {
+       int x, y;
+    } XPoint;
+    typedef int Widget;
+    typedef int Display;
+
+    extern Widget Groot;
+    extern Display *Gdisplay;
+    extern int Gpopdownflag;
+    extern int Gscreenn;
+    extern int Gdepth;
+
+    int Ginitgraphics(void);
+    int Gtermgraphics(void);
+    int Gsync(void);
+
+    int GAcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GAsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GAgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GAdestroywidget(Gwidget_t *);
+
+    int GBcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GBsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GBgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GBdestroywidget(Gwidget_t *);
+
+    int GCcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GCsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GCgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GCdestroywidget(Gwidget_t *);
+    int GCcanvasclear(Gwidget_t *);
+    int GCsetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GCgetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GCarrow(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GCline(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GCbox(Gwidget_t *, Grect_t, Ggattr_t *);
+    int GCpolygon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GCsplinegon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GCarc(Gwidget_t *, Gpoint_t, Gsize_t, double, double, Ggattr_t *);
+    int GCtext(Gwidget_t *, Gtextline_t *, int, Gpoint_t,
+              char *, double, char *, Ggattr_t *);
+    int GCgettextsize(Gwidget_t *, Gtextline_t *, int, char *, double,
+                     Gsize_t *);
+    int GCcreatebitmap(Gwidget_t *, Gbitmap_t *, Gsize_t);
+    int GCdestroybitmap(Gbitmap_t *);
+    int GCreadbitmap(Gwidget_t *, Gbitmap_t *, FILE *);
+    int GCwritebitmap(Gbitmap_t *, FILE *);
+    int GCbitblt(Gwidget_t *, Gpoint_t, Grect_t, Gbitmap_t *, char *,
+                Ggattr_t *);
+    int GCgetmousecoords(Gwidget_t *, Gpoint_t *, int *);
+
+    int GLcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GLsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GLgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GLdestroywidget(Gwidget_t *);
+
+    int GMcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GMsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GMgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GMdestroywidget(Gwidget_t *);
+    int GMmenuaddentries(Gwidget_t *, int, char **);
+    int GMmenudisplay(Gwidget_t *, Gwidget_t *);
+
+    int GPcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GPsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GPgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GPdestroywidget(Gwidget_t *);
+    int GPcanvasclear(Gwidget_t *);
+    int GPsetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GPgetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GParrow(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GPline(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GPbox(Gwidget_t *, Grect_t, Ggattr_t *);
+    int GPpolygon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GPsplinegon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GParc(Gwidget_t *, Gpoint_t, Gsize_t, double, double, Ggattr_t *);
+    int GPtext(Gwidget_t *, Gtextline_t *, int, Gpoint_t,
+              char *, double, char *, Ggattr_t *);
+    int GPcreatebitmap(Gwidget_t *, Gbitmap_t *, Gsize_t);
+    int GPdestroybitmap(Gbitmap_t *);
+    int GPreadbitmap(Gwidget_t *, Gbitmap_t *, FILE *);
+    int GPwritebitmap(Gbitmap_t *, FILE *);
+    int GPbitblt(Gwidget_t *, Gpoint_t, Grect_t, Gbitmap_t *, char *,
+                Ggattr_t *);
+
+    int GQcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GQsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GQgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GQdestroywidget(Gwidget_t *);
+    int GQqueryask(Gwidget_t *, char *, char *, char *, int);
+
+    int GScreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GSsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GSgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GSdestroywidget(Gwidget_t *);
+
+    int GTcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GTsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GTgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GTdestroywidget(Gwidget_t *);
+
+    int GVcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GVsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GVgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GVdestroywidget(Gwidget_t *);
+
+    void Gawdefcoordscb(int, Gawdata_t *);
+
+#endif                         /* _GCOMMON_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/ws/none/glabel.c b/cmd/lefty/ws/none/glabel.c
new file mode 100644 (file)
index 0000000..c683240
--- /dev/null
@@ -0,0 +1,42 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+int GLcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+}
+
+int GLsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GLgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GLdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
diff --git a/cmd/lefty/ws/none/gmenu.c b/cmd/lefty/ws/none/gmenu.c
new file mode 100644 (file)
index 0000000..fdc5157
--- /dev/null
@@ -0,0 +1,52 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+int GMcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+}
+
+int GMsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GMgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GMdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
+
+int GMmenuaddentries(Gwidget_t * widget, int en, char **ep)
+{
+    return 0;
+}
+
+int GMmenudisplay(Gwidget_t * parent, Gwidget_t * widget)
+{
+    return -1;
+}
diff --git a/cmd/lefty/ws/none/gpcanvas.c b/cmd/lefty/ws/none/gpcanvas.c
new file mode 100644 (file)
index 0000000..52ea580
--- /dev/null
@@ -0,0 +1,128 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+char *Gpscanvasname = "out.ps";
+
+int GPcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+}
+
+int GPsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GPgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GPdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
+
+int GPcanvasclear(Gwidget_t * widget)
+{
+    return 0;
+}
+
+int GPsetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GPgetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GParrow(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GPline(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GPbox(Gwidget_t * widget, Grect_t gr, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GPpolygon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GPsplinegon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GParc(Gwidget_t * widget, Gpoint_t gc, Gsize_t gs,
+         double ang1, double ang2, Ggattr_t * ap)
+{
+    return 0;
+}
+
+int GPtext(Gwidget_t * widget, Gtextline_t * tlp, int n, Gpoint_t go,
+          char *fn, double fs, char *justs, Ggattr_t * ap)
+{
+    return 0;
+}
+
+static char *findfont(char *name)
+{
+    return NULL;
+}
+
+int GPcreatebitmap(Gwidget_t * widget, Gbitmap_t * bitmap, Gsize_t s)
+{
+    return 0;
+}
+
+int GPdestroybitmap(Gbitmap_t * bitmap)
+{
+    return 0;
+}
+
+int GPreadbitmap(Gwidget_t * widget, Gbitmap_t * bitmap, FILE * fp)
+{
+    return 0;
+}
+
+int GPwritebitmap(Gbitmap_t * bitmap, FILE * fp)
+{
+    return -1;
+}
+
+int GPbitblt(Gwidget_t * widget, Gpoint_t gp, Grect_t gr,
+            Gbitmap_t * bitmap, char *mode, Ggattr_t * ap)
+{
+    return 0;
+}
diff --git a/cmd/lefty/ws/none/gquery.c b/cmd/lefty/ws/none/gquery.c
new file mode 100644 (file)
index 0000000..2da5229
--- /dev/null
@@ -0,0 +1,48 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+int GQcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+}
+
+int GQsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GQgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GQdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
+
+int GQqueryask(Gwidget_t * widget, char *prompt, char *args,
+              char *responsep, int responsen)
+{
+    return 0;
+}
diff --git a/cmd/lefty/ws/none/gscroll.c b/cmd/lefty/ws/none/gscroll.c
new file mode 100644 (file)
index 0000000..c49cbb2
--- /dev/null
@@ -0,0 +1,42 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+int GScreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+}
+
+int GSsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GSgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GSdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
diff --git a/cmd/lefty/ws/none/gtext.c b/cmd/lefty/ws/none/gtext.c
new file mode 100644 (file)
index 0000000..b8aa4be
--- /dev/null
@@ -0,0 +1,43 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+int GTcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+}
+
+int GTsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GTgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GTdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
diff --git a/cmd/lefty/ws/none/gview.c b/cmd/lefty/ws/none/gview.c
new file mode 100644 (file)
index 0000000..3335875
--- /dev/null
@@ -0,0 +1,42 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+int GVcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    return -1;
+}
+
+int GVsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GVgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    return 0;
+}
+
+int GVdestroywidget(Gwidget_t * widget)
+{
+    return 0;
+}
diff --git a/cmd/lefty/ws/x11/.cvsignore b/cmd/lefty/ws/x11/.cvsignore
new file mode 100644 (file)
index 0000000..9fb9857
--- /dev/null
@@ -0,0 +1,6 @@
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
diff --git a/cmd/lefty/ws/x11/Makefile.am b/cmd/lefty/ws/x11/Makefile.am
new file mode 100644 (file)
index 0000000..2418ae9
--- /dev/null
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = libfilereq
+
+AM_CPPFLAGS = -I$(top_srcdir)/lefty @XAW_INCLUDES@
+
+AM_CFLAGS =  @X_CFLAGS@
+
+noinst_HEADERS = gcommon.h
+if WITH_X
+noinst_LTLIBRARIES = libws.la
+endif
+
+libws_la_SOURCES = garray.c gbutton.c gcanvas.c gcommon.c glabel.c \
+       gmenu.c gpcanvas.c gquery.c gscroll.c gtext.c gview.c
+
+EXTRA_DIST = $(libws_la_SOURCES)
diff --git a/cmd/lefty/ws/x11/garray.c b/cmd/lefty/ws/x11/garray.c
new file mode 100644 (file)
index 0000000..4291c30
--- /dev/null
@@ -0,0 +1,562 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WAU widget->u.a
+
+extern WidgetClass arrayWidgetClass;
+
+static void awcallback(Widget, XtPointer, XtPointer);
+
+int GAcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    int ai;
+    XColor c;
+    int color;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    WAU->func = NULL;
+    ps.x = ps.y = MINAWSIZE;
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINAWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRMODE:
+           if (Strcmp("horizontal", attrp[ai].u.t) == 0) {
+               ADD2ARGS(XtNorientation, XtorientHorizontal);
+               WAU->mode = G_AWHARRAY;
+           } else if (Strcmp("vertical", attrp[ai].u.t) == 0) {
+               WAU->mode = G_AWVARRAY;
+           } else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRLAYOUT:
+           if (Strcmp("on", attrp[ai].u.t) == 0)
+               Gawsetmode(widget, FALSE);
+           else if (Strcmp("off", attrp[ai].u.t) == 0)
+               Gawsetmode(widget, TRUE);
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRRESIZECB:
+           WAU->func = (Gawcoordscb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    ADD2ARGS(XtNwidth, ps.x);
+    ADD2ARGS(XtNheight, ps.y);
+    if (!(widget->w = XtCreateWidget("array", arrayWidgetClass,
+                                    parent->w, argp, argn))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    XtAddCallback(widget->w, XtNcallback, awcallback, (XtPointer) NULL);
+    Glazymanage(widget->w);
+    return 0;
+}
+
+int GAsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    int ai;
+
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINAWSIZE);
+           ADD2ARGS(XtNwidth, ps.x);
+           ADD2ARGS(XtNheight, ps.y);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRMODE:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "mode");
+           return -1;
+       case G_ATTRLAYOUT:
+           if (Strcmp("on", attrp[ai].u.t) == 0)
+               Gawsetmode(widget, FALSE);
+           else if (Strcmp("off", attrp[ai].u.t) == 0)
+               Gawsetmode(widget, TRUE);
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRRESIZECB:
+           WAU->func = (Gawcoordscb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    XtSetValues(widget->w, argp, argn);
+    return 0;
+}
+
+int GAgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Dimension width, height;
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       RESETARGS;
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           ADD2ARGS(XtNwidth, &width);
+           ADD2ARGS(XtNheight, &height);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.s.x = width, attrp[ai].u.s.y = height;
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, &width);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.i = width;
+           break;
+       case G_ATTRMODE:
+           attrp[ai].u.t = (WAU->mode == G_AWHARRAY) ?
+               "horizontal" : "vertical";
+           break;
+       case G_ATTRLAYOUT:
+           attrp[ai].u.t = (Gawgetmode(widget)) ? "off" : "on";
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", XtWindow(widget->w));
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRRESIZECB:
+           attrp[ai].u.func = (void *) (WAU->func);
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GAdestroywidget(Gwidget_t * widget)
+{
+    XtDestroyWidget(widget->w);
+    return 0;
+}
+
+static void awcallback(Widget w, XtPointer clientdata, XtPointer calldata)
+{
+    Gwidget_t *widget;
+
+    if (!(widget = findwidget((unsigned long) w, G_ARRAYWIDGET)))
+       return;
+    if (WAU->func)
+       (*WAU->func) (widget - &Gwidgets[0], (Gawdata_t *) calldata);
+    else
+       Gawdefcoordscb(widget - &Gwidgets[0], (Gawdata_t *) calldata);
+}
+
+/* the rest of this file contains the implementation of the array widget */
+
+#include <X11/IntrinsicP.h>
+
+typedef struct _ArrayClassRec *ArrayWidgetClass;
+typedef struct _ArrayRec *ArrayWidget;
+
+typedef struct _ArrayClassPart {
+    int dummy;                 /* not used */
+} ArrayClassPart;
+
+typedef struct _ArrayClassRec {
+    CoreClassPart core_class;
+    CompositeClassPart composite_class;
+    ArrayClassPart array_class;
+} ArrayClassRec;
+
+typedef struct _ArrayPart {
+    XtCallbackList callbacks;
+    XtOrientation orientation;
+    Gawdata_t data;
+    int batchmode;
+} ArrayPart;
+
+typedef struct _ArrayRec {
+    CorePart core;
+    CompositePart composite;
+    ArrayPart array;
+} ArrayRec;
+
+#define CHILDINCR 10
+#define CHILDSIZE sizeof (Gawcarray_t)
+
+static XtResource resources[] = {
+    {
+     XtNcallback,
+     XtCCallback,
+     XtRCallback,
+     sizeof(XtPointer),
+     XtOffsetOf(ArrayRec, array.callbacks),
+     XtRCallback, (XtPointer) NULL}
+    ,
+    {
+     XtNorientation,
+     XtCOrientation,
+     XtROrientation,
+     sizeof(XtOrientation),
+     XtOffsetOf(ArrayRec, array.orientation),
+     XtRImmediate, (XtPointer) XtorientVertical}
+};
+
+static void ClassInitialize(void);
+static void Initialize(Widget, Widget, ArgList, Cardinal *);
+static void Destroy(Widget);
+static void Resize(Widget);
+static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal *);
+static XtGeometryResult GeometryManager(Widget,
+                                       XtWidgetGeometry *,
+                                       XtWidgetGeometry *);
+static void ChangeManaged(Widget);
+static void InsertChild(Widget);
+static void DeleteChild(Widget);
+static void dolayout(ArrayWidget, int);
+
+ArrayClassRec arrayClassRec = {
+    {                          /* core_class fields */
+     /* superclass          */ (WidgetClass) & compositeClassRec,
+     /* class_name          */ "Array",
+     /* widget_size         */ sizeof(ArrayRec),
+     /* class_initialize    */ ClassInitialize,
+     /* class_part_init     */ NULL,
+     /* class_inited        */ FALSE,
+     /* initialize          */ Initialize,
+     /* initialize_hook     */ NULL,
+     /* realize             */ XtInheritRealize,
+     /* actions             */ NULL,
+     /* num_actions         */ 0,
+     /* resources           */ resources,
+     /* num_resources       */ XtNumber(resources),
+     /* xrm_class           */ NULLQUARK,
+     /* compress_motion     */ TRUE,
+     /* compress_exposure   */ TRUE,
+     /* compress_enterleave */ TRUE,
+     /* visible_interest    */ FALSE,
+     /* destroy             */ Destroy,
+     /* resize              */ Resize,
+     /* expose              */ XtInheritExpose,
+     /* set_values          */ SetValues,
+     /* set_values_hook     */ NULL,
+     /* set_values_almost   */ XtInheritSetValuesAlmost,
+     /* get_values_hook     */ NULL,
+     /* accept_focus        */ NULL,
+     /* version             */ XtVersion,
+     /* callback_private    */ NULL,
+     /* tm_table            */ NULL,
+     /* query_geometry      */ XtInheritQueryGeometry,
+     /* display_accelerator */ XtInheritDisplayAccelerator,
+     /* extension           */ NULL
+     }
+    ,
+    {                          /* composite_class fields */
+     /* geometry_manager */ GeometryManager,
+     /* change_managed   */ ChangeManaged,
+     /* insert_child     */ InsertChild,
+     /* delete_child     */ DeleteChild,
+     /* extension        */ NULL
+     }
+    ,
+    {                          /* array_class fields */
+     /* dummy */ 0
+     }
+};
+
+WidgetClass arrayWidgetClass = (WidgetClass) & arrayClassRec;
+
+int Gaworder(Gwidget_t * widget, void *data, Gawordercb func)
+{
+    ArrayWidget aw;
+
+    aw = (ArrayWidget) widget->w;
+    (*func) (data, &aw->array.data);
+    dolayout(aw, TRUE);
+    return 0;
+}
+
+int Gawsetmode(Gwidget_t * widget, int mode)
+{
+    ArrayWidget aw;
+
+    aw = (ArrayWidget) widget->w;
+    aw->array.batchmode = mode;
+    dolayout(aw, TRUE);
+    return 0;
+}
+
+int Gawgetmode(Gwidget_t * widget)
+{
+    ArrayWidget aw;
+
+    aw = (ArrayWidget) widget->w;
+    return aw->array.batchmode;
+}
+
+void Gawdefcoordscb(int wi, Gawdata_t * dp)
+{
+    Gawcarray_t *cp;
+    int sx, sy, csx, csy, ci;
+
+    sx = dp->sx, sy = dp->sy;
+    csx = csy = 0;
+    for (ci = 0; ci < dp->cj; ci++) {
+       cp = &dp->carray[ci];
+       if (!cp->flag)
+           continue;
+       cp->ox = csx, cp->oy = csy;
+       if (dp->type == G_AWVARRAY)
+           cp->sx = sx - 2 * cp->bs, csy += cp->sy + 2 * cp->bs;
+       else
+           cp->sy = sy - 2 * cp->bs, csx += cp->sx + 2 * cp->bs;
+    }
+    if (dp->type == G_AWVARRAY)
+       dp->sy = csy;
+    else
+       dp->sx = csx;
+}
+
+static void ClassInitialize(void)
+{
+    XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
+                  NULL, (Cardinal) 0);
+}
+
+static void Initialize(Widget reqw, Widget neww,
+                      ArgList args, Cardinal * num_args)
+{
+    ArrayWidget aw;
+
+    aw = (ArrayWidget) neww;
+    if (aw->array.orientation == XtorientVertical)
+       aw->array.data.type = G_AWVARRAY;
+    else
+       aw->array.data.type = G_AWHARRAY;
+    aw->array.data.carray = Marrayalloc((long) CHILDINCR * CHILDSIZE);
+    aw->array.data.cn = CHILDINCR;
+    aw->array.data.cj = 0;
+    aw->array.batchmode = FALSE;
+    if (aw->core.width == 0)
+       aw->core.width = 100;
+    if (aw->core.height == 0)
+       aw->core.height = 100;
+}
+
+static void Destroy(Widget w)
+{
+    ArrayWidget aw;
+
+    aw = (ArrayWidget) w;
+    Marrayfree(aw->array.data.carray);
+    aw->array.data.cn = aw->array.data.cj = 0;
+}
+
+static void Resize(Widget w)
+{
+    dolayout((ArrayWidget) w, FALSE);
+}
+
+static Boolean SetValues(Widget curw, Widget reqw, Widget neww,
+                        ArgList args, Cardinal * num_args)
+{
+    ArrayWidget curaw;
+    ArrayWidget newaw;
+
+    curaw = (ArrayWidget) curw;
+    newaw = (ArrayWidget) neww;
+    if (curaw->array.orientation != newaw->array.orientation) {
+       if (newaw->array.orientation == XtorientVertical)
+           newaw->array.data.type = G_AWVARRAY;
+       else
+           newaw->array.data.type = G_AWHARRAY;
+       dolayout(newaw, TRUE);
+       return TRUE;
+    }
+    return FALSE;
+}
+
+static XtGeometryResult GeometryManager(Widget w,
+                                       XtWidgetGeometry * req,
+                                       XtWidgetGeometry * rep)
+{
+    Dimension width, height;
+
+    if (req->request_mode & ~(CWX | CWY | CWWidth | CWHeight))
+       return XtGeometryNo;
+
+    if (req->request_mode & (CWX | CWY | CWWidth | CWHeight)) {
+       width = (req->request_mode & CWWidth) ? req->width : w->core.width;
+       height =
+           (req->request_mode & CWHeight) ? req->height : w->core.height;
+       w->core.width = width, w->core.height = height;
+       dolayout((ArrayWidget) XtParent(w), TRUE);
+       return XtGeometryYes;
+    }
+    return XtGeometryYes;
+}
+
+static void ChangeManaged(Widget w)
+{
+    ArrayWidget aw;
+
+    aw = (ArrayWidget) w;
+    if (!aw->array.batchmode)
+       dolayout(aw, TRUE);
+}
+
+static void InsertChild(Widget w)
+{
+    ArrayWidget aw;
+    CompositeWidgetClass sclass;
+
+    sclass = (CompositeWidgetClass) compositeWidgetClass;
+    (*sclass->composite_class.insert_child) (w);
+    aw = (ArrayWidget) XtParent(w);
+    if (aw->array.data.cj == aw->array.data.cn) {
+       aw->array.data.carray = Marraygrow(aw->array.data.carray,
+                                          (long) (aw->array.data.cn +
+                                                  CHILDINCR) * CHILDSIZE);
+       aw->array.data.cn += CHILDINCR;
+    }
+    aw->array.data.carray[aw->array.data.cj++].w = w;
+}
+
+static void DeleteChild(Widget w)
+{
+    ArrayWidget aw;
+    CompositeWidgetClass sclass;
+    int ci;
+
+    sclass = (CompositeWidgetClass) compositeWidgetClass;
+    (*sclass->composite_class.delete_child) (w);
+    aw = (ArrayWidget) XtParent(w);
+    for (ci = 0; ci < aw->array.data.cj; ci++)
+       if (aw->array.data.carray[ci].w == w)
+           break;
+    if (ci < aw->array.data.cj) {
+       for (; ci + 1 < aw->array.data.cj; ci++)
+           aw->array.data.carray[ci].w = aw->array.data.carray[ci + 1].w;
+       aw->array.data.cj--;
+    }
+}
+
+static void dolayout(ArrayWidget aw, int flag)
+{
+    XtWidgetGeometry req, ret_req;
+    Gawdata_t *dp;
+    Gawcarray_t *cp;
+    int sx, sy, ci;
+
+    if (aw->array.batchmode)
+       return;
+    dp = &aw->array.data;
+    for (ci = 0; ci < dp->cj; ci++) {
+       if (!XtIsManaged(dp->carray[ci].w)) {
+           dp->carray[ci].flag = 0;
+           continue;
+       }
+       cp = &dp->carray[ci];
+       cp->flag = 1;
+       cp->ox = cp->w->core.x;
+       cp->oy = cp->w->core.y;
+       cp->sx = cp->w->core.width;
+       cp->sy = cp->w->core.height;
+       cp->bs = cp->w->core.border_width;
+    }
+    dp->sx = aw->core.width, dp->sy = aw->core.height;
+    XtCallCallbackList((Widget) aw, aw->array.callbacks, dp);
+    if ((sx = dp->sx) < MINAWSIZE)
+       sx = MINAWSIZE;
+    if ((sy = dp->sy) < MINAWSIZE)
+       sy = MINAWSIZE;
+    if (flag && (aw->core.width != sx || aw->core.height != sy)) {
+       req.width = sx, req.height = sy;
+       req.request_mode = CWWidth | CWHeight;
+       if (XtMakeGeometryRequest((Widget) aw, &req, &ret_req) ==
+           XtGeometryAlmost) {
+           req = ret_req;
+           XtMakeGeometryRequest((Widget) aw, &req, &ret_req);
+           dp->sx = req.width, dp->sy = req.height;
+           XtCallCallbackList((Widget) aw, aw->array.callbacks, dp);
+       }
+    }
+    for (ci = 0; ci < dp->cj; ci++) {
+       cp = &dp->carray[ci];
+       if (!cp->flag)
+           continue;
+       XtConfigureWidget(cp->w, cp->ox, cp->oy, cp->sx, cp->sy, cp->bs);
+    }
+}
diff --git a/cmd/lefty/ws/x11/gbutton.c b/cmd/lefty/ws/x11/gbutton.c
new file mode 100644 (file)
index 0000000..bf66b6d
--- /dev/null
@@ -0,0 +1,223 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+#define WBU widget->u.b
+
+static void bwcallback(Widget, XtPointer, XtPointer);
+
+int GBcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    char *s;
+    int ai;
+    XColor c;
+    int color;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    WBU->func = NULL;
+    ps.x = ps.y = MINBWSIZE;
+    s = NULL;
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINBWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRTEXT:
+           s = attrp[ai].u.t;
+           ADD2ARGS(XtNlabel, s);
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRBUTTONCB:
+           WBU->func = (Gbuttoncb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    if (!s || s[0] == '\000') {
+       ADD2ARGS(XtNwidth, ps.x);
+       ADD2ARGS(XtNheight, ps.y);
+    } else {
+       if (ps.x > MINBWSIZE)
+           ADD2ARGS(XtNwidth, ps.x);
+       if (ps.y > MINBWSIZE)
+           ADD2ARGS(XtNheight, ps.y);
+    }
+    ADD2ARGS(XtNhighlightThickness, 0);
+    ADD2ARGS(XtNinternalHeight, 0);
+    ADD2ARGS(XtNinternalWidth, 0);
+    ADD2ARGS(XtNjustify, XtJustifyLeft);
+    if (!(widget->w = XtCreateWidget("command", commandWidgetClass,
+                                    parent->w, argp, argn))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    XtAddCallback(widget->w, XtNcallback, bwcallback,
+                 (XtPointer) widget->udata);
+    Glazymanage(widget->w);
+    return 0;
+}
+
+int GBsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    int ai;
+    XColor c;
+    int color;
+
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINBWSIZE);
+           ADD2ARGS(XtNwidth, ps.x);
+           ADD2ARGS(XtNheight, ps.y);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRTEXT:
+           ADD2ARGS(XtNlabel, attrp[ai].u.t);
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRBUTTONCB:
+           WBU->func = (Gbuttoncb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    XtSetValues(widget->w, argp, argn);
+    return 0;
+}
+
+int GBgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Dimension width, height;
+    char *s;
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       RESETARGS;
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           ADD2ARGS(XtNwidth, &width);
+           ADD2ARGS(XtNheight, &height);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.s.x = width, attrp[ai].u.s.y = height;
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, &width);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.i = width;
+           break;
+       case G_ATTRTEXT:
+           ADD2ARGS(XtNlabel, &s);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.t = s;
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", XtWindow(widget->w));
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRBUTTONCB:
+           attrp[ai].u.func = (void *) (WBU->func);
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GBdestroywidget(Gwidget_t * widget)
+{
+    XtDestroyWidget(widget->w);
+    return 0;
+}
+
+static void bwcallback(Widget w, XtPointer clientdata, XtPointer calldata)
+{
+    Gwidget_t *widget;
+
+    widget = findwidget((unsigned long) w, G_BUTTONWIDGET);
+    if (WBU->func)
+       (*WBU->func) (widget - &Gwidgets[0], clientdata);
+}
diff --git a/cmd/lefty/ws/x11/gcanvas.c b/cmd/lefty/ws/x11/gcanvas.c
new file mode 100644 (file)
index 0000000..72c8766
--- /dev/null
@@ -0,0 +1,1664 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+#ifdef FEATURE_GMAP
+#include <gmap.h>
+#endif
+
+#define WCU widget->u.c
+#define WINDOW widget->u.c->window
+#define GC widget->u.c->gc
+#define ISVISIBLE(r) ( \
+    (r.o.x <= WCU->clip.c.x) && (r.c.x >= WCU->clip.o.x) && \
+    (r.o.y <= WCU->clip.c.y) && (r.c.y >= WCU->clip.o.y) \
+)
+
+#define IS8BIT(font) ((font)->min_byte1 == 0 && (font)->max_byte1 == 0)
+
+static struct cursormap_t {
+    Cursor id;
+    char name[40];
+} cursormap[XC_num_glyphs];
+static int curcursori = -1;
+
+#define max(a, b) (((a) >= (b)) ? (a) : (b))
+#define min(a, b) (((a) <= (b)) ? (a) : (b))
+
+static char gstyles[][2] = {
+    /* G_SOLID */ {16, 0,},
+    /* G_DASHED */ {4, 4,},
+    /* G_DOTTED */ {2, 2,},
+    /* G_LONGDASHED */ {4, 12,},
+    /* G_SHORTDASHED */ {12, 4,},
+};
+
+static char grays[][4] = {
+    {0x00, 0x00, 0x00, 0x00,},
+    {0x08, 0x00, 0x00, 0x00,},
+    {0x08, 0x00, 0x02, 0x00,},
+    {0x0A, 0x00, 0x02, 0x00,},
+    {0x0A, 0x00, 0x0A, 0x00,},
+    {0x0A, 0x04, 0x0A, 0x00,},
+    {0x0A, 0x04, 0x0A, 0x01,},
+    {0x0A, 0x05, 0x0A, 0x01,},
+    {0x0A, 0x05, 0x0A, 0x05,},
+    {0x0E, 0x05, 0x0A, 0x05,},
+    {0x0E, 0x05, 0x0B, 0x05,},
+    {0x0F, 0x05, 0x0B, 0x05,},
+    {0x0F, 0x05, 0x0F, 0x05,},
+    {0x0F, 0x0D, 0x0F, 0x05,},
+    {0x0F, 0x0D, 0x0F, 0x07,},
+    {0x0F, 0x0F, 0x0F, 0x07,},
+    {0x0F, 0x0F, 0x0F, 0x0F,},
+};
+
+static void bezier(PIXpoint_t, PIXpoint_t, PIXpoint_t, PIXpoint_t);
+static XFontStruct *findfont(char *, int);
+static int scalebitmap(Gwidget_t *, Gbitmap_t *, Gsize_t, int, int);
+static void setgattr(Gwidget_t *, Ggattr_t *);
+
+static PIXrect_t rdrawtopix(Gwidget_t *, Grect_t);
+static PIXpoint_t pdrawtopix(Gwidget_t *, Gpoint_t);
+static PIXsize_t sdrawtopix(Gwidget_t *, Gsize_t);
+static Gpoint_t Gppixtodraw(Gwidget_t *, PIXpoint_t);
+static Gsize_t spixtodraw(Gwidget_t *, PIXsize_t);
+static Grect_t rpixtodraw(Gwidget_t *, PIXrect_t);
+static PIXrect_t rdrawtobpix(Gbitmap_t *, Grect_t);
+static PIXpoint_t pdrawtobpix(Gbitmap_t *, Gpoint_t);
+static void adjustclip(Gwidget_t *);
+
+static Bool cwvpredicate(Display *, XEvent *, XPointer);
+static void cweventhandler(Widget, XtPointer, XEvent *, Boolean *);
+static Bool cwepredicate(Display *, XEvent *, XPointer);
+
+int GCcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    Dimension width, height;
+#ifdef FEATURE_BACKINGSTORE
+    XSetWindowAttributes xswa;
+#endif
+    XEvent ev;
+    XColor *cp;
+    XGCValues gcv;
+    int curi, color, ai, r, g, b, i;
+#ifdef FEATURE_GMAP
+    XVisualInfo *vip;
+    int gmapmode = FALSE;
+#endif
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    WCU->func = NULL;
+    WCU->needredraw = FALSE;
+    WCU->buttonsdown = 0;
+    WCU->bstate[0] = WCU->bstate[1] = WCU->bstate[2] = 0;
+    ps.x = ps.y = MINCWSIZE;
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINCWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+#ifdef FEATURE_GMAP
+       case G_ATTRMODE:
+           if (Strcmp("gmap", attrp[ai].u.t) == 0) {
+               gmapmode = TRUE;
+           } else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+#endif
+       case G_ATTRCURSOR:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRCOLOR:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRVIEWPORT:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRWINDOW:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WCU->func = (Gcanvascb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    ADD2ARGS(XtNwidth, ps.x);
+    ADD2ARGS(XtNheight, ps.y);
+#ifdef FEATURE_GMAP
+    if (gmapmode) {
+       vip = pfChooseFBConfig(Gdisplay, -1, NULL);
+       ADD2ARGS(GLwNvisualInfo, vip);
+       if (!
+           (widget->w =
+            XtCreateWidget("graphics", glwDrawingAreaWidgetClass,
+                           parent->w, argp, argn))) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+    } else {
+       if (!(widget->w = XtCreateWidget("graphics", coreWidgetClass,
+                                        parent->w, argp, argn))) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+    }
+    WCU->gmapmode = gmapmode;
+#else
+    if (!(widget->w = XtCreateWidget("graphics", coreWidgetClass,
+                                    parent->w, argp, argn))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+#endif
+    XtOverrideTranslations(widget->w, Gcwanytable);
+    XtAddEventHandler(widget->w, VisibilityChangeMask | ExposureMask,
+                     FALSE, cweventhandler, (XtPointer) 0);
+    Glazymanage(widget->w);
+    Gflushlazyq();
+#ifdef FEATURE_BACKINGSTORE
+    xswa.backing_store = WhenMapped;
+    XChangeWindowAttributes(Gdisplay, XtWindow(widget->w),
+                           CWBackingStore, &xswa);
+#endif
+    /* wait for window to become visible */
+    XPeekIfEvent(Gdisplay, &ev, cwvpredicate,
+                (XPointer) XtWindow(widget->w));
+    RESETARGS;
+    ADD2ARGS(XtNwidth, &width);
+    ADD2ARGS(XtNheight, &height);
+    XtGetValues(widget->w, argp, argn);
+    ps.x = width, ps.y = height;
+    WCU->window = XtWindow(widget->w);
+    WCU->cmap = DefaultColormap(Gdisplay, Gscreenn);
+    WCU->gc = XCreateGC(Gdisplay, WCU->window, 0, NULL);
+    RESETARGS;
+    WCU->colors[0].color.pixel = WCU->colors[1].color.pixel = 1000000;
+    ADD2ARGS(XtNbackground, &WCU->colors[0].color.pixel);
+    ADD2ARGS(XtNforeground, &WCU->colors[1].color.pixel);
+    XtGetValues(widget->w, argp, argn);
+    if (WCU->colors[0].color.pixel == 1000000) {
+       if (XGetGCValues(Gdisplay, GC, GCBackground, &gcv) != 0)
+           WCU->colors[0].color.pixel = gcv.background;
+       else
+           WCU->colors[0].color.pixel = WhitePixel(Gdisplay, Gscreenn);
+    }
+    if (WCU->colors[1].color.pixel == 1000000) {
+       if (XGetGCValues(Gdisplay, GC, GCForeground, &gcv) != 0)
+           WCU->colors[1].color.pixel = gcv.foreground;
+       else
+           WCU->colors[1].color.pixel = BlackPixel(Gdisplay, Gscreenn);
+    }
+    XQueryColor(Gdisplay, WCU->cmap, &WCU->colors[0].color);
+    WCU->colors[0].inuse = TRUE;
+    XQueryColor(Gdisplay, WCU->cmap, &WCU->colors[1].color);
+    WCU->colors[1].inuse = TRUE;
+    WCU->allocedcolor[0] = WCU->allocedcolor[1] = FALSE;
+    for (i = 2; i < G_MAXCOLORS; i++)
+       WCU->colors[i].inuse = FALSE;
+    WCU->gattr.color = 1;
+    XSetBackground(Gdisplay, GC, WCU->colors[0].color.pixel);
+    XSetForeground(Gdisplay, GC, WCU->colors[1].color.pixel);
+    WCU->gattr.width = 0;
+    WCU->gattr.mode = G_SRC;
+    WCU->gattr.fill = 0;
+    WCU->gattr.style = 0;
+    WCU->defgattr = WCU->gattr;
+    WCU->font = NULL;
+    WCU->wrect.o.x = 0.0, WCU->wrect.o.y = 0.0;
+    WCU->wrect.c.x = 1.0, WCU->wrect.c.y = 1.0;
+    WCU->vsize.x = ps.x, WCU->vsize.y = ps.y;
+    if (Gdepth == 1) {
+       XSetFillStyle(Gdisplay, GC, FillTiled);
+       for (i = 0; i < 17; i++)
+           WCU->grays[i] =
+               XCreatePixmapFromBitmapData(Gdisplay, WCU->window,
+                                           &grays[i][0], 4, 4,
+                                           BlackPixel(Gdisplay, Gscreenn),
+                                           WhitePixel(Gdisplay, Gscreenn),
+                                           1);
+    }
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRCURSOR:
+           if ((curi = XmuCursorNameToIndex(attrp[ai].u.t)) == -1) {
+               if (Strcmp(attrp[ai].u.t, "default") == 0) {
+                   XUndefineCursor(Gdisplay, XtWindow(widget->w));
+                   curcursori = -1;
+               } else {
+                   Gerr(POS, G_ERRNOSUCHCURSOR, attrp[ai].u.t);
+                   return -1;
+               }
+           } else {
+               if (!cursormap[curi].id) {
+                   cursormap[curi].id = XCreateFontCursor(Gdisplay, curi);
+                   strcpy(cursormap[curi].name, attrp[ai].u.t);
+               }
+               XDefineCursor(Gdisplay, XtWindow(widget->w),
+                             cursormap[curi].id);
+               curcursori = curi;
+           }
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           r = attrp[ai].u.c.r * 257;
+           g = attrp[ai].u.c.g * 257;
+           b = attrp[ai].u.c.b * 257;
+           cp = &WCU->colors[color].color;
+           if (WCU->colors[color].inuse)
+               if (cp->red != r || cp->green != g || cp->blue != b)
+                   if (color > 1 || WCU->allocedcolor[color])
+                       XFreeColors(Gdisplay, WCU->cmap, &cp->pixel, 1, 0);
+           cp->red = r, cp->green = g, cp->blue = b;
+           if (XAllocColor(Gdisplay, WCU->cmap, cp)) {
+               WCU->colors[color].inuse = TRUE;
+               if (color <= 1)
+                   WCU->allocedcolor[color] = TRUE;
+           }
+           /* XAllocColor may change the rgb values */
+           cp->red = r, cp->green = g, cp->blue = b;
+           if (color == WCU->gattr.color)
+               WCU->gattr.color = -1;
+           if (color == 0 || color == 1) {
+               RESETARGS;
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, cp->pixel);
+               else
+                   ADD2ARGS(XtNforeground, cp->pixel);
+               XtSetValues(widget->w, argp, argn);
+           }
+           break;
+       case G_ATTRVIEWPORT:
+           WCU->vsize.x = (int) (attrp[ai].u.s.x + 0.5);
+           WCU->vsize.y = (int) (attrp[ai].u.s.y + 0.5);
+           RESETARGS;
+           ADD2ARGS(XtNwidth, WCU->vsize.x);
+           ADD2ARGS(XtNheight, WCU->vsize.y);
+           XtSetValues(widget->w, argp, argn);
+           break;
+       case G_ATTRWINDOW:
+           WCU->wrect = attrp[ai].u.r;
+           break;
+       }
+    }
+    adjustclip(widget);
+    return 0;
+}
+
+int GCsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    XColor *cp;
+    int curi, color, ai, r, g, b;
+
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINCWSIZE);
+           ADD2ARGS(XtNwidth, ps.x);
+           ADD2ARGS(XtNheight, ps.y);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRCURSOR:
+           if ((curi = XmuCursorNameToIndex(attrp[ai].u.t)) == -1) {
+               if (Strcmp(attrp[ai].u.t, "default") == 0) {
+                   XUndefineCursor(Gdisplay, XtWindow(widget->w));
+                   curcursori = -1;
+               } else {
+                   Gerr(POS, G_ERRNOSUCHCURSOR, attrp[ai].u.t);
+                   return -1;
+               }
+           } else {
+               if (!cursormap[curi].id) {
+                   cursormap[curi].id = XCreateFontCursor(Gdisplay, curi);
+                   strcpy(cursormap[curi].name, attrp[ai].u.t);
+               }
+               XDefineCursor(Gdisplay, XtWindow(widget->w),
+                             cursormap[curi].id);
+               curcursori = curi;
+           }
+           Gsync();
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           r = attrp[ai].u.c.r * 257;
+           g = attrp[ai].u.c.g * 257;
+           b = attrp[ai].u.c.b * 257;
+           cp = &WCU->colors[color].color;
+           if (WCU->colors[color].inuse)
+               if (cp->red != r || cp->green != g || cp->blue != b)
+                   if (color > 1 || WCU->allocedcolor[color])
+                       XFreeColors(Gdisplay, WCU->cmap, &cp->pixel, 1, 0);
+           cp->red = r, cp->green = g, cp->blue = b;
+           if (XAllocColor(Gdisplay, WCU->cmap, cp)) {
+               WCU->colors[color].inuse = TRUE;
+               if (color <= 1)
+                   WCU->allocedcolor[color] = TRUE;
+           }
+           /* XAllocColor may change the rgb values */
+           cp->red = r, cp->green = g, cp->blue = b;
+           if (color == WCU->gattr.color)
+               WCU->gattr.color = -1;
+           break;
+       case G_ATTRVIEWPORT:
+           WCU->vsize.x = (int) (attrp[ai].u.s.x + 0.5);
+           WCU->vsize.y = (int) (attrp[ai].u.s.y + 0.5);
+           ADD2ARGS(XtNwidth, WCU->vsize.x);
+           ADD2ARGS(XtNheight, WCU->vsize.y);
+           XtSetValues(widget->w, argp, argn);
+           adjustclip(widget);
+           RESETARGS;
+           break;
+       case G_ATTRWINDOW:
+           WCU->wrect = attrp[ai].u.r;
+           XtSetValues(widget->w, argp, argn);
+           adjustclip(widget);
+           RESETARGS;
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WCU->func = (Gcanvascb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    XtSetValues(widget->w, argp, argn);
+    return 0;
+}
+
+int GCgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    XColor *cp;
+    Dimension width, height;
+    int ai, color;
+
+    for (ai = 0; ai < attrn; ai++) {
+       RESETARGS;
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           ADD2ARGS(XtNwidth, &width);
+           ADD2ARGS(XtNheight, &height);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.s.x = width, attrp[ai].u.s.y = height;
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, &width);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.i = width;
+           break;
+       case G_ATTRCURSOR:
+           attrp[ai].u.t = (curcursori == -1) ?
+               "default" : cursormap[curcursori].name;
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           if (WCU->colors[color].inuse) {
+               cp = &WCU->colors[color].color;
+               attrp[ai].u.c.r = cp->red / 257.0;
+               attrp[ai].u.c.g = cp->green / 257.0;
+               attrp[ai].u.c.b = cp->blue / 257.0;
+           } else {
+               attrp[ai].u.c.r = -1;
+               attrp[ai].u.c.g = -1;
+               attrp[ai].u.c.b = -1;
+           }
+           break;
+       case G_ATTRVIEWPORT:
+           attrp[ai].u.s = WCU->vsize;
+           break;
+       case G_ATTRWINDOW:
+           attrp[ai].u.r = WCU->wrect;
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", XtWindow(widget->w));
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTREVENTCB:
+           attrp[ai].u.func = (void *) (WCU->func);
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GCdestroywidget(Gwidget_t * widget)
+{
+    XtDestroyWidget(widget->w);
+    return 0;
+}
+
+int GCcanvasclear(Gwidget_t * widget)
+{
+    XEvent ev;
+    int gotit;
+
+    XClearWindow(Gdisplay, WINDOW);
+    /* avoid a redraw */
+    WCU->needredraw = FALSE;
+    XSync(Gdisplay, False);
+    gotit = FALSE;
+    while (XCheckIfEvent(Gdisplay,
+                        &ev, cwepredicate, (XPointer) WINDOW) == True)
+       gotit = TRUE;
+    if (gotit)
+       adjustclip(widget);
+    return 0;
+}
+
+int GCsetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    setgattr(widget, ap);
+    WCU->defgattr = WCU->gattr;
+    return 0;
+}
+
+int GCgetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    if ((ap->flags & G_GATTRCOLOR))
+       ap->color = WCU->gattr.color;
+    if ((ap->flags & G_GATTRWIDTH))
+       ap->width = WCU->gattr.width;
+    if ((ap->flags & G_GATTRMODE))
+       ap->mode = WCU->gattr.mode;
+    if ((ap->flags & G_GATTRFILL))
+       ap->fill = WCU->gattr.fill;
+    if ((ap->flags & G_GATTRSTYLE))
+       ap->style = WCU->gattr.style;
+    return 0;
+}
+
+int GCarrow(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    PIXpoint_t pp1, pp2, pa, pb, pd;
+    Grect_t gr;
+    double tangent, l;
+
+    if (gp1.x < gp2.x)
+       gr.o.x = gp1.x, gr.c.x = gp2.x;
+    else
+       gr.o.x = gp2.x, gr.c.x = gp1.x;
+    if (gp1.y < gp2.y)
+       gr.o.y = gp1.y, gr.c.y = gp2.y;
+    else
+       gr.o.y = gp2.y, gr.c.y = gp1.y;
+    if (!ISVISIBLE(gr))
+       return 1;
+    pp1 = pdrawtopix(widget, gp1), pp2 = pdrawtopix(widget, gp2);
+    pd.x = pp1.x - pp2.x, pd.y = pp1.y - pp2.y;
+    if (pd.x == 0 && pd.y == 0)
+       return 0;
+    tangent = atan2((double) pd.y, (double) pd.x);
+    if ((l = sqrt((double) (pd.x * pd.x + pd.y * pd.y))) > 30)
+       l = 30;
+    pa.x = l * cos(tangent + M_PI / 7) + pp2.x;
+    pa.y = l * sin(tangent + M_PI / 7) + pp2.y;
+    pb.x = l * cos(tangent - M_PI / 7) + pp2.x;
+    pb.y = l * sin(tangent - M_PI / 7) + pp2.y;
+    setgattr(widget, ap);
+    XDrawLine(Gdisplay, WINDOW, GC, pp1.x, pp1.y, pp2.x, pp2.y);
+    XDrawLine(Gdisplay, WINDOW, GC, pa.x, pa.y, pp2.x, pp2.y);
+    XDrawLine(Gdisplay, WINDOW, GC, pb.x, pb.y, pp2.x, pp2.y);
+    return 0;
+}
+
+int GCline(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    PIXpoint_t pp1, pp2;
+    Grect_t gr;
+
+    if (gp1.x < gp2.x)
+       gr.o.x = gp1.x, gr.c.x = gp2.x;
+    else
+       gr.o.x = gp2.x, gr.c.x = gp1.x;
+    if (gp1.y < gp2.y)
+       gr.o.y = gp1.y, gr.c.y = gp2.y;
+    else
+       gr.o.y = gp2.y, gr.c.y = gp1.y;
+    if (!ISVISIBLE(gr))
+       return 1;
+    pp1 = pdrawtopix(widget, gp1), pp2 = pdrawtopix(widget, gp2);
+    setgattr(widget, ap);
+    XDrawLine(Gdisplay, WINDOW, GC, pp1.x, pp1.y, pp2.x, pp2.y);
+    return 0;
+}
+
+int GCbox(Gwidget_t * widget, Grect_t gr, Ggattr_t * ap)
+{
+    PIXrect_t pr;
+    Gxy_t p;
+
+    if (gr.o.x > gr.c.x)
+       p.x = gr.o.x, gr.o.x = gr.c.x, gr.c.x = p.x;
+    if (gr.o.y > gr.c.y)
+       p.y = gr.o.y, gr.o.y = gr.c.y, gr.c.y = p.y;
+    if (!ISVISIBLE(gr))
+       return 1;
+    pr = rdrawtopix(widget, gr);
+    setgattr(widget, ap);
+    if (WCU->gattr.fill)
+       XFillRectangle(Gdisplay, WINDOW, GC,
+                      pr.o.x, pr.o.y, pr.c.x - pr.o.x, pr.c.y - pr.o.y);
+    else
+       XDrawRectangle(Gdisplay, WINDOW, GC,
+                      pr.o.x, pr.o.y, pr.c.x - pr.o.x, pr.c.y - pr.o.y);
+    return 0;
+}
+
+int GCpolygon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    Grect_t gr;
+    int n, i;
+
+    if (gpn == 0)
+       return 0;
+    gr.o = gpp[0], gr.c = gpp[0];
+    for (i = 1; i < gpn; i++) {
+       gr.o.x = min(gr.o.x, gpp[i].x);
+       gr.o.y = min(gr.o.y, gpp[i].y);
+       gr.c.x = max(gr.c.x, gpp[i].x);
+       gr.c.y = max(gr.c.y, gpp[i].y);
+    }
+    if (!ISVISIBLE(gr))
+       return 1;
+    if (gpn + 1 > Gppn) {
+       n = (((gpn + 1) + PPINCR - 1) / PPINCR) * PPINCR;
+       Gppp = Marraygrow(Gppp, (long) n * PPSIZE);
+       Gppn = n;
+    }
+    for (i = 0; i < gpn; i++)
+       Gppp[i] = pdrawtopix(widget, gpp[i]);
+    setgattr(widget, ap);
+    if (WCU->gattr.fill) {
+       if (Gppp[gpn - 1].x != Gppp[0].x || Gppp[gpn - 1].y != Gppp[0].y)
+           Gppp[gpn] = Gppp[0], gpn++;
+       XFillPolygon(Gdisplay, WINDOW, GC, Gppp, gpn,
+                    Complex, CoordModeOrigin);
+    } else
+       XDrawLines(Gdisplay, WINDOW, GC, Gppp, gpn, CoordModeOrigin);
+    return 0;
+}
+
+int GCsplinegon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    PIXpoint_t p0, p1, p2, p3;
+    Grect_t gr;
+    int n, i;
+
+    if (gpn == 0)
+       return 0;
+    gr.o = gpp[0], gr.c = gpp[0];
+    for (i = 1; i < gpn; i++) {
+       gr.o.x = min(gr.o.x, gpp[i].x);
+       gr.o.y = min(gr.o.y, gpp[i].y);
+       gr.c.x = max(gr.c.x, gpp[i].x);
+       gr.c.y = max(gr.c.y, gpp[i].y);
+    }
+    if (!ISVISIBLE(gr))
+       return 1;
+    Gppi = 1;
+    if (Gppi >= Gppn) {
+       n = (((Gppi + 1) + PPINCR - 1) / PPINCR) * PPINCR;
+       Gppp = Marraygrow(Gppp, (long) n * PPSIZE);
+       Gppn = n;
+    }
+    Gppp[0] = p3 = pdrawtopix(widget, gpp[0]);
+    for (i = 1; i < gpn; i += 3) {
+       p0 = p3;
+       p1 = pdrawtopix(widget, gpp[i]);
+       p2 = pdrawtopix(widget, gpp[i + 1]);
+       p3 = pdrawtopix(widget, gpp[i + 2]);
+       bezier(p0, p1, p2, p3);
+    }
+    setgattr(widget, ap);
+    if (WCU->gattr.fill) {
+       if (Gppp[Gppi - 1].x != Gppp[0].x || Gppp[Gppi - 1].y != Gppp[0].y)
+           Gppp[Gppi] = Gppp[0], Gppi++;
+       XFillPolygon(Gdisplay, WINDOW, GC, Gppp, Gppi,
+                    Complex, CoordModeOrigin);
+    } else
+       XDrawLines(Gdisplay, WINDOW, GC, Gppp, Gppi, CoordModeOrigin);
+    return 0;
+}
+
+static void bezier(PIXpoint_t p0, PIXpoint_t p1,
+                  PIXpoint_t p2, PIXpoint_t p3)
+{
+    Gpoint_t gp0, gp1, gp2;
+    Gsize_t s;
+    PIXpoint_t p;
+    double t;
+    int n, i, steps;
+
+    if ((s.x = p3.x - p0.x) < 0)
+       s.x = -s.x;
+    if ((s.y = p3.y - p0.y) < 0)
+       s.y = -s.y;
+    if (s.x > s.y)
+       steps = s.x / 5 + 1;
+    else
+       steps = s.y / 5 + 1;
+    for (i = 0; i <= steps; i++) {
+       t = i / (double) steps;
+       gp0.x = p0.x + t * (p1.x - p0.x);
+       gp0.y = p0.y + t * (p1.y - p0.y);
+       gp1.x = p1.x + t * (p2.x - p1.x);
+       gp1.y = p1.y + t * (p2.y - p1.y);
+       gp2.x = p2.x + t * (p3.x - p2.x);
+       gp2.y = p2.y + t * (p3.y - p2.y);
+       gp0.x = gp0.x + t * (gp1.x - gp0.x);
+       gp0.y = gp0.y + t * (gp1.y - gp0.y);
+       gp1.x = gp1.x + t * (gp2.x - gp1.x);
+       gp1.y = gp1.y + t * (gp2.y - gp1.y);
+       p.x = gp0.x + t * (gp1.x - gp0.x) + 0.5;
+       p.y = gp0.y + t * (gp1.y - gp0.y) + 0.5;
+       if (Gppi >= Gppn) {
+           n = (((Gppi + 1) + PPINCR - 1) / PPINCR) * PPINCR;
+           Gppp = Marraygrow(Gppp, (long) n * PPSIZE);
+           Gppn = n;
+       }
+       Gppp[Gppi++] = p;
+    }
+}
+
+int GCarc(Gwidget_t * widget, Gpoint_t gc, Gsize_t gs, double ang1,
+         double ang2, Ggattr_t * ap)
+{
+    PIXpoint_t pc;
+    PIXsize_t ps;
+    Grect_t gr;
+
+    gr.o.x = gc.x - gs.x, gr.o.y = gc.y - gs.y;
+    gr.c.x = gc.x + gs.x, gr.c.y = gc.y + gs.y;
+    if (!ISVISIBLE(gr))
+       return 1;
+    pc = pdrawtopix(widget, gc), ps = sdrawtopix(widget, gs);
+    setgattr(widget, ap);
+    if (WCU->gattr.fill)
+       XFillArc(Gdisplay, WINDOW, GC, pc.x - ps.x, pc.y - ps.y,
+                ps.x * 2, ps.y * 2, (int) (ang1 * 64), (int) (ang2 * 64));
+    else
+       XDrawArc(Gdisplay, WINDOW, GC, pc.x - ps.x, pc.y - ps.y,
+                ps.x * 2, ps.y * 2, (int) (ang1 * 64), (int) (ang2 * 64));
+    return 0;
+}
+
+int GCtext(Gwidget_t * widget, Gtextline_t * tlp, int n, Gpoint_t go,
+          char *fn, double fs, char *justs, Ggattr_t * ap)
+{
+    Gsize_t gs;
+    PIXpoint_t po;
+    PIXsize_t ps;
+    PIXrect_t pr;
+    Grect_t gr;
+    XFontStruct *font;
+    int dir, asc, des, x = 0, y, w, h, i;
+    XCharStruct txtinfo;
+
+    po = pdrawtopix(widget, go);
+    gs.x = 0, gs.y = fs;
+    ps = sdrawtopix(widget, gs);
+    if (!(font = findfont(fn, ps.y))) {
+       XDrawRectangle(Gdisplay, WINDOW, GC, po.x, po.y, 1, 1);
+       return 0;
+    }
+    setgattr(widget, ap);
+    SETFONT(font);
+    for (w = h = 0, i = 0; i < n; i++) {
+       if (IS8BIT(font))
+           XTextExtents(font, tlp[i].p, tlp[i].n, &dir, &asc, &des,
+                        &txtinfo);
+       else
+           XTextExtents16(font, (XChar2b *) tlp[i].p, tlp[i].n / 2,
+                          &dir, &asc, &des, &txtinfo);
+       tlp[i].w = txtinfo.width, tlp[i].h = asc + des;
+       w = max(w, txtinfo.width), h += asc + des;
+    }
+    switch (justs[0]) {
+    case 'l':
+       po.x += w / 2;
+       break;
+    case 'r':
+       po.x -= w / 2;
+       break;
+    }
+    switch (justs[1]) {
+    case 'd':
+       po.y -= h;
+       break;
+    case 'c':
+       po.y -= h / 2;
+       break;
+    }
+    pr.o.x = po.x - w / 2, pr.o.y = po.y;
+    pr.c.x = po.x + w / 2, pr.c.y = po.y + h;
+    gr = rpixtodraw(widget, pr);
+    if (!ISVISIBLE(gr))
+       return 1;
+    for (i = 0; i < n; i++) {
+       switch (tlp[i].j) {
+       case 'l':
+           x = po.x - w / 2;
+           break;
+       case 'n':
+           x = po.x - tlp[i].w / 2;
+           break;
+       case 'r':
+           x = po.x - (tlp[i].w - w / 2);
+           break;
+       }
+       y = po.y + (i + 1) * tlp[i].h - des;
+       if (IS8BIT(font))
+           XDrawString(Gdisplay, WINDOW, GC, x, y, tlp[i].p, tlp[i].n);
+       else
+           XDrawString16(Gdisplay, WINDOW, GC, x, y,
+                         (XChar2b *) tlp[i].p, tlp[i].n / 2);
+    }
+    return 0;
+}
+
+int GCgettextsize(Gwidget_t * widget, Gtextline_t * tlp, int n, char *fn,
+                 double fs, Gsize_t * gsp)
+{
+    Gsize_t gs;
+    PIXsize_t ps;
+    XFontStruct *font;
+    int i, dir, asc, des;
+    XCharStruct txtinfo;
+
+    gs.x = 0, gs.y = fs;
+    ps = sdrawtopix(widget, gs);
+    if (!(font = findfont(fn, ps.y))) {
+       gsp->x = 1, gsp->y = 1;
+       return 0;
+    }
+    SETFONT(font);
+    for (ps.x = ps.y = 0, i = 0; i < n; i++) {
+       if (IS8BIT(font))
+           XTextExtents(font, tlp[i].p, tlp[i].n, &dir, &asc, &des,
+                        &txtinfo);
+       else
+           XTextExtents16(font, (XChar2b *) tlp[i].p, tlp[i].n / 2,
+                          &dir, &asc, &des, &txtinfo);
+       ps.x = max(ps.x, txtinfo.width), ps.y += asc + des;
+    }
+    *gsp = spixtodraw(widget, ps);
+    return 0;
+}
+
+static XFontStruct *findfont(char *name, int size)
+{
+    XFontStruct *font;
+    int fi, n, i;
+
+    if (name[0] == '\000')
+       return Gfontp[0].font;
+
+    sprintf(&Gbufp[0], name, size);
+    for (fi = 0; fi < Gfontn; fi++)
+       if (Strcmp(&Gbufp[0], Gfontp[fi].name) == 0)
+           return Gfontp[fi].font;
+    if (!(font = XLoadQueryFont(Gdisplay, &Gbufp[0]))) {
+       n = strlen(&Gbufp[0]) + 1;
+       for (i = 1; i < size; i++) {
+           sprintf(&Gbufp[n], name, size - i);
+           if ((font = XLoadQueryFont(Gdisplay, &Gbufp[n])))
+               break;
+           sprintf(&Gbufp[n], name, size + i);
+           if ((font = XLoadQueryFont(Gdisplay, &Gbufp[n])))
+               break;
+       }
+    }
+    if (!font)
+       font = Gfontp[0].font;
+
+    Gfontp = Marraygrow(Gfontp, (long) (Gfontn + 1) * FONTSIZE);
+    Gfontp[Gfontn].name = strdup(&Gbufp[0]);
+    Gfontp[Gfontn].font = font;
+    Gfontn++;
+    return font;
+}
+
+int GCcreatebitmap(Gwidget_t * widget, Gbitmap_t * bitmap, Gsize_t s)
+{
+    if (!widget) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    if (!
+       (bitmap->u.bmap.orig =
+        XCreatePixmap(Gdisplay, XtWindow(widget->w), (int) s.x, (int) s.y,
+                      Gdepth))) {
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    bitmap->u.bmap.scaled = 0;
+    bitmap->scale.x = bitmap->scale.y = 1;
+    bitmap->ctype = widget->type;
+    bitmap->canvas = widget - &Gwidgets[0];
+    bitmap->size = s;
+    return 0;
+}
+
+int GCdestroybitmap(Gbitmap_t * bitmap)
+{
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    XFreePixmap(Gdisplay, bitmap->u.bmap.orig);
+    if (bitmap->u.bmap.scaled)
+       XFreePixmap(Gdisplay, bitmap->u.bmap.scaled);
+    return 0;
+}
+
+#define COMPDIFF(a, b) (((a) > (b)) ? (a) - (b) : (b) - (a))
+#define CDIFF(a, b) (COMPDIFF (a.red, b[0]) + COMPDIFF (a.green, b[1]) + \
+        COMPDIFF (a.blue, b[2]))
+#define CMINMAXDIFF 20000
+
+int GCreadbitmap(Gwidget_t * widget, Gbitmap_t * bitmap, FILE * fp)
+{
+    Gsize_t s = { 0, 0 };
+    XImage *img;
+    XColor colors[G_MAXCOLORS];
+    char bufp[2048];
+    int rgb[3];
+    char *s1, *s2;
+    char c;
+    int cmaxdiff, colori, colorn, bufn, bufi, step, x, y, k;
+
+    if (!widget) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    step = 0;
+    while (step < 3) {
+      l1:
+       if (!fgets(bufp, 2048, fp)) {
+           Gerr(POS, G_ERRCANNOTREADBITMAP);
+           return -1;
+       }
+       s1 = &bufp[0];
+      l2:
+       for (; *s1 && isspace(*s1); s1++);
+       if (!*s1 || *s1 == '#')
+           goto l1;
+       switch (step) {
+       case 0:
+           if (strncmp(s1, "P6", 2) != 0) {
+               Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           step++, s1 += 2;
+           goto l2;
+       case 1:
+           for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++);
+           c = *s2, *s2 = 0;
+           if (s2 == s1 || (s.x = atoi(s1)) <= 0) {
+               *s2 = c, Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           *s2 = c, step++, s1 = s2;
+           goto l2;
+       case 2:
+           for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++);
+           c = *s2, *s2 = 0;
+           if (s2 == s1 || (s.y = atoi(s1)) <= 0) {
+               *s2 = c, Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           *s2 = c, step++, s1 = s2;
+           goto l2;
+       }
+    }
+    if (!
+       (bitmap->u.bmap.orig =
+        XCreatePixmap(Gdisplay, XtWindow(widget->w), (int) s.x, (int) s.y,
+                      Gdepth))) {
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    bitmap->u.bmap.scaled = 0;
+    bitmap->scale.x = bitmap->scale.y = 1;
+    bitmap->ctype = widget->type;
+    bitmap->canvas = widget - &Gwidgets[0];
+    bitmap->size = s;
+    if ((img = XCreateImage(Gdisplay, DefaultVisual(Gdisplay, Gscreenn),
+                           Gdepth, ZPixmap, 0, NULL, (int) s.x, (int) s.y,
+                           32, 0)) == NULL) {
+       XFreePixmap(Gdisplay, bitmap->u.bmap.orig);
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    if ((img->data = malloc(img->bytes_per_line * img->height)) == NULL) {
+       XFreePixmap(Gdisplay, bitmap->u.bmap.orig);
+       XDestroyImage(img);
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    colorn = 0;
+    for (colori = 0; colori < G_MAXCOLORS; colori++) {
+       if (widget->u.c->colors[colori].inuse) {
+           colors[colorn] = widget->u.c->colors[colori].color;
+           colorn++;
+       }
+    }
+    bufi = bufn = 0;
+    bufp[bufi] = 0;
+    for (y = 0; y < s.y; y++) {
+       for (x = 0; x < s.x; x++) {
+           for (k = 0; k < 3; k++) {
+               if (bufi == bufn) {
+                   if ((bufn = fread(bufp, 1, 2047, fp)) == 0) {
+                       XFreePixmap(Gdisplay, bitmap->u.bmap.orig);
+                       XDestroyImage(img);
+                       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+                       return -1;
+                   }
+                   bufi = 0;
+               }
+               rgb[k] = 257 * (unsigned char) bufp[bufi++];
+           }
+           cmaxdiff = CMINMAXDIFF;
+         l3:
+           for (colori = 0; colori < colorn; colori++)
+               if (CDIFF(colors[colori], rgb) < cmaxdiff)
+                   break;
+           if (colori == colorn && colorn < G_MAXCOLORS &&
+               cmaxdiff == CMINMAXDIFF) {
+               colors[colorn].red = rgb[0];
+               colors[colorn].green = rgb[1];
+               colors[colorn].blue = rgb[2];
+               if (XAllocColor
+                   (Gdisplay, widget->u.c->cmap, &colors[colorn]))
+                   colorn++;
+           }
+           if (colori == colorn) {
+               cmaxdiff *= 10;
+               goto l3;
+           }
+           XPutPixel(img, x, y, colors[colori].pixel);
+       }
+    }
+    for (colori = 0; colori < G_MAXCOLORS && colori < colorn; colori++) {
+       if (!widget->u.c->colors[colori].inuse) {
+           widget->u.c->colors[colori].color = colors[colori];
+           widget->u.c->colors[colori].inuse = TRUE;
+       }
+    }
+    XPutImage(Gdisplay, bitmap->u.bmap.orig, widget->u.c->gc, img,
+             0, 0, 0, 0, (int) s.x, (int) s.y);
+    XDestroyImage(img);
+    return 0;
+}
+
+int GCwritebitmap(Gbitmap_t * bitmap, FILE * fp)
+{
+    Gwidget_t *widget;
+    XImage *img;
+    XColor colors[G_MAXCOLORS];
+    char bufp[2048];
+    int colori, bufi, x, y, w, h;
+
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    if (bitmap->canvas < 0 || bitmap->canvas >= Gwidgetn ||
+       !Gwidgets[bitmap->canvas].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, bitmap->canvas);
+       return -1;
+    }
+    widget = &Gwidgets[bitmap->canvas];
+    if (widget->type != G_CANVASWIDGET && widget->type != G_PCANVASWIDGET) {
+       Gerr(POS, G_ERRNOTACANVAS, bitmap->canvas);
+       return -1;
+    }
+    for (colori = 0; colori < G_MAXCOLORS; colori++)
+       colors[colori].pixel = colori;
+    XQueryColors(Gdisplay, widget->u.c->cmap, &colors[0], G_MAXCOLORS);
+    if (!(img = XGetImage(Gdisplay, bitmap->u.bmap.orig, 0, 0,
+                         bitmap->size.x, bitmap->size.y, AllPlanes,
+                         ZPixmap))) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    fprintf(fp, "P6\n%d %d 255\n", (int) bitmap->size.x,
+           (int) bitmap->size.y);
+    bufi = 0;
+    w = bitmap->size.x;
+    h = bitmap->size.y;
+    for (y = 0; y < h; y++) {
+       for (x = 0; x < w; x++) {
+           colori = XGetPixel(img, x, y);
+           bufp[bufi++] = colors[colori].red / 257;
+           bufp[bufi++] = colors[colori].green / 257;
+           bufp[bufi++] = colors[colori].blue / 257;
+           if (bufi + 3 >= 2048) {
+               fwrite(bufp, 1, bufi, fp);
+               bufi = 0;
+           }
+       }
+    }
+    if (bufi > 0)
+       fwrite(bufp, 1, bufi, fp);
+    XDestroyImage(img);
+    return 0;
+}
+
+int GCbitblt(Gwidget_t * widget, Gpoint_t gp, Grect_t gr,
+            Gbitmap_t * bitmap, char *mode, Ggattr_t * ap)
+{
+    PIXrect_t pr;
+    PIXpoint_t pp;
+    Gsize_t scale;
+    Gxy_t p;
+    Pixmap pix;
+    double tvx, tvy, twx, twy;
+
+    if (gr.o.x > gr.c.x)
+       p.x = gr.o.x, gr.o.x = gr.c.x, gr.c.x = p.x;
+    if (gr.o.y > gr.c.y)
+       p.y = gr.o.y, gr.o.y = gr.c.y, gr.c.y = p.y;
+    if (strcmp(mode, "b2c") == 0) {
+       if (!ISVISIBLE(gr))
+           return 1;
+       tvx = WCU->vsize.x, tvy = WCU->vsize.y;
+       twx = WCU->wrect.c.x - WCU->wrect.o.x;
+       twy = WCU->wrect.c.y - WCU->wrect.o.y;
+       scale.x = tvx / twx, scale.y = tvy / twy;
+       if (scale.x >= 1.0 - 1E-4 && scale.x <= 1.0 + 1E-4 &&
+           scale.y >= 1.0 - 1E-4 && scale.y <= 1.0 + 1E-4) {
+           pix = bitmap->u.bmap.orig;
+           bitmap->scale = scale;
+       } else {
+           if (scale.x != bitmap->scale.x || scale.y != bitmap->scale.y)
+               scalebitmap(widget, bitmap, scale, TRUE, 1);
+           pix = bitmap->u.bmap.scaled;
+       }
+       pr = rdrawtopix(widget, gr);
+       pp = pdrawtobpix(bitmap, gp);
+       setgattr(widget, ap);
+       XCopyArea(Gdisplay, pix, WINDOW, GC, pp.x, pp.y - pr.c.y + pr.o.y,
+                 pr.c.x - pr.o.x + 1, pr.c.y - pr.o.y + 1, pr.o.x,
+                 pr.o.y);
+    } else if (strcmp(mode, "c2b") == 0) {
+       tvx = WCU->vsize.x, tvy = WCU->vsize.y;
+       twx = WCU->wrect.c.x - WCU->wrect.o.x;
+       twy = WCU->wrect.c.y - WCU->wrect.o.y;
+       scale.x = tvx / twx, scale.y = tvy / twy;
+       if (scale.x >= 1.0 - 1E-4 && scale.x <= 1.0 + 1E-4 &&
+           scale.y >= 1.0 - 1E-4 && scale.y <= 1.0 + 1E-4) {
+           pix = bitmap->u.bmap.orig;
+           bitmap->scale = scale;
+       } else {
+           if (scale.x != bitmap->scale.x || scale.y != bitmap->scale.y)
+               scalebitmap(widget, bitmap, scale, FALSE, 1);
+           pix = bitmap->u.bmap.scaled;
+       }
+       pr = rdrawtobpix(bitmap, gr);
+       pp = pdrawtopix(widget, gp);
+       setgattr(widget, ap);
+       XCopyArea(Gdisplay, WINDOW, pix, GC, pp.x, pp.y - pr.c.y + pr.o.y,
+                 pr.c.x - pr.o.x + 1, pr.c.y - pr.o.y + 1, pr.o.x,
+                 pr.o.y);
+       if (pix != bitmap->u.bmap.orig)
+           scalebitmap(widget, bitmap, scale, TRUE, -1);
+    }
+    return 0;
+}
+
+static int scalebitmap(Gwidget_t * widget, Gbitmap_t * bitmap,
+                      Gsize_t scale, int copybits, int dir)
+{
+    Gsize_t nsize, o2n;
+    Pixmap spix;
+    XImage *oimg, *simg;
+    XColor colors[G_MAXCOLORS];
+    int cmaxdiff, colorn, colori, x, y, x2, y2 = 0, xp, yp;
+    double prod, rgb[3], xr2, yr2 = 0, xl2, yl2 =
+       0, xf2, yf2, xr, yr, xl, yl;
+
+    if (!copybits) {
+       if (dir == 1) {
+           nsize.x = (int) (bitmap->size.x * scale.x);
+           nsize.y = (int) (bitmap->size.y * scale.y);
+           if (!(spix = XCreatePixmap(Gdisplay, XtWindow(widget->w),
+                                      (int) nsize.x, (int) nsize.y,
+                                      Gdepth))) {
+               Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+               return -1;
+           }
+           if (bitmap->u.bmap.scaled)
+               XFreePixmap(Gdisplay, bitmap->u.bmap.scaled);
+           bitmap->u.bmap.scaled = spix;
+           bitmap->scale = scale;
+       }
+       return 0;
+    }
+    for (colori = 0; colori < G_MAXCOLORS; colori++)
+       colors[colori].pixel = colori;
+    colorn = G_MAXCOLORS;
+    XQueryColors(Gdisplay, widget->u.c->cmap, &colors[0], G_MAXCOLORS);
+    if (dir == 1) {
+       nsize.x = (int) (bitmap->size.x * scale.x);
+       nsize.y = (int) (bitmap->size.y * scale.y);
+       o2n.x = 1 / scale.x, o2n.y = 1 / scale.y;
+       if (!(oimg = XGetImage(Gdisplay, bitmap->u.bmap.orig, 0, 0,
+                              (int) bitmap->size.x, (int) bitmap->size.y,
+                              AllPlanes, ZPixmap))) {
+           Gerr(POS, G_ERRNOBITMAP);
+           return -1;
+       }
+       if (!(spix = XCreatePixmap(Gdisplay, XtWindow(widget->w),
+                                  (int) nsize.x, (int) nsize.y,
+                                  Gdepth))) {
+           XDestroyImage(oimg);
+           Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+           return -1;
+       }
+    } else {
+       nsize.x = (int) bitmap->size.x;
+       nsize.y = (int) bitmap->size.y;
+       o2n.x = scale.x, o2n.y = scale.y;
+       if (!(oimg = XGetImage(Gdisplay, bitmap->u.bmap.scaled, 0, 0,
+                              (int) (bitmap->size.x * scale.x),
+                              (int) (bitmap->size.y * scale.y), AllPlanes,
+                              ZPixmap))) {
+           Gerr(POS, G_ERRNOBITMAP);
+           return -1;
+       }
+       spix = bitmap->u.bmap.orig;
+    }
+    prod = o2n.x * o2n.y;
+    if (!(simg = XCreateImage(Gdisplay, DefaultVisual(Gdisplay, Gscreenn),
+                             Gdepth, ZPixmap, 0, NULL, (int) nsize.x,
+                             (int) nsize.y, 32, 0))) {
+       XFreePixmap(Gdisplay, spix);
+       XDestroyImage(oimg);
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    if ((simg->data = malloc(simg->bytes_per_line * simg->height)) == NULL) {
+       XFreePixmap(Gdisplay, spix);
+       XDestroyImage(oimg);
+       XDestroyImage(simg);
+       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+       return -1;
+    }
+    y = 0;
+    yr = o2n.y;
+    yl = 0;
+    for (yp = 0; yp < nsize.y; yp++) {
+       x = 0;
+       xr = o2n.x;
+       xl = 0;
+       for (xp = 0; xp < nsize.x; xp++) {
+           y2 = y;
+           yr2 = yr;
+           yl2 = yl;
+           rgb[0] = rgb[1] = rgb[2] = 0;
+           do {
+               x2 = x;
+               xr2 = xr;
+               xl2 = xl;
+               yf2 = (yl2 + yr2 > 1) ? 1 - yl2 : yr2, yr2 -= yf2;
+               do {
+                   xf2 = (xl2 + xr2 > 1) ? 1 - xl2 : xr2, xr2 -= xf2;
+                   colori = XGetPixel(oimg, x2, y2);
+                   rgb[0] += (colors[colori].red * xf2 * yf2 / prod);
+                   rgb[1] += (colors[colori].green * xf2 * yf2 / prod);
+                   rgb[2] += (colors[colori].blue * xf2 * yf2 / prod);
+                   xl2 += xf2;
+                   if (xl2 >= 1)
+                       x2++, xl2 -= 1;
+               } while (xr2 > 0);
+               xr2 = o2n.x;
+               yl2 += yf2;
+               if (yl2 >= 1)
+                   y2++, yl2 -= 1;
+           } while (yr2 > 0);
+           yr2 = o2n.y;
+           cmaxdiff = CMINMAXDIFF;
+         l4:
+           for (colori = 0; colori < colorn; colori++)
+               if (CDIFF(colors[colori], rgb) < cmaxdiff)
+                   break;
+           if (colori == colorn) {
+               cmaxdiff *= 10;
+               goto l4;
+           }
+           XPutPixel(simg, xp, yp, colors[colori].pixel);
+           x = x2;
+           xr = xr2;
+           xl = xl2;
+       }
+       y = y2;
+       yr = yr2;
+       yl = yl2;
+    }
+    XPutImage(Gdisplay, spix, widget->u.c->gc, simg, 0, 0, 0, 0,
+             (int) nsize.x, (int) nsize.y);
+    XDestroyImage(simg);
+    XDestroyImage(oimg);
+    if (dir == 1) {
+       if (bitmap->u.bmap.scaled)
+           XFreePixmap(Gdisplay, bitmap->u.bmap.scaled);
+       bitmap->u.bmap.scaled = spix;
+       bitmap->scale = scale;
+    }
+    return 0;
+}
+
+int GCgetmousecoords(Gwidget_t * widget, Gpoint_t * gpp, int *count)
+{
+    PIXpoint_t pp;
+    Window rwin, cwin;
+    int rx, ry, x, y;
+    unsigned int mask;
+
+    XQueryPointer(Gdisplay, WINDOW, &rwin, &cwin, &rx, &ry, &x, &y, &mask);
+    pp.x = x, pp.y = y;
+    *gpp = Gppixtodraw(widget, pp);
+    *count =
+       ((mask & Button1Mask) ? 1 : 0) + ((mask & Button2Mask) ? 1 : 0) +
+       ((mask & Button3Mask) ? 1 : 0);
+    return 0;
+}
+
+void Gcwbutaction(Widget w, XEvent * evp, char **app, unsigned int *anp)
+{
+    Gwidget_t *widget;
+    Gevent_t gev;
+    PIXpoint_t pp;
+    int wi, xtype, bn;
+
+    widget = findwidget((unsigned long) w, G_CANVASWIDGET);
+    switch ((xtype = evp->type)) {
+    case ButtonPress:
+    case ButtonRelease:
+       gev.type = G_MOUSE;
+       gev.code = (xtype == ButtonPress) ? G_DOWN : G_UP;
+       gev.data = evp->xbutton.button - Button1;
+       if (gev.data > 2)
+           return;
+       pp.x = evp->xbutton.x, pp.y = evp->xbutton.y;
+       gev.p = Gppixtodraw(widget, pp);
+       bn = WCU->bstate[gev.data];
+       WCU->bstate[gev.data] = (xtype == ButtonPress) ? 1 : 0;
+       bn = WCU->bstate[gev.data] - bn;
+       WCU->buttonsdown += bn;
+       Gbuttonsdown += bn;
+       break;
+    default:
+       return;
+    }
+    wi = gev.wi = widget - &Gwidgets[0];
+    Gpopdownflag = FALSE;
+    if (WCU->func)
+       (*WCU->func) (&gev);
+    if (Gpopdownflag) {
+       Gpopdownflag = FALSE;
+       if (gev.code == G_DOWN) {
+           gev.code = G_UP;
+           widget = &Gwidgets[wi];
+           WCU->bstate[gev.data] = 0;
+           WCU->buttonsdown--;
+           Gbuttonsdown--;
+           if (widget->inuse && WCU->func)
+               (*WCU->func) (&gev);
+       }
+    }
+}
+
+void Gcwkeyaction(Widget w, XEvent * evp, char **app, unsigned int *anp)
+{
+    Gwidget_t *widget;
+    Gevent_t gev;
+    PIXpoint_t pp;
+    Window rwin, cwin;
+    int xtype, rx, ry, x, y;
+    unsigned int mask;
+    char c;
+
+    widget = findwidget((unsigned long) w, G_CANVASWIDGET);
+    switch ((xtype = evp->type)) {
+    case KeyPress:
+    case KeyRelease:
+       if (!XLookupString((XKeyEvent *) evp, &c, 1, NULL, NULL))
+           return;
+       XQueryPointer(Gdisplay, WCU->window, &rwin, &cwin,
+                     &rx, &ry, &x, &y, &mask);
+       gev.type = G_KEYBD;
+       gev.code = (xtype == KeyPress) ? G_DOWN : G_UP;
+       gev.data = c;
+       pp.x = x, pp.y = y;
+       gev.p = Gppixtodraw(widget, pp);
+       break;
+    default:
+       return;
+    }
+    gev.wi = widget - &Gwidgets[0];
+    Gpopdownflag = FALSE;
+    if (WCU->func)
+       (*WCU->func) (&gev);
+    if (Gpopdownflag)
+       Gpopdownflag = FALSE;
+}
+
+static void setgattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    XGCValues gcv;
+    XColor *cp;
+    int color, width, mode, style, pati;
+    double intens;
+
+    if (!(ap->flags & G_GATTRCOLOR))
+       ap->color = WCU->defgattr.color;
+    if (!(ap->flags & G_GATTRWIDTH))
+       ap->width = WCU->defgattr.width;
+    if (!(ap->flags & G_GATTRMODE))
+       ap->mode = WCU->defgattr.mode;
+    if (!(ap->flags & G_GATTRFILL))
+       ap->fill = WCU->defgattr.fill;
+    if (!(ap->flags & G_GATTRSTYLE))
+       ap->style = WCU->defgattr.style;
+    color = ap->color;
+    if (color >= G_MAXCOLORS || !(WCU->colors[color].inuse))
+       color = 1;
+    if (color != WCU->gattr.color) {
+       WCU->gattr.color = color;
+       if (ap->mode == G_XOR)
+           XSetForeground(Gdisplay, GC,
+                          WCU->colors[WCU->gattr.color].color.pixel ^
+                          WCU->colors[0].color.pixel);
+       else
+           XSetForeground(Gdisplay, GC,
+                          WCU->colors[WCU->gattr.color].color.pixel);
+       if (Gdepth == 1) {
+           cp = &WCU->colors[color].color;
+           intens = (0.3 * cp->blue + 0.59 * cp->red +
+                     0.11 * cp->green) / 65535.0;
+           pati = (intens <= 0.0625) ? 16 :
+               -16.0 * (log(intens) / 2.7725887222);
+           XSetTile(Gdisplay, GC, WCU->grays[pati]);
+       }
+    }
+    mode = ap->mode;
+    if (mode != WCU->gattr.mode) {
+       WCU->gattr.mode = mode;
+       XSetFunction(Gdisplay, GC, WCU->gattr.mode);
+       if (mode == G_XOR)
+           XSetForeground(Gdisplay, GC,
+                          WCU->colors[WCU->gattr.color].color.pixel ^
+                          WCU->colors[0].color.pixel);
+       else
+           XSetForeground(Gdisplay, GC,
+                          WCU->colors[WCU->gattr.color].color.pixel);
+    }
+    width = ap->width;
+    if (width != WCU->gattr.width) {
+       gcv.line_width = WCU->gattr.width = width;
+       XChangeGC(Gdisplay, GC, GCLineWidth, &gcv);
+    }
+    WCU->gattr.fill = ap->fill;
+    style = ap->style;
+    if (style != WCU->gattr.style) {
+       WCU->gattr.style = style;
+       if (style == G_SOLID) {
+           gcv.line_style = LineSolid;
+           XChangeGC(Gdisplay, GC, GCLineStyle, &gcv);
+       } else {
+           XSetDashes(Gdisplay, GC, 0, gstyles[style], 2);
+           gcv.line_style = LineOnOffDash;
+           XChangeGC(Gdisplay, GC, GCLineStyle, &gcv);
+       }
+    }
+}
+
+static PIXrect_t rdrawtopix(Gwidget_t * widget, Grect_t gr)
+{
+    PIXrect_t pr;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    pr.o.x = tvx * (gr.o.x - WCU->wrect.o.x) / twx + 0.5;
+    pr.o.y = tvy * (1.0 - (gr.c.y - WCU->wrect.o.y) / twy) + 0.5;
+    pr.c.x = tvx * (gr.c.x - WCU->wrect.o.x) / twx + 0.5;
+    pr.c.y = tvy * (1.0 - (gr.o.y - WCU->wrect.o.y) / twy) + 0.5;
+    return pr;
+}
+
+static PIXpoint_t pdrawtopix(Gwidget_t * widget, Gpoint_t gp)
+{
+    PIXpoint_t pp;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    pp.x = tvx * (gp.x - WCU->wrect.o.x) / twx + 0.5;
+    pp.y = tvy * (1.0 - (gp.y - WCU->wrect.o.y) / twy) + 0.5;
+    return pp;
+}
+
+static PIXsize_t sdrawtopix(Gwidget_t * widget, Gsize_t gs)
+{
+    PIXsize_t ps;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    ps.x = tvx * (gs.x - 1) / twx + 1.5;
+    ps.y = tvy * (gs.y - 1) / twy + 1.5;
+    return ps;
+}
+
+static Gpoint_t Gppixtodraw(Gwidget_t * widget, PIXpoint_t pp)
+{
+    Gpoint_t gp;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    gp.x = (pp.x / tvx) * twx + WCU->wrect.o.x;
+    gp.y = (1.0 - pp.y / tvy) * twy + WCU->wrect.o.y;
+    return gp;
+}
+
+static Gsize_t spixtodraw(Gwidget_t * widget, PIXsize_t ps)
+{
+    Gsize_t gs;
+    double tvx, tvy, twx, twy;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    gs.x = ((ps.x - 1) / tvx) * twx + 1;
+    gs.y = ((ps.y - 1) / tvy) * twy + 1;
+    return gs;
+}
+
+static Grect_t rpixtodraw(Gwidget_t * widget, PIXrect_t pr)
+{
+    Grect_t gr;
+    double tvx, tvy, twx, twy, n;
+
+    tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
+    twx = WCU->wrect.c.x - WCU->wrect.o.x;
+    twy = WCU->wrect.c.y - WCU->wrect.o.y;
+    gr.o.x = (pr.o.x / tvx) * twx + WCU->wrect.o.x;
+    gr.o.y = (1.0 - pr.c.y / tvy) * twy + WCU->wrect.o.y;
+    gr.c.x = (pr.c.x / tvx) * twx + WCU->wrect.o.x;
+    gr.c.y = (1.0 - pr.o.y / tvy) * twy + WCU->wrect.o.y;
+    if (gr.o.x > gr.c.x)
+       n = gr.o.x, gr.o.x = gr.c.x, gr.c.x = n;
+    if (gr.o.y > gr.c.y)
+       n = gr.o.y, gr.o.y = gr.c.y, gr.c.y = n;
+    return gr;
+}
+
+static PIXrect_t rdrawtobpix(Gbitmap_t * bitmap, Grect_t gr)
+{
+    PIXrect_t pr;
+    double tvy;
+
+    tvy = (bitmap->size.y - 1) * bitmap->scale.y;
+    pr.o.x = gr.o.x + 0.5;
+    pr.o.y = tvy - gr.c.y + 0.5;
+    pr.c.x = gr.c.x + 0.5;
+    pr.c.y = tvy - gr.o.y + 0.5;
+    return pr;
+}
+
+static PIXpoint_t pdrawtobpix(Gbitmap_t * bitmap, Gpoint_t gp)
+{
+    PIXpoint_t pp;
+    double tvy;
+
+    tvy = (bitmap->size.y - 1) * bitmap->scale.y;
+    pp.x = gp.x + 0.5;
+    pp.y = tvy - gp.y + 0.5;
+    return pp;
+}
+
+static void adjustclip(Gwidget_t * widget)
+{
+    Gwidget_t *parent;
+    Dimension width, height, pwidth, pheight;
+    Position x, y;
+    PIXrect_t pr;
+
+    parent = &Gwidgets[widget->pwi];
+    RESETARGS;
+    ADD2ARGS(XtNx, &x);
+    ADD2ARGS(XtNy, &y);
+    ADD2ARGS(XtNwidth, &width);
+    ADD2ARGS(XtNheight, &height);
+    XtGetValues(widget->w, argp, argn);
+    RESETARGS;
+    ADD2ARGS(XtNwidth, &pwidth);
+    ADD2ARGS(XtNheight, &pheight);
+    XtGetValues(parent->w, argp, argn);
+    pr.o.x = max(0, -x);
+    pr.o.y = max(0, -y);
+    pr.c.x = min(width, pr.o.x + pwidth);
+    pr.c.y = min(height, pr.o.y + pheight);
+    pr.c.x = max(pr.o.x, pr.c.x);
+    pr.c.y = max(pr.o.y, pr.c.y);
+    WCU->clip = rpixtodraw(widget, pr);
+#ifdef FEATURE_GMAP
+    if (WCU->gmapmode) {
+       GMAPwinsetsize(XtWindow(widget->w), width, height);
+       GMAPchansetaspect(XtWindow(widget->w), width, height);
+    }
+#endif
+}
+
+static Bool cwepredicate(Display * display, XEvent * evp, XPointer data)
+{
+    if (evp->type == Expose
+       && ((XAnyEvent *) evp)->window == (Window) data)
+       return True;
+    return False;
+}
+
+static void cweventhandler(Widget w, XtPointer data, XEvent * evp,
+                          Boolean * cont)
+{
+    Gwidget_t *widget;
+
+    widget = findwidget((unsigned long) w, G_CANVASWIDGET);
+    Gneedredraw = WCU->needredraw = TRUE;
+    adjustclip(widget);
+#ifdef FEATURE_GMAP
+    if (WCU->gmapmode)
+       GMAPneedupdate = TRUE;
+#endif
+}
+
+static Bool cwvpredicate(Display * display, XEvent * evp, XPointer data)
+{
+    if (evp->type == VisibilityNotify &&
+       ((XAnyEvent *) evp)->window == (Window) data)
+       return True;
+    return False;
+}
diff --git a/cmd/lefty/ws/x11/gcommon.c b/cmd/lefty/ws/x11/gcommon.c
new file mode 100644 (file)
index 0000000..952d489
--- /dev/null
@@ -0,0 +1,319 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+int Gxfd;
+Widget Groot;
+Display *Gdisplay;
+int Gpopdownflag;
+int Gscreenn;
+int Gdepth;
+Glazyq_t Glazyq;
+
+PIXpoint_t *Gppp;
+int Gppn, Gppi;
+
+char *Gbufp = NULL;
+int Gbufn = 0, Gbufi = 0;
+
+Gfont_t *Gfontp;
+int Gfontn;
+
+/* Xt[GS]etValues arguments */
+Arg argp[MAXARGS];
+int argn;
+
+/* action and translation tables */
+static XtActionsRec actiontable[] = {
+    {"cwbut", Gcwbutaction},
+    {"cwkey", Gcwkeyaction},
+    {"lwbut", Glwbutaction},
+    {"lwkey", Glwkeyaction},
+    {"tweol", Gtweolaction},
+    {"qwpop", Gqwpopaction},
+    {"wmdel", Gwmdelaction},
+};
+static char defcwanytrans[] = "\
+    <BtnDown>: cwbut()\n\
+    <BtnUp>: cwbut()\n\
+    <KeyDown>: cwkey()\n\
+    <KeyUp>: cwkey()";
+static char deflwanytrans[] = "\
+    <BtnDown>: lwbut()\n\
+    <BtnUp>: lwbut()\n\
+    <KeyDown>: lwkey()\n\
+    <KeyUp>: lwkey()";
+static char deftweoltrans[] =
+    "<Key>Return: newline()\n<KeyUp>Return: tweol()";
+static char defqwpoptrans[] = "<KeyDown>Return:\n<KeyUp>Return: qwpop()";
+static char defwmdeltrans[] = "<Message>WM_PROTOCOLS: wmdel()\n";
+XtTranslations Gtweoltable;
+XtTranslations Gqwpoptable;
+XtTranslations Glwanytable;
+XtTranslations Gcwanytable;
+XtTranslations Gwmdeltable;
+
+Atom Gwmdelatom;
+
+static XtAppContext appcontext;
+static XFontStruct *deffont;
+
+#ifdef FEATURE_NEXTAW
+static char *props[] = {
+    "*Font: -*-lucidatypewriter-*-r-*-*-*-120-*-*-*-*-iso8859-1",
+    "*ShapeStyle: Rectangle",
+    "*Command.ShapeStyle: Rectangle",
+    "*Toggle.ShapeStyle: Rectangle",
+    "*ShadowWidth: 2",
+    "*Form*Text*Background: white",
+    "*Form*Text*Scrollbar*Background: gray",
+    "*Text*Background: white",
+    "*Text*Foreground: black",
+    "*Text*ThreeD*Background: gray",
+    "*Text*Scrollbar*Background: gray",
+    "*Viewport.background: gray",
+    "*BeNiceToColormap: 0",
+    "*Toggle.Font: -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
+    "*Command.Font: -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
+    "*Command.Background: gray",
+    "*Command.highlightThickness: 0",
+    "*Toggle.highlightThickness: 0",
+    "*Toggle.Background: gray",
+    "*MenuButton.Background: gray",
+    "*SimpleMenu.Background: gray",
+    "*List*Background: gray",
+    "*Box.Background: gray",
+    "*Label.Background: gray",
+    "*Label.ShadowWidth: 0",
+    "*Label*shadowWidth: 2",
+    "*Scrollbar.Background: gray",
+    "*SimpleMenu*MenuLabel*Background: black",
+    "*SimpleMenu*MenuLabel*Font: -adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*",
+    "*SimpleMenu*Font: -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
+    "*Dialog.Background: gray",
+    "*Form*Background: gray",
+    "*Form*Viewport*Background: white",
+    "*Form*Viewport*Scrollbar*Background: grey",
+    "*MenuButton.translations: Any<BtnDown>: PopupMenu()",
+    NULL
+};
+#else
+#ifdef FEATURE_XAW3D
+static char *props[] = {
+    "*Font: -*-lucidatypewriter-*-r-*-*-*-120-*-*-*-*-iso8859-1",
+    "*ShapeStyle: Rectangle",
+    "*Command.ShapeStyle: Rectangle",
+    "*Toggle.ShapeStyle: Rectangle",
+    "*ShadowWidth: 2",
+    "*Form*Text*Background: white",
+    "*Form*Text*Scrollbar*Background: gray",
+    "*Text*Background: white",
+    "*Text*Foreground: black",
+    "*Text*ThreeD*Background: gray",
+    "*Text*Scrollbar*Background: gray",
+    "*Viewport.background: gray",
+    "*BeNiceToColormap: 0",
+    "*Toggle.Font: -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
+    "*Command.Font: -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
+    "*Command.Background: gray",
+    "*Command.highlightThickness: 0",
+    "*Toggle.highlightThickness: 0",
+    "*Toggle.Background: gray",
+    "*MenuButton.Background: gray",
+    "*SimpleMenu.Background: gray",
+    "*List*Background: gray",
+    "*Box.Background: gray",
+    "*Label.Background: gray",
+    "*Label.ShadowWidth: 0",
+    "*Label*shadowWidth: 2",
+    "*Scrollbar.Background: gray",
+    "*SimpleMenu*MenuLabel*Background: black",
+    "*SimpleMenu*MenuLabel*Font: -adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*",
+    "*SimpleMenu*Font: -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
+    "*Dialog.Background: gray",
+    "*Form*Background: gray",
+    "*Form*Viewport*Background: white",
+    "*Form*Viewport*Scrollbar*Background: grey",
+    "*MenuButton.translations: Any<BtnDown>: PopupMenu()",
+    NULL
+};
+#endif
+#endif
+
+int Ginitgraphics(void)
+{
+    argn = 0;
+#if defined(FEATURE_NEXTAW) || defined(FEATURE_XAW3D)
+    if (!(Groot = XtAppInitialize(&appcontext, "LEFTY", NULL, 0,
+                                 &argn, NULL, props, NULL, 0)))
+#else
+    if (!(Groot = XtAppInitialize(&appcontext, "LEFTY", NULL, 0,
+                                 &argn, NULL, NULL, NULL, 0)))
+#endif
+       Gerr(POS, G_ERRINITFAILED);
+    XtAppAddActions(appcontext, actiontable, XtNumber(actiontable));
+    Gtweoltable = XtParseTranslationTable(deftweoltrans);
+    Gqwpoptable = XtParseTranslationTable(defqwpoptrans);
+    Glwanytable = XtParseTranslationTable(deflwanytrans);
+    Gcwanytable = XtParseTranslationTable(defcwanytrans);
+    Gwmdeltable = XtParseTranslationTable(defwmdeltrans);
+    XtRegisterGrabAction(Glwbutaction, True,
+                        ButtonPressMask | ButtonReleaseMask,
+                        GrabModeAsync, GrabModeAsync);
+    XtRegisterGrabAction(Gcwbutaction, True,
+                        ButtonPressMask | ButtonReleaseMask,
+                        GrabModeAsync, GrabModeAsync);
+    Gdisplay = XtDisplay(Groot);
+    Gscreenn = DefaultScreen(Gdisplay);
+    Gdepth = DefaultDepth(Gdisplay, Gscreenn);
+    deffont = XLoadQueryFont(Gdisplay, "fixed");
+    Gxfd = ConnectionNumber(Gdisplay);
+    Gwmdelatom = XInternAtom(Gdisplay, "WM_DELETE_WINDOW", False);
+    Gpopdownflag = FALSE;
+    Glazyq.flag = LAZYUNDEF;
+    Gbufp = Marrayalloc((long) BUFINCR * BUFSIZE);
+    Gbufn = BUFINCR;
+    Gppp = Marrayalloc((long) PPINCR * PPSIZE);
+    Gppn = PPINCR;
+    Gfontp = Marrayalloc((long) FONTSIZE);
+    Gfontn = 1;
+    Gfontp[0].name = strdup("default");
+    if (!Gdefaultfont)
+       Gfontp[0].font = deffont;
+    else if (Gdefaultfont[0] != '\000')
+       Gfontp[0].font = XLoadQueryFont(Gdisplay, Gdefaultfont);
+    else
+       Gfontp[0].font = NULL;
+    return 0;
+}
+
+int Gtermgraphics(void)
+{
+    int fi;
+
+    for (fi = 0; fi < Gfontn; fi++)
+       free(Gfontp[fi].name);
+    Marrayfree(Gfontp), Gfontp = NULL, Gfontn = 0;
+    Marrayfree(Gppp), Gppp = NULL, Gppn = 0;
+    Marrayfree(Gbufp), Gbufp = NULL, Gbufn = 0;
+    XtDestroyWidget(Groot);
+    return 0;
+}
+
+void Gflushlazyq(void)
+{
+    if (Glazyq.flag & LAZYMANAGE) {
+       XtManageChildren(Glazyq.mws, Glazyq.mwn);
+       Glazyq.flag &= ~LAZYMANAGE;
+    }
+    if (Glazyq.flag & LAZYREALIZE) {
+       XtRealizeWidget(Glazyq.rw);
+       if (Glazyq.flag & LAZYRHINTS)
+           XSetWMNormalHints(Gdisplay, XtWindow(Glazyq.rw),
+                             &Glazyq.hints);
+       XSetWMProtocols(Gdisplay, XtWindow(Glazyq.rw), &Gwmdelatom, 1);
+       XtOverrideTranslations(Glazyq.rw, Gwmdeltable);
+       Glazyq.flag &= ~LAZYRHINTS;
+       Glazyq.flag &= ~LAZYREALIZE;
+    }
+}
+
+void Glazyrealize(Widget w, int hintsflag, XSizeHints * hintsp)
+{
+    if (Glazyq.flag & LAZYREALIZE) {
+       XtRealizeWidget(Glazyq.rw);
+       if (Glazyq.flag & LAZYRHINTS)
+           XSetWMNormalHints(Gdisplay, XtWindow(Glazyq.rw),
+                             &Glazyq.hints);
+       XSetWMProtocols(Gdisplay, XtWindow(Glazyq.rw), &Gwmdelatom, 1);
+       XtOverrideTranslations(Glazyq.rw, Gwmdeltable);
+    } else
+       Glazyq.flag |= LAZYREALIZE;
+    Glazyq.rw = w;
+    if (hintsflag) {
+       Glazyq.flag |= LAZYRHINTS;
+       Glazyq.hints = *hintsp;
+    } else
+       Glazyq.flag &= ~LAZYRHINTS;
+}
+
+void Glazymanage(Widget w)
+{
+    if (Glazyq.flag & LAZYMANAGE) {
+       if (XtParent(Glazyq.mws[Glazyq.mwn - 1]) != XtParent(w) ||
+           Glazyq.mwn >= LAZYQNUM) {
+           XtManageChildren(Glazyq.mws, Glazyq.mwn);
+           Glazyq.mwn = 0;
+       }
+    } else {
+       Glazyq.flag |= LAZYMANAGE;
+       Glazyq.mwn = 0;
+    }
+    Glazyq.mws[Glazyq.mwn++] = w;
+}
+
+int Gsync(void)
+{
+    if (Glazyq.flag)
+       Gflushlazyq();
+    XFlush(Gdisplay);
+    return 0;
+}
+
+int Gresetbstate(int wi)
+{
+    Gcw_t *cw;
+    int bn;
+
+    cw = Gwidgets[wi].u.c;
+    bn = cw->bstate[0] + cw->bstate[1] + cw->bstate[2];
+    cw->bstate[0] = cw->bstate[1] = cw->bstate[2] = 0;
+    cw->buttonsdown -= bn;
+    Gbuttonsdown -= bn;
+    return 0;
+}
+
+int Gprocessevents(int waitflag, Geventmode_t mode)
+{
+    int rtn;
+
+    if (Glazyq.flag)
+       Gflushlazyq();
+    rtn = 0;
+    switch (waitflag) {
+    case TRUE:
+       XtAppProcessEvent(appcontext, XtIMAll);
+       if (mode == G_ONEEVENT)
+           return 1;
+       rtn = 1;
+       /* FALL THROUGH */
+    case FALSE:
+       while (XtAppPending(appcontext)) {
+           XtAppProcessEvent(appcontext, XtIMAll);
+           if (mode == G_ONEEVENT)
+               return 1;
+           rtn = 1;
+       }
+       break;
+    }
+    return rtn;
+}
diff --git a/cmd/lefty/ws/x11/gcommon.h b/cmd/lefty/ws/x11/gcommon.h
new file mode 100644 (file)
index 0000000..5683b39
--- /dev/null
@@ -0,0 +1,212 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _GCOMMON_H
+#define _GCOMMON_H
+#if XlibSpecificationRelease < 5
+    typedef char *XPointer;
+#endif
+
+/* point and rect structures */
+    typedef XPoint PIXxy_t;
+    typedef PIXxy_t PIXpoint_t;
+    typedef PIXxy_t PIXsize_t;
+    typedef struct PIXrect_t {
+       PIXxy_t o, c;
+    } PIXrect_t;
+
+    extern Widget Groot;
+    extern Display *Gdisplay;
+    extern int Gpopdownflag;
+    extern int Gscreenn;
+    extern int Gdepth;
+
+    extern Arg argp[];
+    extern int argn;
+#define MAXARGS 50
+#define RESETARGS (argn = 0)
+#define ADD2ARGS(var, val) \
+    XtSetArg (argp[argn], (var), (val)), argn++
+
+/* structures used to minimize number of calls to
+   XtManage and XtRealize functions (which are expensive) */
+    typedef enum {
+       LAZYUNDEF = 0, LAZYREALIZE = 1, LAZYRHINTS = 2, LAZYMANAGE = 4
+    } Glazyflag_t;
+#define LAZYQNUM 100
+    typedef struct Glazyq_t {
+       Glazyflag_t flag;
+       Widget rw;
+       XSizeHints hints;
+       Widget mws[LAZYQNUM];
+       int mwn;
+    } Glazyq_t;
+    extern Glazyq_t Glazyq;
+
+    typedef struct Gfont_t {
+       char *name;
+       XFontStruct *font;
+    } Gfont_t;
+    extern Gfont_t *Gfontp;
+    extern int Gfontn;
+#define FONTSIZE sizeof (Gfont_t)
+#define SETFONT(font) { \
+    XGCValues gcv; \
+    if (font != WCU->font) { \
+        WCU->font = font, gcv.font = font->fid; \
+        XChangeGC (Gdisplay, GC, GCFont, &gcv); \
+    } \
+}
+
+    extern char *Gbufp;
+    extern int Gbufn, Gbufi;
+#define BUFINCR 1024
+#define BUFSIZE sizeof (char)
+
+    extern PIXpoint_t *Gppp;
+    extern int Gppn, Gppi;
+#define PPINCR 100
+#define PPSIZE sizeof (PIXpoint_t)
+
+#define GETSIZE(sin, sout, smin) \
+    sout.x = (sin.x > smin) ? sin.x + 0.5 : smin, \
+    sout.y = (sin.y > smin) ? sin.y + 0.5 : smin
+#define GETORIGIN(oin, oout) \
+    oout.x = oin.x + 0.5, oout.y = oin.y + 0.5
+
+    int Ginitgraphics(void);
+    int Gtermgraphics(void);
+    void Gflushlazyq(void);
+    void Glazyrealize(Widget, int, XSizeHints *);
+    void Glazymanage(Widget);
+    int Gsync(void);
+
+    int GAcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GAsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GAgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GAdestroywidget(Gwidget_t *);
+
+    int GBcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GBsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GBgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GBdestroywidget(Gwidget_t *);
+
+    int GCcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GCsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GCgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GCdestroywidget(Gwidget_t *);
+    int GCcanvasclear(Gwidget_t *);
+    int GCsetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GCgetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GCarrow(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GCline(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GCbox(Gwidget_t *, Grect_t, Ggattr_t *);
+    int GCpolygon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GCsplinegon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GCarc(Gwidget_t *, Gpoint_t, Gsize_t, double, double, Ggattr_t *);
+    int GCtext(Gwidget_t *, Gtextline_t *, int, Gpoint_t,
+              char *, double, char *, Ggattr_t *);
+    int GCgettextsize(Gwidget_t *, Gtextline_t *, int, char *, double,
+                     Gsize_t *);
+    int GCcreatebitmap(Gwidget_t *, Gbitmap_t *, Gsize_t);
+    int GCdestroybitmap(Gbitmap_t *);
+    int GCreadbitmap(Gwidget_t *, Gbitmap_t *, FILE *);
+    int GCwritebitmap(Gbitmap_t *, FILE *);
+    int GCbitblt(Gwidget_t *, Gpoint_t, Grect_t, Gbitmap_t *, char *,
+                Ggattr_t *);
+    int GCgetmousecoords(Gwidget_t *, Gpoint_t *, int *);
+
+    int GLcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GLsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GLgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GLdestroywidget(Gwidget_t *);
+
+    int GMcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GMsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GMgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GMdestroywidget(Gwidget_t *);
+    int GMmenuaddentries(Gwidget_t *, int, char **);
+    int GMmenudisplay(Gwidget_t *, Gwidget_t *);
+
+    int GPcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GPsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GPgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GPdestroywidget(Gwidget_t *);
+    int GPcanvasclear(Gwidget_t *);
+    int GPsetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GPgetgfxattr(Gwidget_t *, Ggattr_t *);
+    int GParrow(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GPline(Gwidget_t *, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int GPbox(Gwidget_t *, Grect_t, Ggattr_t *);
+    int GPpolygon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GPsplinegon(Gwidget_t *, int, Gpoint_t *, Ggattr_t *);
+    int GParc(Gwidget_t *, Gpoint_t, Gsize_t, double, double, Ggattr_t *);
+    int GPtext(Gwidget_t *, Gtextline_t *, int, Gpoint_t,
+              char *, double, char *, Ggattr_t *);
+    int GPcreatebitmap(Gwidget_t *, Gbitmap_t *, Gsize_t);
+    int GPdestroybitmap(Gbitmap_t *);
+    int GPreadbitmap(Gwidget_t *, Gbitmap_t *, FILE *);
+    int GPwritebitmap(Gbitmap_t *, FILE *);
+    int GPbitblt(Gwidget_t *, Gpoint_t, Grect_t, Gbitmap_t *, char *,
+                Ggattr_t *);
+
+    int GQcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GQsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GQgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GQdestroywidget(Gwidget_t *);
+    int GQqueryask(Gwidget_t *, char *, char *, char *, int);
+
+    int GScreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GSsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GSgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GSdestroywidget(Gwidget_t *);
+
+    int GTcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GTsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GTgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GTdestroywidget(Gwidget_t *);
+
+    int GVcreatewidget(Gwidget_t *, Gwidget_t *, int, Gwattr_t *);
+    int GVsetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GVgetwidgetattr(Gwidget_t *, int, Gwattr_t *);
+    int GVdestroywidget(Gwidget_t *);
+
+    void Gcwbutaction(Widget, XEvent *, char **, unsigned int *);
+    void Gcwkeyaction(Widget, XEvent *, char **, unsigned int *);
+    void Glwbutaction(Widget, XEvent *, char **, unsigned int *);
+    void Glwkeyaction(Widget, XEvent *, char **, unsigned int *);
+    void Gtweolaction(Widget, XEvent *, char **, unsigned int *);
+    void Gqwpopaction(Widget, XEvent *, char **, unsigned int *);
+    void Gwmdelaction(Widget, XEvent *, char **, unsigned int *);
+    extern XtTranslations Gtweoltable;
+    extern XtTranslations Gqwpoptable;
+    extern XtTranslations Glwanytable;
+    extern XtTranslations Gcwanytable;
+    extern XtTranslations Gwmdeltable;
+
+    extern Atom Gwmdelatom;
+
+#endif                         /* _GCOMMON_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/ws/x11/glabel.c b/cmd/lefty/ws/x11/glabel.c
new file mode 100644 (file)
index 0000000..bff2885
--- /dev/null
@@ -0,0 +1,259 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+#define WLU widget->u.l
+
+int GLcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    int ai;
+    XColor c;
+    int color;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    WLU->func = NULL;
+    ps.x = ps.y = MINLWSIZE;
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINLWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRTEXT:
+           ADD2ARGS(XtNlabel, attrp[ai].u.t);
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WLU->func = (Glabelcb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    ADD2ARGS(XtNwidth, ps.x);
+    ADD2ARGS(XtNheight, ps.y);
+    ADD2ARGS(XtNhighlightThickness, 0);
+    ADD2ARGS(XtNinternalHeight, 0);
+    ADD2ARGS(XtNinternalWidth, 0);
+    ADD2ARGS(XtNjustify, XtJustifyLeft);
+    if (!(widget->w = XtCreateWidget("label", labelWidgetClass,
+                                    parent->w, argp, argn))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    XtOverrideTranslations(widget->w, Glwanytable);
+    Glazymanage(widget->w);
+    return 0;
+}
+
+int GLsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    int ai;
+    XColor c;
+    int color;
+
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINLWSIZE);
+           ADD2ARGS(XtNwidth, ps.x);
+           ADD2ARGS(XtNheight, ps.y);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRTEXT:
+           ADD2ARGS(XtNlabel, attrp[ai].u.t);
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WLU->func = (Glabelcb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    XtSetValues(widget->w, argp, argn);
+    return 0;
+}
+
+int GLgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Dimension width, height;
+    char *s;
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       RESETARGS;
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           ADD2ARGS(XtNwidth, &width);
+           ADD2ARGS(XtNheight, &height);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.s.x = width, attrp[ai].u.s.y = height;
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, &width);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.i = width;
+           break;
+       case G_ATTRTEXT:
+           ADD2ARGS(XtNlabel, &s);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.t = s;
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", XtWindow(widget->w));
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTREVENTCB:
+           attrp[ai].u.func = (void *) (WLU->func);
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GLdestroywidget(Gwidget_t * widget)
+{
+    XtDestroyWidget(widget->w);
+    return 0;
+}
+
+void Glwbutaction(Widget w, XEvent * evp, char **app, unsigned int *anp)
+{
+    Gwidget_t *widget;
+    Gevent_t gev;
+    int wi, xtype;
+
+    widget = findwidget((unsigned long) w, G_LABELWIDGET);
+    switch ((xtype = evp->type)) {
+    case ButtonPress:
+    case ButtonRelease:
+       gev.type = G_MOUSE;
+       gev.code = (xtype == ButtonPress) ? G_DOWN : G_UP;
+       gev.data = evp->xbutton.button - Button1;
+       break;
+    default:
+       return;
+    }
+    wi = gev.wi = widget - &Gwidgets[0];
+    if (WLU->func)
+       (*WLU->func) (&gev);
+    if (Gpopdownflag) {
+       Gpopdownflag = FALSE;
+       if (gev.code == G_DOWN) {
+           gev.code = G_UP;
+           widget = &Gwidgets[wi];
+           if (widget->inuse && WLU->func)
+               (*WLU->func) (&gev);
+       }
+    }
+}
+
+void Glwkeyaction(Widget w, XEvent * evp, char **app, unsigned int *anp)
+{
+    Gwidget_t *widget;
+    Gevent_t gev;
+    int xtype;
+    char c;
+
+    widget = findwidget((unsigned long) w, G_LABELWIDGET);
+    switch ((xtype = evp->type)) {
+    case KeyPress:
+    case KeyRelease:
+       if (!XLookupString((XKeyEvent *) evp, &c, 1, NULL, NULL))
+           return;
+       gev.type = G_KEYBD;
+       gev.code = (xtype == KeyPress) ? G_DOWN : G_UP;
+       gev.data = c;
+       break;
+    default:
+       return;
+    }
+    gev.wi = widget - &Gwidgets[0];
+    if (WLU->func)
+       (*WLU->func) (&gev);
+    if (Gpopdownflag)
+       Gpopdownflag = FALSE;
+}
diff --git a/cmd/lefty/ws/x11/gmenu.c b/cmd/lefty/ws/x11/gmenu.c
new file mode 100644 (file)
index 0000000..b1d533f
--- /dev/null
@@ -0,0 +1,151 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+#define WMU widget->u.m
+
+static int menupoped;
+static int menuselected;
+
+static void mwcallback(Widget, XtPointer, XtPointer);
+
+int GMcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    if (!(widget->w = XtCreatePopupShell("menu",
+                                        simpleMenuWidgetClass, Groot,
+                                        NULL, 0))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    XtAddCallback(widget->w, XtNpopdownCallback, mwcallback,
+                 (XtPointer) - 1);
+    WMU->count = 0;
+    return 0;
+}
+
+int GMsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GMgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GMdestroywidget(Gwidget_t * widget)
+{
+    XtDestroyWidget(widget->w);
+    return 0;
+}
+
+int GMmenuaddentries(Gwidget_t * widget, int en, char **ep)
+{
+    Widget mep;
+    int ei;
+
+    for (ei = 0; ei < en; ei++) {
+       mep = XtCreateManagedWidget(ep[ei], smeBSBObjectClass,
+                                   widget->w, NULL, 0);
+       XtAddCallback(mep, XtNcallback, mwcallback,
+                     (XtPointer) WMU->count++);
+    }
+    return 0;
+}
+
+int GMmenudisplay(Gwidget_t * parent, Gwidget_t * widget)
+{
+    Window rwin, cwin;
+    Dimension width, height;
+    int rx, ry, x, y;
+    unsigned int mask;
+
+    XQueryPointer(Gdisplay, XtWindow(parent->w),
+                 &rwin, &cwin, &rx, &ry, &x, &y, &mask);
+    XtRealizeWidget(widget->w);
+    RESETARGS;
+    ADD2ARGS(XtNwidth, &width);
+    ADD2ARGS(XtNheight, &height);
+    XtGetValues(widget->w, argp, argn);
+    if (rx + width > DisplayWidth(Gdisplay, Gscreenn))
+       rx = DisplayWidth(Gdisplay, Gscreenn) - width;
+    if (ry + height > DisplayHeight(Gdisplay, Gscreenn))
+       ry = DisplayHeight(Gdisplay, Gscreenn) - height;
+    if (rx < 0)
+       rx = 0;
+    if (ry < 0)
+       ry = 0;
+    RESETARGS;
+    ADD2ARGS(XtNx, rx);
+    ADD2ARGS(XtNy, ry);
+    XtSetValues(widget->w, argp, argn);
+    menupoped = TRUE;
+    menuselected = -1;
+    XtPopupSpringLoaded(widget->w);
+    while (menupoped)
+       Gprocessevents(TRUE, G_ONEEVENT);
+    Gpopdownflag = TRUE;
+    return menuselected;
+}
+
+static void mwcallback(Widget w, XtPointer clientdata, XtPointer calldata)
+{
+    if (((long) clientdata) > -1)
+       menuselected = (long) clientdata;
+    menupoped = FALSE;
+}
diff --git a/cmd/lefty/ws/x11/gpcanvas.c b/cmd/lefty/ws/x11/gpcanvas.c
new file mode 100644 (file)
index 0000000..328b022
--- /dev/null
@@ -0,0 +1,777 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define PSDPI 300.0
+#define PSMAXPIXW (8.0  * PSDPI)
+#define PSMAXPIXH (10.5 * PSDPI)
+#define PSPTPI 72.0
+#define PSMAXPTW (8.0  * PSPTPI)
+#define PSMAXPTH (10.5 * PSPTPI)
+#define PSXOFF 18
+#define PSYOFF 18
+
+static PIXsize_t maxsize = { PSMAXPIXW, PSMAXPIXH };
+static long count;
+
+#define WPU widget->u.p
+#define FP widget->u.p->fp
+
+#define RED   WPU->colors[WPU->gattr.color].nr
+#define GREEN WPU->colors[WPU->gattr.color].ng
+#define BLUE  WPU->colors[WPU->gattr.color].nb
+
+static char *gstyles[5] = {
+    /* G_SOLID */ "16  0",
+    /* G_DASHED */ " 4  4",
+    /* G_DOTTED */ " 2  2",
+    /* G_LONGDASHED */ " 4 12",
+    /* G_SHORTDASHED */ "12  4",
+};
+
+char *Gpscanvasname = "out.ps";
+
+static char *findfont(char *);
+static void setgattr(Gwidget_t *, Ggattr_t *);
+
+static PIXrect_t rdrawtopix(Gwidget_t *, Grect_t);
+static PIXpoint_t pdrawtopix(Gwidget_t *, Gpoint_t);
+static PIXsize_t sdrawtopix(Gwidget_t *, Gsize_t);
+static PIXpoint_t pdrawtobpix(Gbitmap_t *, Gpoint_t);
+
+int GPcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXpoint_t po;
+    PIXsize_t ps;
+    struct Gpwcolor_t *cp;
+    FILE *pfp;
+    char buf[120];
+    char *s, *path;
+    int color, lflag, ai, i, x, y, w, h, r, g, b;
+
+    if (!(path = buildpath("lefty.psp", 0)) || !(pfp = fopen(path, "r"))) {
+       Gerr(POS, G_ERRCANNOTOPENFILE, "lefty.psp");
+       return -1;
+    }
+    s = Gpscanvasname;
+    lflag = FALSE;
+    po.x = po.y = 0;
+    ps.x = ps.y = MINPWSIZE;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           GETORIGIN(attrp[ai].u.p, po);
+           break;
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINPWSIZE);
+           break;
+       case G_ATTRNAME:
+           if (attrp[ai].u.t && attrp[ai].u.t[0])
+               s = attrp[ai].u.t;
+           break;
+       case G_ATTRMODE:
+           if (Strcmp("landscape", attrp[ai].u.t) == 0)
+               lflag = TRUE;
+           else if (Strcmp("portrait", attrp[ai].u.t) == 0)
+               lflag = FALSE;
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRCOLOR:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRVIEWPORT:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRWINDOW:
+           /* will do it after the widget is created */
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    if (!(FP = fopen(s, "w"))) {
+       Gerr(POS, G_ERRCANNOTOPENFILE, s);
+       return -1;
+    }
+    WPU->colors[0].r = WPU->colors[0].g = WPU->colors[0].b = 255;
+    WPU->colors[0].nr = WPU->colors[0].ng = WPU->colors[0].nb = 1.0;
+    WPU->colors[0].inuse = TRUE;
+    WPU->colors[1].r = WPU->colors[1].g = WPU->colors[1].b = 0;
+    WPU->colors[1].nr = WPU->colors[1].ng = WPU->colors[1].nb = 0.0;
+    WPU->colors[1].inuse = TRUE;
+    for (i = 2; i < G_MAXCOLORS; i++)
+       WPU->colors[i].inuse = FALSE;
+    WPU->gattr.color = 1;
+    WPU->gattr.width = 0;
+    WPU->gattr.mode = -1;
+    WPU->gattr.fill = 0;
+    WPU->gattr.style = 0;
+    WPU->wrect.o.x = 0.0, WPU->wrect.o.y = 0.0;
+    WPU->wrect.c.x = 1.0, WPU->wrect.c.y = 1.0;
+    WPU->vsize.x = ps.x, WPU->vsize.y = ps.y;
+    if (lflag)
+       x = po.y, y = po.x, w = ps.y, h = ps.x;
+    else
+       x = po.x, y = po.y, w = ps.x, h = ps.y;
+    fprintf(FP, "%%! LEFTY Output\n");
+    fprintf(FP, "%%%%BoundingBox: %d %d %d %d\n",
+           (int) (PSXOFF + PSMAXPTW * x / maxsize.x + 0.5),
+           (int) (PSYOFF + PSMAXPTH * y / maxsize.y + 0.5),
+           (int) (PSXOFF + PSMAXPTW * w / maxsize.x + 0.5),
+           (int) (PSYOFF + PSMAXPTH * h / maxsize.y + 0.5));
+    fprintf(FP, "%%%%EndComments\n");
+    while (fgets(buf, 120, pfp))
+       fputs(&buf[0], FP);
+    fclose(pfp);
+    fprintf(FP, "/ICP { %d %d %d %d SCP } def\n",
+           (int) po.x, (int) po.y,
+           (int) (po.x + ps.x - 1), (int) (po.y + ps.y - 1));
+    fprintf(FP, "/BB { %d %d %d %d } def\n",
+           (int) po.x, (int) po.y,
+           (int) (po.x + ps.x - 1), (int) (po.y + ps.y - 1));
+    if (!lflag)
+       fprintf(FP, "[%f 0 0 %f %d %d] concat\n",
+               PSPTPI / PSDPI, PSPTPI / PSDPI, PSXOFF, PSYOFF);
+    else
+       fprintf(FP, "[0 %f %f 0 %d %d] concat\n",
+               PSPTPI / PSDPI, -PSPTPI / PSDPI,
+               (int) (PSXOFF + ps.y * PSPTPI / PSDPI), PSYOFF);
+    fprintf(FP, "%d %d translate ICP\n", (int) po.x, (int) po.y);
+    fprintf(FP, "1 setlinecap\n");
+    fprintf(FP, "ICP\n");
+    WPU->gattr.color = 1;
+    fprintf(FP, "%f %f %f CL\n", RED, GREEN, BLUE);
+    WPU->defgattr = WPU->gattr;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, attrp[ai].u.c.index);
+               return -1;
+           }
+           cp = &WPU->colors[color];
+           r = attrp[ai].u.c.r;
+           g = attrp[ai].u.c.g;
+           b = attrp[ai].u.c.b;
+           cp->r = r, cp->g = g, cp->b = b;
+           cp->nr = r / 256.0, cp->ng = g / 256.0, cp->nb = b / 256.0;
+           cp->inuse = TRUE;
+           break;
+       case G_ATTRVIEWPORT:
+           WPU->vsize.x = (int) (attrp[ai].u.s.x + 0.5);
+           WPU->vsize.y = (int) (attrp[ai].u.s.y + 0.5);
+           fprintf(FP, "0 0 %d %d SCP\n",
+                   (int) WPU->vsize.x, (int) WPU->vsize.y);
+           break;
+       case G_ATTRWINDOW:
+           WPU->wrect = attrp[ai].u.r;
+           break;
+       }
+    }
+    return 0;
+}
+
+int GPsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    struct Gpwcolor_t *cp;
+    int color, ai, r, g, b;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           break;
+       case G_ATTRSIZE:
+           break;
+       case G_ATTRNAME:
+           break;
+       case G_ATTRMODE:
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS) {
+               Gerr(POS, G_ERRBADCOLORINDEX, attrp[ai].u.c.index);
+               return -1;
+           }
+           cp = &WPU->colors[color];
+           r = attrp[ai].u.c.r;
+           g = attrp[ai].u.c.g;
+           b = attrp[ai].u.c.b;
+           cp->r = r, cp->g = g, cp->b = b;
+           cp->nr = r / 256.0, cp->ng = g / 256.0, cp->nb = b / 256.0;
+           cp->inuse = TRUE;
+           break;
+       case G_ATTRVIEWPORT:
+           WPU->vsize.x = (int) (attrp[ai].u.s.x + 0.5);
+           WPU->vsize.y = (int) (attrp[ai].u.s.y + 0.5);
+           fprintf(FP, "0 0 %d %d SCP\n",
+                   (int) WPU->vsize.x, (int) WPU->vsize.y);
+           break;
+       case G_ATTRWINDOW:
+           WPU->wrect = attrp[ai].u.r;
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GPgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    struct Gpwcolor_t *cp;
+    int color, ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           break;
+       case G_ATTRSIZE:
+           break;
+       case G_ATTRNAME:
+           break;
+       case G_ATTRMODE:
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color < 0 || color > G_MAXCOLORS
+               || !WPU->colors[color].inuse) {
+               Gerr(POS, G_ERRBADCOLORINDEX, attrp[ai].u.c.index);
+               return -1;
+           }
+           cp = &WPU->colors[color];
+           attrp[ai].u.c.r = cp->r;
+           attrp[ai].u.c.g = cp->g;
+           attrp[ai].u.c.b = cp->b;
+           break;
+       case G_ATTRVIEWPORT:
+           attrp[ai].u.s = WPU->vsize;
+           break;
+       case G_ATTRWINDOW:
+           attrp[ai].u.r = WPU->wrect;
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTGETATTR, "windowid");
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GPdestroywidget(Gwidget_t * widget)
+{
+    fprintf(FP, "stroke showpage\n");
+    fclose(FP);
+    return 0;
+}
+
+int GPcanvasclear(Gwidget_t * widget)
+{
+    fprintf(FP, "ICP DO\n");
+    WPU->gattr.color = 0;
+    fprintf(FP, "%f %f %f CL\n", RED, GREEN, BLUE);
+    fprintf(FP, "BB BOX FI\n");
+    WPU->gattr.color = -1;
+    fprintf(FP, "0 0 %d %d SCP\n", (int) WPU->vsize.x, (int) WPU->vsize.y);
+    return 0;
+}
+
+int GPsetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    setgattr(widget, ap);
+    WPU->defgattr = WPU->gattr;
+    return 0;
+}
+
+int GPgetgfxattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    if ((ap->flags & G_GATTRCOLOR))
+       ap->color = WPU->gattr.color;
+    if ((ap->flags & G_GATTRWIDTH))
+       ap->width = WPU->gattr.width;
+    if ((ap->flags & G_GATTRMODE))
+       ap->mode = WPU->gattr.mode;
+    if ((ap->flags & G_GATTRFILL))
+       ap->fill = WPU->gattr.fill;
+    if ((ap->flags & G_GATTRSTYLE))
+       ap->style = WPU->gattr.style;
+    return 0;
+}
+
+int GParrow(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    PIXpoint_t pp1, pp2, pa, pb, pd;
+    double tangent;
+    int l;
+
+    pp1 = pdrawtopix(widget, gp1), pp2 = pdrawtopix(widget, gp2);
+    pd.x = pp1.x - pp2.x, pd.y = pp1.y - pp2.y;
+    if (pd.x == 0 && pd.y == 0)
+       return 0;
+    tangent = atan2((double) pd.y, (double) pd.x);
+    if ((l = sqrt((double) (pd.x * pd.x + pd.y * pd.y))) < 30)
+       l = 30;
+    pa.x = l * cos(tangent + M_PI / 7) + pp2.x;
+    pa.y = l * sin(tangent + M_PI / 7) + pp2.y;
+    pb.x = l * cos(tangent - M_PI / 7) + pp2.x;
+    pb.y = l * sin(tangent - M_PI / 7) + pp2.y;
+    setgattr(widget, ap);
+    fprintf(FP, "%d %d %d %d LI\n",
+           (int) pp2.x, (int) pp2.y, (int) pp1.x, (int) pp1.y);
+    fprintf(FP, "%d %d %d %d LI\n",
+           (int) pp2.x, (int) pp2.y, (int) pa.x, (int) pa.y);
+    fprintf(FP, "%d %d %d %d LI\n",
+           (int) pp2.x, (int) pp2.y, (int) pb.x, (int) pb.y);
+    return 0;
+}
+
+int GPline(Gwidget_t * widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    PIXpoint_t pp1, pp2;
+
+    pp1 = pdrawtopix(widget, gp1), pp2 = pdrawtopix(widget, gp2);
+    if (count++ == 100)
+       count = 0, fprintf(FP, "DO\n");
+    setgattr(widget, ap);
+    fprintf(FP, "%d %d %d %d LI\n",
+           (int) pp2.x, (int) pp2.y, (int) pp1.x, (int) pp1.y);
+    return 0;
+}
+
+int GPbox(Gwidget_t * widget, Grect_t gr, Ggattr_t * ap)
+{
+    PIXrect_t pr;
+
+    pr = rdrawtopix(widget, gr);
+    setgattr(widget, ap);
+    if (WPU->gattr.fill)
+       fprintf(FP, "DO %d %d %d %d BOX FI\n",
+               (int) pr.o.x, (int) pr.o.y, (int) pr.c.x, (int) pr.c.y);
+    else
+       fprintf(FP, "DO %d %d %d %d BOX DO\n",
+               (int) pr.o.x, (int) pr.o.y, (int) pr.c.x, (int) pr.c.y);
+    return 0;
+}
+
+int GPpolygon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    PIXpoint_t pp;
+    int i;
+
+    if (gpn == 0)
+       return 0;
+
+    pp = pdrawtopix(widget, gpp[0]);
+    setgattr(widget, ap);
+    fprintf(FP, "DO %d %d moveto\n", (int) pp.x, (int) pp.y);
+    for (i = 1; i < gpn; i++) {
+       pp = pdrawtopix(widget, gpp[i]);
+       fprintf(FP, "%d %d lineto\n", (int) pp.x, (int) pp.y);
+    }
+    if (WPU->gattr.fill)
+       fprintf(FP, "FI\n");
+    else
+       fprintf(FP, "DO\n");
+    return 0;
+}
+
+int GPsplinegon(Gwidget_t * widget, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    PIXpoint_t p0, p1, p2, p3;
+    int i;
+
+    if (gpn == 0)
+       return 0;
+
+    p0 = pdrawtopix(widget, gpp[0]);
+    setgattr(widget, ap);
+    fprintf(FP, "DO %d %d moveto\n", (int) p0.x, (int) p0.y);
+    for (i = 1; i < gpn; i += 3) {
+       p1 = pdrawtopix(widget, gpp[i]);
+       p2 = pdrawtopix(widget, gpp[i + 1]);
+       p3 = pdrawtopix(widget, gpp[i + 2]);
+       fprintf(FP, "%d %d %d %d %d %d CT\n", (int) p1.x, (int) p1.y,
+               (int) p2.x, (int) p2.y, (int) p3.x, (int) p3.y);
+    }
+    if (WPU->gattr.fill)
+       fprintf(FP, "FI\n");
+    else
+       fprintf(FP, "DO\n");
+    return 0;
+}
+
+int GParc(Gwidget_t * widget, Gpoint_t gc, Gsize_t gs,
+         double ang1, double ang2, Ggattr_t * ap)
+{
+    PIXpoint_t pc;
+    PIXsize_t ps;
+
+    pc = pdrawtopix(widget, gc), ps = sdrawtopix(widget, gs);
+    setgattr(widget, ap);
+    if (WPU->gattr.fill)
+       fprintf(FP, "DO %d %d %f %d %f %f ARF\n",
+               pc.x, pc.y, (double) ps.y / (double) ps.x, ps.x, ang1,
+               ang2);
+    else
+       fprintf(FP, "DO %d %d %f %d %f %f AR\n",
+               pc.x, pc.y, (double) ps.y / (double) ps.x, ps.x, ang1,
+               ang2);
+    return 0;
+}
+
+int GPtext(Gwidget_t * widget, Gtextline_t * tlp, int n, Gpoint_t go,
+          char *fn, double fs, char *justs, Ggattr_t * ap)
+{
+    Gsize_t gs;
+    PIXpoint_t po;
+    PIXsize_t ps;
+    char *font;
+    char c, *p;
+    int i;
+
+    po = pdrawtopix(widget, go);
+    gs.x = 0, gs.y = fs;
+    ps = sdrawtopix(widget, gs);
+    font = findfont(fn);
+    setgattr(widget, ap);
+    fprintf(FP, "DO %d (%c) %d (%c) [",
+           (int) po.x, justs[0], (int) po.y, justs[1]);
+    for (i = 0; i < n; i++) {
+       c = tlp[i].p[tlp[i].n], tlp[i].p[tlp[i].n] = '\000';
+       fprintf(FP, "[ (");
+       for (p = tlp[i].p; *p; p++) {   /* generate PS escapes as needed */
+           if ((*p == '(') || (*p == ')') || (*p == '\\'))
+               fputc('\\', FP);
+           fputc(*p, FP);
+       }
+       fprintf(FP, ") (%c) ] ", tlp[i].j);
+       tlp[i].p[tlp[i].n] = c;
+    }
+    fprintf(FP, "] %d /%s %d TXT\n", n, font, (int) ps.y);
+    return 0;
+}
+
+static char *findfont(char *name)
+{
+    char *font;
+
+    if (name[0] == '\000' || Strcmp(name, "default") == 0)
+       font = "Times-Roman";
+    else
+       font = name;
+    return font;
+}
+
+int GPcreatebitmap(Gwidget_t * widget, Gbitmap_t * bitmap, Gsize_t s)
+{
+    if (!widget) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    bitmap->u.bits = Marrayalloc((long) ((int) s.x * (int) s.y * 3));
+    bitmap->ctype = widget->type;
+    bitmap->canvas = widget - &Gwidgets[0];
+    bitmap->size = s;
+    return 0;
+}
+
+int GPdestroybitmap(Gbitmap_t * bitmap)
+{
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    Marrayfree(bitmap->u.bits);
+    return 0;
+}
+
+int GPreadbitmap(Gwidget_t * widget, Gbitmap_t * bitmap, FILE * fp)
+{
+    Gsize_t s = { 0, 0 };
+    char bufp[2048];
+    char *s1, *s2;
+    char c;
+    int bufn, bufi, step, x, y, k;
+
+    if (!widget) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    if (!bitmap) {
+       Gerr(POS, G_ERRNOBITMAP);
+       return -1;
+    }
+    step = 0;
+    while (step < 3) {
+      l1:
+       if (!fgets(bufp, 2048, fp)) {
+           Gerr(POS, G_ERRCANNOTREADBITMAP);
+           return -1;
+       }
+       s1 = &bufp[0];
+      l2:
+       for (; *s1 && isspace(*s1); s1++);
+       if (!*s1 || *s1 == '#')
+           goto l1;
+       switch (step) {
+       case 0:
+           if (strncmp(s1, "P6", 2) != 0) {
+               Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           step++, s1 += 2;
+           goto l2;
+       case 1:
+           for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++);
+           c = *s2, *s2 = 0;
+           if (s2 == s1 || (s.x = atoi(s1)) <= 0) {
+               *s2 = c, Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           *s2 = c, step++, s1 = s2;
+           goto l2;
+       case 2:
+           for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++);
+           c = *s2, *s2 = 0;
+           if (s2 == s1 || (s.y = atoi(s1)) <= 0) {
+               *s2 = c, Gerr(POS, G_ERRCANNOTREADBITMAP);
+               return -1;
+           }
+           *s2 = c, step++, s1 = s2;
+           goto l2;
+       }
+    }
+    bitmap->u.bits = Marrayalloc((long) ((int) s.x * (int) s.y * 3));
+    bitmap->ctype = widget->type;
+    bitmap->canvas = widget - &Gwidgets[0];
+    bitmap->size = s;
+    bufi = bufn = 0;
+    bufp[bufi] = 0;
+    s1 = (char *) bitmap->u.bits;
+    for (y = 0; y < s.y; y++) {
+       for (x = 0; x < s.x; x++) {
+           for (k = 0; k < 3; k++) {
+               if (bufi == bufn) {
+                   if ((bufn = fread(bufp, 1, 2047, fp)) == 0) {
+                       Marrayfree(bitmap->u.bits);
+                       Gerr(POS, G_ERRCANNOTCREATEBITMAP);
+                       return -1;
+                   }
+                   bufi = 0;
+               }
+               *s1++ = bufp[bufi++];
+           }
+       }
+    }
+    return 0;
+}
+
+int GPwritebitmap(Gbitmap_t * bitmap, FILE * fp)
+{
+    return -1;
+}
+
+int GPbitblt(Gwidget_t * widget, Gpoint_t gp, Grect_t gr,
+            Gbitmap_t * bitmap, char *mode, Ggattr_t * ap)
+{
+    PIXrect_t pr, br;
+    PIXpoint_t pp;
+    PIXsize_t bs;
+    Gsize_t scale;
+    Gxy_t p;
+    int x, y, hi, lo;
+    double tvx, tvy, twx, twy;
+    char *s;
+
+    if (gr.o.x > gr.c.x)
+       p.x = gr.o.x, gr.o.x = gr.c.x, gr.c.x = p.x;
+    if (gr.o.y > gr.c.y)
+       p.y = gr.o.y, gr.o.y = gr.c.y, gr.c.y = p.y;
+    tvx = WPU->vsize.x, tvy = WPU->vsize.y;
+    twx = WPU->wrect.c.x - WPU->wrect.o.x;
+    twy = WPU->wrect.c.y - WPU->wrect.o.y;
+    scale.x = tvx / twx, scale.y = tvy / twy;
+    pr = rdrawtopix(widget, gr);
+    pp = pdrawtobpix(bitmap, gp);
+    bs.x = (pr.c.x - pr.o.x + 1) / scale.x;
+    bs.y = (pr.c.y - pr.o.y + 1) / scale.y;
+    br.o.x = pp.x, br.o.y = pp.y - bs.y + 1;
+    br.c.x = br.o.x + bs.x - 1, br.c.y = br.o.y + bs.y - 1;
+    if (br.o.x < 0)
+       pr.o.x -= br.o.x * scale.x, br.o.x = 0;
+    if (br.o.y < 0)
+       pr.o.y -= br.o.y * scale.y, br.o.y = 0;
+    if (br.c.x >= bitmap->size.x) {
+       pr.c.x -= (br.c.x + 1 - bitmap->size.x) * scale.x;
+       br.c.x = bitmap->size.x - 1;
+    }
+    if (br.c.y >= bitmap->size.y) {
+       pr.c.y -= (br.c.y + 1 - bitmap->size.y) * scale.y;
+       br.c.y = bitmap->size.y - 1;
+    }
+    if (pr.o.x < 0)
+       br.o.x -= pr.o.x / scale.x, pr.o.x = 0;
+    if (pr.o.y < 0)
+       br.o.y -= pr.o.y / scale.y, pr.o.y = 0;
+    if (pr.c.x >= tvx)
+       br.c.x -= (pr.c.x + 1 - tvx) / scale.x, pr.c.x = tvx - 1;
+    if (pr.c.y >= tvy)
+       br.c.y -= (pr.c.y + 1 - tvy) / scale.y, pr.c.y = tvy - 1;
+    bs.x = (pr.c.x - pr.o.x + 1) / scale.x;
+    bs.y = (pr.c.y - pr.o.y + 1) / scale.y;
+    setgattr(widget, ap);
+    fprintf(FP, "DO gsave\n");
+    fprintf(FP, "%d %d translate\n", pr.o.x, pr.o.y);
+    fprintf(FP, "%f %f scale\n", scale.x * bs.x, scale.y * bs.y);
+    fprintf(FP, "/mystr %d string def\n", 3 * bs.x);
+    fprintf(FP, "%d %d 8\n", bs.x, bs.y);
+    fprintf(FP, "[%d 0 0 %d 0 %d]\n", bs.x, -bs.y, bs.y);
+    fprintf(FP,
+           "{currentfile mystr readhexstring pop} false 3 colorimage\n");
+    for (y = 0; y < bs.y; y++) {
+       s = (char *) (bitmap->u.bits +
+                     3 * ((int) bitmap->size.x * (br.o.y + y) + br.o.x));
+       for (x = 0; x < bs.x; x++) {
+           hi = (*s >> 4) & 15, lo = *s++ && 15;
+           fprintf(FP, "%x%x", hi, lo);
+           hi = (*s >> 4) & 15, lo = *s++ && 15;
+           fprintf(FP, "%x%x", hi, lo);
+           hi = (*s >> 4) & 15, lo = *s++ && 15;
+           fprintf(FP, "%x%x", hi, lo);
+       }
+       fprintf(FP, "\n");
+    }
+    fprintf(FP, "grestore\nNP\n");
+    return 0;
+}
+
+static void setgattr(Gwidget_t * widget, Ggattr_t * ap)
+{
+    int color, width, style;
+
+    if (!(ap->flags & G_GATTRCOLOR))
+       ap->color = WPU->defgattr.color;
+    if (!(ap->flags & G_GATTRWIDTH))
+       ap->width = WPU->defgattr.width;
+    if (!(ap->flags & G_GATTRFILL))
+       ap->fill = WPU->defgattr.fill;
+    if (!(ap->flags & G_GATTRSTYLE))
+       ap->style = WPU->defgattr.style;
+    color = ap->color;
+    if (color >= G_MAXCOLORS || !(WPU->colors[color].inuse))
+       color = 1;
+    if (color != WPU->gattr.color) {
+       WPU->gattr.color = color;
+       fprintf(FP, "%f %f %f CL\n", RED, GREEN, BLUE);
+    }
+    width = ap->width;
+    if (width != WPU->gattr.width) {
+       WPU->gattr.width = width;
+       fprintf(FP, "DO %d setlinewidth NP\n", width);
+    }
+    WPU->gattr.fill = ap->fill;
+    style = ap->style;
+    if (style != WPU->gattr.style) {
+       WPU->gattr.style = style;
+       fprintf(FP, "DO [ %s ] 0 setdash NP\n", gstyles[style]);
+    }
+}
+
+static PIXrect_t rdrawtopix(Gwidget_t * widget, Grect_t gr)
+{
+    PIXrect_t pr;
+    double tvx, tvy, twx, twy;
+
+    tvx = WPU->vsize.x - 1, tvy = WPU->vsize.y - 1;
+    twx = WPU->wrect.c.x - WPU->wrect.o.x;
+    twy = WPU->wrect.c.y - WPU->wrect.o.y;
+    pr.o.x = tvx * (gr.o.x - WPU->wrect.o.x) / twx + 0.5;
+    pr.o.y = tvy * (gr.o.y - WPU->wrect.o.y) / twy + 0.5;
+    pr.c.x = tvx * (gr.c.x - WPU->wrect.o.x) / twx + 0.5;
+    pr.c.y = tvy * (gr.c.y - WPU->wrect.o.y) / twy + 0.5;
+    return pr;
+}
+
+static PIXpoint_t pdrawtopix(Gwidget_t * widget, Gpoint_t gp)
+{
+    PIXpoint_t pp;
+    double tvx, tvy, twx, twy;
+
+    tvx = WPU->vsize.x - 1, tvy = WPU->vsize.y - 1;
+    twx = WPU->wrect.c.x - WPU->wrect.o.x;
+    twy = WPU->wrect.c.y - WPU->wrect.o.y;
+    pp.x = tvx * (gp.x - WPU->wrect.o.x) / twx + 0.5;
+    pp.y = tvy * (gp.y - WPU->wrect.o.y) / twy + 0.5;
+    return pp;
+}
+
+static PIXsize_t sdrawtopix(Gwidget_t * widget, Gsize_t gs)
+{
+    PIXsize_t ps;
+    double tvx, tvy, twx, twy;
+
+    tvx = WPU->vsize.x - 1, tvy = WPU->vsize.y - 1;
+    twx = WPU->wrect.c.x - WPU->wrect.o.x;
+    twy = WPU->wrect.c.y - WPU->wrect.o.y;
+    ps.x = tvx * (gs.x - 1) / twx + 1.5;
+    ps.y = tvy * (gs.y - 1) / twy + 1.5;
+    return ps;
+}
+
+static PIXpoint_t pdrawtobpix(Gbitmap_t * bitmap, Gpoint_t gp)
+{
+    PIXpoint_t pp;
+    double tvy;
+
+    tvy = bitmap->size.y - 1;
+    pp.x = gp.x + 0.5;
+    pp.y = tvy - gp.y + 0.5;
+    return pp;
+}
diff --git a/cmd/lefty/ws/x11/gquery.c b/cmd/lefty/ws/x11/gquery.c
new file mode 100644 (file)
index 0000000..593c5c9
--- /dev/null
@@ -0,0 +1,335 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+#define WQU widget->u.q
+
+static void qwcallback(Widget, XtPointer, XtPointer);
+static void qbwcallback(Widget, XtPointer, XtPointer);
+
+int GQcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    Widget w;
+    int ai;
+
+    WQU->mode = G_QWSTRING;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRMODE:
+           if (Strcmp("string", attrp[ai].u.t) == 0)
+               WQU->mode = G_QWSTRING;
+           else if (Strcmp("file", attrp[ai].u.t) == 0)
+               WQU->mode = G_QWFILE;
+           else if (Strcmp("choice", attrp[ai].u.t) == 0)
+               WQU->mode = G_QWCHOICE;
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    switch (WQU->mode) {
+    case G_QWSTRING:
+       if (!(widget->w = XtCreatePopupShell("popup",
+                                            transientShellWidgetClass,
+                                            Groot, NULL, 0))) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+       XtAddCallback(widget->w, XtNpopdownCallback, qwcallback, NULL);
+       RESETARGS;
+       ADD2ARGS(XtNlabel, "abcdefghijklmno");
+       ADD2ARGS(XtNvalue, "");
+       if (!(WQU->w = XtCreateManagedWidget("dialog",
+                                            dialogWidgetClass, widget->w,
+                                            argp, argn))) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+       XawDialogAddButton(WQU->w, "Cancel", qbwcallback, (XtPointer) 1);
+       XawDialogAddButton(WQU->w, "OK", qbwcallback, (XtPointer) 2);
+       if (!(w = XtNameToWidget(WQU->w, "value"))) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+       XtOverrideTranslations(w, Gqwpoptable);
+       break;
+    case G_QWFILE:
+       widget->w = 0;
+       break;
+    case G_QWCHOICE:
+       if (!(widget->w = XtCreatePopupShell("popup",
+                                            transientShellWidgetClass,
+                                            Groot, NULL, 0))) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+       XtAddCallback(widget->w, XtNpopdownCallback, qwcallback, NULL);
+       RESETARGS;
+       ADD2ARGS(XtNlabel, "abcdefghijklmno");
+       if (!(WQU->w = XtCreateManagedWidget("dialog",
+                                            dialogWidgetClass, widget->w,
+                                            argp, argn))) {
+           Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+           return -1;
+       }
+       break;
+    }
+    return 0;
+}
+
+int GQsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRMODE:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "mode");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GQgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRMODE:
+           switch (WQU->mode) {
+           case G_QWSTRING:
+               attrp[ai].u.t = "string";
+               break;
+           case G_QWFILE:
+               attrp[ai].u.t = "file";
+               break;
+           case G_QWCHOICE:
+               attrp[ai].u.t = "choice";
+               break;
+           }
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GQdestroywidget(Gwidget_t * widget)
+{
+    switch (WQU->mode) {
+    case G_QWSTRING:
+       XtDestroyWidget(widget->w);
+       break;
+    case G_QWFILE:
+       break;
+    case G_QWCHOICE:
+       XtDestroyWidget(widget->w);
+       break;
+    }
+    return 0;
+}
+
+int XsraSelFile(Widget toplevel, char *prompt, char *ok, char *cancel,
+               char *failed, char *init_path, char *mode,
+               int (*show_entry) (), char *name_return, int name_size);
+
+int GQqueryask(Gwidget_t * widget, char *prompt, char *args,
+              char *responsep, int responsen)
+{
+    Window rwin, cwin;
+    Dimension width, height;
+    int rx, ry, x, y;
+    long i;
+    unsigned int mask;
+    Widget widgets[20];
+    char buttons[20][40];
+    char *s1, *s2;
+    char c;
+
+    switch (WQU->mode) {
+    case G_QWSTRING:
+       RESETARGS;
+       ADD2ARGS(XtNlabel, prompt);
+       ADD2ARGS(XtNvalue, args ? args : "");
+       XtSetValues(WQU->w, argp, argn);
+       XtRealizeWidget(widget->w);
+       XQueryPointer(Gdisplay, XtWindow(widget->w),
+                     &rwin, &cwin, &rx, &ry, &x, &y, &mask);
+       RESETARGS;
+       ADD2ARGS(XtNwidth, &width);
+       ADD2ARGS(XtNheight, &height);
+       XtGetValues(widget->w, argp, argn);
+       rx -= (width / 2), ry -= (height / 2);
+       if (rx + width > DisplayWidth(Gdisplay, Gscreenn))
+           rx = DisplayWidth(Gdisplay, Gscreenn) - width;
+       if (ry + height > DisplayHeight(Gdisplay, Gscreenn))
+           ry = DisplayHeight(Gdisplay, Gscreenn) - height;
+       if (rx < 0)
+           rx = 0;
+       if (ry < 0)
+           ry = 0;
+       RESETARGS;
+       ADD2ARGS(XtNx, rx);
+       ADD2ARGS(XtNy, ry);
+       XtSetValues(widget->w, argp, argn);
+       WQU->state = 2;
+       WQU->button = 0;
+       XtPopup(widget->w, XtGrabExclusive);
+       while (WQU->state) {
+           if (WQU->state == 1) {
+               if (WQU->button != 1) {
+                   strncpy(responsep,
+                           XawDialogGetValueString(WQU->w), responsen);
+               }
+               XtPopdown(widget->w);
+           }
+           Gprocessevents(TRUE, G_ONEEVENT);
+       }
+       XtUnrealizeWidget(widget->w);
+       Gpopdownflag = TRUE;
+       if (WQU->button == 1)
+           return -1;
+       break;
+    case G_QWFILE:
+       if (!XsraSelFile(Groot, prompt, "OK", "Cancel", "FAIL",
+                        (args ? args : "/"), "r", NULL, responsep,
+                        responsen)) {
+           Gpopdownflag = TRUE;
+           return -1;
+       }
+       Gpopdownflag = TRUE;
+       break;
+    case G_QWCHOICE:
+       if (!args)
+           return -1;
+       RESETARGS;
+       ADD2ARGS(XtNlabel, prompt);
+       XtSetValues(WQU->w, argp, argn);
+       for (s1 = args, i = 1; *s1; i++) {
+           s2 = s1;
+           while (*s2 && *s2 != '|')
+               s2++;
+           c = *s2, *s2 = 0;
+           strcpy(buttons[i], s1);
+           widgets[i] = XtCreateManagedWidget(s1, commandWidgetClass,
+                                              WQU->w, NULL, 0);
+           XtAddCallback(widgets[i], XtNcallback, qbwcallback,
+                         (XtPointer) i);
+           *s2 = c;
+           s1 = s2;
+           if (*s1)
+               s1++;
+       }
+       XtRealizeWidget(widget->w);
+       XQueryPointer(Gdisplay, XtWindow(widget->w),
+                     &rwin, &cwin, &rx, &ry, &x, &y, &mask);
+       RESETARGS;
+       ADD2ARGS(XtNwidth, &width);
+       ADD2ARGS(XtNheight, &height);
+       XtGetValues(widget->w, argp, argn);
+       rx -= (width / 2), ry -= (height / 2);
+       if (rx + width > DisplayWidth(Gdisplay, Gscreenn))
+           rx = DisplayWidth(Gdisplay, Gscreenn) - width;
+       if (ry + height > DisplayHeight(Gdisplay, Gscreenn))
+           ry = DisplayHeight(Gdisplay, Gscreenn) - height;
+       if (rx < 0)
+           rx = 0;
+       if (ry < 0)
+           ry = 0;
+       RESETARGS;
+       ADD2ARGS(XtNx, rx);
+       ADD2ARGS(XtNy, ry);
+       XtSetValues(widget->w, argp, argn);
+       WQU->state = 2;
+       WQU->button = 0;
+       XtPopup(widget->w, XtGrabExclusive);
+       while (WQU->state) {
+           if (WQU->state == 1) {
+               if (WQU->button > 0)
+                   strncpy(responsep, buttons[WQU->button], responsen);
+               XtPopdown(widget->w);
+           }
+           Gprocessevents(TRUE, G_ONEEVENT);
+       }
+       XtUnrealizeWidget(widget->w);
+       for (i--; i > 0; i--)
+           XtDestroyWidget(widgets[i]);
+       Gpopdownflag = TRUE;
+       break;
+    }
+    if (responsep[0] && responsep[strlen(responsep) - 1] == '\n')
+       responsep[strlen(responsep) - 1] = 0;
+    return 0;
+}
+
+void Gqwpopaction(Widget w, XEvent * evp, char **app, unsigned int *anp)
+{
+    Gwidget_t *widget;
+    char c;
+
+    if (evp->type == KeyPress || evp->type == KeyRelease)
+       XLookupString((XKeyEvent *) evp, &c, 1, NULL, NULL);
+    if (c != 13)
+       return;
+    widget = findwidget((unsigned long) XtParent(XtParent(w)),
+                       G_QUERYWIDGET);
+    WQU->state = 1;
+}
+
+static void qwcallback(Widget w, XtPointer clientdata, XtPointer calldata)
+{
+    Gwidget_t *widget;
+
+    widget = findwidget((unsigned long) w, G_QUERYWIDGET);
+    WQU->state = 0;
+}
+
+static void qbwcallback(Widget w, XtPointer clientdata, XtPointer calldata)
+{
+    Gwidget_t *widget;
+
+    widget = findwidget((unsigned long) XtParent(XtParent(w)),
+                       G_QUERYWIDGET);
+    WQU->button = (long) clientdata;
+    WQU->state = 1;
+}
diff --git a/cmd/lefty/ws/x11/gscroll.c b/cmd/lefty/ws/x11/gscroll.c
new file mode 100644 (file)
index 0000000..f736577
--- /dev/null
@@ -0,0 +1,293 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+int GScreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+#if XlibSpecificationRelease < 5
+    Widget w;
+#endif
+    int ai;
+    XColor c;
+    int color;
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    ps.x = ps.y = MINSWSIZE;
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINSWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRCHILDCENTER:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "childcenter");
+           return -1;
+       case G_ATTRMODE:
+           if (Strcmp("forcebars", attrp[ai].u.t) == 0)
+               ADD2ARGS(XtNforceBars, True);
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    ADD2ARGS(XtNallowHoriz, True);
+    ADD2ARGS(XtNallowVert, True);
+    ADD2ARGS(XtNwidth, ps.x);
+    ADD2ARGS(XtNheight, ps.y);
+    if (!(widget->w = XtCreateWidget("scroll", viewportWidgetClass,
+                                    parent->w, argp, argn))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+#if XlibSpecificationRelease < 5
+    RESETARGS;
+    ADD2ARGS(XtNwidth, ps.x);
+    ADD2ARGS(XtNheight, ps.y);
+    if (!(w = XtCreateWidget("owsucks", formWidgetClass,
+                            widget->w, argp, argn))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    Glazymanage(w);
+    Glazymanage(widget->w);
+    XtDestroyWidget(w);
+#else
+    Glazymanage(widget->w);
+#endif
+    return 0;
+}
+
+int GSsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PIXpoint_t po;
+    PIXsize_t ps;
+    Dimension width, height;
+    int ai;
+    XColor c;
+    int color;
+
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINSWSIZE);
+           ADD2ARGS(XtNwidth, ps.x);
+           ADD2ARGS(XtNheight, ps.y);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRCHILDCENTER:
+           GETORIGIN(attrp[ai].u.p, po);
+           ADD2ARGS(XtNwidth, &width);
+           ADD2ARGS(XtNheight, &height);
+           XtGetValues(widget->w, argp, argn);
+           po.x -= width / 2, po.y -= height / 2;
+           if (po.x < 0)
+               po.x = 0;
+           if (po.y < 0)
+               po.y = 0;
+           XawViewportSetCoordinates(widget->w, po.x, po.y);
+           break;
+       case G_ATTRMODE:
+           if (Strcmp("forcebars", attrp[ai].u.t) == 0)
+               ADD2ARGS(XtNforceBars, True);
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    XtSetValues(widget->w, argp, argn);
+    return 0;
+}
+
+int GSgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Dimension width, height;
+    Position x, y;
+    Boolean tf;
+    Gwidget_t *child;
+    int ai, wi;
+    child = 0;
+
+    for (ai = 0; ai < attrn; ai++) {
+       RESETARGS;
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           ADD2ARGS(XtNwidth, &width);
+           ADD2ARGS(XtNheight, &height);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.s.x = width, attrp[ai].u.s.y = height;
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, &width);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.i = width;
+           break;
+       case G_ATTRCHILDCENTER:
+           for (wi = 0; wi < Gwidgetn; wi++) {
+               child = &Gwidgets[wi];
+               if (child->inuse && child->pwi == widget - &Gwidgets[0])
+                   break;
+           }
+           if (wi == Gwidgetn) {
+               Gerr(POS, G_ERRNOCHILDWIDGET);
+               return -1;
+           }
+           ADD2ARGS(XtNwidth, &width);
+           ADD2ARGS(XtNheight, &height);
+           XtGetValues(widget->w, argp, argn);
+           RESETARGS;
+           ADD2ARGS(XtNx, &x);
+           ADD2ARGS(XtNy, &y);
+           XtGetValues(child->w, argp, argn);
+           attrp[ai].u.p.x = width / 2 - x, attrp[ai].u.p.y =
+               height / 2 - y;
+           break;
+       case G_ATTRMODE:
+           ADD2ARGS(XtNforceBars, &tf);
+           attrp[ai].u.t = (tf == True) ? "forcebars" : "";
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", XtWindow(widget->w));
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GSdestroywidget(Gwidget_t * widget)
+{
+    XtDestroyWidget(widget->w);
+    return 0;
+}
+
+#if XlibSpecificationRelease < 5
+#include <X11/IntrinsicP.h>
+#include <X11/Xaw/ViewportP.h>
+
+void XawViewportSetCoordinates(Widget gw, Position x, Position y)
+{
+    ViewportWidget w = (ViewportWidget) gw;
+    Widget child = w->viewport.child;
+    Widget clip = w->viewport.clip;
+    Position mx, my;
+
+    if (x > (int) child->core.width)
+       x = child->core.width;
+    else if (x < 0)
+       x = child->core.x;
+
+    if (y > (int) child->core.height)
+       y = child->core.height;
+    else if (y < 0)
+       y = child->core.y;
+
+    mx = -x, my = -y;
+
+    if (-mx + (int) clip->core.width > (int) child->core.width)
+       mx = -(child->core.width - clip->core.width);
+
+    if (-my + (int) clip->core.height > (int) child->core.height)
+       my = -(child->core.height - clip->core.height);
+
+    /* make sure we never move past left/top borders */
+    if (mx >= 0)
+       mx = 0;
+    if (my >= 0)
+       my = 0;
+
+    XtMoveWidget(child, mx, my);
+    XtUnmanageChild(child);
+    XtManageChild(child);
+}
+#endif
diff --git a/cmd/lefty/ws/x11/gtext.c b/cmd/lefty/ws/x11/gtext.c
new file mode 100644 (file)
index 0000000..86ec9d2
--- /dev/null
@@ -0,0 +1,322 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+#define WTU widget->u.t
+
+int GTcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    char *s;
+    int ai;
+    XColor c;
+    int color;
+
+    static XawTextSelectType sarray[] = {
+       XawselectLine, XawselectNull
+    };
+
+    if (!parent) {
+       Gerr(POS, G_ERRNOPARENTWIDGET);
+       return -1;
+    }
+    WTU->func = NULL;
+    ps.x = ps.y = MINTWSIZE;
+    s = "oneline";
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINTWSIZE);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRTEXT:
+           ADD2ARGS(XtNstring, attrp[ai].u.t);
+           break;
+       case G_ATTRAPPENDTEXT:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "appendtext");
+           return -1;
+       case G_ATTRMODE:
+           s = attrp[ai].u.t;
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTRNEWLINECB:
+           WTU->func = (Gtwnewlinecb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    ADD2ARGS(XtNwidth, ps.x);
+    ADD2ARGS(XtNheight, ps.y);
+    if (Strcmp("oneline", s) == 0)
+       ADD2ARGS(XtNeditType, XawtextAppend);
+    else if (Strcmp("input", s) == 0 || Strcmp("select", s) == 0)
+       ADD2ARGS(XtNeditType, XawtextEdit);
+    else if (Strcmp("output", s) == 0)
+       ADD2ARGS(XtNeditType, XawtextRead);
+    else {
+       Gerr(POS, G_ERRBADATTRVALUE, s);
+       return -1;
+    }
+    ADD2ARGS(XtNscrollHorizontal, XawtextScrollWhenNeeded);
+    ADD2ARGS(XtNscrollVertical, XawtextScrollWhenNeeded);
+    if (!(widget->w = XtCreateWidget("ascii", asciiTextWidgetClass,
+                                    parent->w, argp, argn))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    if (Strcmp("oneline", s) == 0 || Strcmp("input", s) == 0)
+       XtOverrideTranslations(widget->w, Gtweoltable);
+    else if (Strcmp("select", s) == 0)
+       XawTextSetSelectionArray(widget->w, sarray);
+    Glazymanage(widget->w);
+    return 0;
+}
+
+int GTsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PIXsize_t ps;
+    XawTextBlock tb;
+    int ai, li;
+    XColor c;
+    int color;
+
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINTWSIZE);
+           ADD2ARGS(XtNwidth, ps.x);
+           ADD2ARGS(XtNheight, ps.y);
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, attrp[ai].u.i);
+           break;
+       case G_ATTRTEXT:
+           ADD2ARGS(XtNstring, attrp[ai].u.t);
+           break;
+       case G_ATTRAPPENDTEXT:
+           XawTextSetInsertionPoint(widget->w, 327670000);
+           li = XawTextGetInsertionPoint(widget->w);
+           tb.firstPos = 0, tb.length = strlen(attrp[ai].u.t);
+           tb.ptr = attrp[ai].u.t, tb.format = FMT8BIT;
+           XawTextReplace(widget->w, li, li, &tb);
+           li = XawTextGetInsertionPoint(widget->w);
+           tb.firstPos = 0, tb.length = 1;
+           tb.ptr = "\n", tb.format = FMT8BIT;
+           XawTextReplace(widget->w, li, li, &tb);
+           break;
+       case G_ATTRMODE:
+           if (Strcmp("oneline", attrp[ai].u.t) == 0)
+               ADD2ARGS(XtNeditType, XawtextAppend);
+           else if (Strcmp("input", attrp[ai].u.t) == 0)
+               ADD2ARGS(XtNeditType, XawtextEdit);
+           else if (Strcmp("output", attrp[ai].u.t) == 0)
+               ADD2ARGS(XtNeditType, XawtextRead);
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTRNEWLINECB:
+           WTU->func = (Gtwnewlinecb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    XtSetValues(widget->w, argp, argn);
+    return 0;
+}
+
+int GTgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Dimension width, height;
+    XawTextEditType mode;
+    XawTextBlock tb;
+    Widget w;
+    int rtn, ai;
+    long fi, li;
+
+    for (ai = 0; ai < attrn; ai++) {
+       RESETARGS;
+       switch (attrp[ai].id) {
+       case G_ATTRSIZE:
+           ADD2ARGS(XtNwidth, &width);
+           ADD2ARGS(XtNheight, &height);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.s.x = width, attrp[ai].u.s.y = height;
+           break;
+       case G_ATTRBORDERWIDTH:
+           ADD2ARGS(XtNborderWidth, &width);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.i = width;
+           break;
+       case G_ATTRTEXT:
+           w = XawTextGetSource(widget->w);
+           tb.firstPos = 0, tb.ptr = NULL, tb.format =
+               FMT8BIT, tb.length = 0;
+           rtn = XawTextSourceRead(w, 0, &tb, 30000);
+           if (rtn > Gbufn + 1) {
+               Gbufp = Marraygrow(Gbufp, (long) (rtn + 1) * BUFSIZE);
+               Gbufn = rtn + 1;
+           }
+           for (Gbufi = 0; Gbufi < rtn; Gbufi++)
+               Gbufp[Gbufi] = tb.ptr[Gbufi];
+           Gbufp[Gbufi++] = '\000';
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRAPPENDTEXT:
+           Gerr(POS, G_ERRCANNOTGETATTR, "appendtext");
+           return -1;
+       case G_ATTRSELECTION:
+           w = XawTextGetSource(widget->w);
+           XawTextGetSelectionPos(widget->w, &fi, &li);
+           tb.firstPos = 0, tb.ptr = NULL, tb.format =
+               FMT8BIT, tb.length = 0;
+           rtn = XawTextSourceRead(w, fi, &tb, li - fi);
+           if (li - fi > Gbufn + 1) {
+               Gbufp = Marraygrow(Gbufp, (long) (rtn + 1) * BUFSIZE);
+               Gbufn = rtn + 1;
+           }
+           for (Gbufi = 0; Gbufi < li - fi; Gbufi++)
+               Gbufp[Gbufi] = tb.ptr[Gbufi];
+           Gbufp[Gbufi++] = '\000';
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRMODE:
+           ADD2ARGS(XtNeditType, &mode);
+           XtGetValues(widget->w, argp, argn);
+           if (mode == XawtextAppend)
+               attrp[ai].u.t = "oneline";
+           else if (mode == XawtextEdit)
+               attrp[ai].u.t = "input";
+           else if (mode == XawtextRead)
+               attrp[ai].u.t = "output";
+           else {
+               panic(POS, "GTgetwidgetattr", "unexpected text mode");
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", XtWindow(widget->w));
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTRNEWLINECB:
+           attrp[ai].u.func = (void *) (WTU->func);
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GTdestroywidget(Gwidget_t * widget)
+{
+    XtDestroyWidget(widget->w);
+    return 0;
+}
+
+/* used for one line text input widgets */
+void Gtweolaction(Widget w, XEvent * evp, char **app, unsigned int *anp)
+{
+    Gwidget_t *widget;
+    Widget ww;
+    XawTextBlock tb;
+    int ret, fi, li, n, i;
+
+    widget = findwidget((unsigned long) w, G_TEXTWIDGET);
+    li = XawTextGetInsertionPoint(w) - 1;
+    ww = XawTextGetSource(w);
+    tb.firstPos = 0, tb.ptr = "\n", tb.format = FMT8BIT, tb.length = 1;
+    fi = XawTextSourceSearch(ww, li, XawsdLeft, &tb);
+    if (fi == XawTextSearchError)
+       fi = 0;
+    else
+       fi++;
+    n = li - fi;
+    Gbufp[(Gbufi = 0)] = '\000';
+    while (Gbufi != n) {
+       ret = XawTextSourceRead(ww, fi, &tb, n - Gbufi) - fi;
+       for (i = 0; i < ret; i++)
+           Gbufp[Gbufi++] = tb.ptr[i];
+    }
+    Gbufp[Gbufi] = '\000';
+    if (n >= 0 && WTU->func)
+       (*WTU->func) (widget - &Gwidgets[0], Gbufp);
+}
diff --git a/cmd/lefty/ws/x11/gview.c b/cmd/lefty/ws/x11/gview.c
new file mode 100644 (file)
index 0000000..c816adb
--- /dev/null
@@ -0,0 +1,250 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+
+#define WVU widget->u.v
+
+int GVcreatewidget(Gwidget_t * parent, Gwidget_t * widget,
+                  int attrn, Gwattr_t * attrp)
+{
+    PIXpoint_t po = { 0, 0 };
+    PIXsize_t ps;
+    XSizeHints hints;
+    char *s;
+    int haveorigin, ai;
+    XColor c;
+    int color;
+
+    WVU->func = NULL;
+    WVU->closing = FALSE;
+    s = "LEFTY";
+    ps.x = ps.y = MINVWSIZE;
+    haveorigin = FALSE;
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           haveorigin = TRUE;
+           GETORIGIN(attrp[ai].u.p, po);
+           ADD2ARGS(XtNx, po.x);
+           ADD2ARGS(XtNy, po.y);
+           break;
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINVWSIZE);
+           break;
+       case G_ATTRNAME:
+           s = attrp[ai].u.t;
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRZORDER:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "zorder");
+           return -1;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR1, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WVU->func = (Gviewcb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    ADD2ARGS(XtNwidth, ps.x);
+    ADD2ARGS(XtNheight, ps.y);
+    if (!(widget->w = XtAppCreateShell(s, "LEFTY",
+                                      topLevelShellWidgetClass, Gdisplay,
+                                      argp, argn))) {
+       Gerr(POS, G_ERRCANNOTCREATEWIDGET);
+       return -1;
+    }
+    if (haveorigin) {
+       hints.x = po.x, hints.y = po.y;
+       hints.width = ps.x, hints.height = ps.y;
+       hints.flags = USPosition;
+       Glazyrealize(widget->w, TRUE, &hints);
+    } else
+       Glazyrealize(widget->w, FALSE, NULL);
+    return 0;
+}
+
+int GVsetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    PIXpoint_t po;
+    PIXsize_t ps;
+    int ai;
+    XColor c;
+    int color;
+
+    RESETARGS;
+    for (ai = 0; ai < attrn; ai++) {
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           GETORIGIN(attrp[ai].u.p, po);
+           ADD2ARGS(XtNx, po.x);
+           ADD2ARGS(XtNy, po.y);
+           break;
+       case G_ATTRSIZE:
+           GETSIZE(attrp[ai].u.s, ps, MINVWSIZE);
+           ADD2ARGS(XtNwidth, ps.x);
+           ADD2ARGS(XtNheight, ps.y);
+           break;
+       case G_ATTRNAME:
+#if XlibSpecificationRelease < 5
+           return -1;
+#else
+           XmbSetWMProperties(Gdisplay, XtWindow(widget->w),
+                              attrp[ai].u.t, attrp[ai].u.t, NULL, 0, NULL,
+                              NULL, NULL);
+#endif
+           break;
+       case G_ATTRCOLOR:
+           color = attrp[ai].u.c.index;
+           if (color != 0 && color != 1) {
+               Gerr(POS, G_ERRBADCOLORINDEX, color);
+               return -1;
+           }
+           c.red = attrp[ai].u.c.r * 257;
+           c.green = attrp[ai].u.c.g * 257;
+           c.blue = attrp[ai].u.c.b * 257;
+           if (XAllocColor
+               (Gdisplay, DefaultColormap(Gdisplay, Gscreenn), &c)) {
+               if (color == 0)
+                   ADD2ARGS(XtNbackground, c.pixel);
+               else
+                   ADD2ARGS(XtNforeground, c.pixel);
+           }
+           break;
+       case G_ATTRZORDER:
+           Gflushlazyq();
+           if (Strcmp(attrp[ai].u.t, "top") == 0)
+               XRaiseWindow(Gdisplay, XtWindow(widget->w));
+           else if (Strcmp(attrp[ai].u.t, "bottom") == 0)
+               XLowerWindow(Gdisplay, XtWindow(widget->w));
+           else {
+               Gerr(POS, G_ERRBADATTRVALUE, attrp[ai].u.t);
+               return -1;
+           }
+           break;
+       case G_ATTRWINDOWID:
+           Gerr(POS, G_ERRCANNOTSETATTR2, "windowid");
+           return -1;
+       case G_ATTREVENTCB:
+           WVU->func = (Gviewcb) attrp[ai].u.func;
+           break;
+       case G_ATTRUSERDATA:
+           widget->udata = attrp[ai].u.u;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    XtSetValues(widget->w, argp, argn);
+    return 0;
+}
+
+int GVgetwidgetattr(Gwidget_t * widget, int attrn, Gwattr_t * attrp)
+{
+    Position x, y;
+    Dimension width, height;
+    int ai;
+
+    for (ai = 0; ai < attrn; ai++) {
+       RESETARGS;
+       switch (attrp[ai].id) {
+       case G_ATTRORIGIN:
+           ADD2ARGS(XtNx, &x);
+           ADD2ARGS(XtNy, &y);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.p.x = x, attrp[ai].u.p.y = y;
+           break;
+       case G_ATTRSIZE:
+           ADD2ARGS(XtNwidth, &width);
+           ADD2ARGS(XtNheight, &height);
+           XtGetValues(widget->w, argp, argn);
+           attrp[ai].u.s.x = width, attrp[ai].u.s.y = height;
+           break;
+       case G_ATTRNAME:
+           Gerr(POS, G_ERRNOTIMPLEMENTED);
+           return -1;
+       case G_ATTRZORDER:
+           Gerr(POS, G_ERRNOTIMPLEMENTED);
+           return -1;
+       case G_ATTRWINDOWID:
+           sprintf(&Gbufp[0], "0x%lx", XtWindow(widget->w));
+           attrp[ai].u.t = &Gbufp[0];
+           break;
+       case G_ATTREVENTCB:
+           attrp[ai].u.func = (void *) (WVU->func);
+           break;
+       case G_ATTRUSERDATA:
+           attrp[ai].u.u = widget->udata;
+           break;
+       default:
+           Gerr(POS, G_ERRBADATTRID, attrp[ai].id);
+           return -1;
+       }
+    }
+    return 0;
+}
+
+int GVdestroywidget(Gwidget_t * widget)
+{
+    WVU->closing = TRUE;
+    XtDestroyWidget(widget->w);
+    return 0;
+}
+
+void Gwmdelaction(Widget w, XEvent * evp, char **app, unsigned int *anp)
+{
+    Gwidget_t *widget;
+    Gevent_t gev;
+
+    widget = findwidget((unsigned long) w, G_VIEWWIDGET);
+    if (!widget)
+       exit(0);
+    gev.type = G_MOUSE, gev.code = 0, gev.data = 0;
+    gev.wi = widget - &Gwidgets[0];
+    if (WVU->func)
+       (*WVU->func) (&gev);
+    else
+       exit(0);
+}
diff --git a/cmd/lefty/ws/x11/libfilereq/.cvsignore b/cmd/lefty/ws/x11/libfilereq/.cvsignore
new file mode 100644 (file)
index 0000000..9fb9857
--- /dev/null
@@ -0,0 +1,6 @@
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
diff --git a/cmd/lefty/ws/x11/libfilereq/Dir.c b/cmd/lefty/ws/x11/libfilereq/Dir.c
new file mode 100644 (file)
index 0000000..d3dc853
--- /dev/null
@@ -0,0 +1,206 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef FEATURE_CS
+#include <ast.h>
+#endif
+/*
+ * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Software Research Associates
+ * makes no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Erik M. van der Poel
+ *         Software Research Associates, Inc., Tokyo, Japan
+ *         erik@sra.co.jp
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef SEL_FILE_IGNORE_CASE
+#include <ctype.h>
+#endif                         /* def SEL_FILE_IGNORE_CASE */
+
+#include "SFinternal.h"
+
+#if HAVE_DIRENT_H
+#  include <dirent.h>
+#  define DIRENT_DONE
+#else
+#  define dirent direct
+#  if HAVE_SYS_NDIR_H
+#    include <sys/ndir.h>
+#    define DIRENT_DONE
+#  endif
+#  if HAVE_NDIR_H
+#    include <ndir.h>
+#    define DIRENT_DONE
+#  endif
+#endif
+
+#ifndef DIRENT_DONE
+#if defined(SVR4) || defined(SYSV) || defined(USG) || defined(__osf__) || defined (__svr4__) || defined (__FreeBSD__) || defined(SCO)
+#include <dirent.h>
+#else                          /* defined(SVR4) || defined(SYSV) || defined(USG) */
+#include <sys/dir.h>
+#define dirent direct
+#endif                         /* defined(SVR4) || defined(SYSV) || defined(USG) */
+#endif
+
+#include <sys/stat.h>
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+extern void qsort();
+#endif                         /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include "SFDecls.h"
+
+#ifdef SEL_FILE_IGNORE_CASE
+int SFcompareEntries(const void *vp, const void *vq)
+{
+    SFEntry *p = (SFEntry *) vp, *q = (SFEntry *) vq;
+    register char *r, *s;
+    register char c1, c2;
+
+    r = p->real;
+    s = q->real;
+
+    c1 = *r++;
+    if (islower(c1)) {
+       c1 = toupper(c1);
+    }
+    c2 = *s++;
+    if (islower(c2)) {
+       c2 = toupper(c2);
+    }
+
+    while (c1 == c2) {
+       if (!c1) {
+           return strcmp(p->real, q->real);
+       }
+       c1 = *r++;
+       if (islower(c1)) {
+           c1 = toupper(c1);
+       }
+       c2 = *s++;
+       if (islower(c2)) {
+           c2 = toupper(c2);
+       }
+    }
+
+    return c1 - c2;
+}
+#else                          /* def SEL_FILE_IGNORE_CASE */
+int SFcompareEntries(const void *vp, const void *vq)
+{
+    SFEntry *p = (SFEntry *) vp, *q = (SFEntry *) vq;
+    return strcmp(p->real, q->real);
+}
+#endif                         /* def SEL_FILE_IGNORE_CASE */
+
+int SFgetDir(dir)
+SFDir *dir;
+{
+    SFEntry *result = NULL;
+    int alloc = 0;
+    int i;
+    DIR *dirp;
+    struct dirent *dp;
+    char *str;
+    int len;
+    int maxChars;
+    struct stat statBuf;
+
+    maxChars = strlen(dir->dir) - 1;
+
+    dir->entries = NULL;
+    dir->nEntries = 0;
+    dir->nChars = 0;
+
+    result = NULL;
+    i = 0;
+
+    dirp = opendir(".");
+    if (!dirp) {
+       return 1;
+    }
+
+    (void) stat(".", &statBuf);
+    dir->mtime = statBuf.st_mtime;
+
+    (void) readdir(dirp);      /* throw away "." */
+
+#ifndef S_IFLNK
+    (void) readdir(dirp);      /* throw away ".." */
+#endif                         /* ndef S_IFLNK */
+
+    while ((dp = readdir(dirp))) {
+       if (i >= alloc) {
+           alloc = 2 * (alloc + 1);
+           result = (SFEntry *) XtRealloc((char *) result,
+                                          (unsigned) (alloc *
+                                                      sizeof(SFEntry)));
+       }
+       result[i].statDone = 0;
+       str = dp->d_name;
+       len = strlen(str);
+       result[i].real = XtMalloc((unsigned) (len + 2));
+       (void) strcat(strcpy(result[i].real, str), " ");
+       if (len > maxChars) {
+           maxChars = len;
+       }
+       result[i].shown = result[i].real;
+       i++;
+    }
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+    qsort((char *) result, (unsigned) i, sizeof(SFEntry),
+         SFcompareEntries);
+#else                          /* defined(SVR4) || defined(SYSV) || defined(USG) */
+    qsort((char *) result, i, sizeof(SFEntry), SFcompareEntries);
+#endif                         /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+    dir->entries = result;
+    dir->nEntries = i;
+    dir->nChars = maxChars + 1;
+
+    closedir(dirp);
+
+    return 0;
+}
diff --git a/cmd/lefty/ws/x11/libfilereq/Draw.c b/cmd/lefty/ws/x11/libfilereq/Draw.c
new file mode 100644 (file)
index 0000000..4586dcd
--- /dev/null
@@ -0,0 +1,858 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef FEATURE_CS
+#include <ast.h>
+#endif
+/*
+ * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Software Research Associates
+ * makes no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Erik M. van der Poel
+ *         Software Research Associates, Inc., Tokyo, Japan
+ *         erik@sra.co.jp
+ */
+
+#include <stdio.h>
+#include "SFinternal.h"
+#include "xstat.h"
+#include <X11/StringDefs.h>
+#include <X11/Xaw/Scrollbar.h>
+#include <X11/Xaw/Cardinals.h>
+
+#include "SFDecls.h"
+
+#define SF_DEFAULT_FONT "9x15"
+
+#ifdef ABS
+#undef ABS
+#endif
+#define ABS(x) (((x) < 0) ? (-(x)) : (x))
+
+typedef struct {
+    char *fontname;
+} TextData, *textPtr;
+
+int SFcharWidth, SFcharAscent, SFcharHeight;
+
+int SFcurrentInvert[3] = { -1, -1, -1 };
+
+static GC SFlineGC, SFscrollGC, SFinvertGC, SFtextGC;
+
+static XtResource textResources[] = {
+    {XtNfont, XtCFont, XtRString, sizeof(char *),
+     XtOffset(textPtr, fontname), XtRString, SF_DEFAULT_FONT},
+};
+
+static XFontStruct *SFfont;
+
+static int SFcurrentListY;
+
+static XtIntervalId SFscrollTimerId;
+
+void SFinitFont()
+{
+    TextData *data;
+
+    data = XtNew(TextData);
+
+    XtGetApplicationResources(selFileForm, (XtPointer) data, textResources,
+                             XtNumber(textResources), (Arg *) NULL, ZERO);
+
+    SFfont = XLoadQueryFont(SFdisplay, data->fontname);
+    if (!SFfont) {
+       SFfont = XLoadQueryFont(SFdisplay, SF_DEFAULT_FONT);
+       if (!SFfont) {
+           char sbuf[256];
+
+           (void) sprintf(sbuf, "XsraSelFile: can't get font %s",
+                          SF_DEFAULT_FONT);
+
+           XtAppError(SFapp, sbuf);
+       }
+    }
+
+    SFcharWidth =
+       (SFfont->max_bounds.width + SFfont->min_bounds.width) / 2;
+    SFcharAscent = SFfont->max_bounds.ascent;
+    SFcharHeight = SFcharAscent + SFfont->max_bounds.descent;
+}
+
+void SFcreateGC()
+{
+    XGCValues gcValues;
+    XRectangle rectangles[1];
+
+    gcValues.foreground = SFfore;
+
+    SFlineGC = XtGetGC(selFileLists[0], (XtGCMask)
+                      GCForeground | 0, &gcValues);
+
+    SFscrollGC = XtGetGC(selFileLists[0], (XtGCMask)
+                        0, &gcValues);
+
+    gcValues.function = GXinvert;
+    gcValues.plane_mask = (SFfore ^ SFback);
+
+    SFinvertGC = XtGetGC(selFileLists[0], (XtGCMask)
+                        GCFunction | GCPlaneMask | 0, &gcValues);
+
+    gcValues.foreground = SFfore;
+    gcValues.background = SFback;
+    gcValues.font = SFfont->fid;
+
+    SFtextGC = XCreateGC(SFdisplay,
+                        XtWindow(selFileLists[0]), (unsigned long)
+                        GCForeground |
+                        GCBackground | GCFont | 0, &gcValues);
+
+    rectangles[0].x = SFlineToTextH + SFbesideText;
+    rectangles[0].y = 0;
+    rectangles[0].width = SFcharsPerEntry * SFcharWidth;
+    rectangles[0].height = SFupperY + 1;
+
+    XSetClipRectangles(SFdisplay, SFtextGC, 0, 0, rectangles, 1, Unsorted);
+}
+
+void SFclearList(n, doScroll)
+long n;
+int doScroll;
+{
+    SFDir *dir;
+
+    SFcurrentInvert[n] = -1;
+
+    XClearWindow(SFdisplay, XtWindow(selFileLists[n]));
+
+    XDrawSegments(SFdisplay, XtWindow(selFileLists[n]), SFlineGC, SFsegs,
+                 2);
+
+    if (doScroll) {
+       dir = &(SFdirs[SFdirPtr + n]);
+
+       if ((SFdirPtr + n < SFdirEnd) && dir->nEntries && dir->nChars) {
+           XawScrollbarSetThumb(selFileVScrolls[n],
+                                (float) (((double) dir->vOrigin) /
+                                         dir->nEntries),
+                                (float) (((double)
+                                          ((dir->nEntries < SFlistSize)
+                                           ? dir->
+                                           nEntries : SFlistSize)) /
+                                         dir->nEntries)
+               );
+
+           XawScrollbarSetThumb(selFileHScrolls[n],
+                                (float) (((double) dir->hOrigin) /
+                                         dir->nChars),
+                                (float) (((double)
+                                          ((dir->nChars <
+                                            SFcharsPerEntry) ? dir->
+                                           nChars : SFcharsPerEntry)) /
+                                         dir->nChars)
+               );
+       } else {
+           XawScrollbarSetThumb(selFileVScrolls[n], (float) 0.0,
+                                (float) 1.0);
+           XawScrollbarSetThumb(selFileHScrolls[n], (float) 0.0,
+                                (float) 1.0);
+       }
+    }
+}
+
+static void SFdeleteEntry(dir, entry)
+SFDir *dir;
+SFEntry *entry;
+{
+    register SFEntry *e;
+    register SFEntry *end;
+    int n;
+    int idx;
+
+    idx = entry - dir->entries;
+
+    if (idx < dir->beginSelection) {
+       dir->beginSelection--;
+    }
+    if (idx <= dir->endSelection) {
+       dir->endSelection--;
+    }
+    if (dir->beginSelection > dir->endSelection) {
+       dir->beginSelection = dir->endSelection = -1;
+    }
+
+    if (idx < dir->vOrigin) {
+       dir->vOrigin--;
+    }
+
+    XtFree(entry->real);
+
+    end = &(dir->entries[dir->nEntries - 1]);
+
+    for (e = entry; e < end; e++) {
+       *e = *(e + 1);
+    }
+
+    if (!(--dir->nEntries)) {
+       return;
+    }
+
+    n = dir - &(SFdirs[SFdirPtr]);
+    if ((n < 0) || (n > 2)) {
+       return;
+    }
+
+    XawScrollbarSetThumb(selFileVScrolls[n],
+                        (float) (((double) dir->vOrigin) / dir->nEntries),
+                        (float) (((double) ((dir->nEntries < SFlistSize) ?
+                                            dir->nEntries : SFlistSize)) /
+                                 dir->nEntries)
+       );
+}
+
+static void SFwriteStatChar(name, last, statBuf)
+char *name;
+int last;
+struct stat *statBuf;
+{
+    name[last] = SFstatChar(statBuf);
+}
+
+static int SFstatAndCheck(dir, entry)
+SFDir *dir;
+SFEntry *entry;
+{
+    struct stat statBuf;
+    char save;
+    int last;
+
+    /*
+     * must be restored before returning
+     */
+    save = *(dir->path);
+    *(dir->path) = 0;
+
+    if (!SFchdir(SFcurrentPath)) {
+       last = strlen(entry->real) - 1;
+       entry->real[last] = 0;
+       entry->statDone = 1;
+       if ((!stat(entry->real, &statBuf))
+#ifdef S_IFLNK
+           || (!lstat(entry->real, &statBuf))
+#endif                         /* ndef S_IFLNK */
+           ) {
+           if (SFfunc) {
+               char *shown;
+
+               shown = NULL;
+               if (SFfunc(entry->real, &shown, &statBuf)) {
+                   if (shown) {
+                       int len;
+
+                       len = strlen(shown);
+                       entry->shown = XtMalloc((unsigned) (len + 2)
+                           );
+                       (void) strcpy(entry->shown, shown);
+                       SFwriteStatChar(entry->shown, len, &statBuf);
+                       entry->shown[len + 1] = 0;
+                   }
+               } else {
+                   SFdeleteEntry(dir, entry);
+
+                   *(dir->path) = save;
+                   return 1;
+               }
+           }
+           SFwriteStatChar(entry->real, last, &statBuf);
+       } else {
+           entry->real[last] = ' ';
+       }
+    }
+
+    *(dir->path) = save;
+    return 0;
+}
+
+static void SFdrawStrings(w, dir, from, to)
+register Window w;
+register SFDir *dir;
+register int from;
+register int to;
+{
+    register int i;
+    register SFEntry *entry;
+    int x;
+
+    x = SFtextX - dir->hOrigin * SFcharWidth;
+
+    if (dir->vOrigin + to >= dir->nEntries) {
+       to = dir->nEntries - dir->vOrigin - 1;
+    }
+    for (i = from; i <= to; i++) {
+       entry = &(dir->entries[dir->vOrigin + i]);
+       if (!(entry->statDone)) {
+           if (SFstatAndCheck(dir, entry)) {
+               if (dir->vOrigin + to >= dir->nEntries) {
+                   to = dir->nEntries - dir->vOrigin - 1;
+               }
+               i--;
+               continue;
+           }
+       }
+       XDrawImageString(SFdisplay,
+                        w,
+                        SFtextGC,
+                        x,
+                        SFtextYoffset + i * SFentryHeight,
+                        entry->shown, strlen(entry->shown)
+           );
+       if (dir->vOrigin + i == dir->beginSelection) {
+           XDrawLine(SFdisplay,
+                     w,
+                     SFlineGC,
+                     SFlineToTextH + 1,
+                     SFlowerY + i * SFentryHeight,
+                     SFlineToTextH + SFentryWidth - 2,
+                     SFlowerY + i * SFentryHeight);
+       }
+       if ((dir->vOrigin + i >= dir->beginSelection) &&
+           (dir->vOrigin + i <= dir->endSelection)
+           ) {
+           SFcompletionSegs[0].y1 = SFcompletionSegs[1].y1 =
+               SFlowerY + i * SFentryHeight;
+           SFcompletionSegs[0].y2 = SFcompletionSegs[1].y2 =
+               SFlowerY + (i + 1) * SFentryHeight - 1;
+           XDrawSegments(SFdisplay, w, SFlineGC, SFcompletionSegs, 2);
+       }
+       if (dir->vOrigin + i == dir->endSelection) {
+           XDrawLine(SFdisplay,
+                     w,
+                     SFlineGC,
+                     SFlineToTextH + 1,
+                     SFlowerY + (i + 1) * SFentryHeight - 1,
+                     SFlineToTextH + SFentryWidth - 2,
+                     SFlowerY + (i + 1) * SFentryHeight - 1);
+       }
+    }
+}
+
+void SFdrawList(n, doScroll)
+long n;
+int doScroll;
+{
+    SFDir *dir;
+    Window w;
+
+    SFclearList(n, doScroll);
+
+    if (SFdirPtr + n < SFdirEnd) {
+       dir = &(SFdirs[SFdirPtr + n]);
+       w = XtWindow(selFileLists[n]);
+       XDrawImageString(SFdisplay,
+                        w,
+                        SFtextGC,
+                        SFtextX - dir->hOrigin * SFcharWidth,
+                        SFlineToTextV + SFaboveAndBelowText +
+                        SFcharAscent, dir->dir, strlen(dir->dir)
+           );
+       SFdrawStrings(w, dir, 0, SFlistSize - 1);
+    }
+}
+
+void SFdrawLists(doScroll)
+int doScroll;
+{
+    long i;
+
+    for (i = 0; i < 3; i++) {
+       SFdrawList(i, doScroll);
+    }
+}
+
+static void SFinvertEntry(n)
+register int n;
+{
+    XFillRectangle(SFdisplay,
+                  XtWindow(selFileLists[n]),
+                  SFinvertGC,
+                  SFlineToTextH,
+                  SFcurrentInvert[n] * SFentryHeight + SFlowerY,
+                  SFentryWidth, SFentryHeight);
+}
+
+static unsigned long SFscrollTimerInterval()
+{
+    static int maxVal = 200;
+    static int varyDist = 50;
+    static int minDist = 50;
+    int t;
+    int dist;
+
+    if (SFcurrentListY < SFlowerY) {
+       dist = SFlowerY - SFcurrentListY;
+    } else if (SFcurrentListY > SFupperY) {
+       dist = SFcurrentListY - SFupperY;
+    } else {
+       return (unsigned long) 1;
+    }
+
+    t = maxVal - ((maxVal / varyDist) * (dist - minDist));
+
+    if (t < 1) {
+       t = 1;
+    }
+
+    if (t > maxVal) {
+       t = maxVal;
+    }
+
+    return (unsigned long) t;
+}
+
+static void SFscrollTimer(p, id)
+XtPointer p;
+XtIntervalId *id;
+{
+    SFDir *dir;
+    int save;
+    long n;
+
+    n = (long) p;
+
+    dir = &(SFdirs[SFdirPtr + n]);
+    save = dir->vOrigin;
+
+    if (SFcurrentListY < SFlowerY) {
+       if (dir->vOrigin > 0) {
+           SFvSliderMovedCallback(selFileVScrolls[n], n,
+                                  dir->vOrigin - 1);
+       }
+    } else if (SFcurrentListY > SFupperY) {
+       if (dir->vOrigin < dir->nEntries - SFlistSize) {
+           SFvSliderMovedCallback(selFileVScrolls[n], n,
+                                  dir->vOrigin + 1);
+       }
+    }
+
+    if (dir->vOrigin != save) {
+       if (dir->nEntries) {
+           XawScrollbarSetThumb(selFileVScrolls[n],
+                                (float) (((double) dir->vOrigin) /
+                                         dir->nEntries),
+                                (float) (((double)
+                                          ((dir->nEntries <
+                                            SFlistSize) ? dir->
+                                           nEntries : SFlistSize)) /
+                                         dir->nEntries)
+               );
+       }
+    }
+
+    if (SFbuttonPressed) {
+       SFscrollTimerId = XtAppAddTimeOut(SFapp,
+                                         SFscrollTimerInterval(),
+                                         SFscrollTimer, (XtPointer) n);
+    }
+}
+
+static int SFnewInvertEntry(n, event)
+register long n;
+register XMotionEvent *event;
+{
+    register int x, y;
+    register int new;
+    static int SFscrollTimerAdded = 0;
+
+    x = event->x;
+    y = event->y;
+
+    if (SFdirPtr + n >= SFdirEnd) {
+       return -1;
+    } else if ((x >= 0) && (x <= SFupperX) &&
+              (y >= SFlowerY) && (y <= SFupperY)
+       ) {
+       register SFDir *dir = &(SFdirs[SFdirPtr + n]);
+
+       if (SFscrollTimerAdded) {
+           SFscrollTimerAdded = 0;
+           XtRemoveTimeOut(SFscrollTimerId);
+       }
+
+       new = (y - SFlowerY) / SFentryHeight;
+       if (dir->vOrigin + new >= dir->nEntries) {
+           return -1;
+       }
+       return new;
+    } else {
+       if (SFbuttonPressed) {
+           SFcurrentListY = y;
+           if (!SFscrollTimerAdded) {
+               SFscrollTimerAdded = 1;
+               SFscrollTimerId = XtAppAddTimeOut(SFapp,
+                                                 SFscrollTimerInterval(),
+                                                 SFscrollTimer,
+                                                 (XtPointer) n);
+           }
+       }
+
+       return -1;
+    }
+}
+
+/* ARGSUSED */
+void SFenterList(w, n, event)
+Widget w;
+register int n;
+register XEnterWindowEvent *event;
+{
+    register int new;
+
+    /* sanity */
+    if (SFcurrentInvert[n] != -1) {
+       SFinvertEntry(n);
+       SFcurrentInvert[n] = -1;
+    }
+
+    new = SFnewInvertEntry((long) n, (XMotionEvent *) event);
+    if (new != -1) {
+       SFcurrentInvert[n] = new;
+       SFinvertEntry(n);
+    }
+}
+
+/* ARGSUSED */
+void SFleaveList(w, n, event)
+Widget w;
+register int n;
+XEvent *event;
+{
+    if (SFcurrentInvert[n] != -1) {
+       SFinvertEntry(n);
+       SFcurrentInvert[n] = -1;
+    }
+}
+
+/* ARGSUSED */
+void SFmotionList(w, n, event)
+Widget w;
+register int n;
+register XMotionEvent *event;
+{
+    register int new;
+
+    new = SFnewInvertEntry((long) n, event);
+
+    if (new != SFcurrentInvert[n]) {
+       if (SFcurrentInvert[n] != -1) {
+           SFinvertEntry(n);
+       }
+       SFcurrentInvert[n] = new;
+       if (new != -1) {
+           SFinvertEntry(n);
+       }
+    }
+}
+
+/* ARGSUSED */
+void SFvFloatSliderMovedCallback(w, n, fnew)
+Widget w;
+int n;
+float *fnew;
+{
+    int new;
+
+    new = (*fnew) * SFdirs[SFdirPtr + n].nEntries;
+
+    SFvSliderMovedCallback(w, n, new);
+}
+
+/* ARGSUSED */
+void SFvSliderMovedCallback(w, n, new)
+Widget w;
+int n;
+int new;
+{
+    int old;
+    register Window win;
+    SFDir *dir;
+
+    dir = &(SFdirs[SFdirPtr + n]);
+
+    old = dir->vOrigin;
+    dir->vOrigin = new;
+
+    if (old == new) {
+       return;
+    }
+
+    win = XtWindow(selFileLists[n]);
+
+    if (ABS(new - old) < SFlistSize) {
+       if (new > old) {
+           XCopyArea(SFdisplay,
+                     win,
+                     win,
+                     SFscrollGC,
+                     SFlineToTextH,
+                     SFlowerY + (new - old) * SFentryHeight,
+                     SFentryWidth + SFlineToTextH,
+                     (SFlistSize - (new - old)) * SFentryHeight,
+                     SFlineToTextH, SFlowerY);
+           XClearArea(SFdisplay,
+                      win,
+                      SFlineToTextH,
+                      SFlowerY + (SFlistSize - (new - old)) *
+                      SFentryHeight,
+                      SFentryWidth + SFlineToTextH,
+                      (new - old) * SFentryHeight, False);
+           SFdrawStrings(win, dir, SFlistSize - (new - old),
+                         SFlistSize - 1);
+       } else {
+           XCopyArea(SFdisplay,
+                     win,
+                     win,
+                     SFscrollGC,
+                     SFlineToTextH,
+                     SFlowerY,
+                     SFentryWidth + SFlineToTextH,
+                     (SFlistSize - (old - new)) * SFentryHeight,
+                     SFlineToTextH,
+                     SFlowerY + (old - new) * SFentryHeight);
+           XClearArea(SFdisplay,
+                      win,
+                      SFlineToTextH,
+                      SFlowerY,
+                      SFentryWidth + SFlineToTextH,
+                      (old - new) * SFentryHeight, False);
+           SFdrawStrings(win, dir, 0, old - new);
+       }
+    } else {
+       XClearArea(SFdisplay,
+                  win,
+                  SFlineToTextH,
+                  SFlowerY,
+                  SFentryWidth + SFlineToTextH,
+                  SFlistSize * SFentryHeight, False);
+       SFdrawStrings(win, dir, 0, SFlistSize - 1);
+    }
+}
+
+/* ARGSUSED */
+void SFvAreaSelectedCallback(w, n, pnew)
+Widget w;
+int n;
+int pnew;
+{
+    SFDir *dir;
+    int new;
+
+    dir = &(SFdirs[SFdirPtr + n]);
+
+    new = dir->vOrigin +
+       (((double) pnew) / SFvScrollHeight) * dir->nEntries;
+
+    if (new > dir->nEntries - SFlistSize) {
+       new = dir->nEntries - SFlistSize;
+    }
+
+    if (new < 0) {
+       new = 0;
+    }
+
+    if (dir->nEntries) {
+       float f;
+
+       f = ((double) new) / dir->nEntries;
+
+       XawScrollbarSetThumb(w,
+                            f,
+                            (float) (((double)
+                                      ((dir->nEntries <
+                                        SFlistSize) ? dir->
+                                       nEntries : SFlistSize)) /
+                                     dir->nEntries)
+           );
+    }
+
+    SFvSliderMovedCallback(w, n, new);
+}
+
+/* ARGSUSED */
+void SFhSliderMovedCallback(w, n, new)
+Widget w;
+int n;
+float *new;
+{
+    SFDir *dir;
+    int save;
+
+    dir = &(SFdirs[SFdirPtr + n]);
+    save = dir->hOrigin;
+    dir->hOrigin = (*new) * dir->nChars;
+    if (dir->hOrigin == save) {
+       return;
+    }
+
+    SFdrawList(n, SF_DO_NOT_SCROLL);
+}
+
+/* ARGSUSED */
+void SFhAreaSelectedCallback(w, n, pnew)
+Widget w;
+int n;
+int pnew;
+{
+    SFDir *dir;
+    int new;
+
+    dir = &(SFdirs[SFdirPtr + n]);
+
+    new = dir->hOrigin + (((double) pnew) / SFhScrollWidth) * dir->nChars;
+
+    if (new > dir->nChars - SFcharsPerEntry) {
+       new = dir->nChars - SFcharsPerEntry;
+    }
+
+    if (new < 0) {
+       new = 0;
+    }
+
+    if (dir->nChars) {
+       float f;
+
+       f = ((double) new) / dir->nChars;
+
+       XawScrollbarSetThumb(w,
+                            f,
+                            (float) (((double)
+                                      ((dir->nChars <
+                                        SFcharsPerEntry) ? dir->
+                                       nChars : SFcharsPerEntry)) /
+                                     dir->nChars)
+           );
+
+       SFhSliderMovedCallback(w, n, &f);
+    }
+}
+
+/* ARGSUSED */
+void SFpathSliderMovedCallback(w, client_data, new)
+Widget w;
+XtPointer client_data;
+float *new;
+{
+    SFDir *dir;
+    int n;
+    XawTextPosition pos;
+    int SFdirPtrSave;
+
+    SFdirPtrSave = SFdirPtr;
+    SFdirPtr = (*new) * SFdirEnd;
+    if (SFdirPtr == SFdirPtrSave) {
+       return;
+    }
+
+    SFdrawLists(SF_DO_SCROLL);
+
+    n = 2;
+    while (SFdirPtr + n >= SFdirEnd) {
+       n--;
+    }
+
+    dir = &(SFdirs[SFdirPtr + n]);
+
+    pos = dir->path - SFcurrentPath;
+
+    if (!strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) {
+       pos -= strlen(SFstartDir);
+       if (pos < 0) {
+           pos = 0;
+       }
+    }
+
+    XawTextSetInsertionPoint(selFileField, pos);
+}
+
+/* ARGSUSED */
+
+void SFpathAreaSelectedCallback(w, client_data, pnew)
+Widget w;
+XtPointer client_data;
+int pnew;
+{
+    int new;
+    float f;
+
+    new = SFdirPtr + (((double) pnew) / SFpathScrollWidth) * SFdirEnd;
+
+    if (new > SFdirEnd - 3) {
+       new = SFdirEnd - 3;
+    }
+
+    if (new < 0) {
+       new = 0;
+    }
+
+    f = ((double) new) / SFdirEnd;
+
+    XawScrollbarSetThumb(w,
+                        f,
+                        (float) (((double)
+                                  ((SFdirEnd <
+                                    3) ? SFdirEnd : 3)) / SFdirEnd)
+       );
+
+    SFpathSliderMovedCallback(w, (XtPointer) NULL, &f);
+}
+
+Boolean SFworkProc()
+{
+    register SFDir *dir;
+    register SFEntry *entry;
+
+    for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) {
+       if (!(dir->nEntries)) {
+           continue;
+       }
+       for (entry = &(dir->entries[dir->nEntries - 1]);
+            entry >= dir->entries; entry--) {
+           if (!(entry->statDone)) {
+               (void) SFstatAndCheck(dir, entry);
+               return False;
+           }
+       }
+    }
+
+    SFworkProcAdded = 0;
+
+    return True;
+}
diff --git a/cmd/lefty/ws/x11/libfilereq/Makefile.am b/cmd/lefty/ws/x11/libfilereq/Makefile.am
new file mode 100644 (file)
index 0000000..4b5bcbc
--- /dev/null
@@ -0,0 +1,14 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = @X_CFLAGS@ \
+       $(XAW_INCLUDES)
+
+noinst_HEADERS = SFinternal.h SFDecls.h xstat.h
+if WITH_X
+noinst_LTLIBRARIES = libfilereq.la
+endif
+
+libfilereq_la_SOURCES = Dir.c Draw.c Path.c SelFile.c
+libfilereq_la_LIBADD = $(XAW_LIBS)
+
+EXTRA_DIST = README.selfile
diff --git a/cmd/lefty/ws/x11/libfilereq/Path.c b/cmd/lefty/ws/x11/libfilereq/Path.c
new file mode 100644 (file)
index 0000000..cfaceaa
--- /dev/null
@@ -0,0 +1,893 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef FEATURE_CS
+#include <ast.h>
+#endif
+/*
+ * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Software Research Associates
+ * makes no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Erik M. van der Poel
+ *         Software Research Associates, Inc., Tokyo, Japan
+ *         erik@sra.co.jp
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef SEL_FILE_IGNORE_CASE
+#include <ctype.h>
+#endif                         /* def SEL_FILE_IGNORE_CASE */
+
+#include <X11/Xos.h>
+#include <pwd.h>
+#include "SFinternal.h"
+#include "xstat.h"
+#include <X11/Xaw/Scrollbar.h>
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+extern uid_t getuid();
+extern void qsort();
+#endif                         /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include "SFDecls.h"
+
+typedef struct {
+    char *name;
+    char *dir;
+} SFLogin;
+
+SFDir *SFdirs = NULL;
+
+int SFdirEnd;
+
+int SFdirPtr;
+
+int SFbuttonPressed = 0;
+
+static int SFdoNotTouchDirPtr = 0;
+
+static int SFdoNotTouchVorigin = 0;
+
+static SFDir SFrootDir, SFhomeDir;
+
+static SFLogin *SFlogins;
+
+static int SFtwiddle = 0;
+
+void SFsetText(char *path);
+
+int SFchdir(path)
+char *path;
+{
+    int result;
+
+    result = 0;
+
+    if (strcmp(path, SFcurrentDir)) {
+       result = chdir(path);
+       if (!result) {
+           (void) strcpy(SFcurrentDir, path);
+       }
+    }
+
+    return result;
+}
+
+static void SFfree(i)
+int i;
+{
+    register SFDir *dir;
+    register int j;
+
+    dir = &(SFdirs[i]);
+
+    for (j = dir->nEntries - 1; j >= 0; j--) {
+       if (dir->entries[j].shown != dir->entries[j].real) {
+           XtFree(dir->entries[j].shown);
+       }
+       XtFree(dir->entries[j].real);
+    }
+
+    XtFree((char *) dir->entries);
+
+    XtFree(dir->dir);
+
+    dir->dir = NULL;
+}
+
+static void SFstrdup(s1, s2)
+char **s1;
+char *s2;
+{
+    *s1 = strcpy(XtMalloc((unsigned) (strlen(s2) + 1)), s2);
+}
+
+static void SFunreadableDir(dir)
+SFDir *dir;
+{
+    char *cannotOpen = "<cannot open> ";
+
+    dir->entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
+    dir->entries[0].statDone = 1;
+    SFstrdup(&dir->entries[0].real, cannotOpen);
+    dir->entries[0].shown = dir->entries[0].real;
+    dir->nEntries = 1;
+    dir->nChars = strlen(cannotOpen);
+}
+
+#ifdef SEL_FILE_IGNORE_CASE
+static SFstrncmp(p, q, n)
+register char *p, *q;
+register int n;
+{
+    register char c1, c2;
+    char *psave, *qsave;
+    int nsave;
+
+    psave = p;
+    qsave = q;
+    nsave = n;
+
+    c1 = *p++;
+    if (islower(c1)) {
+       c1 = toupper(c1);
+    }
+    c2 = *q++;
+    if (islower(c2)) {
+       c2 = toupper(c2);
+    }
+
+    while ((--n >= 0) && (c1 == c2)) {
+       if (!c1) {
+           return strncmp(psave, qsave, nsave);
+       }
+       c1 = *p++;
+       if (islower(c1)) {
+           c1 = toupper(c1);
+       }
+       c2 = *q++;
+       if (islower(c2)) {
+           c2 = toupper(c2);
+       }
+    }
+
+    if (n < 0) {
+       return strncmp(psave, qsave, nsave);
+    }
+
+    return c1 - c2;
+}
+#endif                         /* def SEL_FILE_IGNORE_CASE */
+
+static void SFreplaceText(dir, str)
+SFDir *dir;
+char *str;
+{
+    int len;
+
+    *(dir->path) = 0;
+    len = strlen(str);
+    if (str[len - 1] == '/') {
+       (void) strcat(SFcurrentPath, str);
+    } else {
+       (void) strncat(SFcurrentPath, str, len - 1);
+    }
+    if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) {
+       SFsetText(SFcurrentPath);
+    } else {
+       SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
+    }
+
+    SFtextChanged();
+}
+
+static void SFexpand(str)
+char *str;
+{
+    int len;
+    int cmp;
+    char *name, *growing;
+    SFDir *dir;
+    SFEntry *entry, *max;
+
+    len = strlen(str);
+
+    dir = &(SFdirs[SFdirEnd - 1]);
+
+    if (dir->beginSelection == -1) {
+       SFstrdup(&str, str);
+       SFreplaceText(dir, str);
+       XtFree(str);
+       return;
+    } else if (dir->beginSelection == dir->endSelection) {
+       SFreplaceText(dir, dir->entries[dir->beginSelection].shown);
+       return;
+    }
+
+    max = &(dir->entries[dir->endSelection + 1]);
+
+    name = dir->entries[dir->beginSelection].shown;
+    SFstrdup(&growing, name);
+
+    cmp = 0;
+    while (!cmp) {
+       entry = &(dir->entries[dir->beginSelection]);
+       while (entry < max) {
+           if ((cmp = strncmp(growing, entry->shown, len))) {
+               break;
+           }
+           entry++;
+       }
+       len++;
+    }
+
+    /*
+     * SFreplaceText() expects filename
+     */
+    growing[len - 2] = ' ';
+
+    growing[len - 1] = 0;
+    SFreplaceText(dir, growing);
+    XtFree(growing);
+}
+
+static int SFfindFile(dir, str)
+SFDir *dir;
+register char *str;
+{
+    register int i, last, max;
+    register char *name, save;
+    SFEntry *entries;
+    int len;
+    int begin, end;
+    int result;
+
+    len = strlen(str);
+
+    if (str[len - 1] == ' ') {
+       SFexpand(str);
+       return 1;
+    } else if (str[len - 1] == '/') {
+       len--;
+    }
+
+    max = dir->nEntries;
+
+    entries = dir->entries;
+
+    i = 0;
+    while (i < max) {
+       name = entries[i].shown;
+       last = strlen(name) - 1;
+       save = name[last];
+       name[last] = 0;
+
+#ifdef SEL_FILE_IGNORE_CASE
+       result = SFstrncmp(str, name, len);
+#else                          /* def SEL_FILE_IGNORE_CASE */
+       result = strncmp(str, name, len);
+#endif                         /* def SEL_FILE_IGNORE_CASE */
+
+       name[last] = save;
+       if (result <= 0) {
+           break;
+       }
+       i++;
+    }
+    begin = i;
+    while (i < max) {
+       name = entries[i].shown;
+       last = strlen(name) - 1;
+       save = name[last];
+       name[last] = 0;
+
+#ifdef SEL_FILE_IGNORE_CASE
+       result = SFstrncmp(str, name, len);
+#else                          /* def SEL_FILE_IGNORE_CASE */
+       result = strncmp(str, name, len);
+#endif                         /* def SEL_FILE_IGNORE_CASE */
+
+       name[last] = save;
+       if (result) {
+           break;
+       }
+       i++;
+    }
+    end = i;
+
+    if (begin != end) {
+       if ((dir->beginSelection != begin) ||
+           (dir->endSelection != end - 1)
+           ) {
+           dir->changed = 1;
+           dir->beginSelection = begin;
+           if (str[strlen(str) - 1] == '/') {
+               dir->endSelection = begin;
+           } else {
+               dir->endSelection = end - 1;
+           }
+       }
+    } else {
+       if (dir->beginSelection != -1) {
+           dir->changed = 1;
+           dir->beginSelection = -1;
+           dir->endSelection = -1;
+       }
+    }
+
+    if (SFdoNotTouchVorigin ||
+       ((begin > dir->vOrigin) && (end < dir->vOrigin + SFlistSize))
+       ) {
+       SFdoNotTouchVorigin = 0;
+       return 0;
+    }
+
+    i = begin - 1;
+    if (i > max - SFlistSize) {
+       i = max - SFlistSize;
+    }
+    if (i < 0) {
+       i = 0;
+    }
+
+    if (dir->vOrigin != i) {
+       dir->vOrigin = i;
+       dir->changed = 1;
+    }
+
+    return 0;
+}
+
+static void SFunselect()
+{
+    SFDir *dir;
+
+    dir = &(SFdirs[SFdirEnd - 1]);
+    if (dir->beginSelection != -1) {
+       dir->changed = 1;
+    }
+    dir->beginSelection = -1;
+    dir->endSelection = -1;
+}
+
+static int SFcompareLogins(const void *vp, const void *vq)
+{
+    SFLogin *p = (SFLogin *) vp, *q = (SFLogin *) vq;
+    return strcmp(p->name, q->name);
+}
+
+static void SFgetHomeDirs()
+{
+    struct passwd *pw;
+    int alloc;
+    int i;
+    SFEntry *entries = NULL;
+    int len;
+    int maxChars;
+
+    {
+       alloc = 1;
+       i = 1;
+       entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
+       SFlogins = (SFLogin *) XtMalloc(sizeof(SFLogin));
+       entries[0].real = XtMalloc(3);
+       (void) strcpy(entries[0].real, "~");
+       entries[0].shown = entries[0].real;
+       entries[0].statDone = 1;
+       SFlogins[0].name = "";
+       pw = getpwuid((int) getuid());
+       SFstrdup(&SFlogins[0].dir, pw ? pw->pw_dir : "/");
+       maxChars = 0;
+    }
+
+    (void) setpwent();
+
+    while ((pw = getpwent()) && (*(pw->pw_name))) {
+       if (i >= alloc) {
+           alloc *= 2;
+           entries = (SFEntry *) XtRealloc((char *) entries,
+                                           (unsigned) (alloc *
+                                                       sizeof(SFEntry))
+               );
+           SFlogins = (SFLogin *) XtRealloc((char *) SFlogins,
+                                            (unsigned) (alloc *
+                                                        sizeof(SFLogin))
+               );
+       }
+       len = strlen(pw->pw_name);
+       entries[i].real = XtMalloc((unsigned) (len + 3));
+       (void) strcat(strcpy(entries[i].real, "~"), pw->pw_name);
+       entries[i].shown = entries[i].real;
+       entries[i].statDone = 1;
+       if (len > maxChars) {
+           maxChars = len;
+       }
+       SFstrdup(&SFlogins[i].name, pw->pw_name);
+       SFstrdup(&SFlogins[i].dir, pw->pw_dir);
+       i++;
+    }
+
+    SFhomeDir.dir = XtMalloc(1);
+    SFhomeDir.dir[0] = 0;
+    SFhomeDir.path = SFcurrentPath;
+    SFhomeDir.entries = entries;
+    SFhomeDir.nEntries = i;
+    SFhomeDir.vOrigin = 0;     /* :-) */
+    SFhomeDir.nChars = maxChars + 2;
+    SFhomeDir.hOrigin = 0;
+    SFhomeDir.changed = 1;
+    SFhomeDir.beginSelection = -1;
+    SFhomeDir.endSelection = -1;
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+    qsort((char *) entries, (unsigned) i, sizeof(SFEntry),
+         SFcompareEntries);
+    qsort((char *) SFlogins, (unsigned) i, sizeof(SFLogin),
+         SFcompareLogins);
+#else                          /* defined(SVR4) || defined(SYSV) || defined(USG) */
+    qsort((char *) entries, i, sizeof(SFEntry), SFcompareEntries);
+    qsort((char *) SFlogins, i, sizeof(SFLogin), SFcompareLogins);
+#endif                         /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+    for (i--; i >= 0; i--) {
+       (void) strcat(entries[i].real, "/");
+    }
+}
+
+static int SFfindHomeDir(begin, end)
+char *begin, *end;
+{
+    char save;
+    char *theRest;
+    int i;
+
+    save = *end;
+    *end = 0;
+
+    for (i = SFhomeDir.nEntries - 1; i >= 0; i--) {
+       if (!strcmp(SFhomeDir.entries[i].real, begin)) {
+           *end = save;
+           SFstrdup(&theRest, end);
+           (void) strcat(strcat(strcpy(SFcurrentPath,
+                                       SFlogins[i].dir), "/"), theRest);
+           XtFree(theRest);
+           SFsetText(SFcurrentPath);
+           SFtextChanged();
+           return 1;
+       }
+    }
+
+    *end = save;
+
+    return 0;
+}
+
+void SFupdatePath()
+{
+    static int alloc;
+    static int wasTwiddle = 0;
+    char *begin, *end;
+    int i, j;
+    int prevChange;
+    int SFdirPtrSave, SFdirEndSave;
+    SFDir *dir;
+
+    if (!SFdirs) {
+       SFdirs = (SFDir *) XtMalloc((alloc = 10) * sizeof(SFDir));
+       dir = &(SFdirs[0]);
+       SFstrdup(&dir->dir, "/");
+       (void) SFchdir("/");
+       (void) SFgetDir(dir);
+       for (j = 1; j < alloc; j++) {
+           SFdirs[j].dir = NULL;
+       }
+       dir->path = SFcurrentPath + 1;
+       dir->vOrigin = 0;
+       dir->hOrigin = 0;
+       dir->changed = 1;
+       dir->beginSelection = -1;
+       dir->endSelection = -1;
+       SFhomeDir.dir = NULL;
+    }
+
+    SFdirEndSave = SFdirEnd;
+    SFdirEnd = 1;
+
+    SFdirPtrSave = SFdirPtr;
+    SFdirPtr = 0;
+
+    begin = NULL;
+
+    if (SFcurrentPath[0] == '~') {
+       if (!SFtwiddle) {
+           SFtwiddle = 1;
+           dir = &(SFdirs[0]);
+           SFrootDir = *dir;
+           if (!SFhomeDir.dir) {
+               SFgetHomeDirs();
+           }
+           *dir = SFhomeDir;
+           dir->changed = 1;
+       }
+       end = SFcurrentPath;
+       SFdoNotTouchDirPtr = 1;
+       wasTwiddle = 1;
+    } else {
+       if (SFtwiddle) {
+           SFtwiddle = 0;
+           dir = &(SFdirs[0]);
+           *dir = SFrootDir;
+           dir->changed = 1;
+       }
+       end = SFcurrentPath + 1;
+    }
+
+    i = 0;
+
+    prevChange = 0;
+
+    while (*end) {
+       while (*end++ == '/') {
+           ;
+       }
+       end--;
+       begin = end;
+       while ((*end) && (*end++ != '/')) {
+           ;
+       }
+       if ((end - SFcurrentPath <= SFtextPos) && (*(end - 1) == '/')) {
+           SFdirPtr = i - 1;
+           if (SFdirPtr < 0) {
+               SFdirPtr = 0;
+           }
+       }
+       if (*begin) {
+           if (*(end - 1) == '/') {
+               char save = *end;
+
+               if (SFtwiddle) {
+                   if (SFfindHomeDir(begin, end)) {
+                       return;
+                   }
+               }
+               *end = 0;
+               i++;
+               SFdirEnd++;
+               if (i >= alloc) {
+                   SFdirs = (SFDir *) XtRealloc((char *) SFdirs,
+                                                (unsigned) ((alloc *= 2) *
+                                                            sizeof(SFDir))
+                       );
+                   for (j = alloc / 2; j < alloc; j++) {
+                       SFdirs[j].dir = NULL;
+                   }
+               }
+               dir = &(SFdirs[i]);
+               if ((!(dir->dir)) || prevChange || strcmp(dir->dir, begin)
+                   ) {
+                   if (dir->dir) {
+                       SFfree(i);
+                   }
+                   prevChange = 1;
+                   SFstrdup(&dir->dir, begin);
+                   dir->path = end;
+                   dir->vOrigin = 0;
+                   dir->hOrigin = 0;
+                   dir->changed = 1;
+                   dir->beginSelection = -1;
+                   dir->endSelection = -1;
+                   (void) SFfindFile(dir - 1, begin);
+                   if (SFchdir(SFcurrentPath) || SFgetDir(dir)
+                       ) {
+                       SFunreadableDir(dir);
+                       break;
+                   }
+               }
+               *end = save;
+               if (!save) {
+                   SFunselect();
+               }
+           } else {
+               if (SFfindFile(&(SFdirs[SFdirEnd - 1]), begin)) {
+                   return;
+               }
+           }
+       } else {
+           SFunselect();
+       }
+    }
+
+    if ((end == SFcurrentPath + 1) && (!SFtwiddle)) {
+       SFunselect();
+    }
+
+    for (i = SFdirEnd; i < alloc; i++) {
+       if (SFdirs[i].dir) {
+           SFfree(i);
+       }
+    }
+
+    if (SFdoNotTouchDirPtr) {
+       if (wasTwiddle) {
+           wasTwiddle = 0;
+           SFdirPtr = SFdirEnd - 2;
+           if (SFdirPtr < 0) {
+               SFdirPtr = 0;
+           }
+       } else {
+           SFdirPtr = SFdirPtrSave;
+       }
+       SFdoNotTouchDirPtr = 0;
+    }
+
+    if ((SFdirPtr != SFdirPtrSave) || (SFdirEnd != SFdirEndSave)) {
+       XawScrollbarSetThumb(selFileHScroll,
+                            (float) (((double) SFdirPtr) / SFdirEnd),
+                            (float) (((double)
+                                      ((SFdirEnd <
+                                        3) ? SFdirEnd : 3)) / SFdirEnd)
+           );
+    }
+
+    if (SFdirPtr != SFdirPtrSave) {
+       SFdrawLists(SF_DO_SCROLL);
+    } else {
+       for (i = 0; i < 3; i++) {
+           if (SFdirPtr + i < SFdirEnd) {
+               if (SFdirs[SFdirPtr + i].changed) {
+                   SFdirs[SFdirPtr + i].changed = 0;
+                   SFdrawList(i, SF_DO_SCROLL);
+               }
+           } else {
+               SFclearList(i, SF_DO_SCROLL);
+           }
+       }
+    }
+}
+
+void SFsetText(path)
+char *path;
+{
+    XawTextBlock text;
+
+    text.firstPos = 0;
+    text.length = strlen(path);
+    text.ptr = path;
+    text.format = FMT8BIT;
+
+    XawTextReplace(selFileField, 0, strlen(SFtextBuffer), &text);
+    XawTextSetInsertionPoint(selFileField, strlen(SFtextBuffer));
+}
+
+/* ARGSUSED */
+void SFbuttonPressList(w, n, event)
+Widget w;
+int n;
+XButtonPressedEvent *event;
+{
+    SFbuttonPressed = 1;
+}
+
+/* ARGSUSED */
+void SFbuttonReleaseList(w, n, event)
+Widget w;
+int n;
+XButtonReleasedEvent *event;
+{
+    SFDir *dir;
+
+    SFbuttonPressed = 0;
+
+    if (SFcurrentInvert[n] != -1) {
+       if (n < 2) {
+           SFdoNotTouchDirPtr = 1;
+       }
+       SFdoNotTouchVorigin = 1;
+       dir = &(SFdirs[SFdirPtr + n]);
+       SFreplaceText(dir,
+                     dir->entries[dir->vOrigin +
+                                  SFcurrentInvert[n]].shown);
+       SFmotionList(w, n, event);
+    }
+}
+
+static int SFcheckDir(n, dir)
+int n;
+SFDir *dir;
+{
+    struct stat statBuf;
+    int i;
+
+    if ((!stat(".", &statBuf)) && (statBuf.st_mtime != dir->mtime)
+       ) {
+
+       /*
+        * If the pointer is currently in the window that we are about
+        * to update, we must warp it to prevent the user from
+        * accidentally selecting the wrong file.
+        */
+       if (SFcurrentInvert[n] != -1) {
+           XWarpPointer(SFdisplay,
+                        None,
+                        XtWindow(selFileLists[n]), 0, 0, 0, 0, 0, 0);
+       }
+
+       for (i = dir->nEntries - 1; i >= 0; i--) {
+           if (dir->entries[i].shown != dir->entries[i].real) {
+               XtFree(dir->entries[i].shown);
+           }
+           XtFree(dir->entries[i].real);
+       }
+       XtFree((char *) dir->entries);
+       if (SFgetDir(dir)) {
+           SFunreadableDir(dir);
+       }
+       if (dir->vOrigin > dir->nEntries - SFlistSize) {
+           dir->vOrigin = dir->nEntries - SFlistSize;
+       }
+       if (dir->vOrigin < 0) {
+           dir->vOrigin = 0;
+       }
+       if (dir->hOrigin > dir->nChars - SFcharsPerEntry) {
+           dir->hOrigin = dir->nChars - SFcharsPerEntry;
+       }
+       if (dir->hOrigin < 0) {
+           dir->hOrigin = 0;
+       }
+       dir->beginSelection = -1;
+       dir->endSelection = -1;
+       SFdoNotTouchVorigin = 1;
+       if ((dir + 1)->dir) {
+           (void) SFfindFile(dir, (dir + 1)->dir);
+       } else {
+           (void) SFfindFile(dir, dir->path);
+       }
+
+       if (!SFworkProcAdded) {
+           SFworkProcId = XtAppAddWorkProc(SFapp, SFworkProc, NULL);
+           SFworkProcAdded = 1;
+       }
+
+       return 1;
+    }
+
+    return 0;
+}
+
+static int SFcheckFiles(dir)
+SFDir *dir;
+{
+    int from, to;
+    int result;
+    char old, new;
+    int i;
+    char *str;
+    int last;
+    struct stat statBuf;
+
+    result = 0;
+
+    from = dir->vOrigin;
+    to = dir->vOrigin + SFlistSize;
+    if (to > dir->nEntries) {
+       to = dir->nEntries;
+    }
+
+    for (i = from; i < to; i++) {
+       str = dir->entries[i].real;
+       last = strlen(str) - 1;
+       old = str[last];
+       str[last] = 0;
+       if (stat(str, &statBuf)) {
+           new = ' ';
+       } else {
+           new = SFstatChar(&statBuf);
+       }
+       str[last] = new;
+       if (new != old) {
+           result = 1;
+       }
+    }
+
+    return result;
+}
+
+void SFdirModTimer(cl, id)
+XtPointer cl;
+XtIntervalId *id;
+{
+    static int n = -1;
+    static int f = 0;
+    char save;
+    SFDir *dir;
+
+    if ((!SFtwiddle) && (SFdirPtr < SFdirEnd)) {
+       n++;
+       if ((n > 2) || (SFdirPtr + n >= SFdirEnd)) {
+           n = 0;
+           f++;
+           if ((f > 2) || (SFdirPtr + f >= SFdirEnd)) {
+               f = 0;
+           }
+       }
+       dir = &(SFdirs[SFdirPtr + n]);
+       save = *(dir->path);
+       *(dir->path) = 0;
+       if (SFchdir(SFcurrentPath)) {
+           *(dir->path) = save;
+
+           /*
+            * force a re-read
+            */
+           *(dir->dir) = 0;
+
+           SFupdatePath();
+       } else {
+           *(dir->path) = save;
+           if (SFcheckDir(n, dir) || ((f == n) && SFcheckFiles(dir))
+               ) {
+               SFdrawList(n, SF_DO_SCROLL);
+           }
+       }
+    }
+
+    SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
+                                     SFdirModTimer, (XtPointer) NULL);
+}
+
+/* Return a single character describing what kind of file STATBUF is.  */
+
+char SFstatChar(statBuf)
+struct stat *statBuf;
+{
+    if (S_ISDIR(statBuf->st_mode)) {
+       return '/';
+    } else if (S_ISREG(statBuf->st_mode)) {
+       return S_ISXXX(statBuf->st_mode) ? '*' : ' ';
+#ifdef S_ISSOCK
+    } else if (S_ISSOCK(statBuf->st_mode)) {
+       return '=';
+#endif                         /* S_ISSOCK */
+    } else {
+       return ' ';
+    }
+}