1 
2 /******************************************************************************
3 * MODULE     : boxes.cpp
4 * DESCRIPTION: Important routines for all boxes
5 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "boxes.hpp"
13 #include "formatter.hpp"
14 #include "point.hpp"
15 #include "printer.hpp"
16 #include "file.hpp"
17 #include "merge_sort.hpp"
18 
19 /******************************************************************************
20 * Default settings for virtual routines
21 ******************************************************************************/
22 
subnr()23 int box_rep::subnr () { return 0; }
subbox(int i)24 box box_rep::subbox (int i) { (void) i; return box (); }
operator [](path p)25 box box::operator [] (path p) {
26   if (is_nil (p)) return *this; else return rep->subbox(p->item)[p->next]; }
left_slope()27 double box_rep::left_slope () { return 0.0; }
right_slope()28 double box_rep::right_slope () { return 0.0; }
left_correction()29 SI box_rep::left_correction () { return (SI) (-min (0, y1) * left_slope ()); }
right_correction()30 SI box_rep::right_correction () { return (SI) (max (0, y2) * right_slope ()); }
lsub_correction()31 SI box_rep::lsub_correction () { return 0; }
lsup_correction()32 SI box_rep::lsup_correction () { return 0; }
rsub_correction()33 SI box_rep::rsub_correction () { return 0; }
rsup_correction()34 SI box_rep::rsup_correction () { return 0; }
sub_lo_base(int level)35 SI box_rep::sub_lo_base (int level) { (void) level; return y1; }
sub_hi_lim(int level)36 SI box_rep::sub_hi_lim  (int level) { (void) level; return y1 + ((y2-y1)/3); }
sup_lo_lim(int level)37 SI box_rep::sup_lo_lim  (int level) { (void) level; return (y1 + y2) >> 1; }
sup_lo_base(int level)38 SI box_rep::sup_lo_base (int level) { (void) level; return y2 - ((y2-y1)/3); }
sup_hi_lim(int level)39 SI box_rep::sup_hi_lim  (int level) { (void) level; return y2; }
get_bracket_extents(SI & lo,SI & hi)40 void box_rep::get_bracket_extents (SI& lo, SI& hi) { lo= y1; hi= y2; }
41 
42 /******************************************************************************
43 * Positioning routines
44 ******************************************************************************/
45 
46 bool
outside(SI x,SI delta,SI x1,SI x2)47 outside (SI x, SI delta, SI x1, SI x2) {
48   return
49     (x<x1) || ((x==x1) && (delta<0)) ||
50     (x>x2) || ((x==x2) && (delta>=0));
51 }
52 
53 SI
get_delta(SI x,SI x1,SI x2)54 get_delta (SI x, SI x1, SI x2) {
55   if (x1==x2) return 0;
56   if (x==x1) return -1;
57   if (x==x2) return 1;
58   return 0;
59 }
60 
61 SI
distance(int i,SI x,SI y,SI delta)62 box_rep::distance (int i, SI x, SI y, SI delta) {
63   box b= subbox (i);
64   x -= sx(i);
65   y -= sy(i);
66   int dx, dy;
67   if (x <=  b->x1) dx = b->x1- x- (delta<0? 1:0);
68   else if (x >=  b->x2) dx = x- b->x2+ (delta<0? 0:1);
69   else dx = 0;
70   if (y <  b->y1) dy = b->y1- y;
71   else if (y >= b->y2) dy = y- b->y2;
72   else dy = 0;
73   return dx+dy;
74 }
75 
76 bool
in_rectangle(SI X1,SI Y1,SI X2,SI Y2)77 box_rep::in_rectangle (SI X1, SI Y1, SI X2, SI Y2) {
78   return x1>=X1 && y1>=Y1 && x2<=X2 && y2<=Y2;
79 }
80 
81 bool
contains_rectangle(SI X1,SI Y1,SI X2,SI Y2)82 box_rep::contains_rectangle (SI X1, SI Y1, SI X2, SI Y2) {
83   return x1<=X1 && y1<=Y1 && x2>=X2 && y2>=Y2;
84 }
85 
86 box
adjust_kerning(int mode,double factor)87 box_rep::adjust_kerning (int mode, double factor) {
88   return this;
89 }
90 
91 void
get_cell_extents(SI & l,SI & r)92 box_rep::get_cell_extents (SI& l, SI& r) {
93   (void) l; (void) r;
94   FAILED ("cell box expected");
95 }
96 
97 box
adjust_cell_geometry(SI dx,SI dl,SI dr)98 box_rep::adjust_cell_geometry (SI dx, SI dl, SI dr) {
99   (void) dx; (void) dl; (void) dr;
100   FAILED ("cell box expected");
101   return this;
102 }
103 
104 /******************************************************************************
105 * Cursor routines
106 ******************************************************************************/
107 
108 path
find_box_path(SI x,SI y,SI delta,bool force,bool & found)109 box_rep::find_box_path (SI x, SI y, SI delta, bool force, bool& found) {
110   (void) y;
111   (void) force;
112   found= true;
113   SI m= (x1+x2)>>1;
114   return path (((x<m) || ((x==m) && (delta<0)))? 0: 1);
115 }
116 
117 path
find_lip()118 box_rep::find_lip () {
119   return descend (ip, 0);
120 }
121 
122 path
find_rip()123 box_rep::find_rip () {
124   return descend (ip, 1);
125 }
126 
127 path
find_left_box_path()128 box_rep::find_left_box_path () {
129   return path (0);
130 }
131 
132 path
find_right_box_path()133 box_rep::find_right_box_path () {
134   return path (1);
135 }
136 
137 path
find_box_path(path p,bool & found)138 box_rep::find_box_path (path p, bool& found) {
139   // cout << "Find box path " << box (this) << ", " << p
140   //      << "; " << reverse (ip)
141   //      << ", " << reverse (find_lip ())
142   //      << " -- " << reverse (find_rip ()) << "\n";
143   found= (!is_nil(p)) && is_accessible (ip);
144   if (last_item (p) == 0) return path (0);
145   else return path (1);
146 }
147 
148 path
find_tree_path(path bp)149 box_rep::find_tree_path (path bp) {
150   if (bp == path (0)) return reverse (descend_decode (ip, 0));
151   else return reverse (descend_decode (ip, 1));
152 }
153 
154 cursor
find_cursor(path bp)155 box_rep::find_cursor (path bp) {
156   bool flag= bp == path (0);
157   double slope= flag? left_slope (): right_slope ();
158   cursor cu (flag? x1: x2, 0);
159   cu->y1= y1; cu->y2= y2;
160   cu->slope= slope;
161   return cu;
162 }
163 
164 selection
find_selection(path lbp,path rbp)165 box_rep::find_selection (path lbp, path rbp) {
166   if (lbp == rbp)
167     return selection (rectangles (),
168 		      find_tree_path (lbp), find_tree_path (rbp));
169   else
170     return selection (rectangle (x1, y1, x2, y2),
171 		      find_tree_path (path (0)), find_tree_path (path (1)));
172 }
173 
174 path
find_tree_path(SI x,SI y,SI delta)175 box_rep::find_tree_path (SI x, SI y, SI delta) {
176   bool found;
177   path bp= find_box_path (x, y, delta, false, found);
178   //cout << "Find " << x << ", " << y << "; " << delta;
179   //cout << " -> " << bp << "\n";
180   return find_tree_path (bp);
181 }
182 
183 cursor
find_check_cursor(path p)184 box_rep::find_check_cursor (path p) {
185   bool found;
186   path bp= find_box_path (p, found);
187   cursor cu= find_cursor (bp);
188   cu->valid= found;
189   return cu;
190 }
191 
192 selection
find_check_selection(path lp,path rp)193 box_rep::find_check_selection (path lp, path rp) {
194   bool lfound= false, rfound= false;
195   path lbp= find_box_path (lp, lfound);
196   path rbp= find_box_path (rp, rfound);
197   selection sel= find_selection (lbp, rbp);
198   sel->valid= lfound && rfound;
199   return sel;
200 }
201 
202 void
relocate(path new_ip,bool force)203 box_rep::relocate (path new_ip, bool force) {
204   if (!force)
205     if (is_nil (ip) || (ip->item >= 0) || (ip == new_ip)) return;
206   ip= new_ip;
207   int i, n= subnr ();
208   for (i=0; i<n; i++) subbox (i)->relocate (ip, force);
209 }
210 
211 box
transform(frame fr)212 box_rep::transform (frame fr) {
213   (void) fr;
214   return box ();
215 }
216 
217 /******************************************************************************
218 * Modified cursor routines in presence of scrolled boxes
219 ******************************************************************************/
220 
221 path
find_innermost_scroll(box b,path p)222 find_innermost_scroll (box b, path p) {
223   // Given a box b and a logical path p, this routine returns
224   // the longest box path sp such that b[sp] is a scroll node
225   path bp;
226   while (true) {
227     bool found= false;
228     bp= b->find_box_path (p, found);
229     if (found) break;
230     p= path_up (p);
231     if (is_nil (p)) return path ();
232   }
233   bp= path_up (bp);
234   path cp, sp;
235   while (!is_nil (bp)) {
236     if (b->get_type () == SCROLL_BOX) sp= reverse (cp);
237     b = b[bp->item];
238     cp= path (bp->item, cp);
239     bp= bp->next;
240   }
241   if (is_nil (sp)) return sp;
242   else return sp * 0;
243 }
244 
245 path
find_scrolled_box_path(box b,path sp,SI x,SI y,SI delta)246 find_scrolled_box_path (box b, path sp, SI x, SI y, SI delta) {
247   if (is_nil (sp)) {
248     bool found;
249     return b->find_box_path (x, y, delta, false, found);
250   }
251   else {
252     int m= sp->item;
253     SI xx= x - b->sx (m), yy= y - b->sy (m);
254     SI dd= delta + get_delta (xx, b[m]->x1, b[m]->x2);
255     return path (m, find_scrolled_box_path (b[m], sp->next, xx, yy, dd));
256   }
257 }
258 
259 /*
260 void
261 debug (box b, path bp) {
262   tree t= (tree) b;
263   if (is_tuple (t) && N(t) > 0) cout << t[0];
264   else cout << t;
265   cout << ", " << bp << "\n";
266   if (!is_nil (bp))
267     debug (b[bp->item], bp->next);
268 }
269 */
270 
271 path
find_scrolled_tree_path(box b,path sp,SI x,SI y,SI delta)272 find_scrolled_tree_path (box b, path sp, SI x, SI y, SI delta) {
273   path bp= find_scrolled_box_path (b, sp, x, y, delta);
274   //cout << "Find " << x << ", " << y << "; " << delta;
275   //cout << " -> " << bp << "\n";
276   return b->find_tree_path (bp);
277 }
278 
279 void
find_canvas_info(box b,path sp,SI & x,SI & y,SI & sx,SI & sy,rectangle & outer,rectangle & inner)280 find_canvas_info (box b, path sp, SI& x, SI& y, SI& sx, SI& sy,
281 		  rectangle& outer, rectangle& inner)
282 {
283   if (is_nil (sp)) {
284     x= y= sx= sy= 0;
285     outer= inner= rectangle (0, 0, 0, 0);
286   }
287   else if (is_atom (sp)) {
288     x    = 0;
289     y    = 0;
290     sx   = b->sx (0);
291     sy   = b->sy (0);
292     outer= rectangle (b->x1, b->y1, b->x2, b->y2);
293     inner= rectangle (b[0]->x1, b[0]->y1, b[0]->x2, b[0]->y2);
294   }
295   else {
296     find_canvas_info (b[sp->item], sp->next, x, y, sx, sy, outer, inner);
297     x += b->sx (sp->item);
298     y += b->sy (sp->item);
299   }
300 }
301 
302 /******************************************************************************
303 * For graphical boxes
304 ******************************************************************************/
305 
306 frame
get_frame()307 box_rep::get_frame () {
308   return frame ();
309 }
310 
311 grid
get_grid()312 box_rep::get_grid () {
313   return grid ();
314 }
315 
316 void
get_limits(point & lim1,point & lim2)317 box_rep::get_limits (point& lim1, point& lim2) {
318   lim1= point (); lim2= point ();
319 }
320 
321 frame
find_frame(path bp,bool last)322 box_rep::find_frame (path bp, bool last) {
323   SI    x= 0;
324   SI    y= 0;
325   box   b= this;
326   frame f= get_frame ();
327   while (!is_nil (bp)) {
328     x += b->sx (bp->item);
329     y += b->sy (bp->item);
330     b  = b->subbox (bp->item);
331     bp = bp->next;
332     frame g= b->get_frame ();
333     if (!is_nil (g)) {
334       if (last)
335 	f= g;
336       else
337 	f= scaling (1.0, point (x, y)) * g;
338     }
339   }
340   return f;
341 }
342 
343 grid
find_grid(path bp)344 box_rep::find_grid (path bp) {
345   box  b= this;
346   grid g= get_grid ();
347   while (!is_nil (bp)) {
348     b  = b->subbox (bp->item);
349     bp = bp->next;
350     grid g2= b->get_grid ();
351     if (!is_nil (g2)) g= g2;
352   }
353   return g;
354 }
355 
356 void
find_limits(path bp,point & lim1,point & lim2)357 box_rep::find_limits (path bp, point& lim1, point& lim2) {
358   box b= this;
359   get_limits (lim1, lim2);
360   while (!is_nil (bp)) {
361     point slim1, slim2;
362     b  = b->subbox (bp->item);
363     bp = bp->next;
364     b->get_limits (slim1, slim2);
365     if (slim1 != point ()) {
366       lim1= slim1;
367       lim2= slim2;
368     }
369   }
370 }
371 
372 SI
graphical_distance(SI x,SI y)373 box_rep::graphical_distance (SI x, SI y) {
374   SI dx, dy;
375   if (x <=  x1) dx= x1 - x;
376   else if (x >=  x2) dx= x - x2;
377   else dx= 0;
378   if (y <  y1) dy= y1 - y;
379   else if (y >= y2) dy= y - y2;
380   else dy= 0;
381   return (SI) norm (point (dx, dy));
382 }
383 
384 gr_selections
graphical_select(SI x,SI y,SI dist)385 box_rep::graphical_select (SI x, SI y, SI dist) {
386   gr_selections res;
387   if (graphical_distance (x, y) <= dist) {
388     gr_selection gs;
389     gs->type= "box";
390     gs->dist= graphical_distance (x, y);
391     gs->cp << find_tree_path (x, y, dist);
392     // FIXME: check whether this is correct: I do not remember whether
393     // find_tree_path returns an absolute or a relative path
394     gs->c= curve ();
395     res << gs;
396   }
397   return res;
398 }
399 
400 gr_selections
graphical_select(SI x1,SI y1,SI x2,SI y2)401 box_rep::graphical_select (SI x1, SI y1, SI x2, SI y2) {
402   gr_selections res;
403   if (in_rectangle (x1, y1, x2, y2)) {
404     gr_selection gs;
405     gs->type= "box";
406     gs->dist= graphical_distance (x1, y1);
407     SI dist= (SI)norm (point (x2-x1, y2-y1));
408     gs->cp << find_tree_path (x1, y1, dist);
409     // FIXME: as above, check whether this is correct or not
410     gs->pts= array<point> (0);
411     gs->c= curve ();
412     res << gs;
413   }
414   return res;
415 }
416 
417 /******************************************************************************
418 * Getting information from boxes
419 ******************************************************************************/
420 
421 int
get_type()422 box_rep::get_type () {
423   return STD_BOX;
424 }
425 
426 tree
get_info(tree in)427 box_rep::get_info (tree in) {
428   (void) in;
429   return "";
430 }
431 
432 int
get_leaf_left_pos()433 box_rep::get_leaf_left_pos () {
434   failed_error << "The box is " << box (this) << "\n";
435   FAILED ("this box is not textual");
436   return 0;
437 }
438 
439 int
get_leaf_right_pos()440 box_rep::get_leaf_right_pos () {
441   failed_error << "The box is " << box (this) << "\n";
442   FAILED ("this box is not textual");
443   return 0;
444 }
445 
446 string
get_leaf_string()447 box_rep::get_leaf_string () {
448   failed_error << "The box is " << box (this) << "\n";
449   FAILED ("this box is not textual");
450   return "";
451 }
452 
453 font
get_leaf_font()454 box_rep::get_leaf_font () {
455   failed_error << "The box is " << box (this) << "\n";
456   FAILED ("this box is not textual");
457   return font ();
458 }
459 
460 pencil
get_leaf_pencil()461 box_rep::get_leaf_pencil () {
462   failed_error << "The box is " << box (this) << "\n";
463   FAILED ("this box is not textual");
464   return pencil (false);
465 }
466 
467 language
get_leaf_language()468 box_rep::get_leaf_language () {
469   failed_error << "The box is " << box (this) << "\n";
470   FAILED ("this box is not textual");
471   return language ();
472 }
473 
474 tree
get_leaf_tree()475 box_rep::get_leaf_tree () {
476   failed_error << "The box is " << box (this) << "\n";
477   FAILED ("no tree attached to this box");
478   return "";
479 }
480 
481 box
get_leaf_box()482 box_rep::get_leaf_box () {
483   failed_error << "The box is " << box (this) << "\n";
484   FAILED ("no box attached to this box");
485   return box ();
486 }
487 
488 lazy
get_leaf_lazy()489 box_rep::get_leaf_lazy () {
490   failed_error << "The box is " << box (this) << "\n";
491   FAILED ("no lazy attached to this box");
492   return lazy ();
493 }
494 
495 SI
get_leaf_offset(string search)496 box_rep::get_leaf_offset (string search) {
497   (void) search;
498   return w();
499 }
500 
501 /******************************************************************************
502 * Redrawing boxes
503 ******************************************************************************/
504 
505 int nr_painted= 0;
506 
507 void
clear_pattern_rectangles(renderer ren,rectangles l)508 clear_pattern_rectangles (renderer ren, rectangles l) {
509   while (!is_nil (l)) {
510     rectangle r (l->item);
511     ren->clear_pattern (r->x1- ren->ox, r->y1- ren->oy,
512 			r->x2- ren->ox, r->y2- ren->oy);
513     l= l->next;
514   }
515 }
516 
517 int
reindex(int i,int item,int n)518 box_rep::reindex (int i, int item, int n) {
519   if (item<0) item=0;
520   if (item>n) item=n;
521   if (i==0) return item;
522   if ((i <= (item<<1)) && (i <= ((n-item)<<1))) {
523     int d=(i+1)>>1;
524     if (((i+1)&1)==0) return item-d;
525     else return item+d;
526   }
527   if (i > (item<<1)) return i;
528   return n-i;
529 }
530 
531 void
redraw(renderer ren,path p,rectangles & l)532 box_rep::redraw (renderer ren, path p, rectangles& l) {
533   if ((nr_painted&15) == 15 && ren->is_screen && gui_interrupted (true)) return;
534   ren->move_origin (x0, y0);
535   SI delta= ren->pixel; // adjust visibility to compensate truncation
536   if (ren->is_visible (x3- delta, y3- delta, x4+ delta, y4+ delta)) {
537     rectangles ll;
538     l= rectangles();
539     pre_display (ren);
540 
541     int i, item=-1, n=subnr (), i1= n, i2= -1;
542     if (!is_nil(p)) i1= i2= item= p->item;
543     for (i=0; i<n; i++) {
544       int k= reindex (i, item, n-1);
545       if (is_nil(p)) subbox (k)->redraw (ren, path (), ll);
546       else if (i!=0) {
547         if (k > item) subbox(k)->redraw (ren, path (0), ll);
548         else subbox(k)->redraw (ren, path (subbox(k)->subnr()-1), ll);
549       }
550       else subbox(k)->redraw (ren, p->next, ll);
551       if (!is_nil(ll)) {
552         i1= min (i1, k);
553         i2= max (i2, k);
554         l = ll * l;
555         ll= rectangles ();
556       }
557     }
558 
559     if ((nr_painted&15) == 15 && ren->is_screen && gui_interrupted ()) {
560       l= translate (l, -ren->ox, -ren->oy);
561       clear_incomplete (l, ren->pixel, item, i1, i2);
562       l= translate (l, ren->ox, ren->oy);
563     }
564     else {
565       l= rectangle (x3+ ren->ox, y3+ ren->oy, x4+ ren->ox, y4+ ren->oy);
566       display (ren);
567       if (nr_painted < 15) ren->apply_shadow (x1, y1, x2, y2);
568       nr_painted++;
569     }
570 
571     post_display (ren);
572   }
573   ren->move_origin (-x0, -y0);
574 }
575 
576 void
redraw(renderer ren,path p,rectangles & l,SI x,SI y)577 box_rep::redraw (renderer ren, path p, rectangles& l, SI x, SI y) {
578   ren->move_origin (x, y);
579   redraw (ren, p, l);
580   ren->move_origin (-x, -y);
581 }
582 
583 void
clear_incomplete(rectangles & rs,SI pixel,int i,int i1,int i2)584 box_rep::clear_incomplete (rectangles& rs, SI pixel, int i, int i1, int i2) {
585   (void) rs; (void) pixel; (void) i; (void) i1; (void) i2;
586 }
587 
588 void
pre_display(renderer & ren)589 box_rep::pre_display (renderer &ren) {
590   (void) ren;
591 }
592 
593 void
post_display(renderer & ren)594 box_rep::post_display (renderer &ren) {
595   (void) ren;
596 }
597 
598 /******************************************************************************
599 * The cursor class
600 ******************************************************************************/
601 
cursor(SI x,SI y,SI delta,SI y1,SI y2,double slope,bool valid)602 cursor::cursor (SI x, SI y, SI delta, SI y1, SI y2, double slope, bool valid):
603   rep (tm_new<cursor_rep> ())
604 {
605   rep->ox= x ; rep->oy= y ; rep->delta= delta;
606   rep->y1= y1; rep->y2= y2; rep->slope= slope;
607   rep->valid= valid;
608 }
609 
610 cursor
copy(cursor cu)611 copy (cursor cu) {
612   return cursor (cu->ox, cu->oy, cu->delta, cu->y1, cu->y2,
613 		 cu->slope, cu->valid);
614 }
615 
616 bool
operator ==(cursor cu1,cursor cu2)617 operator == (cursor cu1, cursor cu2) {
618   return
619     (cu1->ox == cu2->ox) && (cu1->oy == cu2->oy) &&
620     // (cu1->delta == cu2->delta) &&
621     (cu1->y1 == cu2->y1) && (cu1->y2 == cu2->y2) &&
622     (cu1->slope == cu2->slope);
623 }
624 
625 bool
operator !=(cursor cu1,cursor cu2)626 operator != (cursor cu1, cursor cu2) {
627   return ! (cu1 == cu2);
628 }
629 
630 tm_ostream&
operator <<(tm_ostream & out,cursor cu)631 operator << (tm_ostream& out, cursor cu) {
632   out << "cursor (" << (cu->ox>>8) << ", " << (cu->oy>>8) << ": "
633       << cu->delta << ": "
634       << (cu->y1>>8) << ", " << (cu->y2>>8) << ": "
635       << cu->slope << ")";
636   return out;
637 }
638 
639 /******************************************************************************
640 * Selections
641 ******************************************************************************/
642 
selection(rectangles rs,path start,path end,bool valid)643 selection::selection (rectangles rs, path start, path end, bool valid):
644   rep (tm_new<selection_rep> ())
645 {
646   rep->rs   = rs;
647   rep->start= start;
648   rep->end  = end;
649   rep->valid= valid;
650 }
651 
652 bool
operator ==(selection sel1,selection sel2)653 operator == (selection sel1, selection sel2) {
654   return
655     (sel1->start == sel2->start) &&
656     (sel1->end == sel2->end);
657 }
658 
659 bool
operator !=(selection sel1,selection sel2)660 operator != (selection sel1, selection sel2) {
661   return !(sel1 == sel2);
662 }
663 
664 tm_ostream&
operator <<(tm_ostream & out,selection sel)665 operator << (tm_ostream& out, selection sel) {
666   return out << "selection (" << sel->start << ", " << sel->end << ")";
667 }
668 
669 /******************************************************************************
670 * Graphical selections
671 ******************************************************************************/
672 
gr_selection(array<path> cp,SI dist)673 gr_selection::gr_selection (array<path> cp, SI dist):
674   rep (tm_new<gr_selection_rep> ())
675 {
676   rep->cp  = cp;
677   rep->dist= dist;
678 }
679 
680 tm_ostream&
operator <<(tm_ostream & out,gr_selection sel)681 operator << (tm_ostream& out, gr_selection sel) {
682   return out << "gr_selection (" << sel->type << ", "
683 	     << sel->dist << ", " << sel->cp << ")";
684 }
685 
686 struct less_eq_gr_selection {
leqless_eq_gr_selection687   static inline bool leq (gr_selection& a, gr_selection& b) {
688     return a->dist <= b->dist; }
689 };
690 
691 void
sort(gr_selections & sels)692 sort (gr_selections& sels) {
693   merge_sort_leq <gr_selection, less_eq_gr_selection> (sels);
694 }
695 
696 tree
as_tree(gr_selections sels)697 as_tree (gr_selections sels) {
698   sort (sels);
699   int i, n= N(sels);
700   array<array<path> > res (n);
701   for (i=0; i<n; i++)
702     res[i]= sels[i]->cp;
703   return (tree) res;
704 }
705 
706 
707 /******************************************************************************
708 * Animations
709 ******************************************************************************/
710 
711 int
anim_length()712 box_rep::anim_length () {
713   int i, n= subnr (), len=0;
714   for (i=0; i<n; i++) {
715     int slen= subbox (i)->anim_length ();
716     if (slen == -1) return -1;
717     if (slen > len) len= slen;
718   }
719   return len;
720 }
721 
722 bool
anim_started()723 box_rep::anim_started () {
724   int i, n= subnr ();
725   for (i=0; i<n; i++)
726     if (!subbox (i)->anim_started ()) return false;
727   return true;
728 }
729 
730 bool
anim_finished()731 box_rep::anim_finished () {
732   int i, n= subnr ();
733   for (i=0; i<n; i++)
734     if (!subbox (i)->anim_finished ()) return false;
735   return true;
736 }
737 
738 void
anim_start_at(time_t at)739 box_rep::anim_start_at (time_t at) {
740   int i, n= subnr ();
741   for (i=0; i<n; i++)
742     subbox (i)->anim_start_at (at);
743 }
744 
745 void
anim_finish_now()746 box_rep::anim_finish_now () {
747   int i, n= subnr ();
748   for (i=0; i<n; i++)
749     subbox (i)->anim_finish_now ();
750 }
751 
752 time_t
anim_next_update()753 box_rep::anim_next_update () {
754   FAILED ("invalid situation");
755   return texmacs_time ();
756 }
757 
758 void
anim_check_invalid(bool & flag,time_t & at,rectangles & rs)759 box_rep::anim_check_invalid (bool& flag, time_t& at, rectangles& rs) {
760   time_t now= texmacs_time ();
761   time_t finish_at= anim_next_update ();
762   if (finish_at - now < 0) finish_at= now;
763   if (flag && at - now < 0) at= now;
764   if (!flag || finish_at - (at - 3) < 0) {
765     flag= true;
766     at  = finish_at;
767     rs  = rectangle (x1, y1, x2, y2);
768   }
769   else if (finish_at - (at + 3) <= 0) {
770     rs << rectangle (x1, y1, x2, y2);
771     if (finish_at - at < 0)
772       at= finish_at;
773   }
774 }
775 
776 void
anim_get_invalid(bool & flag,time_t & at,rectangles & rs)777 box_rep::anim_get_invalid (bool& flag, time_t& at, rectangles& rs) {
778   int i, n= subnr ();
779   for (i=0; i<n; i++) {
780     bool   flag2= false;
781     time_t at2= at;
782     rectangles rs2;
783     subbox (i)->anim_get_invalid (flag2, at2, rs2);
784     if (flag2) {
785       rs2= translate (rs2, sx (i), sy (i));
786       if (at2 - (at-3) < 0) rs= rs2;
787       else rs << rs2;
788       flag= true;
789       if (at2 - at < 0) at= at2;
790     }
791   }
792 }
793 
794 /******************************************************************************
795 * Miscellaneous routines
796 ******************************************************************************/
797 
798 tree
action(tree t,SI x,SI y,SI delta)799 box_rep::action (tree t, SI x, SI y, SI delta) {
800   (void) x; (void) y; (void) delta; (void) t;
801   return "";
802 }
803 
804 void
loci(SI x,SI y,SI delta,list<string> & ids,rectangles & rs)805 box_rep::loci (SI x, SI y, SI delta, list<string>& ids, rectangles& rs) {
806   (void) x; (void) y; (void) delta;
807   ids= list<string> ();
808   rs = rectangles ();
809 }
810 
811 void
position_at(SI x,SI y,rectangles & change_log)812 box_rep::position_at (SI x, SI y, rectangles& change_log) {
813   int i, n= subnr ();
814   x += x0; y += y0;
815   for (i=0; i<n; i++) subbox (i)->position_at (x, y, change_log);
816 }
817 
818 void
collect_page_numbers(hashmap<string,tree> & h,tree page)819 box_rep::collect_page_numbers (hashmap<string,tree>& h, tree page) {
820   (void) h; (void) page;
821 }
822 
823 path
find_tag(string name)824 box_rep::find_tag (string name) {
825   (void) name;
826   return path ();
827 }
828 
operator ==(box b2)829 bool box::operator == (box b2) { return rep==b2.rep; }
operator !=(box b2)830 bool box::operator != (box b2) { return rep!=b2.rep; }
831 
operator tree()832 box::operator tree () { return tree (*rep); }
operator <<(tm_ostream & out,box b)833 tm_ostream& operator << (tm_ostream& out, box b) { return out << ((tree) b); }
834 
835 path
descend_decode(path ip,int side)836 descend_decode (path ip, int side) {
837   if (is_nil (ip)) return descend (ip, side);
838   else switch (ip->item) {
839   case DECORATION       : return ip->next;
840   case DECORATION_LEFT  : return descend (ip->next, 0);
841   case DECORATION_MIDDLE: return descend (ip->next, side);
842   case DECORATION_RIGHT : return descend (ip->next, 1);
843   default               : return descend (ip, side);
844   }
845 }
846 
847 tree
attach_dip(tree ref,path dip)848 attach_dip (tree ref, path dip) {
849   path old_ip= obtain_ip (ref);
850   if (old_ip != path (DETACHED)) return ref;
851   if (is_atomic (ref)) {
852     tree r (ref->label);
853     r->obs= list_observer (ip_observer (dip), r->obs);
854     return r;
855   }
856   else {
857     int i, n= N(ref);
858     tree r (ref, n);
859     for (i=0; i<n; i++)
860       r[i]= attach_dip (ref[i], descend (dip, i));
861     r->obs= list_observer (ip_observer (dip), r->obs);
862     return r;
863   }
864 }
865 
866 /******************************************************************************
867 * Convert to postscript
868 ******************************************************************************/
869 
870 void
make_eps(url name,box b,int dpi=600)871 make_eps (url name, box b, int dpi=600) {
872   double inch= ((double) dpi * PIXEL);
873   double cm  = inch / 2.54;
874   SI w= b->x4 - b->x3;
875   SI h= b->y4 - b->y3;
876   b->x0= -b->x3;
877   b->y0= -b->y4;
878   renderer ren= printer (name, dpi, 1, "user", false, w/cm, h/cm);
879   ren->set_background (white);
880   ren->set_pencil (black);
881   rectangles rs;
882   b->redraw (ren, path (0), rs);
883   tm_delete (ren);
884 }
885