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