Back to index

texmacs  1.0.7.15
boxes.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : boxes.cpp
00004 * DESCRIPTION: Important routines for all boxes
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "boxes.hpp"
00013 #include "formatter.hpp"
00014 #include "Graphics/point.hpp"
00015 #include "printer.hpp"
00016 #include "file.hpp"
00017 #include "merge_sort.hpp"
00018 
00019 /******************************************************************************
00020 * Default settings for virtual routines
00021 ******************************************************************************/
00022 
00023 int box_rep::subnr () { return 0; }
00024 box box_rep::subbox (int i) { (void) i; return box (); }
00025 box box::operator [] (path p) {
00026   if (is_nil (p)) return *this; else return rep->subbox(p->item)[p->next]; }
00027 double box_rep::left_slope () { return 0.0; }
00028 double box_rep::right_slope () { return 0.0; }
00029 SI box_rep::left_correction () { return (SI) (-min (0, y1) * left_slope ()); }
00030 SI box_rep::right_correction () { return (SI) (max (0, y2) * right_slope ()); }
00031 SI box_rep::lsub_correction () { return 0; }
00032 SI box_rep::lsup_correction () { return 0; }
00033 SI box_rep::rsub_correction () { return 0; }
00034 SI box_rep::rsup_correction () { return 0; }
00035 SI box_rep::sub_lo_base (int level) { (void) level; return y1; }
00036 SI box_rep::sub_hi_lim  (int level) { (void) level; return y1 + ((y2-y1)/3); }
00037 SI box_rep::sup_lo_lim  (int level) { (void) level; return (y1 + y2) >> 1; }
00038 SI box_rep::sup_lo_base (int level) { (void) level; return y2 - ((y2-y1)/3); }
00039 SI box_rep::sup_hi_lim  (int level) { (void) level; return y2; }
00040 
00041 /******************************************************************************
00042 * Positioning routines
00043 ******************************************************************************/
00044 
00045 bool
00046 outside (SI x, SI delta, SI x1, SI x2) {
00047   return
00048     (x<x1) || ((x==x1) && (delta<0)) ||
00049     (x>x2) || ((x==x2) && (delta>=0));
00050 }
00051 
00052 SI
00053 get_delta (SI x, SI x1, SI x2) {
00054   if (x1==x2) return 0;
00055   if (x==x1) return -1;
00056   if (x==x2) return 1;
00057   return 0;
00058 }
00059 
00060 SI
00061 box_rep::distance (int i, SI x, SI y, SI delta) {
00062   box b= subbox (i);
00063   x -= sx(i);
00064   y -= sy(i);
00065   int dx, dy;
00066   if (x <=  b->x1) dx = b->x1- x- (delta<0? 1:0);
00067   else if (x >=  b->x2) dx = x- b->x2+ (delta<0? 0:1);
00068   else dx = 0;
00069   if (y <  b->y1) dy = b->y1- y;
00070   else if (y >= b->y2) dy = y- b->y2;
00071   else dy = 0;
00072   return dx+dy;
00073 }
00074 
00075 bool
00076 box_rep::in_rectangle (SI X1, SI Y1, SI X2, SI Y2) {
00077   return x1>=X1 && y1>=Y1 && x2<=X2 && y2<=Y2;
00078 }
00079 
00080 bool
00081 box_rep::contains_rectangle (SI X1, SI Y1, SI X2, SI Y2) {
00082   return x1<=X1 && y1<=Y1 && x2>=X2 && y2>=Y2;
00083 }
00084 
00085 /******************************************************************************
00086 * Cursor routines
00087 ******************************************************************************/
00088 
00089 path
00090 box_rep::find_box_path (SI x, SI y, SI delta, bool force) {
00091   (void) y;
00092   (void) force;
00093   SI m= (x1+x2)>>1;
00094   return path (((x<m) || ((x==m) && (delta<0)))? 0: 1);
00095 }
00096 
00097 path
00098 box_rep::find_lip () {
00099   return descend (ip, 0);
00100 }
00101 
00102 path
00103 box_rep::find_rip () {
00104   return descend (ip, 1);
00105 }
00106 
00107 path
00108 box_rep::find_left_box_path () {
00109   return path (0);
00110 }
00111 
00112 path
00113 box_rep::find_right_box_path () {
00114   return path (1);
00115 }
00116 
00117 path
00118 box_rep::find_box_path (path p, bool& found) {
00119   // cout << "Find box path " << box (this) << ", " << p
00120   //      << "; " << reverse (ip)
00121   //      << ", " << reverse (find_lip ())
00122   //      << " -- " << reverse (find_rip ()) << "\n";
00123   found= (!is_nil(p)) && is_accessible (ip);
00124   if (last_item (p) == 0) return path (0);
00125   else return path (1);
00126 }
00127 
00128 path
00129 box_rep::find_tree_path (path bp) {
00130   if (bp == path (0)) return reverse (descend_decode (ip, 0));
00131   else return reverse (descend_decode (ip, 1));
00132 }
00133 
00134 cursor
00135 box_rep::find_cursor (path bp) {
00136   bool flag= bp == path (0);
00137   double slope= flag? left_slope (): right_slope ();
00138   cursor cu (flag? x1: x2, 0);
00139   cu->y1= y1; cu->y2= y2;
00140   cu->slope= slope;
00141   return cu;
00142 }
00143 
00144 selection
00145 box_rep::find_selection (path lbp, path rbp) {
00146   if (lbp == rbp)
00147     return selection (rectangles (),
00148                     find_tree_path (lbp), find_tree_path (rbp));
00149   else
00150     return selection (rectangle (x1, y1, x2, y2),
00151                     find_tree_path (path (0)), find_tree_path (path (1)));
00152 }
00153 
00154 path
00155 box_rep::find_tree_path (SI x, SI y, SI delta) {
00156   path bp= find_box_path (x, y, delta, false);
00157   //cout << "Find " << x << ", " << y << "; " << delta;
00158   //cout << " -> " << bp << "\n";
00159   return find_tree_path (bp);
00160 }
00161 
00162 cursor
00163 box_rep::find_check_cursor (path p) {
00164   bool found;
00165   path bp= find_box_path (p, found);
00166   cursor cu= find_cursor (bp);
00167   cu->valid= found;
00168   return cu;
00169 }
00170 
00171 selection
00172 box_rep::find_check_selection (path lp, path rp) {
00173   bool lfound= false, rfound= false;
00174   path lbp= find_box_path (lp, lfound);
00175   path rbp= find_box_path (rp, rfound);
00176   selection sel= find_selection (lbp, rbp);
00177   sel->valid= lfound && rfound;
00178   return sel;
00179 }
00180 
00181 void
00182 box_rep::relocate (path new_ip, bool force) {
00183   if (!force)
00184     if (is_nil (ip) || (ip->item >= 0) || (ip == new_ip)) return;
00185   ip= new_ip;
00186   int i, n= subnr ();
00187   for (i=0; i<n; i++) subbox (i)->relocate (ip, force);
00188 }
00189 
00190 box
00191 box_rep::transform (frame fr) {
00192   (void) fr;
00193   return box ();
00194 }
00195 
00196 /******************************************************************************
00197 * Modified cursor routines in presence of scrolled boxes
00198 ******************************************************************************/
00199 
00200 path
00201 find_innermost_scroll (box b, path p) {
00202   // Given a box b and a logical path p, this routine returns 
00203   // the longest box path sp such that b[sp] is a scroll node
00204   path bp;
00205   while (true) {
00206     bool found= false;
00207     bp= b->find_box_path (p, found);
00208     if (found) break;
00209     p= path_up (p);
00210     if (is_nil (p)) return path ();
00211   }
00212   bp= path_up (bp);
00213   path cp, sp;
00214   while (!is_nil (bp)) {
00215     if (b->get_type () == SCROLL_BOX) sp= reverse (cp);
00216     b = b[bp->item];
00217     cp= path (bp->item, cp);
00218     bp= bp->next;
00219   }
00220   if (is_nil (sp)) return sp;
00221   else return sp * 0;
00222 }
00223 
00224 path
00225 find_scrolled_box_path (box b, path sp, SI x, SI y, SI delta) {
00226   if (is_nil (sp)) return b->find_box_path (x, y, delta, false);
00227   else {
00228     int m= sp->item;
00229     SI xx= x - b->sx (m), yy= y - b->sy (m);
00230     SI dd= delta + get_delta (xx, b[m]->x1, b[m]->x2);
00231     return path (m, find_scrolled_box_path (b[m], sp->next, xx, yy, dd));
00232   }
00233 }
00234 
00235 path
00236 find_scrolled_tree_path (box b, path sp, SI x, SI y, SI delta) {
00237   path bp= find_scrolled_box_path (b, sp, x, y, delta);
00238   //cout << "Find " << x << ", " << y << "; " << delta;
00239   //cout << " -> " << bp << "\n";
00240   return b->find_tree_path (bp);
00241 }
00242 
00243 void
00244 find_canvas_info (box b, path sp, SI& x, SI& y, SI& sx, SI& sy,
00245                 rectangle& outer, rectangle& inner)
00246 {
00247   if (is_nil (sp)) {
00248     x= y= sx= sy= 0;
00249     outer= inner= rectangle (0, 0, 0, 0);
00250   }
00251   else if (is_atom (sp)) {
00252     x    = 0;
00253     y    = 0;
00254     sx   = b->sx (0);
00255     sy   = b->sy (0);
00256     outer= rectangle (b->x1, b->y1, b->x2, b->y2);
00257     inner= rectangle (b[0]->x1, b[0]->y1, b[0]->x2, b[0]->y2);
00258   }
00259   else {
00260     find_canvas_info (b[sp->item], sp->next, x, y, sx, sy, outer, inner);
00261     x += b->sx (sp->item);
00262     y += b->sy (sp->item);
00263   }
00264 }
00265 
00266 /******************************************************************************
00267 * For graphical boxes
00268 ******************************************************************************/
00269 
00270 frame
00271 box_rep::get_frame () {
00272   return frame ();
00273 }
00274 
00275 grid
00276 box_rep::get_grid () {
00277   return grid ();
00278 }
00279 
00280 void
00281 box_rep::get_limits (point& lim1, point& lim2) {
00282   lim1= point (); lim2= point ();
00283 }
00284 
00285 frame
00286 box_rep::find_frame (path bp, bool last) {
00287   SI    x= 0;
00288   SI    y= 0;
00289   box   b= this;
00290   frame f= get_frame ();
00291   while (!is_nil (bp)) {
00292     x += b->sx (bp->item);
00293     y += b->sy (bp->item);
00294     b  = b->subbox (bp->item);
00295     bp = bp->next;
00296     frame g= b->get_frame ();
00297     if (!is_nil (g)) {
00298       if (last)
00299        f= g;
00300       else
00301        f= scaling (1.0, point (x, y)) * g;
00302     }
00303   }
00304   return f;
00305 }
00306 
00307 grid
00308 box_rep::find_grid (path bp) {
00309   box   b= this;
00310   grid g= get_grid ();
00311   while (!is_nil (bp)) {
00312     b  = b->subbox (bp->item);
00313     bp = bp->next;
00314     grid g2= b->get_grid ();
00315     if (!is_nil (g2)) g= g2;
00316   }
00317   return g;
00318 }
00319 
00320 void
00321 box_rep::find_limits (path bp, point& lim1, point& lim2) {
00322   box b= this;
00323   get_limits (lim1, lim2);
00324   while (!is_nil (bp)) {
00325     point slim1, slim2;
00326     b  = b->subbox (bp->item);
00327     bp = bp->next;
00328     b->get_limits (slim1, slim2);
00329     if (slim1 != point ()) {
00330       lim1= slim1;
00331       lim2= slim2;
00332     }
00333   }
00334 }
00335 
00336 SI
00337 box_rep::graphical_distance (SI x, SI y) {
00338   SI dx, dy;
00339   if (x <=  x1) dx= x1 - x;
00340   else if (x >=  x2) dx= x - x2;
00341   else dx= 0;
00342   if (y <  y1) dy= y1 - y;
00343   else if (y >= y2) dy= y - y2;
00344   else dy= 0;
00345   return (SI) norm (point (dx, dy));
00346 }
00347 
00348 gr_selections
00349 box_rep::graphical_select (SI x, SI y, SI dist) {
00350   gr_selections res;
00351   if (graphical_distance (x, y) <= dist) {
00352     gr_selection gs;
00353     gs->type= "box";
00354     gs->dist= graphical_distance (x, y);
00355     gs->cp << find_tree_path (x, y, dist);
00356     // FIXME: check whether this is correct: I do not remember whether
00357     // find_tree_path returns an absolute or a relative path
00358     gs->c= curve ();
00359     res << gs;
00360   }
00361   return res;
00362 }
00363 
00364 gr_selections
00365 box_rep::graphical_select (SI x1, SI y1, SI x2, SI y2) {
00366   gr_selections res;
00367   if (in_rectangle (x1, y1, x2, y2)) {
00368     gr_selection gs;
00369     gs->type= "box";
00370     gs->dist= graphical_distance (x1, y1);
00371     SI dist= (SI)norm (point (x2-x1, y2-y1));
00372     gs->cp << find_tree_path (x1, y1, dist);
00373     // FIXME: as above, check whether this is correct or not
00374     gs->pts= array<point> (0);
00375     gs->c= curve ();
00376     res << gs;
00377   }
00378   return res;
00379 }
00380 
00381 /******************************************************************************
00382 * Getting information from boxes
00383 ******************************************************************************/
00384 
00385 int
00386 box_rep::get_type () {
00387   return STD_BOX;
00388 }
00389 
00390 tree
00391 box_rep::get_info (tree in) {
00392   (void) in;
00393   return "";
00394 }
00395 
00396 int
00397 box_rep::get_leaf_left_pos () {
00398   cerr << "\nTeXmacs] the box is " << box (this) << "\n";
00399   FAILED ("this box is not textual");
00400   return 0;
00401 }
00402 
00403 int
00404 box_rep::get_leaf_right_pos () {
00405   cerr << "\nTeXmacs] the box is " << box (this) << "\n";
00406   FAILED ("this box is not textual");
00407   return 0;
00408 }
00409 
00410 string
00411 box_rep::get_leaf_string () {
00412   cerr << "\nTeXmacs] the box is " << box (this) << "\n";
00413   FAILED ("this box is not textual");
00414   return "";
00415 }
00416 
00417 font
00418 box_rep::get_leaf_font () {
00419   cerr << "\nTeXmacs] the box is " << box (this) << "\n";
00420   FAILED ("this box is not textual");
00421   return font ();
00422 }
00423 
00424 color
00425 box_rep::get_leaf_color () {
00426   cerr << "\nTeXmacs] the box is " << box (this) << "\n";
00427   FAILED ("this box is not textual");
00428   return 0;
00429 }
00430 
00431 language
00432 box_rep::get_leaf_language () {
00433   cerr << "\nTeXmacs] the box is " << box (this) << "\n";
00434   FAILED ("this box is not textual");
00435   return language ();
00436 }
00437 
00438 tree
00439 box_rep::get_leaf_tree () {
00440   cerr << "\nTeXmacs] the box is " << box (this) << "\n";
00441   FAILED ("no tree attached to this box");
00442   return "";
00443 }
00444 
00445 lazy
00446 box_rep::get_leaf_lazy () {
00447   cerr << "\nTeXmacs] the box is " << box (this) << "\n";
00448   FAILED ("no lazy attached to this box");
00449   return lazy ();
00450 }
00451 
00452 SI
00453 box_rep::get_leaf_offset (string search) {
00454   (void) search;
00455   return w();
00456 }
00457 
00458 /******************************************************************************
00459 * Redrawing boxes
00460 ******************************************************************************/
00461 
00462 int nr_painted= 0;
00463 
00464 void
00465 clear_pattern_rectangles (renderer ren, rectangles l) {
00466   while (!is_nil (l)) {
00467     rectangle r (l->item);
00468     ren->clear_pattern (r->x1- ren->ox, r->y1- ren->oy,
00469                      r->x2- ren->ox, r->y2- ren->oy);
00470     l= l->next;
00471   }
00472 }
00473 
00474 int
00475 box_rep::reindex (int i, int item, int n) {
00476   if (item<0) item=0;
00477   if (item>n) item=n;
00478   if (i==0) return item;
00479   if ((i <= (item<<1)) && (i <= ((n-item)<<1))) {
00480     int d=(i+1)>>1;
00481     if (((i+1)&1)==0) return item-d;
00482     else return item+d;
00483   }
00484   if (i > (item<<1)) return i;
00485   return n-i;
00486 }
00487 
00488 void
00489 box_rep::redraw (renderer ren, path p, rectangles& l) {
00490   if (((nr_painted&15) == 15) && ren->interrupted (true)) return;
00491   ren->move_origin (x0, y0);
00492   SI delta= ren->pixel; // adjust visibility to compensate truncation
00493   if (ren->is_visible (x3- delta, y3- delta, x4+ delta, y4+ delta)) {
00494     rectangles ll;
00495     l= rectangles();
00496     pre_display (ren);
00497 
00498     int i, item=-1, n=subnr (), i1= n, i2= -1;
00499     if (!is_nil(p)) i1= i2= item= p->item;
00500     for (i=0; i<n; i++) {
00501       int k= reindex (i, item, n-1);
00502       if (is_nil(p)) subbox (k)->redraw (ren, path (), ll);
00503       else if (i!=0) {
00504        if (k > item) subbox(k)->redraw (ren, path (0), ll);
00505        else subbox(k)->redraw (ren, path (subbox(k)->subnr()-1), ll);
00506       }
00507       else subbox(k)->redraw (ren, p->next, ll);
00508       if (!is_nil(ll)) {
00509        i1= min (i1, k);
00510        i2= max (i2, k);
00511        l = ll * l;
00512        ll= rectangles ();
00513       }
00514     }
00515 
00516     if (((nr_painted&15) == 15) && ren->interrupted ()) {
00517       l= translate (l, -ren->ox, -ren->oy);
00518       clear_incomplete (l, ren->pixel, item, i1, i2);
00519       l= translate (l, ren->ox, ren->oy);
00520     }
00521     else {
00522       l= rectangle (x3+ ren->ox, y3+ ren->oy, x4+ ren->ox, y4+ ren->oy);
00523       display (ren);
00524       if (nr_painted < 15) ren->apply_shadow (x1, y1, x2, y2);
00525       nr_painted++;
00526     }
00527 
00528     post_display (ren);
00529   }
00530   ren->move_origin (-x0, -y0);
00531 }
00532 
00533 void
00534 box_rep::redraw (renderer ren, path p, rectangles& l, SI x, SI y) {
00535   ren->move_origin (x, y);
00536   redraw (ren, p, l);
00537   ren->move_origin (-x, -y);
00538 }
00539 
00540 void
00541 box_rep::clear_incomplete (rectangles& rs, SI pixel, int i, int i1, int i2) {
00542   (void) rs; (void) pixel; (void) i; (void) i1; (void) i2;
00543 }
00544 
00545 void
00546 box_rep::pre_display (renderer &ren) {
00547   (void) ren;
00548 }
00549 
00550 void
00551 box_rep::post_display (renderer &ren) {
00552   (void) ren;
00553 }
00554 
00555 /******************************************************************************
00556 * The cursor class
00557 ******************************************************************************/
00558 
00559 cursor::cursor (SI x, SI y, SI delta, SI y1, SI y2, double slope, bool valid):
00560   rep (tm_new<cursor_rep> ())
00561 {
00562   rep->ox= x ; rep->oy= y ; rep->delta= delta;
00563   rep->y1= y1; rep->y2= y2; rep->slope= slope;
00564   rep->valid= valid;
00565 }
00566 
00567 cursor
00568 copy (cursor cu) {
00569   return cursor (cu->ox, cu->oy, cu->delta, cu->y1, cu->y2,
00570                cu->slope, cu->valid);
00571 }
00572 
00573 bool
00574 operator == (cursor cu1, cursor cu2) {
00575   return
00576     (cu1->ox == cu2->ox) && (cu1->oy == cu2->oy) &&
00577     // (cu1->delta == cu2->delta) &&
00578     (cu1->y1 == cu2->y1) && (cu1->y2 == cu2->y2) &&
00579     (cu1->slope == cu2->slope);
00580 }
00581 
00582 bool
00583 operator != (cursor cu1, cursor cu2) {
00584   return ! (cu1 == cu2);
00585 }
00586 
00587 tm_ostream&
00588 operator << (tm_ostream& out, cursor cu) {
00589   out << "cursor (" << (cu->ox>>8) << ", " << (cu->oy>>8) << ": "
00590       << cu->delta << ": "
00591       << (cu->y1>>8) << ", " << (cu->y2>>8) << ": "
00592       << cu->slope << ")";
00593   return out;
00594 }
00595 
00596 /******************************************************************************
00597 * Selections
00598 ******************************************************************************/
00599 
00600 selection::selection (rectangles rs, path start, path end, bool valid):
00601   rep (tm_new<selection_rep> ())
00602 {
00603   rep->rs   = rs;
00604   rep->start= start;
00605   rep->end  = end;
00606   rep->valid= valid;
00607 }
00608 
00609 bool
00610 operator == (selection sel1, selection sel2) {
00611   return
00612     (sel1->start == sel2->start) &&
00613     (sel1->end == sel2->end);
00614 }
00615 
00616 bool
00617 operator != (selection sel1, selection sel2) {
00618   return !(sel1 == sel2);
00619 }
00620 
00621 tm_ostream&
00622 operator << (tm_ostream& out, selection sel) {
00623   return out << "selection (" << sel->start << ", " << sel->end << ")";
00624 }
00625 
00626 /******************************************************************************
00627 * Graphical selections
00628 ******************************************************************************/
00629 
00630 gr_selection::gr_selection (array<path> cp, SI dist):
00631   rep (tm_new<gr_selection_rep> ())
00632 {
00633   rep->cp  = cp;
00634   rep->dist= dist;
00635 }
00636 
00637 tm_ostream&
00638 operator << (tm_ostream& out, gr_selection sel) {
00639   return out << "gr_selection (" << sel->type << ", "
00640             << sel->dist << ", " << sel->cp << ")";
00641 }
00642 
00643 struct less_eq_gr_selection {
00644   static inline bool leq (gr_selection& a, gr_selection& b) {
00645     return a->dist <= b->dist; }
00646 };
00647 
00648 void
00649 sort (gr_selections& sels) {
00650   merge_sort_leq <gr_selection, less_eq_gr_selection> (sels);
00651 }
00652 
00653 tree
00654 as_tree (gr_selections sels) {
00655   sort (sels);
00656   int i, n= N(sels);
00657   array<array<path> > res (n);
00658   for (i=0; i<n; i++)
00659     res[i]= sels[i]->cp;
00660   return (tree) res;
00661 }
00662 
00663 
00664 /******************************************************************************
00665 * Animations
00666 ******************************************************************************/
00667 
00668 int
00669 box_rep::anim_length () {
00670   int i, n= subnr (), len=0;
00671   for (i=0; i<n; i++) {
00672     int slen= subbox (i)->anim_length ();
00673     if (slen == -1) return -1;
00674     if (slen > len) len= slen;
00675   }
00676   return len;
00677 }
00678 
00679 bool
00680 box_rep::anim_started () {
00681   int i, n= subnr ();
00682   for (i=0; i<n; i++)
00683     if (!subbox (i)->anim_started ()) return false;
00684   return true;
00685 }
00686 
00687 bool
00688 box_rep::anim_finished () {
00689   int i, n= subnr ();
00690   for (i=0; i<n; i++)
00691     if (!subbox (i)->anim_finished ()) return false;
00692   return true;
00693 }
00694 
00695 void
00696 box_rep::anim_start_at (time_t at) {
00697   int i, n= subnr ();
00698   for (i=0; i<n; i++)
00699     subbox (i)->anim_start_at (at);
00700 }
00701 
00702 void
00703 box_rep::anim_finish_now () {
00704   int i, n= subnr ();
00705   for (i=0; i<n; i++)
00706     subbox (i)->anim_finish_now ();
00707 }
00708 
00709 time_t
00710 box_rep::anim_next_update () {
00711   FAILED ("invalid situation");
00712   return texmacs_time ();
00713 }
00714 
00715 void
00716 box_rep::anim_check_invalid (bool& flag, time_t& at, rectangles& rs) {
00717   time_t now= texmacs_time ();
00718   time_t finish_at= anim_next_update ();
00719   if (finish_at - now < 0) finish_at= now;
00720   if (flag && at - now < 0) at= now;
00721   if (!flag || finish_at - (at - 3) < 0) {
00722     flag= true;
00723     at  = finish_at;
00724     rs  = rectangle (x1, y1, x2, y2);
00725   }
00726   else if (finish_at - (at + 3) <= 0) {
00727     rs << rectangle (x1, y1, x2, y2);
00728     if (finish_at - at < 0)
00729       at= finish_at;
00730   }
00731 }
00732 
00733 void
00734 box_rep::anim_get_invalid (bool& flag, time_t& at, rectangles& rs) {
00735   int i, n= subnr ();
00736   for (i=0; i<n; i++) {
00737     bool   flag2= false;
00738     time_t at2= at;
00739     rectangles rs2;
00740     subbox (i)->anim_get_invalid (flag2, at2, rs2);
00741     if (flag2) {
00742       rs2= translate (rs2, sx (i), sy (i));
00743       if (at2 - (at-3) < 0) rs= rs2;
00744       else rs << rs2;
00745       flag= true;
00746       if (at2 - at < 0) at= at2;
00747     }
00748   }
00749 }
00750 
00751 /******************************************************************************
00752 * Miscellaneous routines
00753 ******************************************************************************/
00754 
00755 tree
00756 box_rep::action (tree t, SI x, SI y, SI delta) {
00757   (void) x; (void) y; (void) delta; (void) t;
00758   return "";
00759 }
00760 
00761 void
00762 box_rep::loci (SI x, SI y, SI delta, list<string>& ids, rectangles& rs) {
00763   (void) x; (void) y; (void) delta;  
00764   ids= list<string> ();
00765   rs = rectangles ();
00766 }
00767 
00768 void
00769 box_rep::position_at (SI x, SI y, rectangles& change_log) {
00770   int i, n= subnr ();
00771   x += x0; y += y0;
00772   for (i=0; i<n; i++) subbox (i)->position_at (x, y, change_log);
00773 }
00774 
00775 void
00776 box_rep::collect_page_numbers (hashmap<string,tree>& h, tree page) {
00777   (void) h; (void) page;
00778 }
00779 
00780 path
00781 box_rep::find_tag (string name) {
00782   (void) name;
00783   return path ();
00784 }
00785 
00786 bool box::operator == (box b2) { return rep==b2.rep; }
00787 bool box::operator != (box b2) { return rep!=b2.rep; }
00788 
00789 box::operator tree () { return tree (*rep); }
00790 tm_ostream& operator << (tm_ostream& out, box b) { return out << ((tree) b); }
00791 
00792 path
00793 descend_decode (path ip, int side) {
00794   if (is_nil (ip)) return descend (ip, side);
00795   else switch (ip->item) {
00796   case DECORATION       : return ip->next;
00797   case DECORATION_LEFT  : return descend (ip->next, 0);
00798   case DECORATION_MIDDLE: return descend (ip->next, side);
00799   case DECORATION_RIGHT : return descend (ip->next, 1);
00800   default               : return descend (ip, side);
00801   }
00802 }
00803 
00804 tree
00805 attach_dip (tree ref, path dip) {
00806   path old_ip= obtain_ip (ref);
00807   if (old_ip != path (DETACHED)) return ref;
00808   if (is_atomic (ref)) {
00809     tree r (ref->label);
00810     r->obs= list_observer (ip_observer (dip), r->obs);
00811     return r;
00812   }
00813   else {
00814     int i, n= N(ref);
00815     tree r (ref, n);
00816     for (i=0; i<n; i++)
00817       r[i]= attach_dip (ref[i], descend (dip, i));
00818     r->obs= list_observer (ip_observer (dip), r->obs);
00819     return r;
00820   }
00821 }
00822 
00823 /******************************************************************************
00824 * Convert to postscript
00825 ******************************************************************************/
00826 
00827 void
00828 make_eps (url name, box b, int dpi) {
00829   double inch= ((double) dpi * PIXEL);
00830   double cm  = inch / 2.54;
00831   SI w= b->x4 - b->x3;
00832   SI h= b->y4 - b->y3;
00833   b->x0= -b->x3;
00834   b->y0= -b->y4;
00835   renderer ren= printer (name, dpi, 1, "user", false, w/cm, h/cm);
00836   ren->set_color (black);
00837   ren->set_background (white);
00838   rectangles rs;
00839   b->redraw (ren, path (0), rs);
00840   tm_delete (ren);
00841 }