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