1 
2 /******************************************************************************
3 * MODULE     : typeset.cpp
4 * DESCRIPTION: typeset the tree being edited
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 <climits>
13 #include "edit_typeset.hpp"
14 #include "tm_buffer.hpp"
15 #include "convert.hpp"
16 #include "file.hpp"
17 #include "analyze.hpp"
18 #include "timer.hpp"
19 #include "Bridge/impl_typesetter.hpp"
20 #include "new_style.hpp"
21 #include "iterator.hpp"
22 #include "merge_sort.hpp"
23 #ifdef EXPERIMENTAL
24 #include "../../Style/Environment/std_environment.hpp"
25 #endif // EXPERIMENTAL
26 
27 //box empty_box (path ip, int x1=0, int y1=0, int x2=0, int y2=0);
28 bool enable_fastenv= false;
29 
30 /******************************************************************************
31 * Contructors, destructors and notification of modifications
32 ******************************************************************************/
33 
edit_typeset_rep()34 edit_typeset_rep::edit_typeset_rep ():
35   the_style (TUPLE),
36   cur (hashmap<string,tree> (UNINIT)),
37   stydef (UNINIT), pre (UNINIT), init (UNINIT), fin (UNINIT),
38   env (drd, buf->buf->master,
39        buf->data->ref, (buf->prj==NULL? buf->data->ref: buf->prj->data->ref),
40        buf->data->aux, (buf->prj==NULL? buf->data->aux: buf->prj->data->aux),
41        buf->data->att, (buf->prj==NULL? buf->data->att: buf->prj->data->att)),
42   ttt (new_typesetter (env, subtree (et, rp), reverse (rp))) {}
~edit_typeset_rep()43 edit_typeset_rep::~edit_typeset_rep () { delete_typesetter (ttt); }
44 
45 void
set_data(new_data data)46 edit_typeset_rep::set_data (new_data data) {
47   set_style (data->style);
48   set_init  (data->init);
49   set_fin   (data->fin);
50   set_att   (data->att);
51   notify_page_change ();
52   add_init (data->init);
53   notify_change (THE_DECORATIONS);
54   typeset_invalidate_env ();
55   iterator<string> it = iterate (data->att);
56   while (it->busy()) {
57     string key= it->next ();
58     (void) call (string ("notify-set-attachment"),
59                  buf->buf->name, key, data->att [key]);
60   }
61 }
62 
63 void
get_data(new_data & data)64 edit_typeset_rep::get_data (new_data& data) {
65   data->style= get_style ();
66   data->init = get_init ();
67   data->fin  = get_fin ();
68   data->att  = get_att ();
69 }
70 
get_typesetter()71 typesetter edit_typeset_rep::get_typesetter () { return ttt; }
get_style()72 tree edit_typeset_rep::get_style () { return the_style; }
set_style(tree t)73 void edit_typeset_rep::set_style (tree t) { the_style= copy (t); }
get_init()74 hashmap<string,tree> edit_typeset_rep::get_init () { return init; }
get_fin()75 hashmap<string,tree> edit_typeset_rep::get_fin () { return fin; }
get_att()76 hashmap<string,tree> edit_typeset_rep::get_att () { return buf->data->att; }
set_fin(hashmap<string,tree> H)77 void edit_typeset_rep::set_fin (hashmap<string,tree> H) { fin= H; }
set_att(hashmap<string,tree> H)78 void edit_typeset_rep::set_att (hashmap<string,tree> H) { buf->data->att= H; }
79 
80 tree
get_att(string key)81 edit_typeset_rep::get_att (string key) {
82   return buf->data->att[key];
83 }
84 
85 void
set_att(string key,tree im)86 edit_typeset_rep::set_att (string key, tree im) {
87   buf->data->att (key)= im;
88 }
89 
90 void
reset_att(string key)91 edit_typeset_rep::reset_att (string key) {
92   buf->data->att->reset (key);
93 }
94 
95 array<string>
list_atts()96 edit_typeset_rep::list_atts () {
97   tree a= (tree) buf->data->att;
98   array<string> v;
99   int i, n= N(a);
100   for (i=0; i<n; i++)
101     v << a[i][0]->label;
102   merge_sort (v);
103   return v;
104 }
105 
106 void
set_init(hashmap<string,tree> H)107 edit_typeset_rep::set_init (hashmap<string,tree> H) {
108   init= hashmap<string,tree> (UNINIT);
109   add_init (H);
110 }
111 
112 void
add_init(hashmap<string,tree> H)113 edit_typeset_rep::add_init (hashmap<string,tree> H) {
114   init->join (H);
115   ::notify_assign (ttt, path(), subtree (et, rp));
116   notify_change (THE_ENVIRONMENT);
117 }
118 
119 void
clear_local_info()120 edit_typeset_rep::clear_local_info () {
121   buf->data->ref= hashmap<string,tree> ();
122   buf->data->aux= hashmap<string,tree> ();
123 }
124 
125 /******************************************************************************
126 * Miscellaneous routines for lengths arithmetic (should be elsewhere)
127 ******************************************************************************/
128 
129 SI
as_length(string l)130 edit_typeset_rep::as_length (string l) {
131   return env->as_length (l); }
132 
133 string
add_lengths(string l1,string l2)134 edit_typeset_rep::add_lengths (string l1, string l2) {
135   return env->add_lengths (l1, l2); }
136 
137 string
multiply_length(double x,string l)138 edit_typeset_rep::multiply_length (double x, string l) {
139   return env->multiply_length (x, l); }
140 
141 bool
is_length(string s)142 edit_typeset_rep::is_length (string s) {
143   return env->is_length (s); }
144 
145 double
divide_lengths(string l1,string l2)146 edit_typeset_rep::divide_lengths (string l1, string l2) {
147   return env->divide_lengths (l1, l2); }
148 
149 /******************************************************************************
150 * Processing preamble
151 ******************************************************************************/
152 
153 void
use_modules(tree t)154 use_modules (tree t) {
155   if (is_tuple (t))
156     for (int i=0; i<N(t); i++) {
157       string s= as_string (t[i]);
158       if (starts (s, "(")) eval ("(use-modules " * s * ")");
159       else if (s != "") eval ("(plugin-initialize '" * s * ")");
160     }
161 }
162 
163 void
typeset_style_use_cache(tree style)164 edit_typeset_rep::typeset_style_use_cache (tree style) {
165   style= preprocess_style (style, buf->buf->master);
166   //cout << "Typesetting style using cache " << style << LF;
167   bool ok;
168   hashmap<string,tree> H;
169   tree t;
170   style_get_cache (style, H, t, ok);
171   if (ok) {
172     env->patch_env (H);
173     ok= drd->set_locals (t);
174     drd->set_environment (H);
175   }
176   if (!ok) {
177     //cout << "Typeset without cache " << style << LF;
178     if (!is_tuple (style)) FAILED ("tuple expected as style");
179     H= get_style_env (style);
180     drd= get_style_drd (style);
181     style_set_cache (style, H, drd->get_locals ());
182     env->patch_env (H);
183     drd->set_environment (H);
184   }
185   use_modules (env->read (THE_MODULES));
186 }
187 
188 void
typeset_preamble()189 edit_typeset_rep::typeset_preamble () {
190   env->write_default_env ();
191   typeset_style_use_cache (the_style);
192   env->update ();
193   env->read_env (stydef);
194   env->patch_env (init);
195   env->update ();
196   env->read_env (pre);
197   drd->heuristic_init (pre);
198 }
199 
200 void
typeset_prepare()201 edit_typeset_rep::typeset_prepare () {
202   env->base_file_name= buf->buf->master;
203   env->read_only= buf->buf->read_only;
204   env->write_default_env ();
205   env->patch_env (pre);
206   env->style_init_env ();
207   env->update ();
208 }
209 
210 void
drd_update()211 edit_typeset_rep::drd_update () {
212   typeset_exec_until (tp);
213   drd->heuristic_init (cur[tp]);
214 }
215 
216 #ifdef EXPERIMENTAL
217 void
environment_update()218 edit_typeset_rep::environment_update () {
219   hashmap<string,tree> h;
220   typeset_prepare ();
221   env->assign ("base-file-name", as_string (env->base_file_name));
222   env->assign ("cur-file-name", as_string (env->cur_file_name));
223   env->assign ("secure", bool_as_tree (env->secure));
224   env->read_env (h);
225   ::primitive (ste, h);
226 }
227 #endif
228 
229 /******************************************************************************
230 * Routines for getting information
231 ******************************************************************************/
232 
233 void
typeset_invalidate_env()234 edit_typeset_rep::typeset_invalidate_env () {
235   cur= hashmap<path,hashmap<string,tree> > (hashmap<string,tree> (UNINIT));
236 }
237 
238 static void
restricted_exec(edit_env env,tree t,int end)239 restricted_exec (edit_env env, tree t, int end) {
240   if (is_func (t, ASSIGN, 2) && end == 2)
241     env->exec (t);
242   else if (is_document (t) || is_concat (t))
243     for (int i=0; i < min (end, 10); i++)
244       restricted_exec (env, t[i], arity (t[i]));
245   else if (is_compound (t, "hide-preamble", 1) ||
246            is_compound (t, "show-preamble", 1))
247     env->exec (t[0]);
248 }
249 
250 static tree
filter_format(tree fm,int i,int n)251 filter_format (tree fm, int i, int n) {
252   array<tree> r;
253   for (int k=0; k<N(fm); k++)
254     if (is_func (fm[k], CWITH) && N(fm[k]) >= 4 &&
255         is_int (fm[k][0]) && is_int (fm[k][1])) {
256       int j1= as_int (fm[k][0]->label);
257       int j2= as_int (fm[k][1]->label);
258       if (j1 > 0) j1--; else j1 += n;
259       if (j2 > 0) j2--; else j2 += n;
260       if (i >= j1 && i <= j2) r << fm[k] (2, N(fm[k]));
261     }
262   return tree (TFORMAT, r);
263 }
264 
265 static void
table_descend(tree & t,path & p,tree & fm)266 table_descend (tree& t, path& p, tree& fm) {
267   while (!is_nil (p)) {
268     if (L(t) == TFORMAT && p->item == N(t) - 1) {
269       array<tree> r;
270       for (int k=0; k<N(t)-1; k++)
271         if (is_func (t[k], CWITH, 6) &&
272             is_atomic (t[k][4]) &&
273             !starts (t[k][4]->label, "cell-"))
274           r << t[k];
275       fm= tree (TFORMAT, r);
276       t= t[N(t)-1];
277       p= p->next;
278     }
279     else if ((L(t) == TABLE || L(t) == ROW) &&
280              p->item >= 0 && p->item < N(t)) {
281       fm= filter_format (fm, p->item, N(t));
282       t= t[p->item];
283       p= p->next;
284     }
285     else break;
286   }
287 }
288 
289 void
typeset_exec_until(path p)290 edit_typeset_rep::typeset_exec_until (path p) {
291   //time_t t1= texmacs_time ();
292   if (has_changed (THE_TREE + THE_ENVIRONMENT))
293     if (p != correct_cursor (et, rp * 0)) {
294       if (DEBUG_STD) std_warning << "resynchronizing for path " << p << "\n";
295       // apply_changes ();
296     }
297   if (p == tp && inside_graphics (true) && p != closest_inside (et, p)) {
298     //cout << "TeXmacs] Warning: corrected cursor\n";
299     tp= closest_inside (et, tp);
300     p = tp;
301   }
302 
303   //cout << "Exec until " << p << LF;
304   if (N(cur[p])!=0) return;
305   if (N(cur)>=25) // avoids out of memory in weird cases
306     typeset_invalidate_env ();
307   typeset_prepare ();
308   if (enable_fastenv) {
309     if (!(rp < p)) {
310       failed_error << "Erroneous path " << p << "\n";
311       FAILED ("invalid typesetting path");
312     }
313     tree t= subtree (et, rp);
314     path q= path_up (p / rp);
315     while (!is_nil (q)) {
316       int i= q->item;
317       restricted_exec (env, t, i);
318       if (L(t) == TFORMAT && i == N(t) - 1) {
319         tree fm= tree (TFORMAT);
320         table_descend (t, q, fm);
321         if (!is_nil (q))
322           for (int k=0; k<N(fm); k++)
323             if (is_func (fm[k], CWITH, 2))
324               env->write (fm[k][0]->label, fm[k][1]);
325       }
326       else {
327         tree w= drd->get_env_child (t, i, tree (ATTR));
328         if (w == "") break;
329         //cout << "t= " << t << "\n";
330         //cout << "i= " << i << "\n";
331         //cout << "w= " << w << "\n";
332         for (int j=0; j<N(w); j+=2) {
333           //cout << w[j] << " := " << env->exec (w[j+1]) << "\n";
334           env->write (w[j]->label, env->exec (w[j+1]));
335         }
336         t= t[i];
337         q= q->next;
338       }
339     }
340     if (env->read (PREAMBLE) == "true")
341       env->write (MODE, "src");
342   }
343   else exec_until (ttt, p / rp);
344   env->read_env (cur (p));
345   //time_t t2= texmacs_time ();
346   //if (t2 - t1 >= 10) cout << "typeset_exec_until took " << t2-t1 << "ms\n";
347 }
348 
349 tree
get_full_env()350 edit_typeset_rep::get_full_env () {
351   typeset_exec_until (tp);
352   return (tree) cur[tp];
353 }
354 
355 bool
defined_at_cursor(string var)356 edit_typeset_rep::defined_at_cursor (string var) {
357   typeset_exec_until (tp);
358   return cur[tp]->contains (var);
359 }
360 
361 tree
get_env_value(string var,path p)362 edit_typeset_rep::get_env_value (string var, path p) {
363   typeset_exec_until (p);
364   tree t= cur[p][var];
365   return is_func (t, BACKUP, 2)? t[0]: t;
366 }
367 
368 tree
get_env_value(string var)369 edit_typeset_rep::get_env_value (string var) {
370  /* FIXME: tp is wrong (and consequently, crashes TeXmacs)
371   *   when we call this routine from inside the code which
372   *   is triggered by a button, for example.
373   *
374   * Test: fire TeXmacs, then open a new Graphics, then click
375   *   on the icon for going in spline mode. Then it crashes,
376   *   because we call (get-env-tree) from inside the Scheme.
377   *   If we call (get-env-tree-at ... (cDr (cursor-path))),
378   *   then it works.
379   */
380   return get_env_value (var, tp);
381 }
382 
383 bool
defined_at_init(string var)384 edit_typeset_rep::defined_at_init (string var) {
385   if (init->contains (var)) return true;
386   if (N(pre)==0) typeset_preamble ();
387   return pre->contains (var);
388 }
389 
390 bool
defined_in_init(string var)391 edit_typeset_rep::defined_in_init (string var) {
392   return init->contains (var);
393 }
394 
395 tree
get_init_value(string var)396 edit_typeset_rep::get_init_value (string var) {
397   if (init->contains (var)) {
398     tree t= init [var];
399     return is_func (t, BACKUP, 2)? t[0]: t;
400   }
401   if (N(pre)==0) typeset_preamble ();
402   tree t= pre [var];
403   return is_func (t, BACKUP, 2)? t[0]: t;
404 }
405 
406 string
get_env_string(string var)407 edit_typeset_rep::get_env_string (string var) {
408   return as_string (get_env_value (var));
409 }
410 
411 string
get_init_string(string var)412 edit_typeset_rep::get_init_string (string var) {
413   return as_string (get_init_value (var));
414 }
415 
416 int
get_env_int(string var)417 edit_typeset_rep::get_env_int (string var) {
418   return as_int (get_env_value (var));
419 }
420 
421 int
get_init_int(string var)422 edit_typeset_rep::get_init_int (string var) {
423   return as_int (get_init_value (var));
424 }
425 
426 double
get_env_double(string var)427 edit_typeset_rep::get_env_double (string var) {
428   return as_double (get_env_value (var));
429 }
430 
431 double
get_init_double(string var)432 edit_typeset_rep::get_init_double (string var) {
433   return as_double (get_init_value (var));
434 }
435 
436 language
get_env_language()437 edit_typeset_rep::get_env_language () {
438   string mode= get_env_string (MODE);
439   if (mode == "text")
440     return text_language (get_env_string (LANGUAGE));
441   else if (mode == "math")
442     return math_language (get_env_string (MATH_LANGUAGE));
443   else return prog_language (get_env_string (PROG_LANGUAGE));
444 }
445 
446 SI
get_page_width()447 edit_typeset_rep::get_page_width () {
448   (void) get_env_string (PAGE_WIDTH);
449   return (env->page_user_width + env->page_odd_margin +
450           env->page_right_margin + std_shrinkf - 1) / std_shrinkf;
451 }
452 
453 SI
get_page_height()454 edit_typeset_rep::get_page_height () {
455   (void) get_env_string (PAGE_HEIGHT);
456   return (env->page_user_height + env->page_top_margin +
457           env->page_bottom_margin + std_shrinkf - 1) / std_shrinkf;
458 }
459 
460 /******************************************************************************
461 * Execution without typesetting
462 ******************************************************************************/
463 
464 static tree
simplify_execed(tree t)465 simplify_execed (tree t) {
466   if (is_atomic (t)) return t;
467   int i, n= N(t);
468   tree r (t, n);
469   for (i=0; i<n; i++)
470     r[i]= simplify_execed (t[i]);
471   if (is_func (r, QUOTE, 1) && is_atomic (r[0]))
472     return r[0];
473   else return r;
474 }
475 
476 static tree
expand_references(tree t,hashmap<string,tree> h)477 expand_references (tree t, hashmap<string,tree> h) {
478   if (is_atomic (t)) return t;
479   if (is_func (t, REFERENCE, 1) || is_func (t, PAGEREF)) {
480     string ref= as_string (simplify_execed (t[0]));
481     if (h->contains (ref)) {
482       int which= is_func (t, REFERENCE, 1)? 0: 1;
483       return tree (HLINK, copy (h[ref][which]), "#" * ref);
484     }
485     return tree (HLINK, "?", "#" * ref);
486   }
487   int i, n= N(t);
488   tree r (t, n);
489   for (i=0; i<n; i++)
490     r[i]= expand_references (t[i], h);
491   return r;
492 }
493 
494 tree
exec(tree t,hashmap<string,tree> H,bool expand_refs)495 edit_typeset_rep::exec (tree t, hashmap<string,tree> H, bool expand_refs) {
496   hashmap<string,tree> H2;
497   env->read_env (H2);
498   env->write_env (H);
499   t= env->exec (t);
500   if (expand_refs)
501     t= expand_references (t, buf->data->ref);
502   t= simplify_execed (t);
503   t= simplify_correct (t);
504   env->write_env (H2);
505   return t;
506 }
507 
508 tree
exec_texmacs(tree t,path p)509 edit_typeset_rep::exec_texmacs (tree t, path p) {
510   typeset_exec_until (p);
511   return exec (t, cur[p]);
512 }
513 
514 tree
exec_texmacs(tree t)515 edit_typeset_rep::exec_texmacs (tree t) {
516   return exec_texmacs (t, rp * 0);
517 }
518 
519 tree
exec_verbatim(tree t,path p)520 edit_typeset_rep::exec_verbatim (tree t, path p) {
521   t= convert_OTS1_symbols_to_universal_encoding (t);
522   typeset_exec_until (p);
523   hashmap<string,tree> H= copy (cur[p]);
524   H ("TeXmacs")= tree (MACRO, "TeXmacs");
525   H ("LaTeX")= tree (MACRO, "LaTeX");
526   H ("TeX")= tree (MACRO, "TeX");
527   return exec (t, H);
528 }
529 
530 tree
exec_verbatim(tree t)531 edit_typeset_rep::exec_verbatim (tree t) {
532   return exec_verbatim (t, rp * 0);
533 }
534 
535 tree
exec_html(tree t,path p)536 edit_typeset_rep::exec_html (tree t, path p) {
537   t= convert_OTS1_symbols_to_universal_encoding (t);
538   if (p == (rp * 0)) typeset_preamble ();
539   typeset_exec_until (p);
540   hashmap<string,tree> H= copy (cur[p]);
541   tree patch= as_tree (eval ("(stree->tree (tmhtml-env-patch))"));
542   hashmap<string,tree> P (UNINIT, patch);
543   H->join (P);
544   tree w (WITH);
545   if (H->contains ("html-title"))
546     w << string ("html-title") << H["html-title"];
547   if (H->contains ("html-css"))
548     w << string ("html-css") << H["html-css"];
549   if (H->contains ("html-head-javascript"))
550     w << string ("html-head-javascript") << H["html-head-javascript"];
551   if (H->contains ("html-head-javascript-src"))
552     w << string ("html-head-javascript-src") << H["html-head-javascript-src"];
553   if (N(w) == 0) return exec (t, H);
554   else {
555     w << t;
556     return exec (w, H);
557   }
558   //tree r= exec (t, H);
559   //cout << "In: " << t << "\n";
560   //cout << "Out: " << r << "\n";
561   //return r;
562 }
563 
564 tree
exec_html(tree t)565 edit_typeset_rep::exec_html (tree t) {
566   return exec_html (t, rp * 0);
567 }
568 
569 static tree
value_to_compound(tree t,hashmap<string,tree> h)570 value_to_compound (tree t, hashmap<string,tree> h) {
571   if (is_atomic (t)) return t;
572   else if (is_func (t, VALUE, 1) &&
573 	   is_atomic (t[0]) &&
574 	   h->contains (t[0]->label))
575     return compound (t[0]->label);
576   else {
577     int i, n= N(t);
578     tree r (t, n);
579     for (i=0; i<n; i++)
580       r[i]= value_to_compound (t[i], h);
581     return r;
582   }
583 }
584 
585 tree
exec_latex(tree t,path p)586 edit_typeset_rep::exec_latex (tree t, path p) {
587   t= convert_OTS1_symbols_to_universal_encoding (t);
588   bool expand_unknown_macros= "on" == as_string (
589       call ("get-preference", "texmacs->latex:expand-macros"));
590   bool expand_user_macro= "on" == as_string (
591       call ("get-preference", "texmacs->latex:expand-user-macros"));
592   if (!expand_unknown_macros && !expand_user_macro)
593     return t;
594   if (p == (rp * 0)) typeset_preamble ();
595   typeset_exec_until (p);
596   hashmap<string,tree> H= copy (cur[p]);
597   object l= null_object ();
598   iterator<string> it= iterate (H);
599   while (it->busy ()) l= cons (object (it->next ()), l);
600   tree patch= as_tree (call ("stree->tree", call ("tmtex-env-patch", t, l)));
601   hashmap<string,tree> P (UNINIT, patch);
602   H->join (P);
603 
604   if (!expand_user_macro &&
605       is_document (t) && is_compound (t[0], "hide-preamble")) {
606     tree r= copy (t);
607     r[0]= "";
608     r= exec (value_to_compound (r, P), H, false);
609     r[0]= exec (t[0], H, false);
610     return r;
611   }
612   else {
613     tree r= exec (value_to_compound (t, P), H, false);
614     return r;
615   }
616 }
617 
618 tree
exec_latex(tree t)619 edit_typeset_rep::exec_latex (tree t) {
620   return exec_latex (t, rp * 0);
621 }
622 
623 tree
texmacs_exec(tree t)624 edit_typeset_rep::texmacs_exec (tree t) {
625   return ::texmacs_exec (env, t);
626 }
627 
628 /******************************************************************************
629 * Initialization
630 ******************************************************************************/
631 
632 void
change_style(tree t)633 edit_typeset_rep::change_style (tree t) {
634   bool changed= (the_style != t);
635   the_style= copy (t);
636   if (changed) {
637     require_save ();
638     notify_change (THE_ENVIRONMENT);
639   }
640 }
641 
642 void
init_style()643 edit_typeset_rep::init_style () {
644   notify_change (THE_ENVIRONMENT);
645 }
646 
647 void
init_style(string name)648 edit_typeset_rep::init_style (string name) {
649   if ((name == "none") || (name == "") || (name == "style")) the_style= TUPLE;
650   else if (arity (the_style) == 0) the_style= tree (TUPLE, name);
651   else the_style= tree (TUPLE, name) * the_style (1, N(the_style));
652   require_save ();
653   notify_change (THE_ENVIRONMENT);
654 }
655 
656 tree
get_init_all()657 edit_typeset_rep::get_init_all () {
658   return (tree) init;
659 }
660 
661 void
init_env(string var,tree by)662 edit_typeset_rep::init_env (string var, tree by) {
663   if (init (var) == by) return;
664   init (var)= by;
665   if (var != PAGE_SCREEN_WIDTH &&
666       var != PAGE_SCREEN_HEIGHT &&
667       var != ZOOM_FACTOR)
668     require_save ();
669   notify_change (THE_ENVIRONMENT);
670 }
671 
672 void
init_default(string var)673 edit_typeset_rep::init_default (string var) {
674   if (!init->contains (var)) return;
675   init->reset (var);
676   if (stydef->contains (var)) pre(var)= stydef[var];
677   else pre->reset (var);
678   notify_change (THE_ENVIRONMENT);
679 }
680 
681 /******************************************************************************
682 * Actual typesetting
683 ******************************************************************************/
684 
685 void
typeset_sub(SI & x1,SI & y1,SI & x2,SI & y2)686 edit_typeset_rep::typeset_sub (SI& x1, SI& y1, SI& x2, SI& y2) {
687   //time_t t1= texmacs_time ();
688   typeset_prepare ();
689   eb= empty_box (reverse (rp));
690   // saves memory, also necessary for change_log update
691   bench_start ("typeset");
692 #ifdef USE_EXCEPTIONS
693   try {
694 #endif
695     eb= ::typeset (ttt, x1, y1, x2, y2);
696 #ifdef USE_EXCEPTIONS
697   }
698   catch (string msg) {
699     the_exception= msg;
700     std_error << "Typesetting failure, resetting to empty document\n";
701     assign (rp, tree (DOCUMENT, ""));
702     ::notify_assign (ttt, path(), subtree (et, rp));
703     eb= ::typeset (ttt, x1, y1, x2, y2);
704   }
705   handle_exceptions ();
706 #endif
707   bench_end ("typeset");
708   //time_t t2= texmacs_time ();
709   //if (t2 - t1 >= 10) cout << "typeset took " << t2-t1 << "ms\n";
710   picture_cache_clean ();
711 }
712 
713 static void
report_missing(hashmap<string,tree> missing)714 report_missing (hashmap<string,tree> missing) {
715   array<string> a;
716   for (iterator<string> it= iterate (missing); it->busy(); a << it->next ());
717   merge_sort (a);
718   for (int i=0; i<N(a); i++)
719     if (!starts (a[i], "bib-"))
720       typeset_warning << "Undefined reference " << a[i] << LF;
721 }
722 
723 static void
report_redefined(array<tree> redefined)724 report_redefined (array<tree> redefined) {
725   for (int i=0; i<N(redefined); i++) {
726     tree t= redefined[i];
727     if (t[1]->label == "")
728       typeset_warning << "Redefined " << t[0]->label << LF;
729     else
730       typeset_warning << "Redefined " << t[0]->label
731                       << " as " << t[1]->label << LF;
732   }
733 }
734 
735 void
typeset(SI & x1,SI & y1,SI & x2,SI & y2)736 edit_typeset_rep::typeset (SI& x1, SI& y1, SI& x2, SI& y2) {
737   int missing_nr= INT_MAX;
738   int redefined_nr= INT_MAX;
739   while (true) {
740     typeset_sub (x1, y1, x2, y2);
741     if (!env->complete) break;
742     env->complete= false;
743     if (N(env->missing) == 0 && N(env->redefined) == 0) break;
744     if ((N(env->missing) == missing_nr && N(env->redefined) == redefined_nr) ||
745         (N(env->missing) > missing_nr || N(env->redefined) > redefined_nr)) {
746       report_missing (env->missing);
747       report_redefined (env->redefined);
748       break;
749     }
750     missing_nr= N(env->missing);
751     redefined_nr= N(env->redefined);
752     ::notify_assign (ttt, path(), ttt->br->st);
753   }
754 }
755 
756 void
typeset_invalidate(path p)757 edit_typeset_rep::typeset_invalidate (path p) {
758   if (rp <= p) {
759     //cout << "Invalidate " << p << "\n";
760     notify_change (THE_TREE);
761     ::notify_assign (ttt, p / rp, subtree (et, p));
762   }
763 }
764 
765 void
typeset_invalidate_all()766 edit_typeset_rep::typeset_invalidate_all () {
767   //cout << "Invalidate all\n";
768   notify_change (THE_ENVIRONMENT);
769   typeset_preamble ();
770   ::notify_assign (ttt, path(), subtree (et, rp));
771 }
772