1 
2 /******************************************************************************
3 * MODULE     : edit_delete.cpp
4 * DESCRIPTION: treat deletions
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 "edit_text.hpp"
13 #include "tree_traverse.hpp"
14 #include "analyze.hpp"
15 
16 bool is_empty_cell (tree t);
17 
18 /******************************************************************************
19 * Getting the point where to delete
20 ******************************************************************************/
21 
22 void
get_deletion_point(path & p,int & last,int & rix,tree & t,tree & u,bool forward)23 edit_text_rep::get_deletion_point (
24   path& p, int& last, int& rix, tree& t, tree& u, bool forward)
25 {
26   // make right-glued positions left-glued
27   p= tp;
28   if (forward) {
29     //cout << HRULE;
30     if ((rp < p) && (N (p / rp) >= 2) &&
31 	is_concat (subtree (et, path_up (p, 2))) &&
32 	(last_item (p) == right_index (subtree (et, path_up (p)))) &&
33 	(last_item (path_up (p)) < (N (subtree (et, path_up (p, 2))) - 1)))
34       {
35 	p= path_up (p);
36 	p= path_inc (p) * start (subtree (et, path_inc (p)), path ());
37       }
38     //cout << "p= " << p << "\n";
39   }
40 
41   // get the position where to delete
42   last= last_item (p);
43   p   = path_up (p);
44   t   = subtree (et, p);
45   rix = right_index (t);
46   //cout << "  t   = " << t << "\n";
47   //cout << "  last= " << last << "\n";
48   //cout << "  rix = " << rix << "\n";
49   while (((forward && (last >= rix)) || ((!forward) && (last == 0))) &&
50 	 (rp < p) && is_format (subtree (et, path_up (p))))
51     {
52       last= last_item (p);
53       p   = path_up (p);
54       t   = subtree (et, p);
55       rix = N(t) - 1;
56       //cout << "  t   = " << t << "\n";
57       //cout << "  last= " << last << "\n";
58       //cout << "  rix = " << rix << "\n";
59     }
60   if (rp < p) u= subtree (et, path_up (p));
61 }
62 
63 /******************************************************************************
64 * Normal deletions
65 ******************************************************************************/
66 
67 static bool
is_multi_paragraph_or_sectional(tree t)68 is_multi_paragraph_or_sectional (tree t) {
69   if (is_atomic (t)) return false;
70   if (is_multi_paragraph (t)) return true;
71   eval ("(use-modules (utils library tree) (text text-drd))");
72   return as_bool (call ("tree-in?", t, call ("section-tag-list")));
73 }
74 
75 void
remove_text_sub(bool forward)76 edit_text_rep::remove_text_sub (bool forward) {
77   path p;
78   int  last, rix;
79   tree t, u;
80   get_deletion_point (p, last, rix, t, u, forward);
81 
82   // multiparagraph delete
83   if (is_document (t)) {
84     if ((forward && (last >= rix)) || ((!forward) && (last == 0))) {
85       if (rp < p) {
86 	tree u= subtree (et, path_up (p));
87 	if (is_func (u, _FLOAT) || is_func (u, WITH) ||
88 	    is_func (u, STYLE_WITH) || is_func (u, VAR_STYLE_WITH) ||
89 	    is_func (u, LOCUS) ||
90 	    is_extension (u))
91 	  {
92 	    if (is_extension (u) && (N(u) > 1)) {
93 	      int i, n= N(u);
94 	      bool empty= true;
95 	      for (i=0; i<n; i++)
96 		empty= empty && ((u[i]=="") || (u[i]==tree (DOCUMENT, "")));
97 	      if (!empty) {
98 		if (forward) go_to (next_valid (et, tp));
99 		else go_to (previous_valid (et, tp));
100 		return;
101 	      }
102 	    }
103 	    if (t == tree (DOCUMENT, "")) {
104 	      if (is_func (u, _FLOAT) || is_compound (u, "footnote", 1)) {
105 		assign (path_up (p), "");
106 		correct (path_up (p, 2));
107 	      }
108 	      else if (is_document (subtree (et, path_up (p, 2))))
109 		assign (path_up (p), "");
110 	      else assign (path_up (p), tree (DOCUMENT, ""));
111 	    }
112 	    else go_to_border (path_up (p), !forward);
113 	  }
114 	else if (is_func (u, TABLE) || is_func (u, SUBTABLE) ||
115 		 is_func (u, CELL) || is_func (u, ROW) ||
116 		 is_func (u, TFORMAT)) {
117 	  if (t == tree (DOCUMENT, ""))
118 	    back_in_table (u, p, forward);
119 	}
120       }
121       return;
122     }
123     else {
124       int l1= forward? last: last-1;
125       int l2= forward? last+1: last;
126       if (is_multi_paragraph_or_sectional (subtree (et, p * l1)) ||
127 	  is_multi_paragraph_or_sectional (subtree (et, p * l2)))
128 	{
129 	  if (subtree (et, p * l1) == "") remove (p * l1, 1);
130 	  else {
131 	    if (subtree (et, p * l2) == "") remove (p * l2, 1);
132 	    if (!forward) go_to_end (p * l1);
133 	    else if (last < N (subtree (et, p)) - 1) go_to_start (p * l2);
134 	  }
135 	}
136       else remove_return (p * l1);
137     }
138     return;
139   }
140 
141   // deleting text
142   if (forward && is_atomic (t) && (last != rix)) {
143     language lan= get_env_language ();
144     int end= last;
145     tm_char_forwards (t->label, end);
146     remove (p * last, end-last);
147     correct (path_up (p));
148     return;
149   }
150 
151   if ((!forward) && is_atomic (t) && (last != 0)) {
152     language lan= get_env_language ();
153     int start= last;
154     tm_char_backwards (t->label, start);
155     remove (p * start, last-start);
156     correct (path_up (p));
157     return;
158   }
159 
160   // deletion governed by parent t
161   if (last == (forward? 0: 1))
162     switch (L(t)) {
163     case RAW_DATA:
164     case HSPACE:
165     case VAR_VSPACE:
166     case VSPACE:
167     case SPACE:
168     case HTAB:
169       back_monolithic (p);
170       return;
171     case AROUND:
172     case VAR_AROUND:
173     case BIG_AROUND:
174       back_around (t, p, forward);
175       return;
176     case LEFT:
177     case MID:
178     case RIGHT:
179     case BIG:
180       back_monolithic (p);
181       return;
182     case LPRIME:
183     case RPRIME:
184       back_prime (t, p, forward);
185       return;
186     case WIDE:
187     case VAR_WIDE:
188       go_to_border (p * 0, forward);
189       return;
190     case TFORMAT:
191     case TABLE:
192     case ROW:
193     case CELL:
194     case SUBTABLE:
195       back_table (p, forward);
196       return;
197     case WITH:
198     case STYLE_WITH:
199     case VAR_STYLE_WITH:
200     case LOCUS:
201       go_to_border (p * (N(t) - 1), forward);
202       return;
203     case VALUE:
204     case QUOTE_VALUE:
205     case ARG:
206     case QUOTE_ARG:
207       if (N(t) == 1) back_monolithic (p);
208       else back_general (p, forward);
209       return;
210     default:
211       if (is_compound (t, "separating-space", 1)) back_monolithic (p);
212       else if (is_compound (t, "application-space", 1)) back_monolithic (p);
213       else back_general (p, forward);
214       break;
215     }
216 
217   // deletion depends on children u
218   if (last == (forward? rix: 0)) {
219     switch (L (u)) {
220     case AROUND:
221     case VAR_AROUND:
222     case BIG_AROUND:
223       back_in_around (u, p, forward);
224       return;
225     case LONG_ARROW:
226       back_in_long_arrow (u, p, forward);
227       return;
228     case WIDE:
229     case VAR_WIDE:
230       back_in_wide (u, p, forward);
231       return;
232     case TREE:
233       back_in_tree (u, p, forward);
234       return;
235     case TFORMAT:
236     case TABLE:
237     case ROW:
238     case CELL:
239     case SUBTABLE:
240       back_in_table (u, p, forward);
241       return;
242     case WITH:
243     case STYLE_WITH:
244     case VAR_STYLE_WITH:
245     case LOCUS:
246       back_in_with (u, p, forward);
247       return;
248     default:
249       if (is_graphical_text (u))
250         back_in_text_at (u, p, forward);
251       else if (is_compound (u, "cell-inert") ||
252                is_compound (u, "cell-input") ||
253                is_compound (u, "cell-output")) {
254         tree st= subtree (et, path_up (p, 2));
255         back_in_table (u, p, forward);
256       }
257       else
258         back_in_general (u, p, forward);
259       break;
260     }
261   }
262 }
263 
264 void
empty_document_fix()265 edit_text_rep::empty_document_fix () {
266   // FIXME: we might want to call this after arbitrary editing operations
267   tree rt= subtree (et, rp);
268   if (exists_accessible_inside (rt)) return;
269   if (!is_func (rt, DOCUMENT)) {
270     insert_node (rt, 0, DOCUMENT);
271     rt= subtree (et, rp);
272   }
273   int n= N(rt);
274   insert (rt, n, tree (DOCUMENT, ""));
275   go_to (rp * path (n, 0));
276 }
277 
278 void
remove_text(bool forward)279 edit_text_rep::remove_text (bool forward) {
280   remove_text_sub (forward);
281   empty_document_fix ();
282 }
283 
284 /******************************************************************************
285 * Structured deletions
286 ******************************************************************************/
287 
288 void
remove_structure(bool forward)289 edit_text_rep::remove_structure (bool forward) {
290   path p;
291   int  last, rix;
292   tree t, u;
293   get_deletion_point (p, last, rix, t, u, forward);
294 
295   // multiparagraph delete
296   if (!(rp < p)) {
297     if (forward) {
298       if (last >= rix) return;
299       remove_return (path (last));
300     }
301     else {
302       if (last == 0) return;
303       remove_return (path (last-1));
304     }
305     return;
306   }
307 
308   // deleting text
309   if (is_atomic (t) && (last != (forward? rix: 0))) {
310     language lan= get_env_language ();
311     int start= last, end= last, pos;
312     string s= t->label;
313     while (true) {
314       if (forward) {
315 	pos= start;
316 	(void) lan->advance (t, pos);
317 	if (pos <= last) break;
318       }
319       else {
320 	int pos= max (start-1, 0);
321 	(void) lan->advance (t, pos);
322 	if (pos < last) break;
323       }
324       end= pos;
325       if (start == 0) break;
326       start--;
327     }
328     if (forward) {
329       start= min (start+1, last);
330       while ((end < N(s)) && (s[end] == ' ')) end++;
331     }
332     else while ((start>0) && (s[start-1] == ' ')) start--;
333     if (end>start) {
334       remove (p * start, end-start);
335       correct (path_up (p));
336     }
337     return;
338   }
339 
340   // deleting structure
341   if (forward) {
342     if (is_concat (t) && (last < rix)) {
343       remove (p * (last+1), 1);
344       correct (path_up (p));
345     }
346     else if (is_compound (t) && (last == 0)) {
347       assign (p, "");
348       correct (path_up (p));
349     }
350     else remove_structure_upwards ();
351   }
352   else {
353     if (last==1) {
354       if (!is_concat (u)) assign (p, "");
355       else remove (p, 1);
356       correct (path_up (p));
357     }
358     else remove_structure_upwards ();
359   }
360 }
361 
362 /******************************************************************************
363 * Deletion of an object
364 ******************************************************************************/
365 
366 void
remove_structure_upwards()367 edit_text_rep::remove_structure_upwards () {
368   path p= path_up (tp);
369   while ((rp < p) && is_format (subtree (et, path_up (p)))) p= path_up (p);
370   if (!(rp < p)) return;
371   int last= last_item (p);
372   p= path_up (p);
373   tree st= subtree (et, p);
374   if (is_func (st, AROUND, 3) ||
375       is_func (st, VAR_AROUND, 3) ||
376       is_func (st, BIG_AROUND, 2))
377     pre_remove_around (p);
378   bool recurse=
379     is_func (st, TFORMAT) || is_func (st, TABLE) ||
380     is_func (st, ROW) || is_func (st, CELL) ||
381     is_compound (st, "shown") ||
382     drd->var_without_border (L(st));
383   remove (p * (last+1), N(st)-(last+1));
384   remove (p * 0, last);
385 
386   do {
387     remove_node (p * 0);
388     last= last_item (p);
389     p= path_up (p);
390     st= subtree (et, p);
391   } while (is_mod_active_once (st));
392 
393   if (is_document (st) && is_document (st[last])) {
394     int very_last= 0;
395     if ((N(tp) >= N(p)+2) && (tp[N(p)] == last)) very_last= tp[N(p)+1];
396     tree left = st[last] (0, very_last);
397     tree right= st[last] (very_last+1, N(st[last]));
398     remove (p * path (last, very_last+1), N(st[last])- (very_last+1));
399     remove (p * path (last, 0), very_last);
400     remove_node (p * path (last, 0));
401     insert (p * (last+1), right);
402     insert (p * last, left);
403   }
404   else correct (p);
405 
406   if (recurse) remove_structure_upwards ();
407 }
408