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