1 /* Part of XPCE --- The SWI-Prolog GUI toolkit
2
3 Author: Jan Wielemaker and Anjo Anjewierden
4 E-mail: jan@swi.psy.uva.nl
5 WWW: http://www.swi.psy.uva.nl/projects/xpce/
6 Copyright (c) 1985-2002, University of Amsterdam
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
12
13 1. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in
18 the documentation and/or other materials provided with the
19 distribution.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <h/kernel.h>
36 #include <h/graphics.h>
37
38 static status computeFormatDevice(Device dev);
39
40 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
41 Class device is an abstract superclass used to define method common
42 to Pictures and Figures: manipulating a chain of graphicals and
43 dispatching events.
44
45 A device is a subclass of graphical and thus can be displayed on other
46 devices.
47
48 Devices maintain the graphical's attribute <->area to reflect the
49 bounding box of all displayed graphicals (e.g. graphicals with
50 <->displayed equals @on). To the X-Y coordinate of this bounding box
51 the <->offset is added to ensure smooth intergration with class
52 figure.
53 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
54
55 /********************************
56 * CREATE/DESTROY *
57 ********************************/
58
59 status
initialiseDevice(Device dev)60 initialiseDevice(Device dev)
61 { initialiseGraphical(dev, ZERO, ZERO, ZERO, ZERO);
62
63 assign(dev, level, ZERO);
64 assign(dev, offset, newObject(ClassPoint, EAV));
65 assign(dev, graphicals, newObject(ClassChain, EAV));
66 assign(dev, badBoundingBox, OFF);
67 assign(dev, badFormat, OFF);
68 assign(dev, format, NIL);
69 assign(dev, pointed, newObject(ClassChain, EAV));
70 assign(dev, clip_area, NIL);
71 assign(dev, recompute, newObject(ClassChain, EAV));
72
73 succeed;
74 }
75
76
77 status
unlinkDevice(Device dev)78 unlinkDevice(Device dev)
79 { if ( notNil(dev->graphicals) )
80 { Graphical gr;
81
82 for_chain(dev->graphicals, gr, DeviceGraphical(gr, NIL));
83 }
84
85 return unlinkGraphical((Graphical) dev);
86 }
87
88 /********************************
89 * CURSOR *
90 ********************************/
91
92 CursorObj
getDisplayedCursorDevice(Device dev)93 getDisplayedCursorDevice(Device dev)
94 { CursorObj c2;
95 Cell cell;
96
97 for_cell(cell, dev->pointed)
98 { if ( (c2=qadGetv(cell->value, NAME_displayedCursor, 0, NULL)) &&
99 notNil(c2) )
100 answer(c2);
101 }
102
103 answer(dev->cursor); /* = getDisplayedCursorGraphical()! */
104 }
105
106
107 /********************************
108 * EVENT HANDLING *
109 ********************************/
110
111 static Chain
get_pointed_objects_device(Device dev,Int x,Int y,Chain ch)112 get_pointed_objects_device(Device dev, Int x, Int y, Chain ch)
113 { Cell cell;
114
115 if ( isDefault(ch) )
116 ch = answerObject(ClassChain, EAV);
117 else
118 clearChain(ch);
119
120 for_cell(cell, dev->graphicals)
121 { register Graphical gr = cell->value;
122
123 if ( gr->displayed == ON &&
124 inEventAreaGraphical(gr, x, y) )
125 prependChain(ch, gr);
126 }
127
128 if ( notDefault(ch) )
129 answer(ch);
130
131 fail;
132 }
133
134
135 Chain
getPointedObjectsDevice(Device dev,Any pos,Chain ch)136 getPointedObjectsDevice(Device dev, Any pos, Chain ch)
137 { Int x, y;
138
139 if ( instanceOfObject(pos, ClassPoint) )
140 { Point pt = pos;
141
142 x = pt->x;
143 y = pt->y;
144 } else /*if ( instanceOfObject(pos, ClassEvent) )*/
145 get_xy_event(pos, dev, OFF, &x, &y);
146
147 return get_pointed_objects_device(dev, x, y, ch);
148 }
149
150 #define MAX_ACTIVE 250 /* Objects under the mouse */
151
152 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
153 updatePointedDevice() updates the <->pointed chain of the device.
154 The <->pointed chain is a chain holding all events that overlap with
155 the mouse position and are editable and displayed. It sends an
156 area_enter event to all graphicals that have been added to the chain
157 and an area_exit event to all graphicals that have been deleted to the
158 chain. Care is taken to prevent this function from creating too many
159 intermediate objects as is it called very frequently.
160
161 The event area_cancel is ok, but area_resume should verify the buttons
162 are in the same state as when the area is left and at least one is
163 down. This requires us to store the status of the button with the
164 graphical object, costing us an additional 4 bytes on each graphical.
165 To do or not to do?
166 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
167
168 static status
updatePointedDevice(Device dev,EventObj ev)169 updatePointedDevice(Device dev, EventObj ev)
170 { Cell cell, c2;
171 Graphical active[MAX_ACTIVE];
172 int n, an = 0;
173 Int x, y;
174 Name enter, exit;
175
176 if ( allButtonsUpEvent(ev) )
177 { enter = NAME_areaEnter;
178 exit = NAME_areaExit;
179 } else
180 { enter = NAME_areaResume;
181 exit = NAME_areaCancel;
182 }
183
184 /* Exit event: leave all children */
185 if ( isAEvent(ev, NAME_areaExit) )
186 { for_cell_save(cell, c2, dev->pointed)
187 generateEventGraphical(cell->value, exit);
188
189 clearChain(dev->pointed);
190 succeed;
191 }
192
193 get_xy_event(ev, dev, OFF, &x, &y);
194
195 /* See which graphicals are left */
196 for_cell_save(cell, c2, dev->pointed)
197 { register Graphical gr = cell->value;
198
199 if ( gr->displayed == OFF || !inEventAreaGraphical(gr, x, y) )
200 { DEBUG(NAME_event, Cprintf("Leaving %s\n", pp(gr)));
201 deleteChain(dev->pointed, gr);
202 generateEventGraphical(gr, exit);
203 }
204 }
205
206 /* See which graphicals are entered */
207 for_cell(cell, dev->graphicals)
208 { register Graphical gr = cell->value;
209
210 if ( gr->displayed == ON && inEventAreaGraphical(gr, x, y) )
211 { active[an++] = gr;
212
213 if ( memberChain(dev->pointed, gr) != SUCCEED )
214 { DEBUG(NAME_event, Cprintf("Entering %s\n", pp(gr)));
215 generateEventGraphical(gr, enter);
216 }
217
218 if ( an == MAX_ACTIVE ) /* Shift to keep top ones */
219 { int n;
220 for( n = 0; n < MAX_ACTIVE-1; n++ )
221 active[n] = active[n+1];
222 an--;
223 }
224 }
225 }
226
227 /* Update the ->pointed chain */
228 for( cell = dev->pointed->head, n = an-1; n >= 0; n--, cell = cell->next )
229 { if ( isNil(cell) ) /* Chain is out; extend it */
230 { for( ; n >=0; n-- )
231 appendChain(dev->pointed, active[n]);
232 break;
233 }
234
235 cellValueChain(dev->pointed, PointerToInt(cell), active[n]);
236 }
237
238 while( notNil(cell) ) /* Remove the tail of the chain */
239 { c2 = cell->next;
240 deleteChain(dev->pointed, cell->value);
241 cell = c2;
242 }
243
244 succeed;
245 }
246
247
248 status
inspectDevice(Device dev,EventObj ev)249 inspectDevice(Device dev, EventObj ev)
250 { Cell cell;
251 DisplayObj d = CurrentDisplay(dev);
252
253 updatePointedDevice(dev, ev);
254
255 for_cell(cell, dev->pointed)
256 { if ( instanceOfObject(cell->value, ClassDevice) )
257 { if ( inspectDevice(cell->value, ev) )
258 succeed;
259 } else
260 { if ( inspectDisplay(d, cell->value, ev) )
261 succeed;
262 }
263 }
264
265 return inspectDisplay(d, (Graphical) dev, ev);
266 }
267
268
269 static Graphical
get_find_device(Device dev,Int x,Int y,Code cond)270 get_find_device(Device dev, Int x, Int y, Code cond)
271 { LocalArray(Graphical, grv, valInt(dev->graphicals->size));
272 int i, grn;
273 Cell cell;
274
275 grn=0;
276 for_cell(cell, dev->graphicals)
277 grv[grn++] = cell->value;
278
279 for(i=grn-1; i >= 0; i--)
280 { Graphical gr = grv[i];
281
282 if ( notDefault(x) && !inEventAreaGraphical(gr, x, y) )
283 continue;
284
285 if ( instanceOfObject(gr, ClassDevice) )
286 { Device dev2 = (Device) gr;
287 Any rval;
288
289 if ( (rval=get_find_device(dev2,
290 isDefault(x) ? x : sub(x, dev2->offset->x),
291 isDefault(y) ? y : sub(y, dev2->offset->y),
292 cond)) )
293 answer(rval);
294 } else
295 { if ( isDefault(cond) ||
296 forwardCodev(cond, 1, (Any *)&gr) )
297 answer(gr);
298 }
299 }
300
301 if ( isDefault(cond) ||
302 forwardCodev(cond, 1, (Any *)&dev) )
303 answer((Graphical) dev);
304
305 fail;
306 }
307
308
309 static Graphical
getFindDevice(Device dev,Any location,Code cond)310 getFindDevice(Device dev, Any location, Code cond)
311 { Int x, y;
312
313 if ( instanceOfObject(location, ClassEvent) )
314 get_xy_event(location, dev, OFF, &x, &y);
315 else if ( isDefault(location) )
316 { x = y = (Int) DEFAULT;
317 } else
318 { Point p = location;
319
320 x = p->x;
321 y = p->y;
322 }
323
324 return get_find_device(dev, x, y, cond);
325 }
326
327
328 status
eventDevice(Any obj,EventObj ev)329 eventDevice(Any obj, EventObj ev)
330 { Device dev = obj;
331
332 if ( dev->active != OFF )
333 { Graphical gr;
334 int done = FALSE;
335
336 updatePointedDevice(dev, ev);
337
338 for_chain(dev->pointed, gr,
339 if ( !done && postEvent(ev, gr, DEFAULT) )
340 done = TRUE);
341 if ( done )
342 succeed;
343
344 return eventGraphical(dev, ev);
345 }
346
347 fail;
348 }
349
350
351 static status
typedDevice(Device dev,EventId id,BoolObj delegate)352 typedDevice(Device dev, EventId id, BoolObj delegate)
353 { Any key = characterName(id);
354 Graphical gr;
355
356 for_chain(dev->graphicals, gr,
357 if ( sendv(gr, NAME_key, 1, &key) )
358 succeed);
359
360 if ( delegate == ON && notNil(dev->device) )
361 return send(dev->device, NAME_typed, id, delegate, EAV);
362
363 fail;
364 }
365
366
367 Button
getDefaultButtonDevice(Device d)368 getDefaultButtonDevice(Device d)
369 { Cell cell;
370
371 for_cell(cell, d->graphicals)
372 { Button b = cell->value;
373
374 if ( instanceOfObject(b, ClassButton) &&
375 b->default_button == ON )
376 answer(b);
377 }
378
379 fail;
380 }
381
382
383 static Int
getWantsKeyboardFocusGraphical(Graphical gr)384 getWantsKeyboardFocusGraphical(Graphical gr)
385 { if ( qadSendv(gr, NAME_WantsKeyboardFocus, 0, NULL) )
386 { if ( instanceOfObject(gr, ClassTextItem) )
387 return toInt(10);
388 if ( instanceOfObject(gr, ClassButton) &&
389 ((Button)gr)->default_button == ON )
390 return toInt(5);
391
392 return toInt(1);
393 }
394
395 fail;
396 }
397
398
399 status
advanceDevice(Device dev,Graphical gr,BoolObj propagate,Name direction)400 advanceDevice(Device dev, Graphical gr, BoolObj propagate, Name direction)
401 { Cell cell;
402 int skip = TRUE; /* before gr */
403 Graphical first = NIL;
404 Graphical last = NIL;
405 PceWindow sw;
406
407 TRY( sw = getWindowGraphical((Graphical) dev) );
408
409 if ( isDefault(gr) )
410 gr = NIL;
411
412 /* Find initial focus */
413 if ( isNil(gr) )
414 { Graphical focus = NIL;
415 int best = -1;
416
417 for_cell(cell, dev->graphicals)
418 { Int v;
419
420 if ( (v = getWantsKeyboardFocusGraphical(cell->value)) &&
421 valInt(v) > best )
422 { best = valInt(v);
423 focus = cell->value;
424 }
425 }
426
427 if ( best != -1 )
428 return keyboardFocusWindow(sw, focus);
429 } else
430 { if ( isDefault(direction) )
431 direction = NAME_forwards;
432
433 for_cell(cell, dev->graphicals)
434 { if ( skip )
435 { if ( isNil(first) &&
436 qadSendv(cell->value, NAME_WantsKeyboardFocus, 0, NULL) )
437 first = cell->value;
438
439 if ( direction == NAME_backwards )
440 { if ( cell->value == gr )
441 { if ( notNil(last) )
442 return keyboardFocusWindow(sw, last);
443 } else
444 { if ( qadSendv(cell->value, NAME_WantsKeyboardFocus, 0, NULL) )
445 last = cell->value;
446 }
447 }
448
449 if ( cell->value == gr )
450 skip = FALSE;
451
452 continue;
453 }
454
455 if ( send(cell->value, NAME_WantsKeyboardFocus, EAV) )
456 { if ( direction == NAME_forwards )
457 return keyboardFocusWindow(sw, cell->value);
458 else
459 last = cell->value;
460 }
461 }
462
463 if ( last && direction == NAME_backwards )
464 return keyboardFocusWindow(sw, last);
465 }
466
467 if ( isDefault(propagate) )
468 propagate = ((Device) sw != dev ? ON : OFF);
469
470 if ( propagate == ON && notNil(dev->device) )
471 send(dev->device, NAME_advance, dev, EAV);
472 else
473 keyboardFocusWindow(sw, first); /* may be NIL */
474
475 succeed;
476 }
477
478
479 /********************************
480 * REPAINT MANAGEMENT *
481 ********************************/
482
483 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
484 The device's repaint manager is responsible for keeping track of the
485 devices area (the bounding box of its displayed graphicals) and of the
486 area that needs repainting if a ->RedrawArea is received. It should
487 issue to appropriate ->RedrawArea request on its associated graphicals
488 if it receives an ->RedrawArea from its parent.
489
490 A number of changes are recognised:
491
492 *) A graphical is added to the device or its displayed attribute has
493 changed to @on.
494 *) A graphical is erased from the device or its displayed attribute
495 has changed to @off.
496 *) The image of a graphical has changed.
497 *) The area of a graphical has changed.
498
499 Graphicals indicate changes through the following call:
500
501 CHANGING_GRAPHICAL(gr,
502 <code>
503 [changedImageGraphical(gr, x, y, w, h)]
504 [changedEntireImageGraphical(gr)]
505 )
506 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
507
508 status
requestComputeDevice(Device dev,Any val)509 requestComputeDevice(Device dev, Any val)
510 { DEBUG(NAME_compute, Cprintf("requestComputeDevice(%s)\n", pp(dev)));
511 assign(dev, badBoundingBox, ON);
512 assign(dev, badFormat, ON);
513
514 return requestComputeGraphical(dev, val);
515 }
516
517
518 status
computeGraphicalsDevice(Device dev)519 computeGraphicalsDevice(Device dev)
520 { Chain ch = dev->recompute;
521
522 while( !emptyChain(ch) ) /* tricky! */
523 { Cell cell;
524 int i, size = valInt(ch->size);
525 ArgVector(array, size);
526
527 for(i=0, cell = ch->head; notNil(cell); cell = cell->next)
528 array[i++] = cell->value;
529
530 clearChain(ch);
531 for(i=0; i<size; i++)
532 { Graphical gr = array[i];
533
534 if ( !isFreedObj(gr) && notNil(gr->request_compute) )
535 { qadSendv(gr, NAME_compute, 0, NULL);
536 assign(gr, request_compute, NIL);
537 }
538 }
539 }
540
541 succeed;
542 }
543
544
545 status
computeLayoutDevice(Device dev)546 computeLayoutDevice(Device dev)
547 { if ( notNil(dev->format) )
548 computeFormatDevice(dev);
549 else if ( notNil(dev->layout_manager) &&
550 notNil(dev->layout_manager->request_compute) )
551 qadSendv(dev->layout_manager, NAME_compute, 0, NULL);
552
553 succeed;
554 }
555
556
557 status
computeDevice(Any obj)558 computeDevice(Any obj)
559 { Device dev = obj;
560
561 if ( notNil(dev->request_compute) )
562 { computeGraphicalsDevice(dev);
563 computeLayoutDevice(dev);
564 computeBoundingBoxDevice(dev);
565
566 assign(dev, request_compute, NIL);
567 }
568
569 succeed;
570 }
571
572
573 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
574 Updates the bounding box and succeeds if if changed. Fails if there are
575 no modifications. The old bounding box is returned in `od', a vector of
576 4 integers.
577 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
578
579 status
updateBoundingBoxDevice(Device dev,Int * od)580 updateBoundingBoxDevice(Device dev, Int *od)
581 { Cell cell;
582 Area a = dev->area;
583
584 od[0] = a->x; od[1] = a->y; od[2] = a->w; od[3] = a->h;
585
586 if ( isNil(dev->layout_manager) ||
587 !qadSendv(dev->layout_manager, NAME_computeBoundingBox, 0, NULL) )
588 { clearArea(a);
589
590 for_cell(cell, dev->graphicals)
591 { Graphical gr = cell->value;
592
593 if ( gr->displayed == ON )
594 unionNormalisedArea(a, gr->area);
595 }
596 }
597 relativeMoveArea(a, dev->offset);
598
599 if ( od[0] != a->x || od[1] != a->y || od[2] != a->w || od[3] != a->h )
600 succeed;
601
602 fail;
603 }
604
605
606 status
computeBoundingBoxDevice(Device dev)607 computeBoundingBoxDevice(Device dev)
608 { if ( dev->badBoundingBox == ON )
609 { Int od[4]; /* ax, ay, aw, ah */
610
611 if ( updateBoundingBoxDevice(dev, od) )
612 { if ( notNil(dev->device) )
613 { requestComputeDevice(dev->device, DEFAULT);
614 updateConnectionsGraphical((Graphical) dev, sub(dev->level, ONE));
615 }
616
617 qadSendv(dev, NAME_changedUnion, 4, od);
618 }
619
620 if ( notNil(dev->clip_area) )
621 { Area a = dev->area;
622
623 relativeMoveBackArea(a, dev->offset);
624 if ( intersectionArea(dev->area, dev->clip_area) == FAIL )
625 { assign(dev->area, w, ZERO);
626 assign(dev->area, h, ZERO);
627 }
628 relativeMoveArea(a, dev->offset);
629 }
630
631 assign(dev, badBoundingBox, OFF);
632 }
633
634 succeed;
635 }
636
637
638 static status
changedUnionDevice(Device dev,Int ox,Int oy,Int ow,Int oh)639 changedUnionDevice(Device dev, Int ox, Int oy, Int ow, Int oh)
640 { succeed;
641 }
642
643
644 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
645 Our parent requests us to repaint an area. This area is in the
646 coordinate system of the device we are displayed on. The requested
647 repaint area may be larger than the area of myself.
648
649 This algorithm can be made more clever on a number of points. First
650 of all we could be more clever with none-square graphicals, notably
651 lines. Next, we could determine that objects are obscured by other
652 objects and thus do not need to be repainted. We will leave these
653 optimisations for later.
654 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
655
656 status
EnterRedrawAreaDevice(Device dev,Area a,DeviceDrawContext ctx)657 EnterRedrawAreaDevice(Device dev, Area a, DeviceDrawContext ctx)
658 { if ( a->w != ZERO && a->h != ZERO )
659 { int ox = valInt(dev->offset->x);
660 int oy = valInt(dev->offset->y);
661
662 ctx->x = a->x;
663 ctx->y = a->y;
664 ctx->w = a->w;
665 ctx->h = a->h;
666
667 qassign(a, x, toInt(valInt(a->x) - ox));
668 qassign(a, y, toInt(valInt(a->y) - oy));
669 r_offset(ox, oy);
670
671 if ( notNil(dev->clip_area) )
672 { if ( !intersectionArea(a, dev->clip_area) )
673 { qassign(a, x, ctx->x);
674 qassign(a, y, ctx->y);
675 qassign(a, w, ctx->w);
676 qassign(a, h, ctx->h);
677
678 fail;
679 }
680
681 clipGraphical((Graphical)dev, a);
682 }
683
684 succeed;
685 }
686
687 fail;
688 }
689
690
691 void
ExitRedrawAreaDevice(Device dev,Area a,DeviceDrawContext ctx)692 ExitRedrawAreaDevice(Device dev, Area a, DeviceDrawContext ctx)
693 { int ox = valInt(dev->offset->x);
694 int oy = valInt(dev->offset->y);
695
696 if ( notNil(dev->clip_area) )
697 unclipGraphical((Graphical)dev);
698
699 r_offset(-ox, -oy);
700
701 qassign(a, x, ctx->x);
702 qassign(a, y, ctx->y);
703 qassign(a, w, ctx->w);
704 qassign(a, h, ctx->h);
705 }
706
707
708
709 status
RedrawAreaDevice(Device dev,Area a)710 RedrawAreaDevice(Device dev, Area a)
711 { device_draw_context ctx;
712
713 if ( EnterRedrawAreaDevice(dev, a, &ctx) )
714 { Cell cell;
715
716 if ( notNil(dev->layout_manager) )
717 qadSendv(dev->layout_manager, NAME_redrawBackground, 1, (Any*)&a);
718
719 for_cell(cell, dev->graphicals)
720 RedrawArea(cell->value, a);
721
722 if ( notNil(dev->layout_manager) )
723 qadSendv(dev->layout_manager, NAME_redrawForeground, 1, (Any*)&a);
724
725 ExitRedrawAreaDevice(dev, a, &ctx);
726 }
727
728 return RedrawAreaGraphical(dev, a);
729 }
730
731
732 status
flashDevice(Device dev,Area a,Int time)733 flashDevice(Device dev, Area a, Int time)
734 { if ( isDefault(a) ||
735 (dev->offset->x == dev->area->x &&
736 dev->offset->y == dev->area->y) )
737 return flashGraphical((Graphical)dev, a, time);
738 else
739 { Area a2;
740 int nx = valInt(a->x) + valInt(dev->offset->x) - valInt(dev->area->x);
741 int ny = valInt(a->y) + valInt(dev->offset->y) - valInt(dev->area->y);
742
743 a2 = answerObject(ClassArea, toInt(nx), toInt(ny), a->w, a->h, EAV);
744 flashGraphical((Graphical)dev, a2, time);
745 doneObject(a2);
746 }
747
748 succeed;
749 }
750
751
752 /********************************
753 * DISPLAY/ERASE *
754 ********************************/
755
756
757 status
clearDevice(Device dev,Name how)758 clearDevice(Device dev, Name how)
759 { Chain ch = dev->graphicals;
760
761 if ( how == NAME_destroy )
762 { while( !emptyChain(ch) )
763 qadSendv(getHeadChain(ch), NAME_destroy, 0, NULL);
764 } else if ( how == NAME_free )
765 { while( !emptyChain(ch) )
766 qadSendv(getHeadChain(ch), NAME_free, 0, NULL);
767 } else /* if ( how == NAME_erase ) */
768 { while( !emptyChain(ch) )
769 eraseDevice(dev, getHeadChain(ch));
770 }
771
772 succeed;
773 }
774
775
776 status
displayDevice(Any Dev,Any Gr,Point pos)777 displayDevice(Any Dev, Any Gr, Point pos)
778 { Device dev = Dev;
779 Graphical gr = Gr;
780
781 if ( notDefault(pos) )
782 { Variable var;
783
784 if ( (var = getInstanceVariableClass(classOfObject(gr), NAME_autoAlign)) )
785 sendVariable(var, gr, OFF);
786
787 setGraphical(gr, pos->x, pos->y, DEFAULT, DEFAULT);
788 }
789
790 DeviceGraphical(gr, dev);
791 DisplayedGraphical(gr, ON);
792
793 succeed;
794 }
795
796
797 status
appendDevice(Device dev,Graphical gr)798 appendDevice(Device dev, Graphical gr)
799 { appendChain(dev->graphicals, gr);
800 assign(gr, device, dev);
801 if ( notNil(gr->request_compute) )
802 { appendChain(dev->recompute, gr);
803 if ( isNil(dev->request_compute) )
804 requestComputeDevice(dev, DEFAULT);
805 }
806
807 if ( gr->displayed == ON )
808 displayedGraphicalDevice(dev, gr, ON);
809
810 qadSendv(gr, NAME_reparent, 0, NULL);
811
812 succeed;
813 }
814
815 /* True if sub is gr or a graphical inside the (device) gr
816 */
817
818 status
subGraphical(Graphical gr,Graphical sub)819 subGraphical(Graphical gr, Graphical sub)
820 { while( notNil(sub) )
821 { if ( sub == gr )
822 succeed;
823 sub = (Graphical)sub->device;
824 }
825
826 fail;
827 }
828
829
830 status
eraseDevice(Device dev,Graphical gr)831 eraseDevice(Device dev, Graphical gr)
832 { if ( gr->device == dev )
833 { PceWindow sw = getWindowGraphical((Graphical) dev);
834
835 if ( sw )
836 { if ( subGraphical(gr, sw->keyboard_focus) )
837 keyboardFocusWindow(sw, NIL);
838 if ( subGraphical(gr, sw->focus) )
839 focusWindow(sw, NIL, NIL, NIL, NIL);
840 }
841
842 if ( gr->displayed == ON )
843 displayedGraphicalDevice(dev, gr, OFF);
844
845 deleteChain(dev->recompute, gr);
846 deleteChain(dev->pointed, gr);
847 assign(gr, device, NIL);
848 GcProtect(dev, deleteChain(dev->graphicals, gr));
849 if ( !isFreedObj(gr) )
850 qadSendv(gr, NAME_reparent, 0, NULL);
851 }
852
853 succeed;
854 }
855
856
857 status
displayedGraphicalDevice(Device dev,Graphical gr,BoolObj val)858 displayedGraphicalDevice(Device dev, Graphical gr, BoolObj val)
859 { BoolObj old = gr->displayed;
860
861 if ( onFlag(gr, F_SOLID) )
862 { clearFlag(gr, F_SOLID);
863 changedEntireImageGraphical(gr);
864 setFlag(gr, F_SOLID);
865 } else
866 changedEntireImageGraphical(gr);
867
868 gr->displayed = val;
869 if ( instanceOfObject(gr, ClassDevice) )
870 updateConnectionsDevice((Device) gr, dev->level);
871 else
872 updateConnectionsGraphical(gr, dev->level);
873
874 requestComputeDevice(dev, DEFAULT); /* TBD: ON: just union */
875 gr->displayed = old;
876
877 succeed;
878 }
879
880 /********************************
881 * EXPOSURE *
882 ********************************/
883
884 status
exposeDevice(Device dev,Graphical gr,Graphical gr2)885 exposeDevice(Device dev, Graphical gr, Graphical gr2)
886 { if ( gr->device != dev || (notDefault(gr2) && gr2->device != dev) )
887 fail;
888
889 if ( isDefault(gr2) )
890 { addCodeReference(gr);
891 deleteChain(dev->graphicals, gr);
892 appendChain(dev->graphicals, gr);
893 delCodeReference(gr);
894 } else
895 { moveAfterChain(dev->graphicals, gr, gr2);
896 changedEntireImageGraphical(gr2);
897 }
898 requestComputeDevice(dev, DEFAULT); /* Actually only needs format */
899
900 changedEntireImageGraphical(gr);
901
902 succeed;
903 }
904
905
906 status
hideDevice(Device dev,Graphical gr,Graphical gr2)907 hideDevice(Device dev, Graphical gr, Graphical gr2)
908 { if ( gr->device != dev || (notDefault(gr2) && gr2->device != dev) )
909 fail;
910
911 if ( isDefault(gr2) )
912 { addCodeReference(gr);
913 deleteChain(dev->graphicals, gr);
914 prependChain(dev->graphicals, gr);
915 delCodeReference(gr);
916 } else
917 { moveBeforeChain(dev->graphicals, gr, gr2);
918 changedEntireImageGraphical(gr2);
919 }
920 requestComputeDevice(dev, DEFAULT); /* Actually only needs format */
921
922 changedEntireImageGraphical(gr);
923
924 succeed;
925 }
926
927
928 status
swapGraphicalsDevice(Device dev,Graphical gr,Graphical gr2)929 swapGraphicalsDevice(Device dev, Graphical gr, Graphical gr2)
930 { if ( gr->device != dev || (notDefault(gr2) && gr2->device != dev) )
931 fail;
932
933 swapChain(dev->graphicals, gr, gr2);
934
935 changedEntireImageGraphical(gr);
936 changedEntireImageGraphical(gr2);
937 requestComputeDevice(dev, DEFAULT); /* Actually only needs format */
938
939 succeed;
940 }
941
942
943 /********************************
944 * SELECTION *
945 ********************************/
946
947
948 static status
selectionDevice(Device dev,Any obj)949 selectionDevice(Device dev, Any obj)
950 { Cell cell;
951
952 if ( instanceOfObject(obj, ClassChain) )
953 { int size = valInt(getSizeChain(obj));
954 ArgVector(selection, size);
955 int i = 0;
956
957 for_cell(cell, (Chain)obj)
958 selection[i++] = checkType(cell->value, TypeGraphical, dev);
959
960 for_cell(cell, dev->graphicals)
961 { for(i=0; i<size; i++)
962 { if ( selection[i] == cell->value )
963 break;
964 }
965 if ( i < size )
966 send(cell->value, NAME_selected, ON, EAV);
967 else
968 send(cell->value, NAME_selected, OFF, EAV);
969 }
970
971 succeed;
972 }
973
974 for_cell(cell, dev->graphicals)
975 send(cell->value, NAME_selected, cell->value == obj ? ON : OFF, EAV);
976
977 succeed;
978 }
979
980
981 static Chain
getSelectionDevice(Device dev)982 getSelectionDevice(Device dev)
983 { Chain ch = answerObject(ClassChain, EAV);
984 Cell cell;
985
986 for_cell(cell, dev->graphicals)
987 { if ( ((Graphical)cell->value)->selected == ON )
988 appendChain(ch, cell->value);
989 }
990
991 answer(ch);
992 }
993
994
995 /*******************************
996 * LAYOUT *
997 *******************************/
998
999 static status
layoutManagerDevice(Device dev,LayoutManager mgr)1000 layoutManagerDevice(Device dev, LayoutManager mgr)
1001 { if ( dev->layout_manager != mgr )
1002 { if ( notNil(dev->layout_manager) )
1003 qadSendv(dev->layout_manager, NAME_detach, 0, NULL);
1004 assign(dev, layout_manager, mgr);
1005 if ( notNil(mgr) )
1006 qadSendv(mgr, NAME_attach, 1, (Any *)&dev);
1007 }
1008
1009 succeed;
1010 }
1011
1012
1013 /********************************
1014 * FORMATTING *
1015 *********************************/
1016
1017 static status
formatDevice(Device dev,Any obj,Any arg)1018 formatDevice(Device dev, Any obj, Any arg)
1019 { status rval = SUCCEED;
1020
1021 if ( isNil(obj) || instanceOfObject(obj, ClassFormat) )
1022 { assign(dev, format, obj);
1023 } else
1024 { if ( isNil(dev->format) )
1025 assign(dev, format, newObject(ClassFormat, EAV));
1026
1027 rval = send(dev->format, (Name)obj, arg, EAV);
1028 }
1029 requestComputeDevice(dev, DEFAULT);
1030
1031 return rval;
1032 }
1033
1034 static void
move_graphical(Graphical gr,int x,int y)1035 move_graphical(Graphical gr, int x, int y)
1036 { Int X = toInt(x);
1037 Int Y = toInt(y);
1038
1039 if ( X != gr->area->x || Y != gr->area->y )
1040 doSetGraphical(gr, X, Y, DEFAULT, DEFAULT);
1041 }
1042
1043
1044 static status
computeFormatDevice(Device dev)1045 computeFormatDevice(Device dev)
1046 { Format l;
1047
1048 if ( dev->badFormat == OFF || isNil(l=dev->format) )
1049 succeed;
1050
1051 #define HV(h, v) (l->direction == NAME_horizontal ? (h) : (v))
1052 #define MUSTBEVISIBLE(dev, gr) { if (gr->displayed == OFF) continue; }
1053
1054 if ( l->columns == ON )
1055 { int *cw; /* column widths */
1056 int *rh; /* row heights */
1057 char *cf; /* column format */
1058 int cs = valInt(l->column_sep); /* column separator size */
1059 int rs = valInt(l->row_sep); /* row separator size */
1060 Cell cell;
1061 int c, r = 0;
1062 int cols = valInt(l->width);
1063 int rows = (valInt(getSizeChain(dev->graphicals)) + cols - 1)/cols;
1064 int x = 0;
1065 int y = 0;
1066
1067 if ( !(cw = alloca(sizeof(int) * cols)) ||
1068 !(cf = alloca(sizeof(char) * cols)) ||
1069 !(rh = alloca(sizeof(int) * (rows+1))) )
1070 return errorPce(dev, NAME_notEnoughMemory);
1071
1072 for(c=0; c < cols; c++)
1073 { cw[c] = 0;
1074 cf[c] = 'l';
1075 }
1076
1077 if ( notNil(l->adjustment) )
1078 { for(c=0; c < cols; c++)
1079 { Name format = (Name) getElementVector(l->adjustment, toInt(c+1));
1080
1081 if ( equalName(format, NAME_center) )
1082 cf[c] = 'c';
1083 else if ( equalName(format, NAME_right) )
1084 cf[c] = 'r';
1085 else
1086 cf[c] = 'l';
1087 }
1088 }
1089
1090 rh[r] = c = 0;
1091 for_cell(cell, dev->graphicals)
1092 { Graphical gr = cell->value;
1093 int gw, gh;
1094
1095 MUSTBEVISIBLE(dev, gr);
1096 gw = valInt(HV(gr->area->w, gr->area->h));
1097 gh = valInt(HV(gr->area->h, gr->area->w));
1098
1099 cw[c] = max(cw[c], gw);
1100 rh[r] = max(rh[r], gh);
1101
1102 if ( ++c >= cols )
1103 { c = 0;
1104 rh[++r] = 0;
1105 }
1106 }
1107
1108 c = r = 0;
1109
1110 for_cell(cell, dev->graphicals)
1111 { Graphical gr = cell->value;
1112 MUSTBEVISIBLE(dev, gr);
1113
1114 if ( l->direction == NAME_horizontal )
1115 { switch( cf[c] )
1116 { case 'l': move_graphical(gr, x, y);
1117 break;
1118 case 'r': move_graphical(gr, x+cw[c]-valInt(gr->area->w), y);
1119 break;
1120 case 'c': move_graphical(gr, x+(cw[c]-valInt(gr->area->w))/2, y);
1121 break;
1122 }
1123 } else
1124 { switch( cf[c] )
1125 { case 'l': move_graphical(gr, y, x);
1126 break;
1127 case 'r': move_graphical(gr, y, x+cw[c]-valInt(gr->area->h));
1128 break;
1129 case 'c': move_graphical(gr, y, x+(cw[c]-valInt(gr->area->h))/2);
1130 break;
1131 }
1132 }
1133
1134 if ( c+1 >= cols )
1135 { y += rh[r++] + rs;
1136 c = 0;
1137 x = 0;
1138 } else
1139 { x += cw[c++] + cs;
1140 }
1141 }
1142 } else /* non-column device */
1143 { int x = 0;
1144 int y = 0;
1145 int w = valInt(l->width);
1146 int cs = valInt(l->column_sep);
1147 int rs = valInt(l->row_sep);
1148 int rh = 0;
1149 int first = TRUE;
1150 Cell cell;
1151
1152 for_cell(cell, dev->graphicals)
1153 { Graphical gr = cell->value;
1154 int gw, gh;
1155
1156 MUSTBEVISIBLE(dev, gr);
1157 gw = valInt(HV(gr->area->w, gr->area->h));
1158 gh = valInt(HV(gr->area->h, gr->area->w));
1159
1160 if ( !first && x + gw > w ) /* start next column */
1161 { y += rh + rs;
1162 rh = 0;
1163 x = 0;
1164 first = TRUE;
1165 }
1166 move_graphical(gr, HV(x, y), HV(y, x));
1167 x += gw + cs;
1168 rh = max(rh, gh);
1169 first = FALSE;
1170 }
1171 }
1172 #undef HV
1173
1174 assign(dev, badFormat, OFF);
1175
1176 succeed;
1177 }
1178
1179
1180 /********************************
1181 * DIALOG LAYOUT *
1182 ********************************/
1183
1184 static HashTable PlacedTable = NULL; /* placed objects */
1185
1186 static int max_rows = 20; /* will be expanded as needed */
1187 static int max_columns = 10; /* same */
1188
1189 /* flags values */
1190 #define DLF_STRETCH_TO_BB 0x1 /* Stretch-right to BB */
1191
1192 typedef struct _unit /* can't use cell! */
1193 { Graphical item; /* Item displayed here */
1194 short x; /* X-position (of column) */
1195 short height; /* Height above reference */
1196 short depth; /* Depth below reference */
1197 short right; /* Right of reference point */
1198 short left; /* Left of reference point */
1199 short hstretch; /* Strechable horizontal */
1200 short vstretch; /* Strechable vertical */
1201 Name alignment; /* alignment of the item */
1202 int flags; /* Misc alignment flags */
1203 } unit, *Unit;
1204
1205 static unit empty_unit = { (Graphical) NIL,
1206 0, 0, 0, 0, 0, 0, 0,
1207 NAME_column, 0
1208 };
1209
1210 typedef struct _matrix
1211 { int cols; /* actual size */
1212 int rows;
1213 unit **units;
1214 } matrix, *Matrix;
1215
1216
1217 #define IsPlaced(gr) (getMemberHashTable(PlacedTable, gr) == ON)
1218 #define SetPlaced(gr) (appendHashTable(PlacedTable, gr, ON))
1219
1220 static status
shift_x_matrix(Matrix m,int * max_x,int * max_y)1221 shift_x_matrix(Matrix m, int *max_x, int *max_y)
1222 { int x, y;
1223
1224 if ( *max_x + 1 > max_columns )
1225 fail;
1226
1227 m->units[*max_x] = alloc(sizeof(unit) * max_rows);
1228 for(y=0; y < *max_y; y++)
1229 { for(x = *max_x; x > 0; x--)
1230 m->units[x][y] = m->units[x-1][y];
1231
1232 m->units[0][y] = empty_unit;
1233 }
1234
1235 (*max_x)++;
1236 succeed;
1237 }
1238
1239
1240 static status
shift_y_matrix(Matrix m,int * max_x,int * max_y)1241 shift_y_matrix(Matrix m, int *max_x, int *max_y)
1242 { int x, y;
1243
1244 if ( *max_y + 1 > max_rows )
1245 fail;
1246
1247 for(x=0; x < *max_x; x++)
1248 { for(y = *max_y; y > 0; y--)
1249 m->units[x][y] = m->units[x][y-1];
1250
1251 m->units[x][0] = empty_unit;
1252 }
1253
1254 (*max_y)++;
1255 succeed;
1256 }
1257
1258
1259 static status
expand_x_matrix(Matrix m,int * max_x,int * max_y)1260 expand_x_matrix(Matrix m, int *max_x, int *max_y)
1261 { int y;
1262
1263 if ( *max_x + 1 > max_columns )
1264 fail;
1265
1266 m->units[*max_x] = alloc(sizeof(unit) * max_rows);
1267 for(y=0; y < *max_y; y++)
1268 m->units[*max_x][y] = empty_unit;
1269
1270 (*max_x)++;
1271 succeed;
1272 }
1273
1274
1275 static status
expand_y_matrix(Matrix m,int * max_x,int * max_y)1276 expand_y_matrix(Matrix m, int *max_x, int *max_y)
1277 { int x;
1278
1279 if ( *max_y + 1 > max_rows )
1280 fail;
1281
1282 for(x=0; x < *max_x; x++)
1283 m->units[x][*max_y] = empty_unit;
1284
1285 (*max_y)++;
1286 succeed;
1287 }
1288
1289
1290 static void
free_matrix_columns(Matrix m,int max_x)1291 free_matrix_columns(Matrix m, int max_x)
1292 { int x;
1293
1294 for(x=0; x<max_x; x++)
1295 unalloc(sizeof(unit) * max_rows, m->units[x]);
1296 }
1297
1298
1299 static status
placeDialogItem(Device d,Matrix m,Graphical i,int x,int y,int * max_x,int * max_y)1300 placeDialogItem(Device d, Matrix m, Graphical i,
1301 int x, int y, int *max_x, int *max_y)
1302 { Graphical gr;
1303
1304 if ( IsPlaced(i) ||
1305 get(i, NAME_autoAlign, EAV) != ON )
1306 succeed;
1307
1308 if ( isNil(i->device) )
1309 displayDevice(d, i, DEFAULT);
1310
1311 /*
1312 if ( i->displayed == OFF )
1313 succeed;
1314 */
1315
1316 SetPlaced(i);
1317
1318 DEBUG(NAME_layout, Cprintf("placing %s\n", pp(i)));
1319
1320 while( x < 0 ) { TRY(shift_x_matrix(m, max_x, max_y)); x++; }
1321 while( y < 0 ) { TRY(shift_y_matrix(m, max_x, max_y)); y++; }
1322 while( x >= *max_x ) TRY(expand_x_matrix(m, max_x, max_y));
1323 while( y >= *max_y ) TRY(expand_y_matrix(m, max_x, max_y));
1324
1325 m->units[x][y].item = i;
1326 m->units[x][y].alignment = get(i, NAME_alignment, EAV);
1327 if ( !m->units[x][y].alignment )
1328 m->units[x][y].alignment = NAME_left;
1329
1330 if ( instanceOfObject((gr = get(i, NAME_below, EAV)), ClassGraphical) )
1331 TRY(placeDialogItem(d, m, gr, x, y-1, max_x, max_y));
1332 if ( instanceOfObject((gr = get(i, NAME_above, EAV)), ClassGraphical) )
1333 TRY(placeDialogItem(d, m, gr, x, y+1, max_x, max_y));
1334 if ( instanceOfObject((gr = get(i, NAME_left, EAV)), ClassGraphical) )
1335 TRY(placeDialogItem(d, m, gr, x+1, y, max_x, max_y));
1336 if ( instanceOfObject((gr = get(i, NAME_right, EAV)), ClassGraphical) )
1337 TRY(placeDialogItem(d, m, gr, x-1, y, max_x, max_y));
1338
1339 succeed;
1340 }
1341
1342
1343 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1344 Adjust to the bounding box by adjusting all columns containing
1345 stretchable items.
1346 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1347
1348 static void
stretchColumns(Matrix m,Size gap,Size bb,Size border)1349 stretchColumns(Matrix m, Size gap, Size bb, Size border)
1350 { if ( notDefault(bb) )
1351 { int twidth = valInt(bb->w) - 2 * valInt(border->w); /* total width */
1352 Stretch s = alloca(sizeof(stretch) * m->cols);
1353 Stretch sp;
1354 int x, y;
1355
1356 twidth -= (m->cols-1) * valInt(gap->w);
1357
1358 for(sp=s, x=0; x<m->cols; x++, sp++)
1359 { int stretch = 0, noshrink=FALSE;
1360
1361 sp->ideal = m->units[x][0].left + m->units[x][0].right;
1362 sp->minimum = 0;
1363 sp->maximum = INT_MAX;
1364
1365 for(y=0; y<m->rows; y++)
1366 { if ( m->units[x][y].alignment == NAME_column )
1367 { stretch = max(stretch, m->units[x][y].hstretch);
1368 if ( m->units[x][y].hstretch == 0 )
1369 noshrink = TRUE;
1370 }
1371 }
1372
1373 sp->stretch = stretch;
1374 if ( stretch > 0 && !noshrink )
1375 sp->shrink = stretch;
1376 else
1377 sp->shrink = 0;
1378 }
1379
1380 distribute_stretches(s, m->cols, twidth);
1381
1382 for(sp=s, x=0; x<m->cols; x++, sp++)
1383 { for(y=0; y<m->rows; y++)
1384 { if ( m->units[x][y].alignment == NAME_column )
1385 m->units[x][0].right = sp->size - m->units[x][0].left;
1386 }
1387 }
1388 }
1389 }
1390
1391
1392 static void
determineXColumns(Matrix m,Size gap,Size bb,Size border)1393 determineXColumns(Matrix m, Size gap, Size bb, Size border)
1394 { int x, y;
1395 int cx = valInt(border->w);
1396
1397 for(x=0; x<m->cols; x++)
1398 { int maxr = 0;
1399
1400 for(y=0; y<m->rows; y++)
1401 { int r;
1402
1403 if ( x == 0 || m->units[x][y].alignment == NAME_column )
1404 m->units[x][y].x = cx;
1405 else
1406 m->units[x][y].x = m->units[x-1][y].x +
1407 m->units[x-1][y].left +
1408 m->units[x-1][y].right +
1409 valInt(gap->w);
1410 r = m->units[x][y].x + m->units[x][y].left + m->units[x][y].right;
1411 maxr = max(maxr, r);
1412 }
1413
1414 cx = maxr + valInt(gap->w);
1415 }
1416 }
1417
1418
1419 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1420 stretchRows() stretches the rows to deal with objects having
1421 <-ver_stretch defined. bbh is the total height that should be taken by
1422 the objects. itemssh is the amount currently used.
1423 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1424
1425 static void
stretchRows(Matrix m,int bbh)1426 stretchRows(Matrix m, int bbh)
1427 { int x, y;
1428 Stretch s = alloca(sizeof(stretch) * m->rows);
1429 Stretch sp;
1430
1431 for(sp = s, y=0; y<m->rows; y++)
1432 { int stretch = 0, noshrink=FALSE;
1433
1434 if ( m->units[0][y].height == 0 && m->units[0][y].depth == 0 )
1435 continue;
1436
1437 sp->ideal = m->units[0][y].height + m->units[0][y].depth;
1438 sp->minimum = 0;
1439 sp->maximum = INT_MAX;
1440
1441 for(x=0; x<m->cols; x++)
1442 { stretch = max(stretch, m->units[x][y].vstretch);
1443 if ( m->units[x][y].vstretch == 0 &&
1444 notNil(m->units[x][y].item) )
1445 noshrink = TRUE;
1446 }
1447
1448 sp->stretch = stretch;
1449 if ( stretch > 0 && !noshrink )
1450 sp->shrink = stretch;
1451 else
1452 sp->shrink = 0;
1453
1454 if ( stretch == 0 && y < m->rows - 1 )
1455 sp->stretch = 1;
1456
1457 sp++;
1458 }
1459
1460 distribute_stretches(s, sp-s, bbh);
1461
1462 for(sp=s, y=0; y<m->rows; y++)
1463 { if ( m->units[0][y].height == 0 && m->units[0][y].depth == 0 )
1464 continue;
1465
1466 for(x=0; x<m->cols; x++)
1467 { if ( !(sp->shrink == 0 &&
1468 sp->size < m->units[x][y].depth + m->units[x][y].height) )
1469 m->units[x][y].depth = sp->size - m->units[x][y].height;
1470 }
1471
1472 sp++;
1473 }
1474 }
1475
1476
1477
1478 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1479 adjustDialogItem() is as doSetGraphical, but returns 0 if there was no
1480 change and 1 if there was a change.
1481
1482 We need a special hack here to deal with windows. Actually, we need
1483 something to tell an object to have a certain geometry in pixels and not
1484 negotiate, but deal properly with containers/decorations, etc.
1485 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1486
1487 static int
adjustDialogItem(Any obj,Int x,Int y,Int w,Int h)1488 adjustDialogItem(Any obj, Int x, Int y, Int w, Int h)
1489 { Graphical gr = obj;
1490
1491 DEBUG(NAME_layout,
1492 Cprintf("%s --> %s %s %s %s\n",
1493 pp(obj), pp(x), pp(y), pp(w), pp(h)));
1494
1495 if ( instanceOfObject(gr, ClassWindow) && /* HACK */
1496 notNil(((PceWindow)gr)->decoration) )
1497 gr = (Graphical)((PceWindow)gr)->decoration;
1498
1499 #define Changed(a) (gr->area->a != a && notDefault(a))
1500 if ( Changed(x) || Changed(y) || Changed(w) || Changed(h) )
1501 { Int av[4];
1502
1503 av[0] = x; av[1] = y; av[2] = w; av[3] = h;
1504
1505 qadSendv(gr, NAME_geometry, 4, av);
1506 return 1;
1507 }
1508 #undef Changed
1509
1510 return 0;
1511 }
1512
1513
1514 static int
grow_max_matrix(int max_x,int max_y)1515 grow_max_matrix(int max_x, int max_y)
1516 { if ( max_x >= max_columns )
1517 max_columns *= 2;
1518 if ( max_y >= max_rows )
1519 max_rows *= 2;
1520
1521 succeed;
1522 }
1523
1524
1525 status
layoutDialogDevice(Device d,Size gap,Size bb,Size border)1526 layoutDialogDevice(Device d, Size gap, Size bb, Size border)
1527 { matrix m;
1528 int x, y, max_x, max_y;
1529 int px, py;
1530 Graphical gr;
1531 Cell cell;
1532 int ln;
1533 int found, changed;
1534 char *align_flags;
1535
1536 retry:
1537 max_x = 0, max_y = 0;
1538 found = 0;
1539 changed = 1;
1540 if ( !(m.units = alloca(sizeof(unit*)*max_columns)) )
1541 { Cprintf("Alloca(%d) failed\n", sizeof(unit*)*max_columns);
1542 fail;
1543 }
1544
1545 if ( isDefault(gap) )
1546 { PceWindow sw = getWindowGraphical((Graphical) d);
1547
1548 if ( instanceOfObject(sw, ClassDialog) )
1549 gap = getClassVariableValueObject(sw, NAME_gap);
1550 else
1551 gap = getClassVariableValueClass(ClassDialog, NAME_gap);
1552
1553 if ( !gap )
1554 gap = answerObject(ClassSize, toInt(15), toInt(8), EAV);
1555 }
1556 if ( isDefault(border) )
1557 border = gap;
1558
1559 for_cell(cell, d->graphicals)
1560 send(cell->value, NAME_layoutDialog, EAV);
1561
1562 if ( !PlacedTable )
1563 PlacedTable = createHashTable(toInt(32), NAME_none);
1564 else
1565 clearHashTable(PlacedTable);
1566
1567 for_cell(cell, d->graphicals)
1568 { if ( !IsPlaced(cell->value) &&
1569 get(cell->value, NAME_autoAlign, EAV) == ON )
1570 { if ( !placeDialogItem(d, &m, cell->value, 0, 0, &max_x, &max_y) )
1571 { free_matrix_columns(&m, max_x);
1572 if ( grow_max_matrix(max_x, max_y) )
1573 goto retry;
1574 else
1575 fail;
1576 }
1577 found++;
1578 break;
1579 }
1580 }
1581
1582 if ( found == 0 )
1583 succeed; /* finished */
1584
1585 m.cols = max_x;
1586 m.rows = max_y;
1587 align_flags = alloca(max_y+1);
1588
1589 for(ln = 0; changed && ln < 4; ln++) /* avoid endless recursion */
1590 { changed = 0; /* see whether something changed */
1591
1592 for(x=0; x<max_x; x++) /* Align labels and values */
1593 { int lw = -1;
1594 int vw = -1;
1595 int chl = FALSE;
1596 int chv = FALSE;
1597
1598 #define AUTO_ALIGN_LABEL 1
1599 #define AUTO_ALIGN_VALUE 2
1600
1601 for(y=0; y<max_y; y++)
1602 { align_flags[y] = 0;
1603
1604 if ( notNil(gr = m.units[x][y].item) &&
1605 gr->displayed == ON &&
1606 m.units[x][y].alignment == NAME_column )
1607 { int w;
1608
1609 if ( get(gr, NAME_autoLabelAlign, EAV) == ON )
1610 { if ( (w = valInt(get(gr, NAME_labelWidth, EAV))) != lw )
1611 { if ( lw >= 0 )
1612 chl++;
1613 lw = max(w, lw);
1614 }
1615 align_flags[y] |= AUTO_ALIGN_LABEL;
1616 }
1617
1618 if ( get(gr, NAME_autoValueAlign, EAV) == ON )
1619 { if ( (w = valInt(get(gr, NAME_valueWidth, EAV))) != vw )
1620 { if ( vw >= 0 )
1621 chv++;
1622 vw = max(w, vw);
1623 }
1624 align_flags[y] |= AUTO_ALIGN_VALUE;
1625 }
1626 }
1627 }
1628 if ( chl )
1629 { changed++;
1630
1631 for(y=0; y<max_y; y++)
1632 { if ( (align_flags[y] & AUTO_ALIGN_LABEL) )
1633 send(m.units[x][y].item, NAME_labelWidth, toInt(lw), EAV);
1634 }
1635 }
1636 if ( chv )
1637 { changed++;
1638
1639 for(y=0; y<max_y; y++)
1640 { if ( (align_flags[y] & AUTO_ALIGN_VALUE) )
1641 send(m.units[x][y].item, NAME_valueWidth, toInt(vw), EAV);
1642 }
1643 }
1644 }
1645
1646 ComputeGraphical(d); /* recompute for possible changes */
1647
1648 for(x=0; x<max_x; x++) /* Get sizes */
1649 { for(y=0; y<max_y; y++)
1650 { Unit u = &m.units[x][y];
1651
1652 if ( notNil(gr = u->item) && gr->displayed == ON )
1653 { Point reference = get(gr, NAME_reference, EAV);
1654 int rx = (reference ? valInt(reference->x) : 0);
1655 int ry = (reference ? valInt(reference->y) : 0);
1656 Int hs = get(gr, NAME_horStretch, EAV);
1657 Int vs = get(gr, NAME_verStretch, EAV);
1658
1659 if ( !hs ) hs = ZERO;
1660 if ( !vs ) vs = ZERO;
1661
1662 u->left = rx;
1663 u->height = ry;
1664 u->depth = valInt(gr->area->h) - ry;
1665 u->right = valInt(gr->area->w) - rx;
1666 u->hstretch = valInt(hs);
1667 u->vstretch = valInt(vs);
1668
1669 DEBUG(NAME_layout,
1670 Cprintf("%d,%d %s lrhd=%d %d %d %d\n",
1671 x+1, y+1, pp(gr),
1672 u->left, u->right, u->height, u->depth));
1673 } else
1674 { u->left = 0;
1675 u->height = 0;
1676 u->depth = 0;
1677 u->right = 0;
1678 }
1679 }
1680 }
1681
1682
1683 for(x=0; x<max_x; x++) /* Determine unit width */
1684 { int r = 0, l = 0;
1685
1686 for(y=0; y<max_y; y++)
1687 { if ( m.units[x][y].alignment == NAME_column )
1688 { if ( m.units[x][y].right > r ) r = m.units[x][y].right;
1689 if ( m.units[x][y].left > l ) l = m.units[x][y].left;
1690 }
1691 }
1692
1693 for(y=0; y<max_y; y++)
1694 { if ( m.units[x][y].alignment == NAME_column )
1695 { m.units[x][y].right = r;
1696 m.units[x][y].left = l;
1697 }
1698 }
1699 }
1700 stretchColumns(&m, gap, bb, border);
1701 determineXColumns(&m, gap, bb, border);
1702
1703
1704 { int gaph = valInt(gap->h);
1705
1706 for(y=0; y<max_y; y++) /* Determine unit height */
1707 { int h = -1000, d = -1000;
1708
1709 for(x=0; x<max_x; x++)
1710 { if ( m.units[x][y].height > h ) h = m.units[x][y].height;
1711 if ( m.units[x][y].depth > d ) d = m.units[x][y].depth;
1712 }
1713 DEBUG(NAME_layout,
1714 Cprintf("Row %d +ascent-descent +%d-%d\n", y+1, h, d));
1715 for(x=0; x<max_x; x++)
1716 { m.units[x][y].height = h;
1717 m.units[x][y].depth = d;
1718 }
1719 }
1720
1721 /* distribute in Y-direction */
1722 if ( notDefault(bb) && max_y > 1 && valInt(bb->h) > 0 )
1723 stretchRows(&m,
1724 valInt(bb->h) - valInt(border->h) * 2 - (max_y-1) * gaph);
1725
1726 /* Place the items */
1727 for(py = valInt(border->h), y=0; y<max_y; y++)
1728 { int px = valInt(border->w);
1729 int lx = px; /* x for left aligned items */
1730 int gapw = valInt(gap->w);
1731
1732 if ( m.units[0][y].depth == 0 && m.units[0][y].height == 0 )
1733 { DEBUG(NAME_layout, Cprintf("Skipping empty row %d\n", y+1));
1734 continue; /* empty row (not displayed) */
1735 }
1736
1737 for(x = 0; x < max_x; x++)
1738 { if ( notNil(gr = m.units[x][y].item) &&
1739 gr->displayed == ON )
1740 { Point reference = get(gr, NAME_reference, EAV);
1741 int rx = (reference ? valInt(reference->x) : 0);
1742 int ry = (reference ? valInt(reference->y) : 0);
1743 int ix, iy = py + m.units[x][y].height;
1744 Int iw = DEFAULT;
1745 Int ih = DEFAULT;
1746
1747 DEBUG(NAME_layout,
1748 Cprintf("Placing %s at %d,%d\n", pp(gr), x+1,y+1));
1749
1750 if ( m.units[x][y].alignment == NAME_column )
1751 ix = m.units[x][y].x;
1752 else
1753 ix = lx;
1754 ix += m.units[x][y].left;
1755
1756 /* hor_stretch handling */
1757 if ( m.units[x][y].hstretch )
1758 { int nx=0; /* make compiler happy */
1759
1760 if ( x+1 < max_x && notNil(m.units[x+1][y].item) )
1761 { nx = m.units[x][y].left + m.units[x][y].right + gapw;
1762
1763 if ( m.units[x+1][y].alignment == NAME_column )
1764 nx += px;
1765 else
1766 nx += ix - rx;
1767
1768 DEBUG(NAME_layout,
1769 Cprintf("Right stretch of %s to next column at %d\n",
1770 pp(gr), nx));
1771 } else if ( notDefault(bb) )
1772 { iw = toInt(valInt(bb->w) - valInt(border->w) - (ix - rx));
1773 DEBUG(NAME_layout,
1774 Cprintf("Right stretch of %s to BB at %d\n",
1775 pp(gr), valInt(bb->w)));
1776 } else
1777 { m.units[x][y].flags |= DLF_STRETCH_TO_BB;
1778 DEBUG(NAME_layout,
1779 Cprintf("Flagged right stretch of %s to BB\n",
1780 pp(gr)));
1781 iw = toInt(m.units[x][y].left + m.units[x][y].right);
1782 /* DEBUG(NAME_layout,
1783 Cprintf("Right stretch of %s to column width %d\n",
1784 pp(gr), valInt(iw))); */
1785 }
1786
1787 if ( isDefault(iw) )
1788 iw = toInt(nx - gapw - (ix - rx));
1789 }
1790
1791 if ( m.units[x][y].vstretch ) /* ver_stretch handling */
1792 { ih = toInt(m.units[x][y].height + m.units[x][y].depth);
1793 }
1794
1795 changed += adjustDialogItem(gr,
1796 toInt(ix - rx), toInt(iy - ry),
1797 iw, ih);
1798 lx = valInt(gr->area->x) + valInt(gr->area->w) + gapw;
1799 }
1800 px += m.units[x][y].left + m.units[x][y].right + gapw;
1801 }
1802
1803 py += m.units[0][y].depth + m.units[0][y].height + gaph;
1804 DEBUG(NAME_layout, Cprintf("Moving to row %d at %d\n",
1805 y+1, py));
1806 }
1807 }
1808
1809 ComputeGraphical(d); /* recompute bounding-box */
1810
1811 for(y = 0; y < max_y; y++)
1812 { if ( notDefault(bb) )
1813 { px = valInt(bb->w); /* px: right-side of bb */
1814 } else
1815 { if ( instanceOfObject(d, ClassWindow) )
1816 { PceWindow sw = (PceWindow) d;
1817
1818 px = valInt(sw->bounding_box->x) +
1819 valInt(sw->bounding_box->w) +
1820 valInt(border->w);
1821 } else
1822 { px = valInt(d->area->x) - valInt(d->offset->x) +
1823 valInt(d->area->w)/* + valInt(border->w)*/;
1824 }
1825 }
1826
1827 for(x = max_x-1; x >= 0; x--)
1828 { if ( notNil(gr = m.units[x][y].item) &&
1829 gr->displayed == ON )
1830 { if ( m.units[x][y].flags & DLF_STRETCH_TO_BB )
1831 { int iw = px-valInt(border->w)-valInt(gr->area->x);
1832
1833 if ( iw > valInt(gr->area->w) )
1834 { adjustDialogItem(gr, DEFAULT, DEFAULT, toInt(iw), DEFAULT);
1835 DEBUG(NAME_layout,
1836 Cprintf("Delayed right stretch of %s to BB %d\n",
1837 pp(gr), iw));
1838 }
1839 } else if ( m.units[x][y].alignment == NAME_right ||
1840 m.units[x][y].alignment == NAME_center )
1841 { Name algnmt = m.units[x][y].alignment;
1842 int x2;
1843 Graphical gr2 = NULL, grl = gr;
1844 int tw, dx;
1845
1846 DEBUG(NAME_layout, Cprintf("%s is aligned %s\n",
1847 pp(gr), pp(algnmt)));
1848
1849 for(x2 = x-1; x2 >= 0; x2--)
1850 { if ( notNil(gr2 = m.units[x2][y].item) &&
1851 gr2->displayed == ON )
1852 { if ( m.units[x2][y].alignment != algnmt )
1853 break;
1854 else
1855 { DEBUG(NAME_layout, Cprintf("\tadding %s\n",
1856 pp(m.units[x2][y].item)));
1857 grl = gr2;
1858 }
1859 }
1860 }
1861
1862 tw = valInt(getRightSideGraphical(gr)) - valInt(grl->area->x);
1863
1864 if ( m.units[x][y].alignment == NAME_right )
1865 dx = px - tw - valInt(gap->w);
1866 else
1867 { int sx = (x2 < 0 ? 0 : valInt(getRightSideGraphical(gr2)));
1868 DEBUG(NAME_layout, Cprintf("sx = %d; ", sx));
1869 dx = (px - sx - tw)/2 + sx;
1870 }
1871 dx -= valInt(getLeftSideGraphical(grl));
1872
1873 DEBUG(NAME_layout,
1874 Cprintf("R = %d; Total width = %d, shift = %d\n",
1875 px, tw, dx));
1876
1877 for(; ; x--)
1878 { if ( notNil(gr = m.units[x][y].item) &&
1879 gr->displayed == ON )
1880 { changed += adjustDialogItem(gr,
1881 toInt(valInt(gr->area->x) + dx),
1882 DEFAULT, DEFAULT, DEFAULT);
1883 DEBUG(NAME_layout, Cprintf("\t moved %s\n", pp(gr)));
1884 if ( gr == grl )
1885 break;
1886 }
1887 }
1888 }
1889
1890 px = valInt(gr->area->x);
1891 }
1892 }
1893 }
1894 }
1895
1896 free_matrix_columns(&m, max_x);
1897
1898 if ( hasSendMethodObject(d, NAME_assignAccelerators) )
1899 send(d, NAME_assignAccelerators, EAV);
1900
1901 { PceWindow sw;
1902
1903 if ( (sw = getWindowGraphical((Graphical) d)) &&
1904 isNil(sw->keyboard_focus) )
1905 send(d, NAME_advance, NIL, EAV);
1906 }
1907
1908 succeed;
1909 }
1910
1911 status
appendDialogItemDevice(Device d,Graphical item,Name where)1912 appendDialogItemDevice(Device d, Graphical item, Name where)
1913 { Graphical di;
1914 Name algmnt;
1915
1916 if ( emptyChain(d->graphicals) )
1917 return appendDialogItemNetworkDevice(d, item);
1918
1919 send(item, NAME_autoAlign, ON, EAV);
1920
1921 di = getTailChain(d->graphicals);
1922 if ( isDefault(where) )
1923 { if ( instanceOfObject(di, ClassButton) &&
1924 instanceOfObject(item, ClassButton) )
1925 where = NAME_right;
1926 else
1927 where = NAME_nextRow;
1928 } else if ( where == NAME_right &&
1929 (algmnt = get(di, NAME_alignment, EAV)) != NAME_column )
1930 send(item, NAME_alignment, algmnt, EAV);
1931
1932 if ( where == NAME_nextRow )
1933 { Graphical left;
1934
1935 while ( (left = get(di, NAME_right, EAV)) && notNil(left) )
1936 di = left;
1937 where = NAME_below;
1938 }
1939 /* Do not use the implementation of */
1940 /* class window */
1941 return vm_send(item, where, ClassGraphical, 1, (Any *)&di);
1942 }
1943
1944
1945 /********************************
1946 * MISCELLANEOUS *
1947 ********************************/
1948
1949
1950 static status
convertLoadedObjectDevice(Device dev,Int ov,Int cv)1951 convertLoadedObjectDevice(Device dev, Int ov, Int cv)
1952 { if ( isNil(dev->recompute) )
1953 assign(dev, recompute, newObject(ClassChain, EAV));
1954
1955 succeed;
1956 }
1957
1958
1959 static status
reparentDevice(Device dev)1960 reparentDevice(Device dev)
1961 { Cell cell;
1962
1963 if ( isNil(dev->device) )
1964 assign(dev, level, ZERO);
1965 else
1966 assign(dev, level, add(dev->device->level, ONE));
1967
1968 for_cell(cell, dev->graphicals)
1969 qadSendv(cell->value, NAME_reparent, 0, NULL);
1970
1971 return reparentGraphical((Graphical) dev);
1972 }
1973
1974
1975 static status
roomDevice(Device dev,Area area)1976 roomDevice(Device dev, Area area)
1977 { register Cell cell;
1978
1979 ComputeGraphical(dev);
1980 for_cell(cell, dev->graphicals)
1981 { Graphical gr = cell->value;
1982
1983 if ( gr->displayed == ON && overlapArea(gr->area, area) )
1984 fail;
1985 }
1986
1987 succeed;
1988 }
1989
1990
1991 static Chain
getInsideDevice(Device dev,Area a)1992 getInsideDevice(Device dev, Area a)
1993 { register Cell cell;
1994 Chain ch;
1995
1996 ch = answerObject(ClassChain, EAV);
1997
1998 ComputeGraphical(dev);
1999 for_cell(cell, dev->graphicals)
2000 { if ( insideArea(a, ((Graphical) cell->value)->area) )
2001 appendChain(ch, cell->value);
2002 }
2003
2004 answer(ch);
2005 }
2006
2007
2008 static status
resizeDevice(Device dev,Real xfactor,Real yfactor,Point origin)2009 resizeDevice(Device dev, Real xfactor, Real yfactor, Point origin)
2010 { float xf, yf;
2011 int ox = valInt(dev->offset->x);
2012 int oy = valInt(dev->offset->y);
2013 Point p;
2014 Cell cell;
2015
2016 init_resize_graphical(dev, xfactor, yfactor, origin, &xf, &yf, &ox, &oy);
2017 if ( xf == 1.0 && yf == 1.0 )
2018 succeed;
2019
2020 p = tempObject(ClassPoint, toInt(ox - valInt(dev->offset->x)),
2021 toInt(oy - valInt(dev->offset->y)), EAV);
2022 for_cell(cell, dev->graphicals)
2023 send(cell->value, NAME_resize, xfactor, yfactor, p, EAV);
2024 considerPreserveObject(p);
2025
2026 succeed;
2027 }
2028
2029
2030 /********************************
2031 * NAMED MEMBERS *
2032 ********************************/
2033
2034 Graphical
getMemberDevice(Device dev,Name name)2035 getMemberDevice(Device dev, Name name)
2036 { if ( notNil(dev->graphicals) )
2037 { Cell cell;
2038
2039 for_cell(cell, dev->graphicals)
2040 { if (((Graphical)cell->value)->name == name)
2041 answer(cell->value);
2042 }
2043 }
2044
2045 fail;
2046 }
2047
2048
2049 static status
forSomeDevice(Device dev,Name name,Code msg)2050 forSomeDevice(Device dev, Name name, Code msg)
2051 { Cell cell, c2;
2052
2053 for_cell_save(cell, c2, dev->graphicals)
2054 { Graphical gr = cell->value;
2055
2056 if ( isDefault(name) || gr->name == name )
2057 forwardReceiverCode(msg, dev, gr, EAV);
2058 }
2059
2060 succeed;
2061 }
2062
2063
2064 static status
forAllDevice(Device dev,Name name,Code msg)2065 forAllDevice(Device dev, Name name, Code msg)
2066 { Cell cell, c2;
2067
2068 for_cell_save(cell, c2, dev->graphicals)
2069 { Graphical gr = cell->value;
2070
2071 if ( isDefault(name) || gr->name == name )
2072 TRY(forwardReceiverCode(msg, dev, gr, EAV));
2073 }
2074
2075 succeed;
2076 }
2077
2078 /********************************
2079 * MOVING *
2080 ********************************/
2081
2082
2083 status
updateConnectionsDevice(Device dev,Int level)2084 updateConnectionsDevice(Device dev, Int level)
2085 { Cell cell;
2086
2087 for_cell(cell, dev->graphicals)
2088 { if ( instanceOfObject(cell->value, ClassDevice) )
2089 updateConnectionsDevice(cell->value, level);
2090 else
2091 updateConnectionsGraphical(cell->value, level);
2092 }
2093
2094 return updateConnectionsGraphical((Graphical) dev, level);
2095 }
2096
2097
2098 status
geometryDevice(Device dev,Int x,Int y,Int w,Int h)2099 geometryDevice(Device dev, Int x, Int y, Int w, Int h)
2100 { ComputeGraphical(dev);
2101
2102 if ( isDefault(x) ) x = dev->area->x;
2103 if ( isDefault(y) ) y = dev->area->y;
2104
2105 if ( x != dev->area->x || y != dev->area->y )
2106 { Int dx = sub(x, dev->area->x);
2107 Int dy = sub(y, dev->area->y);
2108
2109 CHANGING_GRAPHICAL(dev,
2110 assign(dev->offset, x, add(dev->offset->x, dx));
2111 assign(dev->offset, y, add(dev->offset->y, dy));
2112 if ( notNil(dev->clip_area) )
2113 { assign(dev, badBoundingBox, ON); /* TBD: ??? */
2114 computeBoundingBoxDevice(dev);
2115 } else
2116 { assign(dev->area, x, x);
2117 assign(dev->area, y, y);
2118 });
2119
2120 updateConnectionsDevice(dev, sub(dev->level, ONE));
2121 }
2122
2123 succeed;
2124 }
2125
2126
2127 /********************************
2128 * REFERENCE *
2129 ********************************/
2130
2131
2132 static status
referenceDevice(Device dev,Point pos)2133 referenceDevice(Device dev, Point pos)
2134 { Int x, y;
2135
2136 if ( isDefault(pos) )
2137 { ComputeGraphical(dev);
2138 x = sub(dev->area->x, dev->offset->x);
2139 y = sub(dev->area->y, dev->offset->y);
2140 } else
2141 { x = pos->x;
2142 y = pos->y;
2143 }
2144
2145 if ( x != ZERO || y != ZERO )
2146 { Cell cell;
2147 Point move = tempObject(ClassPoint, sub(ZERO, x), sub(ZERO, y), EAV);
2148
2149 offsetPoint(dev->offset, x, y);
2150 for_cell(cell, dev->graphicals)
2151 relativeMoveGraphical(cell->value, move);
2152
2153 considerPreserveObject(move);
2154 }
2155
2156 succeed;
2157 }
2158
2159
2160 static status
set_position_device(Device dev,Int x,Int y)2161 set_position_device(Device dev, Int x, Int y)
2162 { ComputeGraphical(dev);
2163
2164 if ( isDefault(x) ) x = dev->offset->x;
2165 if ( isDefault(y) ) y = dev->offset->y;
2166
2167 x = add(dev->area->x, sub(x, dev->offset->x));
2168 y = add(dev->area->y, sub(y, dev->offset->y));
2169
2170 return setGraphical(dev, x, y, DEFAULT, DEFAULT);
2171 }
2172
2173
2174 static status
positionDevice(Device dev,Point pos)2175 positionDevice(Device dev, Point pos)
2176 { return set_position_device(dev, pos->x, pos->y);
2177 }
2178
2179
2180 static status
xDevice(Device dev,Int x)2181 xDevice(Device dev, Int x)
2182 { return set_position_device(dev, x, DEFAULT);
2183 }
2184
2185
2186 static status
yDevice(Device dev,Int y)2187 yDevice(Device dev, Int y)
2188 { return set_position_device(dev, DEFAULT, y);
2189 }
2190
2191
2192 static Point
getPositionDevice(Device dev)2193 getPositionDevice(Device dev)
2194 { ComputeGraphical(dev);
2195 answer(dev->offset);
2196 }
2197
2198
2199 static Int
getXDevice(Device dev)2200 getXDevice(Device dev)
2201 { answer(getPositionDevice(dev)->x);
2202 }
2203
2204
2205 static Int
getYDevice(Device dev)2206 getYDevice(Device dev)
2207 { answer(getPositionDevice(dev)->y);
2208 }
2209
2210
2211 static Point
getOffsetDevice(Device dev)2212 getOffsetDevice(Device dev)
2213 { ComputeGraphical(dev);
2214 answer(dev->offset);
2215 }
2216
2217
2218 /********************************
2219 * CATCH ALL *
2220 ********************************/
2221
2222 static Any
getCatchAllDevice(Device dev,Name name)2223 getCatchAllDevice(Device dev, Name name)
2224 { Name base;
2225
2226 if ( (base = getDeleteSuffixName(name, NAME_Member)) )
2227 answer(getMemberDevice(dev, base));
2228
2229 errorPce(dev, NAME_noBehaviour, CtoName("<-"), name);
2230 fail;
2231 }
2232
2233 /********************************
2234 * VISUAL *
2235 ********************************/
2236
2237 static Chain
getContainsDevice(Device dev)2238 getContainsDevice(Device dev)
2239 { answer(dev->graphicals);
2240 }
2241
2242 /*******************************
2243 * CLASS DECLARATION *
2244 *******************************/
2245
2246 /* Type declarations */
2247
2248 static char *T_DnameD_code[] =
2249 { "[name]", "code" };
2250 static char *T_find[] =
2251 { "at=[point|event]", "condition=[code]" };
2252 static char *T_pointedObjects[] =
2253 { "at=point|event", "append_to=[chain]" };
2254 static char *T_typed[] =
2255 { "event_id", "[bool]" };
2256 static char *T_format[] =
2257 { "format*|name", "[any]" };
2258 static char *T_layout[] =
2259 { "gap=[size]", "size=[size]", "border=[size]" };
2260 static char *T_modifiedItem[] =
2261 { "graphical", "bool" };
2262 static char *T_display[] =
2263 { "graphical", "position=[point]" };
2264 static char *T_appendDialogItem[] =
2265 { "item=graphical", "relative_to_last=[{below,right,next_row}]" };
2266 static char *T_convertLoadedObject[] =
2267 { "old_version=int", "new_version=int" };
2268 static char *T_changedUnion[] =
2269 { "ox=int", "oy=int", "ow=int", "oh=int" };
2270 static char *T_geometry[] =
2271 { "x=[int]", "y=[int]", "width=[int]", "height=[int]" };
2272 static char *T_resize[] =
2273 { "x_factor=real", "y_factor=[real]", "origin=[point]" };
2274 static char *T_flash[] =
2275 { "area=[area]", "time=[int]" };
2276 static char *T_advance[] =
2277 { "from=[graphical]*",
2278 "propagate=[bool]",
2279 "direction=[{forwards,backwards}]"
2280 };
2281
2282 /* Instance Variables */
2283
2284 static vardecl var_device[] =
2285 { IV(NAME_level, "int", IV_GET,
2286 NAME_organisation, "Nesting depth to topmost device"),
2287 IV(NAME_offset, "point", IV_NONE,
2288 NAME_area, "Offset of origin"),
2289 IV(NAME_clipArea, "area*", IV_NONE,
2290 NAME_scroll, "Clip all graphicals to this area"),
2291 IV(NAME_graphicals, "chain", IV_GET,
2292 NAME_organisation, "Displayed graphicals (members)"),
2293 IV(NAME_pointed, "chain", IV_GET,
2294 NAME_event, "Graphicals pointed-to by the mouse"),
2295 SV(NAME_layoutManager, "layout_manager*", IV_GET|IV_STORE, layoutManagerDevice,
2296 NAME_layout, "Layout manager for <-graphicals"),
2297 IV(NAME_format, "format*", IV_GET,
2298 NAME_layout, "Use tabular layout"),
2299 IV(NAME_badFormat, "bool", IV_NONE,
2300 NAME_update, "Format needs to be recomputed"),
2301 IV(NAME_badBoundingBox, "bool", IV_NONE,
2302 NAME_update, "Indicate bounding box is out-of-date"),
2303 IV(NAME_recompute, "chain", IV_NONE,
2304 NAME_update, "Graphicals that requested a recompute")
2305 };
2306
2307 /* Send Methods */
2308
2309 static senddecl send_device[] =
2310 { SM(NAME_geometry, 4, T_geometry, geometryDevice,
2311 DEFAULT, "Move device"),
2312 SM(NAME_initialise, 0, NULL, initialiseDevice,
2313 DEFAULT, "Create an empty device"),
2314 SM(NAME_unlink, 0, NULL, unlinkDevice,
2315 DEFAULT, "Clear device and unlink from super-device"),
2316 SM(NAME_typed, 2, T_typed, typedDevice,
2317 NAME_accelerator, "Handle accelerators"),
2318 SM(NAME_foreground, 1, "colour", colourGraphical,
2319 NAME_appearance, "Default colour for all members"),
2320 SM(NAME_move, 1, "point", positionDevice,
2321 NAME_area, "Set origin"),
2322 SM(NAME_position, 1, "point", positionDevice,
2323 NAME_area, "Set origin"),
2324 SM(NAME_reference, 1, "[point]", referenceDevice,
2325 NAME_area, "Move origin, while moving members opposite"),
2326 SM(NAME_resize, 3, T_resize, resizeDevice,
2327 NAME_area, "Resize device with specified factor"),
2328 SM(NAME_x, 1, "int", xDevice,
2329 NAME_area, "Set X of origin"),
2330 SM(NAME_y, 1, "int", yDevice,
2331 NAME_area, "Set Y of origin"),
2332 SM(NAME_convertLoadedObject, 2, T_convertLoadedObject,
2333 convertLoadedObjectDevice,
2334 NAME_compatibility, "Initialise recompute and request_compute"),
2335 SM(NAME_event, 1, "event", eventDevice,
2336 NAME_event, "Process an event"),
2337 SM(NAME_updatePointed, 1, "event", updatePointedDevice,
2338 NAME_event, "Update <-pointed, sending area_enter and area_exit events"),
2339 SM(NAME_advance, 3, T_advance, advanceDevice,
2340 NAME_focus, "Advance keyboard focus to next item"),
2341 SM(NAME_flash, 2, T_flash, flashDevice,
2342 NAME_report, "Alert visual by temporary inverting"),
2343 SM(NAME_forAll, 2, T_DnameD_code, forAllDevice,
2344 NAME_iterate, "Run code on graphicals; demand acceptance"),
2345 SM(NAME_forSome, 2, T_DnameD_code, forSomeDevice,
2346 NAME_iterate, "Run code on all graphicals"),
2347 SM(NAME_format, 2, T_format, formatDevice,
2348 NAME_layout, "Use tabular layout"),
2349 SM(NAME_layoutDialog, 3, T_layout, layoutDialogDevice,
2350 NAME_layout, "(Re)compute layout of dialog_items"),
2351 SM(NAME_room, 1, "area", roomDevice,
2352 NAME_layout, "Test if no graphicals are in area"),
2353 SM(NAME_appendDialogItem, 2, T_appendDialogItem, appendDialogItemDevice,
2354 NAME_organisation, "Append dialog_item {below,right,next_row} last"),
2355 SM(NAME_clear, 1, "[{destroy,free,erase}]", clearDevice,
2356 NAME_organisation, "Erase all graphicals"),
2357 SM(NAME_display, 2, T_display, displayDevice,
2358 NAME_organisation, "Display graphical at point"),
2359 SM(NAME_erase, 1, "graphical", eraseDevice,
2360 NAME_organisation, "Erase a graphical"),
2361 SM(NAME_reparent, 0, NULL, reparentDevice,
2362 NAME_organisation, "Device's parent-chain has changed"),
2363 SM(NAME_DrawPostScript, 1, "{head,body}", drawPostScriptDevice,
2364 NAME_postscript, "Create PostScript"),
2365 SM(NAME_changedUnion, 4, T_changedUnion, changedUnionDevice,
2366 NAME_resize, "Trap changes to the union of all graphicals"),
2367 SM(NAME_selection, 1, "graphical|chain*", selectionDevice,
2368 NAME_selection, "Set selection to graphical or chain"),
2369 SM(NAME_compute, 0, NULL, computeDevice,
2370 NAME_update, "Recompute device"),
2371 SM(NAME_modifiedItem, 2, T_modifiedItem, failObject,
2372 NAME_virtual, "Trap modification of item (fail)")
2373 };
2374
2375 /* Get Methods */
2376
2377 static getdecl get_device[] =
2378 { GM(NAME_contains, 0, "chain", NULL, getContainsDevice,
2379 DEFAULT, "Chain with visuals contained"),
2380 GM(NAME_offset, 0, "point", NULL, getOffsetDevice,
2381 NAME_area, "Get origin (also <-position)"),
2382 GM(NAME_position, 0, "point", NULL, getPositionDevice,
2383 NAME_area, "Get origin"),
2384 GM(NAME_x, 0, "int", NULL, getXDevice,
2385 NAME_area, "Get X of origin"),
2386 GM(NAME_y, 0, "int", NULL, getYDevice,
2387 NAME_area, "Get Y of origin"),
2388 GM(NAME_pointedObjects, 2, "chain", T_pointedObjects,
2389 getPointedObjectsDevice,
2390 NAME_event, "New chain holding graphicals at point|event"),
2391 GM(NAME_defaultButton, 0, "button", NULL, getDefaultButtonDevice,
2392 NAME_accelerator, "Current Button connected to `RET'"),
2393 GM(NAME_catchAll, 1, "graphical", "name", getCatchAllDevice,
2394 NAME_organisation, "Find named graphicals"),
2395 GM(NAME_member, 1, "graphical", "graphical_name=name", getMemberDevice,
2396 NAME_organisation, "Find named graphical"),
2397 GM(NAME_find, 2, "graphical", T_find, getFindDevice,
2398 NAME_search, "Find most local graphical"),
2399 GM(NAME_displayedCursor, 0, "cursor*", NULL, getDisplayedCursorDevice,
2400 NAME_cursor, "Currently displayed cursor"),
2401 GM(NAME_inside, 1, "chain", "area", getInsideDevice,
2402 NAME_selection, "New chain with graphicals inside area"),
2403 GM(NAME_selection, 0, "chain", NULL, getSelectionDevice,
2404 NAME_selection, "New chain of selected graphicals")
2405 };
2406
2407 /* Resources */
2408
2409 #define rc_device NULL
2410 /*
2411 static classvardecl rc_device[] =
2412 {
2413 };
2414 */
2415
2416 /* Class Declaration */
2417
2418 ClassDecl(device_decls,
2419 var_device, send_device, get_device, rc_device,
2420 0, NULL,
2421 "$Rev$");
2422
2423
2424 status
makeClassDevice(Class class)2425 makeClassDevice(Class class)
2426 { declareClass(class, &device_decls);
2427 setRedrawFunctionClass(class, RedrawAreaDevice);
2428
2429 succeed;
2430 }
2431