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