1
2 /******************************************************************************
3 * MODULE : edit_text.cpp
4 * DESCRIPTION: modification of text
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 "file.hpp"
14 #include "analyze.hpp"
15 #include "scheme.hpp"
16
17 /******************************************************************************
18 * Constructors and destructors
19 ******************************************************************************/
20
edit_text_rep()21 edit_text_rep::edit_text_rep () {}
~edit_text_rep()22 edit_text_rep::~edit_text_rep () {}
23
24 /******************************************************************************
25 * Correction routines for the edit tree
26 ******************************************************************************/
27
28 void
correct_concat(path p,int done)29 edit_text_rep::correct_concat (path p, int done) {
30 tree t (subtree (et, p));
31 if (L(t) != CONCAT) {
32 failed_error << "The tree was '" << t << "'\n";
33 FAILED ("concat expected");
34 }
35
36 int i, n= N(t);
37 if (n == 0) { assign (p, string ("")); return; }
38 if (n == 1) { remove_node (p * 0); return; }
39 for (i=done; i<n; i++) {
40 if (t[i] == "") {
41 remove (p * i, 1);
42 correct_concat (p, i);
43 return;
44 }
45 if ((i<n-1) && is_atomic (t[i]) && is_atomic (t[i+1])) {
46 join (p * i);
47 correct_concat (p, i);
48 return;
49 }
50 if (is_concat (t[i])) {
51 insert_node (p * 0, CONCAT);
52 split (p * path (0, i));
53 split (p * path (1, 1));
54 remove_node (p * path (1, 0));
55 if (subtree (et, p * 0) == tree (CONCAT)) remove (p * 0, 1);
56 else join (p * 0);
57 if (subtree (et, p * 1) == tree (CONCAT)) remove (p * 1, 1);
58 else join (p * 0);
59 remove_node (p * 0);
60 correct_concat (p, max (i-1, 0));
61 return;
62 }
63 else if (is_multi_paragraph (t[i]) &&
64 is_document (subtree (et, path_up (p))))
65 {
66 if ((i+1)<n) {
67 split (p * (i+1));
68 correct_concat (path_inc (p));
69 }
70 if (i==0) remove_node (p * 0);
71 else {
72 split (p * i);
73 remove_node (path_inc (p) * 0);
74 correct_concat (p);
75 }
76 return;
77 }
78 }
79 }
80
81 void
correct(path p)82 edit_text_rep::correct (path p) {
83 tree t (subtree (et, p));
84 if (L(t) == CONCAT) correct_concat (p);
85 if (t == "") correct (path_up (p));
86 }
87
88 /******************************************************************************
89 * Inserting line breaks (text-mode only)
90 ******************************************************************************/
91
92 bool
pure_line(path p)93 edit_text_rep::pure_line (path p) {
94 p= path_up (p);
95 tree st= subtree (et, path_up (p));
96 return
97 is_document (st) ||
98 ((is_func (st, WITH) || is_func (st, LOCUS) ||
99 is_func (st, CANVAS) || is_func (st, ORNAMENT)) &&
100 (last_item (p) == (N(st)-1)) && pure_line (p)) ||
101 (is_extension (st) && (last_item (p) >= 0) && pure_line (p));
102 }
103
104 bool
accepts_return(path p)105 edit_text_rep::accepts_return (path p) {
106 tree st= subtree (et, path_up (p));
107 return
108 is_document (st) ||
109 (is_func (st, SURROUND, 3) && (last_item (p) == 2)) ||
110 (is_func (st, _FLOAT) && (last_item (p) == (N(st)-1))) ||
111 (is_func (st, DATOMS) &&
112 (last_item (p) == (N(st)-1)) && pure_line (p)) ||
113 (is_func (st, MACRO) && (last_item (p) == (N(st)-1))) ||
114 (is_func (st, XMACRO, 2) && (last_item (p) == 1)) ||
115 ((is_func (st, WITH) || is_mod_active (st) ||
116 is_func (st, STYLE_WITH) || is_func (st, VAR_STYLE_WITH) ||
117 is_func (st, LOCUS) ||
118 is_func (st, CANVAS) || is_func (st, ORNAMENT)) &&
119 (last_item (p) == (N(st)-1)) && pure_line (p)) ||
120 (is_extension (st) && (last_item (p) >= 0) && pure_line (p));
121 }
122
123 bool
insert_return()124 edit_text_rep::insert_return () {
125 if (accepts_return (path_up (tp)))
126 insert_node (path_up (tp) * 0, CONCAT);
127 path p= path_up (tp, 2);
128 if (!is_concat (subtree (et, p))) return true;
129 if (!accepts_return (p)) return true;
130 if (!is_document (subtree (et, path_up (p)))) {
131 insert_node (p * 0, DOCUMENT);
132 p= path_up (tp, 2);
133 }
134
135 if (is_atomic (subtree (et, path_up (tp)))) split (tp);
136 if (last_item (tp)==right_index (subtree (et, path_up (tp))))
137 split (path_inc (path_up (tp)));
138 else split (path_up (tp));
139 go_to (correct_cursor (et, path_inc (p) * 0));
140 correct_concat (p);
141 correct_concat (path_inc (p));
142 return false;
143 }
144
145 void
remove_return(path p)146 edit_text_rep::remove_return (path p) {
147 if (!is_document (subtree (et, path_up (p))))
148 FAILED ("parent is not a document");
149
150 if (!is_concat (subtree (et, p)))
151 insert_node (p * 0, CONCAT);
152 if (!is_concat (subtree (et, path_inc (p))))
153 insert_node (path_inc (p) * 0, CONCAT);
154 join (p);
155 correct_concat (p);
156 }
157
158 /******************************************************************************
159 * Inserting a simple formula or tree
160 ******************************************************************************/
161
162 path
prepare_for_insert()163 edit_text_rep::prepare_for_insert () {
164 path p = path_up (tp);
165 int l = last_item (tp);
166 tree st= subtree (et, p);
167
168 if ((!is_document (st)) && is_multi_paragraph (st)) {
169 if (!is_document (subtree (et, path_up (p))))
170 insert_node (p * 0, DOCUMENT);
171 else {
172 if (l == 0) {
173 insert (p, tree (DOCUMENT, ""));
174 go_to (p * 0);
175 }
176 else {
177 insert (path_inc (p), tree (DOCUMENT, ""));
178 go_to (path_inc (p) * 0);
179 }
180 }
181 return prepare_for_insert ();
182 }
183
184 if ((rp < p) && is_concat (subtree (et, path_up (p)))) {
185 if (l==0) return p;
186 if (is_compound (st) || (l==N(st->label))) return path_inc (p);
187 split (tp);
188 return path_inc (p);
189 }
190
191 insert_node (p * 0, CONCAT);
192 if (is_atomic (st) && (l!=0) && (l!=N(st->label))) {
193 split (p * path (0, l));
194 return p * 1;
195 }
196 return p * ((l==0)? 0: 1);
197 }
198
199 void
insert_tree(tree t,path p_in_t)200 edit_text_rep::insert_tree (tree t, path p_in_t) {
201 if (!as_bool (call ("like-emacs?"))) selection_cut ("none");
202 if (is_atomic (t) && (p_in_t == end (t)) &&
203 is_atomic (subtree (et, path_up (tp))))
204 insert (tp, t);
205 else if (is_document (t)) {
206 if (subtree (et, path_up (tp)) == "" &&
207 accepts_return (path_up (tp)) &&
208 !is_func (subtree (et, path_up (tp, 2)), DOCUMENT))
209 insert_node (path_up (tp) * 0, DOCUMENT);
210 if (insert_return ()) return;
211 path p= search_parent_upwards (DOCUMENT);
212 bool empty= (subtree (et, p) == "");
213 if (empty) remove (p, 1);
214 insert (p, t);
215 path q= path_add (p, p_in_t->item) * p_in_t->next;
216 go_to (correct_cursor (et, q));
217 remove_return (path_dec (p));
218 if (!empty) remove_return (path_add (p, N(t)-2));
219 }
220 else if (is_multi_paragraph (t)) {
221 if (subtree (et, path_up (tp)) == "" &&
222 accepts_return (path_up (tp)) &&
223 !is_func (subtree (et, path_up (tp, 2)), DOCUMENT))
224 insert_node (path_up (tp) * 0, DOCUMENT);
225 if (make_return_after ()) return;
226 path p= search_parent_upwards (DOCUMENT);
227 if (subtree (et, p) == "") remove (p, 1);
228 insert (p, tree (DOCUMENT, t));
229 go_to (correct_cursor (et, p * p_in_t));
230 }
231 else {
232 path p= prepare_for_insert ();
233 if (!is_concat (t)) { t= tree (CONCAT, t); p_in_t= path (0, p_in_t); }
234 insert (p, t);
235 path q= path_add (p, p_in_t->item) * p_in_t->next;
236 go_to (correct_cursor (et, q));
237 correct_concat (path_up (p));
238 }
239 }
240
241 void
insert_tree(tree t)242 edit_text_rep::insert_tree (tree t) {
243 t= copy (simplify_correct (t));
244 insert_tree (t, end (t));
245 }
246
247 void
var_insert_tree(tree t,path p_in_t)248 edit_text_rep::var_insert_tree (tree t, path p_in_t) {
249 if (selection_active_any ()) {
250 selection_cut ("primary");
251 insert_tree (t, p_in_t);
252 selection_paste ("primary");
253 }
254 else insert_tree (t, p_in_t);
255 }
256
257 /******************************************************************************
258 * Spaces
259 ******************************************************************************/
260
261 static string
get_unit(tree t)262 get_unit (tree t) {
263 int i;
264 string s= as_string (t);
265 for (i=0; i<N(s); i++)
266 if ((s[i]>='a') && (s[i]<='z')) break;
267 return s (i, N(s));
268 }
269
270 static string
get_quantity(tree t)271 get_quantity (tree t) {
272 int i;
273 string s= as_string (t);
274 for (i=0; i<N(s); i++)
275 if ((s[i]>='a') && (s[i]<='z')) break;
276 return s (0, i);
277 }
278
279 void
make_space(tree u)280 edit_text_rep::make_space (tree u) {
281 if (is_atomic (u)) return;
282 tree t= subtree (et, path_up (tp));
283
284 int i, n= N(u);
285 bool flag= is_func (t, L (u), n);
286 for (i=0; i<n; i++) {
287 if (!flag) break;
288 string u1= get_unit (t[i]);
289 string u2= get_unit (t[0]);
290 double x1= as_double (get_quantity (t[i]));
291 double x2= as_double (get_quantity (u[i]));
292 flag= flag && (u1==u2) && ((x1*x2)>0);
293 }
294
295 if (flag) {
296 for (i=0; i<n; i++) {
297 double w1= as_double (get_quantity (t[i]));
298 double w2= as_double (get_quantity (u[i]));
299 string sum= as_string (w1+w2) * get_unit (t[i]);
300 assign (path_up (tp) * i, sum);
301 }
302 }
303 else insert_tree (u);
304 }
305
306 void
make_space(string w)307 edit_text_rep::make_space (string w) {
308 make_space (tree (SPACE, w));
309 }
310
311 void
make_space(string w,string y1,string y2)312 edit_text_rep::make_space (string w, string y1, string y2) {
313 insert_tree (tree (SPACE, w, y1, y2));
314 }
315
316 void
make_hspace(string s)317 edit_text_rep::make_hspace (string s) {
318 make_space (tree (HSPACE, s));
319 }
320
321 void
make_hspace(string smin,string sdef,string smax)322 edit_text_rep::make_hspace (string smin, string sdef, string smax) {
323 make_space (tree (HSPACE, smin, sdef, smax));
324 }
325
326 void
make_vspace_before(string s)327 edit_text_rep::make_vspace_before (string s) {
328 make_space (tree (VAR_VSPACE, s));
329 }
330
331 void
make_vspace_before(string smin,string sdef,string smax)332 edit_text_rep::make_vspace_before (string smin, string sdef, string smax) {
333 make_space (tree (VAR_VSPACE, smin, sdef, smax));
334 }
335
336 void
make_vspace_after(string s)337 edit_text_rep::make_vspace_after (string s) {
338 make_space (tree (VSPACE, s));
339 }
340
341 void
make_vspace_after(string smin,string sdef,string smax)342 edit_text_rep::make_vspace_after (string smin, string sdef, string smax) {
343 make_space (tree (VSPACE, smin, sdef, smax));
344 }
345
346 /******************************************************************************
347 * Insert formatting objects and miscellaneous
348 ******************************************************************************/
349
350 void
make_htab(string spc)351 edit_text_rep::make_htab (string spc) {
352 insert_tree (tree (HTAB, spc));
353 }
354
355 void
make_image(string file_name,bool link,string w,string h,string x,string y)356 edit_text_rep::make_image (
357 string file_name, bool link, string w, string h, string x, string y)
358 {
359 url image= url_unix (file_name);
360 string type= "";
361 tree t (IMAGE);
362 if (link) {
363 if (is_rooted (image, "default")) image= reroot (image, "file");
364 t << as_string (image, URL_STANDARD);
365 }
366 else {
367 string s;
368 tree vim= verbatim (as_string (image));
369 load_string (relative (get_name (), image), s, false);
370 if (s == "") {
371 set_message (concat ("File '", vim, "' not found"), "make image");
372 return;
373 }
374 type= suffix (image);
375 if (type == "") {
376 set_message (concat ("File '", vim, "' is not an image"), "make image");
377 return;
378 }
379 t << tuple (tree (RAW_DATA, s), type);
380 }
381 t << tree (w) << tree (h) << tree (x) << tree (y);
382 insert_tree (t);
383 }
384