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