summaryrefslogtreecommitdiffstats
path: root/src.scim2/undo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src.scim2/undo.c')
-rwxr-xr-xsrc.scim2/undo.c543
1 files changed, 543 insertions, 0 deletions
diff --git a/src.scim2/undo.c b/src.scim2/undo.c
new file mode 100755
index 0000000..790b09e
--- /dev/null
+++ b/src.scim2/undo.c
@@ -0,0 +1,543 @@
+#ifdef UNDO
+/*
+----------------------------------------------------------------------------------------
+UNDO and REDO features works with an 'undo' struct list.
+Which contains:
+ p_ant: pointer to 'undo' struct. If NULL, this node is the first change
+ for the session.
+ struct ent * added: 'ent' elements added by the change
+ struct ent * removed: 'ent' elements removed by the change
+ struct undo_range_shift * range_shift: range shifted by change
+ row_hidded: integers list (int *) hidden rows on screen
+ row_showed: integers list (int *) visible rows on screen
+ col_hidded: integers list (int *) hidden columns on screen
+ col_showed: integers list (int *) visible columns on screen
+ NOTE: the first position of the lists contains (number of elements - 1) in the list
+ p_sig: pointer to 'undo' struct, If NULL, this node is the last change in
+ the session.
+
+Follows one level UNDO/REDO scheme. A change (C1) is made, then an UNDO operation, and
+another change (C2). From there later changes are removed.
+Scheme:
+
++ C1 -> + -> UNDO -
+^ \
+|_ |
+ \---------------/
+|
+|
+|
+ \-> C2 --> + ...
+
+undo_shift_range struct contains:
+ int delta_rows: delta rows for the range shift
+ int delta_cols: delta columns for the range shift
+ int tlrow: Upper left row defining the range of the shift
+ (As if a cell range shift is made)
+ int tlcol: Upper left column defining the range of the shift
+ (As if a cell range shift is made)
+ int brrow: Lower right row defining the range of the shift
+ (As if a cell range shift is made)
+ int brcol: Lower right column defining the range of the shift
+ (As if a cell range shift is made)
+
+Implemented actions for UNDO/REDO:
+1. Remove content from cell or range
+2. Input content into a cell
+3. Edit a cell
+4. Change alginment of range or cell
+5. Paste range or cell
+6. Shift range or cell with sh, sj, sk, sl
+7. Insert row or column
+8. Delete row or column
+9. Paste row or column
+10. Hide/show rows and columns
+11. Sort of a range
+12. Change in the format of a range or cell
+13. '-' and '+' commands in normal mode
+14. Lock and unlock of cells
+15. datefmt command
+
+NOT implemented:
+1. Change format of an entire column
+2. Recover equations after redo of changes over ents that have equations on them.
+
+----------------------------------------------------------------------------------------
+*/
+
+#include <stdlib.h>
+#include "undo.h"
+#include "macros.h"
+#include "curses.h"
+#include "conf.h"
+#include "sc.h"
+#include "cmds.h"
+#include "color.h" // for set_ucolor
+#include "marks.h"
+#include "shift.h"
+
+// undolist
+static struct undo * undo_list = NULL;
+
+// current position in the list
+static int undo_list_pos = 0;
+
+// Number of elements in the list
+static int undo_list_len = 0;
+
+// Temporal variable
+static struct undo undo_item;
+
+// Init 'unto_item'
+void create_undo_action() {
+ undo_item.added = NULL;
+ undo_item.removed = NULL;
+ undo_item.p_ant = NULL;
+ undo_item.p_sig = NULL;
+ undo_item.range_shift = NULL;
+
+ undo_item.row_hidded = NULL;
+ undo_item.row_showed = NULL;
+ undo_item.col_hidded = NULL;
+ undo_item.col_showed = NULL;
+ return;
+}
+
+// Save undo_item copy with 'ent' elements modified, and the undo range shift
+// struct into the undolist
+void end_undo_action() {
+ add_to_undolist(undo_item);
+
+ // in case we need to dismissed this undo_item!
+ if ((undo_item.added == NULL &&
+ undo_item.removed == NULL && undo_item.range_shift == NULL &&
+ undo_item.row_hidded == NULL && undo_item.row_showed == NULL &&
+ undo_item.col_hidded == NULL && undo_item.col_showed == NULL) || loading) {
+ if (undo_list->p_ant != NULL) undo_list = undo_list->p_ant;
+ undo_list_pos--;
+ clear_from_current_pos();
+ }
+
+ return;
+}
+
+// Add a undo node to the undolist,
+// allocate memory for undo struct,
+// fill variable with undo_item value and append it to the list
+void add_to_undolist(struct undo u) {
+ // If not at the end of the list, remove from the end
+ if ( undo_list != NULL && undo_list_pos != len_undo_list() )
+ clear_from_current_pos();
+
+ struct undo * ul = (struct undo *) malloc (sizeof(struct undo));
+ ul->p_sig = NULL;
+
+ // Add 'ent' elements
+ ul->added = u.added;
+ ul->removed = u.removed;
+ ul->range_shift = u.range_shift;
+ ul->row_hidded = u.row_hidded;
+ ul->col_hidded = u.col_hidded;
+ ul->row_showed = u.row_showed;
+ ul->col_showed = u.col_showed;
+
+ if (undo_list == NULL) {
+ ul->p_ant = NULL;
+ undo_list = ul;
+ } else {
+ ul->p_ant = undo_list;
+
+ // Voy hasta el final de la lista
+ while (undo_list->p_sig != NULL) undo_list = undo_list->p_sig;
+
+ undo_list->p_sig = ul;
+ undo_list = undo_list->p_sig;
+ }
+ undo_list_pos++;
+ undo_list_len++;
+ return;
+}
+
+// Cascade free UNDO node memory
+void free_undo_node(struct undo * ul) {
+
+ struct ent * de;
+ struct ent * en;
+ struct undo * e;
+
+ // Remove from current position
+ while (ul != NULL) {
+ en = ul->added;
+ while (en != NULL) {
+ de = en->next;
+ clearent(en);
+ free(en);
+ en = de;
+ }
+ en = ul->removed;
+ while (en != NULL) {
+ de = en->next;
+ clearent(en);
+ free(en);
+ en = de;
+ }
+ e = ul->p_sig;
+
+ if (ul->range_shift != NULL) free(ul->range_shift); // Free undo_range_shift memory
+ if (ul->row_hidded != NULL) free(ul->row_hidded); // Free hidden row memory
+ if (ul->col_hidded != NULL) free(ul->col_hidded); // Free hidden col memory
+ if (ul->row_showed != NULL) free(ul->row_showed); // Free showed row memory
+ if (ul->col_showed != NULL) free(ul->col_showed); // Free showed col memory
+
+ free(ul);
+ undo_list_len--;
+ ul = e;
+ }
+ return;
+}
+
+// Remove nodes below the current position from the undolist
+void clear_from_current_pos() {
+ if (undo_list == NULL) return;
+
+ if (undo_list->p_ant == NULL) {
+ free_undo_node(undo_list);
+ undo_list = NULL;
+ } else {
+ struct undo * ul = undo_list->p_sig; // Previous
+ free_undo_node(ul);
+ undo_list->p_sig = NULL;
+ }
+
+ return;
+}
+
+// Remove undolist content
+void clear_undo_list () {
+ if (undo_list == NULL) return;
+
+ // Go to the beginning of the list
+ while (undo_list->p_ant != NULL ) {
+ undo_list = undo_list->p_ant;
+ }
+
+ struct undo * ul = undo_list;
+
+ free_undo_node(ul);
+
+ undo_list = NULL;
+ undo_list_pos = 0;
+
+ return;
+}
+
+int len_undo_list() {
+ return undo_list_len;
+}
+
+// Take a range of 'ent' elements and create new ones (as many as elements
+// inside the specified range).
+// Then copy the content of the original ones to the new ones and save them into
+// the 'added' or 'removed' list of undo_item, according to the char type.
+void copy_to_undostruct (int row_desde, int col_desde, int row_hasta, int col_hasta, char type) {
+ int c, r;
+ for (r = row_desde; r <= row_hasta; r++)
+ for (c = col_desde; c <= col_hasta; c++) {
+ struct ent * e = (struct ent *) malloc( (unsigned) sizeof(struct ent) );
+ cleanent(e);
+ copyent(e, lookat(r, c), 0, 0, 0, 0, r, c, 0);
+
+ // Append 'ent' element at the beginning
+ if (type == 'a') {
+ e->next = undo_item.added;
+ undo_item.added = e;
+ } else {
+ e->next = undo_item.removed;
+ undo_item.removed = e;
+ }
+
+ }
+ return;
+}
+
+// Takes a range, a rows and columns delta and save them in the undo struct
+// Used to shift ranges when UNDO or REDO without duplicating 'ent' elements
+void save_undo_range_shift(int delta_rows, int delta_cols, int tlrow, int tlcol, int brrow, int brcol) {
+ struct undo_range_shift * urs = (struct undo_range_shift *) malloc( (unsigned) sizeof(struct undo_range_shift ) );
+ urs->delta_rows = delta_rows;
+ urs->delta_cols = delta_cols;
+ urs->tlrow = tlrow;
+ urs->tlcol = tlcol;
+ urs->brrow = brrow;
+ urs->brcol = brcol;
+ undo_item.range_shift = urs;
+ return;
+}
+
+// This function is used for undoing and redoing
+// changes caused by commands that hide/show rows/columns of screen
+// such as Zr Zc Sc Sr commands.
+// it stores in four different lists (int * list) the row or columns numbers
+// that are showed or hidden because of a change.
+// As these lists are dynamically built, in the first position of every list,
+// we always store the number of elements that the list has.
+void undo_hide_show(int row, int col, char type, int arg) {
+ int i;
+ if (type == 'h') {
+ if (row > -1) { // hide row
+ if (undo_item.row_hidded == NULL) {
+ undo_item.row_hidded = (int *) malloc(sizeof(int) * (arg + 1));
+ undo_item.row_hidded[0] = 0;
+ } else
+ undo_item.row_hidded = (int *) realloc(undo_item.row_hidded, sizeof(int) * (undo_item.row_hidded[0] + arg + 1));
+
+ for (i=0; i < arg; i++)
+ undo_item.row_hidded[undo_item.row_hidded[0] + i + 1] = row + i;
+
+ undo_item.row_hidded[0] += arg; // keep in first position the number of elements (rows)
+
+ } else if (col > -1) { // hide col
+ if (undo_item.col_hidded == NULL) {
+ undo_item.col_hidded = (int *) malloc(sizeof(int) * (arg + 1));
+ undo_item.col_hidded[0] = 0;
+ } else
+ undo_item.col_hidded = (int *) realloc(undo_item.col_hidded, sizeof(int) * (undo_item.col_hidded[0] + arg + 1));
+
+ for (i=0; i < arg; i++)
+ undo_item.col_hidded[undo_item.col_hidded[0] + i + 1] = col + i;
+
+ undo_item.col_hidded[0] += arg; // keep in first position the number of elements (cols)
+ }
+
+ } else if (type == 's') {
+ if (row > -1) { // show row
+ if (undo_item.row_showed == NULL) {
+ undo_item.row_showed = (int *) malloc(sizeof(int) * (arg + 1));
+ undo_item.row_showed[0] = 0;
+ } else
+ undo_item.row_showed = (int *) realloc(undo_item.row_showed, sizeof(int) * (undo_item.row_showed[0] + arg + 1));
+
+ for (i=0; i < arg; i++)
+ undo_item.row_showed[undo_item.row_showed[0] + i + 1] = row + i;
+
+ undo_item.row_showed[0] += arg; // keep in first position the number of elements (rows)
+
+ } else if (col > -1) { // show col
+ if (undo_item.col_showed == NULL) {
+ undo_item.col_showed = (int *) malloc(sizeof(int) * (arg + 1));
+ undo_item.col_showed[0] = 0;
+ } else
+ undo_item.col_showed = (int *) realloc(undo_item.col_showed, sizeof(int) * (undo_item.col_showed[0] + arg + 1));
+
+ for (i=0; i < arg; i++)
+ undo_item.col_showed[undo_item.col_showed[0] + i + 1] = col + i;
+
+ undo_item.col_showed[0] += arg; // keep in first position the number of elements (cols)
+
+ }
+ }
+ return;
+}
+
+// Do UNDO operation
+// Shift a range of an undo shift range to the original position, if any, append
+// 'ent' elements from 'removed' and remove those from 'added'
+void do_undo() {
+ if (undo_list == NULL || undo_list_pos == 0) {
+ scerror("Not UNDO's left");
+ return;
+ }
+ //scinfo("%d %d", undo_list_pos, len_undo_list());
+
+ int ori_currow = currow;
+ int ori_curcol = curcol;
+ int mf = modflg; // save modflag status
+
+ struct undo * ul = undo_list;
+
+ // Make undo shift, if any
+ if (ul->range_shift != NULL) {
+ // fix marks
+ if (ul->range_shift->delta_rows > 0) // sj
+ 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);
+ else if (ul->range_shift->delta_rows < 0) // sk
+ 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);
+ if (ul->range_shift->delta_cols > 0) // sl
+ 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);
+ else if (ul->range_shift->delta_cols < 0) // sh
+ 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);
+
+ shift_range(- ul->range_shift->delta_rows, - ul->range_shift->delta_cols,
+ ul->range_shift->tlrow, ul->range_shift->tlcol, ul->range_shift->brrow, ul->range_shift->brcol);
+ }
+
+ // Remove 'ent' elements
+ struct ent * i = ul->added;
+ while (i != NULL) {
+ struct ent * pp = *ATBL(tbl, i->row, i->col);
+ clearent(pp);
+ cleanent(pp);
+ i = i->next;
+ }
+
+ // Change cursor position
+ //if (ul->removed != NULL) {
+ // currow = ul->removed->row;
+ // curcol = ul->removed->col;
+ //}
+
+ // Append 'ent' elements from the removed ones
+ struct ent * j = ul->removed;
+ while (j != NULL) {
+ struct ent * e_now = lookat(j->row, j->col);
+ (void) copyent(e_now, j, 0, 0, 0, 0, j->row, j->col, 0);
+ j = j->next;
+ }
+
+ // Show hidden cols and rows
+ // Hide visible cols and rows
+ if (ul->col_hidded != NULL) {
+ int * pd = ul->col_hidded;
+ int left = *(pd++);
+ while (left--) {
+ col_hidden[*(pd++)] = FALSE;
+ }
+ }
+ else if (ul->col_showed != NULL) {
+ int * pd = ul->col_showed;
+ int left = *(pd++);
+ while (left--) {
+ col_hidden[*(pd++)] = TRUE;
+ }
+ }
+ else if (ul->row_hidded != NULL) {
+ int * pd = ul->row_hidded;
+ int left = *(pd++);
+ while (left--) {
+ row_hidden[*(pd++)] = FALSE;
+ }
+ }
+ else if (ul->row_showed != NULL) {
+ int * pd = ul->row_showed;
+ int left = *(pd++);
+ while (left--) {
+ row_hidden[*(pd++)] = TRUE;
+ }
+ }
+
+ // Restores cursor position
+ currow = ori_currow;
+ curcol = ori_curcol;
+
+ // decrease modflg
+ modflg= mf - 1;
+
+ if (undo_list->p_ant != NULL) undo_list = undo_list->p_ant;
+ undo_list_pos--;
+ scinfo("Change: %d of %d", undo_list_pos, len_undo_list());
+
+ return;
+}
+
+// Do REDO
+// Shift a range of an undo shift range to the original position, if any, append
+// 'ent' elements from 'added' and remove those from 'removed'
+void do_redo() {
+ if ( undo_list == NULL || undo_list_pos == len_undo_list() ) {
+ scerror("Not REDO's left");
+ return;
+ }
+ //scinfo("%d %d", undo_list_pos, len_undo_list());
+
+ int ori_currow = currow;
+ int ori_curcol = curcol;
+ int mf = modflg; // save modflag status
+
+ if (undo_list->p_ant == NULL && undo_list_pos == 0) ;
+ else if (undo_list->p_sig != NULL) undo_list = undo_list->p_sig;
+
+ struct undo * ul = undo_list;
+
+ // Make undo shift, if any
+ if (ul->range_shift != NULL) {
+ // fix marks
+ if (ul->range_shift->delta_rows > 0) // sj
+ 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);
+ else if (ul->range_shift->delta_rows < 0) // sk
+ 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);
+ if (ul->range_shift->delta_cols > 0) // sl
+ 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);
+ else if (ul->range_shift->delta_cols < 0) // sh
+ 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);
+
+ shift_range(ul->range_shift->delta_rows, ul->range_shift->delta_cols,
+ ul->range_shift->tlrow, ul->range_shift->tlcol, ul->range_shift->brrow, ul->range_shift->brcol);
+ }
+
+ // Remove 'ent' elements
+ struct ent * i = ul->removed;
+ while (i != NULL) {
+ struct ent * pp = *ATBL(tbl, i->row, i->col);
+ clearent(pp);
+ cleanent(pp);
+ i = i->next;
+ }
+
+ // Change cursor position
+ //if (ul->p_sig != NULL && ul->p_sig->removed != NULL) {
+ // currow = ul->p_sig->removed->row;
+ // curcol = ul->p_sig->removed->col;
+ //}
+
+ // Append 'ent' elements
+ struct ent * j = ul->added;
+ while (j != NULL) {
+ struct ent * e_now = lookat(j->row, j->col);
+ (void) copyent(e_now, j, 0, 0, 0, 0, j->row, j->col, 0);
+ j = j->next;
+ }
+
+ // Hide previously hidden cols and rows
+ // Show previously visible cols and rows
+ if (ul->col_hidded != NULL) {
+ int * pd = ul->col_hidded;
+ int left = *(pd++);
+ while (left--) {
+ col_hidden[*(pd++)] = TRUE;
+ }
+ }
+ else if (ul->col_showed != NULL) {
+ int * pd = ul->col_showed;
+ int left = *(pd++);
+ while (left--) {
+ col_hidden[*(pd++)] = FALSE;
+ }
+ }
+ else if (ul->row_hidded != NULL) {
+ int * pd = ul->row_hidded;
+ int left = *(pd++);
+ while (left--) {
+ row_hidden[*(pd++)] = TRUE;
+ }
+ }
+ else if (ul->row_showed != NULL) {
+ int * pd = ul->row_showed;
+ int left = *(pd++);
+ while (left--) {
+ row_hidden[*(pd++)] = FALSE;
+ }
+ }
+
+ // Restores cursor position
+ currow = ori_currow;
+ curcol = ori_curcol;
+
+ // increase modflg
+ modflg= mf + 1;
+
+ scinfo("Change: %d of %d", undo_list_pos + 1, len_undo_list());
+ undo_list_pos++;
+
+ return;
+}
+#endif