/* Part of XPCE --- The SWI-Prolog GUI toolkit Author: Jan Wielemaker and Anjo Anjewierden E-mail: jan@swi.psy.uva.nl WWW: http://www.swi.psy.uva.nl/projects/xpce/ Copyright (c) 1985-2002, University of Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include static status orientationGraphical(Graphical gr, Name orientation); static Point getCenterGraphical(Graphical gr); static status updateHideExposeConnectionsGraphical(Graphical gr); static Any getContainerGraphical(Any gr); /******************************** * CREATE/DESTROY * ********************************/ status initialiseGraphical(Any obj, Int x, Int y, Int w, Int h) { Graphical gr = obj; Class class = classOfObject(gr); assign(gr, displayed, OFF); assign(gr, area, newObject(ClassArea, EAV)); assign(gr, selected, OFF); assign(gr, name, class->name); assign(gr, inverted, OFF); assign(gr, active, ON); obtainClassVariablesObject(obj); if ( class->solid == ON ) setFlag(gr, F_SOLID); setArea(gr->area, x, y, w, h); succeed; } status unlinkGraphical(Graphical gr) { if ( notNil(gr->layout_interface) ) freeObject(gr->layout_interface); /* another message? */ /* very dubious, but it can't */ /* be in class dialog_item */ if ( onFlag(gr, F_ATTRIBUTE) || instanceOfObject(gr, ClassDialogItem) ) { aboveGraphical(gr, NIL); belowGraphical(gr, NIL); rightGraphical(gr, NIL); leftGraphical(gr, NIL); } disconnectGraphical(gr, DEFAULT, DEFAULT, DEFAULT, DEFAULT); DeviceGraphical(gr, NIL); succeed; } status copyGraphical(Any obj1, Any obj2) { Graphical gr1 = obj1; Graphical gr2 = obj2; copyArea(gr1->area, gr2->area); /*assign(gr1, device, gr2->device); very dubious */ assign(gr1, pen, gr2->pen); assign(gr1, texture, gr2->texture); assign(gr1, handles, gr2->handles); assign(gr1, selected, gr2->selected); assign(gr1, inverted, gr2->inverted); assign(gr1, displayed, gr2->displayed); assign(gr1, colour, gr2->colour); assign(gr1, cursor, gr2->cursor); assign(gr1, name, gr2->name); succeed; } /******************************** * CONVERT * ********************************/ static Graphical getConvertGraphical(Class class, Any obj) { Graphical gr; if ( isObject(obj) && hasGetMethodObject(obj, NAME_image) && (gr = get(obj, NAME_image, EAV)) && instanceOfObject(gr, ClassGraphical) ) answer(gr); fail; } /******************************** * DISPLAY/ERASE * ********************************/ static status displayOnGraphical(Graphical gr, Device dev) { TRY( DeviceGraphical(gr, dev) ); return DisplayedGraphical(gr, ON); } status DeviceGraphical(Any obj, Device dev) { Graphical gr = obj; if ( gr->device == dev ) succeed; return qadSendv(obj, NAME_device, 1, (Any *) &dev); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - We do a bit of checking here as this code is often called bypassing the message-passing checking and we want more graceful crashes of something bad happens. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ status deviceGraphical(Any obj, Device dev) { Graphical gr = obj; if ( notNil(dev) && isNil(dev->graphicals) ) return errorPce(dev, NAME_notInitialised); if ( !isObject(obj) || isFreedObj(obj) ) return errorPce(PCE, NAME_freedObject, obj); if ( notNil(gr->device) ) qadSendv(gr->device, NAME_erase, 1, (Any *) &gr); if ( notNil(dev) ) appendDevice(dev, gr); succeed; } status reparentGraphical(Graphical gr) { if ( notNil(gr->connections) ) { Cell cell; for_cell(cell, gr->connections) updateDeviceConnection(cell->value); } succeed; } status DisplayedGraphical(Any obj, BoolObj val) { Graphical gr = obj; if ( gr->displayed != val ) qadSendv(obj, NAME_displayed, 1, (Any *)&val); succeed; } status displayedGraphical(Any obj, BoolObj val) { Graphical gr = obj; if ( gr->displayed != val ) { if ( val == ON ) assign(gr, displayed, val); if ( notNil(gr->device) ) { if ( notNil(gr->request_compute) ) { PceWindow sw = getWindowGraphical(gr); if ( sw && sw->displayed == ON ) ComputeGraphical(gr); } displayedGraphicalDevice(gr->device, gr, val); } if ( val == OFF ) assign(gr, displayed, val); } succeed; } BoolObj getIsDisplayedGraphical(Graphical gr, Device dev) { do { if ( gr->displayed == ON && gr->device == dev ) return ON; if ( gr->displayed == OFF ) return OFF; gr = (Graphical) gr->device; } while( notNil(gr) ); return isDefault(dev) ? ON : OFF; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initialise the device on which the graphical is displayed. This function is called before ->drawSelf and sets the device for the primitive drawing functions and returns the absolute area to be able to draw the graphical. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ status initialiseDeviceGraphical(Any obj, int *x, int *y, int *w, int *h) { Graphical gr = obj; *x = valInt(gr->area->x); *y = valInt(gr->area->y); *w = valInt(gr->area->w); *h = valInt(gr->area->h); succeed; } status initialiseRedrawAreaGraphical(Any obj, Area a, int *x, int *y, int *w, int *h, IArea redraw) { iarea a2; Graphical gr = obj; initialiseDeviceGraphical(obj, x, y, w, h); redraw->x = *x + valInt(a->x) - valInt(gr->area->x); /* normalised! */ redraw->y = *y + valInt(a->y) - valInt(gr->area->y); redraw->w = valInt(a->w); redraw->h = valInt(a->h); a2.x = *x, a2.y = *y, a2.w = *w, a2.h = *h; NormaliseArea(a2.x, a2.y, a2.w, a2.h); intersection_iarea(redraw, &a2); succeed; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Computes the offset caused by intermediate devices to the windows coordinate system. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ status offsetDeviceGraphical(Any obj, int *x, int *y) { Graphical gr = obj; register Device dev = gr->device; *x = 0; *y = 0; while( notNil(dev) && !instanceOfObject(dev, ClassWindow) ) { Point p = dev->offset; *x += valInt(p->x); *y += valInt(p->y); dev = dev->device; } succeed; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Compute the area of a graphical in the coordinate system of the real drawing device (the picture). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ Area getAbsoluteAreaGraphical(Graphical gr, Device device) { if ( gr->device == device || isNil(gr->device) ) answer(gr->area); else { Device dev = gr->device; int x, y; x = valInt(gr->area->x); y = valInt(gr->area->y); while( notNil(dev) && !instanceOfObject(dev, ClassWindow) && dev != device ) { x += valInt(dev->offset->x); y += valInt(dev->offset->y); dev = dev->device; } answer(answerObject(ClassArea, toInt(x), toInt(y), gr->area->w, gr->area->h, EAV)); } } Graphical getRootGraphical(Graphical gr) { for(;; gr = (Graphical) gr->device) { if ( isNil(gr->device) ) answer(gr); } } PceWindow getWindowGraphical(Graphical gr) { while( notNil(gr) && !instanceOfObject(gr, ClassWindow) ) gr = (Graphical) gr->device; if ( notNil(gr) ) answer((PceWindow) gr); fail; } FrameObj getFrameGraphical(Graphical gr) { Graphical root = getRootGraphical(gr); if ( instanceOfObject(root, ClassWindow) ) { PceWindow sw = (PceWindow) root; if ( notNil(sw->frame) ) return sw->frame; } fail; } DisplayObj getDisplayGraphical(Graphical gr) { FrameObj fr = getFrameGraphical(gr); if ( fr ) answer(fr->display); fail; } Monitor getMonitorGraphical(Graphical gr) { DisplayObj d = getDisplayGraphical(gr); Point pt = NIL; Monitor mon = NULL; ComputeGraphical(gr); if ( (d = getDisplayGraphical(gr)) && (pt = getDisplayPositionGraphical(gr)) ) { Area a = tempObject(ClassArea, pt->x, pt->y, gr->area->w, gr->area->h, EAV); mon = getMonitorDisplay(d, a); considerPreserveObject(a); } doneObject(pt); answer(mon); } Application getApplicationGraphical(Graphical gr) { FrameObj fr = getFrameGraphical(gr); if ( fr && notNil(fr->application) ) answer(fr->application); fail; } Device getCommonDeviceGraphical(Graphical gr1, Graphical gr2) { register Device dev1 = gr1->device; register Device dev2 = gr2->device; if ( dev1 == dev2 ) /* very common case */ { if ( notNil(dev1) ) answer(dev1); fail; } /* Get the same level */ if ( isNil(dev2) ) fail; while( notNil(dev1) && valInt(dev1->level) > valInt(dev2->level) ) dev1 = dev1->device; if ( isNil(dev1) ) fail; while( notNil(dev2) && valInt(dev2->level) > valInt(dev1->level) ) dev2 = dev2->device; /* Walk along both branches */ while( notNil(dev1) && notNil(dev2) ) { if ( dev1 == dev2 ) answer(dev1); dev1 = dev1->device; dev2 = dev2->device; } fail; } /******************************** * CHANGES * ********************************/ int get_extension_margin_graphical(Graphical gr) { if ( instanceOfObject(gr, ClassText) || instanceOfObject(gr, ClassDialogItem) ) { int m = 5; if ( instanceOfObject(gr, ClassButton) ) { Button b = (Button)gr; if ( b->look == NAME_motif || b->look == NAME_gtk ) m = GTK_BUTTON_MARGIN + 1; } return m; } return 0; } status changedAreaGraphical(Any obj, Int x, Int y, Int w, Int h) { Graphical gr = obj; if ( notNil(gr->device) && gr->displayed == ON ) { Device d; int offx=0, offy=0; /* Offset to the window */ requestComputeDevice(gr->device, DEFAULT); updateConnectionsGraphical(gr, gr->device->level); if ( notNil(gr->layout_interface) ) changedAreaLayoutInterface(gr->layout_interface); for(d = gr->device; notNil(d); d = d->device) { if ( d->displayed == OFF ) break; offx += valInt(d->offset->x); offy += valInt(d->offset->y); if ( instanceOfObject(d, ClassWindow) ) { PceWindow sw = (PceWindow) d; Area a = gr->area; int ox = valInt(x), oy = valInt(y), ow = valInt(w), oh = valInt(h); int cx = valInt(a->x), cy = valInt(a->y), cw = valInt(a->w), ch = valInt(a->h); int m; if ( !createdWindow(sw) ) break; NormaliseArea(ox, oy, ow, oh); NormaliseArea(cx, cy, cw, ch); ox += offx; oy += offy; cx += offx; cy += offy; /* HACKS ... */ if ( (m = get_extension_margin_graphical(gr)) ) { int m2 = m*2; ox -= m; oy -= m; ow += m2; oh += m2; cx -= m; cy -= m; cw += m2; ch += m2; } /* end hacks! */ changed_window(sw, ox, oy, ow, oh, TRUE); changed_window(sw, cx, cy, cw, ch, offFlag(gr, F_SOLID)); addChain(ChangedWindows, sw); break; /* A window stops propagation */ } } } if ( onFlag(gr, F_CONSTRAINT) ) return updateConstraintsObject(gr); succeed; } status changedImageGraphical(Any obj, Int x, Int y, Int w, Int h) { Graphical gr = obj; Device d; int ox=0, oy=0; /* Offset to the window */ if ( instanceOfObject(obj, ClassWindow) ) d = obj; else if ( gr->displayed != ON ) succeed; else d = gr->device; for(; notNil(d); d = d->device) { if ( d->displayed == OFF ) succeed; ox += valInt(d->offset->x); oy += valInt(d->offset->y); if ( instanceOfObject(d, ClassWindow) ) { PceWindow sw = (PceWindow) d; int cx, cy, cw, ch; if ( !createdWindow(sw) ) succeed; if ( isDefault(x) ) x = ZERO; if ( isDefault(y) ) y = ZERO; if ( isDefault(w) ) w = gr->area->w; if ( isDefault(h) ) h = gr->area->h; cx = valInt(x) + valInt(gr->area->x), cy = valInt(y) + valInt(gr->area->y), cw = valInt(w), ch = valInt(h); NormaliseArea(cx, cy, cw, ch); cx += ox; cy += oy; if ( instanceOfObject(gr, ClassText) || instanceOfObject(gr, ClassDialogItem) ) { cx -= 5; cy -= 5; cw += 10; ch += 10; } /* Motif hack */ DEBUG(NAME_changesData, Cprintf("Change of %s --> %d %d %d %d%s\n", pp(obj), cx, cy, cw, ch, offFlag(gr, F_SOLID) ? " clear" : " no clear")); changed_window(sw, cx, cy, cw, ch, offFlag(gr, F_SOLID)); addChain(ChangedWindows, sw); break; } } succeed; } status changedEntireImageGraphical(Any obj) { Graphical gr = obj; return changedImageGraphical(gr, ZERO, ZERO, gr->area->w, gr->area->h); } status redrawGraphical(Graphical gr, Area a) { if ( isDefault(a) ) return changedEntireImageGraphical(gr); return changedImageGraphical(gr, a->x, a->y, a->w, a->h); } /******************************** * COMPUTING * ********************************/ status requestComputeGraphical(Any obj, Any val) { Graphical gr = obj; if ( isFreeingObj(gr) || /* not needed */ (notNil(gr->request_compute) && isDefault(val)) || gr->request_compute == val ) succeed; if ( isDefault(val) ) { val = ON; } else if ( isNil(val) ) { assign(gr, request_compute, val); succeed; } if ( notNil(gr->request_compute) && gr->request_compute != val ) ComputeGraphical(gr); assign(gr, request_compute, val); if ( instanceOfObject(gr, ClassWindow) && gr->displayed == ON ) { if ( !memberChain(ChangedWindows, gr) ) { DEBUG(NAME_window, Cprintf("Adding %s to ChangedWindows\n", pp(gr))); prependChain(ChangedWindows, gr); } } else if ( notNil(gr->device) ) { appendChain(gr->device->recompute, gr); requestComputeGraphical((Graphical) gr->device, DEFAULT); } succeed; } status ComputeGraphical(Any obj) { Graphical gr = obj; if ( notNil(gr->request_compute) && !isFreeingObj(gr) ) { qadSendv(gr, NAME_compute, 0, NULL); assign(gr, request_compute, NIL); } succeed; } static status computeGraphical(Graphical gr) { assign(gr, request_compute, NIL); succeed; } /******************************** * REPAINT * ********************************/ static void selection_bubble(int x, int y, int w, int h, int wx, int wy) { int bw = min(5, w); int bh = min(5, h); int bx = x + (w - bw) * wx / 2; int by = y + (h - bh) * wy / 2; r_fill(bx, by, bw, bh, BLACK_COLOUR); /*r_complement(bx, by, bw, bh);*/ } static status drawGraphical(Graphical gr, Point offset, Area area) { int ox = 0; int oy = 0; if ( notDefault(offset) ) { ox = valInt(offset->x); oy = valInt(offset->y); } if ( isDefault(area) ) { static Area large_area = NULL; if ( !large_area ) large_area = globalObject(NIL, ClassArea, toInt(PCE_MIN_INT/2), toInt(PCE_MIN_INT/2), toInt(PCE_MAX_INT), toInt(PCE_MAX_INT), EAV); area = large_area; } r_offset(ox, oy); RedrawArea(gr, area); r_offset(-ox, -oy); succeed; } #define InitAreaA int ax = valInt(a->x), ay = valInt(a->y), \ aw = valInt(a->w), ah = valInt(a->h) #define InitAreaB int bx = valInt(b->x), by = valInt(b->y), \ bw = valInt(b->w), bh = valInt(b->h) static status overlapExtendedAreaGraphical(Graphical gr, Area b) { int m; Area a = gr->area; InitAreaA; InitAreaB; NormaliseArea(ax, ay, aw, ah); /* b is normalised */ if ( (m = get_extension_margin_graphical(gr)) ) { int m2 = 2*m; ax -= m; ay -= m; aw += m2; ah += m2; } if (by > ay+ah || by+bh < ay || bx > ax+aw || bx+bw < ax) fail; succeed; } status RedrawArea(Any obj, Area area) { Graphical gr = obj; Any ofg; int fix = 0; int clearbg = 0; struct colour_context ctx; status rval; Any inactive_colour; ComputeGraphical(obj); /* should not be necessary: */ if ( !( gr->area == area || /* image->draw_in and friends */ ( gr->displayed == ON && overlapExtendedAreaGraphical(gr, area) ) ) ) succeed; if ( gr->active == OFF && (inactive_colour = getClassVariableValueObject(gr, NAME_inactiveColour)) && notNil(inactive_colour) ) { fix++; r_fix_colours(inactive_colour, DEFAULT, &ctx); } else if ( gr->selected == ON ) { PceWindow sw = getWindowGraphical(gr); if ( sw ) { Any feedback = sw->selection_feedback; if ( instanceOfObject(feedback, ClassColour) ) { fix++; r_fix_colours(feedback, DEFAULT, &ctx); } else if ( feedback == NAME_colour ) { Any c1, c2; c1 = getClassVariableValueObject(obj, NAME_selectedForeground); c2 = getClassVariableValueObject(obj, NAME_selectedBackground); fix++; clearbg++; r_fix_colours(c1, c2, &ctx); } } } if ( !fix && notDefault(gr->colour) ) ofg = r_default_colour(gr->colour); else ofg = NULL; if ( instanceOfObject(gr, ClassWindow) ) /* Must be quicker */ { PceWindow sw = (PceWindow) gr; if ( !createdWindow(sw) ) updatePositionWindow(sw); rval = RedrawAreaGraphical(sw, area); } else { if ( clearbg ) { int x, y, w, h; initialiseDeviceGraphical(obj, &x, &y, &w, &h); r_clear(x, y, w, h); } rval = qadSendv(gr, NAME_RedrawArea, 1, (Any *)&area); } if ( fix ) r_unfix_colours(&ctx); else if ( ofg ) r_default_colour(ofg); return rval; } status paintSelectedGraphical(Graphical gr) { PceWindow sw = getWindowGraphical(gr); Any feedback; if ( sw ) feedback = sw->selection_feedback; else fail; if ( notNil(feedback) ) { int x, y, w, h; initialiseDeviceGraphical(gr, &x, &y, &w, &h); if ( feedback == (Any) NAME_invert ) { r_complement(x, y, w, h); } else if ( feedback == (Any) NAME_handles ) { Name which = getClassVariableValueObject(gr, NAME_selectionHandles); if ( which == NAME_corners ) { selection_bubble(x, y, w, h, 0, 0); selection_bubble(x, y, w, h, 0, 2); selection_bubble(x, y, w, h, 2, 0); selection_bubble(x, y, w, h, 2, 2); } else if ( which == NAME_sides ) { selection_bubble(x, y, w, h, 0, 1); selection_bubble(x, y, w, h, 1, 0); selection_bubble(x, y, w, h, 1, 2); selection_bubble(x, y, w, h, 2, 1); } else if ( which == NAME_line ) { paintSelectedLine((Line)gr); } else if ( which == NAME_cornersAndSides ) { selection_bubble(x, y, w, h, 0, 0); selection_bubble(x, y, w, h, 0, 2); selection_bubble(x, y, w, h, 2, 0); selection_bubble(x, y, w, h, 2, 2); selection_bubble(x, y, w, h, 0, 1); selection_bubble(x, y, w, h, 1, 0); selection_bubble(x, y, w, h, 1, 2); selection_bubble(x, y, w, h, 2, 1); } } else if ( instanceOfObject(feedback, ClassElevation) ) { r_3d_box(x, y, w, h, 0, feedback, TRUE); } } succeed; } status RedrawAreaGraphical(Any obj, Area area) { Graphical gr = obj; if ( gr->inverted == ON ) { int x, y, w, h; initialiseDeviceGraphical(gr, &x, &y, &w, &h); r_complement(x, y, w, h); } if ( gr->selected == ON ) qadSendv(gr, NAME_paintSelected, 0, NULL); succeed; } status flushGraphical(Any gr) { PceWindow sw; if ( (sw = getWindowGraphical(gr)) ) flushWindow(sw); succeed; } status synchroniseGraphical(Graphical gr, BoolObj always) { DisplayObj d; static long last; if ( always != ON ) { long now = mclock(); if ( now - last < 50 ) succeed; last = now; } if ( (d = getDisplayGraphical(gr)) ) synchroniseDisplay(d); succeed; } /******************************** * HIDE/EXPOSE * ********************************/ static status hideGraphical(Any obj1, Any obj2) { Graphical gr1 = obj1; Graphical gr2 = obj2; if ( notNil(gr1->device) && (isDefault(gr2) || gr2->device == gr1->device) ) { hideDevice(gr1->device, gr1, gr2); updateHideExposeConnectionsGraphical(gr1); } succeed; } status exposeGraphical(Any obj1, Any obj2) { Graphical gr1 = obj1; Graphical gr2 = obj2; if ( notNil(gr1->device) && (isDefault(gr2) || gr2->device == gr1->device) ) { exposeDevice(gr1->device, gr1, gr2); updateHideExposeConnectionsGraphical(gr1); } succeed; } static status swapGraphical(Any obj1, Any obj2) { Graphical gr1 = obj1; Graphical gr2 = obj2; if ( gr1->device == gr2->device && notNil(gr1->device) ) swapGraphicalsDevice(gr1->device, gr1, gr2); succeed; } /******************************** * GEOMETRY MANAGEMENT * ********************************/ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The user accessible geometry management method (->x, ->size, etc.) are defined on the super-class graphical. All subclasses use these methods. All these methods are translated into Graphical ->set: [x], [y], [w], [h]. Most of these translations are done using hard-coded functions calls. To allow for sub-classes of graphical to specialise geometry management, Graphical ->set invokes ->geometry: [x], [y], [w], [h]. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ status setGraphical(Any obj, Int x, Int y, Int w, Int h) { Graphical gr = obj; #define Changed(a) (gr->area->a != a && notDefault(a)) if ( Changed(x) || Changed(y) || Changed(w) || Changed(h) ) { Int av[4]; av[0] = x; av[1] = y; av[2] = w; av[3] = h; return qadSendv(gr, NAME_requestGeometry, 4, av); } #undef Changed succeed; } status doSetGraphical(Any obj, Int x, Int y, Int w, Int h) { Graphical gr = obj; #define Changed(a) (gr->area->a != a && notDefault(a)) if ( Changed(x) || Changed(y) || Changed(w) || Changed(h) ) { Int av[4]; av[0] = x; av[1] = y; av[2] = w; av[3] = h; return qadSendv(gr, NAME_geometry, 4, av); } #undef Changed succeed; } status requestGeometryGraphical(Any gr, Int x, Int y, Int w, Int h) { Int av[4]; av[0] = x; av[1] = y; av[2] = w; av[3] = h; return qadSendv(gr, NAME_geometry, 4, av); } status geometryGraphical(Any obj, Int x, Int y, Int w, Int h) { Graphical gr = obj; CHANGING_GRAPHICAL(gr, setArea(gr->area, x, y, w, h)); succeed; } static status areaGraphical(Graphical gr, Area area) { return setGraphical(gr, area->x, area->y, area->w, area->h); } status xGraphical(Graphical gr, Int x) { return setGraphical(gr, x, DEFAULT, DEFAULT, DEFAULT); } status yGraphical(Graphical gr, Int y) { return setGraphical(gr, DEFAULT, y, DEFAULT, DEFAULT); } static status widthGraphical(Graphical gr, Int w) { return setGraphical(gr, DEFAULT, DEFAULT, w, DEFAULT); } status heightGraphical(Graphical gr, Int h) { return setGraphical(gr, DEFAULT, DEFAULT, DEFAULT, h); } status positionGraphical(Graphical gr, Point pos) { return setGraphical(gr, pos->x, pos->y, DEFAULT, DEFAULT); } static status sizeGraphical(Graphical gr, Size size) { return setGraphical(gr, DEFAULT, DEFAULT, size->w, size->h); } static status setCornerGraphical(Graphical gr, Int x, Int y) { if ( isDefault(x) ) x = add(gr->area->x, gr->area->w); if ( isDefault(y) ) y = add(gr->area->y, gr->area->h); return setGraphical(gr, DEFAULT, DEFAULT, sub(x, gr->area->x), sub(y, gr->area->y)); } static status cornerGraphical(Graphical gr, Point pos) { return setCornerGraphical(gr, pos->x, pos->y); } static status cornerXGraphical(Graphical gr, Int x) { return setCornerGraphical(gr, x, DEFAULT); } static status cornerYGraphical(Graphical gr, Int y) { return setCornerGraphical(gr, DEFAULT, y); } status centerGraphical(Graphical gr, Point pos) { ComputeGraphical(gr); return setGraphical(gr, dif(pos->x, gr->area->w), dif(pos->y, gr->area->h), DEFAULT, DEFAULT); } static status centerXGraphical(Graphical gr, Int c) { ComputeGraphical(gr); return setGraphical(gr, dif(c, gr->area->w), DEFAULT, DEFAULT, DEFAULT); } static status centerYGraphical(Graphical gr, Int c) { ComputeGraphical(gr); return setGraphical(gr, DEFAULT, dif(c, gr->area->h), DEFAULT, DEFAULT); } status relativeMoveGraphical(Graphical gr, Point pos) { ComputeGraphical(gr); return setGraphical(gr, add(gr->area->x, pos->x), add(gr->area->y, pos->y), DEFAULT, DEFAULT); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Highly dubious. What about lines and bitmaps? Should the result always be normalised? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static status rotateGraphical(Graphical gr, Int degrees) { int d; Point pos; Size size; d = valInt(degrees); if ((d%90) != 0) return errorPce(gr, NAME_rotate90); d %= 360; if (d == 90 || d == 270) { pos = getCenterGraphical(gr); size = getSizeGraphical(gr); CHANGING_GRAPHICAL(gr, widthGraphical(gr, size->h); heightGraphical(gr, size->w); centerGraphical(gr, pos)); } succeed; } /******************************** * RESIZING * ********************************/ status init_resize_graphical(Any obj, Real xfactor, Real yfactor, Point origin, float *xf, float *yf, int *ox, int *oy) { *xf = valReal(xfactor); *yf = (isDefault(yfactor) ? *xf : valReal(yfactor)); if ( notDefault(origin) ) { *ox = valInt(origin->x); *oy = valInt(origin->y); } succeed; } static status resizeGraphical(Graphical gr, Real xfactor, Real yfactor, Point origin) { float xf, yf; int ox = valInt(gr->area->x); int oy = valInt(gr->area->y); int nx, ny, nw, nh; Any av[4]; init_resize_graphical(gr, xfactor, yfactor, origin, &xf, &yf, &ox, &oy); if ( xf == 1.0 && yf == 1.0 ) succeed; nx = ox + rfloat((float) (valInt(gr->area->x)-ox) * xf); ny = oy + rfloat((float) (valInt(gr->area->y)-oy) * yf); nw = rfloat((float) valInt(gr->area->w) * xf); nh = rfloat((float) valInt(gr->area->h) * yf); av[0] = toInt(nx); av[1] = toInt(ny); av[2] = toInt(nw); av[3] = toInt(nh); return qadSendv(gr, NAME_doSet, 4, av); } /******************************** * NORMALISATION * ********************************/ static status normaliseGraphical(Graphical gr) { return orientationGraphical(gr, NAME_northWest); } static status orientationGraphical(Graphical gr, Name orientation) { if ( instanceOfObject(gr, ClassBox) || instanceOfObject(gr, ClassCircle) || instanceOfObject(gr, ClassEllipse) ) orientationArea(gr->area, orientation); succeed; } static Name getOrientationGraphical(Graphical gr) { answer( getOrientationArea(gr->area) ); } /******************************** * GET AREA ATTRIBUTES * ********************************/ Area getAreaGraphical(Graphical gr) { ComputeGraphical(gr); answer(gr->area); } Int getXGraphical(Graphical gr) { answer(getAreaGraphical(gr)->x); } Int getYGraphical(Graphical gr) { answer(getAreaGraphical(gr)->y); } static Int getCornerXGraphical(Graphical gr) { answer(add(getAreaGraphical(gr)->x, getAreaGraphical(gr)->w)); } static Int getCornerYGraphical(Graphical gr) { answer(add(getAreaGraphical(gr)->y, getAreaGraphical(gr)->h)); } Int getWidthGraphical(Graphical gr) { answer(getAreaGraphical(gr)->w); } Int getHeightGraphical(Graphical gr) { answer(getAreaGraphical(gr)->h); } Int getLeftSideGraphical(Graphical gr) { Area a = getAreaGraphical(gr); if ( valInt(a->w) >= 0 ) answer(a->x); else answer(add(a->x, a->w)); } Int getRightSideGraphical(Graphical gr) { Area a = getAreaGraphical(gr); if ( valInt(a->w) >= 0 ) answer(add(a->x, a->w)); else answer(a->x); } static Int getTopSideGraphical(Graphical gr) { Area a = getAreaGraphical(gr); if ( valInt(a->h) >= 0 ) answer(a->y); else answer(add(a->y, a->h)); } Int getBottomSideGraphical(Graphical gr) { Area a = getAreaGraphical(gr); if ( valInt(a->h) >= 0 ) answer(add(a->y, a->h)); else answer(a->y); } static status rightSideGraphical(Graphical gr, Int right) { Int cl = getLeftSideGraphical(gr); Int av[4]; av[0] = av[1] = av[3] = (Int) DEFAULT; av[2] = sub(right, cl); return qadSendv(gr, NAME_doSet, 4, av); } static status leftSideGraphical(Graphical gr, Int left) { Int cr = getRightSideGraphical(gr); Int av[4]; av[0] = av[1] = av[3] = (Int) DEFAULT; av[2] = sub(cr, left); return qadSendv(gr, NAME_doSet, 4, av); } static status bottomSideGraphical(Graphical gr, Int bottom) { Int ct = getTopSideGraphical(gr); Int av[4]; av[0] = av[1] = av[2] = (Int) DEFAULT; av[3] = sub(bottom, ct); return qadSendv(gr, NAME_doSet, 4, av); } static status topSideGraphical(Graphical gr, Int top) { Int cb = getBottomSideGraphical(gr); Int av[4]; av[0] = av[1] = av[2] = (Int) DEFAULT; av[3] = sub(cb, top); return qadSendv(gr, NAME_doSet, 4, av); } Point getPositionGraphical(Graphical gr) { answer(answerObject(ClassPoint,getAreaGraphical(gr)->x, getAreaGraphical(gr)->y,EAV)); } status get_absolute_xy_graphical(Graphical gr, Device *dev, Int *X, Int *Y) { int x, y; DEBUG(NAME_absolutePosition, Cprintf("get_absolutePosition(%s, %s) ... ", pp(gr), pp(*dev))); ComputeGraphical(gr); x = valInt(gr->area->x); y = valInt(gr->area->y); while( !instanceOfObject(gr->device, ClassWindow) && !isNil(gr->device) && gr->device != *dev ) { Point offset = gr->device->offset; x += valInt(offset->x); y += valInt(offset->y); gr = (Graphical)gr->device; } if ( notDefault(*dev) && gr->device != *dev ) { DEBUG(NAME_absolutePosition, Cprintf("failed\n")); fail; } *dev = gr->device; *X = toInt(x); *Y = toInt(y); DEBUG(NAME_absolutePosition, Cprintf("X=%s; Y=%s\n", pp(*X), pp(*Y))); succeed; } Int getAbsoluteXGraphical(Any gr, Device dev) { Int x, y; TRY( get_absolute_xy_graphical(gr, &dev, &x, &y) ); answer(x); } Int getAbsoluteYGraphical(Any gr, Device dev) { Int x, y; TRY( get_absolute_xy_graphical(gr, &dev, &x, &y) ); answer(y); } static Point getAbsolutePositionGraphical(Graphical gr, Device dev) { Int x, y; TRY( get_absolute_xy_graphical(gr, &dev, &x, &y) ); answer(answerObject(ClassPoint, x, y, EAV)); } Point getDisplayPositionGraphical(Graphical gr) { Int x, y; int ox, oy, wx, wy; PceWindow w = DEFAULT; /* relative to window system */ if ( instanceOfObject(gr, ClassWindow) ) { x = y = ZERO; w = (PceWindow) gr; ox = oy = 0; } else { get_absolute_xy_graphical(gr, (Device *)&w, &x, &y); if ( !instanceOfObject(w, ClassWindow) ) fail; /* not displayed */ offset_window(w, &ox, &oy); } /* relative to display */ get_display_position_window(w, &wx, &wy); x = toInt(valInt(x) + ox + wx); y = toInt(valInt(y) + oy + wy); answer(answerObject(ClassPoint, x, y, EAV)); } Size getSizeGraphical(Graphical gr) { answer(answerObject(ClassSize, getAreaGraphical(gr)->w, getAreaGraphical(gr)->h, EAV)); } static Point getCornerGraphical(Graphical gr) { Area a = getAreaGraphical(gr); answer(answerObject(ClassPoint, add(a->x,a->w), add(a->y,a->h), EAV)); } static Point getCenterGraphical(Graphical gr) { Area a = getAreaGraphical(gr); answer(answerObject(ClassPoint, mid(a->x,a->w), mid(a->y,a->h), EAV)); } static Int getCenterXGraphical(Graphical gr) { Area a = getAreaGraphical(gr); answer(mid(a->x, a->w)); } static Int getCenterYGraphical(Graphical gr) { Area a = getAreaGraphical(gr); answer(mid(a->y, a->h)); } static Area getBoundingBoxGraphical(Graphical gr) { answer(getAreaGraphical(gr)); } static Int getDistanceGraphical(Graphical gr, Graphical gr2) { answer(getDistanceArea(gr->area, gr2->area)); } static Int getDistanceXGraphical(Graphical gr, Graphical gr2) { answer(getDistanceXArea(gr->area, gr2->area)); } static Int getDistanceYGraphical(Graphical gr, Graphical gr2) { answer(getDistanceYArea(gr->area, gr2->area)); } /******************************* * DIALOG POSITIONS * *******************************/ status appendDialogItemNetworkDevice(Device dev, Graphical gr1) { Graphical gr2; if ( notNil(gr1) && ((Graphical)getContainerGraphical(gr1))->device != dev ) { send(gr1, NAME_autoAlign, ON, EAV); DEBUG(NAME_dialog, Cprintf("Adding %s to %s\n", pp(gr1), pp(dev))); displayDevice(dev, gr1, DEFAULT); if ( (gr2 = get(gr1, NAME_left, EAV)) ) appendDialogItemNetworkDevice(dev, gr2); if ( (gr2 = get(gr1, NAME_right, EAV)) ) appendDialogItemNetworkDevice(dev, gr2); if ( (gr2 = get(gr1, NAME_above, EAV)) ) appendDialogItemNetworkDevice(dev, gr2); if ( (gr2 = get(gr1, NAME_below, EAV)) ) appendDialogItemNetworkDevice(dev, gr2); } succeed; } static status same_device(Graphical gr1, Graphical gr2) { gr1 = getContainerGraphical(gr1); gr2 = getContainerGraphical(gr2); if ( notNil(gr1) && notNil(gr2) && gr1->device != gr2->device ) { if ( isNil(gr1->device) ) appendDialogItemNetworkDevice(gr2->device, gr1); else if ( isNil(gr2->device) ) appendDialogItemNetworkDevice(gr1->device, gr2); else return errorPce(gr1, NAME_alreadyShown, 12, gr2->device); } succeed; } static status assignDialogItem(Graphical gr, Name slot, Any value) { Variable var; Graphical gr2; DEBUG(NAME_left, Cprintf("assignDialogItem(%s, %s, %s)\n", pp(gr), pp(slot), pp(value))); if ( (var = getInstanceVariableClass(classOfObject(gr), slot)) && var->context == ClassDialogItem ) return sendVariable(var, gr, value); if ( isNil(value) ) deleteAttributeObject(gr, slot); else attributeObject(gr, slot, value); if ( (gr2=getContainerGraphical(gr)) != gr ) assignDialogItem(gr2, slot, value); succeed; } status aboveGraphical(Graphical gr1, Graphical gr2) { Graphical gr; TRY(same_device(gr1, gr2)); if ( notNil(gr2) ) { belowGraphical(gr2, NIL); assignDialogItem(gr2, NAME_below, gr1); } if ( (gr = get(gr1, NAME_above, EAV)) && notNil(gr) ) assignDialogItem(gr, NAME_below, NIL); assignDialogItem(gr1, NAME_above, gr2); succeed; } status belowGraphical(Graphical gr1, Graphical gr2) { Graphical gr; TRY(same_device(gr1, gr2)); if ( notNil(gr2) ) { aboveGraphical(gr2, NIL); assignDialogItem(gr2, NAME_above, gr1); } if ( (gr = get(gr1, NAME_below, EAV)) && notNil(gr) ) assignDialogItem(gr, NAME_above, NIL); assignDialogItem(gr1, NAME_below, gr2); succeed; } status rightGraphical(Graphical gr1, Graphical gr2) { Graphical gr; DEBUG(NAME_left, Cprintf("rightGraphical(%s,%s)\n", pp(gr1), pp(gr2))); TRY(same_device(gr1, gr2)); if ( notNil(gr2) ) { leftGraphical(gr2, NIL); assignDialogItem(gr2, NAME_left, gr1); } if ( (gr = get(gr1, NAME_right, EAV)) && notNil(gr) ) assignDialogItem(gr, NAME_left, NIL); assignDialogItem(gr1, NAME_right, gr2); succeed; } status leftGraphical(Graphical gr1, Graphical gr2) { Graphical gr; DEBUG(NAME_left, Cprintf("leftGraphical(%s,%s)\n", pp(gr1), pp(gr2))); TRY(same_device(gr1, gr2)); if ( notNil(gr2) ) { rightGraphical(gr2, NIL); assignDialogItem(gr2, NAME_right, gr1); } if ( (gr = get(gr1, NAME_right, EAV)) && notNil(gr) ) assignDialogItem(gr, NAME_right, NIL); assignDialogItem(gr1, NAME_left, gr2); succeed; } status referenceGraphical(Graphical gr, Point ref) { return assignDialogItem(gr, NAME_reference, ref); } static status alignmentGraphical(Graphical gr, Name alignment) { return assignDialogItem(gr, NAME_alignment, alignment); } static Name getAlignmentGraphical(Graphical gr) { Name alignment; if ( isName(alignment = getAttributeObject(gr, NAME_alignment)) ) answer(alignment); if ( isName(alignment = getClassVariableValueObject(gr, NAME_alignment)) ) answer(alignment); answer(NAME_left); } static status autoAlignGraphical(Graphical gr, BoolObj align) { return assignDialogItem(gr, NAME_autoAlign, align); } static status autoLabelAlignGraphical(Graphical gr, BoolObj val) { return assignDialogItem(gr, NAME_autoLabelAlign, val); } static status autoValueAlignGraphical(Graphical gr, BoolObj val) { return assignDialogItem(gr, NAME_autoValueAlign, val); } static BoolObj getAutoAlignGraphical(Graphical gr) { BoolObj rval; if ( (rval = getAttributeObject(gr, NAME_autoAlign)) && instanceOfObject(rval, ClassBool) ) answer(rval); if ( onFlag(gr, F_ATTRIBUTE) ) { if ( getAttributeObject(gr, NAME_above) || getAttributeObject(gr, NAME_below) || getAttributeObject(gr, NAME_left) || getAttributeObject(gr, NAME_right) ) answer(ON); } answer(OFF); } static BoolObj getAutoLabelAlignGraphical(Graphical gr) { BoolObj rval; if ( (rval = getAttributeObject(gr, NAME_autoLabelAlign)) && instanceOfObject(rval, ClassBool) ) answer(rval); answer(OFF); } static BoolObj getAutoValueAlignGraphical(Graphical gr) { BoolObj rval; if ( (rval = getAttributeObject(gr, NAME_autoValueAlign)) && instanceOfObject(rval, ClassBool) ) answer(rval); answer(OFF); } static status layoutDialogGraphical(Graphical gr) { ComputeGraphical(gr); succeed; } /******************************** * PEN * ********************************/ status penGraphical(Graphical gr, Int pen) { if (gr->pen != pen) { CHANGING_GRAPHICAL(gr, assign(gr, pen, pen); changedEntireImageGraphical(gr)); } succeed; } status shadowGraphical(Graphical gr, Int s) { return assignGraphical(gr, NAME_shadow, s); } status fillPatternGraphical(Graphical gr, Image pattern) { return assignGraphical(gr, NAME_fillPattern, pattern); } status fillOffsetGraphical(Graphical gr, Point pattern) { return assignGraphical(gr, NAME_fillOffset, pattern); } static status textureGraphical(Graphical gr, Name texture) { if (gr->texture != texture) { CHANGING_GRAPHICAL(gr, assign(gr, texture, texture); changedEntireImageGraphical(gr)); } succeed; } status colourGraphical(Graphical gr, Any c) { if ( gr->colour != c ) { CHANGING_GRAPHICAL(gr, assign(gr, colour, c); changedEntireImageGraphical(gr)); } succeed; } Any getDisplayColourGraphical(Graphical gr) { while( notNil(gr) ) { if ( notDefault(gr->colour) ) answer(gr->colour); gr = (Graphical) gr->device; } fail; } /******************************** * SELECTION * ********************************/ static status toggleSelectedGraphical(Graphical gr) { return send(gr, NAME_selected, gr->selected == ON ? OFF : ON, EAV); } static status selectedGraphical(Graphical gr, BoolObj val) { if (gr->selected != val) { CHANGING_GRAPHICAL(gr, assign(gr, selected, val); changedEntireImageGraphical(gr)); } succeed; } /******************************** * HANDLES * ********************************/ static status handleGraphical(Graphical gr, Handle h) { if (isNil(gr->handles)) assign(gr, handles, newObject(ClassChain, EAV)); return appendChain(gr->handles, h); } Handle getHandleGraphical(Graphical gr, Name name) { Class class; if ( notNil(gr->handles) ) { Cell cell; for_cell(cell, gr->handles) { Handle h = cell->value; if ( h->name == name ) answer(h); } } class = classOfObject(gr); if ( notNil(class->handles) ) { Cell cell; for_cell(cell, class->handles) { Handle h = cell->value; if ( h->name == name ) answer(h); } } fail; } Point getHandlePositionGraphical(Graphical gr, Name name, Device dev) { Int x, y; Handle h; if ( isDefault(dev) ) dev = gr->device; TRY(h = getHandleGraphical(gr, name)); TRY(x = getXHandle(h, gr, dev)); TRY(y = getYHandle(h, gr, dev)); answer(answerObject(ClassPoint, x, y, EAV)); } Chain getHandlesGraphical(Graphical gr, Point pos, Name kind, Int distance) { int maxdx=0, maxdy=0; int px=0, py=0; Cell cell; Class class; Chain rval = NIL; int use_range; if ( notDefault(distance) && notDefault(pos) ) { px = valInt(pos->x); py = valInt(pos->y); maxdx = (valInt(distance) * valInt(gr->area->w) + 99) / 100; maxdy = (valInt(distance) * valInt(gr->area->h) + 99) / 100; use_range = TRUE; } else use_range = FALSE; if ( notNil(gr->handles) ) { for_cell(cell, gr->handles) { Handle h = cell->value; int hx, hy; if ( notDefault(kind) && h->kind != kind ) continue; if ( use_range ) { hx = valInt(getXHandle(h, gr, gr->device)); hy = valInt(getYHandle(h, gr, gr->device)); if ( abs(hx-px) > maxdx || abs(hy-py) > maxdy ) continue; } if ( isNil(rval) ) rval = answerObject(ClassChain, h, EAV); else appendChain(rval, h); } } class = classOfObject(gr); if ( notNil(class->handles) ) { for_cell(cell, class->handles) { Handle h = cell->value; int hx, hy; if ( notDefault(kind) && h->kind != kind ) continue; if ( use_range ) { hx = valInt(getXHandle(h, gr, gr->device)); hy = valInt(getYHandle(h, gr, gr->device)); if ( abs(hx-px) > maxdx || abs(hy-py) > maxdy ) continue; } if ( isNil(rval) ) rval = answerObject(ClassChain, h, EAV); else appendChain(rval, h); } } if ( notNil(rval) ) answer(rval); fail; } /******************************** * MASKING PATTERNS * ********************************/ static status invertedGraphical(Graphical gr, BoolObj val) { if ( gr->inverted != val ) CHANGING_GRAPHICAL(gr, assign(gr, inverted, val); changedEntireImageGraphical(gr)); succeed; } status activeGraphical(Graphical gr, BoolObj val) { if ( gr->active != val ) { CHANGING_GRAPHICAL(gr, assign(gr, active, val); changedEntireImageGraphical(gr)); } succeed; } /******************************** * CURSOR * ********************************/ static status cursorGraphical(Graphical gr, CursorObj cursor) { PceWindow w = getWindowGraphical(gr); assign(gr, cursor, cursor); if ( w ) updateCursorWindow(w); flushGraphical(gr); succeed; } status focusCursorGraphical(Graphical gr, CursorObj cursor) { PceWindow w = getWindowGraphical(gr); if ( w ) return focusCursorWindow(w, cursor); succeed; } static CursorObj getDisplayedCursorGraphical(Graphical gr) { answer(gr->cursor); } /******************************** * FOCUS * ********************************/ status focusGraphical(Graphical gr, Recogniser recogniser, CursorObj cursor, Name button) { PceWindow sw = getWindowGraphical(gr); if ( sw != FAIL ) focusWindow(sw, gr, recogniser, cursor, button); succeed; } static status WantsKeyboardFocusGraphical(Graphical gr) { fail; } /******************************** * CONNECTIONS * ********************************/ /* Update connections due to move/resize */ status updateConnectionsGraphical(Graphical gr, Int level) { if ( notNil(gr->connections) ) { Cell cell; for_cell(cell, gr->connections) { Connection c = cell->value; if ( notNil(c->device) && valInt(c->device->level) <= valInt(level) ) requestComputeGraphical(cell->value, DEFAULT); } } if ( instanceOfObject(gr, ClassWindow) ) /* HACK */ updatePositionWindow((PceWindow) gr); succeed; } static status updateHideExposeConnectionsGraphical(Graphical gr) { if ( notNil(gr->connections) ) { Cell cell; for_cell(cell, gr->connections) updateHideExposeConnection(cell->value); } succeed; } status connectGraphical(Graphical gr, Graphical gr2, Link link, Name from, Name to) { if ( get(link, NAME_connection, gr, gr2, from, to, EAV) ) succeed; fail; } status attachConnectionGraphical(Graphical gr, Connection c) { if ( isNil(gr->connections) ) assign(gr, connections, newObject(ClassChain, c, EAV)); else appendChain(gr->connections, c); succeed; } status detachConnectionGraphical(Graphical gr, Connection c) { if ( notNil(gr->connections) && deleteChain(gr->connections, c) && emptyChain(gr->connections) ) assign(gr, connections, NIL); succeed; } static status match_connection(Connection c, Link link, Name from, Name to) { if ( (c->link == link || isDefault(link)) && (c->from_handle == from || isDefault(from)) && (c->to_handle == to || isDefault(to)) ) succeed; fail; } static Connection getConnectedGraphical(Graphical gr, Graphical gr2, Link link, Name from, Name to) { Chain ch; Cell cell; if ( notNil(ch = gr->connections) ) { for_cell(cell, ch) { Connection c = cell->value; if ( (isDefault(gr2) || c->to == gr2 || c->from == gr2) && match_connection(c, link, from, to) ) answer(c); } } fail; } status connectedGraphical(Graphical gr, Graphical gr2, Link link, Name from, Name to) { return getConnectedGraphical(gr, gr2, link, from, to) ? SUCCEED : FAIL; } status disconnectGraphical(Graphical gr, Graphical gr2, Link link, Name from, Name to) { Chain ch; if ( notNil(ch = gr->connections) ) { Connection c; for_chain(ch, c, if ( (isDefault(gr2) || c->to == gr2 || c->from == gr2) && match_connection(c, link, from, to) ) freeObject(c)); } succeed; } static Chain getConnectionsGraphical(Graphical gr, Graphical gr2, Link link, Name from, Name to) { Chain ch; Cell cell; Chain rval = NIL; if ( isDefault(gr2) && isDefault(link) && isDefault(from) && isDefault(to) ) { if ( notNil(gr->connections) ) answer(gr->connections); fail; } if ( notNil(ch = gr->connections) ) { for_cell(cell, ch) { Connection c = cell->value; if ( (isDefault(gr2) || c->to == gr2 || c->from == gr2) && match_connection(c, link, from, to) ) { if ( isNil(rval) ) rval = newObject(ClassChain, c, EAV); else appendChain(rval, c); } } if ( notNil(rval) ) answer(rval); } fail; } static status extendNetworkGraphical(Graphical gr, Link link, Name from, Name to, Chain members) { if ( memberChain(members, gr) == SUCCEED ) succeed; appendChain(members, gr); if ( notNil(gr->connections) ) { Cell cell; for_cell(cell, gr->connections) { Connection c = cell->value; if ( match_connection(c, link, from, to) ) extendNetworkGraphical((c->to == gr ? c->from : c->to), link, from, to, members); } } succeed; } static Chain getNetworkGraphical(Graphical gr, Link link, Name from, Name to) { Chain connections; connections = answerObject(ClassChain, EAV); extendNetworkGraphical(gr, link, from, to, connections); answer(connections); } /******************************** * LAYOUT * ********************************/ /* (AA) send(Graphical, layout, C1, C2, C3, C4, C5, C6) Heuristic layout of a graph (based on an algorithm given in: Eades, P. (1984), "A Heuristic for Graph Drawing", Congressus Numerantium, vol. 42, pp. 149-160. All figures (indirectly) related to Figure, i.e. the graph, are moved such: (a) the distance between two figures which are connected is constant and (b) the distance between two figures which are not connected is as large as possible. The algorithm computes the forces on each figure (the links can be seen as springs), where connected figures attract and unconnected figures repel each other. The force on each figure is computed repeatedly, and incremental use of the algorithm produces better results until a stable state is reached. The algorithm is suitable for graphs in which the vertices are initially placed at random. */ /* Attraction of connected vertices is given by: C1 * log(d/C2) Repelling of unconnected vertices is given by: -C3 / sqrt(d) where d is the distance between the vertices. ** Tue Sep 15 15:14:42 1987 anjo@swivax.uucp (Anjo Anjewierden) */ static int forceAttract(int d, float C1, float C2) { if ( d < 10 ) d = 10; return (int) (2048.0 * C1 * log((float)d/C2)) / d; } static int forceRepel(int d, float C3) { if ( d < 10 ) d = 10; return (int) (-2048.0 * C3 / sqrt((float)d)) / d; } typedef struct { int fx; /* force in X-direction */ int fy; /* force in Y-direction */ Connection c; /* the connection */ Int ideal_len; /* ideal length */ } lg_relation; typedef struct { Graphical gr; /* the graphical */ iarea area; /* its current area */ unsigned update : 1; /* update position */ unsigned moved : 1; /* we moved it */ unsigned fixed : 1; /* do not move this one */ } lg_object; static inline int cx_object(lg_object *o) { return o->area.x + o->area.w/2; } static inline int cy_object(lg_object *o) { return o->area.y + o->area.h/2; } static void place_object(lg_object *o) { if ( o->update ) { Any av[4]; o->update = FALSE; av[0] = toInt(o->area.x); av[1] = toInt(o->area.y); if ( o->gr->area->x != av[0] || o->gr->area->y != av[1] ) { av[2] = DEFAULT; av[3] = DEFAULT; qadSendv(o->gr, NAME_set, 4, av); } } } static int distance_area(IArea a, IArea b) { int bx = b->x - a->x; /* normalise on (ax,ay) == (0,0) */ int by = b->y - a->y; if (a->h < by) /* a above b */ { if (bx+b->w < 0) /* b left a */ return(distance(bx+b->w, by, 0, a->h)); if (bx > a->w) /* a left b */ return(distance(a->w, a->h, bx, by)); return(by-(a->h)); } if (by+b->h < 0) /* b above a */ { if (a->w < bx) return(distance(a->w, 0, bx, by+b->h)); if (bx+b->w < 0) return(distance(bx+b->w, by+b->h, 0, 0)); return(-(by+b->h)); } if (a->w < bx) /* a and b equal height */ return(bx-(a->w)); if (bx+b->w < 0) return(-(bx+b->w)); return(0); /* overlap */ } static status layoutGraphical(Graphical gr, Real argC1, /* strength of connections */ Real argC2, /* natural distance */ Real argC3, /* strength of not-connected */ Int argC4, /* addaption-speed */ Int argC5, /* max iterations */ Area area, /* Bounce objects in this area */ Chain work, /* network to layout */ Chain move_only) /* only move these */ { lg_relation **r; /* relation matrix */ lg_object *objects; /* object array */ lg_object *op; /* current object */ int force; int dx, dy, d; int n, l, i, j; Cell cell; float C1 = (isDefault(argC1) ? 2.0 : valReal(argC1)); float C2 = (isDefault(argC2) ? 30.0 : valReal(argC2)); float C3 = (isDefault(argC3) ? 2.0 : valReal(argC3)); int C4 = (isDefault(argC4) ? 15 : valInt(argC4)); int C5 = (isDefault(argC5) ? 100 : valInt(argC5)); int moved; Chain network; iarea limit; if ( isNil(gr->device) ) fail; if ( isDefault(area) ) { limit.x = limit.y = 5; limit.w = limit.h = INT_MAX/2; } else { limit.x = valInt(area->x); limit.y = valInt(area->y); limit.w = valInt(area->w); limit.h = valInt(area->h); NormaliseArea(limit.x, limit.y, limit.w, limit.h); } if ( isDefault(work) ) network = get(gr, NAME_network, EAV); else network = work; n = valInt(getSizeChain(network)); if ( n <= 1 ) /* nothing to be done */ succeed; r = pceMalloc(n*sizeof(lg_relation *)); for (i=0; ihead, op=objects; notNil(cell); op++, cell=cell->next) { Graphical gr = cell->value; op->gr = gr; op->area.x = valInt(gr->area->x); op->area.y = valInt(gr->area->y); op->area.w = valInt(gr->area->w); op->area.h = valInt(gr->area->h); op->moved = TRUE; op->update = FALSE; if ( notDefault(move_only) ) { if ( memberChain(move_only, gr) ) op->fixed = FALSE; else op->fixed = TRUE; } else op->fixed = FALSE; } if ( isDefault(work) ) doneObject(network); for (i=0, op=objects; igr; rp->c = qadGetv(op->gr, NAME_connected, 4, av); if ( rp->c ) rp->ideal_len = qadGetv(rp->c, NAME_idealLength, 0, NULL); } r[i][i].fx = r[i][i].fy = 0; /* clean diagonal */ } moved = TRUE; for (l=1; l<=C5 && moved; l++) { int recheck = (l%10 == 0); /* recheck computed length */ for (i=0; ifx = f); r[j][i].fy = -(rp->fy = f); continue; } dx = (cx_object(&objects[j]) - cx_object(&objects[i])) << 10; dy = (cy_object(&objects[j]) - cy_object(&objects[i])) << 10; if ( rp->c ) { float c2; if ( recheck && rp->ideal_len ) { place_object(&objects[i]); place_object(&objects[j]); ComputeGraphical(rp->c); rp->ideal_len = qadGetv(rp->c, NAME_idealLength, 0, NULL); } if ( rp->ideal_len ) c2 = (float)valInt(rp->ideal_len); else c2 = C2; force = forceAttract(d, C1, c2); } else force = forceRepel(d, C3); r[j][i].fx = -(rp->fx = (dx * force) >> 11); r[j][i].fy = -(rp->fy = (dy * force) >> 11); } } moved = FALSE; for (i=0, op=objects; ifixed ) { op->moved = FALSE; continue; } dx = dy = 0; for (j=0; j> 10; dy = (((dy * C4) / n) + 512) >> 10; if (dx == 0 && dy == 0) { op->moved = FALSE; continue; } op->update = op->moved = moved = TRUE; op->area.x += dx; op->area.y += dy; if ( op->area.x+op->area.w > limit.x + limit.w ) /* bounce on Window */ op->area.x = limit.x + limit.w - op->area.w; if ( op->area.y+op->area.h > limit.y + limit.h ) /* bounce on Window */ op->area.y = limit.y + limit.h - op->area.h; if ( op->area.x < limit.x ) /* bounce on Window */ op->area.x = limit.x; if ( op->area.y < limit.y ) op->area.y = limit.y; } } for (i=0, op=objects; iactive != OFF ) { Chain recognisers; Cell cell; TRY( recognisers = getAllRecognisersGraphical(gr, OFF) ); for_cell(cell, recognisers) if ( qadSendv(cell->value, NAME_event, 1, (Any*)&ev) ) succeed; } fail; } static status keyGraphical(Graphical gr, Name key) { fail; } static status keyboardFocusGraphical(Graphical gr, BoolObj val) { PceWindow sw = getWindowGraphical(gr); if ( sw ) { if ( val == OFF ) send(sw, NAME_keyboardFocus, NIL, EAV); else if ( val == ON || send(gr, NAME_WantsKeyboardFocus, EAV) ) send(sw, NAME_keyboardFocus, gr, EAV); } succeed; } BoolObj getKeyboardFocusGraphical(Graphical gr) { PceWindow sw = getWindowGraphical(gr); if ( sw && sw->keyboard_focus == gr ) answer(ON); answer(OFF); } status generateEventGraphical(Graphical gr, Name name) { int rval; EventObj ev = tempObject(ClassEvent, name, getWindowGraphical(gr), EAV); rval = postEvent(ev, gr, DEFAULT); considerPreserveObject(ev); return rval; } status inEventAreaGraphical(Graphical gr, Int xc, Int yc) { Area a = gr->area; int ax = valInt(a->x), ay = valInt(a->y), aw = valInt(a->w), ah = valInt(a->h); int x = valInt(xc), y = valInt(yc); static int evtol = -1; if ( evtol < 0 ) { Int v = getClassVariableValueObject(gr, NAME_eventTolerance); evtol = (v ? valInt(v) : 5); } NormaliseArea(ax, ay, aw, ah); if ( aw < evtol ) ax -= (evtol-aw)/2, aw = evtol; if ( ah < evtol ) ay -= (evtol-ah)/2, ah = evtol; if ( x >= ax && x <= ax + aw && y >= ay && y <= ay + ah ) { Class class = classOfObject(gr); if ( class->in_event_area_function ) { if ( (SendFunc)class->in_event_area_function == INVOKE_FUNC ) { Any av[2]; av[0] = xc; av[1] = yc; return sendv(gr, NAME_inEventArea, 2, av); } else return (*class->in_event_area_function)(gr, xc, yc); } succeed; } fail; } static status recogniserGraphical(Any gr, Any r) { Chain ch = getAllRecognisersGraphical(gr, ON); return appendChain(ch, r); } static status prependRecogniserGraphical(Any gr, Any r) { Chain ch = getAllRecognisersGraphical(gr, ON); return prependChain(ch, r); } static status deleteRecogniserGraphical(Any gr, Any r) { Chain ch; TRY(ch = getAllRecognisersGraphical(gr, OFF)); return deleteChain(ch, r); } Chain getAllRecognisersGraphical(Any obj, BoolObj create) { if ( onFlag(obj, F_RECOGNISER) ) answer(getMemberHashTable(ObjectRecogniserTable, obj)); if ( create == ON ) { Chain ch = newObject(ClassChain, EAV); setFlag(obj, F_RECOGNISER); appendHashTable(ObjectRecogniserTable, obj, ch); answer(ch); } fail; } /******************************** * MISCELLANEOUS * ********************************/ status assignGraphical(Any obj, Name slot, Any value) { Graphical gr = obj; Class class = classOfObject(gr); Variable var; if ( (var = getInstanceVariableClass(class, (Any) slot)) != FAIL ) { if ( getGetVariable(var, gr) != value ) { setSlotInstance(gr, var, value); requestComputeGraphical(gr, DEFAULT); if ( gr->displayed == ON ) { CHANGING_GRAPHICAL(gr, ComputeGraphical(gr); changedEntireImageGraphical(gr)); } } succeed; } fail; } static status bellGraphical(Graphical gr, Int volume) { DisplayObj d; TRY( d = getDisplayGraphical(gr) ); return send(d, NAME_bell, volume, EAV); } status flashGraphical(Graphical gr, Area a, Int time) { PceWindow sw = getWindowGraphical(gr); if ( sw ) { int x, y; Int w, h; Area a2; if ( isDefault(time) ) time = getClassVariableValueObject(gr, NAME_visualBellDuration); if ( !isInteger(time) ) time = toInt(250); offsetDeviceGraphical(gr, &x, &y); x += valInt(gr->area->x); y += valInt(gr->area->y); if ( isDefault(a) ) { w = gr->area->w; h = gr->area->h; } else { x += valInt(a->x); y += valInt(a->y); w = a->w; h = a->h; } a2 = answerObject(ClassArea, toInt(x), toInt(y), w, h, EAV); flashWindow(sw, a2, time); doneObject(a2); } succeed; } status alertGraphical(Graphical gr) { if ( getClassVariableValueObject(gr, NAME_visualBell) == ON ) return send(gr, NAME_flash, EAV); else return send(gr, NAME_bell, EAV); } Node getNodeGraphical(Graphical gr) { Tree t = (Tree) gr->device; if ( instanceOfObject(t, ClassTree) ) answer(getFindNodeNode(t->displayRoot, gr)); fail; } static status popupGraphical(Graphical gr, PopupObj popup) { if ( getInstanceVariableClass(classOfObject(gr), NAME_popup) ) return send(gr, NAME_slot, NAME_popup, popup, EAV); send(gr, NAME_attribute, newObject(ClassAttribute, NAME_popup, popup, EAV), EAV); send(gr, NAME_recogniser, popupGesture(), EAV); succeed; } static PopupObj getPopupGraphical(Graphical gr) { return getAttributeObject(gr, NAME_popup); } status pointerGraphical(Graphical gr, Point pos) { Int x, y; PceWindow sw = DEFAULT; get_absolute_xy_graphical(gr, (Device *)&sw, &x, &y); if ( instanceOfObject(sw, ClassWindow) ) { Point p2; p2 = tempObject(ClassPoint, add(x, pos->x), add(y, pos->y), EAV); pointerWindow(sw, p2); considerPreserveObject(p2); } succeed; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Return the master of a graphical. This is supposed to deal with the case where another object is actually managing me. For the moment this is just a clutch. A more principal solution for this problem is studied. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ Any getMasterGraphical(Graphical gr) { if ( instanceOfObject(gr->device, ClassTree) ) { Tree t = (Tree) gr->device; Any master; if ( (master=getFindNodeNode(t->displayRoot, gr)) ) answer(master); } answer(gr); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Similar problem. See `device->append_dialog_item'. Without this, the methods are done on the window, rather than on its decorator. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static Any getContainerGraphical(Any gr) { if ( instanceOfObject(gr, ClassWindow) ) { PceWindow sw = (PceWindow) gr; if ( notNil(sw->decoration) ) answer(sw->decoration); } answer(gr); } status nameGraphical(Graphical gr, Name name) { assign(gr, name, name); succeed; } static status overlapGraphical(Graphical gr, Any obj) { if ( instanceOfObject(obj, ClassGraphical) ) return overlapArea(getAreaGraphical(gr), getAreaGraphical(obj)); else return overlapArea(getAreaGraphical(gr), obj); } /******************************* * GENERIC LAYOUT MANAGEMENT * *******************************/ static status layoutInterfaceGraphical(Graphical gr, LayoutInterface itf) { if ( notNil(itf) && notNil(gr->layout_interface) ) return errorPce(gr, NAME_noChangeLayoutInterface); assign(gr, layout_interface, itf); succeed; } /******************************** * VISUAL * ********************************/ Any getContainedInGraphical(Graphical gr) { if ( notNil(gr->device) ) { if ( instanceOfObject(gr->device, ClassTree) ) answer(getNodeGraphical(gr)); answer(gr->device); } fail; } status initialiseNewSlotGraphical(Graphical gr, Variable new) { if ( new->name == NAME_shadow ) setSlotInstance(gr, new, ZERO); else if ( new->name == NAME_active ) setSlotInstance(gr, new, ON); succeed; } /******************************* * POSTSCRIPT * *******************************/ static status drawPostScriptGraphical(Graphical gr, Name hb) { if ( gr->area->w != ZERO && gr->area->h != ZERO ) { Image i; if ( (i=checkType(gr, nameToType(NAME_image), gr)) ) { BitmapObj bm = answerObject(ClassBitmap, i, EAV); setGraphical(bm, gr->area->x, gr->area->y, DEFAULT, DEFAULT); send(bm, NAME_DrawPostScript, hb, EAV); doneObject(bm); doneObject(i); succeed; } fail; } succeed; } /******************************* * DRAW * *******************************/ status clipGraphical(Graphical gr, Area a) { if ( isDefault(a) ) a = gr->area; d_clip(valInt(a->x), valInt(a->y), valInt(a->w), valInt(a->h)); succeed; } status unclipGraphical(Graphical gr) { d_clip_done(); succeed; } static status saveGraphicsStateGraphical(Graphical gr) { g_save(); succeed; } static status restoreGraphicsStateGraphical(Graphical gr) { g_restore(); succeed; } static status graphicsStateGraphical(Graphical gr, Int pen, Name texture, Colour colour, Colour background) { if ( notDefault(pen) ) r_thickness(valInt(pen)); if ( notDefault(texture) ) r_dash(texture); if ( notDefault(colour) ) r_colour(colour); if ( notDefault(background) ) r_background(background); succeed; } static status drawLineGraphical(Graphical gr, Int x1, Int y1, Int x2, Int y2) { r_line(valInt(x1), valInt(y1), valInt(x2), valInt(y2)); succeed; } static status drawPolyGraphical(Graphical gr, Any points, BoolObj closed, Any fill) { IPoint pts; int npts = 0; if ( instanceOfObject(points, ClassChain) ) { Chain ch = points; Cell cell; pts = (IPoint)alloca(sizeof(ipoint) * valInt(ch->size)); for_cell(cell, ch) { Point pt = cell->value; if ( instanceOfObject(pt, ClassPoint) ) { pts[npts].x = valInt(pt->x); pts[npts].y = valInt(pt->y); npts++; } else { return errorPce(pt, NAME_unexpectedType, nameToType(NAME_point)); } } } else /* vector */ { Vector vector = points; Point pt; pts = (IPoint) alloca(sizeof(ipoint) * valInt(vector->size)); for_vector(vector, pt, { if ( instanceOfObject(pt, ClassPoint) ) { pts[npts].x = valInt(pt->x); pts[npts].y = valInt(pt->y); npts++; } else { return errorPce(pt, NAME_unexpectedType, nameToType(NAME_point)); } }); } r_polygon(pts, npts, closed == ON); if ( notDefault(fill) && notNil(fill) ) { r_fillpattern(fill, NAME_foreground); r_fill_polygon(pts, npts); } succeed; } static status drawArcGraphical(Graphical gr, /* has to handle mode */ Int x, Int y, Int w, Int h, Real start, Real end, Any fill) { int s = (isDefault(start) ? 0 : rfloat(valReal(start) * 64.0)); int e = (isDefault(end) ? 360*64 : rfloat(valReal(end) * 64.0)); if ( isDefault(fill) ) fill = NIL; r_arc(valInt(x), valInt(y), valInt(w), valInt(h), s, e, fill); succeed; } static status drawBoxGraphical(Graphical gr, Int x, Int y, Int w, Int h, Int r, Any fill, BoolObj up) { int radius = (isDefault(r) ? 0 : valInt(r)); Any fillp; Elevation e; if ( isNil(fill) || isDefault(fill) ) { e = NIL; fillp = NIL; } else if ( instanceOfObject(fill, ClassElevation) ) { e = fill; fillp = NIL; } else { e = NIL; fillp = fill; } if ( isNil(e) ) r_box(valInt(x), valInt(y), valInt(w), valInt(h), radius, fillp); else r_3d_box(valInt(x), valInt(y), valInt(w), valInt(h), radius, e, up != OFF); succeed; } static status drawFillGraphical(Graphical gr, Int x, Int y, Int w, Int h, Any fill) { int ax = valInt(x), ay = valInt(y), aw = valInt(w), ah = valInt(h); if ( isNil(fill) ) r_clear(ax, ay, aw, ah); else if ( isDefault(fill) ) r_fill(ax, ay, aw, ah, fill); succeed; } static status drawImageGraphical(Graphical gr, Image img, Int x, Int y, Int sx, Int sy, Int sw, Int sh, BoolObj transparent) { if ( isDefault(transparent) ) transparent = ON; r_image(img, isDefault(sx) ? 0 : valInt(sx), isDefault(sy) ? 0 : valInt(sy), valInt(x), valInt(y), isDefault(sw) ? valInt(img->size->w) : valInt(sw), isDefault(sh) ? valInt(img->size->h) : valInt(sh), transparent); succeed; } static status drawTextGraphical(Graphical gr, CharArray txt, FontObj font, Int x, Int y, Int w, Int h, Name hadjust, Name vadjust) { if ( isDefault(w) && isDefault(h) ) { s_print(&txt->data, valInt(x), valInt(y), font); } else { if ( isDefault(hadjust) ) hadjust = NAME_left; if ( isDefault(vadjust) ) vadjust = NAME_top; str_string(&txt->data, font, valInt(x), valInt(y), valInt(w), valInt(h), hadjust, vadjust, 0); } succeed; } static status solidGraphical(Graphical gr, BoolObj solid) { if ( solid == ON ) setFlag(gr, F_SOLID); else clearFlag(gr, F_SOLID); succeed; } static BoolObj getSolidGraphical(Graphical gr) { answer(onFlag(gr, F_SOLID) ? ON : OFF); } /* Type declaractions */ static char *T_layout[] = { "attract=[real]", "nominal=[real]", "repel=[real]", "adapt=[int]", "iterations=[int]", "area=[area]", "network=[chain]", "move_only=[chain]" }; static char *T_resize[] = { "factor_x=real", "factor_y=[real]", "origin=[point]" }; static char *T_drawImage[] = { "image", "x=int", "y=int", "sx=[int]", "sy=[int]", "sw=[int]", "sh=[int]", "transparent=[bool]" }; static char *T_postscript[] = { "landscape=[bool]", "maximum_area=[area]" }; static char *T_network[] = { "link=[link]", "from_kind=[name]", "to_kind=[name]" }; static char *T_handlePosition[] = { "name=name", "device=[device]" }; static char *T_handles[] = { "near=[point]", "kind=[name]", "distance=[int]" }; static char *T_draw[] = { "offset=[point]", "area=[area]" }; static char *T_graphicsState[] = { "pen=[0..]", "texture=[texture_name]", "colour=[colour|pixmap]", "background=[colour|pixmap]" }; static char *T_drawPoly[] = { "points=chain|vector", "closed=[bool]", "fill=[colour|image]*" }; static char *T_focus[] = { "recogniser=[recogniser]", "cursor=[cursor]", "button=[name]" }; static char *T_drawText[] = { "string=char_array", "font", "x=int", "y=int", "w=[0..]", "h=[0..]", "hadjust=[{left,center,right}]", "vadjust=[{top,center,bottom}]" }; static char *T_connections[] = { "to=[graphical]", "link=[link]", "from_kind=[name]", "to_kind=[name]" }; static char *T_link[] = { "to=[graphical]", "link=[link]", "to_kind=[name]", "from_kind=[name]" }; static char *T_drawLine[] = { "x1=[int]", "y1=[int]", "x2=[int]", "y2=[int]" }; static char *T_geometry[] = { "x=[int]", "y=[int]", "width=[int]", "height=[int]" }; static char *T_inEventArea[] = { "x=int", "y=int" }; static char *T_drawArc[] = { "x=int", "y=int", "w=int", "h=int", "angle1=[real]", "angle2=[real]", "fill=[colour|image]*" }; static char *T_drawFill[] = { "x=int", "y=int", "w=int", "h=int", "fill=[colour|image]*" }; static char *T_drawBox[] = { "x=int", "y=int", "w=int", "h=int", "radius=[0..]", "fill=[image|colour|elevation]", "up=[bool]" }; static char *T_flash[] = { "area=[area]", "time=[int]" }; static char *T_containerSizeChanged[] = { "width=[int]", "height=[int]" }; /* Instance Variables */ static vardecl var_graphical[] = { SV(NAME_device, "device*", IV_GET|IV_STORE, deviceGraphical, NAME_organisation, "Device I'm displayed on"), SV(NAME_area, "area", IV_NONE|IV_STORE, areaGraphical, NAME_area, "Bounding box of affected pixels"), SV(NAME_displayed, "bool", IV_GET|IV_STORE, displayedGraphical, NAME_visibility, "If @on, graphical is visible"), SV(NAME_pen, "0..", IV_GET|IV_STORE, penGraphical, NAME_appearance, "Thickness of drawing pen"), SV(NAME_texture, "texture_name", IV_GET|IV_STORE, textureGraphical, NAME_appearance, "Stipple pattern of drawing pen"), SV(NAME_colour, "[colour|pixmap]", IV_GET|IV_STORE, colourGraphical, NAME_appearance, "Colour of drawing pen"), IV(NAME_handles, "chain*", IV_NONE, NAME_relation, "Connection points for connections"), IV(NAME_connections, "chain*", IV_NONE, NAME_relation, "Connections (links) to other graphicals"), IV(NAME_name, "name", IV_BOTH, NAME_name, "Name of graphical"), SV(NAME_selected, "bool", IV_GET|IV_STORE, selectedGraphical, NAME_selection, "If @on, I'm selected"), SV(NAME_inverted, "bool", IV_GET|IV_STORE, invertedGraphical, NAME_appearance, "If @on, invert bounding box after painting"), SV(NAME_active, "bool", IV_GET|IV_STORE, activeGraphical, NAME_event, "If @off, greyed out and insensitive"), SV(NAME_cursor, "cursor*", IV_GET|IV_STORE, cursorGraphical, NAME_cursor, "Cursor when in focus of events"), SV(NAME_layoutInterface, "layout_interface*", IV_GET|IV_STORE, layoutInterfaceGraphical, NAME_layout, "Interface to layout-manager"), IV(NAME_requestCompute, "any*", IV_GET, NAME_update, "Graphical requests recomputing") }; /* Send Methods */ static senddecl send_graphical[] = { SM(NAME_initialise, 4, T_geometry, initialiseGraphical, DEFAULT, "Create from XYWH"), SM(NAME_unlink, 0, NULL, unlinkGraphical, DEFAULT, "Erase from device"), SM(NAME_key, 1, "name", keyGraphical, NAME_accelerator, "Accelerator-key pressed (fail)"), SM(NAME_flush, 0, NULL, flushGraphical, NAME_animate, "Flush changes to the display"), SM(NAME_synchronise, 1, "[always=bool]", synchroniseGraphical, NAME_animate, "->flush and process all events"), SM(NAME_apply, 1, "[bool]", virtualObject, NAME_apply, "Virtual method"), SM(NAME_restore, 0, NULL, virtualObject, NAME_apply, "Virtual method"), SM(NAME_bottomSide, 1, "int", bottomSideGraphical, NAME_area, "Resize graphical to set bottom-side"), SM(NAME_center, 1, "point", centerGraphical, NAME_area, "Move to make point the center"), SM(NAME_centerX, 1, "int", centerXGraphical, NAME_area, "Move horizontal to make int the <-x_center"), SM(NAME_centerY, 1, "int", centerYGraphical, NAME_area, "Move vertical to make int the <-y_center"), SM(NAME_corner, 1, "point", cornerGraphical, NAME_area, "Resize to make opposite of origin point"), SM(NAME_cornerX, 1, "int", cornerXGraphical, NAME_area, "Resize to set X of ->corner"), SM(NAME_cornerY, 1, "int", cornerYGraphical, NAME_area, "Resize to set Y of ->corner"), SM(NAME_doSet, 4, T_geometry, doSetGraphical, NAME_area, "Set X, Y, W and H for graphical"), SM(NAME_height, 1, "int", heightGraphical, NAME_area, "Set height"), SM(NAME_leftSide, 1, "int", leftSideGraphical, NAME_area, "Resize graphical to set left-side"), SM(NAME_move, 1, "point", positionGraphical, NAME_area, "Move origin to argument"), SM(NAME_normalise, 0, NULL, normaliseGraphical, NAME_area, "Make top-left corner the origin"), SM(NAME_orientation, 1, "{north_west,south_west,north_east,south_east}", orientationGraphical, NAME_area, "Put origin at {north,south}_{west,east}"), SM(NAME_position, 1, "point", positionGraphical, NAME_area, "Move origin to argument (as ->move)"), SM(NAME_relativeMove, 1, "point", relativeMoveGraphical, NAME_area, "Move origin by argument"), SM(NAME_resize, 3, T_resize, resizeGraphical, NAME_area, "Resize graphical with specified factor"), SM(NAME_rightSide, 1, "int", rightSideGraphical, NAME_area, "Resize graphical to set right-side"), SM(NAME_set, 4, T_geometry, setGraphical, NAME_area, "Request new X, Y, W and H for graphical"), SM(NAME_size, 1, "size", sizeGraphical, NAME_area, "Resize to specified size"), SM(NAME_topSide, 1, "int", topSideGraphical, NAME_area, "Resize graphical to set top-side"), SM(NAME_width, 1, "int", widthGraphical, NAME_area, "Width of graphical"), SM(NAME_x, 1, "int", xGraphical, NAME_area, "Move graphical horizontally"), SM(NAME_y, 1, "int", yGraphical, NAME_area, "Move graphical vertically"), SM(NAME_clip, 1, "[area]", clipGraphical, NAME_draw, "Clip subsequent drawing actions to area"), SM(NAME_drawArc, 7, T_drawArc, drawArcGraphical, NAME_draw, "Draw a ellipse-part"), SM(NAME_drawBox, 7, T_drawBox, drawBoxGraphical, NAME_draw, "Draw rectangular (rounded) box"), SM(NAME_drawFill, 5, T_drawFill, drawFillGraphical, NAME_draw, "Fill rectangle with specified pattern"), SM(NAME_drawImage, 8, T_drawImage, drawImageGraphical, NAME_draw, "Draw a bitmap or pixmap image"), SM(NAME_drawLine, 4, T_drawLine, drawLineGraphical, NAME_draw, "Draw line segment from (X1,Y1) to (X2,Y2)"), SM(NAME_drawPoly, 3, T_drawPoly, drawPolyGraphical, NAME_draw, "Draw/fill a polyfon"), SM(NAME_drawText, 8, T_drawText, drawTextGraphical, NAME_draw, "Draw text-string"), SM(NAME_graphicsState, 4, T_graphicsState, graphicsStateGraphical, NAME_draw, "Modify the graphics state"), SM(NAME_restoreGraphicsState, 0, NULL, restoreGraphicsStateGraphical, NAME_draw, "Restore saved pen, texture, colours and font"), SM(NAME_saveGraphicsState, 0, NULL, saveGraphicsStateGraphical, NAME_draw, "Save current pen, texture, colours and font"), SM(NAME_unclip, 0, NULL, unclipGraphical, NAME_draw, "Undo previous ->clip"), SM(NAME_solid, 1, "solid=bool", solidGraphical, NAME_draw, "a ->_redraw_area touched all pixels"), SM(NAME_deleteRecogniser, 1, "recogniser", deleteRecogniserGraphical, NAME_event, "Delete a recogniser"), SM(NAME_event, 1, "event", eventGraphical, NAME_event, "Handle a user-event"), SM(NAME_generateEvent, 1, "event_id", generateEventGraphical, NAME_event, "Generate named event for graphical"), SM(NAME_inEventArea, 2, T_inEventArea, inEventAreaGraphical, NAME_event, "Test if (X,Y) is in the sensitive area for events"), SM(NAME_keyboardFocus, 1, "[bool]", keyboardFocusGraphical, NAME_event, "Get <-window's keyboard_focus if ->_wants_keyboard_focus"), SM(NAME_prependRecogniser, 1, "recogniser", prependRecogniserGraphical, NAME_event, "Add recogniser for user events (first)"), SM(NAME_recogniser, 1, "recogniser", recogniserGraphical, NAME_event, "Add recogniser for user events (last)"), SM(NAME_initialiseNewSlot, 1, "new=variable", initialiseNewSlotGraphical, NAME_file, "Assigns <-shadow to ZERO, active to @off"), SM(NAME_WantsKeyboardFocus, 0, NULL, WantsKeyboardFocusGraphical, NAME_focus, "Test if graphicals wants keyboard events (fail)"), SM(NAME_focus, 3, T_focus, focusGraphical, NAME_focus, "Set window focus to this graphical"), SM(NAME_focusCursor, 1, "cursor*", focusCursorGraphical, NAME_focus, "Set cursor until focus in released"), SM(NAME_above, 1, "graphical*", aboveGraphical, NAME_layout, "Put me above argument"), SM(NAME_alignment, 1, "{left,center,right,column}", alignmentGraphical, NAME_layout, "Dialog item integration"), SM(NAME_autoAlign, 1, "bool", autoAlignGraphical, NAME_layout, "Dialog_item integration"), SM(NAME_autoLabelAlign, 1, "bool", autoLabelAlignGraphical, NAME_layout, "Dialog item integration"), SM(NAME_autoValueAlign, 1, "bool", autoValueAlignGraphical, NAME_layout, "Dialog item integration"), SM(NAME_below, 1, "graphical*", belowGraphical, NAME_layout, "Put me below argument"), SM(NAME_layout, 8, T_layout, layoutGraphical, NAME_layout, "Make graph-layout for connected graphicals"), SM(NAME_left, 1, "graphical*", leftGraphical, NAME_layout, "Put me left of argument"), SM(NAME_reference, 1, "point", referenceGraphical, NAME_layout, "Dialog item integration"), SM(NAME_right, 1, "graphical*", rightGraphical, NAME_layout, "Put me right of argument"), SM(NAME_layoutDialog, 0, NULL, layoutDialogGraphical, NAME_layout, "Compute layout as a dialog object"), SM(NAME_popup, 1, "popup", popupGraphical, NAME_menu, "Associate a popup menu with the graphical"), SM(NAME_displayOn, 1, "device*", displayOnGraphical, NAME_organisation, "Set device and ensure ->displayed: @on"), SM(NAME_reparent, 0, NULL, reparentGraphical, NAME_organisation, "Graphicals parent-chain has changed"), SM(NAME_pointer, 1, "point", pointerGraphical, NAME_pointer, "Warp pointer relative to graphical"), SM(NAME_DrawPostScript, 1, "{head,body}", drawPostScriptGraphical, NAME_postscript, "Create PostScript using intermediate image object"), SM(NAME_Postscript, 1, "{head,body}", postscriptGraphical, NAME_postscript, "Create PostScript"), SM(NAME_connect, 4, T_link, connectGraphical, NAME_relation, "Create a connection to another graphical"), SM(NAME_connected, 4, T_link, connectedGraphical, NAME_relation, "Test if graphical has specified connection"), SM(NAME_disconnect, 4, T_link, disconnectGraphical, NAME_relation, "Delete matching connections"), SM(NAME_handle, 1, "handle", handleGraphical, NAME_relation, "Add connection point for connection"), SM(NAME_draw, 2, T_draw, drawGraphical, NAME_repaint, "Draw specified area"), SM(NAME_paintSelected, 0, NULL, paintSelectedGraphical, NAME_repaint, "Paint selection feedback"), SM(NAME_redraw, 1, "[area]", redrawGraphical, NAME_repaint, "Request to repaint indicated area"), SM(NAME_alert, 0, NULL, alertGraphical, NAME_report, "Alert visual or using the bell"), SM(NAME_bell, 1, "[int]", bellGraphical, NAME_report, "Ring the bell on associated display"), SM(NAME_flash, 2, T_flash, flashGraphical, NAME_report, "Alert visual by temporary inverting"), SM(NAME_geometry, 4, T_geometry, geometryGraphical, NAME_resize, "Resize graphical"), SM(NAME_requestGeometry, 4, T_geometry, requestGeometryGraphical, NAME_resize, "Request resize for graphical"), SM(NAME_rotate, 1, "int", rotateGraphical, NAME_rotate, "Rotate (multiple of 90) degrees"), SM(NAME_toggleSelected, 0, NULL, toggleSelectedGraphical, NAME_selection, "Change selected status"), SM(NAME_expose, 1, "[graphical]", exposeGraphical, NAME_stacking, "Place graphical on top or above argument"), SM(NAME_hide, 1, "[graphical]", hideGraphical, NAME_stacking, "Place in background or below argument"), SM(NAME_overlap, 1, "graphical|area", overlapGraphical, NAME_stacking, "Succeeds if graphical overlaps with argument"), SM(NAME_swap, 1, "graphical", swapGraphical, NAME_stacking, "Swap stacking order of graphicals"), SM(NAME_compute, 0, NULL, computeGraphical, NAME_update, "Update status of graphical"), SM(NAME_requestCompute, 1, "[any]*", requestComputeGraphical, NAME_update, "Request a ->compute on next repaint"), SM(NAME_containerSizeChanged, 2, T_containerSizeChanged, virtualObject, NAME_area, "<-width or <-height of <-contained_in changed") }; /* Get Methods */ static getdecl get_graphical[] = { GM(NAME_containedIn, 0, "device|node", NULL, getContainedInGraphical, DEFAULT, "Device I'm contained in"), GM(NAME_displayColour, 0, "colour|pixmap", NULL, getDisplayColourGraphical, NAME_appearance, "Colour graphical is displayed in"), GM(NAME_absolutePosition, 1, "point", "[device]", getAbsolutePositionGraphical, NAME_area, "Get position relative to device (or window)"), GM(NAME_absoluteX, 1, "int", "[device]", getAbsoluteXGraphical, NAME_area, "Get X-position relative to device"), GM(NAME_absoluteY, 1, "int", "[device]", getAbsoluteYGraphical, NAME_area, "Get Y-position relative to device"), GM(NAME_area, 0, "area", NULL, getAreaGraphical, NAME_area, "->compute and return area slot"), GM(NAME_bottomSide, 0, "int", NULL, getBottomSideGraphical, NAME_area, "Bottom-side of graphical"), GM(NAME_center, 0, "point", NULL, getCenterGraphical, NAME_area, "New point representing center"), GM(NAME_centerX, 0, "int", NULL, getCenterXGraphical, NAME_area, "X-coordinate of center"), GM(NAME_centerY, 0, "int", NULL, getCenterYGraphical, NAME_area, "Y-coordinate of center"), GM(NAME_corner, 0, "point", NULL, getCornerGraphical, NAME_area, "New point from point opposite origin"), GM(NAME_cornerX, 0, "int", NULL, getCornerXGraphical, NAME_area, "X-coordinate of corner"), GM(NAME_cornerY, 0, "int", NULL, getCornerYGraphical, NAME_area, "Y-coordinate of corner"), GM(NAME_displayedCursor, 0, "cursor*", NULL, getDisplayedCursorGraphical, NAME_cursor, "Currently displayed cursor"), GM(NAME_displayPosition, 0, "point", NULL, getDisplayPositionGraphical, NAME_area, "Position relative to display"), GM(NAME_height, 0, "int", NULL, getHeightGraphical, NAME_area, "Height of graphical"), GM(NAME_leftSide, 0, "int", NULL, getLeftSideGraphical, NAME_area, "Left-side of graphical"), GM(NAME_orientation, 0, "{north_west,south_west,north_east,south_east}", NULL, getOrientationGraphical, NAME_area, "Current orientation"), GM(NAME_position, 0, "point", NULL, getPositionGraphical, NAME_area, "New point representing origin"), GM(NAME_rightSide, 0, "int", NULL, getRightSideGraphical, NAME_area, "Right-side of graphical"), GM(NAME_size, 0, "size", NULL, getSizeGraphical, NAME_area, "New size representing size"), GM(NAME_topSide, 0, "int", NULL, getTopSideGraphical, NAME_area, "Top-side of graphical"), GM(NAME_width, 0, "int", NULL, getWidthGraphical, NAME_area, "Width of graphical"), GM(NAME_x, 0, "int", NULL, getXGraphical, NAME_area, "X or origin"), GM(NAME_y, 0, "int", NULL, getYGraphical, NAME_area, "Y of origin"), GM(NAME_convert, 1, "graphical", "object", getConvertGraphical, NAME_conversion, "Convert using <-image"), GM(NAME_keyboardFocus, 0, "bool", NULL, getKeyboardFocusGraphical, NAME_focus, "@on if graphical is in focus of the keyboard"), GM(NAME_above, 0, "graphical", NULL, getFailObject, NAME_layout, "Dialog_item integration; fails"), GM(NAME_alignment, 0, "name", NULL, getAlignmentGraphical, NAME_layout, "Dialog_item integration"), GM(NAME_autoAlign, 0, "bool", NULL, getAutoAlignGraphical, NAME_layout, "Dialog_item integration"), GM(NAME_autoLabelAlign, 0, "bool", NULL, getAutoLabelAlignGraphical, NAME_layout, "Dialog_item integration"), GM(NAME_autoValueAlign, 0, "bool", NULL, getAutoValueAlignGraphical, NAME_layout, "Dialog_item integration"), GM(NAME_below, 0, "graphical", NULL, getFailObject, NAME_layout, "Dialog_item integration; fails"), GM(NAME_left, 0, "graphical", NULL, getFailObject, NAME_layout, "Dialog_item integration; fails"), GM(NAME_reference, 0, "point", NULL, getFailObject, NAME_layout, "Dialog_item integration; fails"), GM(NAME_horStretch, 0, "0..100", NULL, getFailObject, NAME_layout, "Horizontal stretchability of dialog item (fail)"), GM(NAME_verStretch, 0, "0..100", NULL, getFailObject, NAME_layout, "Vertical stretchability of dialog item (fail)"), GM(NAME_right, 0, "graphical", NULL, getFailObject, NAME_layout, "Dialog_item integration; fails"), GM(NAME_popup, 0, "popup", NULL, getPopupGraphical, NAME_menu, "Associated ->popup"), GM(NAME_allRecognisers, 1, "chain", "create=[bool]", getAllRecognisersGraphical, NAME_meta, "Chain with all recognisers"), GM(NAME_node, 0, "node", NULL, getNodeGraphical, NAME_nodes, "When image of node in tree, find the node"), GM(NAME_commonDevice, 1, "device", "with=graphical", getCommonDeviceGraphical, NAME_organisation, "Deepest device both are displayed on"), GM(NAME_display, 0, "display", NULL, getDisplayGraphical, NAME_organisation, "Display graphical is displayed on"), GM(NAME_monitor, 0, "monitor", NULL, getMonitorGraphical, NAME_organisation, "Monitor graphical is displayed on"), GM(NAME_application, 0, "application", NULL, getApplicationGraphical, NAME_organisation, "Application my frame belongs too"), GM(NAME_distance, 1, "int", "graphical", getDistanceGraphical, NAME_compute, "Closest distance between areas"), GM(NAME_distanceX, 1, "int", "graphical", getDistanceXGraphical, NAME_compute, "Distance between graphicals's in X-direction"), GM(NAME_distanceY, 1, "int", "graphical", getDistanceYGraphical, NAME_compute, "Distance between graphicals's in Y-direction"), GM(NAME_frame, 0, "frame", NULL, getFrameGraphical, NAME_organisation, "Frame graphical is displayed on"), GM(NAME_window, 0, "window", NULL, getWindowGraphical, NAME_organisation, "Window graphical is displayed on"), GM(NAME_boundingBox, 0, "area", NULL, getBoundingBoxGraphical, NAME_postscript, "Same as <-area; used for PostScript"), GM(NAME_postscript, 2, "string", T_postscript, getPostscriptObject, NAME_postscript, "New string holding PostScript description"), GM(NAME_connections, 4, "chain", T_connections, getConnectionsGraphical, NAME_relation, "New chain with matching connections"), GM(NAME_connected, 4, "connection", T_link, getConnectedGraphical, NAME_relation, "Find specified connection"), GM(NAME_handle, 1, "handle", "name", getHandleGraphical, NAME_relation, "Find handle with given name"), GM(NAME_handlePosition, 2, "point", T_handlePosition, getHandlePositionGraphical, NAME_relation, "New point with position of handle"), GM(NAME_handles, 3, "chain", T_handles, getHandlesGraphical, NAME_relation, "New chain with matching handles"), GM(NAME_network, 3, "chain", T_network, getNetworkGraphical, NAME_relation, "New chain with connected graphicals"), GM(NAME_isDisplayed, 1, "bool", "[device]", getIsDisplayedGraphical, NAME_visibility, "@on if graphical is visible on device"), GM(NAME_solid, 0, "bool", NULL, getSolidGraphical, NAME_draw, "a ->_redraw_area touched all pixels") }; /* Resources */ static classvardecl rc_graphical[] = { RC(NAME_colour, "[colour|pixmap]", "@default", "Default colour for this object"), RC(NAME_pen, "0..", "1", NULL), RC(NAME_texture, NULL, "none", NULL), RC(NAME_inactiveColour, "colour|pixmap*", "when(@colour_display, colour(grey60), @grey50_image)", "Colour when <-active == @off"), RC(NAME_selectedForeground, "colour*", UXWIN("white", "win_highlighttext"), "Colour when <-selected == @on"), RC(NAME_selectedBackground, "colour*", UXWIN("black", "win_highlight"), "Background when <-selected == @on"), RC(NAME_selectionHandles, "{corners,sides,corners_and_sides,line}*", "corners_and_sides", "Visual feedback of <->selected"), RC(NAME_visualBell, "bool", "@on", "@on: flash; @off: ring bell on ->alert"), RC(NAME_visualBellDuration, "int", "100", "Length of flash in milliseconds"), RC(NAME_eventTolerance, "0..", "5", "Minimum size of event-area") }; /* Class Declaration */ static Name graphical_termnames[] = { NAME_x, NAME_y, NAME_width, NAME_height }; ClassDecl(graphical_decls, var_graphical, send_graphical, get_graphical, rc_graphical, 4, graphical_termnames, "$Rev$"); status makeClassGraphical(Class class) { declareClass(class, &graphical_decls); saveStyleVariableClass(class, NAME_device, NAME_nil); cloneStyleVariableClass(class, NAME_device, NAME_nil); setRedrawFunctionClass(class, RedrawAreaGraphical); delegateClass(class, NAME_layoutInterface); ChangedWindows = globalObject(NAME_changedWindows, ClassChain, EAV); succeed; }