1 
2 /******************************************************************************
3 * MODULE     : edit_math.cpp
4 * DESCRIPTION: modify mathematical structures
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_math.hpp"
13 #include "analyze.hpp"
14 
15 /******************************************************************************
16 * Constructors and destructors
17 ******************************************************************************/
18 
edit_math_rep()19 edit_math_rep::edit_math_rep () {}
~edit_math_rep()20 edit_math_rep::~edit_math_rep () {}
21 
22 /******************************************************************************
23 * Making mathematical objects
24 ******************************************************************************/
25 
26 void
make_rigid()27 edit_math_rep::make_rigid () {
28   if (selection_active_small ())
29     insert_tree (tree (RIGID, selection_get_cut ()));
30   else {
31     insert_tree (tree (RIGID, ""), path (0, 0));
32     set_message ("move to the right when finished", "group");
33   }
34 }
35 
36 void
make_lprime(string s)37 edit_math_rep::make_lprime (string s) {
38   tree& st= subtree (et, path_up (tp));
39   if (is_func (st, LPRIME, 1) && (last_item (tp) == 1)) {
40     if (is_atomic (st[0]))
41       insert (path_up (tp) * path (0, N (st[0]->label)), s);
42   }
43   else insert_tree (tree (LPRIME, s));
44 }
45 
46 void
make_rprime(string s)47 edit_math_rep::make_rprime (string s) {
48   tree& st= subtree (et, path_up (tp));
49   if (is_func (st, RPRIME, 1) && (last_item (tp) == 1)) {
50     if (is_atomic (st[0]))
51       insert (path_up (tp) * path (0, N (st[0]->label)), s);
52   }
53   else insert_tree (tree (RPRIME, s));
54 }
55 
56 void
make_below()57 edit_math_rep::make_below () {
58   if (selection_active_small ()) {
59     insert_tree (tree (BELOW, selection_get_cut (), ""), path (1, 0));
60     set_message ("type script, move right", "under");
61   }
62   else {
63     insert_tree (tree (BELOW, "", ""), path (0, 0));
64     set_message ("type body, move down, type script", "under");
65   }
66 }
67 
68 void
make_above()69 edit_math_rep::make_above () {
70   if (selection_active_small ()) {
71     insert_tree (tree (ABOVE, selection_get_cut (), ""), path (1, 0));
72     set_message ("type script, move right", "above");
73   }
74   else {
75     insert_tree (tree (ABOVE, "", ""), path (0, 0));
76     set_message ("type body, move up, type script", "above");
77   }
78 }
79 
80 void
make_script(bool sup,bool right)81 edit_math_rep::make_script (bool sup, bool right) {
82   tree_label s (sup? SUP (right): SUB (right));
83   if (selection_active_small ())
84     insert_tree (tree (s, selection_get_cut ()));
85   else {
86     path   p= path_up (tp);
87     tree   t= subtree (et, p);
88     bool   flag;
89 
90     if (is_format (p))
91       FAILED ("bad cursor position");
92     if (is_script (t, flag) && (flag==right) && (L(t)==s)) {
93       go_to_end (p * 0);
94       return;
95     }
96     insert_tree (tree (s, ""), path (0, 0));
97     set_message ("move to the right when finished",
98 		 (char*) (sup? (right? "superscript": "left superscript"):
99 			       (right? "subscript": "left subscript")));
100   }
101 }
102 
103 void
make_fraction()104 edit_math_rep::make_fraction () {
105   if (selection_active_small ()) {
106     insert_tree (tree (FRAC, selection_get_cut (), ""), path (1, 0));
107     set_message ("type denominator, move right", "fraction");
108   }
109   else {
110     insert_tree (tree (FRAC, "", ""), path (0, 0));
111     set_message ("type numerator, move down, type denominator", "fraction");
112   }
113 }
114 
115 void
make_sqrt()116 edit_math_rep::make_sqrt () {
117   if (selection_active_small ())
118     insert_tree (tree (SQRT, selection_get_cut ()));
119   else {
120     insert_tree (tree (SQRT, ""), path (0, 0));
121     set_message ("move to the right when finished", "square root");
122   }
123 }
124 
125 void
make_var_sqrt()126 edit_math_rep::make_var_sqrt () {
127   if (selection_active_small ()) {
128     tree t= selection_get_cut ();
129     if (is_func (t, SQRT, 1))
130       insert_tree (tree (SQRT, t[0], ""), path (1, 0));
131     else insert_tree (tree (SQRT, t, ""), path (1, 0));
132   }
133   else {
134     insert_tree (tree (SQRT, "", ""), path (0, 0));
135     set_message (concat (kbd ("left"), ": set n",
136 			 kbd ("right"), ": when finished"),
137 		 "n-th root");
138   }
139 }
140 
141 void
make_wide(string wide)142 edit_math_rep::make_wide (string wide) {
143   if (selection_active_small ())
144     insert_tree (tree (WIDE, selection_get_cut (), wide));
145   else {
146     insert_tree (tree (WIDE, "", wide), path (0, 0));
147     set_message ("move to the right when finished", "wide accent");
148   }
149 }
150 
151 void
make_wide_under(string wide)152 edit_math_rep::make_wide_under (string wide) {
153   if (selection_active_small ())
154     insert_tree (tree (VAR_WIDE, selection_get_cut (), wide));
155   else {
156     insert_tree (tree (VAR_WIDE, "", wide), path (0, 0));
157     set_message ("move to the right when finished", "wide under accent");
158   }
159 }
160 
161 void
make_neg()162 edit_math_rep::make_neg () {
163   if (selection_active_small ())
164     insert_tree (tree (NEG, selection_get_cut ()));
165   else {
166     insert_tree (tree (NEG, ""), path (0, 0));
167     set_message ("move to the right when finished", "negation");
168   }
169 }
170 
171 /******************************************************************************
172 * Deleting mathematical objects
173 ******************************************************************************/
174 
175 static bool
is_deleted(tree t)176 is_deleted (tree t) {
177   return t == "<nobracket>" || t == tree (LEFT, ".") || t == tree (RIGHT, ".");
178 }
179 
180 void
back_around(tree t,path p,bool forward)181 edit_math_rep::back_around (tree t, path p, bool forward) {
182   bool match= (get_preference ("automatic brackets") != "off");
183   if (is_func (t, BIG_AROUND)) {
184     if (match || forward)
185       go_to_border (p * 1, forward);
186     else {
187       remove_node (t, 1);
188       correct (path_up (p));
189     }
190   }
191   else {
192     int i= (forward? 0: 2);
193     if (is_deleted (t[i]));
194     else if (is_atomic (t[i]))
195       assign (t[i], "<nobracket>");
196     else if (is_func (t[i], LEFT))
197       assign (t[i], tree (LEFT, "."));
198     else if (is_func (t[i], RIGHT))
199       assign (t[i], tree (RIGHT, "."));
200     go_to_border (p * 1, forward);
201     if (is_deleted (t[0]) && is_deleted (t[2])) {
202       remove_node (t, 1);
203       correct (path_up (p));
204     }
205   }
206   if (!match) call ("brackets-refresh");
207 }
208 
209 void
back_in_around(tree t,path p,bool forward)210 edit_math_rep::back_in_around (tree t, path p, bool forward) {
211   bool match= (get_preference ("automatic brackets") != "off");
212   if (is_empty (t[1]) && match) {
213     assign (t, "");
214     correct (path_up (p, 2));
215   }
216   else if (is_func (t, BIG_AROUND)) {
217     if (match || forward)
218       go_to_border (path_up (p), !forward);
219     else {
220       remove_node (t, 1);
221       correct (path_up (p, 2));
222     }
223   }
224   else {
225     int i= (forward? 2: 0);
226     if (is_deleted (t[i]));
227     else if (is_atomic (t[i]))
228       assign (t[i], "<nobracket>");
229     else if (is_func (t[i], LEFT))
230       assign (t[i], tree (LEFT, "."));
231     else if (is_func (t[i], RIGHT))
232       assign (t[i], tree (RIGHT, "."));
233     go_to_border (path_up (p), !forward);
234     if (is_deleted (t[0]) && is_deleted (t[2])) {
235       remove_node (t, 1);
236       correct (path_up (p, 2));
237     }
238   }
239   if (!match) call ("brackets-refresh");
240 }
241 
242 void
back_in_long_arrow(tree t,path p,bool forward)243 edit_math_rep::back_in_long_arrow (tree t, path p, bool forward) {
244   int i= last_item (p);
245   if (i == 2) {
246     if (is_empty (t[2])) remove (path_up (p) * 2, 1);
247     if (forward) go_to_border (path_up (p), !forward);
248     else go_to_border (path_up (p) * 1, forward);
249   }
250   else if (i == 1) {
251     if (N(t) == 2 && is_empty (t[1])) {
252       assign (path_up (p), "");
253       correct (path_up (p, 2));
254     }
255     else if (forward && N(t) >= 3)
256       go_to_border (path_up (p) * 2, forward);
257     else go_to_border (path_up (p), !forward);
258   }
259   else go_to_border (path_up (p), !forward);
260 }
261 
262 void
back_prime(tree t,path p,bool forward)263 edit_math_rep::back_prime (tree t, path p, bool forward) {
264   if ((N(t) == 1) && is_atomic (t[0])) {
265     string s= t[0]->label;
266     if (forward) {
267       int i= 0, n= N(s);
268       tm_char_forwards (s, i);
269       if (i >= n) {
270 	assign (p, "");
271 	correct (path_up (p));
272       }
273       else remove (p * path (0, 0), i);
274     }
275     else {
276       int n= N(s), i= n;
277       tm_char_backwards (s, i);
278       if (i <= 0) {
279 	assign (p, "");
280 	correct (path_up (p));
281       }
282       else remove (p * path (0, i), n-i);
283     }
284   }
285 }
286 
287 void
back_in_wide(tree t,path p,bool forward)288 edit_math_rep::back_in_wide (tree t, path p, bool forward) {
289   int i= last_item (p);
290   if ((i == 0) && is_empty (t[0])) {
291     assign (path_up (p), "");
292     correct (path_up (p, 2));
293   }
294   else go_to_border (path_up (p), !forward);
295 }
296 
297 void
pre_remove_around(path p)298 edit_math_rep::pre_remove_around (path p) {
299   tree st= subtree (et, p);
300   if (is_script (st[1]) || is_prime (st[1])) assign (st[1], "");
301   else if (is_concat (st[1]) && N(st[1]) > 0) {
302     int li= 0, ri= N(st[1])-1;
303     while (li<N(st[1])) {
304       tree sst= st[1][li];
305       if (!is_func (sst, RSUB) &&
306 	  !is_func (sst, RSUP) &&
307 	  !is_func (sst, RPRIME))
308 	break;
309       li++;
310     }
311     while (ri >= 0) {
312       tree sst= st[1][ri];
313       if (!is_func (sst, LSUB) &&
314 	  !is_func (sst, LSUP) &&
315 	  !is_func (sst, LPRIME))
316 	break;
317       ri--;
318     }
319     if (ri != N(st[1])-1) remove (p * path (1, ri+1), N(st[1])-1-ri);
320     if (li != 0) remove (p * path (1, 0), li);
321     correct (p * 1);
322   }
323 }
324 
325 /******************************************************************************
326 * Trees
327 ******************************************************************************/
328 
329 void
make_tree()330 edit_math_rep::make_tree () {
331   if (selection_active_small ())
332     insert_tree (tree (TREE, selection_get_cut (), ""), path (1, 0));
333   else {
334     insert_tree (tree (TREE, "", ""), path (0, 0));
335     set_message (concat (kbd_shortcut ("(structured-insert-right)"),
336 			 ": insert a new branch"),
337 		 "tree");
338   }
339 }
340 
341 void
back_in_tree(tree t,path p,bool forward)342 edit_math_rep::back_in_tree (tree t, path p, bool forward) {
343   int i= last_item (p);
344   if (i>0) {
345     if ((i>0) && (t[i] == "")) {
346       path q= path_up (p);
347       if (N (t) == 2) {
348 	assign (q, t[0]);
349 	correct (path_up (q));
350       }
351       else {
352 	remove (q * i, 1);
353 	if (forward) {
354 	  if (i == N (subtree (et, q))) go_to_end (q);
355 	  else go_to_start (q * i);
356 	}
357       }
358     }
359     else if (!forward) go_to_end (path_up (p) * (i-1));
360     else if (i == N(t)-1) go_to_end (path_up (p));
361     else go_to_start (path_up (p) * (i+1));
362   }
363   else {
364     if (t == tree (TREE, "", "")) {
365       p= path_up (p);
366       assign (p, "");
367       correct (path_up (p));
368     }
369     else if (forward) go_to_start (path_inc (p));
370     else go_to_start (path_up (p));
371   }
372 }
373