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 undo.c
40 * \author Andrés Martinelli <andmarti@gmail.com>
41 * \date 04/05/2021
42 * \brief This file contains the main functions to support the undo/redo feature.
43 */
44
45 /*
46 * UNDO and REDO feature works with an 'undo' struct list.
47 * Which contains:
48 * p_ant: pointer to 'undo' struct. If NULL, this node is the first change
49 * for the session.
50 *
51 * struct ent * added: 'ent' elements added by the change
52 *
53 * struct ent * removed: 'ent' elements removed by the change
54 *
55 * struct undo_range_shift * range_shift: range shifted by change
56 *
57 * row_hidded: integers list (int *) hidden rows on screen
58 *
59 * row_showed: integers list (int *) visible rows on screen
60 *
61 * row_frozed: integers list (int *) frozen rows on screen
62 *
63 * row_unfrozed: integers list (int *) unfrozen rows on screen
64 *
65 * col_hidded: integers list (int *) hidden columns on screen
66 *
67 * col_showed: integers list (int *) visible columns on screen
68 * NOTE: the first position of the lists contains (number of elements - 1) in the list
69 *
70 * col_frozed: integers list (int *) frozen cols on screen
71 *
72 * col_unfrozed: integers list (int *) unfrozen cols on screen
73 *
74 * struct ent_ptr * allocations: since we alloc over added and removed
75 * list in batches. we need to keep the first position in memory of each calloc.
76 *
77 * int alloc_size: the number of batch allocations.
78 *
79 * struct undo_cols_format * cols_format: list of 'undo_col_info' elements used for
80 * undoing / redoing changes in columns format (fwidth, precision y realfmt)
81 *
82 * struct undo_rows_format * rows_format: list of 'undo_row_info' elements used for
83 * undoing / redoing changes in rows format (height)
84 *
85 * p_sig: pointer to 'undo' struct, If NULL, this node is the last change in
86 * the session.
87 *
88 * Follows one level UNDO/REDO scheme. A change (C1) is made, then an UNDO operation, and
89 * another change (C2). From there later changes are removed.
90 * Scheme:
91 *
92 * + C1 -> + -> UNDO -
93 * ^ \
94 * |_ |
95 * \---------------/
96 * |
97 * |
98 * |
99 * \-> C2 --> + ...
100 *
101 * undo_shift_range struct contains:
102 * int delta_rows: delta rows for the range shift
103 * int delta_cols: delta columns for the range shift
104 * int tlrow: Upper left row defining the range of the shift
105 * (As if a cell range shift is made)
106 * int tlcol: Upper left column defining the range of the shift
107 * (As if a cell range shift is made)
108 * int brrow: Lower right row defining the range of the shift
109 * (As if a cell range shift is made)
110 * int brcol: Lower right column defining the range of the shift
111 * (As if a cell range shift is made)
112 *
113 * Implemented actions for UNDO/REDO:
114 * 1. Remove content from cell or range
115 * 2. Input content into a cell
116 * 3. Edit a cell
117 * 4. Change alginment of range or cell
118 * 5. Paste range or cell
119 * 6. Shift range or cell with sh, sj, sk, sl
120 * 7. Insert row or column
121 * 8. Delete row or column
122 * 9. Paste row or column
123 * 10. Hide/show rows and columns
124 * 11. Sort of a range
125 * 12. Change in the format of a range or cell
126 * 13. '-' and '+' commands in normal mode
127 * 14. Lock and unlock of cells
128 * 15. datefmt command
129 * 16. the cellcolor command
130 * 17. Change in format of a column as a result of the 'f' command
131 * 18. Change in format of a column as a result of auto_jus
132 * 19. Change format of columns as a result of ic dc
133 * 20. fill command
134 * 21. unformat
135 * 22. change in the format of rows
136 * 23. undo of freeze / unfreeze commands
137 *
138 */
139
140 #ifdef UNDO
141
142 #include <stdlib.h>
143 #include "undo.h"
144 #include "macros.h"
145 #include "curses.h"
146 #include "conf.h"
147 #include "sc.h"
148 #include "cmds.h"
149 #include "marks.h"
150 #include "shift.h"
151 #include "dep_graph.h"
152
153 // undolist
154 static struct undo * undo_list = NULL;
155
156 // current position in the list
157 static int undo_list_pos = 0;
158
159 // Number of elements in the list
160 static int undo_list_len = 0;
161
162 // Temporal variable
163 static struct undo undo_item;
164
165 /**
166 * \brief Init 'undo_item'
167 *
168 * \return none
169 */
create_undo_action()170 void create_undo_action() {
171 undo_item.added = NULL;
172 undo_item.removed = NULL;
173 undo_item.allocations = NULL;
174 undo_item.alloc_size = 0;
175 undo_item.p_ant = NULL;
176 undo_item.p_sig = NULL;
177 undo_item.range_shift = NULL;
178 undo_item.cols_format = NULL;
179 undo_item.rows_format = NULL;
180
181 undo_item.row_hidded = NULL;
182 undo_item.row_showed = NULL;
183 undo_item.row_frozed = NULL;
184 undo_item.row_unfrozed = NULL;
185 undo_item.col_hidded = NULL;
186 undo_item.col_showed = NULL;
187 undo_item.col_frozed = NULL;
188 undo_item.col_unfrozed = NULL;
189 return;
190 }
191
192 /**
193 * @brief end_undo_action()
194 *
195 * \details Save undo_item copy with 'ent' elements modified, and the
196 * undo range shift struct into the undolist.
197 *
198 * \return none
199 */
200
end_undo_action()201 void end_undo_action() {
202 add_to_undolist(undo_item);
203
204 // in case we need to dismiss this undo_item!
205 if ((undo_item.added == NULL && undo_item.allocations == NULL &&
206 undo_item.removed == NULL && undo_item.range_shift == NULL &&
207 undo_item.row_hidded == NULL && undo_item.row_showed == NULL &&
208 undo_item.row_frozed == NULL && undo_item.col_frozed == NULL &&
209 undo_item.row_unfrozed == NULL && undo_item.col_unfrozed == NULL &&
210 undo_item.cols_format == NULL && undo_item.rows_format == NULL &&
211 undo_item.col_hidded == NULL && undo_item.col_showed == NULL) || loading) {
212 if (undo_list->p_ant != NULL) undo_list = undo_list->p_ant;
213 undo_list_pos--;
214 clear_from_current_pos();
215 }
216
217 return;
218 }
219
220 /**
221 * \brief Document add_to_undolist()
222 *
223 * \details Add an undo node to the undolist. Allocate memory for
224 * undo struct. Fill variable with undo_item value and append it
225 * to the list.
226 *
227 * \param[in] u
228 *
229 * \return none
230 */
231
add_to_undolist(struct undo u)232 void add_to_undolist(struct undo u) {
233 // If not at the end of the list, remove from the end
234 if (undo_list != NULL && undo_list_pos != len_undo_list()) clear_from_current_pos();
235
236 struct undo * ul = (struct undo *) malloc (sizeof(struct undo)) ;
237 ul->p_sig = NULL;
238
239 // Add 'ent' elements
240 ul->added = u.added;
241 ul->removed = u.removed;
242 ul->allocations = u.allocations;
243 ul->alloc_size = u.alloc_size;
244 ul->range_shift = u.range_shift;
245 ul->cols_format = u.cols_format;
246 ul->rows_format = u.rows_format;
247 ul->row_hidded = u.row_hidded;
248 ul->col_hidded = u.col_hidded;
249 ul->row_showed = u.row_showed;
250 ul->col_showed = u.col_showed;
251 ul->row_frozed = u.row_frozed;
252 ul->col_frozed = u.col_frozed;
253 ul->row_unfrozed = u.row_unfrozed;
254 ul->col_unfrozed = u.col_unfrozed;
255
256 if (undo_list == NULL) {
257 ul->p_ant = NULL;
258 undo_list = ul;
259 } else {
260 ul->p_ant = undo_list;
261
262 // go to end of list
263 // TODO we can improve this by keeping always the last pointer at hand
264 while (undo_list->p_sig != NULL) undo_list = undo_list->p_sig;
265
266 undo_list->p_sig = ul;
267 undo_list = undo_list->p_sig;
268 }
269 undo_list_pos++;
270 undo_list_len++;
271 return;
272 }
273
274 /**
275 * \brief Dismiss current undo_item
276 *
277 * \details This function frees memory of a struct undo item.
278 * It is internally used by free_undo_node(). But as well, this function
279 * shall be called instead of end_undo_action in case we want to cancel
280 * a previous create_undo_action.
281 * If called for this purpose, argument shall be NULL.
282 *
283 * \param[in] ul
284 *
285 * \return none
286 */
287
dismiss_undo_item(struct undo * ul)288 void dismiss_undo_item(struct undo * ul) {
289
290 if (ul == NULL) ul = &undo_item;
291
292 // first free inside each added and removed ents
293 // (their labels, expressions, etc.
294 struct ent * en;
295 struct ent * de;
296 en = ul->added; // free added
297 while (en != NULL) {
298 de = en->next;
299 clearent(en);
300 //free(en); // do not free the struct * ent. thats get freed later in batches
301 en = de;
302 }
303
304 en = ul->removed; // free removed
305 while (en != NULL) {
306 de = en->next;
307 clearent(en);
308 //free(en); // do not free the struct * ent. thats get freed below in batches
309 en = de;
310 }
311
312 // now free added and removed lists
313 // in the way they were alloc'ed (as batches)
314 int i, size = ul->allocations != NULL ? ul->allocations[0].vf : 0;
315 struct ent_ptr * alls = ul->allocations;
316 for (i = 0; i < size; i++) {
317 free(alls->vp);
318 alls->vp = NULL;
319 alls++;
320 }
321 free(ul->allocations);
322 ul->allocations = NULL;
323
324 if (ul->range_shift != NULL) free(ul->range_shift); // Free undo_range_shift memory
325 if (ul->cols_format != NULL) { // Free cols_format memory
326 free(ul->cols_format->cols);
327 free(ul->cols_format);
328 }
329 if (ul->rows_format != NULL) { // Free rows_format memory
330 free(ul->rows_format->rows);
331 free(ul->rows_format);
332 }
333 if (ul->row_hidded != NULL) free(ul->row_hidded); // Free hidden row memory
334 if (ul->col_hidded != NULL) free(ul->col_hidded); // Free hidden col memory
335 if (ul->row_showed != NULL) free(ul->row_showed); // Free showed row memory
336 if (ul->row_frozed != NULL) free(ul->row_frozed); // Free frozed row memory
337 if (ul->col_showed != NULL) free(ul->col_showed); // Free showed col memory
338 if (ul->col_frozed != NULL) free(ul->col_frozed); // Free frozed col memory
339 if (ul->row_unfrozed != NULL) free(ul->row_unfrozed); // Free unfrozed row memory
340 if (ul->col_unfrozed != NULL) free(ul->col_unfrozed); // Free unfrozed col memory
341
342 return;
343 }
344
345 /**
346 * \brief Cascade free UNDO node memory
347 *
348 * \param[in] ul
349 *
350 * \return none
351 */
352
free_undo_node(struct undo * ul)353 void free_undo_node(struct undo * ul) {
354 struct undo * e;
355
356 // Remove from current position
357 while (ul != NULL) {
358 dismiss_undo_item(ul);
359
360 e = ul->p_sig;
361 free(ul);
362 undo_list_len--;
363 ul = e;
364 }
365 return;
366 }
367
368 /**
369 * \brief Remove nodes below the current position from the undolist
370 *
371 * \return none
372 */
373
clear_from_current_pos()374 void clear_from_current_pos() {
375 if (undo_list == NULL) return;
376
377 if (undo_list->p_ant == NULL) {
378 free_undo_node(undo_list);
379 undo_list = NULL;
380 } else {
381 struct undo * ul = undo_list->p_sig; // Previous
382 free_undo_node(ul);
383 undo_list->p_sig = NULL;
384 }
385
386 return;
387 }
388
389 /**
390 * \brief Remove undolist content
391 *
392 * \return none
393 */
394
clear_undo_list()395 void clear_undo_list() {
396 if (undo_list == NULL) return;
397
398 // Go to the beginning of the list
399 while (undo_list->p_ant != NULL ) {
400 undo_list = undo_list->p_ant;
401 }
402
403 struct undo * ul = undo_list;
404
405 free_undo_node(ul);
406
407 undo_list = NULL;
408 undo_list_pos = 0;
409
410 return;
411 }
412
413 /**
414 * \brief Return the length of the undo list
415 *
416 * \return length of undolist
417 */
418
len_undo_list()419 int len_undo_list() {
420 return undo_list_len;
421 }
422
423 /**
424 * \brief copy_to_undostruct()
425 *
426 * \details Take a range of 'ent' elements and create ent copies to keep in undo structs lists
427 * such as the 'added' or 'removed' lists.
428 *
429 * char type: indicates UNDO_ADD ('a') for added list. or UNDO_DEL ('d') for the 'removed' list.
430 *
431 * handle_deps: if set to HANDLE_DEPS it will store the dependencies of the specified range as well.
432 * remember deps is a global variable.
433 *
434 * destination: struct ent * pointer to use in the copy. if none was given, just malloc one.
435 * returns: none
436 */
437
copy_to_undostruct(int ri,int ci,int rf,int cf,char type,short handle_deps,struct ent ** destination)438 void copy_to_undostruct (int ri, int ci, int rf, int cf, char type, short handle_deps, struct ent ** destination) {
439 int i, c, r;
440 struct ent * p;
441 extern struct ent_ptr * deps;
442
443 //int repeated;
444
445 // ask for memory to keep struct ent * for the whole range
446 // and only if no destination pointer was given
447 struct ent * y_cells = destination == NULL ? NULL : *destination;
448 if (y_cells == NULL && handle_deps == HANDLE_DEPS && deps != NULL)
449 y_cells = (struct ent *) calloc((rf-ri+1)*(cf-ci+1)+deps->vf, sizeof(struct ent));
450 else if (y_cells == NULL)
451 y_cells = (struct ent *) calloc((rf-ri+1)*(cf-ci+1), sizeof(struct ent));
452
453 // if no destination pointer was given
454 // we save the pointer for future free
455 if (destination == NULL) save_pointer_after_calloc(y_cells);
456
457 for (r = ri; r <= rf; r++)
458 for (c = ci; c <= cf; c++) {
459 p = *ATBL(tbl, r, c);
460 if (p == NULL) continue;
461
462 // initialize the 'ent'
463 cleanent(y_cells);
464
465 /* here check that ent to add is not already in the list
466 * if so, avoid to add a duplicate ent
467 * commented cause its resource consuming and harmless to duplicate
468 struct ent * lista = type == 'a' ? undo_item.added : undo_item.removed;
469 repeated = 0;
470 while (lista != NULL) {
471 if (lista->row == r && lista->col == c) {
472 repeated = 1;
473 break;
474 }
475 lista = lista->next;
476 }
477 if (repeated) continue;
478 */
479
480 // Copy cell at 'r, c' contents to 'y_cells' ent
481 copyent(y_cells, lookat(r, c), 0, 0, 0, 0, 0, 0, 'u');
482
483 // Append 'ent' element at the beginning
484 if (type == UNDO_ADD) {
485 y_cells->next = undo_item.added;
486 undo_item.added = y_cells;
487 } else {
488 y_cells->next = undo_item.removed;
489 undo_item.removed = y_cells;
490 }
491
492 // increase pointer!
493 if (destination == NULL) y_cells++;
494 else *destination = ++y_cells;
495 }
496
497 // do the same for dependencies
498 if (handle_deps == HANDLE_DEPS)
499 for (i = 0; deps != NULL && i < deps->vf; i++) {
500 p = *ATBL(tbl, deps[i].vp->row, deps[i].vp->col);
501 if (p == NULL) continue;
502
503 // initialize the 'ent'
504 cleanent(y_cells);
505
506 // Copy cell at deps[i].vp->row, deps[i].vp->col contents to 'y_cells' ent
507 copyent(y_cells, lookat(deps[i].vp->row, deps[i].vp->col), 0, 0, 0, 0, 0, 0, 'u');
508
509 // Append 'ent' element at the beginning
510 if (type == UNDO_ADD) {
511 y_cells->next = undo_item.added;
512 undo_item.added = y_cells;
513 } else {
514 y_cells->next = undo_item.removed;
515 undo_item.removed = y_cells;
516 }
517 if (destination == NULL) y_cells++;
518 else *destination = ++y_cells;
519 }
520 return;
521 }
522
523 /**
524 * \brief save_pointer_after_calloc()
525 *
526 * \details This function keeps in a pointer array every pointer that was returned by calloc
527 * so we can free them
528 *
529 * \param[in] struct ent e
530 * \return void
531 */
save_pointer_after_calloc(struct ent * e)532 void save_pointer_after_calloc(struct ent * e) {
533 undo_item.allocations = (struct ent_ptr *) realloc(undo_item.allocations, sizeof(struct ent_ptr) * (++(undo_item.alloc_size)));
534 undo_item.allocations[0].vf = undo_item.alloc_size; // we always keep size of list in the first position !
535 undo_item.allocations[undo_item.alloc_size-1].vp = e; // keep the pointer so later can be freed
536 }
537
538 /**
539 * \brief copy_cell_to_undostruct()
540 *
541 * \details This function adds an struct ent * (new) to undo struct lists.
542 * its contents are based on the struct ent * (ori).
543 * could be added list or deleted list depending on the type.
544 * \param[in] struct ent e
545 * \param[in] struct ent ori
546 * \param[in] char type: indicates UNDO_ADD ('a') for added list. or UNDO_DEL ('d') for the 'removed' list.
547 *
548 * the struct ent pointer is already alloc'ed
549 *
550 * \return void
551 */
copy_cell_to_undostruct(struct ent * e,struct ent * ori,char type)552 void copy_cell_to_undostruct (struct ent * e, struct ent * ori, char type) {
553 struct ent * new = e;
554 // initialize the 'ent'
555 cleanent(new);
556
557 // Copy 'ori' cell contents to 'new' ent
558 copyent(new, ori, 0, 0, 0, 0, 0, 0, 'u');
559
560 // Append 'ent' element at the beginning
561 if (type == UNDO_ADD) {
562 new->next = undo_item.added;
563 undo_item.added = new;
564 } else {
565 new->next = undo_item.removed;
566 undo_item.removed = new;
567 }
568 return;
569 }
570
571 /**
572 * \brief add_undo_col_format()
573 *
574 * \param[in] col
575 * \param[in] type
576 * \param[in] fwidth
577 * \param[in] precision
578 * \param[in] realfmt
579 *
580 * \return none
581 */
add_undo_col_format(int col,int type,int fwidth,int precision,int realfmt)582 void add_undo_col_format(int col, int type, int fwidth, int precision, int realfmt) {
583 if (undo_item.cols_format == NULL) {
584 undo_item.cols_format = (struct undo_cols_format *) malloc( sizeof(struct undo_cols_format));
585 undo_item.cols_format->length = 1;
586 undo_item.cols_format->cols = (struct undo_col_info *) malloc( sizeof(struct undo_col_info));
587 } else {
588 undo_item.cols_format->length++;
589 undo_item.cols_format->cols = (struct undo_col_info *) realloc(undo_item.cols_format->cols, undo_item.cols_format->length * sizeof(struct undo_col_info));
590 }
591 undo_item.cols_format->cols[ undo_item.cols_format->length - 1].type = type;
592 undo_item.cols_format->cols[ undo_item.cols_format->length - 1].col = col;
593 undo_item.cols_format->cols[ undo_item.cols_format->length - 1].fwidth = fwidth;
594 undo_item.cols_format->cols[ undo_item.cols_format->length - 1].precision = precision;
595 undo_item.cols_format->cols[ undo_item.cols_format->length - 1].realfmt = realfmt;
596 return;
597 }
598
599 /**
600 * \brief add_undo_row_format()
601 *
602 * \param[in] row
603 * \param[in] type
604 * \param[in] format
605 *
606 * \return none
607 */
add_undo_row_format(int row,int type,unsigned char format)608 void add_undo_row_format(int row, int type, unsigned char format) {
609 if (undo_item.rows_format == NULL) {
610 undo_item.rows_format = (struct undo_rows_format *) malloc( sizeof(struct undo_rows_format));
611 undo_item.rows_format->length = 1;
612 undo_item.rows_format->rows = (struct undo_row_info *) malloc( sizeof(struct undo_row_info));
613 } else {
614 undo_item.rows_format->length++;
615 undo_item.rows_format->rows = (struct undo_row_info *) realloc(undo_item.rows_format->rows, undo_item.rows_format->length * sizeof(struct undo_row_info));
616 }
617 undo_item.rows_format->rows[ undo_item.rows_format->length - 1].type = type;
618 undo_item.rows_format->rows[ undo_item.rows_format->length - 1].row = row;
619 undo_item.rows_format->rows[ undo_item.rows_format->length - 1].format = format;
620 return;
621 }
622
623 /**
624 * \brief TODO Document save_undo_range_shift()
625 *
626 * \detials Take a range, a rows and columns delta and save them into
627 * they undo struct. Used to shift ranges when UNDO or REDO without
628 * duplicating 'ent'elements.
629 *
630 * \param[in] delta_rows
631 * \param[in] delta_cols
632 * \param[in] tlrow
633 * \param[in] tlcol
634 * \param[in] brrow
635 * \param[in] brcol
636 *
637 * \return none
638 */
639
save_undo_range_shift(int delta_rows,int delta_cols,int tlrow,int tlcol,int brrow,int brcol)640 void save_undo_range_shift(int delta_rows, int delta_cols, int tlrow, int tlcol, int brrow, int brcol) {
641 struct undo_range_shift * urs = (struct undo_range_shift *) malloc( (unsigned) sizeof(struct undo_range_shift ) );
642 urs->delta_rows = delta_rows;
643 urs->delta_cols = delta_cols;
644 urs->tlrow = tlrow;
645 urs->tlcol = tlcol;
646 urs->brrow = brrow;
647 urs->brcol = brcol;
648 undo_item.range_shift = urs;
649 return;
650 }
651
652 /*
653 * This function is used for undoing and redoing
654 * changes caused by commands that hide/show rows/columns of screen
655 * such as Zr Zc Sc Sr commands.
656 * it stores in four different lists (int * list) the row or columns numbers
657 * that are showed or hidden because of a change.
658 * As these lists are dynamically built, in the first position of every list,
659 * we always store the number of elements that the list has.
660 */
661 /**
662 * \brief todo document undo_hide_show()
663 *
664 * \details this function is used for undoint and redoing changes
665 * caused by commands that hide/show rows/columns of screen such
666 * as zr, zc, sc, and sr commands.
667 *
668 * it stores in four different lists (int * list) the row or column numbers
669 * that are shown or hidden because of a change. as these lists are
670 * dynamically built, in the first position of every list, we always store
671 * the number of elements that the list has.
672 *
673 * \param[in] row
674 * \param[in] col
675 * \param[in] type
676 * \param[in] arg
677 *
678 * \return none
679 */
undo_hide_show(int row,int col,char type,int arg)680 void undo_hide_show(int row, int col, char type, int arg) {
681 int i;
682 if (type == 'h') {
683 if (row > -1) { // hide row
684 if (undo_item.row_hidded == NULL) {
685 undo_item.row_hidded = (int *) malloc(sizeof(int) * (arg + 1));
686 undo_item.row_hidded[0] = 0;
687 } else
688 undo_item.row_hidded = (int *) realloc(undo_item.row_hidded, sizeof(int) * (undo_item.row_hidded[0] + arg + 1));
689
690 for (i=0; i < arg; i++)
691 undo_item.row_hidded[undo_item.row_hidded[0] + i + 1] = row + i;
692
693 undo_item.row_hidded[0] += arg; // keep in first position the number of elements (rows)
694
695 } else if (col > -1) { // hide col
696 if (undo_item.col_hidded == NULL) {
697 undo_item.col_hidded = (int *) malloc(sizeof(int) * (arg + 1));
698 undo_item.col_hidded[0] = 0;
699 } else
700 undo_item.col_hidded = (int *) realloc(undo_item.col_hidded, sizeof(int) * (undo_item.col_hidded[0] + arg + 1));
701
702 for (i=0; i < arg; i++)
703 undo_item.col_hidded[undo_item.col_hidded[0] + i + 1] = col + i;
704
705 undo_item.col_hidded[0] += arg; // keep in first position the number of elements (cols)
706 }
707
708 } else if (type == 's') {
709 if (row > -1) { // show row
710 if (undo_item.row_showed == NULL) {
711 undo_item.row_showed = (int *) malloc(sizeof(int) * (arg + 1));
712 undo_item.row_showed[0] = 0;
713 } else
714 undo_item.row_showed = (int *) realloc(undo_item.row_showed, sizeof(int) * (undo_item.row_showed[0] + arg + 1));
715
716 for (i=0; i < arg; i++)
717 undo_item.row_showed[undo_item.row_showed[0] + i + 1] = row + i;
718
719 undo_item.row_showed[0] += arg; // keep in first position the number of elements (rows)
720
721 } else if (col > -1) { // show col
722 if (undo_item.col_showed == NULL) {
723 undo_item.col_showed = (int *) malloc(sizeof(int) * (arg + 1));
724 undo_item.col_showed[0] = 0;
725 } else
726 undo_item.col_showed = (int *) realloc(undo_item.col_showed, sizeof(int) * (undo_item.col_showed[0] + arg + 1));
727
728 for (i=0; i < arg; i++)
729 undo_item.col_showed[undo_item.col_showed[0] + i + 1] = col + i;
730
731 undo_item.col_showed[0] += arg; // keep in first position the number of elements (cols)
732
733 }
734 }
735 return;
736 }
737
738
739 /**
740 * \brief undo_freeze_unfreeze()
741 *
742 * \details this function is used for undoint and redoing changes
743 * caused by freeze row/col and unfreeze row/col commands
744 *
745 * \param[in] row
746 * \param[in] col
747 * \param[in] type 'f' or 'u'
748 * \param[in] arg
749 *
750 * \return none
751 */
752
undo_freeze_unfreeze(int row,int col,char type,int arg)753 void undo_freeze_unfreeze(int row, int col, char type, int arg) {
754 int i;
755 if (type == 'f') {
756 if (row > -1) { // hide row
757 if (undo_item.row_frozed == NULL) {
758 undo_item.row_frozed = (int *) malloc(sizeof(int) * (arg + 1));
759 undo_item.row_frozed[0] = 0;
760 } else
761 undo_item.row_frozed = (int *) realloc(undo_item.row_frozed, sizeof(int) * (undo_item.row_frozed[0] + arg + 1));
762
763 for (i=0; i < arg; i++)
764 undo_item.row_frozed[undo_item.row_frozed[0] + i + 1] = row + i;
765
766 undo_item.row_frozed[0] += arg; // keep in first position the number of elements (rows)
767
768 } else if (col > -1) { // hide col
769 if (undo_item.col_frozed == NULL) {
770 undo_item.col_frozed = (int *) malloc(sizeof(int) * (arg + 1));
771 undo_item.col_frozed[0] = 0;
772 } else
773 undo_item.col_frozed = (int *) realloc(undo_item.col_frozed, sizeof(int) * (undo_item.col_frozed[0] + arg + 1));
774
775 for (i=0; i < arg; i++)
776 undo_item.col_frozed[undo_item.col_frozed[0] + i + 1] = col + i;
777
778 undo_item.col_frozed[0] += arg; // keep in first position the number of elements (cols)
779 }
780 } else if (type == 'u') {
781 if (row > -1) { // unfreeze row
782 if (undo_item.row_unfrozed == NULL) {
783 undo_item.row_unfrozed = (int *) malloc(sizeof(int) * (arg + 1));
784 undo_item.row_unfrozed[0] = 0;
785 } else
786 undo_item.row_unfrozed = (int *) realloc(undo_item.row_unfrozed, sizeof(int) * (undo_item.row_unfrozed[0] + arg + 1));
787
788 for (i=0; i < arg; i++)
789 undo_item.row_unfrozed[undo_item.row_unfrozed[0] + i + 1] = row + i;
790
791 undo_item.row_unfrozed[0] += arg; // keep in first position the number of elements (rows)
792
793 } else if (col > -1) { // unfreeze col
794 if (undo_item.col_unfrozed == NULL) {
795 undo_item.col_unfrozed = (int *) malloc(sizeof(int) * (arg + 1));
796 undo_item.col_unfrozed[0] = 0;
797 } else
798 undo_item.col_unfrozed = (int *) realloc(undo_item.col_unfrozed, sizeof(int) * (undo_item.col_unfrozed[0] + arg + 1));
799
800 for (i=0; i < arg; i++)
801 undo_item.col_unfrozed[undo_item.col_unfrozed[0] + i + 1] = col + i;
802
803 undo_item.col_unfrozed[0] += arg; // keep in first position the number of elements (cols)
804
805 }
806 }
807 return;
808 }
809
810
811 /**
812 * \brief Do UNDO operation
813 *
814 * Shift a range of an undo shift range to the original position, if any,
815 * append 'ent' elements 'removed' and remove those from 'added'.
816 *
817 * \return none
818 */
819
do_undo()820 void do_undo() {
821 if (undo_list == NULL || undo_list_pos == 0) {
822 sc_error("No UNDO's left.");
823 return;
824 }
825
826 int ori_currow = currow;
827 int ori_curcol = curcol;
828 int mf = modflg; // save modflag status
829
830 struct undo * ul = undo_list;
831
832 // removed added ents
833 struct ent * i = ul->added;
834 while (i != NULL) {
835 struct ent * pp = *ATBL(tbl, i->row, i->col);
836 clearent(pp);
837 cleanent(pp);
838 i = i->next;
839 }
840
841 // Make undo shift, if any
842 if (ul->range_shift != NULL) {
843 // fix marks for rows
844 if (ul->range_shift->delta_rows > 0) // sj
845 fix_marks(-(ul->range_shift->brrow - ul->range_shift->tlrow + 1), 0, ul->range_shift->tlrow, maxrow, ul->range_shift->tlcol, ul->range_shift->brcol);
846 else if (ul->range_shift->delta_rows < 0) // sk
847 fix_marks( (ul->range_shift->brrow - ul->range_shift->tlrow + 1), 0, ul->range_shift->tlrow, maxrow, ul->range_shift->tlcol, ul->range_shift->brcol);
848
849 // handle row_hidden
850 fix_row_hidden(ul->range_shift->delta_rows, ul->range_shift->tlrow, maxrow);
851
852 // fix marks for cols
853 if (ul->range_shift->delta_cols > 0) // sl
854 fix_marks(0, -(ul->range_shift->brcol - ul->range_shift->tlcol + 1), ul->range_shift->tlrow, ul->range_shift->brrow, ul->range_shift->tlcol, maxcol);
855 else if (ul->range_shift->delta_cols < 0) // sh
856 fix_marks(0, (ul->range_shift->brcol - ul->range_shift->tlcol + 1), ul->range_shift->tlrow, ul->range_shift->brrow, ul->range_shift->tlcol, maxcol);
857
858 // handle col_hidden
859 fix_col_hidden(ul->range_shift->delta_cols, ul->range_shift->tlcol, maxcol);
860
861 // handle row_frozen
862 fix_row_frozen(ul->range_shift->delta_rows, ul->range_shift->tlrow, maxrow);
863
864 // handle col_frozen
865 fix_col_frozen(ul->range_shift->delta_cols, ul->range_shift->tlcol, maxcol);
866
867 // shift range now
868 shift_range(- ul->range_shift->delta_rows, - ul->range_shift->delta_cols,
869 ul->range_shift->tlrow, ul->range_shift->tlcol, ul->range_shift->brrow, ul->range_shift->brcol);
870
871 // shift col_formats here.
872 if (ul->range_shift->tlcol >= 0 && ul->range_shift->tlrow == 0 && ul->range_shift->brrow == maxrow) { // && ul->range_shift->delta_cols > 0) {
873 int i;
874 if (ul->range_shift->delta_cols > 0)
875 for (i = ul->range_shift->brcol + ul->range_shift->delta_cols; i <= maxcol; i++) {
876 fwidth[i - ul->range_shift->delta_cols] = fwidth[i];
877 precision[i - ul->range_shift->delta_cols] = precision[i];
878 realfmt[i - ul->range_shift->delta_cols] = realfmt[i];
879 }
880 else
881 for (i = maxcol; i >= ul->range_shift->tlcol - ul->range_shift->delta_cols; i--) {
882 fwidth[i] = fwidth[i + ul->range_shift->delta_cols];
883 precision[i] = precision[i + ul->range_shift->delta_cols];
884 realfmt[i] = realfmt[i + ul->range_shift->delta_cols];
885 }
886 }
887 // do the same for rows here.
888 if (ul->range_shift->tlrow >= 0 && ul->range_shift->tlcol == 0 && ul->range_shift->brcol == maxcol) { // && ul->range_shift->delta_rows > 0) {
889 int i;
890 if (ul->range_shift->delta_rows > 0)
891 for (i = ul->range_shift->brrow + ul->range_shift->delta_rows; i <= maxrow; i++)
892 row_format[i - ul->range_shift->delta_rows] = row_format[i];
893 else
894 for (i = maxrow; i >= ul->range_shift->tlrow - ul->range_shift->delta_rows; i--)
895 row_format[i] = row_format[i + ul->range_shift->delta_rows];
896 }
897 }
898
899 // Change cursor position
900 //if (ul->removed != NULL) {
901 // currow = ul->removed->row;
902 // curcol = ul->removed->col;
903 //}
904
905 // Append 'ent' elements from the removed ones
906 struct ent * j = ul->removed;
907 while (j != NULL) {
908 struct ent * h;
909 if ((h = *ATBL(tbl, j->row, j->col))) clearent(h);
910 struct ent * e_now = lookat(j->row, j->col);
911 (void) copyent(e_now, j, 0, 0, 0, 0, 0, 0, 0);
912 j = j->next;
913 }
914
915 // Show hidden cols and rows
916 // Hide visible cols and rows
917 if (ul->col_hidded != NULL) {
918 int * pd = ul->col_hidded;
919 int left = *(pd++);
920 while (left--) {
921 col_hidden[*(pd++)] = FALSE;
922 }
923 }
924 else if (ul->col_showed != NULL) {
925 int * pd = ul->col_showed;
926 int left = *(pd++);
927 while (left--) {
928 col_hidden[*(pd++)] = TRUE;
929 }
930 }
931 else if (ul->row_hidded != NULL) {
932 int * pd = ul->row_hidded;
933 int left = *(pd++);
934 while (left--) {
935 row_hidden[*(pd++)] = FALSE;
936 }
937 }
938 else if (ul->row_showed != NULL) {
939 int * pd = ul->row_showed;
940 int left = *(pd++);
941 while (left--) {
942 row_hidden[*(pd++)] = TRUE;
943 }
944 }
945
946 // freeze frozen cols and rows
947 // unfreeze unfrozen cols and rows
948 if (ul->col_unfrozed != NULL) {
949 int * pd = ul->col_unfrozed;
950 int left = *(pd++);
951 while (left--) {
952 col_frozen[*(pd++)] = TRUE;
953 }
954 }
955 if (ul->col_frozed != NULL) {
956 int * pd = ul->col_frozed;
957 int left = *(pd++);
958 while (left--) {
959 col_frozen[*(pd++)] = FALSE;
960 }
961 }
962 if (ul->row_unfrozed != NULL) {
963 int * pd = ul->row_unfrozed;
964 int left = *(pd++);
965 while (left--) {
966 row_frozen[*(pd++)] = TRUE;
967 }
968 }
969 if (ul->row_frozed != NULL) {
970 int * pd = ul->row_frozed;
971 int left = *(pd++);
972 while (left--) {
973 row_frozen[*(pd++)] = FALSE;
974 }
975 }
976
977 // Restore previous col format
978 if (ul->cols_format != NULL) {
979 struct undo_cols_format * uf = ul->cols_format;
980 int size = uf->length;
981 int i;
982
983 for (i=0; i < size; i++) {
984 if (uf->cols[i].type == 'R') {
985 fwidth[uf->cols[i].col] = uf->cols[i].fwidth;
986 precision[uf->cols[i].col] = uf->cols[i].precision;
987 realfmt[uf->cols[i].col] = uf->cols[i].realfmt;
988 }
989 }
990 }
991
992 // Restore previous row format
993 if (ul->rows_format != NULL) {
994 struct undo_rows_format * uf = ul->rows_format;
995 int size = uf->length;
996 int i;
997
998 for (i=0; i < size; i++) {
999 if (uf->rows[i].type == 'R') {
1000 row_format[uf->rows[i].row] = uf->rows[i].format;
1001 }
1002 }
1003 }
1004
1005 // for every ent in added and removed, we reeval expression to update graph
1006 struct ent * ie = ul->added;
1007 while (ie != NULL) {
1008 struct ent * p;
1009 if ((p = *ATBL(tbl, ie->row, ie->col)) && p->expr)
1010 EvalJustOneVertex(p, 1);
1011 ie = ie->next;
1012 }
1013 ie = ul->removed;
1014 while (ie != NULL) {
1015 struct ent * p;
1016 if ((p = *ATBL(tbl, ie->row, ie->col)) && p->expr)
1017 EvalJustOneVertex(p, 1);
1018 ie = ie->next;
1019 }
1020
1021 // Restores cursor position
1022 currow = ori_currow;
1023 curcol = ori_curcol;
1024
1025 // decrease modflg
1026 modflg = mf - 1;
1027
1028 if (undo_list->p_ant != NULL) undo_list = undo_list->p_ant;
1029 sc_info("Change: %d of %d", --undo_list_pos, len_undo_list());
1030 return;
1031 }
1032
1033 /**
1034 * \brief Do REDO
1035 *
1036 * Shift a range of an undo shift range to the original position, if any,
1037 * append 'ent' elements from 'added' and remove those from 'removed'.
1038 *
1039 * \return none
1040 */
1041
do_redo()1042 void do_redo() {
1043 //FIXME check why undo_list_pos can sometimes be > len_undo_list(). it shouldnt!!
1044 //if ( undo_list == NULL || undo_list_pos >= len_undo_list() ) {
1045 if ( undo_list == NULL || undo_list_pos == len_undo_list() ) {
1046 sc_error("No REDO's left.");
1047 return;
1048 }
1049
1050 int ori_currow = currow;
1051 int ori_curcol = curcol;
1052 int mf = modflg; // save modflag status
1053
1054 //if (undo_list->p_ant == NULL && undo_list_pos == 0);
1055 //else if (undo_list->p_sig != NULL) undo_list = undo_list->p_sig;
1056 if ((undo_list->p_ant != NULL || undo_list_pos != 0)
1057 && (undo_list->p_sig != NULL)) undo_list = undo_list->p_sig;
1058
1059 struct undo * ul = undo_list;
1060
1061 // Remove 'ent' elements
1062 struct ent * i = ul->removed;
1063 while (i != NULL) {
1064 struct ent * pp = *ATBL(tbl, i->row, i->col);
1065 clearent(pp);
1066 cleanent(pp);
1067 i = i->next;
1068 }
1069
1070 // Make undo shift, if any
1071 if (ul->range_shift != NULL) {
1072 // fix marks for rows
1073 if (ul->range_shift->delta_rows > 0) // sj
1074 fix_marks( (ul->range_shift->brrow - ul->range_shift->tlrow + 1), 0, ul->range_shift->tlrow, maxrow, ul->range_shift->tlcol, ul->range_shift->brcol);
1075 else if (ul->range_shift->delta_rows < 0) // sk
1076 fix_marks(-(ul->range_shift->brrow - ul->range_shift->tlrow + 1), 0, ul->range_shift->tlrow, maxrow, ul->range_shift->tlcol, ul->range_shift->brcol);
1077
1078 // handle row_hidden
1079 fix_row_hidden(-ul->range_shift->delta_rows, ul->range_shift->tlrow, maxrow);
1080
1081 // fix marks for cols
1082 if (ul->range_shift->delta_cols > 0) // sl
1083 fix_marks(0, (ul->range_shift->brcol - ul->range_shift->tlcol + 1), ul->range_shift->tlrow, ul->range_shift->brrow, ul->range_shift->tlcol, maxcol);
1084 else if (ul->range_shift->delta_cols < 0) // sh
1085 fix_marks(0, -(ul->range_shift->brcol - ul->range_shift->tlcol + 1), ul->range_shift->tlrow, ul->range_shift->brrow, ul->range_shift->tlcol, maxcol);
1086
1087 // handle col_hidden
1088 fix_col_hidden(-ul->range_shift->delta_cols, ul->range_shift->tlcol, maxcol);
1089
1090 // shift range now
1091 shift_range(ul->range_shift->delta_rows, ul->range_shift->delta_cols,
1092 ul->range_shift->tlrow, ul->range_shift->tlcol, ul->range_shift->brrow, ul->range_shift->brcol);
1093
1094 // shift col_formats here
1095 if (ul->range_shift->tlcol >= 0 && ul->range_shift->tlrow == 0 && ul->range_shift->brrow == maxrow) {
1096 int i;
1097 if (ul->range_shift->delta_cols > 0)
1098 for (i = maxcol; i >= ul->range_shift->tlcol + ul->range_shift->delta_cols; i--) {
1099 fwidth[i] = fwidth[i - ul->range_shift->delta_cols];
1100 precision[i] = precision[i - ul->range_shift->delta_cols];
1101 realfmt[i] = realfmt[i - ul->range_shift->delta_cols];
1102 }
1103 else
1104 for (i = ul->range_shift->tlcol; i - ul->range_shift->delta_cols <= maxcol; i++) {
1105 fwidth[i] = fwidth[i - ul->range_shift->delta_cols];
1106 precision[i] = precision[i - ul->range_shift->delta_cols];
1107 realfmt[i] = realfmt[i - ul->range_shift->delta_cols];
1108 }
1109 }
1110 // do the same for rows here
1111 if (ul->range_shift->tlrow >= 0 && ul->range_shift->tlcol == 0 && ul->range_shift->brcol == maxcol) {
1112 int i;
1113 if (ul->range_shift->delta_rows > 0)
1114 for (i = maxrow; i >= ul->range_shift->tlrow + ul->range_shift->delta_rows; i--)
1115 row_format[i] = row_format[i - ul->range_shift->delta_rows];
1116 else
1117 for (i = ul->range_shift->tlrow; i - ul->range_shift->delta_rows <= maxrow; i++)
1118 row_format[i] = row_format[i - ul->range_shift->delta_rows];
1119 }
1120 }
1121
1122 // Change cursor position
1123 //if (ul->p_sig != NULL && ul->p_sig->removed != NULL) {
1124 // currow = ul->p_sig->removed->row;
1125 // curcol = ul->p_sig->removed->col;
1126 //}
1127
1128 // Append 'ent' elements
1129 struct ent * j = ul->added;
1130 while (j != NULL) {
1131 struct ent * h;
1132 if ((h = *ATBL(tbl, j->row, j->col))) clearent(h);
1133 struct ent * e_now = lookat(j->row, j->col);
1134 (void) copyent(e_now, j, 0, 0, 0, 0, 0, 0, 0);
1135 j = j->next;
1136 }
1137
1138 // Hide previously hidden cols and rows
1139 // Show previously visible cols and rows
1140 if (ul->col_hidded != NULL) {
1141 int * pd = ul->col_hidded;
1142 int left = *(pd++);
1143 while (left--) {
1144 col_hidden[*(pd++)] = TRUE;
1145 }
1146 }
1147 else if (ul->col_showed != NULL) {
1148 int * pd = ul->col_showed;
1149 int left = *(pd++);
1150 while (left--) {
1151 col_hidden[*(pd++)] = FALSE;
1152 }
1153 }
1154 else if (ul->row_hidded != NULL) {
1155 int * pd = ul->row_hidded;
1156 int left = *(pd++);
1157 while (left--) {
1158 row_hidden[*(pd++)] = TRUE;
1159 }
1160 }
1161 else if (ul->row_showed != NULL) {
1162 int * pd = ul->row_showed;
1163 int left = *(pd++);
1164 while (left--) {
1165 row_hidden[*(pd++)] = FALSE;
1166 }
1167 }
1168
1169 // freeze frozen cols and rows
1170 // unfreeze unfrozen cols and rows
1171 if (ul->col_unfrozed != NULL) {
1172 int * pd = ul->col_unfrozed;
1173 int left = *(pd++);
1174 while (left--) {
1175 col_frozen[*(pd++)] = FALSE;
1176 }
1177 }
1178 if (ul->col_frozed != NULL) {
1179 int * pd = ul->col_frozed;
1180 int left = *(pd++);
1181 while (left--) {
1182 col_frozen[*(pd++)] = TRUE;
1183 }
1184 }
1185 if (ul->row_unfrozed != NULL) {
1186 int * pd = ul->row_unfrozed;
1187 int left = *(pd++);
1188 while (left--) {
1189 row_frozen[*(pd++)] = FALSE;
1190 }
1191 }
1192 if (ul->row_frozed != NULL) {
1193 int * pd = ul->row_frozed;
1194 int left = *(pd++);
1195 while (left--) {
1196 row_frozen[*(pd++)] = TRUE;
1197 }
1198 }
1199
1200 // Restore new col format
1201 if (ul->cols_format != NULL) {
1202 struct undo_cols_format * uf = ul->cols_format;
1203 int size = uf->length;
1204 int i;
1205
1206 for (i=0; i < size; i++) {
1207 if (uf->cols[i].type == 'A') {
1208 fwidth[uf->cols[i].col] = uf->cols[i].fwidth;
1209 precision[uf->cols[i].col] = uf->cols[i].precision;
1210 realfmt[uf->cols[i].col] = uf->cols[i].realfmt;
1211 }
1212 }
1213 }
1214
1215 // Restore new row format
1216 if (ul->rows_format != NULL) {
1217 struct undo_rows_format * uf = ul->rows_format;
1218 int size = uf->length;
1219 int i;
1220
1221 for (i=0; i < size; i++) {
1222 if (uf->rows[i].type == 'A') {
1223 row_format[uf->rows[i].row] = uf->rows[i].format;
1224 }
1225 }
1226 }
1227
1228 // for every ent in added and removed, we reeval expression to update graph
1229 struct ent * ie = ul->added;
1230 while (ie != NULL) {
1231 struct ent * p;
1232 if ((p = *ATBL(tbl, ie->row, ie->col)) && p->expr)
1233 EvalJustOneVertex(p, 1);
1234 ie = ie->next;
1235 }
1236 ie = ul->removed;
1237 while (ie != NULL) {
1238 struct ent * p;
1239 if ((p = *ATBL(tbl, ie->row, ie->col)) && p->expr)
1240 EvalJustOneVertex(p, 1);
1241 ie = ie->next;
1242 }
1243
1244 // Restores cursor position
1245 currow = ori_currow;
1246 curcol = ori_curcol;
1247
1248 // increase modflg
1249 modflg = mf + 1;
1250
1251 sc_info("Change: %d of %d", ++undo_list_pos, len_undo_list());
1252
1253 return;
1254 }
1255 #endif
1256