1 /*******************************************************************************
2 * Copyright (c) 2013-2021, Andrés Martinelli <andmarti@gmail.com> *
3 * All rights reserved. *
4 * *
5 * This file is a part of SC-IM *
6 * *
7 * SC-IM is a spreadsheet program that is based on SC. The original authors *
8 * of SC are James Gosling and Mark Weiser, and mods were later added by *
9 * Chuck Martin. *
10 * *
11 * Redistribution and use in source and binary forms, with or without *
12 * modification, are permitted provided that the following conditions are met: *
13 * 1. Redistributions of source code must retain the above copyright *
14 * notice, this list of conditions and the following disclaimer. *
15 * 2. Redistributions in binary form must reproduce the above copyright *
16 * notice, this list of conditions and the following disclaimer in the *
17 * documentation and/or other materials provided with the distribution. *
18 * 3. All advertising materials mentioning features or use of this software *
19 * must display the following acknowledgement: *
20 * This product includes software developed by Andrés Martinelli *
21 * <andmarti@gmail.com>. *
22 * 4. Neither the name of the Andrés Martinelli nor the *
23 * names of other contributors may be used to endorse or promote products *
24 * derived from this software without specific prior written permission. *
25 * *
26 * THIS SOFTWARE IS PROVIDED BY ANDRES MARTINELLI ''AS IS'' AND ANY *
27 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
28 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
29 * DISCLAIMED. IN NO EVENT SHALL ANDRES MARTINELLI BE LIABLE FOR ANY *
30 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;*
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
33 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE *
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
36 *******************************************************************************/
37
38 /**
39 * \file cmds_normal.c
40 * \author Andrés Martinelli <andmarti@gmail.com>
41 * \date 2017-07-18
42 * \brief TODO Write a tbrief file description.
43 */
44
45 #include <ctype.h>
46 #include <stdlib.h>
47
48 #include "yank.h"
49 #include "marks.h"
50 #include "cmds.h"
51 #include "conf.h"
52 #include "tui.h"
53 #include "cmds_edit.h"
54 #include "history.h"
55 #include "hide_show.h"
56 #include "shift.h"
57 #include "main.h" // for sig_winchg
58 #include "interp.h"
59 #include "freeze.h"
60 #include "utils/extra.h"
61 #ifdef UNDO
62 #include "undo.h"
63 #endif
64
65
66 #include "dep_graph.h"
67 extern graphADT graph;
68 extern char valores;
69 extern int cmd_multiplier;
70 extern void start_visualmode(int tlrow, int tlcol, int brrow, int brcol);
71 extern void ins_in_line(wint_t d);
72 extern void openfile_under_cursor(int r, int c);
73
74 extern wchar_t interp_line[BUFFERSIZE];
75
76 #ifdef HISTORY_FILE
77 extern struct history * commandline_history;
78 #endif
79
80 #ifdef INS_HISTORY_FILE
81 extern struct history * insert_history;
82 extern char ori_insert_edit_submode;
83 #endif
84
85 /**
86 * \brief TODO Document do_normalmode()
87 *
88 * \param[in] buf
89 *
90 * \return none
91 */
92
do_normalmode(struct block * buf)93 void do_normalmode(struct block * buf) {
94 int bs = get_bufsize(buf);
95 struct ent * e;
96
97 switch (buf->value) {
98 // FOR TEST PURPOSES
99 case L'A':
100 //;
101 //wchar_t t = ui_query_opt(L"show a message. q / a / d to quit", L"qad");
102 break;
103
104 case L'W':
105 break;
106
107 case L'Q':
108 break;
109
110 // MOVEMENT COMMANDS
111 case L'j':
112 case OKEY_DOWN:
113 lastcol = curcol;
114 lastrow = currow;
115 currow = forw_row(1)->row;
116 unselect_ranges();
117 ui_update(TRUE);
118 break;
119
120 case L'k':
121 case OKEY_UP:
122 lastcol = curcol;
123 lastrow = currow;
124 currow = back_row(1)->row;
125 unselect_ranges();
126 ui_update(TRUE);
127 break;
128
129 case L'h':
130 case OKEY_LEFT:
131 lastrow = currow;
132 lastcol = curcol;
133 curcol = back_col(1)->col;
134 unselect_ranges();
135 ui_update(TRUE);
136 break;
137
138 case L'l':
139 case OKEY_RIGHT:
140 lastrow = currow;
141 lastcol = curcol;
142 curcol = forw_col(1)->col;
143 unselect_ranges();
144 ui_update(TRUE);
145 break;
146
147 case L'0':
148 if (get_conf_int("numeric_zero") == 1 && get_conf_int("numeric") == 1) goto numeric;
149 case OKEY_HOME:
150 lastrow = currow;
151 lastcol = curcol;
152 curcol = left_limit()->col;
153 unselect_ranges();
154 ui_update(TRUE);
155 break;
156
157 case L'$':
158 case OKEY_END:
159 lastrow = currow;
160 lastcol = curcol;
161 curcol = right_limit(currow)->col;
162 unselect_ranges();
163 ui_update(TRUE);
164 break;
165
166 case L'^':
167 lastcol = curcol;
168 lastrow = currow;
169 currow = goto_top()->row;
170 unselect_ranges();
171 ui_update(TRUE);
172 break;
173
174 case L'#':
175 lastcol = curcol;
176 lastrow = currow;
177 currow = goto_bottom()->row;
178 if (currow == lastrow && curcol == lastcol) currow = go_end()->row;
179 unselect_ranges();
180 ui_update(TRUE);
181 break;
182
183 // Tick
184 case L'\'':
185 if (bs != 2) break;
186 unselect_ranges();
187 e = tick(buf->pnext->value);
188 if (row_hidden[e->row]) {
189 sc_error("Cell row is hidden");
190 break;
191 }
192 if (col_hidden[e->col]) {
193 sc_error("Cell column is hidden");
194 break;
195 }
196 lastrow = currow;
197 lastcol = curcol;
198 currow = e->row;
199 curcol = e->col;
200 ui_update(TRUE);
201 break;
202
203 // CTRL j
204 case ctl('j'):
205 {
206 int p, c = curcol, cf = curcol;
207 if ( (p = is_range_selected()) != -1) {
208 struct srange * sr = get_range_by_pos(p);
209 c = sr->tlcol;
210 cf = sr->brcol;
211 }
212 auto_justify(c, cf, DEFWIDTH); // auto justify columns
213 ui_update(TRUE);
214 break;
215 }
216
217 // CTRL d
218 case ctl('d'): // set date format using current locate D_FMT format
219 {
220 #ifdef USELOCALE
221 #include <locale.h>
222 #include <langinfo.h>
223 char * loc = NULL;
224 char * f = NULL;
225 loc = setlocale(LC_TIME, "");
226 if (loc != NULL) {
227 f = nl_langinfo(D_FMT);
228 } else {
229 sc_error("No locale set. Nothing changed");
230 }
231 int p, r = currow, c = curcol, rf = currow, cf = curcol;
232 if ( (p = is_range_selected()) != -1) {
233 struct srange * sr = get_range_by_pos(p);
234 r = sr->tlrow;
235 c = sr->tlcol;
236 rf = sr->brrow;
237 cf = sr->brcol;
238 }
239 if (any_locked_cells(r, c, rf, cf)) {
240 sc_error("Locked cells encountered. Nothing changed");
241 return;
242 }
243 dateformat(lookat(r, c), lookat(rf, cf), f);
244 ui_update(TRUE);
245 break;
246 #else
247 sc_info("Build made without USELOCALE enabled");
248 #endif
249 }
250
251 // CTRL f
252 case ctl('f'):
253 case OKEY_PGDOWN:
254 {
255 int n = calc_mobile_rows(NULL);
256 if (get_conf_int("half_page_scroll")) n = n / 2;
257 lastcol = curcol;
258 lastrow = currow;
259 currow = forw_row(n)->row;
260 unselect_ranges();
261 scroll_down(n);
262 ui_update(TRUE);
263 break;
264 }
265
266 // CTRL b
267 case ctl('b'):
268 case OKEY_PGUP:
269 {
270 int n = calc_mobile_rows(NULL);
271 if (get_conf_int("half_page_scroll")) n = n / 2;
272 lastcol = curcol;
273 lastrow = currow;
274 currow = back_row(n)->row;
275 unselect_ranges();
276 scroll_up(n);
277 ui_update(TRUE);
278 break;
279 }
280
281 case L'w':
282 e = go_forward();
283 lastrow = currow;
284 lastcol = curcol;
285 currow = e->row;
286 curcol = e->col;
287 unselect_ranges();
288 ui_update(TRUE);
289 break;
290
291 case L'b':
292 e = go_backward();
293 lastrow = currow;
294 lastcol = curcol;
295 currow = e->row;
296 curcol = e->col;
297 unselect_ranges();
298 ui_update(TRUE);
299 break;
300
301 case L'H':
302 lastrow = currow;
303 int currow_h = vert_top()->row;
304 currow = currow_h;
305 unselect_ranges();
306 ui_update(TRUE);
307 break;
308
309 case L'M':
310 lastrow = currow;
311 currow = vert_middle()->row;
312 unselect_ranges();
313 ui_update(TRUE);
314 break;
315
316 case L'L':
317 lastrow = currow;
318 currow = vert_bottom()->row;
319 unselect_ranges();
320 ui_update(TRUE);
321 break;
322
323 case L'G': // goto end
324 e = go_end();
325 lastrow = currow;
326 lastcol = curcol;
327 currow = e->row;
328 curcol = e->col;
329 unselect_ranges();
330 ui_update(TRUE);
331 break;
332
333 // GOTO goto
334 case ctl('a'):
335 e = go_home();
336 lastrow = currow;
337 lastcol = curcol;
338 curcol = e->col;
339 currow = e->row;
340 unselect_ranges();
341 offscr_sc_rows = 0;
342 offscr_sc_cols = 0;
343 ui_update(TRUE);
344 break;
345
346 case L'g':
347 if (buf->pnext->value == L'0') { // g0
348 lastcol = curcol;
349 lastrow = currow;
350 curcol = go_bol()->col;
351
352 } else if (buf->pnext->value == L'$') { // g$
353 lastcol = curcol;
354 lastrow = currow;
355 curcol = go_eol()->col;
356
357 } else if (buf->pnext->value == L'f') { // gf
358 unselect_ranges();
359 ui_update(TRUE);
360 ui_stop_screen();
361 openfile_under_cursor(currow, curcol);
362 ui_start_screen();
363 start_default_ucolors();
364 set_colors_param_dict();
365
366 } else if (buf->pnext->value == L'g') { // gg
367 e = go_home();
368 lastcol = curcol;
369 lastrow = currow;
370 curcol = e->col;
371 currow = e->row;
372 offscr_sc_rows = 0;
373 offscr_sc_cols = 0;
374
375 } else if (buf->pnext->value == L'G') { // gG
376 e = go_end();
377 lastcol = curcol;
378 lastrow = currow;
379 currow = e->row;
380 curcol = e->col;
381
382 } else if (buf->pnext->value == L'M') { // gM
383 lastcol = curcol;
384 lastrow = currow;
385 curcol = horiz_middle()->col;
386
387 // goto last cell position
388 } else if (buf->pnext->value == L'l') { // gl
389 int newlr = currow;
390 int newlc = curcol;
391 curcol = lastcol;
392 currow = lastrow;
393 lastrow = newlr;
394 lastcol = newlc;
395 } else if (buf->pnext->value == L't') { // gtA4 (goto cell A4)
396 (void) swprintf(interp_line, BUFFERSIZE, L"goto %s", parse_cell_name(2, buf));
397 send_to_interp(interp_line);
398 }
399 unselect_ranges();
400 ui_update(TRUE);
401 break;
402
403 // repeat last goto command - backwards
404 case L'N':
405 go_previous();
406 ui_update(TRUE);
407 break;
408
409 // repeat last goto command
410 case L'n':
411 go_last();
412 ui_update(TRUE);
413 break;
414
415 // END OF MOVEMENT COMMANDS
416
417 case L'/':
418 {
419 char cadena[] = ":int goto ";
420 int i;
421 for (i=0; i<strlen(cadena); i++) {
422 flush_buf(buf);
423 addto_buf(buf, cadena[i]);
424 exec_single_cmd(buf);
425 }
426 break;
427 }
428
429 case L'?':
430 {
431 char cadena[] = ":int gotob ";
432 int i;
433 for (i=0; i<strlen(cadena); i++) {
434 flush_buf(buf);
435 addto_buf(buf, cadena[i]);
436 exec_single_cmd(buf);
437 }
438 break;
439 }
440
441 // repeat last command
442 case L'.':
443 if (get_conf_int("numeric_decimal") == 1 && get_conf_int("numeric") == 1) goto numeric;
444 copybuffer(lastcmd_buffer, buf); // nose graba en lastcmd_buffer!!
445 cmd_multiplier = 1;
446 exec_mult(buf, COMPLETECMDTIMEOUT);
447 break;
448
449 // enter command mode
450 case L':':
451 chg_mode(':');
452 #ifdef HISTORY_FILE
453 add(commandline_history, L"");
454 #endif
455 ui_handle_cursor();
456 inputline_pos = 0;
457 real_inputline_pos = 0;
458 ui_show_header();
459 break;
460
461 // enter visual mode
462 case L'v':
463 chg_mode('v');
464 ui_show_header();
465 ui_handle_cursor();
466 start_visualmode(currow, curcol, currow, curcol);
467 break;
468
469 // INPUT COMMANDS
470 case L'=':
471 case L'\\':
472 case L'<':
473 case L'>':
474 if (locked_cell(currow, curcol)) return;
475 insert_edit_submode = buf->value;
476 chg_mode(insert_edit_submode);
477 #ifdef INS_HISTORY_FILE
478 ori_insert_edit_submode = buf->value;
479 add(insert_history, L"");
480 #endif
481 inputline_pos = 0;
482 real_inputline_pos = 0;
483 ui_show_header();
484 break;
485
486 // EDITION COMMANDS
487 // edit cell (v)
488 case L'e':
489 if (locked_cell(currow, curcol)) return;
490 inputline_pos = 0;
491 real_inputline_pos = 0;
492 if (start_edit_mode(buf, 'v')) ui_show_header();
493 break;
494
495 // edit cell (s)
496 case L'E':
497 if (locked_cell(currow, curcol)) return;
498 inputline_pos = 0;
499 real_inputline_pos = 0;
500 if (start_edit_mode(buf, 's')) ui_show_header();
501 else {
502 sc_info("No string value to edit");
503 chg_mode('.');
504 ui_print_mode();
505 ui_show_celldetails();
506 }
507 break;
508
509 // del current cell or range
510 case L'x':
511 del_selected_cells();
512 ui_update(TRUE);
513 break;
514
515 // format col or freeze range
516 case L'f':
517 if (bs != 2) return;
518
519 // freeze row / column or area
520 if (buf->pnext->value == 'r' || buf->pnext->value == 'c' || buf->pnext->value == 'a') {
521 int p = is_range_selected(), r = currow, c = curcol, rf = currow, cf = curcol;
522
523 if (p != -1) { // mark range
524 struct srange * sr = get_range_by_pos(p);
525 r = sr->tlrow;
526 c = sr->tlcol;
527 rf = sr->brrow;
528 cf = sr->brcol;
529 }
530
531 if (buf->pnext->value == 'r') {
532 handle_freeze(lookat(r, c), lookat(rf, cf), 1, 'r');
533 sc_info("Row%s frozen", r != rf ? "s" : "");
534 } else if (buf->pnext->value == 'c') {
535 handle_freeze(lookat(r, c), lookat(rf, cf), 1, 'c');
536 sc_info("Column%s frozen", c != cf ? "s" : "");
537 } else if (buf->pnext->value == 'a') {
538 handle_freeze(lookat(r, c), lookat(rf, cf), 1, 'r');
539 handle_freeze(lookat(r, c), lookat(rf, cf), 1, 'c');
540 sc_info("Area frozen");
541 }
542 ui_update(FALSE);
543 break;
544
545 // decrease row height
546 } else if (buf->pnext->value == 'k' || buf->pnext->value == OKEY_UP) {
547
548 #ifdef UNDO
549 create_undo_action();
550 int fmt_ori = row_format[currow];
551 add_undo_row_format(currow, 'R', row_format[currow]);
552 #endif
553 swprintf(interp_line, BUFFERSIZE, L"format %d %d", currow, row_format[currow]-1);
554 send_to_interp(interp_line);
555 #ifdef UNDO
556 if (row_format[currow] != fmt_ori) {
557 add_undo_row_format(currow, 'A', row_format[currow]);
558 end_undo_action();
559 } else dismiss_undo_item(NULL);
560 #endif
561 ui_update(TRUE);
562 break;
563
564 // increase row height
565 } else if (buf->pnext->value == 'j' || buf->pnext->value == OKEY_DOWN) {
566
567 #ifdef UNDO
568 create_undo_action();
569 int fmt_ori = row_format[currow];
570 add_undo_row_format(currow, 'R', row_format[currow]);
571 #endif
572 swprintf(interp_line, BUFFERSIZE, L"format %d %d", currow, row_format[currow]+1);
573 send_to_interp(interp_line);
574 #ifdef UNDO
575 if (row_format[currow] != fmt_ori) {
576 add_undo_row_format(currow, 'A', row_format[currow]);
577 end_undo_action();
578 } else dismiss_undo_item(NULL);
579 #endif
580 ui_update(TRUE);
581 break;
582
583 // change in format
584 } else {
585 #ifdef UNDO
586 create_undo_action();
587 add_undo_col_format(curcol, 'R', fwidth[curcol], precision[curcol], realfmt[curcol]);
588 #endif
589 formatcol(buf->pnext->value);
590 #ifdef UNDO
591 add_undo_col_format(curcol, 'A', fwidth[curcol], precision[curcol], realfmt[curcol]);
592 end_undo_action();
593 #endif
594 }
595 break;
596
597 // mark cell or range
598 case L'm':
599 if (bs != 2) break;
600 int p = is_range_selected();
601 if (p != -1) { // mark range
602 struct srange * sr = get_range_by_pos(p);
603 set_range_mark(buf->pnext->value, sr);
604 } else // mark cell
605 set_cell_mark(buf->pnext->value, currow, curcol);
606 modflg++;
607 break;
608
609 // copy
610 case L'c':
611 {
612 if (bs != 2) break;
613 struct mark * m = get_mark(buf->pnext->value);
614 if ( m == NULL) return;
615
616
617 // if m represents a range
618 if ( m->row == -1 && m->col == -1) {
619 srange * r = m->rng;
620 yank_area(r->tlrow, r->tlcol, r->brrow, r->brcol, 'a', cmd_multiplier);
621 if (paste_yanked_ents(0, 'c') == -1) {
622 sc_error("Locked cells encountered. Nothing changed");
623 break;
624 }
625
626 // if m represents just one cell
627 } else {
628 struct mark * m = get_mark(buf->pnext->value);
629 struct ent * p = lookat(m->row, m->col);
630 struct ent * n;
631 int c1;
632
633 #ifdef UNDO
634 create_undo_action();
635 #endif
636 for (c1 = curcol; cmd_multiplier-- && cmd_multiplier > -1 && c1 < maxcols; c1++) {
637 if ((n = * ATBL(tbl, currow, c1))) {
638 if (n->flags & is_locked)
639 continue;
640 if (! p) {
641 clearent(n);
642 continue;
643 }
644 } else {
645 if (! p) break;
646 n = lookat(currow, c1);
647 }
648 #ifdef UNDO
649 // added for #244 - 22/03/2018
650 ents_that_depends_on_range(n->row, n->col, n->row, n->col);
651 copy_to_undostruct(currow, c1, currow, c1, UNDO_DEL, HANDLE_DEPS, NULL);
652 #endif
653 copyent(n, p, currow - get_mark(buf->pnext->value)->row, c1 - get_mark(buf->pnext->value)->col, 0, 0, maxrow, maxcol, 0);
654
655 n->row += currow - get_mark(buf->pnext->value)->row;
656 n->col += c1 - get_mark(buf->pnext->value)->col;
657
658 n->flags |= is_changed;
659 if (n->expr) EvalJustOneVertex(n, 1);
660
661 #ifdef UNDO
662 copy_to_undostruct(currow, c1, currow, c1, UNDO_ADD, HANDLE_DEPS, NULL);
663 #endif
664 }
665 #ifdef UNDO
666 extern struct ent_ptr * deps;
667 if (deps != NULL) free(deps);
668 deps = NULL;
669 end_undo_action();
670 #endif
671 }
672
673 ui_update(TRUE);
674 break;
675 }
676
677 // range lock / unlock / valueize
678 case L'r':
679 {
680 int p, r = currow, c = curcol, rf = currow, cf = curcol;
681 if ( (p = is_range_selected()) != -1) {
682 struct srange * sr = get_range_by_pos(p);
683 r = sr->tlrow;
684 c = sr->tlcol;
685 rf = sr->brrow;
686 cf = sr->brcol;
687 }
688 if (buf->pnext->value == L'l') {
689 lock_cells(lookat(r, c), lookat(rf, cf));
690 } else if (buf->pnext->value == L'u') { // watch out if you do C-r and u too quickly !
691 unlock_cells(lookat(r, c), lookat(rf, cf));
692 } else if (buf->pnext->value == L'v') {
693 valueize_area(r, c, rf, cf);
694 }
695 ui_update(TRUE);
696 break;
697 }
698
699 // create range with two marks
700 case L'R':
701 if (bs == 3) {
702 create_range(buf->pnext->value, buf->pnext->pnext->value, NULL, NULL);
703 ui_update(TRUE);
704 }
705 break;
706
707 // Zr Zc - Zap col or row - Show col or row - Sr Sc
708 case L'Z':
709 case L'S':
710 {
711 int rs, r = currow, c = curcol, arg = cmd_multiplier;
712 struct srange * sr;
713 if ( (rs = is_range_selected()) != -1) {
714 sr = get_range_by_pos(rs);
715 cmd_multiplier = 1;
716 r = sr->tlrow;
717 c = sr->tlcol;
718 arg = buf->pnext->value == L'r' ? sr->brrow - sr->tlrow + 1 : sr->brcol - sr->tlcol + 1;
719 }
720 if (buf->value == L'Z' && buf->pnext->value == L'r') {
721 hide_row(r, arg);
722 } else if (buf->value == L'Z' && buf->pnext->value == L'c') {
723 hide_col(c, arg);
724 } else if (buf->value == L'S' && buf->pnext->value == L'r') {
725 show_row(r, arg);
726 } else if (buf->value == L'S' && buf->pnext->value == L'c') {
727 show_col(c, arg);
728 }
729 cmd_multiplier = 0;
730 ui_update(TRUE);
731 break;
732 }
733
734 // shift range or cell
735 case L's':
736 {
737 int p, r = currow, c = curcol, rf = currow, cf = curcol;
738 if ( (p = is_range_selected()) != -1) {
739 struct srange * sr = get_range_by_pos(p);
740 r = sr->tlrow;
741 c = sr->tlcol;
742 rf = sr->brrow;
743 cf = sr->brcol;
744 }
745 shift(r, c, rf, cf, buf->pnext->value);
746 unselect_ranges();
747 ui_update(TRUE);
748 break;
749 }
750
751 // delete row or column, or selected cell or range
752 case L'd':
753 {
754 if (bs != 2) return;
755 int ic = cmd_multiplier; // orig
756
757 // deleterow
758 if (buf->pnext->value == L'r') {
759 deleterow(currow, ic);
760 if (cmd_multiplier > 1) cmd_multiplier = 0;
761
762 // deletecol
763 } else if (buf->pnext->value == L'c') {
764 deletecol(curcol, ic);
765 if (cmd_multiplier > 1) cmd_multiplier = 0;
766
767 } else if (buf->pnext->value == L'd') {
768 del_selected_cells();
769 }
770
771 ui_update(TRUE);
772 break;
773 }
774
775 // insert row or column
776 case L'i':
777 {
778 if (bs != 2) return;
779 #ifdef UNDO
780 create_undo_action();
781 #endif
782
783 if (buf->pnext->value == L'r') {
784 #ifdef UNDO
785 save_undo_range_shift(1, 0, currow, 0, currow, maxcol);
786 #endif
787 fix_marks(1, 0, currow, maxrow, 0, maxcol);
788 insert_row(0);
789 #ifdef UNDO
790 add_undo_row_format(currow, 'A', row_format[currow]);
791 #endif
792
793 } else if (buf->pnext->value == L'c') {
794 #ifdef UNDO
795 save_undo_range_shift(0, 1, 0, curcol, maxrow, curcol);
796 #endif
797 fix_marks(0, 1, 0, maxrow, curcol, maxcol);
798 insert_col(0);
799 #ifdef UNDO
800 add_undo_col_format(curcol, 'A', fwidth[curcol], precision[curcol], realfmt[curcol]);
801 #endif
802 }
803 #ifdef UNDO
804 end_undo_action();
805 #endif
806 ui_update(TRUE);
807 break;
808 }
809
810 // open row or column
811 case L'o':
812 {
813 if (bs != 2) return;
814 #ifdef UNDO
815 create_undo_action();
816 #endif
817 if (buf->pnext->value == L'r') {
818 #ifdef UNDO
819 save_undo_range_shift(1, 0, currow+1, 0, currow+1, maxcol);
820 #endif
821 fix_marks(1, 0, currow+1, maxrow, 0, maxcol);
822 insert_row(1);
823 #ifdef UNDO
824 add_undo_row_format(currow, 'A', row_format[currow]);
825 #endif
826
827 } else if (buf->pnext->value == L'c') {
828 #ifdef UNDO
829 save_undo_range_shift(0, 1, 0, curcol+1, maxrow, curcol+1);
830 #endif
831 fix_marks(0, 1, 0, maxrow, curcol+1, maxcol);
832 insert_col(1);
833 #ifdef UNDO
834 add_undo_col_format(curcol, 'A', fwidth[curcol], precision[curcol], realfmt[curcol]);
835 #endif
836 }
837 #ifdef UNDO
838 end_undo_action();
839 #endif
840 ui_update(TRUE);
841 break;
842 }
843
844 case L'y':
845 // yank row
846 if ( bs == 2 && buf->pnext->value == L'r') {
847 yank_area(currow, 0, currow + cmd_multiplier - 1, maxcol, 'r', cmd_multiplier);
848 if (cmd_multiplier > 1) cmd_multiplier = 0;
849
850 // yank col
851 } else if ( bs == 2 && buf->pnext->value == L'c') {
852 yank_area(0, curcol, maxrow, curcol + cmd_multiplier - 1, 'c', cmd_multiplier);
853 if (cmd_multiplier > 1) cmd_multiplier = 0;
854
855 // yank cell
856 } else if ( bs == 2 && buf->pnext->value == L'y' && is_range_selected() == -1) {
857 yank_area(currow, curcol, currow, curcol, 'e', cmd_multiplier);
858
859 // yank range
860 } else if ( bs == 1 && is_range_selected() != -1) {
861 srange * r = get_selected_range();
862 yank_area(r->tlrow, r->tlcol, r->brrow, r->brcol, 'a', cmd_multiplier);
863 }
864 break;
865
866 // paste cell below or left
867 case L'p':
868 if (paste_yanked_ents(0, 'a') == -1) {
869 sc_error("Locked cells encountered. Nothing changed");
870 break;
871 }
872 ui_update(TRUE);
873 break;
874
875 case L'P':
876 case L'T':
877 if (bs != 2) break;
878 if (buf->pnext->value == L'v' || buf->pnext->value == L'f' || buf->pnext->value == L'c') {
879 int res = buf->value == L'P' ? paste_yanked_ents(0, buf->pnext->value) : paste_yanked_ents(1, buf->pnext->value); // paste cell above or right
880 if (res == -1) {
881 sc_error("Locked cells encountered. Nothing changed");
882 break;
883 }
884 ui_update(TRUE);
885 }
886 break;
887
888 // paste cell above or right
889 case L't':
890 if (paste_yanked_ents(1, 'a') == -1) {
891 sc_error("Locked cells encountered. Nothing changed");
892 break;
893 }
894 ui_update(TRUE);
895 break;
896
897 // select inner range - Vir
898 case L'V':
899 if (buf->value == L'V' && bs == 3 &&
900 buf->pnext->value == L'i' && buf->pnext->pnext->value == L'r') {
901 int tlrow = currow;
902 int brrow = currow;
903 int tlcol = curcol;
904 int brcol = curcol;
905 int * tlr = &tlrow;
906 int * brr = &brrow;
907 int * tlc = &tlcol;
908 int * brc = &brcol;
909 select_inner_range(tlr, tlc, brr, brc);
910 start_visualmode(*tlr, *tlc, *brr, *brc);
911 }
912 break;
913
914 // autojus
915 case L'a':
916 if ( bs != 2 ) break;
917
918 if (buf->pnext->value == L'a') {
919 int p, r = currow, c = curcol, rf = currow, cf = curcol;
920 if ( (p = is_range_selected()) != -1) {
921 struct srange * sr = get_range_by_pos(p);
922 r = sr->tlrow;
923 c = sr->tlcol;
924 rf = sr->brrow;
925 cf = sr->brcol;
926 }
927 if (any_locked_cells(r, c, rf, cf)) {
928 sc_error("Locked cells encountered. Nothing changed");
929 return;
930 }
931 wchar_t cline [BUFFERSIZE];
932 swprintf(cline, BUFFERSIZE, L"autojus %s:", coltoa(c));
933 swprintf(cline + wcslen(cline), BUFFERSIZE, L"%s", coltoa(cf));
934 send_to_interp(cline);
935 ui_update(TRUE);
936 }
937 break;
938
939 // scroll
940 case L'z':
941 if ( bs != 2 ) break;
942 int scroll = 0;
943
944 switch (buf->pnext->value) {
945 case L'l':
946 scroll_right(1);
947 break;
948
949 case L'h':
950 scroll_left(1);
951 break;
952
953 case L'H':
954 scroll = calc_mobile_cols(NULL);
955 if (get_conf_int("half_page_scroll")) scroll /= 2;
956 scroll_left(scroll);
957 break;
958
959 case L'L':
960 scroll = calc_mobile_cols(NULL);
961 if (get_conf_int("half_page_scroll")) scroll /= 2;
962 scroll_right(scroll);
963 break;
964
965 case L'm':
966 ;
967 int i = 0, c = 0, ancho = rescol;
968 offscr_sc_cols = 0;
969 for (i = 0; i < curcol; i++) {
970 for (c = i; c < curcol; c++) {
971 if (!col_hidden[c]) ancho += fwidth[c];
972 if (ancho >= (COLS - rescol)/ 2) {
973 ancho = rescol;
974 break;
975 }
976 }
977 if (c == curcol) break;
978 }
979 offscr_sc_cols = i;
980 break;
981
982 case L't':
983 case L'b':
984 case L'z':
985 case L'.':
986 {
987 int i = 0, r = offscr_sc_rows-1;
988
989 if (buf->pnext->value == L't') {
990 while (i < LINES - RESROW - 1 && r < currow) {
991 r++;
992 if (row_frozen[r]) continue;
993 i++;
994 }
995 scroll_down(--i);
996
997 } else if (buf->pnext->value == L'b') {
998 int hidden = 0;
999 while (i < LINES - RESROW - 1) {
1000 r++;
1001 if (row_hidden[r]) { hidden++; continue; }
1002 else if (r < offscr_sc_rows && ! (row_frozen[r])) continue;
1003 else if (row_frozen[r]) continue;
1004 i++;
1005 }
1006 scroll_up(r-currow-hidden);
1007
1008 } else if (buf->pnext->value == L'z' || buf->pnext->value == L'.') {
1009 while (i < LINES - RESROW - 1 && r <= currow) {
1010 r++;
1011 if (row_frozen[r]) continue;
1012 i++;
1013 }
1014 int top = --i;
1015 i = 0, r = offscr_sc_rows-1;
1016 while (i < LINES - RESROW - 1) {
1017 r++;
1018 if (r < offscr_sc_rows && ! (row_frozen[r])) continue;
1019 i++;
1020 }
1021 int bottom = r-currow;
1022 int scroll = (-top + bottom)/2;
1023 if (scroll < 0)
1024 scroll_down(-scroll);
1025 else if (scroll > 0)
1026 scroll_up(scroll);
1027 }
1028 break;
1029 }
1030 }
1031 ui_update(TRUE);
1032 break;
1033
1034 // scroll up a line
1035 case ctl('y'):
1036 scroll_up(1);
1037 ui_update(TRUE);
1038 break;
1039
1040 // scroll down a line
1041 case ctl('e'):
1042 scroll_down(1);
1043 ui_update(TRUE);
1044 break;
1045
1046 // undo
1047 case L'u':
1048 #ifdef UNDO
1049 do_undo();
1050 ui_update(TRUE);
1051 #else
1052 sc_error("Build was done without UNDO support");
1053 #endif
1054 break;
1055
1056 // redo
1057 case ctl('r'):
1058 #ifdef UNDO
1059 do_redo();
1060 ui_update(TRUE);
1061 #else
1062 sc_error("Build was done without UNDO support");
1063 #endif
1064 break;
1065
1066 case L'{': // left align
1067 case L'}': // right align
1068 case L'|': // center align
1069 {
1070 int p, r = currow, c = curcol, rf = currow, cf = curcol;
1071 struct srange * sr;
1072 if ( (p = is_range_selected()) != -1) {
1073 sr = get_range_by_pos(p);
1074 r = sr->tlrow;
1075 c = sr->tlcol;
1076 rf = sr->brrow;
1077 cf = sr->brcol;
1078 }
1079 if (any_locked_cells(r, c, rf, cf)) {
1080 sc_error("Locked cells encountered. Nothing changed");
1081 return;
1082 }
1083 #ifdef UNDO
1084 create_undo_action();
1085 #endif
1086 if (buf->value == L'{') swprintf(interp_line, BUFFERSIZE, L"leftjustify %s", v_name(r, c));
1087 else if (buf->value == L'}') swprintf(interp_line, BUFFERSIZE, L"rightjustify %s", v_name(r, c));
1088 else if (buf->value == L'|') swprintf(interp_line, BUFFERSIZE, L"center %s", v_name(r, c));
1089 if (p != -1) swprintf(interp_line + wcslen(interp_line), BUFFERSIZE, L":%s", v_name(rf, cf));
1090 #ifdef UNDO
1091 copy_to_undostruct(r, c, rf, cf, UNDO_DEL, IGNORE_DEPS, NULL);
1092 #endif
1093 send_to_interp(interp_line);
1094 #ifdef UNDO
1095 copy_to_undostruct(r, c, rf, cf, UNDO_ADD, IGNORE_DEPS, NULL);
1096 end_undo_action();
1097 #endif
1098 cmd_multiplier = 0;
1099 ui_update(TRUE);
1100 break;
1101 }
1102
1103 case ctl('l'):
1104 sig_winchg();
1105 break;
1106
1107 case L'@':
1108 EvalAll();
1109 ui_update(TRUE);
1110 break;
1111
1112 // increase or decrease numeric value of cell or range
1113 case L'-':
1114 case L'+':
1115 {
1116 int r, c, tlrow = currow, tlcol = curcol, brrow = currow, brcol = curcol;
1117 if ( is_range_selected() != -1 ) {
1118 struct srange * sr = get_selected_range();
1119 tlrow = sr->tlrow;
1120 tlcol = sr->tlcol;
1121 brrow = sr->brrow;
1122 brcol = sr->brcol;
1123 }
1124 if (any_locked_cells(tlrow, tlcol, brrow, brcol)) {
1125 sc_error("Locked cells encountered. Nothing changed");
1126 return;
1127 }
1128 if (get_conf_int("numeric") == 1) goto numeric;
1129 struct ent * p;
1130 #ifdef UNDO
1131 create_undo_action();
1132 copy_to_undostruct(tlrow, tlcol, brrow, brcol, UNDO_DEL, IGNORE_DEPS, NULL);
1133 #endif
1134 int arg = cmd_multiplier;
1135 int mf = modflg; // keep original modflg
1136 for (r = tlrow; r <= brrow; r++) {
1137 for (c = tlcol; c <= brcol; c++) {
1138 p = *ATBL(tbl, r, c);
1139 if ( ! p ) {
1140 continue;
1141 } else if (p->expr && !(p->flags & is_strexpr)) {
1142 //sc_error("Can't increment / decrement a formula");
1143 continue;
1144 } else if (p->flags & is_valid) {
1145 p->v += buf->value == L'+' ? (double) arg : - 1 * (double) arg;
1146 if (mf == modflg) modflg++; // increase just one time
1147 }
1148 }
1149 }
1150 #ifdef UNDO
1151 copy_to_undostruct(tlrow, tlcol, brrow, brcol, UNDO_ADD, IGNORE_DEPS, NULL);
1152 end_undo_action();
1153 #endif
1154 if (get_conf_int("autocalc")) EvalRange(tlrow, tlcol, brrow, brcol);
1155 cmd_multiplier = 0;
1156 ui_update(TRUE);
1157 }
1158 break;
1159
1160 // input of numbers
1161 default:
1162 numeric:
1163 if ( (isdigit(buf->value) || buf->value == L'-' || buf->value == L'+' ||
1164 ( buf->value == L'.' && get_conf_int("numeric_decimal") )) &&
1165 get_conf_int("numeric") ) {
1166 if (locked_cell(currow, curcol)) return;
1167 insert_edit_submode='=';
1168 chg_mode(insert_edit_submode);
1169 #ifdef INS_HISTORY_FILE
1170 ori_insert_edit_submode = buf->value;
1171 add(insert_history, L"");
1172 #endif
1173 inputline_pos = 0;
1174 real_inputline_pos = 0;
1175 ins_in_line(buf->value);
1176 ui_show_header();
1177 }
1178 }
1179 return;
1180 }
1181