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.c
40 * \author Andrés Martinelli <andmarti@gmail.com>
41 * \date 05/04/2021
42 * \brief TODO Write brief file description
43 */
44
45 #include <stdlib.h>
46 #include <ctype.h> // for isdigit
47 #include <wchar.h>
48 #include <wctype.h>
49 #include <limits.h>
50 #include "main.h"
51 #include "maps.h"
52 #include "yank.h"
53 #include "marks.h"
54 #include "cmds.h"
55 #include "buffer.h"
56 #include "tui.h"
57 #include "conf.h" // for conf parameters
58 #include "xmalloc.h" // for scxfree
59 #include "vmtbl.h" // for growtbl
60 #include "utils/string.h" // for add_char
61 #include "y.tab.h" // for yyparse
62 #include "dep_graph.h"
63 #include "freeze.h"
64 #ifdef UNDO
65 #include "undo.h"
66 #endif
67
68 void syncref(register struct enode *e);
69 extern int shall_quit;
70 char insert_edit_submode;
71 struct ent * freeents = NULL; // keep deleted ents around before sync_refs
72 wchar_t interp_line[BUFFERSIZE];
73 extern graphADT graph;
74 extern int yyparse(void);
75
76 int offscr_sc_rows = 0, offscr_sc_cols = 0; // off screen spreadsheet rows and columns
77 int nb_frozen_rows = 0, nb_frozen_cols = 0; // total number of frozen rows/cols
78 int nb_frozen_screenrows = 0; // screen rows occupied by those frozen rows
79 int nb_frozen_screencols = 0; // screen cols occupied by those frozen columns
80
81 /**
82 * \brief Maintain ent strucs until they are release for deletion by sync_refs.
83 *
84 * \details This structure is used to keep ent structs around before
85 * they are actually deleted (memory freed) to allow the sync_refs
86 * routine a chance to fix the variable references. If delete flag
87 * is set, is_deleted flag of an ent is set.
88 *
89 * \param[in] p
90 * \param[in] delete
91 *
92 * \return none
93 */
mark_ent_as_deleted(register struct ent * p,int delete)94 void mark_ent_as_deleted(register struct ent * p, int delete) {
95 if (p == NULL) return;
96 if (delete) p->flags |= is_deleted;
97
98 p->next = freeents; /* put this ent on the front of freeents */
99 freeents = p;
100
101 return;
102 }
103
104
105 /**
106 * \brief TODO Write brief description
107 *
108 * \details Iterates throw freeents (ents marked as deleted). Calls
109 * clearent for freeing ents contents memory and free ent pointer. This
110 * function should always be called at exit. This is mandatory, just in
111 * case we want to UNDO any changes.
112 * TODO: any malloc of ents should check here before asking for memory
113 *
114 * \return none
115 */
flush_saved()116 void flush_saved() {
117 register struct ent * p;
118 register struct ent * q;
119
120 p = freeents;
121 while (p != NULL) {
122 (void) clearent(p);
123 q = p->next;
124 free(p);
125 p = q;
126 }
127 freeents = NULL;
128 return;
129 }
130
131
132 /**
133 * \brief TODO Write brief description
134 *
135 * \details Used to remove references to deleted struct ents.
136 *
137 * \details Note that the deleted structure must still be hanging
138 * around before the call, but not referenced by an entry in tbl.
139 *
140 * \return none
141 */
142 // TODO Improve this function such that it does not traverse the whole table
sync_refs()143 void sync_refs() {
144 int i, j;
145 register struct ent * p;
146 for (i=0; i <= maxrow; i++)
147 for (j=0; j <= maxcol; j++)
148 if ( (p = *ATBL(tbl, i, j)) && p->expr ) {
149 syncref(p->expr);
150 }
151 return;
152 }
153
154
155 /**
156 * \brief TODO Write brief description
157 *
158 * Used to remove a reference to deleted a single struct ent.
159 *
160 * Note that the deleted structure must still be hanging around before the
161 * call, but not referenced by an entry in tbl.
162 *
163 * Example usage:
164 * @code
165 * syncref(<variable>);
166 * @endcode
167 * returns: none
168 */
syncref(register struct enode * e)169 void syncref(register struct enode * e) {
170 if ( e == NULL ) {
171 return;
172 } else if ( e->op == ERR_ ) {
173 e->e.o.left = NULL;
174 e->e.o.right = NULL;
175 return;
176 } else if ( e->op == REF_ ) {
177 e->op = REF_;
178 e->e.o.left = NULL;
179 e->e.o.right = NULL;
180 return;
181 } else if (e->op & REDUCE) {
182 e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col);
183 e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col);
184 } else {
185 switch (e->op) {
186 case 'v':
187 if (e->e.v.vp->flags & is_deleted) {
188 //e->op = ERR_;
189 //e->e.o.left = NULL;
190 //e->e.o.right = NULL;
191 break;
192 } else if (e->e.v.vp->flags & may_sync)
193 e->e.v.vp = lookat(e->e.v.vp->row, e->e.v.vp->col);
194 break;
195 case 'k':
196 break;
197 case '$':
198 break;
199 default:
200 syncref(e->e.o.right);
201 syncref(e->e.o.left);
202 break;
203 }
204 }
205 return;
206 }
207
208
209 /**
210 * \brief TODO Write brief description
211 *
212 * \param[in] col
213 * \param[in] mult
214 *
215 * \return none
216 */
deletecol(int col,int mult)217 void deletecol(int col, int mult) {
218 if (any_locked_cells(0, col, maxrow, col + mult)) {
219 sc_error("Locked cells encountered. Nothing changed");
220 return;
221 }
222 #ifdef UNDO
223 create_undo_action();
224 // here we save in undostruct, all the ents that depends on the deleted one (before change)
225 ents_that_depends_on_range(0, col, maxrow, col - 1 + mult);
226 copy_to_undostruct(0, col, maxrow, col - 1 + mult, UNDO_DEL, HANDLE_DEPS, NULL);
227 save_undo_range_shift(0, -mult, 0, col, maxrow, col - 1 + mult);
228
229 int i;
230 for (i=col; i < col + mult; i++) {
231 add_undo_col_format(i, 'R', fwidth[i], precision[i], realfmt[i]);
232 if (col_hidden[i]) undo_hide_show(-1, i, 's', 1);
233 else undo_hide_show(-1, i, 'h', 1);
234 // TODO undo col_frozen
235 }
236 #endif
237
238 fix_marks(0, -mult, 0, maxrow, col + mult -1, maxcol);
239 if (!loading) yank_area(0, col, maxrow, col + mult - 1, 'c', mult);
240
241 // do the job
242 int_deletecol(col, mult);
243
244 // if (get_conf_int("autocalc")) EvalAll();
245
246 if (!loading) modflg++;
247
248 #ifdef UNDO
249 // here we save in undostruct, just the ents that depends on the deleted one (after change)
250 copy_to_undostruct(0, 0, -1, -1, UNDO_ADD, HANDLE_DEPS, NULL);
251
252 extern struct ent_ptr * deps;
253 if (deps != NULL) free(deps);
254 deps = NULL;
255
256 end_undo_action();
257 #endif
258 return;
259 }
260
261
262 /**
263 * \brief TODO Write a brief description
264 *
265 * \details Delete a column. Parameters col = column to delete
266 * multi = cmds multiplier. (commonly 1)
267 *
268 * \param[in] col
269 * \param[in] mult
270 *
271 * \return none
272 */
int_deletecol(int col,int mult)273 void int_deletecol(int col, int mult) {
274 register struct ent ** pp;
275 int r, c, i;
276
277 while (mult--) {
278 // mark ent of column to erase with is_deleted flag
279 for (r = 0; r <= maxrow; r++) {
280 pp = ATBL(tbl, r, col);
281 if ( *pp != NULL ) {
282 mark_ent_as_deleted(*pp, TRUE);
283 //clearent(*pp);
284 //free(*pp);
285 *pp = NULL;
286 }
287 }
288
289 rebuild_graph(); // Rebuild of graph is needed.
290 //But shouldnt! TODO
291
292 // Copy references from right column cells to left column (which gets removed)
293 for (r = 0; r <= maxrow; r++) {
294 for (c = col; c < maxcol; c++) {
295 pp = ATBL(tbl, r, c);
296
297 // nota: pp[1] = ATBL(tbl, r, c+1);
298 if ( pp[1] != NULL ) pp[1]->col--;
299 pp[0] = pp[1];
300 }
301
302 // Free last column memory (Could also initialize 'ent' to zero with `cleanent`).
303 pp = ATBL(tbl, r, maxcol);
304 *pp = (struct ent *) 0;
305 }
306
307 // Fix columns precision and width
308 for (i = col; i < maxcols - 2; i++) {
309 fwidth[i] = fwidth[i+1];
310 precision[i] = precision[i+1];
311 realfmt[i] = realfmt[i+1];
312 col_hidden[i] = col_hidden[i+1];
313 col_frozen[i] = col_frozen[i+1];
314 }
315
316 for (; i < maxcols - 1; i++) {
317 fwidth[i] = DEFWIDTH;
318 precision[i] = DEFPREC;
319 realfmt[i] = DEFREFMT;
320 col_hidden[i] = FALSE;
321 col_frozen[i] = FALSE;
322 }
323
324 maxcol--;
325 sync_refs();
326 EvalAll();
327 //flush_saved(); // we have to flush_saved only at exit.
328 //this is because we have to keep ents in case we want to UNDO
329 }
330
331 return;
332 }
333
334
335 /**
336 * \brief TODO Write a brief description
337 *
338 * \details Copy cell (struct ent). "special" indicates special treatment
339 * when merging two cells for the "pm" command, merging formate only for
340 * the "pf" command, or for adjusting cell references when transposing
341 * with the "pt" command. r1, c1, r2, and c2 define the range in which the
342 * dr and dc values should be used. Special =='u' means special copy from
343 * spreadsheet to undo struct. Since its mandatory to make isolated copies
344 * of p->expr->e.o.right.e.v.vp and p->expr->e.o.right.e.v.vp
345 *
346 * \param[in] n
347 * \param[in] p
348 * \param[in] dr
349 * \param[in] dc
350 * \param[in] r1
351 * \param[in] c1
352 * \param[in] r2
353 * \param[in] c2
354 * \param[in] special
355 * \return none
356 */
copyent(register struct ent * n,register struct ent * p,int dr,int dc,int r1,int c1,int r2,int c2,int special)357 void copyent(register struct ent * n, register struct ent * p, int dr, int dc, int r1, int c1, int r2, int c2, int special) {
358 if (!n || !p) {
359 sc_error("copyent: internal error");
360 return;
361 }
362
363 n->flags = may_sync;
364 if (p->flags & is_deleted)
365 n->flags |= is_deleted;
366
367 if (special != 'f') {
368 if (p->flags & is_valid) {
369 n->v = p->v;
370 n->flags |= p->flags & is_valid;
371 }
372 if (special != 'v' && p->expr && special != 'u') {
373 n->expr = copye(p->expr, dr, dc, r1, c1, r2, c2, special == 't');
374 #ifdef UNDO
375 } else if (special == 'u' && p->expr) { // from spreadsheet to undo
376 n->expr = copye(p->expr, dr, dc, r1, c1, r2, c2, 2);
377 #endif
378 }
379 if (p->expr && p->flags & is_strexpr)
380 n->flags |= is_strexpr;
381 else if (p->expr)
382 n->flags &= ~is_strexpr;
383
384 if (p->label) {
385 if (n->label) scxfree(n->label);
386 n->label = scxmalloc((unsigned) (strlen(p->label) + 1));
387 (void) strcpy(n->label, p->label);
388 n->flags &= ~is_leftflush;
389 n->flags |= ((p->flags & is_label) | (p->flags & is_leftflush));
390 }
391 n->flags |= p->flags & is_locked;
392 }
393 if (p->format && special != 'v') {
394 if (n->format) scxfree(n->format);
395 n->format = scxmalloc((unsigned) (strlen(p->format) + 1));
396 (void) strcpy(n->format, p->format);
397 } else if (special != 'v' && special != 'f')
398 n->format = NULL;
399
400 if (special != 'v') {
401 n->pad = p->pad;
402 if (n->ucolor) { // remove current cellcolor format in n ent
403 free(n->ucolor);
404 n->ucolor = NULL;
405 }
406 if (p->ucolor) { // copy new cellcolor format from p to n ent
407 n->ucolor = (struct ucolor *) malloc (sizeof(struct ucolor));
408 n->ucolor->fg = p->ucolor->fg;
409 n->ucolor->bg = p->ucolor->bg;
410 n->ucolor->bold = p->ucolor->bold;
411 n->ucolor->italic = p->ucolor->italic;
412 n->ucolor->dim = p->ucolor->dim;
413 n->ucolor->reverse = p->ucolor->reverse;
414 n->ucolor->standout = p->ucolor->standout;
415 n->ucolor->underline = p->ucolor->underline;
416 n->ucolor->blink = p->ucolor->blink;
417 }
418 }
419
420 // used for undoing / redoing cells that has errors
421 n->cellerror = p->cellerror;
422
423 if (special == 'c' && n->expr)
424 EvalJustOneVertex(n, 0);
425
426 n->flags |= is_changed;
427 n->row = p->row;
428 n->col = p->col;
429 return;
430 }
431
432
433 /**
434 * \brief TODO Write brief description
435 * \return NUM; STR; etc.
436 */
etype(register struct enode * e)437 int etype(register struct enode *e) {
438 if (e == (struct enode *)0)
439 return NUM;
440 switch (e->op) {
441 case UPPER: case LOWER: case CAPITAL:
442 case O_SCONST: case '#': case DATE: case FMT: case STINDEX:
443 case EXT: case LUA: case SVAL: case SUBSTR:
444 return (STR);
445
446 case '?':
447 case IF:
448 return (etype(e->e.o.right->e.o.left));
449
450 case 'f':
451 return (etype(e->e.o.right));
452
453 case O_VAR: {
454 register struct ent *p;
455 p = e->e.v.vp;
456 if (p->expr)
457 return (p->flags & is_strexpr ? STR : NUM);
458 else if (p->label)
459 return (STR);
460 else
461 return (NUM);
462 }
463
464 default:
465 return (NUM);
466 }
467 return -1;
468 }
469
470
471 /**
472 * \brief TODO Write a brief function description
473 * \details ignorelock is used when sorting so that locked cells
474 * can still be sorted
475 * \param[in] sr
476 * \param[in] sc
477 * \param[in] er
478 * \param[in] ec
479 * \param[in] ignorelock
480 * \param[in] mark_as_deleted
481 * \return none
482 */
erase_area(int sr,int sc,int er,int ec,int ignorelock,int mark_as_deleted)483 void erase_area(int sr, int sc, int er, int ec, int ignorelock, int mark_as_deleted) {
484 int r, c;
485 struct ent **pp;
486
487 if (sr > er) {
488 r = sr; sr = er; er = r;
489 }
490
491 if (sc > ec) {
492 c = sc; sc = ec; ec = c;
493 }
494
495 if (sr < 0)
496 sr = 0;
497 if (sc < 0)
498 sc = 0;
499 checkbounds(&er, &ec);
500
501 /* mark the ent as deleted
502 * Do a lookat() for the upper left and lower right cells of the range
503 * being erased to make sure they are included in the delete buffer so
504 * that pulling cells always works correctly even if the cells at one
505 * or more edges of the range are all empty.
506 */
507 (void) lookat(sr, sc);
508 (void) lookat(er, ec);
509 for (r = sr; r <= er; r++) {
510 for (c = sc; c <= ec; c++) {
511 pp = ATBL(tbl, r, c);
512 if (*pp && (!((*pp)->flags & is_locked) || ignorelock)) {
513
514 /* delete vertex in graph
515 only if this vertex is not referenced by other */
516 vertexT * v = getVertex(graph, *pp, 0);
517 if (v != NULL && v->back_edges == NULL )
518 destroy_vertex(*pp);
519
520 if (mark_as_deleted) {
521 mark_ent_as_deleted(*pp, TRUE);
522 } else {
523 clearent(*pp); // free memory
524 cleanent(*pp); // fill ent with empty values
525 mark_ent_as_deleted(*pp, FALSE);
526 }
527 *pp = NULL;
528 }
529 }
530 }
531 return;
532 }
533
534
535 /**
536 * \brief TODO Write a brief function description
537 * \details Function to copy an expression. It returns the copy.
538 * special = 1 means transpose
539 * special = 2 means copy from spreadsheet to undo struct
540 * \param[in] e
541 * \param[in] Rdelta
542 * \param[in] Cdelta
543 * \param[in] r1
544 * \param[in] c1
545 * \param[in] r2
546 * \param[in] c2
547 * \param[in] special
548 * \return none
549 */
copye(register struct enode * e,int Rdelta,int Cdelta,int r1,int c1,int r2,int c2,int special)550 struct enode * copye(register struct enode *e, int Rdelta, int Cdelta, int r1, int c1, int r2, int c2, int special) {
551 register struct enode * ret;
552 static struct enode * range = NULL;
553
554 if (e == (struct enode *) 0) {
555 ret = (struct enode *) 0;
556
557 } else if (e->op & REDUCE) {
558 int newrow, newcol;
559 ret = (struct enode *) scxmalloc((unsigned) sizeof (struct enode));
560 ret->op = e->op;
561 ret->e.r.left.expr = e->e.r.left.expr ? copye(e->e.r.left.expr, Rdelta, Cdelta, r1, c1, r2, c2, special) : NULL; // important to initialize
562 ret->e.r.right.expr = e->e.r.right.expr ? copye(e->e.r.right.expr, Rdelta, Cdelta, r1, c1, r2, c2, special) : NULL; // important to initialize
563 newrow = e->e.r.left.vf & FIX_ROW || e->e.r.left.vp->row < r1 || e->e.r.left.vp->row > r2 || e->e.r.left.vp->col < c1 || e->e.r.left.vp->col > c2 ? e->e.r.left.vp->row : special == 1 ? r1 + Rdelta + e->e.r.left.vp->col - c1 : e->e.r.left.vp->row + Rdelta;
564 newcol = e->e.r.left.vf & FIX_COL || e->e.r.left.vp->row < r1 || e->e.r.left.vp->row > r2 || e->e.r.left.vp->col < c1 || e->e.r.left.vp->col > c2 ? e->e.r.left.vp->col : special == 1 ? c1 + Cdelta + e->e.r.left.vp->row - r1 : e->e.r.left.vp->col + Cdelta;
565 ret->e.r.left.vp = lookat(newrow, newcol);
566 ret->e.r.left.vf = e->e.r.left.vf;
567 newrow = e->e.r.right.vf & FIX_ROW || e->e.r.right.vp->row < r1 || e->e.r.right.vp->row > r2 || e->e.r.right.vp->col < c1 || e->e.r.right.vp->col > c2 ? e->e.r.right.vp->row : special == 1 ? r1 + Rdelta + e->e.r.right.vp->col - c1 : e->e.r.right.vp->row + Rdelta;
568 newcol = e->e.r.right.vf & FIX_COL || e->e.r.right.vp->row < r1 || e->e.r.right.vp->row > r2 || e->e.r.right.vp->col < c1 || e->e.r.right.vp->col > c2 ? e->e.r.right.vp->col : special == 1 ? c1 + Cdelta + e->e.r.right.vp->row - r1 : e->e.r.right.vp->col + Cdelta;
569 ret->e.r.right.vp = lookat(newrow, newcol);
570 ret->e.r.right.vf = e->e.r.right.vf;
571 } else {
572 struct enode *temprange=0;
573 ret = (struct enode *) scxmalloc((unsigned) sizeof (struct enode));
574 ret->e.r.left.expr = e->e.r.left.expr ? copye(e->e.r.left.expr, Rdelta, Cdelta, r1, c1, r2, c2, special) : NULL; // important to initialize
575 ret->e.r.right.expr = e->e.r.right.expr ? copye(e->e.r.right.expr, Rdelta, Cdelta, r1, c1, r2, c2, special) : NULL; // important to initialize
576 ret->e.r.left.vp = e->e.r.left.vp;
577 ret->e.r.right.vp = e->e.r.right.vp;
578 ret->op = e->op;
579 switch (ret->op) {
580 case SUM:
581 case PROD:
582 case AVG:
583 case COUNT:
584 case STDDEV:
585 case MAX:
586 case MIN:
587 temprange = range;
588 range = e->e.o.left;
589 r1 = 0;
590 c1 = 0;
591 r2 = maxrow;
592 c2 = maxcol;
593 }
594 switch (ret->op) {
595 case 'v':
596 {
597 int newrow, newcol;
598 if (range && e->e.v.vp->row >= range->e.r.left.vp->row && e->e.v.vp->row <= range->e.r.right.vp->row && e->e.v.vp->col >= range->e.r.left.vp->col && e->e.v.vp->col <= range->e.r.right.vp->col) {
599 newrow = range->e.r.left.vf & FIX_ROW ? e->e.v.vp->row : e->e.v.vp->row + Rdelta;
600 newcol = range->e.r.left.vf & FIX_COL ? e->e.v.vp->col : e->e.v.vp->col + Cdelta;
601 } else {
602 newrow = e->e.v.vf & FIX_ROW || e->e.v.vp->row < r1 || e->e.v.vp->row > r2 || e->e.v.vp->col < c1 || e->e.v.vp->col > c2 ? e->e.v.vp->row : special == 1 ? r1 + Rdelta + e->e.v.vp->col - c1 : e->e.v.vp->row + Rdelta;
603 newcol = e->e.v.vf & FIX_COL || e->e.v.vp->row < r1 || e->e.v.vp->row > r2 || e->e.v.vp->col < c1 || e->e.v.vp->col > c2 ? e->e.v.vp->col : special == 1 ? c1 + Cdelta + e->e.v.vp->row - r1 : e->e.v.vp->col + Cdelta;
604 }
605 ret->e.v.vp = lookat(newrow, newcol);
606 ret->e.v.vf = e->e.v.vf;
607 break;
608 }
609 case 'k':
610 ret->e.k = e->e.k;
611 break;
612 case 'f':
613 case 'F':
614 if ((range && ret->op == 'F') || (!range && ret->op == 'f'))
615 Rdelta = Cdelta = 0;
616 ret->e.o.left = copye(e->e.o.left, Rdelta, Cdelta, r1, c1, r2, c2, special);
617 ret->e.o.right = (struct enode *)0;
618 break;
619 case '$':
620 case EXT:
621 ret->e.s = scxmalloc((unsigned) strlen(e->e.s)+1);
622 (void) strcpy(ret->e.s, e->e.s);
623 if (e->op == '$') /* Drop through if ret->op is EXT */
624 break;
625 default:
626 if (e->op == ERR_) {
627 //ret->e.o.left = NULL;
628 //ret->e.o.right = NULL;
629 //ret->e.o.right = (struct enode *)0;
630 break; /* fix #108 */
631 }
632 ret->e.o.left = copye(e->e.o.left, Rdelta, Cdelta, r1, c1, r2, c2, special);
633 ret->e.o.right = copye(e->e.o.right, Rdelta, Cdelta, r1, c1, r2, c2, special);
634 break;
635 }
636 switch (ret->op) {
637 case SUM:
638 case PROD:
639 case AVG:
640 case COUNT:
641 case STDDEV:
642 case MAX:
643 case MIN:
644 range = temprange;
645 }
646 }
647 return ret;
648 }
649
650
651 /**
652 * \brief dorowformat()
653 * \details: apply a row format in lines(size) to a row (r)
654 * \param[in] r
655 * \param[in] size
656 * \return none
657 */
dorowformat(int r,unsigned char size)658 void dorowformat(int r, unsigned char size) {
659 if (size < 1 || size > UCHAR_MAX || size > LINES - RESROW - 1) { sc_error("Invalid row format"); return; }
660
661 if (r >= maxrows && !growtbl(GROWROW, 0, r)) r = maxrows-1 ;
662 checkbounds(&r, &curcol);
663 row_format[r] = size;
664 if (! loading) modflg++;
665 return;
666 }
667
668
669 /**
670 * \brief TODO Write brief function description
671 * \details Note: Modified 9/17/90 THA to handle more formats.
672 * \param[in] c1
673 * \param[in] c2
674 * \param[in] w
675 * \param[in] p
676 * \param[in] r
677 * \return none
678 */
doformat(int c1,int c2,int w,int p,int r)679 void doformat(int c1, int c2, int w, int p, int r) {
680 register int i;
681 int crows = 0;
682 int ccols = c2;
683
684 if (c1 >= maxcols && !growtbl(GROWCOL, 0, c1)) c1 = maxcols-1 ;
685 if (c2 >= maxcols && !growtbl(GROWCOL, 0, c2)) c2 = maxcols-1 ;
686
687 if (w == 0) {
688 sc_info("Width too small - setting to 1");
689 w = 1;
690 }
691
692 if (! get_conf_int("nocurses") && w > COLS - rescol - 2) {
693 sc_info("Width too large - Maximum = %d", COLS - rescol - 2);
694 w = COLS - rescol - 2;
695 }
696
697 if (p > w) {
698 sc_info("Precision too large");
699 p = w;
700 }
701
702 checkbounds(&crows, &ccols);
703 if (ccols < c2) {
704 sc_error("Format statement failed to create implied column %d", c2);
705 return;
706 }
707
708 for (i = c1; i <= c2; i++)
709 fwidth[i] = w, precision[i] = p, realfmt[i] = r;
710
711 modflg++;
712 return;
713
714 }
715
716
717 /**
718 * \brief TODO Document formatcol)
719 * \param[in] c
720 * \return none
721 */
formatcol(int c)722 void formatcol(int c) {
723 int arg = 1;
724 int i;
725
726 switch (c) {
727 case '<':
728 case 'h':
729 case OKEY_LEFT:
730 for (i = curcol; i < curcol + arg; i++) {
731 if (fwidth[i] <= 2) {
732 sc_error("Cannot resize column any longer");
733 return;
734 }
735 fwidth[i]--;
736 if (fwidth[i] <= 1)
737 fwidth[i] = 1;
738 }
739 modflg++;
740 break;
741 case '>':
742 case 'l':
743 case OKEY_RIGHT:
744 for (i = curcol; i < curcol + arg; i++) {
745 fwidth[i]++;
746 if (fwidth[i] > COLS - rescol - 2)
747 fwidth[i] = COLS - rescol - 2;
748 }
749 modflg++;
750 break;
751 case '-':
752 for (i = curcol; i < curcol + arg; i++) {
753 precision[i]--;
754 if (precision[i] < 0)
755 precision[i] = 0;
756 }
757 modflg++;
758 break;
759 case '+':
760 for (i = curcol; i < curcol + arg; i++)
761 precision[i]++;
762 modflg++;
763 break;
764 }
765 sc_info("Current format is %d %d %d", fwidth[curcol], precision[curcol], realfmt[curcol]);
766 ui_update(TRUE);
767 return;
768 }
769
770
771 /**
772 * \brief TODO Document insert_row()
773 * \details Insert a single rox. It will be inserted before currow.
774 * if after is 0; after if it is 1.
775 * \param[in] after
776 * \returnsnone
777 */
insert_row(int after)778 void insert_row(int after) {
779 int r, c;
780 struct ent ** tmprow, ** pp, ** qq;
781 struct ent * p;
782 int lim = maxrow - currow + 1;
783
784 if (currow > maxrow) maxrow = currow;
785 maxrow++;
786 lim = maxrow - lim + after;
787 if (maxrow >= maxrows && ! growtbl(GROWROW, maxrow, 0)) return;
788
789 tmprow = tbl[maxrow];
790 for (r = maxrow; r > lim; r--) {
791 row_hidden[r] = row_hidden[r-1];
792 row_frozen[r] = row_frozen[r-1];
793 row_format[r] = row_format[r-1];
794 tbl[r] = tbl[r-1];
795 for (c = 0, pp = ATBL(tbl, r, 0); c < maxcols; c++, pp++)
796 if (*pp) (*pp)->row = r;
797 }
798 tbl[r] = tmprow; // the last row is never used
799 row_format[r] = 1;
800
801 // if padding exists in the old currow, we copy it to the new row!
802 for (c = 0; c < maxcols; c++) {
803 if (r >= 0 && (qq = ATBL(tbl, r+1, c)) && (*qq) && (*qq)->pad) {
804 p = lookat(r, c);
805 p->pad = (*qq)->pad;
806 }
807 }
808
809 modflg++;
810 return;
811 }
812
813
814 /**
815 * \brief Insert new column
816 * \details Insert a cingle column. The column will be inserted
817 * BEFORE CURCOL if after is 0;
818 * AFTER CURCOL if it is 1.
819 * \param[in] after
820 * \return none
821 */
insert_col(int after)822 void insert_col(int after) {
823 int r, c;
824 register struct ent ** pp, ** qq;
825 struct ent * p;
826 int lim = maxcol - curcol - after + 1;
827
828 if (curcol + after > maxcol)
829 maxcol = curcol + after;
830 maxcol++;
831
832 if ((maxcol >= maxcols) && !growtbl(GROWCOL, 0, maxcol))
833 return;
834
835 for (c = maxcol; c >= curcol + after + 1; c--) {
836 fwidth[c] = fwidth[c-1];
837 precision[c] = precision[c-1];
838 realfmt[c] = realfmt[c-1];
839 col_hidden[c] = col_hidden[c-1];
840 col_frozen[c] = col_frozen[c-1];
841 }
842 for (c = curcol + after; c - curcol - after < 1; c++) {
843 fwidth[c] = DEFWIDTH;
844 precision[c] = DEFPREC;
845 realfmt[c] = DEFREFMT;
846 col_hidden[c] = FALSE;
847 col_frozen[c] = FALSE;
848 }
849
850 for (r=0; r <= maxrow; r++) {
851 pp = ATBL(tbl, r, maxcol);
852 for (c = lim; --c >= 0; pp--)
853 if ((pp[0] = pp[-1])) pp[0]->col++;
854
855 pp = ATBL(tbl, r, curcol + after);
856 for (c = curcol + after; c - curcol - after < 1; c++, pp++)
857 *pp = (struct ent *) 0;
858 }
859
860 // if padding exists in the old curcol, we copy it to the new col!
861 for (r = 0; r < maxrows; r++) {
862 if (c >= 0 && (qq = ATBL(tbl, r, c+1)) && (*qq) && (*qq)->pad) {
863 p = lookat(r, c);
864 p->pad = (*qq)->pad;
865 }
866 }
867
868 curcol += after;
869 modflg++;
870 return;
871 }
872
873
874 /**
875 * \brief Delete a row
876 * \param[in] row
877 * \param[in] mult
878 * \return none
879 */
deleterow(int row,int mult)880 void deleterow(int row, int mult) {
881 if (any_locked_cells(row, 0, row + mult - 1, maxcol)) {
882 sc_error("Locked cells encountered. Nothing changed");
883 return;
884 }
885 #ifdef UNDO
886 create_undo_action();
887 // here we save in undostruct, all the ents that depends on the deleted one (before change)
888 ents_that_depends_on_range(row, 0, row + mult - 1, maxcol);
889 copy_to_undostruct(row, 0, row + mult - 1, maxcol, UNDO_DEL, HANDLE_DEPS, NULL);
890 save_undo_range_shift(-mult, 0, row, 0, row - 1 + mult, maxcol);
891 int i;
892 for (i=row; i < row + mult; i++) {
893 add_undo_row_format(i, 'R', row_format[currow]);
894 if (row_hidden[i]) undo_hide_show(i, -1, 's', 1);
895 //else undo_hide_show(i, -1, 'h', 1);
896 }
897 #endif
898
899 fix_marks(-mult, 0, row + mult - 1, maxrow, 0, maxcol);
900 if (!loading) yank_area(row, 0, row + mult - 1, maxcol, 'r', mult);
901
902 // do the job
903 int_deleterow(row, mult);
904
905 //flush_saved(); // we have to flush only at exit. this is in case we want to UNDO
906
907 if (!loading) modflg++;
908
909 #ifdef UNDO
910 // here we save in undostruct, just the ents that depends on the deleted one (after the change)
911 copy_to_undostruct(0, 0, -1, -1, UNDO_ADD, HANDLE_DEPS, NULL);
912
913 extern struct ent_ptr * deps;
914 if (deps != NULL) free(deps);
915 deps = NULL;
916
917 end_undo_action();
918 #endif
919 return;
920 }
921
922
923 /**
924 * \brief Delete a row
925 * \details Delete a row - internal function
926 * \param[in] row row to delete
927 * \param[in] multi commands multiplier (usually 1)
928 * \return none
929 */
int_deleterow(int row,int mult)930 void int_deleterow(int row, int mult) {
931 register struct ent ** pp;
932 register struct ent * q;
933 int r, c;
934
935 //if (currow > maxrow) return;
936
937 while (mult--) {
938 // we need to decrease row of the row that we delete if
939 // other cells refers to this one.
940 for (c = 0; c < maxcols; c++) {
941 if (row <= maxrow) {
942 pp = ATBL(tbl, row, c);
943 if ((q = *ATBL(tbl, row, c)) != NULL && q->row > 0) q->row--;
944 }
945 }
946 sync_refs();
947
948 // and after that the erase_area of the deleted row
949 erase_area(row, 0, row, maxcol, 0, 1); //important: this mark the ents as deleted
950
951 // and we decrease ->row of all rows after the deleted one
952 for (r = row; r < maxrows - 1; r++) {
953 for (c = 0; c < maxcols; c++) {
954 if (r <= maxrow) {
955 pp = ATBL(tbl, r, c);
956 pp[0] = *ATBL(tbl, r+1, c);
957 if ( pp[0] ) pp[0]->row--;
958 }
959 }
960 //update row_hidden and row_format here
961 row_hidden[r] = row_hidden[r+1];
962 row_frozen[r] = row_frozen[r+1];
963 row_format[r] = row_format[r+1];
964 }
965
966 rebuild_graph(); //TODO CHECK HERE WHY REBUILD IS NEEDED. See NOTE1 in shift.c
967 sync_refs();
968 EvalAll();
969 maxrow--;
970 }
971 return;
972 }
973
974
975 /**
976 * \brief Document ljustify()
977 * \param[in] sr
978 * \param[in] sc
979 * \param[in] er
980 * \param[in] ec
981 * \return none
982 */
ljustify(int sr,int sc,int er,int ec)983 void ljustify(int sr, int sc, int er, int ec) {
984 struct ent *p;
985 int i, j;
986
987 if (sr > er) {
988 i = sr;
989 sr = er;
990 er = i;
991 }
992 if (sc > ec) {
993 i = sc;
994 sc = ec;
995 ec = i;
996 }
997 for (i = sr; i <= er; i++) {
998 for (j = sc; j <= ec; j++) {
999 p = *ATBL(tbl, i, j);
1000 if (p && p->label) {
1001 p->flags &= ~is_label;
1002 p->flags |= is_leftflush | is_changed;
1003 modflg++;
1004 }
1005 }
1006 }
1007 return;
1008 }
1009
1010
1011 /**
1012 * \brief TODO Document rjustify()
1013 * \param[in] sr
1014 * \param[in] sc
1015 * \param[in] er
1016 * \param[in] ec
1017 * \return none
1018 */
rjustify(int sr,int sc,int er,int ec)1019 void rjustify(int sr, int sc, int er, int ec) {
1020 struct ent *p;
1021 int i, j;
1022
1023 if (sr > er) {
1024 i = sr;
1025 sr = er;
1026 er = i;
1027 }
1028 if (sc > ec) {
1029 i = sc;
1030 sc = ec;
1031 ec = i;
1032 }
1033 for (i = sr; i <= er; i++) {
1034 for (j = sc; j <= ec; j++) {
1035 p = *ATBL(tbl, i, j);
1036 if (p && p->label) {
1037 p->flags &= ~(is_label | is_leftflush);
1038 p->flags |= is_changed;
1039 modflg++;
1040 }
1041 }
1042 }
1043 return;
1044 }
1045
1046
1047 /**
1048 * \brief TODO Document center()
1049 * \param[in] sr
1050 * \param[in] sc
1051 * \param[in] er
1052 * \param[in] ec
1053 * \return none
1054 */
center(int sr,int sc,int er,int ec)1055 void center(int sr, int sc, int er, int ec) {
1056 struct ent *p;
1057 int i, j;
1058
1059 if (sr > er) {
1060 i = sr;
1061 sr = er;
1062 er = i;
1063 }
1064 if (sc > ec) {
1065 i = sc;
1066 sc = ec;
1067 ec = i;
1068 }
1069 for (i = sr; i <= er; i++) {
1070 for (j = sc; j <= ec; j++) {
1071 p = *ATBL(tbl, i, j);
1072 if (p && p->label) {
1073 p->flags &= ~is_leftflush;
1074 p->flags |= is_label | is_changed;
1075 modflg++;
1076 }
1077 }
1078 }
1079 return;
1080 }
1081
1082
1083 /**
1084 * @brief TODO Document chg_mode
1085 * \param[in] strcmd
1086 * \return none
1087 */
chg_mode(char strcmd)1088 void chg_mode(char strcmd){
1089 lastmode = curmode;
1090 switch (strcmd) {
1091 case '=':
1092 curmode = INSERT_MODE;
1093 break;
1094 case '<':
1095 curmode = INSERT_MODE;
1096 break;
1097 case '>':
1098 curmode = INSERT_MODE;
1099 break;
1100 case '\\':
1101 curmode = INSERT_MODE;
1102 break;
1103 case 'E':
1104 curmode = EDIT_MODE;
1105 break;
1106 case 'e':
1107 curmode = EDIT_MODE;
1108 break;
1109 case ':':
1110 curmode = COMMAND_MODE;
1111 break;
1112 case '.':
1113 curmode = NORMAL_MODE;
1114 break;
1115 case 'v':
1116 curmode = VISUAL_MODE;
1117 break;
1118 }
1119 return;
1120 }
1121
1122
1123 /**
1124 * \brief Delete selected cells
1125 * \details Delete selected cell or range of cells.
1126 * \return none
1127 */
del_selected_cells()1128 void del_selected_cells() {
1129 int tlrow = currow;
1130 int tlcol = curcol;
1131 int brrow = currow;
1132 int brcol = curcol;
1133
1134 // is we delete a range
1135 if (is_range_selected() != -1) {
1136 srange * r = get_selected_range();
1137 tlrow = r->tlrow;
1138 tlcol = r->tlcol;
1139 brrow = r->brrow;
1140 brcol = r->brcol;
1141 }
1142
1143 if (any_locked_cells(tlrow, tlcol, brrow, brcol)) {
1144 sc_error("Locked cells encountered. Nothing changed");
1145 return;
1146 }
1147
1148 if (is_range_selected() != -1)
1149 yank_area(tlrow, tlcol, brrow, brcol, 'a', 1);
1150 else
1151 yank_area(tlrow, tlcol, brrow, brcol, 'e', 1);
1152
1153 #ifdef UNDO
1154 create_undo_action();
1155 // here we save in undostruct, all the ents that depends on the deleted one (before change)
1156 ents_that_depends_on_range(tlrow, tlcol, brrow, brcol);
1157 copy_to_undostruct(tlrow, tlcol, brrow, brcol, UNDO_DEL, HANDLE_DEPS, NULL);
1158 #endif
1159
1160 erase_area(tlrow, tlcol, brrow, brcol, 0, 0); //important: this erases the ents, but does NOT mark them as deleted
1161 modflg++;
1162 sync_refs();
1163 //flush_saved(); DO NOT UNCOMMENT! flush_saved shall not be called other than at exit.
1164
1165 EvalRange(tlrow, tlcol, brrow, brcol);
1166
1167 #ifdef UNDO
1168 // here we save in undostruct, all the ents that depends on the deleted one (after the change)
1169 copy_to_undostruct(tlrow, tlcol, brrow, brcol, UNDO_ADD, HANDLE_DEPS, NULL);
1170 extern struct ent_ptr * deps;
1171 if (deps != NULL) free(deps);
1172 deps = NULL;
1173 end_undo_action();
1174 #endif
1175
1176 return;
1177 }
1178
1179
1180 /**
1181 * \brief Enter cell content on a cell
1182 * \details Enter cell content on a cell.
1183 * Covers commands LET, LABEL, LEFTSTRING, and RIGHTSTRING
1184 * \param[in] r
1185 * \param[in] c
1186 * \param[in] submode
1187 * \param[in] content
1188 * \return none
1189 */
enter_cell_content(int r,int c,char * submode,wchar_t * content)1190 void enter_cell_content(int r, int c, char * submode, wchar_t * content) {
1191 (void) swprintf(interp_line, BUFFERSIZE, L"%s %s = %ls", submode, v_name(r, c), content);
1192 send_to_interp(interp_line);
1193 if (get_conf_int("autocalc") && ! loading) EvalRange(r, c, r, c);
1194 }
1195
1196
1197 /**
1198 * @brief Send command to interpreter
1199 * \details Send command to interpreter
1200 * wide_char version
1201 * \param[in] oper
1202 * \return none
1203 */
send_to_interp(wchar_t * oper)1204 void send_to_interp(wchar_t * oper) {
1205 if (get_conf_int("nocurses")) {
1206 int pos = -1;
1207 if ((pos = wstr_in_wstr(oper, L"\n")) != -1)
1208 oper[pos] = L'\0';
1209 sc_debug("Interp GOT: %ls", oper);
1210 }
1211 wcstombs(line, oper, BUFFERSIZE);
1212
1213 linelim = 0;
1214 yyparse();
1215 linelim = -1;
1216 line[0]='\0';
1217 // commented on 28/04/2021. EvalAll should be used only on certain ocasions.
1218 // Use EvalRange instead when possible, and certainly not here everytime sending to interp.
1219 //if (get_conf_int("autocalc") && ! loading) EvalAll();
1220 return;
1221 }
1222
1223
1224 /**
1225 * \brief Return a pointer to a cell's [struct ent *]
1226 * Return a pointer to a cell's [struct ent *], creating if needed
1227 * \param[in] row
1228 * \param[in] col
1229 * \return none
1230 */
lookat(int row,int col)1231 struct ent * lookat(int row, int col) {
1232 register struct ent **pp;
1233
1234 checkbounds(&row, &col);
1235 pp = ATBL(tbl, row, col);
1236 if ( *pp == NULL) {
1237 *pp = (struct ent *) malloc( (unsigned) sizeof(struct ent) );
1238 (*pp)->label = (char *) 0;
1239 (*pp)->flags = may_sync;
1240 (*pp)->expr = (struct enode *) 0;
1241 (*pp)->trigger = (struct trigger *) 0;
1242 (*pp)->v = (double) 0.0;
1243 (*pp)->format = (char *) 0;
1244 (*pp)->cellerror = CELLOK;
1245 (*pp)->next = NULL;
1246 (*pp)->ucolor = NULL;
1247 (*pp)->pad = 0;
1248 }
1249 (*pp)->row = row;
1250 (*pp)->col = col;
1251 if (row > maxrow) maxrow = row;
1252 if (col > maxcol) maxcol = col;
1253 return (*pp);
1254 }
1255
1256
1257 /**
1258 * \brief Blank an ent
1259 * \param[in] p
1260 * \return none
1261 */
cleanent(struct ent * p)1262 void cleanent(struct ent * p) {
1263 if (!p) return;
1264 p->label = (char *) 0;
1265 // do not uncomment. if so mod erase_area.
1266 //p->row = 0;
1267 //p->col = 0;
1268
1269 p->flags = may_sync;
1270 p->expr = (struct enode *) 0;
1271 p->v = (double) 0.0;
1272 p->format = (char *) 0;
1273 p->cellerror = CELLOK;
1274 p->next = NULL;
1275 p->ucolor = NULL;
1276 p->pad = 0;
1277 return;
1278 }
1279
1280
1281 /**
1282 * \brief Free memory of an ent and its contents
1283 * \param[in] v
1284 * \return none
1285 */
clearent(struct ent * v)1286 void clearent(struct ent * v) {
1287 if (!v) return;
1288
1289 label(v, "", -1);
1290 v->v = (double)0;
1291
1292 if (v->expr) efree(v->expr);
1293 v->expr = NULL;
1294
1295 if (v->format) scxfree(v->format);
1296 v->format = NULL;
1297
1298 if (v->ucolor) free(v->ucolor);
1299 v->ucolor = NULL;
1300
1301 v->flags = is_changed;
1302
1303 modflg++;
1304
1305 return;
1306 }
1307
1308
1309 /**
1310 * \brief Moves curcol back one displayed column
1311 * \param[in] arg
1312 * \return lookat
1313 */
back_col(int arg)1314 struct ent * back_col(int arg) {
1315 int c = curcol;
1316
1317 while (--arg >= 0) {
1318 if (c) {
1319 curcol = --c;
1320 } else {
1321 sc_info ("At column A");
1322 break;
1323 }
1324 while (col_hidden[c] && c) {
1325 curcol = --c;
1326 }
1327 }
1328 if (curcol < offscr_sc_cols)
1329 offscr_sc_cols = curcol;
1330
1331 return lookat(currow, c);
1332 }
1333
1334
1335 /**
1336 * \brief Moves curcol forward one displayed column
1337 * \param[in] arg
1338 * \return lookat
1339 */
forw_col(int arg)1340 struct ent * forw_col(int arg) {
1341 int c = curcol;
1342
1343 while (--arg >= 0) {
1344 if (c < maxcols - 1) {
1345 c++;
1346 } else {
1347 if (! growtbl(GROWCOL, 0, arg)) { /* get as much as needed */
1348 sc_error("cannot grow");
1349 return lookat(currow, curcol);
1350 } else {
1351 c++;
1352 }
1353 }
1354 while (col_hidden[c] && c < maxcols - 1) {
1355 c++;
1356 }
1357
1358 }
1359 return lookat(currow, c);
1360 }
1361
1362
1363 /**
1364 * \brief Move currow forward one displayed row
1365 * \param[in] arg
1366 * \return lookat
1367 */
forw_row(int arg)1368 struct ent * forw_row(int arg) {
1369 int r = currow;
1370
1371 while (arg--) {
1372 if (r < maxrows - 1)
1373 r++;
1374 else {
1375 if (! growtbl(GROWROW, arg, 0)) {
1376 sc_error("cannot grow");
1377 return lookat(currow, curcol);
1378 } else
1379 r++;
1380 }
1381 while (row_hidden[r] && r < maxrows - 1) r++;
1382 }
1383 return lookat(r, curcol);
1384 }
1385
1386
1387 /**
1388 * \brief Moves currow backward on displayed row
1389 * \return lookat
1390 */
back_row(int arg)1391 struct ent * back_row(int arg) {
1392 int r = currow;
1393
1394 while (--arg >= 0) {
1395 if (r) {
1396 --r;
1397 } else {
1398 sc_info ("At row 0");
1399 break;
1400 }
1401 while (row_hidden[r] && r) --r;
1402 }
1403 return lookat(r, curcol);
1404 }
1405
1406
1407 /**
1408 * \brief Document scroll_down()
1409 * \param[in] n
1410 * \return none
1411 */
scroll_down(int n)1412 void scroll_down(int n) {
1413 while (n--) {
1414 int last, currow_orig = currow;
1415 /* find last mobile row */
1416 calc_mobile_rows(&last);
1417 /* move to next non-hidden non-frozen row */
1418 do {
1419 lookat(++last, curcol);
1420 if (last >= maxrows)
1421 return;
1422 } while (row_hidden[last] || row_frozen[last]);
1423 /* this will adjust offscr_sc_rows */
1424 currow = last;
1425 calc_mobile_rows(NULL);
1426 /* restore currow */
1427 currow = currow_orig;
1428 if (currow < offscr_sc_rows)
1429 currow = offscr_sc_rows;
1430 unselect_ranges();
1431 }
1432 }
1433
1434
1435 /**
1436 * @brief Document scroll_up()
1437 * \param[in] n
1438 * \return none
1439 */
scroll_up(int n)1440 void scroll_up(int n) {
1441 while (n--) {
1442 int first, last, currow_orig = currow;
1443 /* move to previous non-hidden non-frozen row */
1444 first = offscr_sc_rows;
1445 do {
1446 if (--first < 0)
1447 return;
1448 } while (row_hidden[first] || row_frozen[first]);
1449 offscr_sc_rows = first;
1450 /* find corresponding last mobile row */
1451 currow = first;
1452 calc_mobile_rows(&last);
1453 /* restore/adjust currow */
1454 currow = currow_orig;
1455 if (currow > last)
1456 currow = last;
1457 unselect_ranges();
1458 }
1459 }
1460
1461
1462 /**
1463 * \brief TODO Document go_home()
1464 * \return lookat
1465 */
go_home()1466 struct ent * go_home() {
1467 return lookat(0, 0);
1468 }
1469
1470
1471 /**
1472 * \brief vert_top() - for command H in normal mode
1473 * \return lookat
1474 */
vert_top()1475 struct ent * vert_top() {
1476 int r = offscr_sc_rows;
1477 while (row_hidden[r] || row_frozen[r]) r++;
1478 return lookat(r, curcol);
1479 }
1480
1481
1482 /**
1483 * \brief vert_bottom() - for command L in normal mode
1484 * \return lookat
1485 */
vert_bottom()1486 struct ent * vert_bottom() {
1487 int last;
1488 calc_mobile_rows(&last);
1489 return lookat(last, curcol);
1490 }
1491
1492
1493 /**
1494 * \brief vert_middle() - for command M in normal mode
1495 * \return lookat
1496 */
vert_middle()1497 struct ent * vert_middle() {
1498 int i;
1499 int midscreen_pos = (LINES - RESROW - RESCOLHEADER - 1)/2;
1500 int curr_pos = 0;
1501 int mobile_rows = calc_mobile_rows(NULL);
1502
1503 for (i = 0; i < maxrows; i++) {
1504 if (row_hidden[i])
1505 continue;
1506 if (! row_frozen[i]) {
1507 if (i < offscr_sc_rows)
1508 continue;
1509 if (--mobile_rows < 0)
1510 continue;
1511 }
1512
1513 /* compare middle of current row against middle of screen */
1514 if (curr_pos + (row_format[i] - 1)/2 >= midscreen_pos)
1515 return lookat(i, curcol);
1516
1517 curr_pos += row_format[i];
1518 }
1519 return NULL;
1520 }
1521
1522
1523 /**
1524 * \brief go_end(): go to last valid cell of grid
1525 * \return lookat; NULL otherwise
1526 */
go_end()1527 struct ent * go_end() {
1528 int r = 0, c = 0;
1529 int raux = r, caux = c;
1530 register struct ent *p;
1531 do {
1532 if (c < maxcols - 1)
1533 c++;
1534 else {
1535 if (r < maxrows - 1) {
1536 r++;
1537 c = 0;
1538 } else break;
1539 }
1540 if (VALID_CELL(p, r, c) && ! col_hidden[c] && ! row_hidden[r]) { raux = r; caux = c; }
1541 } while ( r < maxrows || c < maxcols );
1542 if ( ! VALID_CELL(p, r, c) && ! col_hidden[c] && ! row_hidden[r] )
1543 return lookat(raux, caux);
1544 return NULL;
1545 }
1546
1547
1548 /**
1549 * \brief TODO Document tick()
1550 * \details if ticks a cell, returns struct ent *
1551 * if ticks a range, return struct ent * to top left cell
1552 * \return lookat; NULL otherwise
1553 */
tick(char ch)1554 struct ent * tick(char ch) {
1555 int r, c;
1556 struct mark * m = get_mark(ch);
1557 //tick cell
1558 r = m->row;
1559
1560 if (r != -1) {
1561 checkbounds(&r, &curcol);
1562 return lookat(r, m->col);
1563 }
1564
1565 // tick range
1566 if (curmode != VISUAL_MODE) {
1567 r = m->rng->tlrow;
1568 c = m->rng->tlcol;
1569 m->rng->selected = 1;
1570 checkbounds(&r, &c);
1571 return lookat(r, c);
1572 }
1573 return NULL;
1574 }
1575
1576
1577 /**
1578 * \brief TODO Document scroll_right()
1579 * \param[in] n
1580 * \return none
1581 */
scroll_right(int n)1582 void scroll_right(int n) {
1583 while (n--) {
1584 int last, curcol_orig = curcol;
1585 /* find last mobile column */
1586 calc_mobile_cols(&last);
1587 /* move to next non-hidden non-frozen column */
1588 do {
1589 lookat(currow, ++last);
1590 if (last >= maxcols)
1591 return;
1592 } while (col_hidden[last] || col_frozen[last]);
1593 /* this will adjust offscr_sc_cols */
1594 curcol = last;
1595 calc_mobile_cols(NULL);
1596 /* restore curcol */
1597 curcol = curcol_orig;
1598 if (curcol < offscr_sc_cols)
1599 curcol = offscr_sc_cols;
1600 unselect_ranges();
1601 }
1602 }
1603
1604 /**
1605 * @brief TODO Document scroll_left()
1606 * \param[in] n
1607 * \return none
1608 */
scroll_left(int n)1609 void scroll_left(int n) {
1610 while (n--) {
1611 int first, last, curcol_orig = curcol;
1612 /* move to previous non-hidden non-frozen column */
1613 first = offscr_sc_cols;
1614 do {
1615 if (--first < 0)
1616 return;
1617 } while (col_hidden[first] || col_frozen[first]);
1618 offscr_sc_cols = first;
1619 /* find corresponding last mobile column */
1620 curcol = first;
1621 calc_mobile_cols(&last);
1622 /* restore/adjust curcol */
1623 curcol = curcol_orig;
1624 if (curcol > last)
1625 curcol = last;
1626 unselect_ranges();
1627 }
1628 }
1629
1630
1631 /**
1632 * \brief TODO Document left_limit()
1633 *
1634 * \return lookat
1635 */
left_limit()1636 struct ent * left_limit() {
1637 int c = 0;
1638 while ( col_hidden[c] && c < curcol ) c++;
1639 return lookat(currow, c);
1640 }
1641
1642
1643 /**
1644 * \brief right_limit()
1645 * \details get the last valid cell to the right
1646 * \param[in] row where to check
1647 * \return lookat
1648 */
right_limit(int row)1649 struct ent * right_limit(int row) {
1650 register struct ent *p;
1651 int c = maxcols - 1;
1652 while ( (! VALID_CELL(p, row, c) && c > 0) || col_hidden[c]) c--;
1653 return lookat(row, c);
1654 }
1655
1656
1657 /**
1658 * \brief TODO Document goto_top()
1659 *
1660 * \return lookat
1661 */
goto_top()1662 struct ent * goto_top() {
1663 int r = 0;
1664 while ( row_hidden[r] && r < currow ) r++;
1665 return lookat(r, curcol);
1666 }
1667
1668
1669 /**
1670 * \brief TODO Document goto_bottom()
1671 *
1672 * \return lookat
1673 */
1674 // FIXME to handle freeze rows/cols
goto_bottom()1675 struct ent * goto_bottom() {
1676 register struct ent *p;
1677 int r = maxrows - 1;
1678 while ( (! VALID_CELL(p, r, curcol) && r > 0) || row_hidden[r]) r--;
1679 return lookat(r, curcol);
1680 }
1681
1682
1683 /**
1684 * \brief TODO Document goto_last_col()
1685 * traverse the table and see which is the max column that has content
1686 * this is because maxcol changes when moving cursor.
1687 * this function is used when exporting files
1688 * \return lookat
1689 */
goto_last_col()1690 struct ent * goto_last_col() {
1691 int r, mr = maxrows;
1692 int c, mc = 0;
1693 register struct ent *p;
1694 int rf = 0;
1695
1696 for (r=0; r<mr; r++) {
1697 for (c=0; c<maxcols; c++) {
1698 if (c >= mc && VALID_CELL(p, r, c)) { mc = c; rf = r; }
1699 }
1700 }
1701 return lookat(rf, mc);
1702 }
1703
1704
1705 /**
1706 * @brief TODO Document go_forward()
1707 *
1708 * \return lookat
1709 */
go_forward()1710 struct ent * go_forward() {
1711 int r = currow, c = curcol;
1712 int r_ori = r, c_ori = c;
1713 register struct ent * p;
1714 do {
1715 if (c < maxcols - 1) {
1716 c++;
1717 } else {
1718 if (r < maxrows - 1) {
1719 r++;
1720 c = 0;
1721 } else break;
1722 }
1723 if (VALID_CELL(p, r, c) && ! col_hidden[c] && ! row_hidden[r] )
1724 return lookat(r, c);
1725 } while (r < maxrows || c < maxcols);
1726
1727 return lookat(r_ori, c_ori);
1728 }
1729
1730
1731 /**
1732 * \brief TODO Document go_bol()
1733 *
1734 * \return lookat
1735 */
go_bol()1736 struct ent * go_bol() {
1737 return lookat(currow, offscr_sc_cols);
1738 }
1739
1740
1741 /**
1742 * \brief TODO Document go_eol()
1743 * \return none
1744 */
go_eol()1745 struct ent * go_eol() {
1746 int last_col;
1747 calc_mobile_cols(&last_col);
1748 return lookat(currow, last_col);
1749 }
1750
1751
1752 /**
1753 * \brief TODO Document horiz_middle()
1754 * \return lookat; NULL otherwise
1755 */
horiz_middle()1756 struct ent * horiz_middle() {
1757 int i;
1758 int midscreen_pos = (COLS - rescol - 1)/2;
1759 int curr_pos = 0;
1760 int mobile_cols = calc_mobile_cols(NULL);
1761
1762 for (i = 0; i < maxcols; i++) {
1763 if (col_hidden[i])
1764 continue;
1765 if (! col_frozen[i]) {
1766 if (i < offscr_sc_cols)
1767 continue;
1768 if (--mobile_cols < 0)
1769 continue;
1770 }
1771
1772 /* compare middle of current col against middle of screen */
1773 if (curr_pos + (fwidth[i] - 1)/2 >= midscreen_pos)
1774 return lookat(currow, i);
1775
1776 curr_pos += fwidth[i];
1777 }
1778 return NULL;
1779 }
1780
1781
1782 /**
1783 * \brief TODO Document go_backward()
1784 *
1785 * \return lookat
1786 */
go_backward()1787 struct ent * go_backward() {
1788 int r = currow, c = curcol;
1789 int r_ori = r, c_ori = c;
1790 register struct ent * p;
1791 do {
1792 if (c)
1793 c--;
1794 else {
1795 if (r) {
1796 r--;
1797 c = maxcols - 1;
1798 } else break;
1799 }
1800 if ( VALID_CELL(p, r, c) && ! col_hidden[c] && ! row_hidden[r] )
1801 return lookat(r, c);
1802 } while ( currow || curcol );
1803
1804 return lookat(r_ori, c_ori);
1805 }
1806
1807
1808 /**
1809 * \brief TODO Document auto_justify()
1810 *
1811 * \param[in] ci
1812 * \param[in] cf
1813 * \param[in] min
1814 *
1815 * \return none
1816 */
auto_justify(int ci,int cf,int min)1817 void auto_justify(int ci, int cf, int min) {
1818 // column width is not set below the min value
1819 int r, c, sum = 0;
1820 char field[1024] = "";
1821 struct ent * p;
1822 wchar_t widestring[BUFFERSIZE] = { L'\0' };
1823 mbstate_t state;
1824 size_t result;
1825 const char * mbsptr;
1826
1827 #ifdef UNDO
1828 create_undo_action();
1829 #endif
1830
1831 checkbounds(&maxrow, &cf);
1832
1833 for (c = ci; c <= cf; c++) {
1834 #ifdef UNDO
1835 add_undo_col_format(c, 'R', fwidth[c], precision[c], realfmt[c]);
1836 #endif
1837 fwidth[c] = min;
1838 for (r = 0; r <= maxrow; r++) {
1839 if ((p = *ATBL(tbl, r, c)) != NULL) {
1840 sum = 0;
1841 if (p->pad) sum += p->pad;
1842 if (p->label) {
1843 memset( &state, '\0', sizeof state );
1844 mbsptr = p->label;
1845 result = mbsrtowcs(widestring, &mbsptr, BUFFERSIZE, &state);
1846 if ( result != (size_t)-1 )
1847 sum += wcswidth(widestring, wcslen(widestring));
1848 }
1849 if (p->flags & is_valid) {
1850 sprintf(field, "%.*f", precision[c], p->v);
1851 sum += strlen(field);
1852 }
1853 if (sum > fwidth[c] && sum < COLS-rescol)
1854 fwidth[c] = sum;
1855 }
1856 }
1857 #ifdef UNDO
1858 add_undo_col_format(c, 'A', fwidth[c], precision[c], realfmt[c]);
1859 #endif
1860 }
1861 #ifdef UNDO
1862 end_undo_action();
1863 #endif
1864
1865 return;
1866 }
1867
1868
1869 /**
1870 * \brief Delete a cell expression and turn into constant
1871 *
1872 * \details Deletes the expression associated with a cell and
1873 * turns it into a constant containing whatever was on the screen.
1874 *
1875 * \param[in] sr
1876 * \param[in] sc
1877 * \param[in] er
1878 * \param[in] ec
1879 *
1880 * \return none
1881 */
valueize_area(int sr,int sc,int er,int ec)1882 void valueize_area(int sr, int sc, int er, int ec) {
1883 int r, c;
1884 struct ent *p;
1885
1886 if (sr > er) {
1887 r = sr; sr = er; er= r;
1888 }
1889
1890 if (sc > ec) {
1891 c = sc; sc = ec; ec= c;
1892 }
1893
1894 if (sr < 0)
1895 sr = 0;
1896 if (sc < 0)
1897 sc = 0;
1898 checkbounds(&er, &ec);
1899
1900 #ifdef UNDO
1901 create_undo_action();
1902 copy_to_undostruct(sr, sc, er, ec, UNDO_DEL, IGNORE_DEPS, NULL);
1903 #endif
1904 for (r = sr; r <= er; r++) {
1905 for (c = sc; c <= ec; c++) {
1906 p = *ATBL(tbl, r, c);
1907 if (p && p->flags & is_locked) {
1908 sc_error(" Cell %s%d is locked", coltoa(c), r);
1909 continue;
1910 }
1911 if (p && p->expr) {
1912 efree(p->expr);
1913 p->expr = (struct enode *)0;
1914 p->flags &= ~is_strexpr;
1915
1916 // TODO move this to depgraph ?
1917 vertexT * v_cur = getVertex(graph, p, 0);
1918 if (v_cur != NULL) { // just in case
1919
1920 // for each edge in edges, we look for the reference to the vertex we are deleting, and we erase it!
1921 edgeT * e = v_cur->edges;
1922 while (e != NULL) { // && v_cur->back_edges == NULL) {
1923 delete_reference(v_cur, e->connectsTo, 1);
1924
1925 // delete vertex only if it end up having no edges, no expression, no value, no label....
1926 if (e->connectsTo->edges == NULL && e->connectsTo->back_edges == NULL && !e->connectsTo->ent->expr && !(e->connectsTo->ent->flags & is_valid) && ! e->connectsTo->ent->label)
1927 destroy_vertex(e->connectsTo->ent);
1928 // WARNING: an orphan vertex now represents an ent that has an enode thats
1929 // need to be evaluated, but do not depend in another cell.
1930 e = e->next;
1931 }
1932
1933 destroy_list_edges(v_cur->edges);
1934 v_cur->edges = NULL;
1935
1936 /* delete vertex in graph
1937 only if this vertex is not referenced by other */
1938 if (v_cur->back_edges == NULL ) destroy_vertex(p);
1939 }
1940 }
1941 }
1942 }
1943 #ifdef UNDO
1944 copy_to_undostruct(sr, sc, er, ec, UNDO_ADD, IGNORE_DEPS, NULL);
1945 end_undo_action();
1946 #endif
1947 sc_info("Removed formulas from range");
1948 return;
1949 }
1950
1951
1952 /**
1953 * \brief TODO Document select_inner_range()
1954 * \param[in] vir_tlrow
1955 * \param[in] vir_tlcol
1956 * \param[in] vir_brrow
1957 * \param[in] vir_brcol
1958 * \return none
1959 */
select_inner_range(int * vir_tlrow,int * vir_tlcol,int * vir_brrow,int * vir_brcol)1960 void select_inner_range(int * vir_tlrow, int * vir_tlcol, int * vir_brrow, int * vir_brcol) {
1961 struct ent * p;
1962 int rr, cc, r, c, mf = 1;
1963
1964 while (mf != 0) {
1965 mf = 0;
1966 for (rr = *vir_tlrow; rr <= *vir_brrow; rr++) {
1967 for (cc = *vir_tlcol; cc <= *vir_brcol; cc++)
1968 for (r=-1; r<=1; r++)
1969 for (c=-1; c<=1; c++) {
1970 if (r == 0 && c == 0) continue;
1971 else if (rr + r < 0 || cc + c < 0 || rr + r > maxrow || cc + c > maxcol) continue;
1972 p = *ATBL(tbl, rr + r, cc + c);
1973 if ( p != NULL && (p->label || p->flags & is_valid) ) {
1974 if (*vir_brcol < cc + c) {
1975 *vir_brcol = cc + c;
1976 mf=1;
1977 }
1978 if (*vir_brrow < rr + r) {
1979 *vir_brrow = rr + r;
1980 mf=1;
1981 }
1982 if (*vir_tlcol > cc + c) {
1983 *vir_tlcol = cc + c;
1984 mf=1;
1985 }
1986 if (*vir_tlrow > rr + r) {
1987 *vir_tlrow = rr + r;
1988 mf = 1;
1989 }
1990 }
1991 }
1992 if (mf) break;
1993 } // rr
1994 }
1995 return;
1996 }
1997
1998
1999 /**
2000 * \brief Check if cell is locked
2001 *
2002 * \return 1 if cell if locked; 0 otherwise
2003 */
locked_cell(int r,int c)2004 int locked_cell(int r, int c) {
2005 struct ent *p = *ATBL(tbl, r, c);
2006 if (p && (p->flags & is_locked)) {
2007 sc_error("Cell %s%d is locked", coltoa(c), r) ;
2008 return 1;
2009 }
2010 return 0;
2011 }
2012
2013
2014 /**
2015 * \brief Check if area contains locked cells
2016 *
2017 * \param[in] r1
2018 * \param[in] c1
2019 * \param[in] r2
2020 * \param[in] c2
2021 *
2022 * \return 1 if area contains a locked cell; 0 otherwise
2023 */
any_locked_cells(int r1,int c1,int r2,int c2)2024 int any_locked_cells(int r1, int c1, int r2, int c2) {
2025 int r, c;
2026 struct ent * p ;
2027
2028 for (r = r1; r <= r2; r++)
2029 for (c = c1; c <= c2; c++) {
2030 p = *ATBL(tbl, r, c);
2031 if (p && (p->flags & is_locked))
2032 return 1;
2033 }
2034 return 0;
2035 }
2036
2037
2038 /**
2039 * \brief sum special command
2040 * \return none
2041 */
fsum()2042 int fsum() {
2043 int r = currow, c = curcol;
2044 struct ent * p;
2045
2046 if (r > 0 && (*ATBL(tbl, r-1, c) != NULL) && (*ATBL(tbl, r-1, c))->flags & is_valid) {
2047 for (r = currow-1; r >= 0; r--) {
2048 p = *ATBL(tbl, r, c);
2049 if (p == NULL) break;
2050 if (! (p->flags & is_valid)) break;
2051 }
2052 if (currow != r) {
2053 swprintf(interp_line, BUFFERSIZE, L"let %s%d = @SUM(", coltoa(curcol), currow);
2054 swprintf(interp_line + wcslen(interp_line), BUFFERSIZE, L"%s%d:", coltoa(curcol), r+1);
2055 swprintf(interp_line + wcslen(interp_line), BUFFERSIZE, L"%s%d)", coltoa(curcol), currow-1);
2056 }
2057 } else if (c > 0 && (*ATBL(tbl, r, c-1) != NULL) && (*ATBL(tbl, r, c-1))->flags & is_valid) {
2058 for (c = curcol-1; c >= 0; c--) {
2059 p = *ATBL(tbl, r, c);
2060 if (p == NULL) break;
2061 if (! (p->flags & is_valid)) break;
2062 }
2063 if (curcol != c) {
2064 swprintf(interp_line, BUFFERSIZE, L"let %s%d = @SUM(", coltoa(curcol), currow);
2065 swprintf(interp_line + wcslen(interp_line), BUFFERSIZE, L"%s%d:", coltoa(c+1), r);
2066 swprintf(interp_line + wcslen(interp_line), BUFFERSIZE, L"%s%d)", coltoa(curcol-1), r);
2067 }
2068 }
2069
2070 if ((currow != r || curcol != c) && wcslen(interp_line))
2071 send_to_interp(interp_line);
2072 EvalRange(currow, curcol, currow, curcol);
2073 return 0;
2074 }
2075
2076
2077 /**
2078 * \brief fcopy special command
2079 * \param[in] action
2080 * \return -1 on error; 0 otherwise
2081 */
fcopy(char * action)2082 int fcopy(char * action) {
2083 int r, ri, rf, c, ci, cf;
2084 struct ent * pdest;
2085 struct ent * pact;
2086 int p = is_range_selected();
2087 struct srange * sr = NULL;
2088 if (p != -1) sr = get_range_by_pos(p);
2089
2090 if (p == -1) { // no range selected
2091 // fail if the cursor is on the first column
2092 if (curcol == 0) {
2093 sc_error("Can't fcopy with no arguments while on column 'A'");
2094 return -1;
2095 }
2096
2097 ri = currow;
2098 ci = curcol;
2099 cf = curcol;
2100 for (r=ri+1; r<maxrow && (*ATBL(tbl, r, cf-1)) != NULL && (*ATBL(tbl, r, cf-1))->flags & is_valid ; r++) {}
2101 rf = --r;
2102 } else { // range is selected
2103 ri = sr->tlrow;
2104 ci = sr->tlcol;
2105 cf = sr->brcol;
2106 rf = sr->brrow;
2107 }
2108
2109 // check if all cells that will be copied to somewhere have a formula in them
2110 if (! strcmp(action, "") || ! strcmp(action, "cells")) {
2111 if (! *ATBL(tbl, ri, ci) ) goto formula_not_found;
2112 if (! (*ATBL(tbl, ri, ci))->expr) goto formula_not_found;
2113 } else if (! strcmp(action, "c") || ! strcmp(action, "columns")) {
2114 for (c=ci; c<=cf; c++) {
2115 if (! *ATBL(tbl, ri, c) ) goto formula_not_found;
2116 if (! (*ATBL(tbl, ri, c))->expr) goto formula_not_found;
2117 }
2118 } else if (! strcmp(action, "r") || ! strcmp(action, "rows")) {
2119 for (r=ri; r<=rf; r++) {
2120 if (! *ATBL(tbl, r, ci) ) goto formula_not_found;
2121 if (! (*ATBL(tbl, r, ci))->expr) goto formula_not_found;
2122 }
2123 } else {
2124 sc_error("Invalid parameter");
2125 }
2126
2127 goto all_formulas_found;
2128
2129 formula_not_found:
2130 sc_error("At least 1 formula not found. Nothing changed");
2131 return -1;
2132
2133 all_formulas_found:
2134
2135 if (any_locked_cells(ri, ci, rf, cf)) {
2136 swprintf(interp_line, BUFFERSIZE, L"");
2137 sc_error("Locked cells encountered. Nothing changed");
2138 return -1;
2139 }
2140 #ifdef UNDO
2141 create_undo_action();
2142 copy_to_undostruct(ri, ci, rf, cf, UNDO_DEL, IGNORE_DEPS, NULL);
2143 #endif
2144
2145 if (! strcmp(action, "")) {
2146 // copy first column down (old behavior), for backwards compatibility
2147 pact = *ATBL(tbl, ri, ci);
2148 for (r=ri+1; r<=rf; r++) {
2149 pdest = lookat(r, ci);
2150 copyent(pdest, pact, r - ri, 0, 0, 0, maxrows, maxcols, 'c');
2151 }
2152 } else if (! strcmp(action, "c") || ! strcmp(action, "columns")) {
2153 // copy all selected columns down
2154 for (c=ci; c<=cf; c++) {
2155 pact = *ATBL(tbl, ri, c);
2156 for (r=ri+1; r<=rf; r++) {
2157 pdest = lookat(r, c);
2158 copyent(pdest, pact, r - ri, 0, 0, 0, maxrows, maxcols, 'c');
2159 }
2160 }
2161 } else if (! strcmp(action, "r") || ! strcmp(action, "rows")) {
2162 // copy all selected rows right
2163 for (r=ri; r<=rf; r++) {
2164 pact = *ATBL(tbl, r, ci);
2165 for (c=ci+1; c<=cf; c++) {
2166 pdest = lookat(r, c);
2167 copyent(pdest, pact, 0, c - ci, 0, 0, maxrows, maxcols, 'c');
2168 }
2169 }
2170 } else if (! strcmp(action, "cells")) {
2171 // copy selected cell down and right
2172 pact = *ATBL(tbl, ri, ci);
2173 for (r=ri; r<=rf; r++) {
2174 for(c=(r==ri?ci+1:ci); c<=cf; c++) {
2175 pdest = lookat(r, c);
2176 copyent(pdest, pact, r - ri, c - ci, 0, 0, maxrows, maxcols, 'c');
2177 }
2178 }
2179 }
2180 EvalRange(ri, ci, rf, cf);
2181
2182 #ifdef UNDO
2183 copy_to_undostruct(ri, ci, rf, cf, UNDO_ADD, IGNORE_DEPS, NULL);
2184 end_undo_action();
2185 #endif
2186
2187 return 0;
2188 }
2189
2190
2191 /**
2192 * \brief Add padding to cells
2193 *
2194 * \details Add padding to cells. This set padding of a range.
2195 *
2196 * \return -1 if locked cell is encountered; 1 if padding exceeded
2197 * column width; 0 otherwise
2198 */
pad(int n,int r1,int c1,int r2,int c2)2199 int pad(int n, int r1, int c1, int r2, int c2) {
2200 int r, c;
2201 struct ent * p ;
2202 int pad_exceed_width = 0;
2203
2204 if (any_locked_cells(r1, c1, r2, c2)) {
2205 sc_info("Locked cells encountered. Nothing changed");
2206 return -1;
2207 }
2208
2209 #ifdef UNDO
2210 create_undo_action();
2211 copy_to_undostruct(r1, c1, r2, c2, UNDO_DEL, IGNORE_DEPS, NULL);
2212 #endif
2213
2214 for (r = r1; r <= r2; r++) {
2215 for (c = c1; c <= c2; c++) {
2216 if (n > fwidth[c]) {
2217 pad_exceed_width = 1;
2218 continue;
2219 }
2220 if ((p = *ATBL(tbl, r, c)) != NULL) p->pad = n;
2221 modflg++;
2222 }
2223 }
2224
2225 #ifdef UNDO
2226 copy_to_undostruct(r1, c1, r2, c2, UNDO_ADD, IGNORE_DEPS, NULL);
2227 end_undo_action();
2228 #endif
2229
2230 if (pad_exceed_width) {
2231 sc_error(" Could not add padding in some cells. Padding exceeded column width");
2232 return 1;
2233 }
2234 return 0;
2235 }
2236
2237
2238 /**
2239 * \brief fix_row_hidden
2240 * \details fix hidden rows after undoing ir dr etc..
2241 * \return none
2242 */
fix_row_hidden(int deltar,int ri,int rf)2243 void fix_row_hidden(int deltar, int ri, int rf) {
2244 int r;
2245 int d = deltar;
2246
2247 // decrease / for dr
2248 if (deltar > 0)
2249 while (d-- > 0)
2250 for (r = ri; r < rf; r++)
2251 row_hidden[r] = row_hidden[r+1];
2252
2253 // increase / for ir
2254 if (deltar < 0)
2255 while (d++ < 0)
2256 for (r = rf; r > ri; r--)
2257 row_hidden[r] = row_hidden[r-1];
2258 return;
2259 }
2260
2261
2262 /**
2263 * \brief fix_col_hidden
2264 * \details fix hidden cols after undoing ic dc etc..
2265 * \return none
2266 */
fix_col_hidden(int deltac,int ci,int cf)2267 void fix_col_hidden(int deltac, int ci, int cf) {
2268 int c;
2269 int d = deltac;
2270
2271 // decrease / for dc
2272 if (deltac > 0)
2273 while (d-- > 0)
2274 for (c = ci; c < cf; c++)
2275 col_hidden[c] = col_hidden[c+1];
2276
2277 // increase / for ic
2278 if (deltac < 0)
2279 while (d++ < 0)
2280 for (c = cf; c > ci; c--)
2281 col_hidden[c] = col_hidden[c-1];
2282 return;
2283 }
2284
2285
2286 /**
2287 * \brief fix_row_frozen
2288 * \details fix frozen rows after undoing ir dr etc..
2289 * \return none
2290 */
fix_row_frozen(int deltar,int ri,int rf)2291 void fix_row_frozen(int deltar, int ri, int rf) {
2292 int r;
2293 int d = deltar;
2294
2295 // decrease / for dr
2296 if (deltar > 0)
2297 while (d-- > 0)
2298 for (r = ri; r < rf; r++)
2299 row_frozen[r] = row_frozen[r+1];
2300
2301 // increase / for ir
2302 if (deltar < 0)
2303 while (d++ < 0)
2304 for (r = rf; r > ri; r--)
2305 row_frozen[r] = row_frozen[r-1];
2306 return;
2307 }
2308
2309
2310 /**
2311 * \brief fix_col_frozen
2312 * \details fix frozen cols after undoing ic dc etc..
2313 * \return none
2314 */
fix_col_frozen(int deltac,int ci,int cf)2315 void fix_col_frozen(int deltac, int ci, int cf) {
2316 int c;
2317 int d = deltac;
2318
2319 // decrease / for dc
2320 if (deltac > 0)
2321 while (d-- > 0)
2322 for (c = ci; c < cf; c++)
2323 col_frozen[c] = col_frozen[c+1];
2324
2325 // increase / for ic
2326 if (deltac < 0)
2327 while (d++ < 0)
2328 for (c = cf; c > ci; c--)
2329 col_frozen[c] = col_frozen[c-1];
2330 return;
2331 }
2332
2333
2334 /**
2335 * \brief Compute number of mobile (unfrozen) rows to fit on screen
2336 *
2337 * \details This function finds the number of mobile rows that can fit on
2338 * the screen. This number excludes frozen rows as they never leave the
2339 * screen hence their total height is subtracted from the available screen
2340 * area. This number also excludes hidden rows. Displayed mobile rows are
2341 * considered starting from offscr_sc_rows, however currow must be within
2342 * the displayed rows. If currow is found to be outside the displayed set
2343 * of rows then offscr_sc_rows is adjusted accordingly.
2344 *
2345 * \param[in] last_p If not NULL then the last mobile row to fit is stored there
2346 *
2347 * \return Number of mobile rows displayable on the screen
2348 */
calc_mobile_rows(int * last_p)2349 int calc_mobile_rows(int *last_p) {
2350 int i, row_space, mobile_rows, last;
2351
2352 /*
2353 * Compute the number of frozen rows and the space they need.
2354 * Eventually this should be added/subtracted when individual rows
2355 * are frozen/unfrozen/enlarged/reduced/deleted and not recomputed
2356 * every time here... or at least have a global flag indicating that
2357 * nothing has changed and that this loop can be skipped.
2358 */
2359 nb_frozen_rows = 0;
2360 nb_frozen_screenrows = 0;
2361 for (i = 0; i < maxrows; i++) {
2362 if (row_hidden[i])
2363 continue;
2364 if (row_frozen[i]) {
2365 nb_frozen_rows++;
2366 nb_frozen_screenrows += row_format[i];
2367 }
2368 }
2369
2370 /* Adjust display start if currow is above it */
2371 if (currow < offscr_sc_rows)
2372 offscr_sc_rows = currow;
2373
2374 /* Determine the space available for mobile rows. */
2375 row_space = LINES - RESROW - RESCOLHEADER - nb_frozen_screenrows;
2376
2377 /*
2378 * Find how many visible mobile rows can fit in there
2379 * and remember which one is the last to fit.
2380 */
2381 mobile_rows = 0;
2382 last = offscr_sc_rows;
2383 for (i = offscr_sc_rows; i < maxrows; i++) {
2384 if (row_hidden[i])
2385 continue;
2386 if (row_frozen[i])
2387 continue;
2388 if (row_format[i] > row_space)
2389 break;
2390 row_space -= row_format[i];
2391 mobile_rows++;
2392 last = i;
2393 }
2394
2395 /*
2396 * If currow is beyond the last row here then we must start over,
2397 * moving backward this time, to properly position start of display.
2398 */
2399 if (last < currow) {
2400 row_space = LINES - RESROW - RESCOLHEADER - nb_frozen_screenrows;
2401 mobile_rows = 0;
2402 last = currow;
2403 for (i = currow; i >= 0; i--) {
2404 if (row_hidden[i])
2405 continue;
2406 if (row_frozen[i])
2407 continue;
2408 if (row_format[i] > row_space)
2409 break;
2410 row_space -= row_format[i];
2411 mobile_rows++;
2412 last = i;
2413 }
2414 offscr_sc_rows = last;
2415 }
2416
2417 if (last_p)
2418 *last_p = last;
2419 return mobile_rows;
2420 }
2421
2422 /**
2423 * \brief Compute number of mobile (unfrozen) columns to fit on screen
2424 *
2425 * \details This function finds the number of mobile columns that can fit on
2426 * the screen. This number excludes frozen columns as they never leave the
2427 * screen, hence their total height is subtracted from the available screen
2428 * area. This number also excludes hidden columns. Displayed mobile columnss
2429 * are considered starting from offscr_sc_cols, however curcol must be within
2430 * the displayed columns. If curcol is found to be outside the displayed set
2431 * of columns then offscr_sc_cols is adjusted accordingly.
2432 *
2433 * \param[in] last_p If not NULL then the last mobile column to fit is stored there
2434 *
2435 * \return Number of mobile columns displayable on the screen
2436 */
calc_mobile_cols(int * last_p)2437 int calc_mobile_cols(int *last_p) {
2438 int i, col_space, mobile_cols, last;
2439
2440 /*
2441 * Compute the number of frozen columns and the space they need.
2442 * Eventually this should be added/subtracted when individual
2443 * columns are frozen/unfrozen/enlarged/reduced/deleted and not
2444 * recomputed every time here... or at least have a flag indicating
2445 * that nothing has changed and that this loop may be skipped.
2446 */
2447 nb_frozen_cols = 0;
2448 nb_frozen_screencols = 0;
2449 for (i = 0; i < maxcols; i++) {
2450 if (row_frozen[i])
2451 continue;
2452 if (col_frozen[i]) {
2453 nb_frozen_cols++;
2454 nb_frozen_screencols += fwidth[i];
2455 }
2456 }
2457
2458 /* Adjust display start if curcol is left of it */
2459 if (curcol < offscr_sc_cols)
2460 offscr_sc_cols = curcol;
2461
2462 /* Determine the space available for mobile columns. */
2463 col_space = COLS - rescol - nb_frozen_screencols;
2464
2465 /*
2466 * Find how many visible mobile columns can fit in there
2467 * and remember which one is the last to fit.
2468 */
2469 mobile_cols = 0;
2470 last = offscr_sc_cols;
2471 for (i = offscr_sc_cols; i < maxcols; i++) {
2472 if (col_hidden[i])
2473 continue;
2474 if (col_frozen[i])
2475 continue;
2476 if (fwidth[i] > col_space)
2477 break;
2478 col_space -= fwidth[i];
2479 mobile_cols++;
2480 last = i;
2481 }
2482
2483 /*
2484 * If curcol is beyond the last column here then we start over,
2485 * moving backward this time, to properly position start of display.
2486 */
2487 if (last < curcol) {
2488 col_space = COLS - rescol - nb_frozen_screencols;
2489 mobile_cols = 0;
2490 last = curcol;
2491 for (i = curcol; i >= 0; i--) {
2492 if (col_hidden[i])
2493 continue;
2494 if (col_frozen[i])
2495 continue;
2496 if (fwidth[i] > col_space)
2497 break;
2498 col_space -= fwidth[i];
2499 mobile_cols++;
2500 last = i;
2501 }
2502 offscr_sc_cols = last;
2503 }
2504
2505 if (last_p)
2506 *last_p = last;
2507 return mobile_cols;
2508 }
2509
2510
2511 /**
2512 * \brief pad_and_align
2513 *
2514 * \details This function aligns text of a cell (align = 0 center,
2515 * align = 1 right, align = -1 left). Also adds padding between cells.
2516 *
2517 * \param[in] srt_value
2518 * \param[in] numeric_value
2519 * \param[in] col_width
2520 * \param[in] align
2521 * \param[in] padding
2522 * \param[in] str_out
2523 * \param[in] rowfmt: rowheight
2524 *
2525 * \return resulting string to be printed to the screen
2526 */
pad_and_align(char * str_value,char * numeric_value,int col_width,int align,int padding,wchar_t * str_out,int rowfmt)2527 void pad_and_align (char * str_value, char * numeric_value, int col_width, int align, int padding, wchar_t * str_out, int rowfmt) {
2528 int str_len = 0;
2529 int num_len = strlen(numeric_value);
2530 str_out[0] = L'\0';
2531
2532 wchar_t wcs_value[BUFFERSIZE] = { L'\0' };
2533 mbstate_t state;
2534 size_t result;
2535 char * str_in;
2536
2537 // Here we handle \" and replace them with "
2538 str_in = str_replace(str_value, "\\\"", "\"");
2539
2540 const char * mbsptr;
2541 mbsptr = str_in;
2542
2543 // create wcs string based on multibyte string..
2544 memset( &state, '\0', sizeof state );
2545 result = mbsrtowcs(wcs_value, &mbsptr, BUFFERSIZE, &state);
2546 if ( result != (size_t)-1 )
2547 str_len = wcswidth(wcs_value, wcslen(wcs_value));
2548
2549 if (str_len == 2 && str_in[0] == '\\') {
2550 wmemset(str_out + wcslen(str_out), str_in[1], col_width);
2551 free(str_in);
2552 return;
2553 } else if (str_len == 3 && str_in[0] == '\\' && str_in[1] == '\\') {
2554 wmemset(str_out + wcslen(str_out), str_in[2], col_width);
2555 free(str_in);
2556 return;
2557 }
2558
2559 // If padding exceedes column width, returns n number of '-' needed to fill column width
2560 if (padding >= col_width ) {
2561 wmemset(str_out + wcslen(str_out), L' ', col_width);
2562 free(str_in);
2563 return;
2564 }
2565
2566 // If content exceedes column width, outputs n number of '*' needed to fill column width
2567 if (str_len + num_len + padding > col_width * rowfmt && ! get_conf_int("truncate") &&
2568 ! get_conf_int("overlap") && ! get_conf_int("autowrap")) {
2569 if (padding) wmemset(str_out + wcslen(str_out), L' ', padding);
2570 wmemset(str_out + wcslen(str_out), L'*', col_width - padding);
2571 free(str_in);
2572 return;
2573 }
2574
2575 // padding
2576 if (padding) swprintf(str_out, BUFFERSIZE, L"%*ls", padding, L"");
2577
2578 // left spaces
2579 int left_spaces = 0;
2580 if (align == 0) { // center align
2581 left_spaces = (col_width - padding - str_len) / 2;
2582 if (num_len > left_spaces) left_spaces = col_width - padding - str_len - num_len;
2583 } else if (align == 1 && str_len && ! num_len) { // right align
2584 left_spaces = col_width - padding - str_len;
2585 }
2586 while (left_spaces-- > 0) add_wchar(str_out, L' ', wcslen(str_out));
2587
2588 // add text
2589 if (align != 1 || ! num_len)
2590 swprintf(str_out + wcslen(str_out), BUFFERSIZE, L"%s", str_in);
2591
2592 // spaces after string value
2593 int spaces = col_width - padding - str_len - num_len;
2594 if (align == 1){
2595 if(num_len) spaces += str_len;
2596 else spaces = 0;
2597 }
2598 if (align == 0) spaces -= (col_width - padding - str_len) / 2;
2599 while (spaces-- > 0) add_wchar(str_out, L' ', wcslen(str_out));
2600
2601 // add number
2602 int fill_with_number = col_width - str_len - padding;
2603 if (num_len && num_len >= fill_with_number) {
2604 swprintf(str_out + wcslen(str_out), BUFFERSIZE, L"%.*s", fill_with_number, & numeric_value[num_len - fill_with_number]);
2605 } else if (num_len) {
2606 swprintf(str_out + wcslen(str_out), BUFFERSIZE, L"%s", numeric_value);
2607 }
2608
2609 // Similar condition to max width '*' condition above, but just trims instead
2610 if (str_len + num_len + padding > col_width * rowfmt && get_conf_int("truncate")) {
2611 str_out[col_width] = '\0';
2612 }
2613
2614 // on each \n chars, replace with n number of L' ' to complete column width
2615 int posnl, leftnl = 0;
2616 while ((posnl = wstr_in_wstr(str_out, L"\\n")) != -1) {
2617 del_range_wchars(str_out, posnl, posnl+1);
2618 if (posnl <= col_width) leftnl = col_width - posnl;
2619 else leftnl = col_width - posnl % col_width;
2620 while (leftnl-- > 0) add_wchar(str_out, L' ', posnl);
2621 }
2622
2623 free(str_in);
2624 return;
2625 }
2626
2627
2628 /**
2629 * \brief Check if the buffer content is a valid command
2630 *
2631 * \details Check if the buffer content is a valid command
2632 * result = 0 or NO_CMD : buf has no command
2633 * result = 1 or EDITION_CMD : buf has a command
2634 * result = 2 or MOVEMENT_CMD : buf has a movement command or a command that do not
2635 * change cell content, and should not be considered by the '.' command
2636 *
2637 * \return result
2638 */
is_single_command(struct block * buf,long timeout)2639 int is_single_command (struct block * buf, long timeout) {
2640 if (buf->value == L'\0') return NO_CMD;
2641 int result = NO_CMD;
2642 int bs = get_bufsize(buf);
2643
2644 if (curmode == COMMAND_MODE && bs == 1 && ( buf->value != ctl('r') ||
2645 buf->value == ctl('v')) ) {
2646 result = MOVEMENT_CMD;
2647
2648 } else if ( curmode == COMMAND_MODE && bs == 2 && buf->value == ctl('r') &&
2649 (buf->pnext->value - (L'a' - 1) < 1 || buf->pnext->value > 26)) {
2650 result = MOVEMENT_CMD;
2651
2652 } else if (curmode == INSERT_MODE && bs == 1 && ( buf->value != ctl('r') ||
2653 buf->value == ctl('v')) ) {
2654 result = MOVEMENT_CMD;
2655
2656 } else if (curmode == INSERT_MODE && bs == 2 && buf->value == ctl('r') &&
2657 (buf->pnext->value - (L'a' - 1) < 1 || buf->pnext->value > 26)) {
2658 result = MOVEMENT_CMD;
2659
2660 } else if (curmode == EDIT_MODE && bs == 1) {
2661 result = MOVEMENT_CMD;
2662
2663 } else if (curmode == NORMAL_MODE && bs == 1) {
2664 // commands for changing mode
2665 if (buf->value == L':') result = MOVEMENT_CMD;
2666 else if (buf->value == L'\\') result = MOVEMENT_CMD;
2667 else if (buf->value == L'<') result = MOVEMENT_CMD;
2668 else if (buf->value == L'>') result = MOVEMENT_CMD;
2669 else if (buf->value == L'=') result = MOVEMENT_CMD;
2670 else if (buf->value == L'e') result = MOVEMENT_CMD;
2671 else if (buf->value == L'E') result = MOVEMENT_CMD;
2672 else if (buf->value == L'v') result = MOVEMENT_CMD;
2673
2674 else if (buf->value == L'Q') result = MOVEMENT_CMD; /* FOR TEST PURPOSES */
2675 else if (buf->value == L'A') result = MOVEMENT_CMD; /* FOR TEST PURPOSES */
2676 else if (buf->value == L'W') result = MOVEMENT_CMD; /* FOR TEST PURPOSES */
2677
2678 // movement commands
2679 else if (buf->value == L'j') result = MOVEMENT_CMD;
2680 else if (buf->value == L'k') result = MOVEMENT_CMD;
2681 else if (buf->value == L'h') result = MOVEMENT_CMD;
2682 else if (buf->value == L'l') result = MOVEMENT_CMD;
2683 else if (buf->value == L'0') result = MOVEMENT_CMD;
2684 else if (buf->value == L'$') result = MOVEMENT_CMD;
2685 else if (buf->value == OKEY_HOME) result = MOVEMENT_CMD;
2686 else if (buf->value == OKEY_END) result = MOVEMENT_CMD;
2687 else if (buf->value == L'#') result = MOVEMENT_CMD;
2688 else if (buf->value == L'^') result = MOVEMENT_CMD;
2689 else if (buf->value == OKEY_LEFT) result = MOVEMENT_CMD;
2690 else if (buf->value == OKEY_RIGHT) result = MOVEMENT_CMD;
2691 else if (buf->value == OKEY_DOWN) result = MOVEMENT_CMD;
2692 else if (buf->value == OKEY_UP) result = MOVEMENT_CMD;
2693 else if (buf->value == OKEY_PGUP) result = MOVEMENT_CMD;
2694 else if (buf->value == OKEY_PGDOWN) result = MOVEMENT_CMD;
2695 else if (buf->value == ctl('f')) result = MOVEMENT_CMD;
2696 else if (buf->value == ctl('j')) result = EDITION_CMD;
2697 else if (buf->value == ctl('d')) result = EDITION_CMD;
2698 else if (buf->value == ctl('b')) result = MOVEMENT_CMD;
2699 else if (buf->value == ctl('a')) result = MOVEMENT_CMD;
2700 else if (buf->value == L'G') result = MOVEMENT_CMD;
2701 else if (buf->value == L'H') result = MOVEMENT_CMD;
2702 else if (buf->value == L'M') result = MOVEMENT_CMD;
2703 else if (buf->value == L'L') result = MOVEMENT_CMD;
2704 else if (buf->value == ctl('y')) result = MOVEMENT_CMD;
2705 else if (buf->value == ctl('e')) result = MOVEMENT_CMD;
2706 else if (buf->value == ctl('l')) result = MOVEMENT_CMD;
2707 else if (buf->value == L'w') result = MOVEMENT_CMD;
2708 else if (buf->value == L'b') result = MOVEMENT_CMD;
2709 else if (buf->value == L'/') result = MOVEMENT_CMD; // search
2710 else if (buf->value == L'?') result = MOVEMENT_CMD; // search backwards
2711 else if (buf->value == L'n') result = MOVEMENT_CMD; // repeat last goto cmd
2712 else if (buf->value == L'N') result = MOVEMENT_CMD; // repeat last goto cmd - backwards
2713
2714 // edition commands
2715 else if (buf->value == L'x') result = EDITION_CMD; // cuts a cell
2716 else if (buf->value == L'u') result = MOVEMENT_CMD; // undo
2717 else if (buf->value == ctl('r')) result = MOVEMENT_CMD; // redo
2718 else if (buf->value == L'@') result = EDITION_CMD; // EvalAll
2719 else if (buf->value == L'{') result = EDITION_CMD;
2720 else if (buf->value == L'}') result = EDITION_CMD;
2721 else if (buf->value == L'|') result = EDITION_CMD;
2722 else if (buf->value == L'p') result = EDITION_CMD; // paste yanked cells below or left
2723 else if (buf->value == L't') result = EDITION_CMD; // paste yanked cells above or right
2724 else if (buf->value == L'-') result = EDITION_CMD;
2725 else if (buf->value == L'+') result = EDITION_CMD;
2726
2727 else if (isdigit(buf->value) && get_conf_int("numeric") )
2728 result = MOVEMENT_CMD; // repeat last command
2729
2730 else if (buf->value == L'.') result = MOVEMENT_CMD; // repeat last command
2731 else if (buf->value == L'y' && is_range_selected() != -1)
2732 result = EDITION_CMD; // yank range
2733
2734 } else if (curmode == NORMAL_MODE) {
2735
2736 if (buf->value == L'g' && bs == 2 && (
2737 buf->pnext->value == L'f' ||
2738 buf->pnext->value == L'M' ||
2739 buf->pnext->value == L'g' ||
2740 buf->pnext->value == L'G' ||
2741 buf->pnext->value == L'0' ||
2742 buf->pnext->value == L'l' ||
2743 buf->pnext->value == L'$'))
2744 result = MOVEMENT_CMD;
2745
2746 else if (buf->value == L'g' && bs > 3 && buf->pnext->value == L't' && timeout >= COMPLETECMDTIMEOUT)
2747 result = MOVEMENT_CMD; // goto cell
2748 // TODO add validation: buf->pnext->pnext->value must be a letter
2749
2750 else if (buf->value == L'P' && bs == 2 && (
2751 buf->pnext->value == L'v' ||
2752 buf->pnext->value == L'f' ||
2753 buf->pnext->value == L'c' ) ) result = EDITION_CMD; // paste yanked cells below or left
2754
2755 else if (buf->value == L'T' && bs == 2 && (
2756 buf->pnext->value == L'v' ||
2757 buf->pnext->value == L'f' ||
2758 buf->pnext->value == L'c' ) ) result = EDITION_CMD; // paste yanked cells above or right
2759
2760 else if (buf->value == L'a' && bs == 2 && // autojus
2761 buf->pnext->value == L'a') result = EDITION_CMD;
2762
2763 else if (buf->value == L'Z' && bs == 2 && timeout >= COMPLETECMDTIMEOUT && // Zap (or hide) col or row
2764 ( buf->pnext->value == L'c' ||
2765 buf->pnext->value == L'r')) result = EDITION_CMD;
2766
2767 else if (buf->value == L'S' && bs == 2 && timeout >= COMPLETECMDTIMEOUT && // Zap (or hide) col or row
2768 ( buf->pnext->value == L'c' ||
2769 buf->pnext->value == L'r')) result = EDITION_CMD;
2770
2771 else if (buf->value == L'y' && bs == 2 && // yank cell
2772 ( buf->pnext->value == L'y' ||
2773 buf->pnext->value == L'r' ||
2774 buf->pnext->value == L'c') ) result = EDITION_CMD;
2775
2776 else if (buf->value == L'm' && bs == 2 && // mark
2777 ((buf->pnext->value - (L'a' - 1)) < 1 ||
2778 buf->pnext->value > 26)) result = MOVEMENT_CMD;
2779
2780 else if (buf->value == L'c' && bs == 2 && // mark
2781 ((buf->pnext->value - (L'a' - 1)) < 1 || buf->pnext->value > 26)) result = EDITION_CMD;
2782
2783 else if (buf->value == L'z' && bs == 2 && // scrolling
2784 ( buf->pnext->value == L'h' ||
2785 buf->pnext->value == L'l' ||
2786 buf->pnext->value == L'z' ||
2787 buf->pnext->value == L'm' ||
2788 buf->pnext->value == L'.' ||
2789 buf->pnext->value == L't' ||
2790 buf->pnext->value == L'b' ||
2791 buf->pnext->value == L'H' ||
2792 buf->pnext->value == L'L')
2793 ) result = MOVEMENT_CMD;
2794
2795 else if (buf->value == L'V' && bs == 3 && // Vir
2796 buf->pnext->value == L'i' &&
2797 buf->pnext->pnext->value == L'r')
2798 result = MOVEMENT_CMD;
2799
2800 else if (buf->value == L'd' && bs == 2 && // cuts a cell
2801 buf->pnext->value == L'd') result = EDITION_CMD;
2802
2803 else if (buf->value == L'\'' && bs == 2 && // tick
2804 ((buf->pnext->value - (L'a' - 1)) < 1 ||
2805 buf->pnext->value > 26)) result = MOVEMENT_CMD;
2806
2807 else if (buf->value == L's' && bs == 2 && // shift cell down or up
2808 ( buf->pnext->value == L'j' ||
2809 buf->pnext->value == L'k' ||
2810 buf->pnext->value == L'h' ||
2811 buf->pnext->value == L'l')) result = EDITION_CMD;
2812
2813 else if (buf->value == L'i' && bs == 2 && // Insert row or column
2814 ( buf->pnext->value == L'r' ||
2815 buf->pnext->value == L'c' )) result = EDITION_CMD;
2816
2817 else if (buf->value == L'o' && bs == 2 && // Open row or column
2818 ( buf->pnext->value == L'r' ||
2819 buf->pnext->value == L'c' )) result = EDITION_CMD;
2820
2821 else if (buf->value == L'd' && bs == 2 && // Delete row or column
2822 ( buf->pnext->value == L'r' ||
2823 buf->pnext->value == L'c' )) result = EDITION_CMD;
2824
2825 else if (buf->value == L'r' && bs == 2 && // range lock / unlock / valueize
2826 ( buf->pnext->value == L'l' ||
2827 buf->pnext->value == L'u' ||
2828 buf->pnext->value == L'v' )) result = EDITION_CMD;
2829
2830 else if (buf->value == L'R' && bs == 3 && // Create range with two marks
2831 // FIXME add better validation
2832 ((buf->pnext->value - (L'a' - 1)) < 1 ||
2833 buf->pnext->value > 26) &&
2834 ((buf->pnext->pnext->value - (L'a' - 1)) < 1 ||
2835 buf->pnext->pnext->value > 26)) result = EDITION_CMD;
2836
2837 else if (buf->value == L'f' && bs == 2 && // Format col
2838 ( buf->pnext->value == L'>' ||
2839 buf->pnext->value == L'<' ||
2840 buf->pnext->value == L'h' ||
2841 buf->pnext->value == OKEY_LEFT ||
2842 buf->pnext->value == L'l' ||
2843 buf->pnext->value == OKEY_RIGHT ||
2844 buf->pnext->value == L'j' ||
2845 buf->pnext->value == OKEY_DOWN ||
2846 buf->pnext->value == L'k' ||
2847 buf->pnext->value == OKEY_UP ||
2848 buf->pnext->value == L'-' ||
2849 buf->pnext->value == L'+' ||
2850 buf->pnext->value == L'r' || // Freeze row / col / area
2851 buf->pnext->value == L'c' ||
2852 buf->pnext->value == L'a'
2853 )
2854 ) result = EDITION_CMD;
2855
2856 } else if (curmode == VISUAL_MODE && bs == 1) {
2857 if (buf->value == L'j' ||
2858 buf->value == OKEY_DOWN ||
2859 buf->value == L'k' ||
2860 buf->value == OKEY_UP ||
2861 buf->value == L'h' ||
2862 buf->value == OKEY_LEFT ||
2863 buf->value == L'l' ||
2864 buf->value == OKEY_RIGHT ||
2865 buf->value == L'$' ||
2866 buf->value == L'0' ||
2867 buf->value == L'#' ||
2868 buf->value == L'^' ||
2869 buf->value == L'y' ||
2870 buf->value == L'p' ||
2871 buf->value == L'P' ||
2872 buf->value == L'x' ||
2873 buf->value == L'w' ||
2874 buf->value == L'b' ||
2875 buf->value == L'H' ||
2876 buf->value == L'M' ||
2877 buf->value == L'L' ||
2878 buf->value == L'G' ||
2879 buf->value == ctl('f') ||
2880 buf->value == ctl('j') ||
2881 buf->value == ctl('d') ||
2882 buf->value == ctl('b') ||
2883 buf->value == ctl('a') ||
2884 buf->value == ctl('o') ||
2885 buf->value == ctl('k') ||
2886 buf->value == L':'
2887 )
2888 result = MOVEMENT_CMD;
2889 else if (buf->value == L'{' ||
2890 buf->value == L'}' ||
2891 buf->value == L'f' ||
2892 buf->value == L'|')
2893 result = EDITION_CMD;
2894
2895 } else if (curmode == VISUAL_MODE && bs == 2) {
2896 if ((buf->value == L'\'') ||
2897 (buf->value == L'd' &&
2898 buf->pnext->value == L'd') ||
2899 (buf->value == L's' && (
2900 buf->pnext->value == L'h' ||
2901 buf->pnext->value == L'j' ||
2902 buf->pnext->value == L'k' ||
2903 buf->pnext->value == L'l' ))
2904 ) {
2905 result = MOVEMENT_CMD;
2906 } else if ((buf->value == L'Z' && (
2907 buf->pnext->value == L'r' ||
2908 buf->pnext->value == L'c' )) ||
2909 (buf->value == L'S' && (
2910 buf->pnext->value == L'r' ||
2911 buf->pnext->value == L'c' )) ) {
2912 result = EDITION_CMD;
2913 } else if (buf->value == L'r' && (
2914 buf->pnext->value == L'l' ||
2915 buf->pnext->value == L'u' ||
2916 buf->pnext->value == L'v' )) {
2917 result = EDITION_CMD;
2918 } else if (buf->value == L'm' && // mark
2919 ((buf->pnext->value - (L'a' - 1)) < 1 ||
2920 buf->pnext->value > 26)) {
2921 result = MOVEMENT_CMD;
2922 }
2923 }
2924 return result;
2925 }
2926