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