[gnumeric] Sheet: unify code for insert/delete columns/rows.



commit 9629707e209bd04d42a2808f1e6ad829e57dd7c9
Author: Morten Welinder <terra gnome org>
Date:   Sun Dec 14 17:27:35 2014 -0500

    Sheet: unify code for insert/delete columns/rows.

 ChangeLog         |   12 +
 src/dependent.c   |   30 ++-
 src/dependent.h   |    4 +-
 src/sheet-style.c |   81 ++++---
 src/sheet-style.h |    2 +-
 src/sheet.c       |  677 +++++++++++++++++++----------------------------------
 6 files changed, 330 insertions(+), 476 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 6f84081..e6ab00e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2014-12-15  Morten Welinder  <terra gnome org>
+
+       * src/sheet-style.c (sheet_style_insdel_colrow): Rename from
+       sheet_style_insert_colrow and handled deletion too.
+
+       * src/sheet.c (sheet_insdel_colrow): Merge from sheet_insert_rows,
+       sheet_insert_cols, sheet_delete_rows, sheet_delete_cols.  Simplify
+       and clean up.
+
+       * src/dependent.c (dependent_has_pos, dependent_move): New
+       functions.
+
 2014-12-14  Morten Welinder  <terra gnome org>
 
        * src/sheet.c (sheet_colrow_add): Make sure the slot we use is
diff --git a/src/dependent.c b/src/dependent.c
index 2e4ae1c..f412df2 100644
--- a/src/dependent.c
+++ b/src/dependent.c
@@ -239,7 +239,7 @@ dummy_dep_eval (G_GNUC_UNUSED GnmDependent *dep)
 static void cell_dep_eval (GnmDependent *dep);
 static void cell_dep_set_expr (GnmDependent *dep, GnmExprTop const *new_texpr);
 static GSList *cell_dep_changed (GnmDependent *dep);
-static GnmCellPos const *cell_dep_pos (GnmDependent const *dep);
+static GnmCellPos *cell_dep_pos (GnmDependent const *dep);
 static void cell_dep_debug_name (GnmDependent const *dep, GString *target);
 static const GnmDependentClass cell_dep_class = {
        cell_dep_eval,
@@ -285,7 +285,7 @@ static const GnmDependentClass managed_dep_class = {
 
 static void style_dep_eval (GnmDependent *dep);
 static GSList *style_dep_changed (GnmDependent *dep);
-static GnmCellPos const *style_dep_pos (GnmDependent const *dep);
+static GnmCellPos *style_dep_pos (GnmDependent const *dep);
 static void style_dep_debug_name (GnmDependent const *dep, GString *target);
 static const GnmDependentClass style_dep_class = {
        style_dep_eval,
@@ -426,6 +426,14 @@ dependent_set_expr (GnmDependent *dep, GnmExprTop const *new_texpr)
        }
 }
 
+gboolean
+dependent_has_pos (GnmDependent const *dep)
+{
+       int const t = dependent_type (dep);
+       GnmDependentClass *klass = g_ptr_array_index (dep_classes, t);
+       return klass->pos != NULL;
+}
+
 GnmCellPos const *
 dependent_pos (GnmDependent const *dep)
 {
@@ -436,6 +444,20 @@ dependent_pos (GnmDependent const *dep)
        return klass->pos ? klass->pos (dep) : &dummy;
 }
 
+void
+dependent_move (GnmDependent *dep, int dx, int dy)
+{
+       int const t = dependent_type (dep);
+       GnmDependentClass *klass = g_ptr_array_index (dep_classes, t);
+       GnmCellPos *pos;
+
+       g_return_if_fail (klass->pos != NULL);
+       pos = klass->pos (dep);
+       /* Need a virtual for this? */
+       pos->col += dx;
+       pos->row += dy;
+}
+
 
 /**
  * dependent_set_sheet:
@@ -1264,7 +1286,7 @@ cell_dep_changed (GnmDependent *dep)
        return work;
 }
 
-static GnmCellPos const *
+static GnmCellPos *
 cell_dep_pos (GnmDependent const *dep)
 {
        return &GNM_DEP_TO_CELL (dep)->pos;
@@ -1392,7 +1414,7 @@ style_dep_changed (GnmDependent *dep)
        return NULL;
 }
 
-static GnmCellPos const *
+static GnmCellPos *
 style_dep_pos (GnmDependent const *dep)
 {
        return &((GnmStyleDependent*)dep)->pos;
diff --git a/src/dependent.h b/src/dependent.h
index a4b8b68..f0eabb2 100644
--- a/src/dependent.h
+++ b/src/dependent.h
@@ -20,7 +20,7 @@ typedef struct {
        void (*eval)       (GnmDependent *dep);
        void (*set_expr)   (GnmDependent *dep, GnmExprTop const *new_texpr);
        GSList* (*changed) (GnmDependent *dep);
-       GnmCellPos const* (*pos) (GnmDependent const *dep);
+       GnmCellPos* (*pos) (GnmDependent const *dep);
        void (*debug_name) (GnmDependent const *dep, GString *target);
 } GnmDependentClass;
 
@@ -98,7 +98,9 @@ void   dependent_add_dynamic_dep (GnmDependent *dep, GnmRangeRef const *rr);
 
 gboolean dependent_is_volatile     (GnmDependent *dep);
 
+gboolean dependent_has_pos (GnmDependent const *dep);
 GnmCellPos const *dependent_pos (GnmDependent const *dep);
+void dependent_move (GnmDependent *dep, int dx, int dy);
 
 GOUndo  *dependents_relocate       (GnmExprRelocateInfo const *info);
 void    dependents_link            (GSList *deps);
diff --git a/src/sheet-style.c b/src/sheet-style.c
index fcd830b..b822e39 100644
--- a/src/sheet-style.c
+++ b/src/sheet-style.c
@@ -2124,56 +2124,63 @@ sheet_style_relocate (GnmExprRelocateInfo const *rinfo)
 }
 
 /**
- * sheet_style_insert_colrow:
+ * sheet_style_insdel_colrow:
  * @rinfo:
  *
- * A utility routine to give the effect of stretching the styles when a col/row
- * is inserted.  This is done by applying the styles from the left/top col/row
- * to the new region.
+ * Insert of delete style columns/rows.
+ *
+ * For the insert case, we stretch the preceding column/row into there space
+ * we open.
  */
 void
-sheet_style_insert_colrow (GnmExprRelocateInfo const *rinfo)
+sheet_style_insdel_colrow (GnmExprRelocateInfo const *rinfo)
 {
+       GnmStyleList *styles = NULL;
+       Sheet *sheet;
        GnmCellPos corner;
-       GnmStyleList *ptr, *styles = NULL;
-       GnmRange r;
+       gboolean is_insert;
 
        g_return_if_fail (rinfo != NULL);
        g_return_if_fail (rinfo->origin_sheet == rinfo->target_sheet);
-
-       /* 1) copy col/row to the top/left of the region, and extend it */
-       corner = rinfo->origin.start;
-       if (rinfo->col_offset != 0) {
-               int const o = rinfo->col_offset - 1;
-               int col = corner.col - 1;
-
-               if (col < 0)
-                       col = 0;
-               corner.row = 0;
-               styles = sheet_style_get_range (rinfo->origin_sheet,
-                              range_init (&r, col, 0, col, gnm_sheet_get_last_row (rinfo->origin_sheet)));
-               if (o > 0)
-                       for (ptr = styles ; ptr != NULL ; ptr = ptr->next)
-                               ((GnmStyleRegion *)ptr->data)->range.end.col = o;
-
-       } else if (rinfo->row_offset != 0) {
-               int const o = rinfo->row_offset - 1;
-               int row = corner.row - 1;
-               if (row < 0)
-                       row = 0;
-               corner.col = 0;
-               range_init_rows (&r, rinfo->origin_sheet, row, row);
-               styles = sheet_style_get_range (rinfo->origin_sheet, &r);
-               if (o > 0)
-                       for (ptr = styles ; ptr != NULL ; ptr = ptr->next)
-                               ((GnmStyleRegion *)ptr->data)->range.end.row = o;
+       g_return_if_fail ((rinfo->col_offset == 0) != (rinfo->row_offset == 0));
+
+       is_insert = (rinfo->col_offset + rinfo->row_offset > 0);
+       sheet = rinfo->origin_sheet;
+
+       if (is_insert) {
+               /* 1) copy col/row to the top/left of the region, and extend it */
+               corner = rinfo->origin.start;
+               if (rinfo->col_offset) {
+                       int col = MAX (corner.col - 1, 0);
+                       GnmStyleList *ptr;
+                       GnmRange r;
+
+                       corner.row = 0;
+                       range_init_cols (&r, sheet, col, col);
+                       styles = sheet_style_get_range (sheet, &r);
+                       for (ptr = styles ; ptr != NULL ; ptr = ptr->next) {
+                               GnmStyleRegion *sr = ptr->data;
+                               sr->range.end.col = rinfo->col_offset - 1;
+                       }
+               } else {
+                       int row = MAX (corner.row - 1, 0);
+                       GnmStyleList *ptr;
+                       GnmRange r;
+
+                       corner.col = 0;
+                       range_init_rows (&r, sheet, row, row);
+                       styles = sheet_style_get_range (sheet, &r);
+                       for (ptr = styles ; ptr != NULL ; ptr = ptr->next) {
+                               GnmStyleRegion *sr = ptr->data;
+                               sr->range.end.row = rinfo->row_offset - 1;
+                       }
+               }
        }
 
        sheet_style_relocate (rinfo);
 
-       if (styles != NULL) {
-               sheet_style_set_list (rinfo->target_sheet, &corner, styles,
-                                     NULL, NULL);
+       if (styles) {
+               sheet_style_set_list (sheet, &corner, styles, NULL, NULL);
                style_list_free (styles);
        }
 }
diff --git a/src/sheet-style.h b/src/sheet-style.h
index 226a83f..8d88671 100644
--- a/src/sheet-style.h
+++ b/src/sheet-style.h
@@ -41,7 +41,7 @@ void   sheet_style_set_pos            (Sheet  *sheet, int col, int row,
 void    sheet_style_apply_pos          (Sheet  *sheet, int col, int row,
                                         GnmStyle *style);
 
-void    sheet_style_insert_colrow      (GnmExprRelocateInfo const *rinfo);
+void    sheet_style_insdel_colrow      (GnmExprRelocateInfo const *rinfo);
 void    sheet_style_relocate           (GnmExprRelocateInfo const *rinfo);
 unsigned int sheet_style_find_conflicts (Sheet const *sheet, GnmRange const *r,
                                         GnmStyle **style, GnmBorder **borders);
diff --git a/src/sheet.c b/src/sheet.c
index 7572a5b..add1364 100644
--- a/src/sheet.c
+++ b/src/sheet.c
@@ -4814,40 +4814,60 @@ sheet_mark_dirty (Sheet *sheet)
 
 /****************************************************************************/
 
-/*
- * Callback for sheet_foreach_cell_in_range to remove a cell from the sheet
- * hash, unlink from the dependent collection and put it in a temporary list.
- */
-static GnmValue *
-cb_collect_cell (GnmCellIter const *iter, gpointer user)
+static void
+sheet_cells_deps_move (GnmExprRelocateInfo *rinfo)
 {
-       GList ** l = user;
-       GnmCell *cell = iter->cell;
-       gboolean needs_recalc = gnm_cell_needs_recalc (cell);
+       Sheet *sheet = rinfo->origin_sheet;
+       GPtrArray *deps = sheet_cells (sheet, &rinfo->origin);
+       unsigned ui;
+
+       /* Phase 1: collect all cells and remove them from hash.  */
+       for (ui = 0; ui < deps->len; ui++) {
+               GnmCell *cell = g_ptr_array_index (deps, ui);
+               gboolean needs_recalc = gnm_cell_needs_recalc (cell);
+               sheet_cell_remove_from_hash (sheet, cell);
+               if (needs_recalc) /* Do we need this now? */
+                       cell->base.flags |= DEPENDENT_NEEDS_RECALC;
+       }
 
-       sheet_cell_remove_from_hash (iter->pp.sheet, cell);
-       *l = g_list_prepend (*l, cell);
-       if (needs_recalc)
-               cell->base.flags |= DEPENDENT_NEEDS_RECALC;
-       return NULL;
+       /* Phase 2: add all non-cell deps with positions */
+       SHEET_FOREACH_DEPENDENT
+               (sheet, dep, {
+                       GnmCellPos const *pos;
+                       if (!dependent_is_cell (dep) &&
+                           dependent_has_pos (dep) &&
+                           (pos = dependent_pos (dep)) &&
+                           range_contains (&rinfo->origin, pos->col, pos->row)) {
+                               dependent_unlink (dep);
+                               g_ptr_array_add (deps, dep);
+                       }
+               });
+
+       /* Phase 3: move everything and add cells to hash.  */
+       for (ui = 0; ui < deps->len; ui++) {
+               GnmDependent *dep = g_ptr_array_index (deps, ui);
+
+               dependent_move (dep, rinfo->col_offset, rinfo->row_offset);
+
+               if (dependent_is_cell (dep))
+                       sheet_cell_add_to_hash (sheet, GNM_DEP_TO_CELL (dep));
+
+               if (dep->texpr)
+                       dependent_link (dep);
+       }
+
+       g_ptr_array_free (deps, TRUE);
 }
 
-/*
- * 1) collects the cells in the source rows/cols
- * 2) Moves the headers to their new location
- * 3) replaces the cells in their new location
- */
+/* Moves the headers to their new location */
 static void
-colrow_move (Sheet *sheet, gboolean is_cols,
-            int const old_pos, int const new_pos)
+sheet_colrow_move (Sheet *sheet, gboolean is_cols,
+                  int const old_pos, int const new_pos)
 {
        ColRowSegment *segment = COLROW_GET_SEGMENT (is_cols ? &sheet->cols : &sheet->rows, old_pos);
        ColRowInfo *info = segment
                ? segment->info[COLROW_SUB_INDEX (old_pos)]
                : NULL;
-       GList *cells = NULL;
-       GnmCell *cell;
-       GnmRange r;
 
        g_return_if_fail (old_pos >= 0);
        g_return_if_fail (new_pos >= 0);
@@ -4855,56 +4875,9 @@ colrow_move (Sheet *sheet, gboolean is_cols,
        if (info == NULL)
                return;
 
-       /* Collect the cells and unlinks them if necessary */
-       (is_cols ? range_init_cols : range_init_rows) (&r, sheet, old_pos, old_pos);
-       sheet_foreach_cell_in_range
-               (sheet, CELL_ITER_IGNORE_NONEXISTENT,
-                r.start.col, r.start.row,
-                r.end.col, r.end.row,
-                &cb_collect_cell, &cells);
-
-       /* Reverse the list so that we start at the top left
-        * which makes things easier for arrays.
-        */
-       cells = g_list_reverse (cells);
-
        /* Update the position */
        segment->info[COLROW_SUB_INDEX (old_pos)] = NULL;
        sheet_colrow_add (sheet, info, is_cols, new_pos);
-
-       /* Insert the cells back */
-       for (; cells != NULL ; cells = g_list_remove (cells, cell)) {
-               cell = cells->data;
-
-               if (is_cols)
-                       cell->pos.col = new_pos;
-               else
-                       cell->pos.row = new_pos;
-
-               sheet_cell_add_to_hash (sheet, cell);
-               if (gnm_cell_has_expr (cell))
-                       dependent_link (GNM_CELL_TO_DEP (cell));
-       }
-       sheet_mark_dirty (sheet);
-}
-
-static void
-sheet_colrow_insdel_finish (GnmExprRelocateInfo const *rinfo, gboolean is_cols,
-                           int pos, GOUndo **pundo)
-{
-       Sheet *sheet = rinfo->origin_sheet;
-
-       sheet_objects_relocate (rinfo, FALSE, pundo);
-       gnm_sheet_merge_relocate (rinfo);
-
-       /* Notify sheet of pending updates */
-       sheet->priv->recompute_visibility = TRUE;
-       sheet_flag_recompute_spans (sheet);
-       sheet_flag_status_update_range (sheet, &rinfo->origin);
-       if (is_cols)
-               sheet->priv->reposition_objects.col = pos;
-       else
-               sheet->priv->reposition_objects.row = pos;
 }
 
 static void
@@ -4935,55 +4908,6 @@ sheet_colrow_set_collapse (Sheet *sheet, gboolean is_cols, int pos)
 }
 
 static void
-sheet_colrow_insert_finish (GnmExprRelocateInfo const *rinfo, gboolean is_cols,
-                           int pos, int count, GOUndo **pundo)
-{
-       Sheet *sheet = rinfo->origin_sheet;
-
-       sheet_style_insert_colrow (rinfo);
-       sheet_colrow_insdel_finish (rinfo, is_cols, pos, pundo);
-       sheet_colrow_set_collapse (sheet, is_cols, pos);
-       sheet_colrow_set_collapse (sheet, is_cols, pos + count);
-       sheet_colrow_set_collapse (sheet, is_cols, colrow_max (is_cols, sheet));
-       gnm_sheet_filter_insdel_colrow (sheet, is_cols, TRUE, pos, count, pundo);
-
-       /* WARNING WARNING WARNING
-        * This is bad practice and should not really be here.
-        * However, we need to ensure that update is run before
-        * sv_panes_insdel_colrow plays with frozen panes, updating those can
-        * trigger redraws before sheet_update has been called. */
-       sheet_update (sheet);
-
-       SHEET_FOREACH_VIEW (sheet, sv,
-               sv_panes_insdel_colrow (sv, is_cols, TRUE, pos, count););
-}
-
-static void
-sheet_colrow_delete_finish (GnmExprRelocateInfo const *rinfo, gboolean is_cols,
-                           int pos, int count, GOUndo **pundo)
-{
-       Sheet *sheet = rinfo->origin_sheet;
-       int end = colrow_max (is_cols, sheet) - count;
-
-       sheet_style_relocate (rinfo);
-       sheet_colrow_insdel_finish (rinfo, is_cols, pos, pundo);
-       sheet_colrow_set_collapse (sheet, is_cols, pos);
-       sheet_colrow_set_collapse (sheet, is_cols, end);
-       gnm_sheet_filter_insdel_colrow (sheet, is_cols, FALSE, pos, count, pundo);
-
-       /* WARNING WARNING WARNING
-        * This is bad practice and should not really be here.
-        * However, we need to ensure that update is run before
-        * sv_panes_insdel_colrow plays with frozen panes, updating those can
-        * trigger redraws before sheet_update has been called. */
-       sheet_update (sheet);
-
-       SHEET_FOREACH_VIEW (sheet, sv,
-               sv_panes_insdel_colrow (sv, is_cols, FALSE, pos, count););
-}
-
-
-static void
 combine_undo (GOUndo **pundo, GOUndo *u)
 {
        if (pundo)
@@ -4999,7 +4923,7 @@ typedef struct {
        ColRowInsDelFunc func;
        Sheet *sheet;
        gboolean is_cols;
-       int idx;
+       int pos;
        int count;
        ColRowStateList *states;
        int state_start;
@@ -5008,7 +4932,7 @@ typedef struct {
 static void
 cb_undo_insdel (ColRowInsDelData *data)
 {
-       data->func (data->sheet, data->idx, data->count, NULL, NULL);
+       data->func (data->sheet, data->pos, data->count, NULL, NULL);
        colrow_set_states (data->sheet, data->is_cols,
                           data->state_start, data->states);
 }
@@ -5020,215 +4944,215 @@ cb_undo_insdel_free (ColRowInsDelData *data)
        g_free (data);
 }
 
-static void
-add_undo_op (GOUndo **pundo, gboolean is_cols,
-            ColRowInsDelFunc func, Sheet *sheet, int idx, int count,
-            ColRowStateList *states, int state_start)
-{
-       ColRowInsDelData *data;
-       GOUndo *u;
-
-       if (!pundo)
-               return;
-
-       data = g_new (ColRowInsDelData, 1);
-       data->func = func;
-       data->sheet = sheet;
-       data->is_cols = is_cols;
-       data->idx = idx;
-       data->count = count;
-       data->states = states;
-       data->state_start = state_start;
-
-       u = go_undo_unary_new (data, (GOUndoUnaryFunc)cb_undo_insdel,
-                              (GFreeFunc)cb_undo_insdel_free);
-
-       combine_undo (pundo, u);
-}
-
-static void
-schedule_reapply_filters (Sheet *sheet, GOUndo **pundo)
-{
-       GSList *l;
-
-       if (!pundo)
-               return;
-
-       for (l = sheet->filters; l; l = l->next) {
-               GnmFilter *filter = l->data;
-               GOUndo *u = go_undo_unary_new
-                       (gnm_filter_ref (filter),
-                        (GOUndoUnaryFunc)gnm_filter_reapply,
-                        (GFreeFunc)gnm_filter_unref);
-               *pundo = go_undo_combine (*pundo, u);
-       }
-}
-
-/**
- * sheet_insert_cols:
- * @sheet: #Sheet
- * @col: At which position we want to insert
- * @count: The number of columns to be inserted
- * @pundo: undo closure, optionally NULL; caller releases result
- * @cc:
- **/
-gboolean
-sheet_insert_cols (Sheet *sheet, int col, int count,
-                  GOUndo **pundo, GOCmdContext *cc)
-{
-       GnmExprRelocateInfo reloc_info;
-       GnmRange region;
-       int i;
+static gboolean
+sheet_insdel_colrow (Sheet *sheet, int pos, int count,
+                    GOUndo **pundo, GOCmdContext *cc,
+                    gboolean is_cols, gboolean is_insert,
+                    const char *description,
+                    ColRowInsDelFunc opposite)
+{
+       
+       GnmRange kill_zone;    /* The range whose contents will be lost.  */
+       GnmRange move_zone;    /* The range whose contents will be moved.  */
+       GnmRange change_zone;  /* The union of kill_zone and move_zone.  */
+       int i, last_pos, max_used_pos;
+       int kill_start, kill_end, move_start, move_end;
+       int scount = is_insert ? count : -count;
        ColRowStateList *states = NULL;
-       int first = gnm_sheet_get_max_cols (sheet) - count;
+       GnmExprRelocateInfo reloc_info;
+       GSList *l;
 
        g_return_val_if_fail (IS_SHEET (sheet), TRUE);
        g_return_val_if_fail (count > 0, TRUE);
 
+       /*
+        * The main undo for an insert col/row is delete col/row and vice versa.
+        * In addition to that, we collect undo information that the main undo
+        * operation will not restore -- for example the contents of the kill
+        * zone.
+        */
        if (pundo) *pundo = NULL;
 
+       last_pos = colrow_max (is_cols, sheet) - 1;
+       max_used_pos = is_cols ? sheet->cols.max_used : sheet->rows.max_used;
+       if (is_insert) {
+               kill_start = last_pos - (count - 1);
+               kill_end = last_pos;
+               move_start = pos;
+               move_end = kill_start - 1;
+       } else {
+               kill_start = pos;
+               kill_end = pos + (count - 1);
+               move_start = kill_end + 1;
+               move_end = last_pos;
+       }
+       (is_cols ? range_init_cols : range_init_rows)
+               (&kill_zone, sheet, kill_start, kill_end);
+       (is_cols ? range_init_cols : range_init_rows)
+               (&move_zone, sheet, move_start, move_end);
+       change_zone = range_union (&kill_zone, &move_zone);
+
        /* 0. Check displaced/deleted region and ensure arrays aren't divided. */
-       /* We need to check at "col" and at "first".  If they coincide, just
-          use the end.  */
-       range_init_cols (&region, sheet, col,
-                        (col < first
-                         ? first - 1
-                         : gnm_sheet_get_last_col (sheet)));
-       if (sheet_range_splits_array (sheet, &region, NULL,
-                                     cc, _("Insert Columns")))
+       if (sheet_range_splits_array (sheet, &kill_zone, NULL, cc, description))
+               return TRUE;
+       if (move_start <= move_end &&
+           sheet_range_splits_array (sheet, &move_zone, NULL, cc, description))
                return TRUE;
 
-       schedule_reapply_filters (sheet, pundo);
+       /*
+        * At this point we're committed.  Anything that can go wrong should
+        * have been ruled out already.
+        */
+
+       if (0) {
+               g_printerr ("Action = %s at %d count %d\n", description, pos, count);
+               g_printerr ("Kill zone: %s\n", range_as_string (&kill_zone));
+       }
 
-       /* 1. Delete all columns (and their cells) that will fall off the end */
+       /* 1. Delete all columns/rows in the kill zone */
        if (pundo) {
-               int last = first + (count - 1);
-               GnmRange r;
-               range_init_cols (&r, sheet, first, last);
-               combine_undo (pundo, clipboard_copy_range_undo (sheet, &r));
-               states = colrow_get_states (sheet, TRUE, first, last);
+               combine_undo (pundo, clipboard_copy_range_undo (sheet, &kill_zone));
+               states = colrow_get_states (sheet, is_cols, kill_start, kill_end);
        }
-       for (i = sheet->cols.max_used; i >= gnm_sheet_get_max_cols (sheet) - count ; --i)
-               sheet_col_destroy (sheet, i, TRUE);
+       for (i = MIN (max_used_pos, kill_end); i >= kill_start; --i)
+               (is_cols ? sheet_col_destroy : sheet_row_destroy)
+                       (sheet, i, TRUE);
+       /* Brutally discard auto filter objects.  Collect the rest for undo.  */
+       sheet_objects_clear (sheet, &kill_zone, GNM_FILTER_COMBO_TYPE, NULL);
+       sheet_objects_clear (sheet, &kill_zone, G_TYPE_NONE, pundo);
 
-       reloc_info.reloc_type = GNM_EXPR_RELOCATE_COLS;
-       reloc_info.sticky_end = TRUE;
-       range_init_cols (&reloc_info.origin, sheet, col, gnm_sheet_get_last_col (sheet));
+       reloc_info.reloc_type = is_cols ? GNM_EXPR_RELOCATE_COLS : GNM_EXPR_RELOCATE_ROWS;
+       reloc_info.sticky_end = is_insert || kill_end > last_pos;
        reloc_info.origin_sheet = reloc_info.target_sheet = sheet;
-       reloc_info.col_offset = count;
-       reloc_info.row_offset = 0;
        parse_pos_init_sheet (&reloc_info.pos, sheet);
 
-       /* 1.5 Get rid of style dependents, see #741197.  */
-       sheet_style_clear_style_dependents (sheet, &reloc_info.origin);
+       /* 2. Get rid of style dependents, see #741197.  */
+       sheet_style_clear_style_dependents (sheet, &change_zone);
 
-       /* 2. Fix references to and from the cells which are moving */
+       /* 3. Invalidate references to kill zone.  */
+       reloc_info.origin = kill_zone;
+       reloc_info.col_offset = is_cols ? last_pos + 1 : 0;  /* Force invalidation */
+       reloc_info.row_offset = is_cols ? 0 : last_pos + 1;
        combine_undo (pundo, dependents_relocate (&reloc_info));
 
-       /* 3. Move the columns to their new location (from right to left) */
-       for (i = sheet->cols.max_used; i >= col ; --i)
-               colrow_move (sheet, TRUE, i, i + count);
+       /* 3. Fix references to the cells which are moving */
+       reloc_info.origin = move_zone;
+       reloc_info.col_offset = is_cols ? scount : 0;
+       reloc_info.row_offset = is_cols ? 0 : scount;
+       combine_undo (pundo, dependents_relocate (&reloc_info));
 
-       /* 4. Move formatting.  */
-       sheet_colrow_insert_finish (&reloc_info, TRUE, col, count, pundo);
+       /* 4. Move the cells */
+       sheet_cells_deps_move (&reloc_info);
 
-       add_undo_op (pundo, TRUE, sheet_delete_cols,
-                    sheet, col, count,
-                    states, first);
+       /* 5. Move the columns/rows to their new location.  */
+       if (is_insert) {
+               /* From right to left */
+               for (i = max_used_pos; i >= pos ; --i)
+                       sheet_colrow_move (sheet, is_cols, i, i + count);
+       } else {
+               /* From left to right */
+               for (i = pos + count ; i <= max_used_pos; ++i)
+                       sheet_colrow_move (sheet, is_cols, i, i - count);
+       }
+       sheet_colrow_set_collapse (sheet, is_cols, pos);
+       sheet_colrow_set_collapse (sheet, is_cols,
+                                  is_insert ? pos + count : last_pos - (count - 1));
 
-       return FALSE;
-}
+       /* 6. Move formatting.  */
+       sheet_style_insdel_colrow (&reloc_info);
 
-/*
- * sheet_delete_cols
- * @sheet: The sheet
- * @col:     At which position we want to start deleting columns
- * @count:   The number of columns to be deleted
- * @pundo: undo closure, optionally NULL; caller releases result
- * @cc: The command context
- */
-gboolean
-sheet_delete_cols (Sheet *sheet, int col, int count,
-                  GOUndo **pundo, GOCmdContext *cc)
-{
-       GnmExprRelocateInfo reloc_info;
-       int i;
-       ColRowStateList *states = NULL;
-       int max_count;
-       gboolean beyond_end;
+       /* 7. Move objects.  */
+       sheet_objects_relocate (&reloc_info, FALSE, pundo);
 
-       g_return_val_if_fail (IS_SHEET (sheet), TRUE);
-       g_return_val_if_fail (count > 0, TRUE);
-
-       max_count = gnm_sheet_get_max_cols (sheet) - col;
-       beyond_end = (count > max_count);
-       if (beyond_end) {
-               /*
-                * We're trying to delete more than we have (not just to the
-                * very end).  We take that as a signal that ranges should
-                * not be sticky at the end.
-                */
-               count = max_count;
-       }
+       /* 8. Move merges.  */
+       gnm_sheet_merge_relocate (&reloc_info);
 
-       if (pundo) *pundo = NULL;
+       /* 9. Move filters.  */
+       gnm_sheet_filter_insdel_colrow (sheet, is_cols, is_insert, pos, count, pundo);
 
-       reloc_info.reloc_type = GNM_EXPR_RELOCATE_COLS;
-       reloc_info.sticky_end = !beyond_end;
-       range_init_cols (&reloc_info.origin, sheet, col, col + count - 1);
-       reloc_info.origin_sheet = reloc_info.target_sheet = sheet;
-       reloc_info.col_offset = gnm_sheet_get_max_cols (sheet); /* force invalidation */
-       reloc_info.row_offset = 0;
-       parse_pos_init_sheet (&reloc_info.pos, sheet);
+       /* Notify sheet of pending updates */
+       sheet_mark_dirty (sheet);
+       sheet->priv->recompute_visibility = TRUE;
+       sheet_flag_recompute_spans (sheet);
+       sheet_flag_status_update_range (sheet, &change_zone);
+       if (is_cols)
+               sheet->priv->reposition_objects.col = pos;
+       else
+               sheet->priv->reposition_objects.row = pos;
 
-       /* 0. Walk cells in deleted cols and ensure arrays aren't divided. */
-       if (sheet_range_splits_array (sheet, &reloc_info.origin, NULL,
-                                     cc, _("Delete Columns")))
-               return TRUE;
+       /* WARNING WARNING WARNING
+        * This is bad practice and should not really be here.
+        * However, we need to ensure that update is run before
+        * sv_panes_insdel_colrow plays with frozen panes, updating those can
+        * trigger redraws before sheet_update has been called. */
+       sheet_update (sheet);
 
-       schedule_reapply_filters (sheet, pundo);
+       SHEET_FOREACH_VIEW (sheet, sv,
+                           sv_panes_insdel_colrow (sv, is_cols, is_insert, pos, count););
 
-       /* 1. Delete the columns (and their cells) */
+       /* The main undo is the opposite operation.  */
        if (pundo) {
-               int last = col + (count - 1);
-               GnmRange r;
-               range_init_cols (&r, sheet, col, last);
-               combine_undo (pundo, clipboard_copy_range_undo (sheet, &r));
-               states = colrow_get_states (sheet, TRUE, col, last);
-       }
-       for (i = col + count ; --i >= col; )
-               sheet_col_destroy (sheet, i, TRUE);
-
-       /* Brutally discard auto filter objects.  Collect the rest for undo.  */
-       sheet_objects_clear (sheet, &reloc_info.origin, GNM_FILTER_COMBO_TYPE, NULL);
-       sheet_objects_clear (sheet, &reloc_info.origin, G_TYPE_NONE, pundo);
+               ColRowInsDelData *data;
+               GOUndo *u;
 
-       /*
-        * 1.5 sheet_colrow_delete_finish will flag the remains as changing,
-        * but we need to mark the cleared area
-        */
-       sheet_flag_status_update_range (sheet, &reloc_info.origin);
+               data = g_new (ColRowInsDelData, 1);
+               data->func = opposite;
+               data->sheet = sheet;
+               data->is_cols = is_cols;
+               data->pos = pos;
+               data->count = count;
+               data->states = states;
+               data->state_start = kill_start;
 
-       /* 2. Invalidate references to the cells in the deleted columns */
-       combine_undo (pundo, dependents_relocate (&reloc_info));
+               u = go_undo_unary_new (data, (GOUndoUnaryFunc)cb_undo_insdel,
+                                      (GFreeFunc)cb_undo_insdel_free);
 
-       /* 3. Fix references to and from the cells which are moving */
-       range_init_cols (&reloc_info.origin, sheet, col + count, gnm_sheet_get_last_col (sheet));
-       reloc_info.col_offset = -count;
-       reloc_info.row_offset = 0;
-       combine_undo (pundo, dependents_relocate (&reloc_info));
+               combine_undo (pundo, u);
+       }
 
-       /* 4. Move the columns to their new location (from left to right) */
-       for (i = col + count ; i <= sheet->cols.max_used; ++i)
-               colrow_move (sheet, TRUE, i, i - count);
+       /* Reapply all filters.  */
+       for (l = sheet->filters; l; l = l->next) {
+               GnmFilter *filter = l->data;
+               gnm_filter_reapply (filter);
+       }
 
-       sheet_colrow_delete_finish (&reloc_info, TRUE, col, count, pundo);
+       return FALSE;
+}
 
-       add_undo_op (pundo, TRUE, sheet_insert_cols,
-                    sheet, col, count,
-                    states, col);
+/**
+ * sheet_insert_cols:
+ * @sheet: #Sheet
+ * @col: At which position we want to insert
+ * @count: The number of columns to be inserted
+ * @pundo: (out): (transfer full): (allow-none): undo closure
+ * @cc:
+ **/
+gboolean
+sheet_insert_cols (Sheet *sheet, int col, int count,
+                  GOUndo **pundo, GOCmdContext *cc)
+{
+       return sheet_insdel_colrow (sheet, col, count, pundo, cc,
+                                   TRUE, TRUE,
+                                   _("Insert Columns"),
+                                   sheet_delete_cols);
+}
 
-       return FALSE;
+/**
+ * sheet_delete_cols
+ * @sheet: The sheet
+ * @col:     At which position we want to start deleting columns
+ * @count:   The number of columns to be deleted
+ * @pundo: (out): (transfer full): (allow-none): undo closure
+ * @cc: The command context
+ */
+gboolean
+sheet_delete_cols (Sheet *sheet, int col, int count,
+                  GOUndo **pundo, GOCmdContext *cc)
+{
+       return sheet_insdel_colrow (sheet, col, count, pundo, cc,
+                                   TRUE, FALSE,
+                                   _("Delete Columns"),
+                                   sheet_insert_cols);
 }
 
 /**
@@ -5236,166 +5160,53 @@ sheet_delete_cols (Sheet *sheet, int col, int count,
  * @sheet: The sheet
  * @row: At which position we want to insert
  * @count: The number of rows to be inserted
- * @pundo: undo closure, optionally NULL; caller releases result
+ * @pundo: (out): (transfer full): (allow-none): undo closure
  * @cc: The command context
  */
 gboolean
 sheet_insert_rows (Sheet *sheet, int row, int count,
                   GOUndo **pundo, GOCmdContext *cc)
 {
-       GnmExprRelocateInfo reloc_info;
-       GnmRange region;
-       int i;
-       ColRowStateList *states = NULL;
-       int first = gnm_sheet_get_max_rows (sheet) - count;
-
-       g_return_val_if_fail (IS_SHEET (sheet), TRUE);
-       g_return_val_if_fail (count > 0, TRUE);
-
-       if (pundo) *pundo = NULL;
-
-       /* 0. Check displaced/deleted region and ensure arrays aren't divided. */
-       /* We need to check at "row" and at "first".  If they coincide, just
-          use the end.  */
-       range_init_rows (&region, sheet, row,
-                        (row < first
-                         ? first - 1
-                         : gnm_sheet_get_last_row (sheet)));
-       if (sheet_range_splits_array (sheet, &region, NULL,
-                                     cc, _("Insert Rows")))
-               return TRUE;
-
-       schedule_reapply_filters (sheet, pundo);
-
-       /* 1. Delete all rows (and their cells) that will fall off the end */
-       if (pundo) {
-               int last = first + (count - 1);
-               GnmRange r;
-               range_init_rows (&r, sheet, first, last);
-               combine_undo (pundo, clipboard_copy_range_undo (sheet, &r));
-               states = colrow_get_states (sheet, FALSE, first, last);
-       }
-       for (i = sheet->rows.max_used; i >= gnm_sheet_get_max_rows (sheet) - count ; --i)
-               sheet_row_destroy (sheet, i, TRUE);
-
-       reloc_info.reloc_type = GNM_EXPR_RELOCATE_ROWS;
-       reloc_info.sticky_end = TRUE;
-       range_init_rows (&reloc_info.origin, sheet, row, gnm_sheet_get_last_row (sheet));
-       reloc_info.origin_sheet = reloc_info.target_sheet = sheet;
-       reloc_info.col_offset = 0;
-       reloc_info.row_offset = count;
-       parse_pos_init_sheet (&reloc_info.pos, sheet);
-
-       /* 1.5 Get rid of style dependents, see #741197.  */
-       sheet_style_clear_style_dependents (sheet, &reloc_info.origin);
-
-       /* 2. Fix references to and from the cells which are moving */
-       combine_undo (pundo, dependents_relocate (&reloc_info));
-
-       /* 3. Move the rows to their new location (from last to first) */
-       for (i = sheet->rows.max_used; i >= row ; --i)
-               colrow_move (sheet, FALSE, i, i + count);
-
-       /* 4. Move formatting.  */
-       sheet_colrow_insert_finish (&reloc_info, FALSE, row, count, pundo);
-
-       add_undo_op (pundo, FALSE, sheet_delete_rows,
-                    sheet, row, count,
-                    states, first);
-
-       return FALSE;
+       return sheet_insdel_colrow (sheet, row, count, pundo, cc,
+                                   FALSE, TRUE,
+                                   _("Insert Rows"),
+                                   sheet_delete_rows);
 }
 
-/*
+/**
  * sheet_delete_rows
  * @sheet: The sheet
  * @row: At which position we want to start deleting rows
  * @count: The number of rows to be deleted
- * @pundo: undo closure, optionally NULL; caller releases result
+ * @pundo: (out): (transfer full): (allow-none): undo closure
  * @cc: The command context
  */
 gboolean
 sheet_delete_rows (Sheet *sheet, int row, int count,
                   GOUndo **pundo, GOCmdContext *cc)
 {
-       GnmExprRelocateInfo reloc_info;
-       int i;
-       ColRowStateList *states = NULL;
-       int max_count;
-       gboolean beyond_end;
-
-       g_return_val_if_fail (IS_SHEET (sheet), TRUE);
-       g_return_val_if_fail (count > 0, TRUE);
-
-       max_count = gnm_sheet_get_max_rows (sheet) - row;
-       beyond_end = (count > max_count);
-       if (beyond_end) {
-               /*
-                * We're trying to delete more than we have (not just to the
-                * very end).  We take that as a signal that ranges should
-                * not be sticky at the end.
-                */
-               count = max_count;
-       }
-
-       if (pundo) *pundo = NULL;
-
-       reloc_info.reloc_type = GNM_EXPR_RELOCATE_ROWS;
-       reloc_info.sticky_end = !beyond_end;
-       range_init_rows (&reloc_info.origin, sheet, row, row + count - 1);
-       reloc_info.origin_sheet = reloc_info.target_sheet = sheet;
-       reloc_info.col_offset = 0;
-       reloc_info.row_offset = gnm_sheet_get_max_rows (sheet); /* force invalidation */
-       parse_pos_init_sheet (&reloc_info.pos, sheet);
-
-       /* 0. Walk cells in deleted rows and ensure arrays aren't divided. */
-       if (sheet_range_splits_array (sheet, &reloc_info.origin, NULL,
-                                     cc, _("Delete Rows")))
-               return TRUE;
-
-       schedule_reapply_filters (sheet, pundo);
-
-       /* 1. Delete the rows (and their content) */
-       if (pundo) {
-               int last = row + (count - 1);
-               GnmRange r;
-               range_init_rows (&r, sheet, row, last);
-               combine_undo (pundo, clipboard_copy_range_undo (sheet, &r));
-               states = colrow_get_states (sheet, FALSE, row, last);
-       }
-       for (i = row + count ; --i >= row; )
-               sheet_row_destroy (sheet, i, TRUE);
-
-       /* Brutally discard auto filter objects.  Collect the rest for undo.  */
-       sheet_objects_clear (sheet, &reloc_info.origin, GNM_FILTER_COMBO_TYPE, NULL);
-       sheet_objects_clear (sheet, &reloc_info.origin, G_TYPE_NONE, pundo);
-
-       /*
-        * 1.5 sheet_colrow_delete_finish will flag the remains as changing,
-        * but we need to mark the cleared area
-        */
-       sheet_flag_status_update_range (sheet, &reloc_info.origin);
-
-       /* 2. Invalidate references to the cells in the deleted rows */
-       combine_undo (pundo, dependents_relocate (&reloc_info));
-
-       /* 3. Fix references to and from the cells which are moving */
-       range_init_rows (&reloc_info.origin, sheet, row + count, gnm_sheet_get_last_row (sheet));
-       reloc_info.col_offset = 0;
-       reloc_info.row_offset = -count;
-       combine_undo (pundo, dependents_relocate (&reloc_info));
-
-       /* 4. Move the rows to their new location (from first to last) */
-       for (i = row + count ; i <= sheet->rows.max_used; ++i)
-               colrow_move (sheet, FALSE, i, i - count);
-
-       sheet_colrow_delete_finish (&reloc_info, FALSE, row, count, pundo);
+       return sheet_insdel_colrow (sheet, row, count, pundo, cc,
+                                   FALSE, FALSE,
+                                   _("Delete Rows"),
+                                   sheet_insert_rows);
+}
 
-       add_undo_op (pundo, FALSE, sheet_insert_rows,
-                    sheet, row, count,
-                    states, row);
+/*
+ * Callback for sheet_foreach_cell_in_range to remove a cell from the sheet
+ * hash, unlink from the dependent collection and put it in a temporary list.
+ */
+static GnmValue *
+cb_collect_cell (GnmCellIter const *iter, gpointer user)
+{
+       GList ** l = user;
+       GnmCell *cell = iter->cell;
+       gboolean needs_recalc = gnm_cell_needs_recalc (cell);
 
-       return FALSE;
+       sheet_cell_remove_from_hash (iter->pp.sheet, cell);
+       *l = g_list_prepend (*l, cell);
+       if (needs_recalc)
+               cell->base.flags |= DEPENDENT_NEEDS_RECALC;
+       return NULL;
 }
 
 /**


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]