Back to index

texmacs  1.0.7.15
edit_dynamic.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : edit_dynamic.cpp
00004 * DESCRIPTION: editing dynamic content
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 "edit_dynamic.hpp"
00013 #include "tree_analyze.hpp"
00014 
00015 /******************************************************************************
00016 * Constructors and destructors
00017 ******************************************************************************/
00018 
00019 edit_dynamic_rep::edit_dynamic_rep () {}
00020 edit_dynamic_rep::~edit_dynamic_rep () {}
00021 
00022 /******************************************************************************
00023 * Subroutines for inactive content
00024 ******************************************************************************/
00025 
00026 static bool env_locked   = false;
00027 static bool env_in_source= false;
00028 
00029 bool
00030 edit_dynamic_rep::in_source () {
00031   // FIXME: we use a very dirty trick to "lock the environment",
00032   // so that no new look-ups are made during modifications of et or tp.
00033   // In fact, we would need to retypeset the document in order to
00034   // get a correct value.
00035   if (!env_locked)
00036     env_in_source= get_env_string (MODE) == "src";
00037   return env_in_source;
00038 }
00039 
00040 path
00041 edit_dynamic_rep::find_dynamic (path p) {
00042   path parent= path_up (p);
00043   if (!(rp < parent)) return path ();
00044   if (drd->is_dynamic (subtree (et, parent))) return p;
00045   return find_dynamic (parent);
00046 }
00047 
00048 /******************************************************************************
00049 * Making general compound objects
00050 ******************************************************************************/
00051 
00052 bool
00053 edit_dynamic_rep::is_multi_paragraph_macro (tree t) {
00054   int n= arity (t);
00055   if (is_document (t) || is_func (t, PARA) || is_func (t, SURROUND))
00056     return true;
00057   if (is_func (t, MACRO) || is_func (t, WITH) ||
00058       is_func (t, LOCUS) ||
00059       is_func (t, CANVAS) || is_func (t, ORNAMENT))
00060     return is_multi_paragraph_macro (t [n-1]);
00061   if (is_extension (t) && (!is_compound (t, "footnote"))) {
00062     int i;
00063     for (i=1; i<n; i++)
00064       if (is_multi_paragraph_macro (t[i]))
00065        return true;
00066     tree f= get_env_value (t[0]->label);
00067     return is_multi_paragraph_macro (f);
00068   }
00069   return false;
00070 }
00071 
00072 static bool
00073 contains_table_format (tree t, tree var) {
00074   // FIXME: this should go into the DRD
00075   if (is_atomic (t)) return false;
00076   else {
00077     int i, n= N(t);
00078     for (i=0; i<n; i++)
00079       if (contains_table_format (t[i], var))
00080        return true;
00081     return is_func (t, TFORMAT) && (t[N(t)-1] == tree (ARG, var));
00082   }
00083 }
00084 
00085 void
00086 edit_dynamic_rep::make_compound (tree_label l, int n= -1) {
00087   //cout << "Make compound " << as_string (l) << ", " << n << "\n";
00088   eval ("(use-modules (generic generic-edit))");
00089   if (n == -1) {
00090     for (n=0; true; n++) {
00091       if (drd->correct_arity (l, n) &&
00092          ((n>0) || (drd->get_arity_mode (l) == ARITY_NORMAL))) break;
00093       if (n == 100) return;
00094     }
00095   }
00096 
00097   tree t (l, n);
00098   path p (0, 0);
00099   int  acc=0;
00100   for (; acc<n; acc++)
00101     if (drd->is_accessible_child (t, acc))
00102       break;
00103   if (acc<n) p->item= acc;
00104   if (n == 0) insert_tree (t, 1);
00105   else if (is_with_like (t) && as_bool (call ("with-like-check-insert", t)));
00106   else {
00107     string s= as_string (l);
00108     tree f= get_env_value (s);
00109     bool block_macro= (N(f) == 2) && is_multi_paragraph_macro (f);
00110     bool table_macro= (N(f) == 2) && contains_table_format (f[1], f[0]);
00111     // FIXME: why do we take the precaution N(f) == 2 ?
00112     if (s == "explain") block_macro= true;
00113 
00114     tree sel= "";
00115     if (selection_active_small () ||
00116        (block_macro && selection_active_normal ()))
00117       sel= selection_get_cut ();
00118     if ((block_macro && (!table_macro)) ||
00119        (l == make_tree_label ("footnote")))
00120       {
00121        t[0]= tree (DOCUMENT, "");
00122        p   = path (0, 0, 0);
00123       }
00124     if (!drd->all_accessible (l))
00125       if (get_init_string (MODE) != "src" && !inside ("show-preamble")) {
00126         t= tree (INACTIVE, t);
00127         p= path (0, p);
00128       }
00129     insert_tree (t, p);
00130     if (table_macro) make_table (1, 1);
00131     if (sel != "") insert_tree (sel, end (sel));
00132     
00133     tree mess= concat ();
00134     if (drd->get_arity_mode (l) != ARITY_NORMAL)
00135       mess= concat (kbd ("A-right"), ": insert argument");
00136     if (!drd->all_accessible (l)) {
00137       if (mess != "") mess << ", ";
00138       mess << kbd ("return") << ": activate";
00139     }
00140     if (mess == concat ()) mess= "Move to the right when finished";
00141     set_message (mess, drd->get_name (l));
00142   }
00143 }
00144 
00145 void
00146 edit_dynamic_rep::activate () {
00147   path p= search_upwards (INACTIVE);
00148   if (is_nil (p)) return;
00149   tree st= subtree (et, p * 0);
00150 
00151   if (is_func (st, COMPOUND) && is_atomic (st[0])) {
00152     tree u (make_tree_label (st[0]->label));
00153     u << A (st (1, N(st)));
00154     assign (p, u);
00155     go_to (end (et, p));
00156     correct (path_up (p));
00157   }
00158   else {
00159     bool acc= (p < path_up (tp) && drd->is_accessible_child (st, tp[N(p)]));
00160     remove_node (p * 0);
00161     if (!acc) go_to (end (et, p));
00162     correct (path_up (p));
00163   }
00164 }
00165 
00166 /******************************************************************************
00167 * Inserting and removing arguments
00168 ******************************************************************************/
00169 
00170 void
00171 edit_dynamic_rep::go_to_argument (path p, bool start_flag) {
00172   tree t= subtree (et, path_up (p));
00173   bool inactive= is_func (subtree (et, path_up (p, 2)), INACTIVE);
00174   int i= last_item (p), n= N(t);
00175   if (i < 0) go_to_start (path_up (p, inactive? 2: 1));
00176   else if (i >= n) go_to_end (path_up (p, inactive? 2: 1));
00177   else {
00178     if ((!drd->is_accessible_child (t, i)) &&
00179        (!inactive) && (!in_source ()))
00180       {
00181        insert_node (path_up (p) * 0, INACTIVE);
00182        p= path_up (p) * path (0, i);
00183       }
00184     if (start_flag) go_to_start (p);
00185     else go_to_end (p);
00186   }
00187 }
00188 
00189 void
00190 edit_dynamic_rep::insert_argument (path p, bool forward) {
00191   tree t= subtree (et, path_up (p));
00192   int i= last_item (p), n= N(t), d= 1;
00193   if (is_func (t, WITH) ||
00194       is_func (t, STYLE_WITH) ||
00195       is_func (t, VAR_STYLE_WITH))
00196     if (i == n-1) i--;
00197   if ((!in_source ()) || drd->contains (as_string (L(t)))) {
00198     if (forward) do i++; while ((i<=n) && (!drd->insert_point (L(t), i, n)));
00199     else while ((i>=0) && (!drd->insert_point (L(t), i, n))) i--;
00200     if ((i<0) || (i>n)) return;
00201     while (!drd->correct_arity (L(t), n+d)) d++;
00202   }
00203   else if (forward) i++;
00204   path q= path_up (p) * i;
00205   tree ins (L(t), d);
00206   insert (q, ins);
00207   go_to_argument (q, forward);
00208 }
00209 
00210 void
00211 edit_dynamic_rep::insert_argument (bool forward) {
00212   path p= find_dynamic (tp);
00213   if (is_nil (p)) return;
00214   if (p == tp) p= find_dynamic (path_up (tp));
00215   if (is_nil (p)) return;
00216   insert_argument (p, forward);
00217 }
00218 
00219 void
00220 edit_dynamic_rep::remove_empty_argument (path p, bool forward) {
00221   tree t= subtree (et, path_up (p));
00222   int i= last_item (p), j, d, n= N(t);
00223   bool src_flag= in_source () && (!drd->contains (as_string (L(t))));
00224 
00225   for (d=1; d<=n-i; d++)
00226     if ((src_flag && (d==1)) ||
00227        ((!src_flag) &&
00228         drd->correct_arity (L(t), n-d) &&
00229         drd->insert_point (L(t), i, n-d)))
00230       {
00231        bool flag= true;
00232        for (j=0; j<d; j++)
00233          flag= flag && is_empty (t[i+j]);
00234        if (flag) {
00235          bool old_locked= env_locked; env_locked= true;
00236          remove (p, d);
00237          if ((d == n) && is_mod_active_once (subtree (et, path_up (p, 2)))) {
00238            remove_node (path_up (p, 2) * 0);
00239            go_to_border (path_up (p, 2), forward);
00240          }
00241          else if (forward) go_to_argument (path_up (p) * i, true);
00242          else go_to_argument (path_up (p) * (i-1), false);
00243          env_locked= old_locked;
00244          return;
00245        }
00246        else break;
00247       }
00248 
00249   bool flag= true;
00250   for (j=0; j<n; j++)
00251     flag= flag && is_empty (t[j]);
00252   if (flag) {
00253     assign (path_up (p), "");
00254     tree st= subtree (et, path_up (p, 2));
00255     if ((is_mod_active_once (st) || is_compound (st, "doc-inactive")) &&
00256        (st[0] == "")) {
00257       assign (path_up (p, 2), "");
00258       correct (path_up (p, 3));
00259     }
00260     // FIXME: temporary hack for doc-data and doc-author-data
00261     else if (is_compound (st, "doc-data") ||
00262             is_compound (st, "doc-author-data")) {
00263       if (N(st)==1) {
00264        assign (path_up (p, 2), "");
00265        correct (path_up (p, 3));
00266       }
00267       else {
00268        int i= last_item (path_up (p)) + (forward? 0: -1);
00269        remove (path_up (p), 1);
00270        if (i<0) go_to_start (path_up (p, 2));
00271        else go_to_border (path_up (p, 2) * i, forward);
00272       }
00273     }
00274     else correct (path_up (p, 2));
00275     return;
00276   }
00277 
00278   if (forward) go_to_argument (path_up (p) * (i+1), true);
00279   else go_to_argument (path_up (p) * (i-1), false);
00280 }
00281 
00282 void
00283 edit_dynamic_rep::remove_argument (path p, bool forward) {
00284   tree t= subtree (et, path_up (p));
00285   int i= last_item (p), n= N(t), d= 1;
00286   if ((!in_source ()) || drd->contains (as_string (L(t)))) {
00287     if (forward) do i++; while (i<=n && !drd->insert_point (L(t), i, n));
00288     else while (i>=0 && !drd->insert_point (L(t), i, n)) i--;
00289     if ((i<0) || (i>n)) return;
00290     while (i>=d && !drd->correct_arity (L(t), n-d)) d++;
00291     if (i<d || n<=d || !drd->insert_point (L(t), i-d, n-d)) return;
00292   }
00293   else {
00294     if (forward) i++;
00295     if (i<d || n<=d) return;
00296   }
00297   path q= path_up (p) * (i-d);
00298   remove (q, d);
00299   go_to_argument (q, forward);
00300 }
00301 
00302 void
00303 edit_dynamic_rep::remove_argument (bool forward) {
00304   path p= find_dynamic (tp);
00305   if (is_nil (p)) return;
00306   if (p == tp) p= find_dynamic (path_up (tp));
00307   if (is_nil (p)) return;
00308   remove_argument (p, forward);
00309 }
00310 
00311 /******************************************************************************
00312 * Backspace and delete
00313 ******************************************************************************/
00314 
00315 void
00316 edit_dynamic_rep::back_monolithic (path p) {
00317   if (!is_concat (subtree (et, path_up (p)))) assign (p, "");
00318   else remove (p, 1);
00319   correct (path_up (p));
00320 }
00321 
00322 void
00323 edit_dynamic_rep::back_general (path p, bool forward) {
00324   tree st= subtree (et, p);
00325   int n= N(st);
00326   if ((L(st) >= START_EXTENSIONS) && in_source () && (forward || (n == 0))) {
00327     tree u (COMPOUND, copy (as_string (L(st))));
00328     u << copy (A (st));
00329     assign (p, u);
00330     go_to_border (p * 0, forward);
00331   }
00332   else if (n==0) back_monolithic (p);
00333   else if ((n==1) && is_func (st[0], DOCUMENT, 1) &&
00334           (is_func (st[0][0], TFORMAT) || is_func (st[0][0], TABLE)))
00335     back_table (p * path (0, 0), forward);
00336   else if ((n==1) && (is_func (st[0], TFORMAT) || is_func (st[0], TABLE)))
00337     back_table (p * 0, forward);
00338   else go_to_argument (p * (forward? 0: n-1), forward);
00339 }
00340 
00341 void
00342 edit_dynamic_rep::back_in_general (tree t, path p, bool forward) {
00343   if (is_func (subtree (et, path_up (p, 2)), INACTIVE) || in_source ())
00344     if ((L(t) >= START_EXTENSIONS) && (last_item (p) == 0) && (!forward)) {
00345       bool src_flag= in_source () && (!drd->contains (as_string (L(t))));
00346       tree u (COMPOUND, copy (as_string (L(t))));
00347       if (is_empty (t[0]) && src_flag) u << A (copy (t (1, N(t))));
00348       else u << A (copy (t));
00349       assign (path_up (p), u);
00350       go_to_end (p);
00351       return;
00352     }
00353   remove_empty_argument (p, forward);
00354 }
00355 
00356 /******************************************************************************
00357 * The WITH tag
00358 ******************************************************************************/
00359 
00360 static tree
00361 remove_changes_in (tree t, string var) {
00362   if (is_atomic (t)) return t;
00363   else if (is_func (t, WITH)) {
00364     int i, n=N(t), k=(n-1)>>1;
00365     if (k==1) {
00366       tree r= remove_changes_in (t[2], var);
00367       if (t[0] != var) r= tree (WITH, t[0], t[1], r);
00368       return simplify_correct (r);
00369     }
00370     tree r (WITH);
00371     for (i=0; i<k; i++)
00372       if (t[i<<1] != var) r << t[i<<1] << t[(i<<1)+1];
00373     r << remove_changes_in (t[i<<1], var);
00374     return simplify_correct (r);
00375   }
00376   else if (is_format (t) || is_func (t, SURROUND)) {
00377     int i, n= N(t);
00378     tree r (t, n);
00379     for (i=0; i<n; i++)
00380       r[i]= remove_changes_in (t[i], var);
00381     return simplify_correct (r);
00382   }
00383   else return t;
00384 }
00385 
00386 void
00387 edit_dynamic_rep::make_with (string var, string val) {
00388   if (selection_active_normal ()) {
00389     tree t= remove_changes_in (selection_get (), var);
00390     selection_cut ();
00391     insert_tree (tree (WITH, var, val, t), path (2, end (t)));
00392   }
00393   else insert_tree (tree (WITH, var, val, ""), path (2, 0));
00394 }
00395 
00396 void
00397 edit_dynamic_rep::insert_with (path p, string var, tree val) {
00398   tree st= subtree (et, p);
00399   if (is_func (st, WITH)) {
00400     int i, n= N(st)-1;
00401     for (i=0; i<n; i+=2)
00402       if (st[i] == var) {
00403        assign (p * (i+1), copy (val));
00404        return;
00405       }
00406     insert (p * n, copy (tree (WITH, var, val)));    
00407   }
00408   else if ((rp < p) && is_func (subtree (et, path_up (p)), WITH))
00409     insert_with (path_up (p), var, val);
00410   else insert_node (p * 2, copy (tree (WITH, var, val)));
00411 }
00412 
00413 void
00414 edit_dynamic_rep::remove_with (path p, string var) {
00415   tree st= subtree (et, p);
00416   if (is_func (st, WITH)) {
00417     int i, n= N(st)-1;
00418     for (i=0; i<n; i+=2)
00419       if (st[i] == var) {
00420        remove (p * i, 2);
00421        if (n == 2) remove_node (p * 0);
00422        return;
00423       }
00424   }
00425   else if ((rp < p) && is_func (subtree (et, path_up (p)), WITH))
00426     remove_with (path_up (p), var);
00427 }
00428 
00429 void
00430 edit_dynamic_rep::back_in_with (tree t, path p, bool forward) {
00431   if (is_func (subtree (et, path_up (p, 2)), INACTIVE) ||
00432       ((is_func (t, WITH) || is_func (t, LOCUS)) && in_source ()))
00433     back_in_general (t, p, forward);
00434   else if (t[N(t)-1] == "") {
00435     assign (path_up (p), "");
00436     correct (path_up (p, 2));
00437   }
00438   else go_to_border (path_up (p), !forward);
00439 }
00440 
00441 /******************************************************************************
00442 * Style file editing
00443 ******************************************************************************/
00444 
00445 void
00446 edit_dynamic_rep::make_mod_active (tree_label l) {
00447   if (selection_active_normal ()) {
00448     tree t= selection_get ();
00449     selection_cut ();
00450     insert_tree (tree (l, t), path (0, end (t)));
00451   }
00452   else if ((l == VAR_STYLE_ONLY) || (l == VAR_ACTIVE) || (l == VAR_INACTIVE))
00453     insert_tree (tree (l, ""), path (0, 0));
00454   else {
00455     path p= path_up (tp);
00456     if (is_atomic (subtree (et, p))) p= path_up (p);
00457     if (rp < p) insert_node (p * 0, l);
00458   }
00459 }
00460 
00461 void
00462 edit_dynamic_rep::insert_style_with (path p, string var, string val) {
00463   if (!(rp < p)) return;
00464   tree st= subtree (et, path_up (p));
00465   if (is_func (st, STYLE_WITH)) {
00466     int i, n= N(st);
00467     for (i=n-1; i>=0; i-=2)
00468       if (st[i] == var) {
00469        assign (path_up (p) * (i+1), copy (val));
00470        return;
00471       }
00472     insert (path_up (p) * (n-1), tree (STYLE_WITH, copy (var), copy (val)));
00473   }
00474   else insert_node (p * 2, copy (tree (STYLE_WITH, var, val)));
00475 }
00476 
00477 void
00478 edit_dynamic_rep::make_style_with (string var, string val) {
00479   if (selection_active_normal ()) {
00480     tree t= selection_get ();
00481     selection_cut ();
00482     if (subtree (et, path_up (tp)) == "") {
00483       insert_style_with (path_up (tp), var, val);
00484       insert_tree (t);
00485     }
00486     else insert_tree (tree (STYLE_WITH, var, val, t), path (2, end (t)));
00487   }
00488   else insert_style_with (path_up (tp), var, val);
00489 }
00490 
00491 /******************************************************************************
00492 * The HYBRID and LATEX tags
00493 ******************************************************************************/
00494 
00495 void
00496 edit_dynamic_rep::make_hybrid () {
00497   tree t (HYBRID, "");
00498   if (selection_active_small ())
00499     t[0]= selection_get_cut ();
00500   if (is_func (t, HYBRID, 1) && (t[0] != "") &&
00501       (!(is_atomic (t[0]) && drd->contains (t[0]->label))))
00502     t= tree (HYBRID, "", t[0]);
00503   path p= end (t, path (0));
00504   if (in_source ()) insert_tree (t, p);
00505   else insert_tree (tree (INACTIVE, t), path (0, p));
00506   set_message (concat (kbd ("return"), ": activate symbol or macro"),
00507               "hybrid");
00508 }
00509 
00510 bool
00511 edit_dynamic_rep::activate_latex () {
00512   path p= search_upwards (LATEX);
00513   if (is_nil (p)) p= search_upwards (HYBRID);
00514   if (is_nil (p)) return false;
00515   tree st= subtree (et, p);
00516   if (is_atomic (st[0])) {
00517     if (is_func (subtree (et, path_up (p)), INACTIVE))
00518       p= path_up (p);
00519     string  s= st[0]->label, help;
00520     command cmd;
00521     if (kbd_get_command (s, help, cmd)) {
00522       cut (p * 0, p * 1);
00523       cmd ();
00524       if (N(st) == 2) insert_tree (copy (st[1]));
00525       return true;
00526     }
00527     set_message ("Error: not a command name",
00528                "activate latex command");
00529   }
00530   return false;
00531 }
00532 
00533 void
00534 edit_dynamic_rep::activate_hybrid (bool with_args_hint) {
00535   // WARNING: update edit_interface_rep::set_hybrid_footer when updating this
00536   if (activate_latex ()) return;
00537   set_message ("", "");
00538   path p= search_upwards (HYBRID);
00539   if (is_nil (p)) return;
00540   tree st= subtree (et, p);
00541   if (is_compound (st[0])) return;
00542   if (is_func (subtree (et, path_up (p)), INACTIVE))
00543     p= path_up (p);
00544 
00545   // activate macro argument
00546   string name= st[0]->label;
00547   path mp= search_upwards (MACRO);
00548   if (!is_nil (mp)) {
00549     tree mt= subtree (et, mp);
00550     int i, n= N(mt)-1;
00551     for (i=0; i<n; i++)
00552       if (mt[i] == name) {
00553        assign (p, tree (ARG, copy (name)));
00554        go_to (end (et, p));
00555        correct (path_up (p));
00556        return;
00557       }
00558   }
00559 
00560   // built-in primitives, macro applications and values
00561   bool old_locked= env_locked; env_locked= true;
00562   tree f= get_env_value (name);
00563   if ((drd->contains (name) && (f == UNINIT)) ||
00564       is_func (f, MACRO) || is_func (f, XMACRO)) {
00565     assign (p, "");
00566     correct (path_up (p));
00567     make_compound (make_tree_label (name));
00568     if (N(st) == 2) insert_tree (st[1]);
00569   }
00570   else if (f != UNINIT) {
00571     assign (p, tree (VALUE, copy (name)));
00572     go_to (end (et, p));
00573     correct (path_up (p));
00574   }
00575   else if (in_source ()) {
00576     assign (p, "");
00577     correct (path_up (p));
00578     make_compound (make_tree_label (name), with_args_hint? 1: 0);
00579     if (N(st) == 2) insert_tree (st[1]);
00580   }
00581   else set_message ("Error: unknown command",
00582                   "activate hybrid command");
00583   env_locked= old_locked;
00584 }
00585 
00586 /******************************************************************************
00587 * Other special tags (SYMBOL and COMPOUND)
00588 ******************************************************************************/
00589 
00590 void
00591 edit_dynamic_rep::activate_symbol () {
00592   path p= search_upwards (SYMBOL);
00593   if (is_nil (p)) return;
00594   tree st= subtree (et, p);
00595   if (is_func (subtree (et, path_up (p)), INACTIVE))
00596     p= path_up (p);
00597   string s= st[0]->label;
00598   if (is_int (s))
00599     assign (p, string (((char) as_int (s))));
00600   else {
00601     int i, n= N(s);
00602     for (i=0; i<n; i++)
00603       if ((s[i]=='<') || (s[i]=='>'))
00604        { s= ""; break; }
00605     assign (p, "<" * s * ">");
00606   }
00607   go_to (end (et, p));
00608   correct (path_up (p));
00609 }
00610 
00611 /******************************************************************************
00612 * Temporary fixes for block structures
00613 ******************************************************************************/
00614 
00615 bool
00616 edit_dynamic_rep::make_return_before () {
00617   bool flag;
00618   path q= tp;
00619   while (!is_document (subtree (et, path_up (q)))) q= path_up (q);
00620   flag= (N (subtree (et, path_up (q))) == (q->item+1)) || (tp != end (et, q));
00621   if (flag) {
00622     flag= insert_return ();
00623     go_to (end (et, q));
00624   }
00625   return flag;
00626 }
00627 
00628 bool
00629 edit_dynamic_rep::make_return_after () {
00630   path q= tp;
00631   while (!is_document (subtree (et, path_up (q)))) {
00632     q= path_up (q);
00633     if (!(rp < q)) return false;
00634   }
00635   if (tp == start (et, q)) return false;
00636   return insert_return ();
00637 }
00638 
00639 void
00640 edit_dynamic_rep::temp_proof_fix () {
00641   /* this routine should be removed as soon as possible */
00642   path p = search_upwards ("proof");
00643   if (is_nil (p) || (N(tp) < N(p)+2)) return;
00644   path q = head (tp, N(p)+2);
00645   tree st= subtree (et, path_up (q));
00646   if ((!is_document (st)) || (last_item (q) != (N(st)-1))) return;
00647   insert (path_inc (q), tree (DOCUMENT, ""));
00648 }