Home
Manual
Packages
Global Index
Keywords
Quick Reference
|
/*
* yeti_gist_gui.i --
*
* Rudimentary Graphic User Interface build on top of Gist.
*
*-----------------------------------------------------------------------------
*
* Copyright (C) 2001-2002 Eric THIEBAUT.
*
* This file is part of Yeti.
*
* Yeti is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Yeti is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Yeti (file "COPYING" in the top source directory); if
* not, write to the Free Software Foundation, Inc., 59 Temple Place,
* Suite 330, Boston, MA 02111-1307 USA.
*
*-----------------------------------------------------------------------------
*
* History:
* $Id$
* $Log$
*/
/* WISH LIST
[ ] fix computation of cell size(s)
[ ] use weights for grid cells
[ ] message box widget
[ ] list box widget
[ ] menu widget
[ ] entry widget (NOT POSSIBLE with basic Gist)
[ ] config method
[ ] add fontsize settings to gg_window
[ ] add gg_get_fontsize routine with DPI keyword (i.e. fontsize is in point)
*/
func gg_dialog (message, label, .., win=, color=, font=)
{
bw = 70; // button width
bh = 25; // button height
pad = 2; // padding space
bd = 2; // border width
fontsize = 20;
font = gg_get_font(font, "font", "timesBI");
color = gg_get_color(color, "color", GG_BLUE);
newlines = where(*pointer(message+"\n\n") == '\n');
hmsg = max(100, fontsize*numberof(newlines));
wmsg = max(200, fontsize*max(newlines(dif))*2/3);
while (more_args()) grow, label, next_arg();
ncols = numberof(label);
if (ncols < 1 || structof(label)!=string) error, "bad button label(s)";
Top = gg_toplevel(bd=bd, relief="groove", padx=pad, pady=pad);
Message = gg_button(text=message, width=wmsg, height=hmsg,
bd=1, relief="sunken",
font=font, fontsize=fontsize, justify="CH",
bg="white", fg=color);
gg_manage, Top, 1, 1, Message, padx=pad, pady=pad,
colspan=ncols+1, anchor="ew";
Button = gg_button(text=string(0), width=bw, height=bh, bd=bd);
for (i=1 ; i<=ncols ; ++i) {
s = label(i);
gg_manage, Top, i,2, h_set(h_copy(Button), text=s, ident=s),
padx=3*pad, pady=pad;
}
__gg_finalize_pass1, Top;
width = Top._w;
height = Top._h;
if (is_void(win)) win = 7;
winkill, win;
window, win, wait=1, width=width, height=height;
h_set, Top, window=win;
geom = window_geometry(Top.win);
one_pixel = geom(2);
xbias = geom(3);
ybias = geom(4);
width = long(geom(5));
height = long(geom(6));
x0 = xbias + one_pixel*max((width - Top._w)/2, 0);
y0 = ybias - one_pixel*(height - 1);
x1 = x0 + one_pixel*(Top._w - 1);
y1 = y0 + one_pixel*(Top._h - 1);
h_set, Top, one_pixel=one_pixel, x0=x0, y0=y0, x1=x1, y1=y1;
__gg_finalize_pass2, Top;
gg_paint, Top;
for (;;) {
while (is_void((ms = mouse(-1, 0, ""))))
;
/* Check that button was clicked and released over same widget. */
clicked = gg_find_clicked(Top, ms);
if (is_hash(clicked)) {
ident = h_get(clicked, ident=);
if (structof(ident) == string) return ident;
}
}
}
func gg_pli_tool (z, y, x, win=, width=, height=, dpi=)
{
/* Get current window geometry. */
win = current_window();
if (win < 0) error, "you must create window first";
geom = window_geometry(win);
dpi = long(geom(1));
one_pixel = geom(2);
xbias = geom(3);
ybias = geom(4);
wtop = long(geom(5));
htop = long(geom(6));
fma;
/* Parse Z, Y and X. */
dims = dimsof(z);
nx = dims(2);
ny = dims(3);
if (is_void(x)) {
x0 = 0.5; x1 = nx+0.5;
} else {
x = double(x);
if (numberof(x) == 2) {
s = (x(2) - x(1)) / nx;
x0 = x(1) - 0.5*s;
x1 = x(2) - 0.5*s;
} else if (numberof(x) == 1) {
x0 = x(1) - 0.5;
x1 = x(1) + nx + 0.5;
} error, "bad dimension for X";
}
if (is_void(y)) {
y0 = 0.5; y1 = ny+0.5;
} else {
y = double(y);
if (numberof(y) == 2) {
s = (y(2) - y(1)) / ny;
y0 = y(1) - 0.5*s;
y1 = y(2) - 0.5*s;
} else if (numberof(y) == 1) {
y0 = y(1) - 0.5;
y1 = y(1) + ny + 0.5;
} error, "bad dimension for Y";
}
dx = abs(x1 - x0);
dy = abs(y1 - y0);
bw = 70; // button width
bh = 25; // button height
pad = 2; // padding space
bd = 2; // border width
// FIXME: not needed ig gg_get_geometry method
wgui = 4*(pad + bd) + 7*bw;
hgui = 8*(pad + bd) + 9*bh;
#if 0
/* Make viewport. */
xmargin = bw;
ymargin = 2*bh;
height = htop - hgui - 2*ymargin;
width = wtop - 2*xmargin;
minsize = 3*dpi;
if (width < minsize || height < minsize) {
a = min(double(minsize)/dx, double(minsize)/dy);
wtop = max(wgui, long(2*xmargin + a*dx + 0.5));
htop = hgui + long(2*ymargin + a*dy + 0.5);
winkill, win;
window, win, dpi=dpi, width=wtop, height=htop, wait=1;
} else {
a = min(double(width)/dx, double(height)/dy);
}
vp = array(long, 4);
wplt = long(a*dx + 0.5);
hplt = long(a*dy + 0.5);
xmargin = (wtop - wplt)/2;
ymargin = (htop - hgui - hplt)/2;
gg_viewport, xmargin, xmargin+wplt-1, htop-ymargin, htop-ymargin-hplt+1,
units=2 /* pixel */;
#endif
/* Create widgets. */
w_top = gg_toplevel(bd=bd, relief="groove", padx=pad, pady=pad);
w_zoom = gg_button(text="Zoom", width=bw, height=bh, bd=bd);
w_unzoom = gg_button(text="Unzoom", width=bw, height=bh, bd=bd);
w_redraw = gg_button(text="Redraw", width=bw, height=bh, bd=bd);
w_ok = gg_button(text="Ok", width=bw, height=bh, bd=bd);
w_cancel = gg_button(text="Cancel", width=bw, height=bh, bd=bd);
w_quit = gg_button(text="Quit", width=bw, height=bh, bd=bd);
w_message = gg_button(text="", width=7*bw, height=5*bh, bd=1,
font="helveticaB", fontsize=14, justify="CT",
bg="white", fg="blue", relief="sunken");
h_set, w_message, text="Try this...";
/* Manage widgets in a grid (from top to bottom). */
gg_manage, w_top, 1, 1, w_zoom, padx=pad, pady=pad;
gg_manage, w_top, 2, 1, w_unzoom, padx=pad, pady=pad;
gg_manage, w_top, 5, 1, w_redraw, padx=pad, pady=pad;
gg_manage, w_top, 1, 2, w_message, padx=pad, pady=pad, colspan=5;
gg_manage, w_top, 1, 3, w_ok, padx=pad, pady=pad;
gg_manage, w_top, 5, 3, w_cancel, padx=pad, pady=pad;
pli, z, x0, y0, x1, y1;
gg_paint, w_top;
do_zoom = 0;
for (;;) {
while (is_void((ms = mouse(-1, 0, ""))))
;
/* Check that button was clicked and released over same widget. */
clicked = gg_find_clicked(w_top, ms);
if (is_void(clicked)) {
if (do_zoom) gg_zoom, ms;
} else {
if (clicked == w_redraw) {
lim = limits;
fma;
pli, z, x0, y0, x1, y1;
//limits, lim;
gg_paint, w_top;
} else if (clicked == w_zoom) {
do_zoom = ! do_zoom;
if (do_zoom) h_set, w_zoom, relief=GG_SUNKEN, fg=GG_RED;
else h_set, w_zoom, relief=GG_RAISED, fg=GG_DEFAULT_FG;
gg_paint, w_zoom;
} else if (clicked == w_unzoom) {
fma;
pli, z, x0, y0, x1, y1;
limits;
gg_paint, w_top;
} else if (clicked == w_cancel) {
return "cancel";
} else if (clicked == w_ok) {
return "ok";
}
}
}
}
func gg_test (dummy, dpi=, width=, height=, win=)
{
if (is_void(win)) win = current_window();
if (win < 0) error, "you must create window first";
//window, win, dpi=dpi, width=width, height=height, wait=1, style="nobox.gs";
//win = current_window();
geom = window_geometry(win);
width = 80;
height = 30;
padx = 10;
pady = 5;
top = gg_toplevel();
gg_manage, top, 1, 1, gg_button(text="Button 11", width=width, height=height,
bd=2);
//gg_paint, g;
//rdline, prompt="hit [return]";
gg_manage, top, 1, 2, gg_button(text="Button 12", width=width, height=height,
fg="red", bd=3);
//gg_paint, g;
//rdline, prompt="hit [return]";
gg_manage, top, 2, 2, gg_label(text="Label 22", width=width, height=height,
fg="red", bd=5, relief="flat");
gg_manage, top, 2, 1, gg_label(text="Label 21", width=width, height=height,
fg="red", bd=3, relief="sunken", bg="white");
gg_manage, top, 3, 1, gg_label(text="Label 31", width=width, height=height,
fg="red", bd=3, relief="solid", bg="white");
gg_manage, top, 3, 2, colspan=2,
gg_label(text="Label 32", width=width, height=height,
fg="red", bd=3, relief="solid", bg="white");
gg_manage, top, 4, 1, colspan=1,
gg_label(text="Label 41", width=width, height=height,
fg="red", bd=4, relief="groove", bg="cyan");
gg_manage, top, 5, 1, rowspan=2,
gg_label(text="Label 51", width=width, height=height,
fg="blue", bd=4, relief="ridge");
gg_manage, top, 1, 3, colspan=5, rowspan=1,
gg_label(text="Message label\nbox with some\nline of text.",
width=width*5, height=height*3, fontsize=24, font="timesBI",
fg="blue", bd=4, relief="ridge");
gg_paint, top;
return top;
}
/*---------------------------------------------------------------------------*/
/* GENERAL */
/* WIDGET INTERNALS
(X0,Y0,X1,Y1) = widget bounding box in NDC units (*)
ONE_PIXEL = pixel size in NDC units (*)
_X, _Y = widget location (top-left) in pixels relative to the
top-left corner of its parent (*)
_W, _H = actual widget size in pixels (*)
COL, ROW = widget position in grid units
COLSPAN, ROWSPAN = widget size in grid units
WIDTH, HEIGHT = requested (minimum) widget size in pixels
PADX, PADY = margins around widget in pixels
FONTSIZE is in pixel (it is converted back into points when text needs
to be plotted)
X0 = PARENT_X0 + ONE_PIXEL*X
X1 = X0 + ONE_PIXEL*(WIDTH - 1)
Y1 = PARENT_Y1 + ONE_PIXEL*Y
Y0 = Y1 - ONE_PIXEL*(HEIGHT - 1)
COORDINATES
Gist plotting system 0 only knows about normalized device coordinates
(NDC) that run from bottom-left to top-right, i.e. unlike window
systems that use pixel coordinates that run from top-left to
bottom-right.
(*) value(s) automatically computed by layout manager */
func gg_paint (widget)
{
old_sys = plsys(0);
op = widget.paint;
op, widget;
if (old_sys) plsys, old_sys;
}
func gg_no_op (..) {}
local GG_ONE_POINT ;
local GG_ONE_INCH ;
GG_ONE_INCH = 72.27*(GG_ONE_POINT = 0.0013000);
func gg_one_pixel(dpi) { return GG_ONE_INCH/dpi; }
/* DOCUMENT GG_ONE_POINT -- one point in NDC units;
-or- GG_ONE_INCH -- one inch in NDC units;
-or- gg_one_pixel(dpi) -- get pixel size in NDC units given the
resolution in pixels per inch. */
/*---------------------------------------------------------------------------*/
/* FRAME WIDGET (layout manager). */
func gg_frame (nil, width=, height=, padx=, pady=,
ident=, bd=, relief=, bg=, fg=, hi=, lo=)
{
if (! is_void(nil)) error, "unexpected argument";
/* Don't do h_new(key=gg_get_integer(key),... to make sure to use
private copy of values. */
bd = gg_get_integer(bd, "bd", GG_DEFAULT_BD, 0);
padx = gg_get_integer(pady, "padx", GG_DEFAULT_PAD, 0);
pady = gg_get_integer(padx, "pady", GG_DEFAULT_PAD, 0);
mn = min(1, 2*(bd + padx)); /* minimum width */
width = gg_get_integer(width, "width", mn, mn);
mn = min(1, 2*(bd + pady)); /* minimum height */
height = gg_get_integer(height, "height", mn, mn);
bg = gg_get_color(bg, "bg", GG_DEFAULT_BG);
fg = gg_get_color(fg, "fg", GG_DEFAULT_FG);
hi = gg_get_color(hi, "hi", GG_DEFAULT_HI);
lo = gg_get_color(lo, "lo", GG_DEFAULT_LO);
relief = gg_get_relief(relief, "relief", GG_DEFAULT_FRAME_RELIEF);
return h_new(class="Frame", paint=__gg_frame_paint, ident=ident,
width=width, height=height, padx=padx, pady=pady,
bd=bd, relief=relief, fontsize=fontsize, font=font,
fg=fg, bg=bg, hi=hi, lo=lo);
}
func gg_toplevel (nil, x=, y=,
width=, height=, padx=, pady=,
ident=, bd=, relief=, bg=, fg=, hi=, lo=)
{
/* Coordinates of upper left corner. */
x = gg_get_integer(x, "x", 0);
y = gg_get_integer(y, "y", 0);
/* A toplevel widget is just a frame that knows its location. */
top = gg_frame(nil, width=width, height=height, padx=padx, pady=pady,
ident=ident, bd=bd, relief=relief,
bg=bg, fg=fg, hi=hi, lo=lo);
return h_set(top, class="Toplevel", x=x, y=y, paint=__gg_resize_and_paint);
}
func __gg_frame_paint (this)
{
gg_draw_3d_rect, this.x0, this.y0, this.x1, this.y1, this.one_pixel,
this.bd, this.relief, this.bg, this.fg, this.hi, this.lo;
for (child=this.child ; is_hash(child) ; child=child.next) {
op = child.paint;
if (is_func(op)) op, child;
}
}
func __gg_toplevel_paint (this)
{
//old_window = current_window();
window, this.window;
gg_draw_3d_rect, x0, y0, x1, y1, one_pixel, this.bd, this.relief,
this.bg, this.fg, this.hi, this.lo;
gg_draw_3d_rect, this.x0, this.y0, this.x1, this.y1, this.one_pixel,
this.bd, this.relief, this.bg, this.fg, this.hi, this.lo;
for (child=this.child ; is_hash(child) ; child=child.next) {
op = child.paint;
if (is_func(op)) op, child;
}
//if (old_window >= 0) window, old_window;
}
func __gg_resize_and_paint (top)
{
gg_finalize, top;
__gg_frame_paint, top;
}
func gg_manage (container, col, row, widget,
colspan=, rowspan=, padx=, pady=, anchor=, insert=)
/* DOCUMENT gg_manage, container, col, row, widget;
Make WIDGET a children of widget CONTAINER that will be displayed into
cell (COL,ROW) of CONTAINER grid.
Keyword ANCHOR can be used to indicates how WIDGET should be anchored
in its cell if there is remaining space (see gg_get_anchor). The
default anchoring is centered.
Keywords COLSPAN and ROWSPAN indicates how many colmuns/rows the widget
will occupy. The default is COLSPAN=1 and ROWSPAN=1.
Keywords PADX and PADY indicates how many pixels shoulkd be left around
the widget in its cell.
If keyword INSERT is true, WIDGET will be the first children of the
list and therefore displayed below all other children; otherwise,
WIDGET will be the topmost one (if children of CONTAINER do not
overlap, this dictinction is irrelevant).
SEE ALSO: gg_finalize, gg_forget. */
{
/* Check container and new children widget. */
class = h_get(container, class=);
if (class != "Toplevel" && class != "Frame")
error, "CONTAINER must be a frame or a toplevel widget";
other = h_get(container, child=);
if (! is_void(h_get(widget, next=)))
error, "widget already managed (call gg_forget before)";
/* Store grid manager information into widget record. */
col = gg_get_integer(col, "col", , 1);
row = gg_get_integer(row, "row", , 1);
colspan = gg_get_integer(colspan, "colspan", 1, 1);
rowspan = gg_get_integer(rowspan, "rowspan", 1, 1);
padx = gg_get_integer(padx, "padx", GG_DEFAULT_PAD, 0);
pady = gg_get_integer(pady, "pady", GG_DEFAULT_PAD, 0);
anchor = gg_get_anchor(anchor, "anchor", 0);
h_set, widget, col=col, row=row, colspan=colspan, rowspan=rowspan,
padx=padx, pady=pady, anchor=anchor;
if (is_void(other)) {
h_set, container, child=widget;
} else if (insert) {
/* Insert new children. */
h_set, widget, next=other;
h_set, container, child=widget;
} else {
/* Append new children. */
for (;;) {
next = h_get(other, next=);
if (is_void(next)) break;
other = next;
}
h_set, other, next=widget;
}
h_set, container, paint=__gg_resize_and_paint;
}
func gg_forget (container, widget)
/* DOCUMENT gg_forget, container, widget;
Remove WIDGET from the descendant list of CONTAINER, WIDGET will
no longer be displayed for subsquent repaints of CONTAINER. If
WIDGET is not a descendant of CONTAINER, nothing is done.
SEE ALSO: gg_manage. */
{
other = h_get(container, child=);
if (other == widget) {
h_set, container, child=h_pop(widget, next=), paint=__gg_resize_and_paint;
return;
}
for (;;) {
next = h_get(other, next=);
if (widget == next) {
h_set, other, next=h_pop(widget, next=), paint=__gg_resize_and_paint;
return;
}
other = next;
}
}
func gg_finalize (top, win=, justify=)
{
/* Check top and get window number. */
if (h_get(top, class=) != "Toplevel")
error, "TOP must be a toplevel widget";
win = gg_get_integer(win, "win", current_window());
if (win < 0) error, "you must create a window first or use the WIN keyword";
__gg_finalize_pass1, top;
/* Make the toplevel widget horizontally centered and vertically anchored
at bottom. */
h_set, top, window=win;
geom = window_geometry(top.win);
one_pixel = geom(2);
xbias = geom(3);
ybias = geom(4);
width = long(geom(5));
height = long(geom(6));
x0 = xbias + one_pixel*max((width - top._w)/2, 0);
y0 = ybias - one_pixel*(height - 1);
x1 = x0 + one_pixel*(top._w - 1);
y1 = y0 + one_pixel*(top._h - 1);
h_set, top, one_pixel=one_pixel, x0=x0, y0=y0, x1=x1, y1=y1;
__gg_finalize_pass2, top;
}
func __gg_finalize_pass2 (container)
/* DOCUMENT __gg_finalize_pass2, container;
Second pass of geometry manager: convert pixel bounding box relative
to container into absolute NDC.
SEE ALSO: gg_finalize, __gg_finalize_pass1. */
{
one_pixel = container.one_pixel;
xbias = container.x0;
ybias = container.y1;
for (child=container.child ; is_hash(child) ; child=child.next) {
x = child._x;
y = child._y;
h_set, child, one_pixel=one_pixel,
x0=xbias + one_pixel*x,
y0=ybias - one_pixel*(y + child._h - 1),
x1=xbias + one_pixel*(x + child._w - 1),
y1=ybias - one_pixel*y;
if (child.class == "Frame") __gg_finalize_pass2, child;
}
h_set, container, paint=__gg_frame_paint;
}
/* ya = yoff - one_pixel*(_y)
yb = yoff - one_pixel*(_y + _h - 1)
*/
func __gg_finalize_pass1 (container)
/* DOCUMENT __gg_finalize_pass1, container;
First pass of geometry manager: compute pixel bounding box relative
to parent for every children of container.
SEE ALSO: gg_finalize, __gg_finalize_pass2. */
{
/* Count number of children and compute sizes of children managed by
sub-frames. */
number = 0;
for (child=container.child ; is_hash(child) ; child=child.next) {
++number;
}
bd = container.bd;
if (number == 0) {
minwidth = 1 + 2*(bd + container.padx);
minheight = 1 + 2*(bd + container.pady);
} else {
/* Collect all children parameters needed to compute sizes. */
col = row = colspan = rowspan = width = height = array(long, number);
for (i=1, child=container.child ; is_hash(child) ; child=child.next, ++i) {
col(i) = child.col;
row(i) = child.row;
colspan(i) = child.colspan;
rowspan(i) = child.rowspan;
if (child.class == "Frame") {
__gg_finalize_pass1, child;
width(i) = child._w + 2*child.padx;
height(i) = child._h + 2*child.pady;
} else {
width(i) = child.width + 2*child.padx;
height(i) = child.height + 2*child.pady;
}
}
/* Compute columns and rows limits in pixels. */
local xmin, xmax, ymin, xmax;
minwidth = __gg_cell_limits(xmin, xmax, col, colspan, width,
bd + container.padx);
minheight = __gg_cell_limits(ymin, ymax, row, rowspan, height,
bd + container.pady);
/* Propagate computed sizes for every (direct) children according to
anchoring. */
for (child=container.child ; is_hash(child) ; child=child.next) {
anchor = child.anchor;
/* Horizontal anchoring. */
a = (anchor & 3);
sz = child.width + 2*(padx = child.padx);
col = child.col;
mn = xmin(col);
mx = xmax(col + child.colspan - 1);
if (a == 0) /* center */ { x=(mn+mx+1-sz)/2; w=sz; }
else if (a == 1) /* west */ { x=mn; w=sz; }
else if (a == 2) /* east */ { x=mx-sz+1; w=sz; }
else /* fill */ { x=mn; w=mx-mn+1; }
/* Vertical anchoring. */
a = (anchor & 12);
sz = child.height + 2*(pady = child.pady);
row = child.row;
mn = ymin(row);
mx = ymax(row + child.rowspan - 1);
if (a == 0) /* center */ { y=(mn+mx+1-sz)/2; h=sz; }
else if (a == 4) /* north */ { y=mn; h=sz; }
else if (a == 8) /* south */ { y=mx-sz+1; h=sz; }
else /* fill */ { y=mn; h=mx-mn+1; }
/* Store pixel bounding box. */
if (padx) {x += padx; w -= 2*padx; }
if (pady) {y += pady; h -= 2*pady; }
h_set, child, _x=x, _y=y, _w=w, _h=h;
}
}
/* Update size of CONTAINER (not its location which is computed by its
parent). */
h_set, container, _w=max(container.width, minwidth),
_h=max(container.height, minheight);
}
func __gg_cell_limits (&cmin, &cmax, first, span, size, bd)
/* DOCUMENT __gg_cell_limits, cmin, cmax, first, span, size, bd;
Compute cell relative limits.
SEE ALSO: __gg_finalize_pass1. */
{
last = first + span;
n = max(last);
offset = array(bd, n);
for (k=2; k<=n ; ++k) {
if (is_array((i = where(last == k))))
offset(k) = max(size(i) + offset(first(i)));
}
for (k=n-1 ; k>=1 ; --k) {
if (is_array((i = where(first == k))))
offset(k) = min(offset(last(i)) - size(i));
}
cmin = offset(:-1);
cmax = offset(2:) - 1;
return offset(0) - offset(1) + 2*bd; /* return total size */
}
/*---------------------------------------------------------------------------*/
/* LABEL WIDGET */
func gg_label (width=, height=, ident=, text=, font=, fontsize=, justify=,
bd=, relief=, fg=, bg=, hi=, lo=)
{
/* Don't do h_new(key=gg_get_integer(key),... to make sure to use
private copy of values. */
text = gg_get_string(text, "text", "");
width = gg_get_integer(width, "width", 50, 1);
height = gg_get_integer(height, "height", 25, 1);
bd = gg_get_integer(bd, "bd", GG_DEFAULT_BD);
fontsize = gg_get_integer(fontsize, "fontsize", 12, 4);
font = gg_get_font(font, "font", GG_DEFAULT_FONT);
justify = gg_get_justify(justify);
fg = gg_get_color(fg, "fg", GG_DEFAULT_FG);
bg = gg_get_color(bg, "bg", GG_DEFAULT_BG);
hi = gg_get_color(hi, "hi", GG_DEFAULT_HI);
lo = gg_get_color(lo, "lo", GG_DEFAULT_LO);
relief = gg_get_relief(relief, "relief", GG_DEFAULT_LABEL_RELIEF);
bd = min(max(bd, 0), min(width, height)/2); // fix border width
return h_new(class="Label", paint=__gg_label_paint,
text=text, ident=ident, width=width, height=height,
bd=bd, relief=relief, fontsize=fontsize, font=font,
justify=justify,
fg=fg, bg=bg, hi=hi, lo=lo);
}
func __gg_label_paint (lab)
{
one_pixel = lab.one_pixel;
x0 = lab.x0;
y0 = lab.y0;
x1 = lab.x1;
y1 = lab.y1;
bd = lab.bd;
gg_draw_3d_rect, x0, y0, x1, y1, one_pixel, bd, lab.relief,
lab.bg, lab.fg, lab.hi, lab.lo;
op = lab.justify;
if (is_func(op)) op, lab.text, x0, y0, x1, y1, bd, lab.font,
(one_pixel/GG_ONE_POINT)*lab.fontsize, lab.fg;
}
/*---------------------------------------------------------------------------*/
/* BUTTON WIDGET */
func gg_button (width=, height=, ident=, text=, font=, fontsize=, justify=,
bd=, relief=, fg=, bg=, hi=, lo=, callback=)
{
/* A Button widget inherits most of its capabilities from a Label widget. */
if (is_void(relief)) relief = GG_DEFAULT_BUTTON_RELIEF;
btn = gg_label(width=width, height=height, ident=ident, text=text,
font=font, fontsize=fontsize, justify=justify,
bd=bd, relief=relief, fg=fg, bg=bg, hi=hi, lo=lo);
return h_set(btn, class="Button", callback=callback);
}
func gg_on_click (wdg, xndc, yndc, btn, mods)
{
local x0, y0, y1, y1;
nil = string(0);
n = 0;
oldSys = plsys(0);
for (;;) {
m = mouse(0, 0, nil);
if (is_void(m)) break;
if (n==0) {
x0 = m(1);
y0 = m(2);
n = 1;
} else if (n==1) {
x1 = m(1);
y1 = m(2);
plg, [y0,y1], [x0,x1], color=GG_XOR, width=0, marks=0 /* type=*/;
id = numberof(plq());
p = *(plq(id)(5));
if (numberof(p) != 3 && p(1) != 2)
error, "peeked wrong graphic element";
address = (vert ? p(2) : p(3));
n = 2;
} else {
x = m(1);
y = m(2);
plg, [y0,y1], [x0,x1], color=GG_XOR, width=0, marks=0 /* type=*/;
if (abs(x1-x, y1-y) < abs(x0-x, y0-y)) {
x1=x; y1=y;
} else {
x0=x; y0=y;
}
plg, [y0,y1], [x0,x1], color=GG_XOR, width=0, marks=0 /* type=*/;
}
}
plsys, oldSys;
}
/*---------------------------------------------------------------------------*/
/* TOPLEVEL WINDOW */
func gg_viewport (xmin, xmax, ymin, ymax, units=, xopt=, yopt=,
fg=, font=, fontsize=, landscape=)
/* DOCUMENT gg_viewport, left, right, bottom, top;
-or- gg_viewport, vp;
SEE ALSO: window, viewport, set_style. */
{
require, "style.i";
/* Get settings for current window. */
win = current_window();
if (win < 0) error, "no current window";
geom = window_geometry(win);
dpi = long(geom(1));
one_pixel = geom(2);
xbias = geom(3);
ybias = geom(4);
wtop = long(geom(5));
htop = long(geom(6));
/* Get coordinates of viewport(s). */
case = is_void(xmin) + 2*is_void(xmax) + 4*is_void(ymin) + 8*is_void(ymax);
nvps = 0;
case;
if (case == 15) {
/* Get current viewport. */
vp = viewport();
if (max(vp) == min(vp)) {
xmin = 0.15;
xmax = 0.85;
ymin = 0.10;
ymax = 0.80;
units = 1;
} else {
xmin = vp(1,);
xmax = vp(2,);
ymin = vp(3,);
ymax = vp(4,);
units = 0;
}
nvps = numberof(xmin);
} else if (case == 14) {
/* Array of viewport(s) specified. */
vp = xmin;
if (is_array(vp) && (dims = dimsof(vp))(1) >= 1 && dims(2) == 4) {
if ((s = structof(vp))==double || s==float ||
s==long || s==int || s==short) {
xmin = vp(1,);
xmax = vp(2,);
ymin = vp(3,);
ymax = vp(4,);
nvps = numberof(xmin);
}
}
} else if (case == 0) {
/* XMIN, XMAX, YMIN and YMAX given. */
if (is_array(xmin) && ! dimsof(xmin)(1) &&
((s=structof(xmin))==double || s==float || s==long ||
s==int || s==short) &&
is_array(xmax) && ! dimsof(xmax)(1) &&
((s=structof(xmax))==double || s==float || s==long ||
s==int || s==short) &&
is_array(ymin) && ! dimsof(ymin)(1) &&
((s=structof(ymin))==double || s==float || s==long ||
s==int || s==short) &&
is_array(ymax) && ! dimsof(ymax)(1) &&
((s=structof(ymax))==double || s==float || s==long ||
s==int || s==short)) {
nvps = 1;
}
}
if (nvps <= 0) error, "bad viewport specification";
/* Convert viewport coordinates to NDC. */
if (units) {
/* Compute viewport size in pixels, then in NDC. */
if (units == 1) {
/* relative units: 0 is min and 1 is max */
xscl = one_pixel*(wtop - 1.0);
yscl = one_pixel*(htop - 1.0);
ymin = 1.0 - ymin;
ymax = 1.0 - ymax;
} else if (units == 2) {
/* pixel units: exchange YMIN and YMAX */
xscl = yscl = one_pixel;
//ytmp = ymax; ymax = ymin; ymin = ytmp;
} else {
error, "bad value for keyword UNITS (0=NDC, 1=relative, 2=pixels)";
}
xmin = xbias + xscl*xmin;
xmax = xbias + xscl*xmax;
ymin = ybias - yscl*ymin;
ymax = ybias - yscl*ymax;
}
if (anyof(xmin >= xmax) || anyof(ymin >= ymax)) error, "bad viewport limits";
/* Parse other parameters. */
if (is_void(xmargin)) xmargin = 2.0;
if (is_void(ymargin)) ymargin = 2.0;
if (is_void(fontsize)) fontsize = 8;
font = gg_map(gg_get_font, font, GG_HELVETICA);
color = gg_map(gg_get_color, color, GG_FG);
xopt = gg_map(gg_get_axis_flags, xopt, 0x33);
yopt = gg_map(gg_get_axis_flags, yopt, 0x33);
if (nvps>1) {
/* Multiple viewports: fix per-viewport settings. */
zero = array(long, dimsof(xmin));
if (is_void(dimsof(xopt, zero))) error, "bad XOPT dimensions";
xopt += zero;
if (is_void(dimsof(yopt, zero))) error, "bad YOPT dimensions";
yopt += zero;
if (is_void(dimsof(fontsize, zero))) error, "bad FONTSIZE dimensions";
fontsize += zero;
if (is_void(dimsof(font, zero))) error, "bad FONT dimensions";
font += zero;
if (is_void(dimsof(color, zero))) error, "bad COLOR dimensions";
color += zero;
}
if (numberof(xopt) != nvps) error, "bad XOPT dimensions";
if (numberof(yopt) != nvps) error, "bad YOPT dimensions";
if (numberof(fontsize) != nvps) error, "bad FONTSIZE dimensions";
if (numberof(font) != nvps) error, "bad FONT dimensions";
if (numberof(color) != nvps) error, "bad COLOR dimensions";
/* Convert font size to points and margins to NDC. */
fontsize = max(long((one_pixel/GG_ONE_POINT)*fontsize + 0.5), 4);
xmargin *= one_pixel;
ymargin *= one_pixel;
/* Build up systems. */
nHDigits = 5;
nVDigits = 6;
tickLen = one_pixel*[5.0, 3.0, 1.0, 0.0, 0.0];
system = array(GfakeSystem, nvps);
for (i=1 ; i<=nvps ; ++i) {
//write, i, xmin(i), xmax(i), ymin(i), ymax(i);
fontHeight= fontsize(i);
xFlags= xopt(i);
yFlags= yopt(i);
tickStyle= GpLineAttribs(color=color(i), type=1, width=1);
frameStyle= GpLineAttribs(color=color(i), type=1, width=1);
gridStyle= GpLineAttribs(color=color(i), type=3, width=1);
textStyle= GpTextAttribs(color=color(i), font=font(i), height=fontHeight,
alignH=0, alignV=0, opaque=0);
xLabelOff= (xFlags&0x08 ? max(tickLen) : 0) + fontHeight/1.5;
yLabelOff= (yFlags&0x08 ? max(tickLen) : 0) + 0.75*fontHeight;
xTickOff= (xmargin ? xmargin + ((xFlags&0x08) ? max(tickLen) : 0.0) : 0.0);
yTickOff= (ymargin ? ymargin + ((yFlags&0x08) ? max(tickLen) : 0.0) : 0.0);
xTickLen= ((xFlags&0x018)==0x018 ? 2.0*tickLen : tickLen);
yTickLen= ((yFlags&0x018)==0x018 ? 2.0*tickLen : tickLen);
horiz= GaAxisStyle(nMajor=7.5, nMinor=50, logAdjMajor=1.2, logAdjMinor=1.2,
nDigits=nHDigits, gridLevel=1, flags=xFlags,
tickOff=xTickOff, labelOff=xLabelOff, tickLen=xTickLen,
tickStyle=tickStyle, gridStyle=gridStyle,
textStyle=textStyle, xOver=xmax(i),
yOver=ymin(i)-fontHeight);
vert= GaAxisStyle(nMajor=7.5, nMinor=50, logAdjMajor=1.2, logAdjMinor=1.2,
nDigits=nVDigits, gridLevel=1, flags=yFlags,
tickOff=yTickOff, labelOff=yLabelOff, tickLen=yTickLen,
tickStyle=tickStyle, gridStyle=gridStyle,
textStyle=textStyle, xOver=xmin(i),
yOver=ymax(i)+fontHeight);
system(i)= GfakeSystem(viewport=[xmin(i), xmax(i), ymin(i), ymax(i)],
ticks=GaTickStyle(horiz=horiz, vert=vert, frame=1,
frameStyle=frameStyle),
legend=swrite(format="System %d", i));
}
/* layout of the plot legends */
legends = GeLegendBox();
/* layout of the contour legends */
clegends = GeLegendBox();
/* create window and apply plot-style settings */
if (is_void(landscape)) landscape=1n;
set_style, landscape, system, legends, clegends;
//fma;
}
func gg_window (win, width=, height=, dpi=, display=, private=, dump=, hcp=,
landscape=, viewport=, units=, font=, size=, color=,
keep=, xopt=, yopt=, xmargin=, ymargin=)
/* DOCUMENT gg_window, win;
-or- gg_window;
-or- gg_window(...);
Create/switch to graphic window WIN. This routine allows one to
create a window of given size with viewport(s) different from the
default one. Otherwise its behaviour should mimics that of the
"window" builtin routine. If called as a function, the return
value is an array of 6 double values:
[WIN, ONE_PIXEL, XMIN, XMAX, YMIN, YMAX]
where WIN is the window number, ONE_PIXEL is the pixel size in NDC
units and [XMIN,XMAX,YMIN,YMAX] is the bounding box in NDC units.
KEYWORDS
DPI, DISPLAY, PRIVATE, DUMP, HCP - Same options as for the
window builtin routine.
WIDTH, HEIGHT - Window width/height in pixels; default: 450 at 75dpi
and 600 at 100dpi.
LANDSCAPE - Orientation.
VIEWPORT - Viewport coordinates: [XMIN, XMAX, YMIN, YMAX]. Several
viewports can be specified at the same time, in this
case, first dimension of VIEWPORT must be a 4 and
XMIN is VIEWPORT(1,..), XMAX is VIEWPORT(2,..) and so on.
UNITS - Units for the viewport keyword: 0 for NDC (default),
1 for relative, and 2 for pixels.
FONT - Font to use to label axes (see gg_get_font); default is
Helvetica.
SIZE - Text size for axis labels in points; default is 12.
COLOR - Axis color (see gg_get_color); default is foreground.
KEEP - Keep WIN if it already exists? Otherwise the window is
forced to be recreated (this is the default behaviour).
XOPT - Options for X-axis of viewport (see gg_get_axis_flags).
YOPT - Options for Y-axis of viewport (see gg_get_axis_flags).
XMARGIN, YMARGIN -
Note: If you specify multiple viewports, FONT, SIZE, COLOR, XOPT
and YOPT can by different for each viewport.
SEE ALSO gg_get_axis_flags, gg_get_color, gg_get_font, window. */
{
require, "style.i";
/* DPI can be set by pldefault but we have no way to know this value
-- see graph.c */
if (is_void(dpi)) dpi = 100;
else dpi = (dpi < 25 ? 25 : (dpi > 300 ? 300 : long(dpi)));
/* Define some constants -- see gist.h */
ONE_POINT= 0.0013000; // one point in NDC units
ONE_INCH= 72.27*ONE_POINT; // one inch in NDC units
ONE_PIXEL= ONE_INCH/dpi; // one pixel in NDC units
/* Figure out window size in pixels (without top text line) -- called
"topWidth" and "topHeight" in xfancy.c */
if (is_void(width)) width = 6*dpi;
else width = (width <= 31 ? 31 : long(width));
if (is_void(height)) height = 6*dpi;
else height = (height <= 31 ? 31 : long(height));
/* Page size (8.5 x 11 inches) in NDC and pixels -- see xfancy.c */
if (is_void(landscape)) landscape = (width > height);
if (landscape) {
pageWidthNDC = 1.033461;
pageHeightNDC = 0.798584;
} else {
pageWidthNDC = 0.798584;
pageHeightNDC = 1.033461;
}
pageWidth = long(pageWidthNDC/ONE_PIXEL);
pageHeight = long(pageHeightNDC/ONE_PIXEL);
if (width > pageWidth) width= pageWidth;
if (height > pageHeight) height= pageHeight;
/* Compute offsets (really tricky to figure out!) and bounding viewport
-- see xfancy.c */
xoff = (pageWidth - width)/2;
if (landscape) {
//yoff = (pageHeight - height)/2;
yoff = (pageHeight - height + 3)/2;
} else {
//yoff = (pageHeight - height) - (pageWidth - height)/2;
yoff = pageHeight - (pageWidth + height)/2;
}
if (xoff < 0) xoff = 0;
if (yoff < 0) yoff = 0;
vp0_offsets = ONE_PIXEL*[xoff, xoff, yoff, yoff];
vp0_pixel = [width, width, height, height] - 1.0;
vp0_ndc = vp0_offsets + ONE_PIXEL*vp0_pixel;
/* Fix viewport coordinates and figure out how many viewports to make. */
if (is_void(viewport)) {
viewport= [0.15, 0.85, 0.1, 0.8];
units= 1;
}
if (units) {
/* Compute viewport size in pixels, then in NDC. */
if (units==1) {
/* relative units: 0 is min and 1 is max */
viewport *= vp0_pixel;
} else if (units!=2) {
error, "bad value for keyword UNITS (0=NDC, 1=relative, 2=pixels)";
}
viewport = vp0_offsets + ONE_PIXEL*viewport;
}
if (! is_array(viewport) || (dims= dimsof(viewport))(1) < 1 || dims(2) != 4)
error, "bad VIEWPORT";
nvps= numberof(viewport)/4;
/* Parse other parameters. */
if (is_void(xmargin)) xmargin= 2.0;
if (is_void(ymargin)) ymargin= 2.0;
if (is_void(size)) size= 12;
font = gg_map(gg_get_font, font, GG_HELVETICA);
color = gg_map(gg_get_color, color, GG_FG);
xopt = gg_map(gg_get_axis_flags, xopt, 0x33);
yopt = gg_map(gg_get_axis_flags, yopt, 0x33);
if (nvps>1) {
/* Multiple viewports: fix per-viewport settings. */
dims= dims(2:);
dims(1)= numberof(dims)-1;
zero= array(long, dims);
if (is_void(dimsof(xopt, zero))) error, "bad XOPT dimensions";
xopt+= zero;
if (is_void(dimsof(yopt, zero))) error, "bad YOPT dimensions";
yopt+= zero;
if (is_void(dimsof(size, zero))) error, "bad SIZE dimensions";
size+= zero;
if (is_void(dimsof(font, zero))) error, "bad FONT dimensions";
font+= zero;
if (is_void(dimsof(color, zero))) error, "bad COLOR dimensions";
color+= zero;
}
if (numberof(xopt)!=nvps) error, "bad XOPT dimensions";
if (numberof(yopt)!=nvps) error, "bad YOPT dimensions";
if (numberof(size)!=nvps) error, "bad SIZE dimensions";
if (numberof(font)!=nvps) error, "bad FONT dimensions";
if (numberof(color)!=nvps) error, "bad COLOR dimensions";
/* Build up systems. */
nHDigits= 5;
nVDigits= 6;
tickLen= ONE_PIXEL*[5.0, 3.0, 1.0, 0.0, 0.0];
system= array(GfakeSystem, nvps);
viewport= viewport(,*); // concatenate extra dims
for (i=1 ; i<=nvps ; ++i) {
fontHeight= size(i)*ONE_POINT;
xFlags= xopt(i);
yFlags= yopt(i);
tickStyle= GpLineAttribs(color=color(i), type=1, width=1);
frameStyle= GpLineAttribs(color=color(i), type=1, width=1);
gridStyle= GpLineAttribs(color=color(i), type=3, width=1);
textStyle= GpTextAttribs(color=color(i), font=font(i), height=fontHeight,
alignH=0, alignV=0, opaque=0);
xLabelOff= (xFlags&0x08 ? max(tickLen) : 0) + fontHeight/1.5;
yLabelOff= (yFlags&0x08 ? max(tickLen) : 0) + 0.75*fontHeight;
xTickOff= (xmargin
? xmargin*ONE_PIXEL + ((xFlags&0x08) ? max(tickLen) : 0.0)
: 0.0);
yTickOff= (ymargin
? ymargin*ONE_PIXEL + ((yFlags&0x08) ? max(tickLen) : 0.0)
: 0.0);
xTickLen= ((xFlags&0x018)==0x018 ? 2.0*tickLen : tickLen);
yTickLen= ((yFlags&0x018)==0x018 ? 2.0*tickLen : tickLen);
horiz= GaAxisStyle(nMajor=7.5, nMinor=50, logAdjMajor=1.2, logAdjMinor=1.2,
nDigits=nHDigits, gridLevel=1, flags=xFlags,
tickOff=xTickOff, labelOff=xLabelOff, tickLen=xTickLen,
tickStyle=tickStyle, gridStyle=gridStyle,
textStyle=textStyle, xOver=viewport(2,i),
yOver=viewport(3,i)-fontHeight);
vert= GaAxisStyle(nMajor=7.5, nMinor=50, logAdjMajor=1.2, logAdjMinor=1.2,
nDigits=nVDigits, gridLevel=1, flags=yFlags,
tickOff=yTickOff, labelOff=yLabelOff, tickLen=yTickLen,
tickStyle=tickStyle, gridStyle=gridStyle,
textStyle=textStyle, xOver=viewport(1,i),
yOver=viewport(4,i)+fontHeight);
system(i)= GfakeSystem(viewport=viewport(,i),
ticks=GaTickStyle(horiz=horiz, vert=vert, frame=1,
frameStyle=frameStyle),
legend=swrite(format="System %d", i));
}
/* layout of the plot legends */
legends= GeLegendBox();
/* layout of the contour legends */
clegends= GeLegendBox();
/* create window and apply plot-style settings */
if (is_void(win)) win= window();
if (! keep) winkill, win;
window, win, wait=1, width=width, height=height,
display=display, private=private, dump=dump, hcp=hcp, dpi=dpi;
set_style, landscape, system, legends, clegends;
fma;
#if 0
for (i=1;i<=nvps;++i) plbox, viewport(1,i), viewport(3,i), viewport(2,i),
viewport(4,i), color="red", system=0;
#endif
if (! am_subroutine()) {
result = array(double, 6);
result(1) = win;
result(2) = ONE_PIXEL;
result(3:6) = vp0_ndc;
return result;
}
}
/*---------------------------------------------------------------------------*/
/* UTILITIES */
func gg_get_integer (value, name, defvalue, minvalue, maxvalue)
{
if (is_array(value)) {
if (dimsof(value)(1)==0 &&
((structof(value))==long || s==int || s==short || s==char) &&
(is_void(minvalue) || value >= minvalue) &&
(is_void(maxvalue) || value <= maxvalue)) return long(value);
} else if (is_void(value) && ! is_void(defvalue)) return defvalue;
if (is_void(name)) msg = "expecting scalar integer";
else msg = swrite(format="\"%s\" must be a scalar integer", name);
if (! is_void(minvalue)) msg += swrite(format=" >=%d", minvalue);
if (! is_void(maxvalue)) msg += swrite(format=" <=%d", maxvalue);
error, msg;
}
func gg_get_real (value, name, defvalue, minvalue, maxvalue)
{
if (is_array(value)) {
if (dimsof(value)(1)==0 &&
((structof(value))==double || s==float || s==long ||
s==int || s==short || s==char) &&
(is_void(minvalue) || value >= minvalue) &&
(is_void(maxvalue) || value <= maxvalue)) return double(value);
} else if (is_void(value) && ! is_void(defvalue)) return defvalue;
if (is_void(name)) msg = "expecting scalar real";
else msg = swrite(format="\"%s\" must be a scalar real", name);
if (! is_void(minvalue)) msg += swrite(format=" >=%d", minvalue);
if (! is_void(maxvalue)) msg += swrite(format=" <=%d", maxvalue);
error, msg;
}
func gg_get_string (value, name, defvalue)
{
if (structof(value)==string && dimsof(value)(1) == 0) return value;
if (is_void(value) && ! is_void(defvalue)) return defvalue;
if (is_void(name)) msg = "expecting scalar string";
else msg = swrite(format="\"%s\" must be a scalar string", name);
error, msg;
}
func gg_is_ndx (colors) { return (colors >= 0) & (colors < 256); }
func gg_is_rgb (colors) { return (colors < 0) | (colors >= 256); }
func gg_rgb_r (colors) { return colors & 0xff; }
func gg_rgb_g (colors) { return (colors >> 8) & 0xff; }
func gg_rgb_b (colors) { return (colors >> 16) & 0xff; }
func gg_rgb (r,g,b) { return r | (g<<8) | (b<<16) | 0x01000000;}
/* DOCUMENT gg_is_ndx(colors) -- true where COLORS is indexed
-or- gg_is_rgb(colors) -- true where COLORS is RGB
-or- gg_rgb(r,g,b) -- convert to RGB color
-or- gg_rgb_r(colors) -- red intensity of RGB COLORS
-or- gg_rgb_g(colors) -- green intensity of RGB COLORS
-or- gg_rgb_b(colors) -- blue intensity of RGB COLORS
These routines are useful to deal with color values (as integers).
R, G, and B are the components of RGB colors (0=black, 255=maximum).
SEE ALSO gg_get_color. */
local GG_BG, GG_FG, GG_BLACK, GG_WHITE, GG_RED, GG_GREEN, GG_BLUE;
local GG_CYAN, GG_MAGENTA, GG_YELLOW, GG_GUI_BG, GG_GUI_FG;
local GG_GUI_HI, GG_GUI_LO, GG_XOR, GG_EXTRA;
local __gg_color_table;
func gg_get_color (value, name, defvalue)
/* DOCUMENT gg_get_color(value, name, defvalue);
Get color specification from VALUE as an integer value or RGB triplet.
VALUE can have any value recognized by Yorick for the "color" keyword
in builtin plotting functions. If VALUE is nil and DEFVALUE is
specified, DEFVALUE is returned. Optional argument NAME can be used to
set the name of the parameter for error message.
SEE ALSO color, gg_get_font, gg_window. */
/* DOCUMENT gg_color, drw, color;
Set the current color for subsequent drawing on DRW. This must be
called not only when the color changes, but also after any graphics
call to any other window has been made there are two distinct types of
colors: indexed (<=255) and RGB (bit 0x01000000 set, R in LSB, then G,
then B). Additionally, within the indexed colors, there are 16
reserved colors; the gg_palette function sets the rgb values for the
other 240 indexed colors (any colors not set by gg_palette are GG_FG).
The following routines are useful:
GG_IS_NDX(colors) true where COLORS is indexed
GG_IS_RGB(colors) true where COLORS is RGB
GG_RGB(r,g,b) convert to RGB color
GG_R(colors),
GG_G(colors),
GG_B(colors) R, G, and B components of RGB colors (0 black,
255 maximum).
The reserved indexed colors are:
255 GG_BG 251 GG_RED 245 GG_GUI_BG
254 GG_FG 250 GG_GREEN 244 GG_GUI_FG
253 GG_BLACK 249 GG_BLUE 243 GG_GUI_HI
252 GG_WHITE 248 GG_CYAN 242 GG_GUI_LO
247 GG_MAGENTA 241 GG_XOR
246 GG_YELLOW 240 GG_EXTRA
(GG_BG and GG_FG are "background" and "foreground", which the user
may be able to define differently on different screens or workspaces).
SEE ALSO: gg_intro. */
{
if (is_array(value)) {
rank = dimsof(value)(1);
if ((s = structof(value))==long || s==char || s==short || s==int) {
if (rank==0) return int(value) + 256n*(value < 0);
if (rank==1 && numberof(value) == 3) return int(value);
}
if (s==string && rank==0 && h_has(__gg_color_table, value)) {
return h_get(__gg_color_table, value);
}
} else if (is_void(value) && ! is_void(defvalue)) return defvalue;
error, (is_void(name) ? "bad color value"
: "bad color value for \""+name+"\"");
}
GG_BG = 255n;
GG_FG = 254n;
GG_BLACK = 253n;
GG_WHITE = 252n;
GG_RED = 251n;
GG_GREEN = 250n;
GG_BLUE = 249n;
GG_CYAN = 248n;
GG_MAGENTA = 247n;
GG_YELLOW = 246n;
GG_GUI_BG = 245n;
GG_GUI_FG = 244n;
GG_GUI_HI = 243n;
GG_GUI_LO = 242n;
GG_XOR = 241n;
GG_EXTRA = 240n;
__gg_color_table = h_new(bg=GG_BG, fg=GG_FG, black=GG_BLACK, white=GG_WHITE,
red=GG_RED, green=GG_GREEN, blue=GG_BLUE,
cyan=GG_CYAN, magenta=GG_MAGENTA, yellow=GG_YELLOW,
gui_bg=GG_GUI_BG, gui_fg=GG_GUI_FG,
gui_hi=GG_GUI_HI, gui_lo=GG_GUI_LO,
xor=GG_XOR, extra=GG_EXTRA);
func gg_get_font (value, name, defvalue)
/* DOCUMENT gg_get_font(value);
Parse font VALUE which can have any value recognized by Yorick
for the "font" keyword in builtin plotting functions and return the
corresponding integer value. If VALUE is nil, the value of keyword
DEFVALUE is returned. Keyword NAME can be used to set the name of the
parameter for error message.
SEE ALSO font, gg_get_color, gg_window. */
{
if (is_void(value)) return defvalue;
if (is_array(value) && dimsof(value)(1)==0) {
if ((s= structof(value))==long) return value;
if (s==string) {
n = strmatch(value, "B") | 2*strmatch(value, "I");
fn = (n==3 ? strpart(value, 1:-2) : (n ? strpart(value, 1:-1) : value));
if (fn=="courier") return n;
if (fn=="times") return n+4;
if (fn=="helvetica") return n+8;
if (fn=="symbol") return n+12;
if (fn=="schoolbook") return n+16;
error, "bad font name \""+value+"\"";
}
if (s==char || s==short || s==int) return long(value);
}
error, (is_void(name) ? "bad font value"
: "bad font value for \""+name+"\"");
}
func gg_get_axis_flags (value, name, defvalue)
/* DOCUMENT gg_get_axis_flags(value, name, defvalue);
Parse axis specification VALUE which can be an integer or a string
where each bits/character toggle an option (see table below). If VALUE
is nil, the value of keyword DEFVALUE is returned. Keyword NAME can
be used to set the name of the parameter for error message.
char bit option
---- ----- ----------------------------------------------------
t 0x001 Draw ticks on bottom or left edge of viewport
T 0x002 Draw ticks on top or right edge of viewport
c 0x004 Draw ticks centered on origin in middle of viewport
i 0x008 Ticks project inward into viewport
o 0x010 Ticks project outward away from viewport (0x18 for both)
l 0x020 Draw tick label numbers on bottom or left edge of viewport
L 0x040 Draw tick label numbers on top or right edge of viewport
g 0x080 Draw all grid lines down to gridLevel
z 0x100 Draw single grid line at origin
SEE ALSO gg_window. */
{
if (is_void(value)) return defvalue;
if (is_array(value) && dimsof(value)(1)==0) {
if ((s= structof(value))==long) return value;
if (s==string) {
flags= 0;
if (strmatch(value, "t")) flags|= 0x001;
if (strmatch(value, "T")) flags|= 0x002;
if (strmatch(value, "c")) flags|= 0x004;
if (strmatch(value, "i")) flags|= 0x008;
if (strmatch(value, "o")) flags|= 0x010;
if (strmatch(value, "l")) flags|= 0x020;
if (strmatch(value, "L")) flags|= 0x040;
if (strmatch(value, "g")) flags|= 0x080;
if (strmatch(value, "z")) flags|= 0x100;
return flags;
}
if (s==char || s==short || s==int) return long(value);
}
error, (is_void(name) ? "bad axis flags"
: "bad axis flags for \""+name+"\"");
}
GG_COURIER = 0;
GG_TIMES = 4;
GG_HELVETICA = 8;
GG_SYMBOL = 12;
GG_NEWCENTURY = 16;
GG_GUI_FONT = 20;
GG_BOLD = 1;
GG_ITALIC = 2;
GG_OPAQUE = 32;
func gg_get_anchor (value, name, defvalue)
{
if (is_void(value)) return defvalue;
if (is_array(value) && dimsof(value)(1)==0) {
if ((s = structof(value))==string) {
flags = 0;
if (strmatch(value, "w")) flags|= 1;
if (strmatch(value, "e")) flags|= 2;
if (strmatch(value, "n")) flags|= 4;
if (strmatch(value, "w")) flags|= 8;
return flags;
}
if (s==long || s==char || s==short || s==int) return (value&15);
}
error, (is_void(name) ? "bad anchor flags"
: "bad anchor flags for \""+name+"\"");
}
GG_ANCHOR_CENTER = 0;
GG_ANCHOR_W = 1;
GG_ANCHOR_E = 2;
GG_ANCHOR_N = 4;
GG_ANCHOR_S = 8;
/*---------------------------------------------------------------------------*/
/* TEXT JUSTIFICATION */
/* The default text justification, justify="NN" is normal is both the
horizontal and vertical directions. Other possibilities are "L", "C",
or "R" for the first character, meaning left, center, and right
horizontal justification, and "T", "C", "H", "A", or "B", meaning top,
capline, half, baseline, and bottom vertical justification. The normal
justification "NN" is equivalent to "LA". Common values are "LA", "CA",
and "RA" for garden variety left, center, and right justified text, with
the y coordinate at the baseline of the last line in the string
presented to plt. The characters labeling the right axis of a plot are
"RH", so that the y value of the text will match the y value of the
corresponding tick. Similarly, the characters labeling the bottom axis
of a plot are "CT". The justify= may also be a number,
horizontal+vertical, where horizontal is 0 for "N", 1 for "L", 2 for
"C", or 3 for "R", and vertical is 0 for "N", 4 for "T", 8 for "C", 12
for "H", 16 for "A", or 20 for "B".
| HORIZONTAL VERTICAL
| 0 N normal 0 N normal
| 1 L left 4 T top
| 2 C center 8 C capline
| 3 R right 12 H half (center)
| 16 A baseline
| 20 B bottom
|
| ANCHOR JUSTIFY
| l LH 13
| c CH 14
| r RH 15
| tl LT 5
| t CT 6
| tr RT 7
| bl LB 21
| b CB 22
| br RB 23
*/
func __gg_text_l (text, x0, y0, x1, y1, bd, font, height, color) {
plt, text, x0 + one_pixel*(bd + 1), 0.5*(y0 + y1), justify=13,
font=font, height=height, color=color, opaque=0; }
func __gg_text_c(text, x0, y0, x1, y1, bd, font, height, color) {
plt, text, 0.5*(x0 + x1), 0.5*(y0 + y1), justify=14,
font=font, height=height, color=color, opaque=0; }
func __gg_text_r(text, x0, y0, x1, y1, bd, font, height, color) {
plt, text, x1 - one_pixel*(bd + 1), 0.5*(y0 + y1), justify=15,
font=font, height=height, color=color, opaque=0; }
func __gg_text_tl(text, x0, y0, x1, y1, bd, font, height, color) {
s = one_pixel*(bd + 1);
plt, text, x0 + s, y1 - s, justify=5,
font=font, height=height, color=color, opaque=0; }
func __gg_text_t(text, x0, y0, x1, y1, bd, font, height, color) {
plt, text, 0.5*(x0 + x1), y1 - one_pixel*(bd + 1), justify=6,
font=font, height=height, color=color, opaque=0; }
func __gg_text_tr(text, x0, y0, x1, y1, bd, font, height, color) {
s = one_pixel*(bd + 1);
plt, text, x1 - s, y1 - s, justify=7,
font=font, height=height, color=color, opaque=0; }
func __gg_text_bl (text, x0, y0, x1, y1, bd, font, height, color) {
s = one_pixel*(bd + 1);
plt, text, x0 + s, y0 + s, justify=21,
font=font, height=height, color=color, opaque=0; }
func __gg_text_b(text, x0, y0, x1, y1, bd, font, height, color) {
plt, text, 0.5*(x0 + x1), y0 + one_pixel*(bd + 1), justify=22,
font=font, height=height, color=color, opaque=0; }
func __gg_text_br(text, x0, y0, x1, y1, bd, font, height, color) {
s = one_pixel*(bd + 1);
plt, text, x1 - s, y0 + s, justify=23,
font=font, height=height, color=color, opaque=0; }
local __gg_justify_table ;
func gg_get_justify(value, name, defvalue)
{
if (is_void(value)) value = "CH";
if (structof(value) == string && dimsof(value)(1)==0 &&
h_has(__gg_justify_table, value)) return h_get(__gg_justify_table, value);
error, "bad JUSTIFY value";
}
__gg_justify_table = h_new(LH=__gg_text_l, CH=__gg_text_c, RH=__gg_text_r,
LT=__gg_text_tl, CT=__gg_text_t, RT=__gg_text_tr,
LB=__gg_text_bl, CB=__gg_text_b, RB=__gg_text_br);
/*---------------------------------------------------------------------------*/
/* 3D BORDER AND RELIEF */
local GG_FLAT ;
local GG_SOLID ;
local GG_SUNKEN ;
local GG_RAISED ;
local GG_GROOVE ;
local GG_RIDGE ;
local __gg_relief_table ;
func gg_get_relief(value, name, defvalue)
/* DOCUMENT gg_get_relief(value);
Get value (as a scalar integer) of relief setting. If VALUE is nil,
the value of keyword DEFVALUE is returned; otherwise, VALUE must be
one of:
0 GG_FLAT "flat"
1 GG_SOLID "solid"
2 GG_SUNKEN "sunken"
3 GG_RAISED "raised"
4 GG_GROOVE "groove"
5 GG_RIDGE "ridge"
Keyword NAME can be used to set the name of the parameter for error
message.
SEE ALSO gg_get_color, gg_get_font, gg_get_integer, gg_get_real,
gg_get_axis_flags. */
{
if (is_void(value)) return defvalue;
if (is_array(value) && dimsof(value)(1)==0) {
if (((s= structof(value))==long || s==char || s==short || s==int)) {
if (value >=0 && value <= 5) return long(value);
} else if (s==string && h_has(__gg_relief_table, value)) {
return h_get(__gg_relief_table, value);
}
}
error, (is_void(name) ? "bad relief value"
: "bad relief value for \""+name+"\"");
}
GG_FLAT = 0;
GG_SOLID = 1;
GG_SUNKEN = 2;
GG_RAISED = 3;
GG_GROOVE = 4;
GG_RIDGE = 5;
__gg_relief_table = h_new(flat=GG_FLAT, solid=GG_SOLID,
sunken=GG_SUNKEN, raised=GG_RAISED,
groove=GG_GROOVE, ridge=GG_RIDGE);
func gg_draw_3d_rect (x0, y0, x1, y1, one_pixel,
bd, relief, bg, fg, hi, lo)
/* DOCUMENT gg_draw_3d_rect, x0, y0, x1, y1, one_pixel,
bd, relief, bg, fg, hi, lo;
Draw 3D rectangle onto current coordinate system. (X0,Y0,X1,Y1) is
the bounding box of rectangle (included) in units of current plotting
system (NDC if plotting system is 0). ONE_PIXEL is the pixel size in
units of current plotting system. BD is the border width in pixels.
RELIEF is the type of relief (see gg_get_relief) FG is the foreground
color (used for "solid" border). BG is the background color (no
background get drawn if set to GG_EXTRA) HI and LO are the colors used
to draw bright/dark parts of the 3D border. All colors (BG, FG, HI
and LO) must be given as scalar integer.
SEE ALSO: gg_get_relief. */
{
draw_border = ((bd > 0)&&(relief != GG_FLAT));
if (numberof(bg) != 1 || bg != GG_EXTRA) {
/* Draw background. (FIXME: there is a one pixel offset bug in pli one
the right and at the bottom). */
_y0 = y0 - one_pixel;
_x1 = x1 + one_pixel
if (draw_border) {
s = bd*one_pixel;
pli, array(char(bg), 1, 1), x0+s, _y0+s, _x1-s, y1-s;
} else {
pli, array(char(bg), 1, 1), x0, _y0, _x1, y1;
}
}
if (draw_border) {
/* Draw border. */
if (relief == GG_SOLID) {
if (numberof(bg) != 1 || fg != GG_EXTRA) {
/* Draw a sort of spiral. */
npts = 1 + bd*4;
xpts = ypts = array(double, npts);
s = one_pixel*indgen(0:bd-1);
xpts(1) = x0;
xpts(2::4) = xpts(3::4) = x1 - s;
xpts(4::4) = xpts(5::4) = x0 + s;
ypts(1:-1:4) = ypts(2::4) = y0 + s;
ypts(3::4) = ypts(4::4) = y1 - s;
ypts(0) = y0 + bd*one_pixel;
plg, ypts, xpts, width=0, color=fg;
}
} else if (relief == GG_SUNKEN) {
__gg_draw_3d_rect_bd, x0, y0, x1, y1, bd, lo, hi;
} else if (relief == GG_RAISED) {
__gg_draw_3d_rect_bd, x0, y0, x1, y1, bd, hi, lo;
} else if (relief == GG_RIDGE) {
if ((hbd = bd/2) > 0) __gg_draw_3d_rect_bd, x0, y0, x1, y1, hbd, lo, hi;
h = one_pixel*hbd;
__gg_draw_3d_rect_bd, x0+h, y0+h, x1-h, y1-h, bd-hbd, hi, lo;
} else if (relief == GG_GROOVE) {
if ((hbd = bd/2) > 0) __gg_draw_3d_rect_bd, x0, y0, x1, y1, hbd, hi, lo;
h = one_pixel*hbd;
__gg_draw_3d_rect_bd, x0+h, y0+h, x1-h, y1-h, bd-hbd, lo, hi;
}
}
}
func __gg_draw3 (x0, y0, x1, y1, x2, y2, color)
{ pldj, [x0, x1], [y0, y1], [x1, x2], [y1, y2], color=color, width=0; }
func __gg_draw_3d_rect_bd (x0, y0, x1, y1, bd, top, bot)
/* DOCUMENT __gg_draw_3d_rect_bd, x0, y0, x1, y1, bd, top, bot;
-or- __gg_draw3, x0, y0, x1, y1, x2, y2, color;
Private routines used by gg_draw_3d_rect to paint the 3D border. Top
and left sides of border get drawn with color TOP, bottom and right
sides get drawn with color BOT.
SEE ALSO: gg_draw_3d_rect. */
{
/**/extern one_pixel;
s = one_pixel*indgen(0:bd-1);
x1 -= s;
y1 -= s;
x0 += s;
y0 += s;
__gg_draw3, x0,y0, x0,y1, x1,y1, top;
__gg_draw3, x0+one_pixel,y0, x1,y0, x1,y1-one_pixel, bot;
}
/*---------------------------------------------------------------------------*/
/* INTERACTION WITH MOUSE */
func gg_find_clicked (top, ms)
/* DOCUMENT gg_find_clicked(top, ms)
Get widget managed by container TOP which received click event as
defined by MS. MS has the same contents/meaning as the result of the
builtin mouse() function. A widget is eligible as the receiver of the
click event if the button press and release events have occured over
this widget. The return value may be nil if no children of TOP is
eligible for the click event. If several widgets are eligible the
topmost one is returned.
SEE ALSO: mouse, gg_find. */
{
/* Check that button was released over same widget. */
match = gg_find(top, ms(5), ms(6));
if (is_hash(match) &&
(x = ms(7)) >= match.x0 && x <= match.x1 &&
(y = ms(8)) >= match.y0 && y <= match.y1) return match;
}
func gg_find (widget, xndc, yndc, match)
/* DOCUMENT gg_find(widget, xndc, yndc)
-or- gg_find(widget, xndc, yndc, match)
Get topmost widget under NDC coordinates (XNDC,YNDC) starting at
WIDGET. Optional argument MATCH is the returned value if no
descendant of WIDGET, nor WIDGET itself, is found under (XNDC,YNDC).
If MATCH is the same as WIDGET, it is assumed that WIDGET is under
(XNDC,YNDC) regardless its actual bounding box (i.e. no check is done
for WIDGET itself in this case).
SEE ALSO: mouse, gg_find_clicked. */
{
if (match != widget) {
if (xndc < widget.x0 || xndc > widget.x1 ||
yndc < widget.y0 || yndc > widget.y1) return match;
match = widget;
}
for (widget=widget.child ; is_hash(widget) ; widget=widget.next) {
if (xndc >= widget.x0 && xndc <= widget.x1 &&
yndc >= widget.y0 && yndc <= widget.y1) {
if (h_has(widget, child=)) match = gg_find(widget, xndc, yndc, widget);
else match = widget;
}
}
return match;
}
func gg_zoom (ms, factor)
/* DOCUMENT gg_zoom, ms;
-or- gg_zoom, ms, zoom_factor;
Mimics the "zooming with the mouse" feature of Gist interactive windows.
Argument MS is identical to the return value of the mouse() function:
MS = [x_pressed, y_pressed, x_released, y_released,
xndc_pressed, yndc_pressed, xndc_released, yndc_released,
system, button, modifiers];
Optional second argument can be used to set the zoom factor to a
real >=1.0. With ZOOM_FACTOR=1.0, you can still drag the viewing
region.
For instance, the following statements will interactively
zoom in/out forever:
for (;;) {
ms = mouse(-1,2,"");
if (is_void(ms)) break;
gg_zoom, ms;
}
SEE ALSO: gg_window, limits, mouse, unzoom. */
{
sys = long(ms(9));
if (sys == 0) return;
factor = gg_get_real(factor, "zoom_factor",
1.41421356237309504880168872421, 1.0);
btn = long(ms(10));
if (btn == 1) q = 1.0/(s = factor); // zoom in
else if (btn == 2) s = q = 1.0; // no zoom, just drag
else if (btn == 3) s = 1.0/(q = factor); // zoom out
else return;
/* EQUATIONS:
* New bounds: x'bnd = (xbnd - xoff)/s where: bnd = min or max
* s = factor for zoom-in
* 1/factor for zoom-out
* New/old widths: w = xmax - xmin
* w' = x'max - x'min = w/s
* Dragging implies: u = (x1 - xmin)/w = (x0 - x'min)/w'
* = (x0 - (xmin - xoff)/s)/(w/s)
* therefore: xoff = x1 - s*x0.
*/
/* We take care to only change bounds if clicked point (X0,X0) is
_strictly_ between limits (beware that we may have XMAX <= XMIN). We
do clip X1 and Y1 within the bounds which is not really necessary if
MS is the result of a call to mouse() since this function already does
that. */
old_sys = plsys(sys);
lim = limits(); /* LIM = [XMIN, XMAX, YMIN, YMAX, FLAGS]; */
x0 = ms(1);
if ((x0 - (xmin = lim(1)))*(x0 - (xmax = lim(2))) < 0.0) {
x1 = (xmax >= xmin ? max(xmin, min(xmax, ms(3)))
: max(xmax, min(xmin, ms(3))));
xoff = x1 - s*x0;
xmod = (xoff != 0.0 || s != 1.0);
if (xmod) { xmin = (xmin - xoff)*q; xmax = (xmax - xoff)*q; }
} else xmod = 0n;
y0 = ms(2);
if ((y0 - (ymin = lim(3)))*(y0 - (ymax = lim(4))) < 0.0) {
y1 = (ymax >= ymin ? max(ymin, min(ymax, ms(4)))
: max(ymax, min(ymin, ms(4))));
yoff = y1 - s*y0;
ymod = (yoff != 0.0 || s != 1.0);
if (ymod) { ymin = (ymin - yoff)*q; ymax = (ymax - yoff)*q; }
} else ymod = 0n;
if (xmod || ymod) limits, xmin, xmax, ymin, ymax;
if (old_sys != sys) plsys, old_sys;
}
/*---------------------------------------------------------------------------*/
/* UTILITIES */
func gg_map (op, arg, defvalue)
/* DOCUMENT gg_map(op, arg);
-or- gg_map(op, arg, defvalue);
Map scalar function OP onto array argument ARG to mimics element-wise
unary operation. If ARG is NIL, DEFVALUE is returned.
*/
{
if ((n= numberof(arg))==0) return defvalue;
/* use structof to avoid unecessary string duplication for string result */
out= array(structof((out1= op(arg(1)))), dimsof(arg));
out(1)= out1;
for (i=2 ; i<=n ; ++i) out(i)= op(arg(i));
return out;
}
/*---------------------------------------------------------------------------*/
/* DEFAULT SETTINGS (must be the last one to have all constants defined). */
func gg_default (fg=, bg=, hi=, lo=, bd=, font=, fontsize=, pad=,
dpi=, buttonrelief=, labelrelief=, framerelief=)
{
GG_DEFAULT_FG = gg_get_color(fg, "fg", GG_DEFAULT_FG);
GG_DEFAULT_BG = gg_get_color(bg, "bg", GG_DEFAULT_BG);
GG_DEFAULT_HI = gg_get_color(hi, "hi", GG_DEFAULT_HI);
GG_DEFAULT_LO = gg_get_color(lo, "lo", GG_DEFAULT_LO);
GG_DEFAULT_BD = gg_get_integer(bd, "bd", GG_DEFAULT_BD, 0);
GG_DEFAULT_PAD = gg_get_integer(padx, "pad", GG_DEFAULT_PAD, 0);
GG_DEFAULT_DPI = gg_get_integer(dpi, "dpi", GG_DEFAULT_DPI, 25, 300);
GG_DEFAULT_FONT = gg_get_font(font, "font", GG_DEFAULT_FONT);
GG_DEFAULT_FONTSIZE = gg_get_integer(fontsize, "fontsize",
GG_DEFAULT_FONTSIZE, 4, 50);
GG_DEFAULT_BUTTON_RELIEF = gg_get_relief(buttonrelief, "buttonrelief",
GG_DEFAULT_BUTTON_RELIEF);
GG_DEFAULT_LABEL_RELIEF = gg_get_relief(labelrelief, "labelrelief",
GG_DEFAULT_LABEL_RELIEF);
GG_DEFAULT_FRAME_RELIEF = gg_get_relief(framerelief, "framerelief",
GG_DEFAULT_FRAME_RELIEF);
}
GG_DEFAULT_FG = [0x00, 0x00, 0x00];
GG_DEFAULT_BG = [0xdc, 0xdc, 0xdc];
GG_DEFAULT_HI = [0xf2, 0xf2, 0xf2];
GG_DEFAULT_LO = [0x84, 0x84, 0x84];
GG_DEFAULT_BD = 3;
GG_DEFAULT_PAD = 2;
GG_DEFAULT_DPI = 150;
GG_DEFAULT_FONT = GG_HELVETICA|GG_BOLD;
GG_DEFAULT_FONTSIZE = 8;
GG_DEFAULT_BUTTON_RELIEF = GG_RAISED;
GG_DEFAULT_LABEL_RELIEF = GG_FLAT;
GG_DEFAULT_FRAME_RELIEF = GG_SUNKEN;
/*---------------------------------------------------------------------------*/
|