1 
2 /******************************************************************************
3 * MODULE     : edit_select.cpp
4 * DESCRIPTION: Selection handling
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 "Replace/edit_select.hpp"
13 #include "Interface/edit_interface.hpp"
14 #include "convert.hpp"
15 #include "packrat.hpp"
16 #include "tree_select.hpp"
17 #include "drd_mode.hpp"
18 
19 /******************************************************************************
20 * Internationalization
21 ******************************************************************************/
22 
23 string
selection_encode(string lan,string s)24 selection_encode (string lan, string s) {
25   if ((lan == "croatian") || (lan == "czech") || (lan == "hungarian") ||
26       (lan == "polish") || (lan == "slovene"))
27     return cork_to_il2 (s);
28   else if (lan == "spanish")
29     return spanish_to_ispanish (s);
30   else if (lan == "german")
31     return german_to_igerman (s);
32   else return s;
33 }
34 
35 string
selection_decode(string lan,string s)36 selection_decode (string lan, string s) {
37   if ((lan == "croatian") || (lan == "czech") || (lan == "hungarian") ||
38       (lan == "polish") || (lan == "slovene"))
39     return il2_to_cork (s);
40   else if (lan == "spanish")
41     return ispanish_to_spanish (s);
42   else if (lan == "german")
43     return igerman_to_german (s);
44   else return s;
45 }
46 
47 /******************************************************************************
48 * Constructor and destructor
49 ******************************************************************************/
50 
edit_select_rep()51 edit_select_rep::edit_select_rep ():
52   cur_sel (no_ranges ()),
53   selecting (false), shift_selecting (false), mid_p (),
54   selection_import ("default"), selection_export ("default"),
55   focus_p (), focus_hold (false) {}
~edit_select_rep()56 edit_select_rep::~edit_select_rep () {}
57 
58 /******************************************************************************
59 * Semantic selections
60 ******************************************************************************/
61 
62 path
semantic_root(path p)63 edit_select_rep::semantic_root (path p) {
64   while (p != rp) {
65     tree st= subtree (et, path_up (p));
66     if (path_up (p) != rp && is_func (st, DOCUMENT, 1))
67       st= subtree (et, path_up (p, 2));
68     if (is_func (st, CELL)) break;
69     if (is_compound (st) && N(st) == 1) {
70       tree env= drd->get_env (L(st), 0);
71       tree mt= drd_env_read (env, MODE);
72       tree pt= drd_env_read (env, "prog-language");
73       if (mt == "math") break;
74       if (mt == "prog" && pt == "minimal") break;
75       if (mt == "text") return rp;
76     }
77     p= path_up (p);
78   }
79   return p;
80 }
81 
82 bool
semantic_active(path q)83 edit_select_rep::semantic_active (path q) {
84   if (as_string (eval ("(get-preference \"semantic editing\")")) == "on") {
85     path p= semantic_root (q);
86     //cout << subtree (et, p) << ", " << p << " -> " << end (et, p) << "\n";
87     tree mt= get_env_value (MODE, end (et, p));
88     tree lt= get_env_value (MODE_LANGUAGE (mt->label), end (et, p));
89     string lan= (is_atomic (lt)? lt->label: string ("std-math"));
90     if (mt == "math" || (mt == "prog" && lan == "minimal"))
91       return packrat_available_path (lan, subtree (et, p), q / p);
92   }
93   return false;
94 }
95 
96 static bool
right_most_inside(path p,tree t)97 right_most_inside (path p, tree t) {
98   if (is_atomic (t)) return p == path (N(t->label));
99   else if (p == path (1)) return true;
100   if (is_nil (p) || is_nil (p->next)) return false;
101   if (N(t) == 1 && p->item == 0)
102     return right_most_inside (p->next, t[0]);
103   if (is_func (t, WIDE, 2))
104     return p->item == 0 && right_most_inside (p->next, t[0]);
105   if (is_func (t, CONCAT) || is_func (t, DOCUMENT))
106     return p->item == N(t) - 1 && right_most_inside (p->next, t[N(t) - 1]);
107   return false;
108 }
109 
110 static path
correct_right_most_inside(path p,tree t)111 correct_right_most_inside (path p, tree t) {
112   if (is_nil (p) || is_nil (p->next) || is_atomic (t)) return p;
113   if (0 > p->item || p->item >= N(t)) return p;
114   p= path (p->item, correct_right_most_inside (p->next, t[p->item]));
115   if (right_most_inside (p, t)) return path (1);
116   else return p;
117 }
118 
119 bool
semantic_select(path p,path & q1,path & q2,int mode)120 edit_select_rep::semantic_select (path p, path& q1, path& q2, int mode) {
121   if (!semantic_active (p)) return false;
122   if (mode < 2 && get_preference ("semantic selections") != "on") return false;
123   p= semantic_root (p);
124   while (p != rp && !(p <= q1 && p <= q2))
125     p= semantic_root (path_up (p));
126   tree mt= get_env_value (MODE, end (et, p));
127   tree lt= get_env_value (MODE_LANGUAGE (mt->label), end (et, p));
128   string lan= (is_atomic (lt)? lt->label: string ("std-math"));
129   path p1= q1 / p, p2= q2 / p;
130   path cp= (p <= tp? tp / p: path ());
131   tree st= subtree (et, p);
132   bool ret= packrat_select (lan, "Main", st, cp, p1, p2, mode);
133   p1= correct_right_most_inside (p1, st);
134   if (ret) {
135     q1= p * p1;
136     q2= p * p2;
137   }
138   return ret;
139 }
140 
141 /******************************************************************************
142 * Selecting particular things
143 ******************************************************************************/
144 
145 void
select(path p1,path p2)146 edit_select_rep::select (path p1, path p2) {
147   //cout << "Select " << p1 << " -- " << p2 << "\n";
148   if (cur_sel == simple_range (p1, p2)) return;
149   if (!(rp <= p1 && rp <= p2)) return;
150   if (is_empty (cur_sel) && p1 == p2) {
151     cur_sel= simple_range (p1, p2);
152     return;
153   }
154   if (p1 != p2) {
155     path cp= common (p1, p2);
156     tree st= subtree (et, cp);
157     if (!is_func (st, TABLE) && !is_func (st, ROW))
158       (void) semantic_select (cp, p1, p2, 0);
159   }
160   if (path_less (p1, p2))
161     cur_sel= simple_range (p1, p2);
162   else
163     cur_sel= simple_range (p2, p1);
164   notify_change (THE_SELECTION);
165 }
166 
167 void
select(path p)168 edit_select_rep::select (path p) {
169   select (start (et, p), end (et, p));
170 }
171 
172 void
select_all()173 edit_select_rep::select_all () {
174   select (rp);
175 }
176 
177 void
select_line()178 edit_select_rep::select_line () {
179   select (search_parent_upwards (DOCUMENT));
180 }
181 
get_selection(path & p1,path & p2)182 void edit_select_rep::get_selection (path& p1, path& p2) {
183   p1= start (cur_sel); p2= end (cur_sel); }
set_selection(path p1,path p2)184 void edit_select_rep::set_selection (path p1, path p2) {
185   select (p1, p2); }
186 
187 /******************************************************************************
188 * For interface with cursor movement
189 ******************************************************************************/
190 
191 void
select_from_cursor()192 edit_select_rep::select_from_cursor () {
193   if (selecting) {
194     select (mid_p, tp);
195     if (shift_selecting) selecting = false;
196   }
197 }
198 
199 void
select_from_cursor_if_active()200 edit_select_rep::select_from_cursor_if_active () {
201   if (selecting) select (mid_p, tp);
202   else selection_cancel ();
203 }
204 
205 void
select_from_keyboard(bool flag)206 edit_select_rep::select_from_keyboard (bool flag) {
207   selecting= flag;
208   shift_selecting= false;
209   if (flag) {
210     cur_sel= simple_range (tp, tp);
211     mid_p  = copy (tp);
212   }
213   else mid_p= rp;
214 }
215 
216 void
select_from_shift_keyboard()217 edit_select_rep::select_from_shift_keyboard () {
218   if (!shift_selecting || is_empty (cur_sel)) mid_p= copy (tp);
219   selecting= true;
220   shift_selecting= true;
221 }
222 
223 /******************************************************************************
224 * Enlarging an existing selection
225 ******************************************************************************/
226 
227 static int
breaking_force(char c)228 breaking_force (char c) {
229   if (c == ' ') return 3;
230   if (is_punctuation (c)) return 2;
231   if (is_iso_alpha (c) || is_digit (c)) return 0;
232   return 1;
233 }
234 
235 void
select_enlarge_text()236 edit_select_rep::select_enlarge_text () {
237   path p= common (cur_sel);
238   if (is_empty (cur_sel) && !is_nil (p)) p= path_up (p);
239   tree st= subtree (et, p);
240   ASSERT (is_atomic (st), "non textual tree");
241   string s= st->label;
242   string mode= get_env_string (MODE);
243   int i1= last_item (start (cur_sel)), j1= i1;
244   int i2= last_item (end   (cur_sel)), j2= i2;
245   path q= path_up (p);
246 
247   if (mode == "text" || mode == "src") {
248     int i, f= 4;
249     if (i1 > 0) {
250       i= i1; tm_char_backwards (s, i);
251       f= min (f, breaking_force (s[i]));
252     }
253     if (i2 < N(s))
254       f= min (f, breaking_force (s[i2]));
255 
256     while (i1 > 0) {
257       i= i1; tm_char_backwards (s, i);
258       if (breaking_force (s[i]) > f) break;
259       i1= i;
260     }
261     while (i2 < N(s)) {
262       if (breaking_force (s[i2]) > f) break;
263       tm_char_forwards (s, i2);
264     }
265 
266     if (i1 < i2 && (i1 != j1 || i2 != j2)) {
267       if (is_concat (subtree (et, q)) && i1 == 0 && i2 == N(s))
268 	select (q * 0, q * 1);
269       else select (p * i1, p * i2);
270       return;
271     }
272   }
273 
274   if (is_concat (subtree (et, q)) || (i1 == 0 && i2 == N(s)))
275     select (q * 0, q * 1);
276   else select (p * 0, p * N(s));
277 }
278 
279 bool
incomplete_script_selection(tree t,path lp,path rp)280 incomplete_script_selection (tree t, path lp, path rp) {
281   if (!is_func (t, CONCAT)) return false;
282   if (N(lp) < 2 || N(rp) < 2) return false;
283   int l= lp->item, r= rp->item;
284   if (is_func (t[l], RSUB) || is_func (t[l], RSUP)) return true;
285   if (is_func (t[r], LSUB) || is_func (t[r], LSUP)) return true;
286   if (l  >0    && (is_func (t[l-1], LSUB) || is_func (t[l-1], LSUP))) return true;
287   if (r+1<N(t) && (is_func (t[r+1], RSUB) || is_func (t[r+1], RSUP))) return true;
288   return false;
289 }
290 
291 void
select_enlarge()292 edit_select_rep::select_enlarge () {
293   path sp, sq;
294   if (is_empty (cur_sel) && !is_nil (start (cur_sel))) {
295     sp= path_up (start (cur_sel));
296     sq= sp;
297   }
298   else {
299     sp= common (cur_sel);
300     if (!(rp < sp)) {
301       selection_cancel ();
302       set_message ("", "");
303       return;
304     }
305     sq= path_up (sp);
306   }
307   path pp= sp, p1= start (cur_sel), p2= end (cur_sel);
308   if (start (cur_sel) == pp * 0 &&
309       end   (cur_sel) == pp * right_index (subtree (et, pp)))
310     if (!is_nil (pp)) pp= path_up (pp);
311   if (is_func (subtree (et, pp), TFORMAT)) pp= path_up (pp);
312   if (semantic_select (pp, p1, p2, 1))
313     select (p1, p2);
314   else {
315     if (is_atomic (subtree (et, sp))) select_enlarge_text ();
316     else select (sq * 0, sq * 1);
317   }
318 
319   path p = common (cur_sel);
320   tree st= subtree (et, p);
321   if (drd->var_without_border (L(st)) ||
322       is_func (st, TFORMAT) ||
323       is_func (st, DOCUMENT, 1) ||
324       is_script (st) ||
325       incomplete_script_selection (st, start (cur_sel) / p, end (cur_sel) / p))
326     select_enlarge ();
327   else {
328     tree s;
329     if (is_atomic (st)) s= "text";
330     else if (is_func (st, COMPOUND)) s= as_string (st[0]);
331     else if (is_func (st, WITH)) s= concat ("with ", as_string (st[0]));
332     else s= as_string (L(st));
333     set_message (concat ("selected ", s), "enlarge selection");
334   }
335   selecting= shift_selecting= false;
336 }
337 
338 static bool
stop_enlarge_environmental(tree t)339 stop_enlarge_environmental (tree t) {
340   if (is_func (t, WITH, 3) && (t[0] == MODE) && (t[1] == "math")) return true;
341   if (!is_extension (t)) return false;
342   if (is_multi_paragraph (t)) return true;
343   string s= as_string (L(t));
344   return
345     (s == "part") ||
346     (s == "chapter") ||
347     (s == "section") ||
348     (s == "subsection") ||
349     (s == "subsubsection") ||
350     (s == "paragraph") ||
351     (s == "subparagraph");
352 }
353 
354 void
select_enlarge_environmental()355 edit_select_rep::select_enlarge_environmental () {
356   select_enlarge ();
357   if (is_empty (cur_sel)) return;
358   path p= common (cur_sel);
359   tree st= subtree (et, p);
360   if (stop_enlarge_environmental (st)) return;
361   select_enlarge_environmental ();
362 }
363 
364 /******************************************************************************
365 * Test whether selection is active
366 ******************************************************************************/
367 
368 bool
selection_active_any()369 edit_select_rep::selection_active_any () {
370   return !is_empty (cur_sel);
371 }
372 
373 bool
selection_active_normal()374 edit_select_rep::selection_active_normal () {
375   return selection_active_any () && (!selection_active_table ());
376 }
377 
378 bool
selection_active_table(bool strict)379 edit_select_rep::selection_active_table (bool strict) {
380   if (!selection_active_any ()) return false;
381   return is_table_selection (et, start (cur_sel), end (cur_sel), strict);
382 }
383 
384 bool
selection_active_small()385 edit_select_rep::selection_active_small () {
386   if (!selection_active_normal ()) return false;
387   path p1, p2;
388   selection_get (p1, p2);
389   if (p2 == p1) return false;
390   if (is_multi_paragraph (subtree (et, common (p1, p2)))) return false;
391   return true;
392 }
393 
394 bool
selection_active_enlarging()395 edit_select_rep::selection_active_enlarging () {
396   return (selecting || !is_empty (cur_sel)) && (mid_p == tp);
397 }
398 
399 /******************************************************************************
400 * Get the selection
401 ******************************************************************************/
402 
403 void
selection_correct(path i1,path i2,path & o1,path & o2)404 edit_select_rep::selection_correct (path i1, path i2, path& o1, path& o2) {
405   ASSERT (rp <= i1 && rp <= i2, "paths not inside document");
406   int old_mode= get_access_mode ();
407   if (get_init_string (MODE) == "src")
408     set_access_mode (DRD_ACCESS_SOURCE);
409   ::selection_correct (subtree (et, rp), i1 / rp, i2 / rp, o1, o2);
410   set_access_mode (old_mode);
411   o1= rp * o1; o2= rp * o2;
412 }
413 
414 path
selection_get_subtable(int & row1,int & col1,int & row2,int & col2)415 edit_select_rep::selection_get_subtable (
416   int& row1, int& col1, int& row2, int& col2)
417 {
418   path fp= find_subtable_selection (et, start (cur_sel), end (cur_sel),
419                                     row1, col1, row2, col2);
420   table_bound (fp, row1, col1, row2, col2);
421   return fp;
422 }
423 
424 selection
compute_selection(path p1,path p2)425 edit_select_rep::compute_selection (path p1, path p2) {
426   if (is_table_selection (et, p1, p2, true)) {
427     int row1, col1, row2, col2;
428     path fp= find_subtable_selection (et, p1, p2, row1, col1, row2, col2);
429     tree st= subtree (et, fp);
430     table_bound (fp, row1, col1, row2, col2);
431 
432     int i, j;
433     rectangle r (0, 0, 0, 0);
434     for (i=row1; i<=row2; i++)
435       for (j=col1; j<=col2; j++) {
436 	path cp= fp * ::table_search_cell (st, i, j);
437 	selection sel= eb->find_check_selection (cp * 0, cp * 1);
438 	if (sel->valid) {
439 	  rectangles rs= sel->rs;
440 	  if (r != rectangle (0, 0, 0, 0)) rs= rectangles (r, rs);
441 	  r= least_upper_bound (rs);
442 	}
443       }
444     return selection (rectangles (r), fp * 0, fp * 1);
445   }
446   else {
447     path p_start, p_end;
448     //cout << "Find " << p1 << " -- " << p2 << "\n";
449     selection_correct (p1, p2, p_start, p_end);
450     //cout << "Find " << p_start << " -- " << p_end << "\n";
451     selection sel= eb->find_check_selection (p_start, p_end);
452     //cout << "sel= " << sel << "\n";
453     return sel;
454   }
455 }
456 
457 selection
compute_selection(range_set sel)458 edit_select_rep::compute_selection (range_set sel) {
459   if (is_empty (sel)) return selection ();
460   int old_mode= set_access_mode (DRD_ACCESS_SOURCE);
461   // FIXME: instead of changing the access mode,
462   // we should already start with setting the correct DRD.
463   // For instance, when searching text using a popup window,
464   // the DRD is incorrect.  Consequently, matching text inside
465   // certain macros can become inaccessible, after which
466   // the entire macro is erroneously highlighted.
467   // Similar remark for the main routine for computing selections
468   rectangles rs;
469   for (int i=0; i+1<N(sel); i+=2) {
470     selection ssel= compute_selection (sel[i], sel[i+1]);
471     if (ssel->valid) rs << ssel->rs;
472   }
473   set_access_mode (old_mode);
474   return selection (rs, start (sel), end (sel));
475 }
476 
477 void
selection_get(selection & sel)478 edit_select_rep::selection_get (selection& sel) {
479   sel= compute_selection (cur_sel);
480 }
481 
482 void
selection_get(path & p1,path & p2)483 edit_select_rep::selection_get (path& p1, path& p2) {
484   if (selection_active_table ()) {
485     int row1, col1, row2, col2;
486     path fp= selection_get_subtable (row1, col1, row2, col2);
487     p1= fp * 0;
488     p2= fp * 1;
489   }
490   else selection_correct (start (cur_sel), end (cur_sel), p1, p2);
491   /*
492   selection sel; selection_get (sel);
493   p1= sel->start;
494   p2= sel->end;
495   */
496 }
497 
498 path
selection_get_start()499 edit_select_rep::selection_get_start () {
500   return start (cur_sel);
501 }
502 
503 path
selection_get_end()504 edit_select_rep::selection_get_end () {
505   return end (cur_sel);
506 }
507 
508 tree
selection_get()509 edit_select_rep::selection_get () {
510   if (!selection_active_any ()) return "";
511   if (selection_active_table ()) {
512     int row1, col1, row2, col2;
513     path fp= selection_get_subtable (row1, col1, row2, col2);
514     return table_get_subtable (fp, row1, col1, row2, col2);
515   }
516   else {
517     path p1, p2;
518     // cout << "Selecting...\n";
519     selection_get (p1, p2);
520     // cout << "Between paths: " << p1 << " and " << p2 << "\n";
521     tree t= selection_compute (et, p1, p2);
522     // cout << "Selection : " << t << "\n";
523     return simplify_correct (t);
524   }
525 }
526 
527 path
selection_get_path()528 edit_select_rep::selection_get_path () {
529   path p1, p2;
530   selection_get (p1, p2);
531   if (p2 == p1 && !is_empty (cur_sel))
532     return path_up (p1);
533   return common (p1, p2);
534 }
535 
536 path
selection_get_cursor_path()537 edit_select_rep::selection_get_cursor_path () {
538   if (!selection_active_any ()) return tp;
539   return start (et, selection_get_path ());
540 }
541 
542 tree
selection_get_env_value(string var)543 edit_select_rep::selection_get_env_value (string var) {
544   if (!selection_active_any ()) return get_env_value (var);
545   return get_env_value (var, selection_get_cursor_path ());
546 }
547 
548 /******************************************************************************
549 * Copy and paste
550 ******************************************************************************/
551 
552 void
selection_raw_set(string key,tree t)553 edit_select_rep::selection_raw_set (string key, tree t) {
554   (void) ::set_selection (key, t, "", "", "", "texmacs");
555 }
556 
557 tree
selection_raw_get(string key)558 edit_select_rep::selection_raw_get (string key) {
559   tree t; string s;
560   (void) ::get_selection (key, t, s, "texmacs");
561   return t;
562 }
563 
564 void
selection_set_start(path p)565 edit_select_rep::selection_set_start (path p) {
566   if (!selection_active_any ()) {
567     if (rp < start (cur_sel)) select (start (cur_sel), start (cur_sel));
568     else select (tp, tp);
569   }
570   if (is_nil (p)) selection_set_start (tp);
571   else if (path_less_eq (end (cur_sel), p)) select (p, p);
572   else if (rp < p) select (p, end (cur_sel));
573 }
574 
575 void
selection_set_end(path p)576 edit_select_rep::selection_set_end (path p) {
577   if (is_nil (p)) selection_set_end (tp);
578   else if (path_less_eq (p, start (cur_sel))) select (p, p);
579   else if (rp < p) select (start (cur_sel), p);
580 }
581 
582 void
selection_set_paths(path p1,path p2)583 edit_select_rep::selection_set_paths (path p1, path p2) {
584   if (is_nil (p1) || is_nil (p2)) selection_set_paths (tp, tp);
585   else if (path_less_eq (p2, p1)) select (p1, p1);
586   else if (rp < p1 && rp < p2) select (p1, p2);
587 }
588 
589 void
selection_set_range_set(range_set sel)590 edit_select_rep::selection_set_range_set (range_set sel) {
591   selection_set_paths (start (sel), end (sel));
592 }
593 
594 void
selection_set(string key,tree t,bool persistant)595 edit_select_rep::selection_set (string key, tree t, bool persistant) {
596   selecting= shift_selecting= false;
597   string mode= as_string (selection_get_env_value (MODE));
598   string lan = as_string (selection_get_env_value (MODE_LANGUAGE (mode)));
599   tree sel= tuple ("texmacs", t, mode, lan);
600   /* TODO: add mode="graphics" somewhere in the context of the <graphics>
601      tag. To be done when implementing the different embeddings for
602      nicely copying graphics into text, text into graphics, etc. */
603   string s, sh, sv;
604   if (key == "primary" || key == "mouse") {
605     if (selection_export == "verbatim") t= exec_verbatim (t, tp);
606     if (selection_export == "html") t= exec_html (t, tp);
607     if (selection_export == "latex") t= exec_latex (t, tp);
608     if ((selection_export == "latex") && (mode == "math"))
609       t= compound ("math", t);
610     if (selection_export != "default")
611       s= tree_to_generic (t, selection_export * "-snippet");
612     else {
613       s= tree_to_generic (t, "texmacs-snippet");
614 #ifdef QTTEXMACS
615       tree tmp;
616       tmp= exec_verbatim (t, tp);
617       sv= tree_to_generic (tmp, "verbatim-snippet");
618       //tmp= exec_html (t, tp);
619       //sh= tree_to_generic (tmp, "html-snippet");
620 #endif
621     }
622     s= selection_encode (lan, s);
623   }
624   if (::set_selection (key, sel, s, sv, sh, selection_export) && !persistant)
625     selection_cancel ();
626 }
627 
628 void
selection_set(tree t)629 edit_select_rep::selection_set (tree t) {
630   selection_set ("primary", t);
631 }
632 
633 void
selection_copy(string key)634 edit_select_rep::selection_copy (string key) {
635   if (inside_active_graphics ()) {
636     tree t= as_tree (eval ("(graphics-copy)"));
637     selection_set (key, t);
638     return;
639   }
640   if (selection_active_any ()) {
641     path old_tp= tp;
642     selection sel; selection_get (sel);
643     go_to (sel->end);
644     tree t= selection_get ();
645     go_to (sel->start);
646     selection_set (key, t);
647     go_to (old_tp);
648   }
649 }
650 
651 void
selection_paste(string key)652 edit_select_rep::selection_paste (string key) {
653   tree t; string s;
654   (void) ::get_selection (key, t, s, selection_import);
655   if (inside_active_graphics ()) {
656     if (is_tuple (t, "texmacs", 3))
657       call ("graphics-paste", t[1]);
658     return;
659   }
660   if (is_tuple (t, "extern", 1)) {
661     string mode= get_env_string (MODE);
662     string lan = get_env_string (MODE_LANGUAGE (mode));
663     string s   = selection_decode (lan, as_string (t[1]));
664     if (mode == "prog")
665       if (selection_import == "latex" || selection_import == "html")
666         selection_import= "verbatim";
667     if (mode == "math")
668       if (selection_import == "latex") {
669         while (starts (s, " ")) s= s(1, N(s));
670         while (ends (s, " ")) s= s(0, N(s)-1);
671         if (!starts (s, "$") && !ends (s, "$"))
672           s= "$" * s * "$";
673       }
674     string fm;
675     if (selection_import == "default") fm= "texmacs-snippet";
676     else fm= selection_import * "-snippet";
677     tree doc= generic_to_tree (s, fm);
678     if (is_func (doc, DOCUMENT, 1)) doc= doc[0]; // temporary fix
679     if (mode == "math" && is_compound (doc, "math", 1)) doc= doc[0];
680     insert_tree (doc);
681   }
682   if (is_tuple (t, "texmacs", 3)) {
683     string mode= get_env_string (MODE);
684     string lan = get_env_string (MODE_LANGUAGE (mode));
685     if (is_compound (t[1], "text", 1) && mode == "text")
686       t= tuple ("texmacs", t[1][0], "text", lan);
687     if (is_compound (t[1], "math", 1) && mode == "math")
688       t= tuple ("texmacs", t[1][0], "math", lan);
689     if (mode == "math" && t[2] == "text")
690       set_message ("Error: invalid paste of text into a formula", "paste");
691     else if (mode == "prog" && t[2] == "math") {
692       tree in= tuple (lan, t[1]);
693       tree r= stree_to_tree (call ("plugin-math-input", tree_to_stree (in)));
694       insert_tree (r);
695     }
696     else {
697       if ((t[2] != mode) && (t[2] != "src") && (mode != "src") &&
698 	  ((t[2] == "math") || (mode == "math"))) {
699         if (t[2] == "math")
700           insert_tree (compound ("math", ""), path (0, 0));
701         else if (t[2] == "text")
702           insert_tree (compound ("text", ""), path (0, 0));
703         else
704           insert_tree (tree (WITH, copy (MODE), copy (t[2]), ""), path (2, 0));
705       }
706       if (is_func (t[1], TFORMAT) || is_func (t[1], TABLE)) {
707 	int row, col;
708 	path fp= search_format (row, col);
709 	if (is_nil (fp)) insert_tree (compound (copy (TABULAR), t[1]));
710 	else table_write_subtable (fp, row, col, t[1]);
711       }
712       else insert_tree (t[1]);
713     }
714   }
715 }
716 
717 void
selection_clear(string key)718 edit_select_rep::selection_clear (string key) {
719   ::clear_selection (key);
720 }
721 
722 void
selection_cancel()723 edit_select_rep::selection_cancel () {
724   selecting= shift_selecting= false;
725   if (is_empty (cur_sel)) return;
726   select (start (cur_sel), start (cur_sel));
727 }
728 
729 void
selection_set_import(string fm)730 edit_select_rep::selection_set_import (string fm) {
731   selection_import= fm;
732 }
733 
734 void
selection_set_export(string fm)735 edit_select_rep::selection_set_export (string fm) {
736   selection_export= fm;
737 }
738 
739 string
selection_get_import()740 edit_select_rep::selection_get_import () {
741   return selection_import;
742 }
743 
744 string
selection_get_export()745 edit_select_rep::selection_get_export () {
746   return selection_export;
747 }
748 
749 /******************************************************************************
750 * Cutting the selection
751 ******************************************************************************/
752 
753 void
cut(path p)754 edit_select_rep::cut (path p) {
755   cut (start (et, p), end (et, p));
756 }
757 
758 void
cut(path p1,path p2)759 edit_select_rep::cut (path p1, path p2) {
760   path p = common (p1, p2);
761   tree st= subtree (et, p);
762   raw_cut (p1, p2);
763   if (!is_func (st, TFORMAT) &&
764       !is_func (st, TABLE) &&
765       !is_func (st, ROW) &&
766       !is_document (subtree (et, p)) &&
767       is_concat (subtree (et, path_up (p))))
768     correct_concat (path_up (p));
769 }
770 
771 void
raw_cut(path p1,path p2)772 edit_select_rep::raw_cut (path p1, path p2) {
773   if (p2 == p1) return;
774   path p = common (p1, p2);
775   tree t = subtree (et, p);
776   int  n = N(p);
777   int  i1= p1[n];
778   int  i2= p2[n];
779 
780   if (is_document (t) || is_concat (t)) {
781     path q1= copy (p); q1 << path (i1, end (t[i1]));
782     path q2= copy (p); q2 << path (i2, start (t[i2]));
783     raw_cut (q2, p2);
784     if (i2>i1+1) remove (p * (i1+1), i2-i1-1);
785     raw_cut (p1, q1);
786     if (is_concat (t)) correct_concat (p);
787     else remove_return (p * i1);
788     return;
789   }
790 
791   if (is_func (t, TFORMAT) || is_func (t, TABLE) || is_func (t, ROW)) {
792     path fp= ::table_search_format (et, p);
793     tree st= subtree (et, fp);
794     int row1, col1, row2, col2, nr_rows, nr_cols;
795     table_search_coordinates (st, tail (p1, N(fp)), row1, col1);
796     table_search_coordinates (st, tail (p2, N(fp)), row2, col2);
797     if (row1>row2) { int tmp= row1; row1= row2; row2= tmp; }
798     if (col1>col2) { int tmp= col1; col1= col2; col2= tmp; }
799     table_get_extents (fp, nr_rows, nr_cols);
800 
801     int i, j;
802     for (i=row1; i<=row2; i++)
803       for (j=col1; j<=col2; j++) {
804         path cp= fp * ::table_search_cell (st, i, j);
805         if (is_func (subtree (et, cp), CELL, 1)) cp= cp * 0;
806         assign (cp, "");
807       }
808     path cp= fp * ::table_search_cell (st, row1, col1);
809     go_to (cp * path (0, 0));
810 
811     if (is_func (st, TFORMAT))
812       table_del_format (fp, row1+1, col1+1, row2+1, col2+1, "");
813 
814     if (fp == search_format ()) {
815       if (col1 == 0 && col2 == nr_cols-1 && row2 > row1)
816         table_remove (fp, row1 + 1, col1, row2 - row1, 0);
817       else if (row1 == 0 && row2 == nr_rows-1 && col2 > col1)
818         table_remove (fp, row1, col1 + 1, 0, col2 - col1);
819       table_correct_block_content ();
820       table_resize_notify ();
821     }
822     else {
823       observer obs= position_new (tp);
824       go_to (start (et, fp * ::table_search_cell (st, row1, col1)));
825       table_correct_block_content ();
826       table_resize_notify ();
827       go_to (position_get (obs));
828       position_delete (obs);
829     }
830     return;
831   }
832 
833   if (is_compound (t) && (!is_format (t))) {
834     assign (p, "");
835     return;
836   }
837 
838   if ((N(p1) != (N(p)+1)) || (N(p2) != (N(p)+1))) {
839     failed_error << "t = " << t << "\n";
840     failed_error << "p = " << p << "\n";
841     failed_error << "p1= " << p1 << "\n";
842     failed_error << "p2= " << p2 << "\n";
843     FAILED ("invalid cut");
844   }
845 
846   if (is_atomic (t)) {
847     int pos= last_item (p1);
848     int nr = last_item (p2)-pos;
849     if (nr>0) remove (p1, nr);
850   }
851   else {
852     if ((last_item (p1) != 0) || (last_item (p2) != 1)) {
853       failed_error << "t = " << t << "\n";
854       failed_error << "p = " << p << "\n";
855       failed_error << "p1= " << p1 << "\n";
856       failed_error << "p2= " << p2 << "\n";
857       FAILED ("invalid object cut");
858     }
859     assign (p, "");
860   }
861 }
862 
863 void
selection_cut(string key)864 edit_select_rep::selection_cut (string key) {
865   if (inside_active_graphics ()) {
866     if (key != "none") {
867       tree t= as_tree (eval ("(graphics-cut)"));
868       selection_set (key, t);
869     }
870   }
871   else if (selection_active_any ()) {
872     path p1, p2;
873     if (selection_active_table ()) {
874       p1= start (cur_sel); p2= end (cur_sel);
875       if(key != "none") {
876         tree sel= selection_get ();
877         selection_set (key, sel);
878       }
879     }
880     else {
881       selection_get (p1, p2);
882       go_to (p2);
883       if (p2 == p1) return;
884       if (key != "none") {
885         tree sel= selection_compute (et, p1, p2);
886         selection_set (key, simplify_correct (sel));
887       }
888     }
889     cut (p1, p2);
890   }
891 }
892 
893 tree
selection_get_cut()894 edit_select_rep::selection_get_cut () {
895   tree t= selection_get ();
896   selection_cut ("none");
897   return t;
898 }
899 
900 void
selection_move()901 edit_select_rep::selection_move () {
902   observer pos= position_new (tp);
903   tree t= selection_get_cut ();
904   go_to (position_get (pos));
905   insert_tree (t);
906   position_delete (pos);
907 }
908 
909 /******************************************************************************
910 * Focus related routines
911 ******************************************************************************/
912 
913 path
manual_focus_get()914 edit_select_rep::manual_focus_get () {
915   return focus_p;
916 }
917 
918 void
manual_focus_set(path p,bool force)919 edit_select_rep::manual_focus_set (path p, bool force) {
920   //cout << "Set focus " << p << ", " << force << ", " << focus_hold << "\n";
921   if (is_nil (p) && focus_hold && !force) return;
922   focus_p= p;
923   focus_hold= !is_nil (p);
924 }
925 
926 void
manual_focus_release()927 edit_select_rep::manual_focus_release () {
928   focus_hold= false;
929 }
930 
931 path
focus_search(path p,bool skip_flag,bool up_flag)932 edit_select_rep::focus_search (path p, bool skip_flag, bool up_flag) {
933   //cout << "Search focus " << p << ", " << skip_flag << ", " << up_flag << "\n";
934   if (!(rp < p)) return rp;
935   tree st= subtree (et, p);
936   if (!skip_flag) return p;
937   if (none_accessible (st) && p == path_up (tp) && last_item (tp) != 0)
938     return p;
939   if (is_atomic (st) ||
940       is_func (st, DOCUMENT) ||
941       is_func (st, CONCAT) ||
942       is_func (st, TFORMAT) ||
943       is_func (st, TABLE) ||
944       is_func (st, ROW) ||
945       is_func (st, CELL) ||
946       is_compound (st, "shown") ||
947       is_func (st, HIDDEN) ||
948       is_compound (st, "shared") ||
949       up_flag)
950     return focus_search (path_up (p), skip_flag, false);
951   return p;
952 }
953 
954 path
focus_get(bool skip_flag)955 edit_select_rep::focus_get (bool skip_flag) {
956   //cout << "Search focus " << focus_p << ", " << skip_flag << "\n";
957   if (!is_nil (focus_p))
958     return focus_search (focus_p, skip_flag, false);
959   if (selection_active_any ())
960     return focus_search (selection_get_path (), skip_flag, false);
961   else
962     return focus_search (path_up (tp), skip_flag, true);
963 }
964 
965 /******************************************************************************
966 * Alternative selections
967 ******************************************************************************/
968 
969 void
set_alt_selection(string name,range_set sel)970 edit_select_rep::set_alt_selection (string name, range_set sel) {
971   if (alt_sels[name] != sel) {
972     alt_sels (name)= sel;
973     notify_change (THE_SELECTION);
974   }
975 }
976 
977 range_set
get_alt_selection(string name)978 edit_select_rep::get_alt_selection (string name) {
979   return alt_sels[name];
980 }
981 
982 void
cancel_alt_selection(string name)983 edit_select_rep::cancel_alt_selection (string name) {
984   if (alt_sels->contains (name)) {
985     alt_sels->reset (name);
986     notify_change (THE_SELECTION);
987   }
988 }
989 
990 void
cancel_alt_selections()991 edit_select_rep::cancel_alt_selections () {
992   if (N(alt_sels) > 0) {
993     alt_sels= hashmap<string,range_set> ();
994     notify_change (THE_SELECTION);
995   }
996 }
997