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