commands.c 211 KB
Newer Older
1
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2

Jody Goldberg's avatar
Jody Goldberg committed
3
/*
4 5
 * commands.c: Handlers to undo & redo commands
 *
6 7
 * Copyright (C) 1999-2008 Jody Goldberg (jody@gnome.org)
 * Copyright (C) 2002-2008 Morten Welinder (terra@gnome.org)
Jody Goldberg's avatar
Jody Goldberg committed
8
 *
9
 * Contributors : Almer S. Tigelaar (almer@gnome.org)
10
 *                Andreas J. Guelzow (aguelzow@taliesin.ca)
11
 *
12 13 14 15
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) version 3.
Jody Goldberg's avatar
Jody Goldberg committed
16
 *
17 18 19 20 21 22 23
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
25
 * USA
Jody Goldberg's avatar
Jody Goldberg committed
26
 */
27
#include <gnumeric-config.h>
28
#include <glib/gi18n-lib.h>
29
#include "gnumeric.h"
Jody Goldberg's avatar
Jody Goldberg committed
30
#include "commands.h"
31
#include "gnm-command-impl.h"
32

33
#include "application.h"
Jody Goldberg's avatar
Jody Goldberg committed
34
#include "sheet.h"
35
#include "sheet-view.h"
Jody Goldberg's avatar
Jody Goldberg committed
36
#include "sheet-style.h"
37
#include "gnm-format.h"
38
#include "format-template.h"
39 40
#include "command-context.h"
#include "workbook-control.h"
Jody Goldberg's avatar
Jody Goldberg committed
41
#include "workbook-view.h"
Jody Goldberg's avatar
Jody Goldberg committed
42
#include "workbook-priv.h" /* For the undo/redo queues and the FOREACH */
43 44
#include "ranges.h"
#include "sort.h"
45
#include "dependent.h"
46
#include "value.h"
Jody Goldberg's avatar
Jody Goldberg committed
47
#include "expr.h"
48
#include "func.h"
49
#include "expr-name.h"
Jody Goldberg's avatar
Jody Goldberg committed
50
#include "cell.h"
51
#include "sheet-merge.h"
52
#include "parse-util.h"
53
#include "print-info.h"
54
#include "clipboard.h"
Jody Goldberg's avatar
Jody Goldberg committed
55
#include "selection.h"
56
#include "colrow.h"
57
#include "style-border.h"
58
#include "auto-correct.h"
59
#include "sheet-autofill.h"
60
#include "mstyle.h"
61
#include "search.h"
62
#include "gutils.h"
63
#include "gui-util.h"
64
#include "sheet-object-cell-comment.h"
65
#include "sheet-object-widget.h"
66
#include "sheet-object.h"
67
#include "sheet-object-component.h"
68
#include "sheet-object-graph.h"
69
#include "sheet-control.h"
70
#include "sheet-control-gui.h"
71
#include "sheet-utils.h"
72
#include "style-color.h"
73
#include "sheet-filter.h"
Morten Welinder's avatar
Morten Welinder committed
74
#include "auto-format.h"
75
#include "tools/dao.h"
76
#include "gnumeric-conf.h"
77
#include "scenarios.h"
78
#include "data-shuffling.h"
79
#include "tools/tabulate.h"
80
#include "wbc-gtk.h"
81
#include "undo.h"
Jody Goldberg's avatar
Jody Goldberg committed
82

83
#include <goffice/goffice.h>
84 85
#include <gsf/gsf-doc-meta-data.h>
#include <string.h>
86

87 88 89
#define UNICODE_ELLIPSIS "\xe2\x80\xa6"


Jody Goldberg's avatar
Jody Goldberg committed
90
/*
91
 * There are several distinct stages to wrapping each command.
92
 *
Jody Goldberg's avatar
Jody Goldberg committed
93 94 95 96 97 98 99 100 101 102
 * 1) Find the appropriate place(s) in the catch all calls to activations
 * of this logical function.  Be careful.  This should only be called by
 * user initiated actions, not internal commands.
 *
 * 2) Copy the boiler plate code into place and implement the descriptor.
 *
 * 3) Implement the guts of the support functions.
 *
 * That way undo redo just become applications of the old or the new styles.
 *
103 104 105 106 107 108 109 110
 * Design thoughts :
 * 1) redo : this should be renamed 'exec' and should be the place that the
 *    the actual command executes.  This avoid duplicating the code for
 *    application and re-application.
 *
 * 2) The command objects are responsible for generating recalc and redraw
 *    events.  None of the internal utility routines should do so.  Those are
 *    expensive events and should only be done once per command to avoid
111 112
 *    duplicating work.  The lower levels can queue redraws if they must, and
 *    flag state changes but the call to workbook_recalc and sheet_update is
113
 *    by GnmCommand.
114
 *
115
 * FIXME: Filter the list of commands when a sheet is deleted.
116
 *
Jody Goldberg's avatar
Jody Goldberg committed
117 118 119
 * TODO : Possibly clear lists on save.
 *
 * TODO : Reqs for selective undo
120 121 122
 *
 * Future thoughts
 * - undoable preference setting ?  XL does not have this.  Do we want it ?
Jody Goldberg's avatar
Jody Goldberg committed
123 124 125
 */
/******************************************************************/

126 127 128 129 130
#define GNM_COMMAND(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_COMMAND_TYPE, GnmCommand))
#define GNM_COMMAND_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST ((k), GNM_COMMAND_TYPE, GnmCommandClass))
#define IS_GNM_COMMAND(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_COMMAND_TYPE))
#define IS_GNM_COMMAND_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNM_COMMAND_TYPE))
#define CMD_CLASS(o)		GNM_COMMAND_CLASS (G_OBJECT_GET_CLASS(cmd))
Jody Goldberg's avatar
Jody Goldberg committed
131

132
GSF_CLASS (GnmCommand, gnm_command, NULL, NULL, G_TYPE_OBJECT)
Jody Goldberg's avatar
Jody Goldberg committed
133

134
void
135
gnm_command_finalize (GObject *obj)
Jody Goldberg's avatar
Jody Goldberg committed
136
{
137
	GnmCommand *cmd = GNM_COMMAND (obj);
138
	GObjectClass *parent;
Jody Goldberg's avatar
Jody Goldberg committed
139 140 141

	/* The const was to avoid accidental changes elsewhere */
	g_free ((gchar *)cmd->cmd_descriptor);
142
	cmd->cmd_descriptor = NULL;
143

144 145 146
	parent = g_type_class_peek (g_type_parent(G_TYPE_FROM_INSTANCE (obj)));
	(*parent->finalize) (obj);
}
Jody Goldberg's avatar
Jody Goldberg committed
147 148 149

/******************************************************************/

150 151
GString *
gnm_cmd_trunc_descriptor (GString *src, gboolean *truncated)
152
{
153
	int max_len = gnm_conf_get_undo_max_descriptor_width ();
154 155 156 157 158 159 160 161 162
	glong len;
	char *pos;

	if (max_len < 5)
		max_len = 5;

	while ((pos = strchr(src->str, '\n')) != NULL ||
	       (pos = strchr(src->str, '\r')) != NULL)
		*pos = ' ';
Morten Welinder's avatar
Morten Welinder committed
163

164
	len = g_utf8_strlen (src->str, -1);
165 166 167

	if (truncated)
		*truncated = (len > max_len);
Morten Welinder's avatar
Morten Welinder committed
168

169 170 171 172 173
	if (len > max_len) {
		gchar* last = g_utf8_offset_to_pointer (src->str,
                                                        max_len - 1);
		g_string_truncate (src, last - src->str);
		g_string_append (src, UNICODE_ELLIPSIS);
174
	}
175
	return src;
176 177 178
}


179
/**
Morten Welinder's avatar
Morten Welinder committed
180
 * checks whether the cells are effectively locked
181 182 183 184
 *
 * static gboolean cmd_cell_range_is_locked_effective
 *
 *
185
 * Do not use this function unless the sheet is part of the
Jody Goldberg's avatar
Jody Goldberg committed
186
 * workbook with the given wbc (otherwise the results may be strange)
187
 */
188
gboolean
Jody Goldberg's avatar
Jody Goldberg committed
189
cmd_cell_range_is_locked_effective (Sheet *sheet, GnmRange *range,
190
				    WorkbookControl *wbc, char const *cmd_name)
191 192
{
	int i, j;
193
	WorkbookView *wbv = wb_control_view (wbc);
194 195

	if (wbv->is_protected || sheet->is_protected)
196 197
		for (i = range->start.row; i <= range->end.row; i++)
			for (j = range->start.col; j <= range->end.col; j++)
198
				if (gnm_style_get_contents_locked (sheet_style_get (sheet, j, i))) {
Jody Goldberg's avatar
Jody Goldberg committed
199
					char *r = global_range_name (sheet, range);
200 201 202
					char *text = g_strdup_printf (wbv->is_protected  ?
						_("%s is locked. Unprotect the workbook to enable editing.") :
						_("%s is locked. Unprotect the sheet to enable editing."),
Jody Goldberg's avatar
Jody Goldberg committed
203
						r);
204 205
					go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
						cmd_name, text);
206
					g_free (text);
Jody Goldberg's avatar
Jody Goldberg committed
207
					g_free (r);
208 209 210 211 212
					return TRUE;
				}
	return FALSE;
}

213
/**
Morten Welinder's avatar
Morten Welinder committed
214
 * checks whether the cells are effectively locked
215 216 217 218
 *
 * static gboolean cmd_dao_is_locked_effective
 *
 *
219 220
 * Do not use this function unless the sheet is part of the
 * workbook with the given wbcg (otherwise the results may be strange)
221 222 223
 *
 */

224
static gboolean
225 226 227
cmd_dao_is_locked_effective (data_analysis_output_t  *dao,
			     WorkbookControl *wbc, char const *cmd_name)
{
Jody Goldberg's avatar
Jody Goldberg committed
228
	GnmRange range;
229 230
	range_init (&range, dao->start_col, dao->start_row,
		    dao->start_col +  dao->cols - 1,  dao->start_row +  dao->rows - 1);
231
	return (dao->type != NewWorkbookOutput &&
232 233 234
		cmd_cell_range_is_locked_effective (dao->sheet, &range, wbc, cmd_name));
}

235 236 237 238 239 240
/**
 * checks whether the selection is effectively locked
 *
 * static gboolean cmd_selection_is_locked_effective
 *
 *
241 242
 * Do not use this function unless the sheet is part of the
 * workbook with the given wbcg (otherwise the results may be strange)
243 244
 *
 */
245
gboolean
246 247
cmd_selection_is_locked_effective (Sheet *sheet, GSList *selection,
				   WorkbookControl *wbc, char const *cmd_name)
248 249
{
	for (; selection; selection = selection->next) {
250
		GnmRange *range = selection->data;
251
		if (cmd_cell_range_is_locked_effective (sheet, range, wbc, cmd_name))
252 253 254 255 256
			return TRUE;
	}
	return FALSE;
}

257 258 259 260 261 262 263
/*
 * A helper routine to select a range and make sure the top-left
 * is visible.
 */
static void
select_range (Sheet *sheet, const GnmRange *r, WorkbookControl *wbc)
{
264 265 266 267 268 269 270 271 272
	SheetView *sv;

	if (sheet->workbook != wb_control_get_workbook (wbc)) {
		/*
		 * We could try to pick a random wbc for the sheet's
		 * workbook.  But not right now.
		 */
		return;
	}
273 274

	wb_control_sheet_focus (wbc, sheet);
275
	sv = sheet_get_view (sheet, wb_control_view (wbc));
276
	sv_selection_reset (sv);
277
	sv_selection_add_range (sv, r);
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	sv_make_cell_visible (sv, r->start.col, r->start.row, FALSE);
}

/*
 * A helper routine to select a list of ranges and make sure the top-left
 * corner of the last is visible.
 */
static void
select_selection (Sheet *sheet, GSList *selection, WorkbookControl *wbc)
{
	SheetView *sv = sheet_get_view (sheet, wb_control_view (wbc));
	const GnmRange *r0 = NULL;
	GSList *l;

	g_return_if_fail (selection != NULL);

	wb_control_sheet_focus (wbc, sheet);
	sv_selection_reset (sv);
	for (l = selection; l; l = l->next) {
297 298
		GnmRange const *r = l->data;
		sv_selection_add_range (sv, r);
299 300 301 302 303
		r0 = r;
	}
	sv_make_cell_visible (sv, r0->start.col, r0->start.row, FALSE);
}

Jody Goldberg's avatar
Jody Goldberg committed
304 305 306 307 308 309 310 311
/**
 * get_menu_label : Utility routine to get the descriptor associated
 *     with a list of commands.
 *
 * @cmd_list : The command list to check.
 *
 * Returns : A static reference to a descriptor.  DO NOT free this.
 */
312
static char const *
Jody Goldberg's avatar
Jody Goldberg committed
313 314 315
get_menu_label (GSList *cmd_list)
{
	if (cmd_list != NULL) {
316
		GnmCommand *cmd = GNM_COMMAND (cmd_list->data);
Jody Goldberg's avatar
Jody Goldberg committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330
		return cmd->cmd_descriptor;
	}

	return NULL;
}

/**
 * undo_redo_menu_labels : Another utility to set the menus correctly.
 *
 * workbook : The book whose undo/redo queues we are modifying
 */
static void
undo_redo_menu_labels (Workbook *wb)
{
331 332 333 334 335 336
	char const *undo_label = get_menu_label (wb->undo_commands);
	char const *redo_label = get_menu_label (wb->redo_commands);

	WORKBOOK_FOREACH_CONTROL (wb, view, control,
		wb_control_undo_redo_labels (control, undo_label, redo_label);
	);
Jody Goldberg's avatar
Jody Goldberg committed
337 338
}

Jody Goldberg's avatar
Jody Goldberg committed
339
static void
340
update_after_action (Sheet *sheet, WorkbookControl *wbc)
Jody Goldberg's avatar
Jody Goldberg committed
341 342 343 344
{
	if (sheet != NULL) {
		g_return_if_fail (IS_SHEET (sheet));

345
		sheet_mark_dirty (sheet);
346
		if (workbook_get_recalcmode (sheet->workbook))
347
			workbook_recalc (sheet->workbook);
Jody Goldberg's avatar
Jody Goldberg committed
348
		sheet_update (sheet);
349

350
		if (sheet->workbook == wb_control_get_workbook (wbc))
351 352
			WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
				  wb_control_sheet_focus (control, sheet););
353 354 355 356 357
	} else if (wbc != NULL) {
		Sheet *sheet = wb_control_cur_sheet (wbc);
		if (sheet)
			sheet_update (sheet);
	}
Jody Goldberg's avatar
Jody Goldberg committed
358 359
}

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
360

361
/**
Jody Goldberg's avatar
Jody Goldberg committed
362
 * command_undo : Undo the last command executed.
363 364 365
 * @wbc : The workbook control which issued the request.
 *        Any user level errors generated by undoing will be reported
 *        here.
Jody Goldberg's avatar
Jody Goldberg committed
366 367
 *
 * @wb : The workbook whose commands to undo.
368
 **/
Jody Goldberg's avatar
Jody Goldberg committed
369
void
370
command_undo (WorkbookControl *wbc)
Jody Goldberg's avatar
Jody Goldberg committed
371
{
372 373
	GnmCommand *cmd;
	GnmCommandClass *klass;
374
	Workbook *wb = wb_control_get_workbook (wbc);
Jody Goldberg's avatar
Jody Goldberg committed
375 376 377 378

	g_return_if_fail (wb != NULL);
	g_return_if_fail (wb->undo_commands != NULL);

379
	cmd = GNM_COMMAND (wb->undo_commands->data);
Jody Goldberg's avatar
Jody Goldberg committed
380 381
	g_return_if_fail (cmd != NULL);

382
	klass = CMD_CLASS (cmd);
383 384
	g_return_if_fail (klass != NULL);

385 386
	g_object_ref (cmd);

387
	/* TRUE indicates a failure to undo.  Leave the command where it is */
388 389
	if (!klass->undo_cmd (cmd, wbc)) {
		gboolean undo_cleared;
390

391
		update_after_action (cmd->sheet, wbc);
392

393
		if (!cmd->workbook_modified_before_do)
394
			go_doc_set_dirty (GO_DOC (wb), FALSE);
395

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
		/*
		 * A few commands clear the undo queue.  For those, we do not
		 * want to stuff the cmd object on the redo queue.
		 */
		undo_cleared = (wb->undo_commands == NULL);

		if (!undo_cleared) {
			wb->undo_commands = g_slist_remove (wb->undo_commands, cmd);
			wb->redo_commands = g_slist_prepend (wb->redo_commands, cmd);

			WORKBOOK_FOREACH_CONTROL (wb, view, control, {
				wb_control_undo_redo_pop (control, TRUE);
				wb_control_undo_redo_push (control, FALSE, cmd->cmd_descriptor, cmd);
			});
			undo_redo_menu_labels (wb);
			/* TODO : Should we mark the workbook as clean or pristine too */
		}
	}

	g_object_unref (cmd);
Jody Goldberg's avatar
Jody Goldberg committed
416 417
}

418
/**
Jody Goldberg's avatar
Jody Goldberg committed
419
 * command_redo : Redo the last command that was undone.
420 421 422
 * @wbc : The workbook control which issued the request.
 *        Any user level errors generated by redoing will be reported
 *        here.
423
 **/
Jody Goldberg's avatar
Jody Goldberg committed
424
void
425
command_redo (WorkbookControl *wbc)
Jody Goldberg's avatar
Jody Goldberg committed
426
{
427 428
	GnmCommand *cmd;
	GnmCommandClass *klass;
429
	Workbook *wb = wb_control_get_workbook (wbc);
Jody Goldberg's avatar
Jody Goldberg committed
430 431 432 433

	g_return_if_fail (wb);
	g_return_if_fail (wb->redo_commands);

434
	cmd = GNM_COMMAND (wb->redo_commands->data);
Jody Goldberg's avatar
Jody Goldberg committed
435 436
	g_return_if_fail (cmd != NULL);

437
	klass = CMD_CLASS (cmd);
438 439
	g_return_if_fail (klass != NULL);

440 441
	g_object_ref (cmd);

442
	cmd->workbook_modified_before_do =
443
		go_doc_is_dirty (wb_control_get_doc (wbc));
444

445
	/* TRUE indicates a failure to redo.  Leave the command where it is */
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
	if (!klass->redo_cmd (cmd, wbc)) {
		gboolean redo_cleared;

		update_after_action (cmd->sheet, wbc);

		/*
		 * A few commands clear the undo queue.  For those, we do not
		 * want to stuff the cmd object on the redo queue.
		 */
		redo_cleared = (wb->redo_commands == NULL);

		if (!redo_cleared) {
			wb->redo_commands = g_slist_remove (wb->redo_commands, cmd);
			wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);

			WORKBOOK_FOREACH_CONTROL (wb, view, control, {
				wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
				wb_control_undo_redo_pop (control, FALSE);
			});
			undo_redo_menu_labels (wb);
		}
	}

	g_object_unref (cmd);
Jody Goldberg's avatar
Jody Goldberg committed
470 471
}

472 473 474 475 476 477 478 479 480 481 482 483
/**
 * command_repeat : Repeat the last command (if possible)
 *
 * @wbc : The workbook control which issued the request.
 *        Any user level errors generated by redoing will be reported
 *        here.
 **/
void
command_repeat (WorkbookControl *wbc)
{
	GnmCommand *cmd;
	GnmCommandClass *klass;
484
	Workbook *wb = wb_control_get_workbook (wbc);
485 486 487 488 489 490 491 492 493 494 495 496 497 498

	g_return_if_fail (wb);
	g_return_if_fail (wb->undo_commands);

	cmd = GNM_COMMAND (wb->undo_commands->data);
	g_return_if_fail (cmd != NULL);

	klass = CMD_CLASS (cmd);
	g_return_if_fail (klass != NULL);

	if (klass->repeat_cmd != NULL)
		(*klass->repeat_cmd) (cmd, wbc);
}

Jody Goldberg's avatar
Jody Goldberg committed
499 500 501
/**
 * command_setup_combos :
 * @wbc :
Morten Welinder's avatar
Morten Welinder committed
502
 *
Jody Goldberg's avatar
Jody Goldberg committed
503 504 505 506 507 508
 * Initialize the combos to correspond to the current undo/redo state.
 */
void
command_setup_combos (WorkbookControl *wbc)
{
	char const *undo_label = NULL, *redo_label = NULL;
509
	GSList *ptr, *tmp;
510
	Workbook *wb = wb_control_get_workbook (wbc);
Jody Goldberg's avatar
Jody Goldberg committed
511 512 513

	g_return_if_fail (wb);

514
	wb_control_undo_redo_truncate (wbc, 0, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
515 516 517
	tmp = g_slist_reverse (wb->undo_commands);
	for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
		undo_label = get_menu_label (ptr);
Jody Goldberg's avatar
Jody Goldberg committed
518
		wb_control_undo_redo_push (wbc, TRUE, undo_label, ptr->data);
Jody Goldberg's avatar
Jody Goldberg committed
519
	}
520
	if (g_slist_reverse (tmp)) {}	/* ignore, list is in undo_commands */
Jody Goldberg's avatar
Jody Goldberg committed
521

522
	wb_control_undo_redo_truncate (wbc, 0, FALSE);
Jody Goldberg's avatar
Jody Goldberg committed
523 524 525
	tmp = g_slist_reverse (wb->redo_commands);
	for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
		redo_label = get_menu_label (ptr);
Jody Goldberg's avatar
Jody Goldberg committed
526
		wb_control_undo_redo_push (wbc, FALSE, redo_label, ptr->data);
Jody Goldberg's avatar
Jody Goldberg committed
527
	}
528
	if (g_slist_reverse (tmp)) {}	/* ignore, list is in redo_commands */
Jody Goldberg's avatar
Jody Goldberg committed
529 530 531 532 533

	/* update the menus too */
	wb_control_undo_redo_labels (wbc, undo_label, redo_label);
}

Jody Goldberg's avatar
Jody Goldberg committed
534 535 536 537 538
/*
 * command_list_release : utility routine to free the resources associated
 *    with a list of commands.
 *
 * @cmd_list : The set of commands to free.
539 540
 *
 * NOTE : remember to NULL the list when you are done.
Jody Goldberg's avatar
Jody Goldberg committed
541 542 543 544 545
 */
void
command_list_release (GSList *cmd_list)
{
	while (cmd_list != NULL) {
Jody Goldberg's avatar
Jody Goldberg committed
546
		GObject *cmd = G_OBJECT (cmd_list->data);
547 548 549

		g_return_if_fail (cmd != NULL);

Jody Goldberg's avatar
Jody Goldberg committed
550
		g_object_unref (cmd);
Jody Goldberg's avatar
Jody Goldberg committed
551 552 553 554
		cmd_list = g_slist_remove (cmd_list, cmd_list->data);
	}
}

555 556 557 558 559 560 561 562 563
/*
 * Each undo item has a certain size.  The size of typing a value into
 * a cell is the unit size.  A large autoformat could have a size of
 * hundreds or even thousands.
 *
 * We wish to have the same undo behaviour across platforms, so please
 * don't use sizeof in computing the undo size.
 */

Morten Welinder's avatar
Morten Welinder committed
564 565
#undef DEBUG_TRUNCATE_UNDO

566 567 568 569 570 571 572
/*
 * Truncate the undo list if it is too big.
 *
 * Returns -1 if no truncation was done, or else the number of elements
 * left.
 */
static int
573
truncate_undo_info (Workbook *wb)
574
{
575
	int size_left;
576
	int max_num;
577 578
	int ok_count;
	GSList *l, *prev;
579

580 581
	size_left = gnm_conf_get_undo_size ();
	max_num   = gnm_conf_get_undo_maxnum ();
582

Morten Welinder's avatar
Morten Welinder committed
583
#ifdef DEBUG_TRUNCATE_UNDO
584
	g_printerr ("Undo sizes:");
Morten Welinder's avatar
Morten Welinder committed
585 586
#endif

587 588 589 590
	for (l = wb->undo_commands, prev = NULL, ok_count = 0;
	     l;
	     prev = l, l = l->next, ok_count++) {
		int min_leave;
591
		GnmCommand *cmd = GNM_COMMAND (l->data);
592 593 594 595 596 597 598 599 600 601 602
		int size = cmd->size;

		if (size < 1) {
				/*
				 * We could g_assert, but that would cause data loss.
				 * Instead, just continue.
				 */
			g_warning ("Faulty undo_size_func, please report.");
			size = 1;
		}

Morten Welinder's avatar
Morten Welinder committed
603
#ifdef DEBUG_TRUNCATE_UNDO
604
			g_printerr (" %d", size);
Morten Welinder's avatar
Morten Welinder committed
605 606
#endif

607
		/* Keep at least one undo item.  */
608
		if (ok_count >= max_num || (size > size_left && ok_count >= 1)) {
609 610
			/* Current item is too big; truncate list here.  */
			command_list_release (l);
611 612 613 614
			if (prev)
				prev->next = NULL;
			else
				wb->undo_commands = NULL;
Morten Welinder's avatar
Morten Welinder committed
615
#ifdef DEBUG_TRUNCATE_UNDO
616
			g_printerr ("[trunc]\n");
Morten Welinder's avatar
Morten Welinder committed
617
#endif
618 619 620 621 622 623 624 625 626 627 628
			return ok_count;
		}

		/*
		 * In order to allow a series of useful small items behind
		 * a big item, leave at least 10% of current item's size.
		 */
		min_leave = size / 10;
		size_left = MAX (size_left - size, min_leave);
	}

Morten Welinder's avatar
Morten Welinder committed
629
#ifdef DEBUG_TRUNCATE_UNDO
630
	g_printerr ("\n");
Morten Welinder's avatar
Morten Welinder committed
631
#endif
632 633 634 635
	return -1;
}


636 637 638 639 640 641 642 643
/**
 * command_register_undo : An internal utility to tack a new command
 *    onto the undo list.
 *
 * @wbc : The workbook control that issued the command.
 * @cmd : The new command to add.
 */
static void
Jody Goldberg's avatar
Jody Goldberg committed
644
command_register_undo (WorkbookControl *wbc, GObject *obj)
645 646
{
	Workbook *wb;
647
	GnmCommand *cmd;
648 649 650
	int undo_trunc;

	g_return_if_fail (wbc != NULL);
651
	wb = wb_control_get_workbook (wbc);
652

653
	cmd = GNM_COMMAND (obj);
654 655 656 657 658
	g_return_if_fail (cmd != NULL);

	command_list_release (wb->redo_commands);
	wb->redo_commands = NULL;

659
	g_object_ref (obj); /* keep a ref in case it gets truncated away */
660 661 662
	wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
	undo_trunc = truncate_undo_info (wb);

663
	WORKBOOK_FOREACH_CONTROL (wb, view, control, {
Jody Goldberg's avatar
Jody Goldberg committed
664
		wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
665
		if (undo_trunc >= 0)
666 667
			wb_control_undo_redo_truncate (control, undo_trunc, TRUE);
		wb_control_undo_redo_truncate (control, 0, FALSE);
668 669
	});
	undo_redo_menu_labels (wb);
670
	g_object_unref (obj);
671 672 673
}


Jody Goldberg's avatar
Jody Goldberg committed
674
/**
675
 * gnm_command_push_undo : An internal utility to tack a new command
Jody Goldberg's avatar
Jody Goldberg committed
676 677
 *    onto the undo list.
 *
678
 * @wbc : The workbook control that issued the command.
Jody Goldberg's avatar
Jody Goldberg committed
679
 * @cmd : The new command to add.
680
 *
681
 * returns : TRUE if there was an error.
Jody Goldberg's avatar
Jody Goldberg committed
682
 */
683 684
gboolean
gnm_command_push_undo (WorkbookControl *wbc, GObject *obj)
Jody Goldberg's avatar
Jody Goldberg committed
685
{
686
	gboolean trouble;
687 688
	GnmCommand *cmd;
	GnmCommandClass *klass;
689

690
	g_return_val_if_fail (wbc != NULL, TRUE);
691

692
	cmd = GNM_COMMAND (obj);
693
	cmd->workbook_modified_before_do =
694
		go_doc_is_dirty (wb_control_get_doc (wbc));
695

696 697
	g_return_val_if_fail (cmd != NULL, TRUE);

698
	klass = CMD_CLASS (cmd);
699 700 701
	g_return_val_if_fail (klass != NULL, TRUE);

	/* TRUE indicates a failure to do the command */
702
	trouble = klass->redo_cmd (cmd, wbc);
703
	update_after_action (cmd->sheet, wbc);
Jody Goldberg's avatar
Jody Goldberg committed
704

705 706 707
	if (!trouble)
		command_register_undo (wbc, obj);
	else
Jody Goldberg's avatar
Jody Goldberg committed
708
		g_object_unref (obj);
709 710

	return trouble;
Jody Goldberg's avatar
Jody Goldberg committed
711 712
}

713 714 715 716 717 718 719 720 721 722 723 724 725
/*
 * command_undo_sheet_delete deletes the sheet without deleting the current cmd.
 * returns true if is indeed deleted the sheet.
 * Note: only call this for a sheet of your current workbook from the undo procedure
 */

static gboolean
command_undo_sheet_delete (Sheet* sheet)
{
	Workbook *wb = sheet->workbook;

        g_return_val_if_fail (IS_SHEET (sheet), FALSE);

726 727 728 729
	if (wb->redo_commands != NULL) {
		command_list_release (wb->redo_commands);
		wb->redo_commands = NULL;
		WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
730
			wb_control_undo_redo_truncate (ctl, 0, FALSE););
731 732 733
		undo_redo_menu_labels (wb);
	}

734
	workbook_sheet_delete (sheet);
735 736 737 738

	return (TRUE);
}

739 740
/******************************************************************/

741 742 743
static GnmValue *
cmd_set_text_full_check_texpr (GnmCellIter const *iter, GnmExprTop const  *texpr)
{
Morten Welinder's avatar
Morten Welinder committed
744
	if (iter->cell == NULL ||
745 746 747 748
	    !gnm_expr_top_equal (iter->cell->base.texpr, texpr))
		return VALUE_TERMINATE;
	return NULL;
}
749

750 751 752 753 754
static GnmValue *
cmd_set_text_full_check_text (GnmCellIter const *iter, char *text)
{
	char *old_text;
	gboolean same;
755
	gboolean quoted = FALSE;
756 757 758 759 760 761 762

	if (gnm_cell_is_blank (iter->cell))
		return ((text == NULL || text[0] == '\0') ? NULL : VALUE_TERMINATE);

	if (text == NULL || text[0] == '\0')
		return VALUE_TERMINATE;

763 764
	old_text = gnm_cell_get_text_for_editing (iter->cell, iter->pp.sheet, NULL, &quoted);
	same = g_strcmp0 (old_text, text) == 0;
765

766
	if (!same && !quoted && iter->cell->value && VALUE_IS_STRING (iter->cell->value)
767
	    && text[0] == '\'')
768
		same = g_strcmp0 (old_text, text + 1) == 0;
Morten Welinder's avatar
Morten Welinder committed
769

770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
	g_free (old_text);

	return (same ? NULL : VALUE_TERMINATE);
}

static GnmValue *
cmd_set_text_full_check_markup (GnmCellIter const *iter, PangoAttrList *markup)
{
	PangoAttrList const *old_markup = NULL;
	gboolean same_markup;

	g_return_val_if_fail (iter->cell != NULL, NULL);

	if (iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
		const GOFormat *fmt = VALUE_FMT (iter->cell->value);
		if (fmt && go_format_is_markup (fmt)) {
			old_markup = go_format_get_markup (fmt);
			if (go_pango_attr_list_is_empty (old_markup))
				old_markup = NULL;
		}
	}

	same_markup = gnm_pango_attr_list_equal (old_markup, markup);

	return same_markup ? NULL : VALUE_TERMINATE;
}

797
/*
798
 * cmd_set_text_full
799 800 801 802 803 804
 *
 * the caller is expected to have ensured:
 *
 * 1) that no array is being split
 * 2) that the range is not locked.
 *
805 806 807
 * Note:
 * We will free the selection but nothing else.
 *
808 809
 */

810 811
static gboolean
cmd_set_text_full (WorkbookControl *wbc, GSList *selection, GnmEvalPos *ep,
Morten Welinder's avatar
Morten Welinder committed
812
		   char const *new_text, PangoAttrList *markup,
813
		   gboolean autocorrect)
Jody Goldberg's avatar
Jody Goldberg committed
814
{
815
	GSList	*l;
816 817 818 819
	char const *expr_txt;
	GnmExprTop const  *texpr = NULL;
	GOUndo *undo = NULL;
	GOUndo *redo = NULL;
820
	gboolean result, autofit_col = FALSE, same_text_and_not_same_markup = FALSE;
821
	char *text = NULL;
822
	char *name;
823 824 825
	Sheet *sheet = ep->sheet;
	GnmParsePos pp;
	ColRowIndexList *cri_col_list = NULL, *cri_row_list = NULL;
826 827
	GOFormat const *format = gnm_style_get_format
		(sheet_style_get (sheet, ep->eval.col, ep->eval.row));
828

829
	g_return_val_if_fail (selection != NULL , TRUE);
Morten Welinder's avatar
Morten Welinder committed
830

831
	parse_pos_init_evalpos (&pp, ep);
832
	name = undo_range_list_name (sheet, selection);
833 834 835 836 837 838 839 840

	if ((format == NULL) || !go_format_is_text (format)) {
		expr_txt = gnm_expr_char_start_p (new_text);
		if (expr_txt != NULL)
			texpr = gnm_expr_parse_str
				(expr_txt, &pp, GNM_EXPR_PARSE_DEFAULT,
				 sheet_get_conventions (sheet), NULL);
	}
Jody Goldberg's avatar
Jody Goldberg committed
841

842
	if (texpr != NULL) {
843
		GOFormat const *sf;
844
		GnmStyle *new_style = NULL;
845 846 847 848 849 850
		gboolean same_texpr = TRUE;

		/* We should check whether we are in fact changing anything: */
		for (l = selection; l != NULL && same_texpr; l = l->next) {
			GnmRange *r = l->data;
			GnmValue *val =
Morten Welinder's avatar
Morten Welinder committed
851
				sheet_foreach_cell_in_range
852 853 854
				(sheet, CELL_ITER_ALL,
				 r->start.col, r->start.row,
				 r->end.col, r->end.row,
Morten Welinder's avatar
Morten Welinder committed
855
				 (CellIterFunc) cmd_set_text_full_check_texpr,
856
				 (gpointer) texpr);
Morten Welinder's avatar
Morten Welinder committed
857

858 859 860 861 862 863 864 865
			same_texpr = (val != VALUE_TERMINATE);
			if (val != NULL && same_texpr)
				value_release (val);
		}

		if (same_texpr) {
			gnm_expr_top_unref (texpr);
			g_free (name);
Morten Welinder's avatar
Morten Welinder committed
866
			range_fragment_free (selection);
867 868
			return TRUE;
		}
Jody Goldberg's avatar
Jody Goldberg committed
869

870
		text = g_strdup_printf (_("Inserting expression in %s"), name);
Jody Goldberg's avatar
Jody Goldberg committed
871

872
		if (go_format_is_general (format)) {
873 874 875 876 877 878
			sf = auto_style_format_suggest (texpr, ep);
			if (sf != NULL) {
				new_style = gnm_style_new ();
				gnm_style_set_format (new_style, sf);
				go_format_unref (sf);
			}
879
		}
Jody Goldberg's avatar
Jody Goldberg committed
880

881 882
		for (l = selection; l != NULL; l = l->next) {
			GnmSheetRange *sr;
Morten Welinder's avatar
Morten Welinder committed
883
			undo = go_undo_combine
884 885
				(undo, clipboard_copy_range_undo (sheet, l->data));
			sr = gnm_sheet_range_new (sheet, l->data);
Morten Welinder's avatar
Morten Welinder committed
886
			redo = go_undo_combine
887 888 889
				(redo, sheet_range_set_expr_undo (sr, texpr));
			if (new_style) {
				sr = gnm_sheet_range_new (sheet, l->data);
Morten Welinder's avatar
Morten Welinder committed
890
				redo = go_undo_combine
891 892 893 894 895 896
					(redo, sheet_apply_style_undo (sr, new_style));
			}
		}
		if (new_style)
			gnm_style_unref (new_style);
		gnm_expr_top_unref (texpr);
897
		autofit_col = TRUE;
898 899
	} else {
		GString *text_str;
900
		PangoAttrList *adj_markup = NULL;
901
		char *corrected;
902 903
		gboolean same_text = TRUE;
		gboolean same_markup = TRUE;
Morten Welinder's avatar
Morten Welinder committed
904

905 906 907 908 909 910
		if (new_text == NULL)
			corrected = NULL;
		else if (autocorrect)
			corrected = autocorrect_tool (new_text);
		else
			corrected = g_strdup (new_text);
911 912 913 914 915

		if (corrected && (corrected[0] == '\'') && corrected[1] == '\0') {
			g_free (corrected);
			corrected = g_strdup ("");
		}
916

917 918 919 920 921
		/* We should check whether we are in fact changing anything: */
		/* We'll handle */
		for (l = selection; l != NULL && same_text; l = l->next) {
			GnmRange *r = l->data;
			GnmValue *val =
Morten Welinder's avatar
Morten Welinder committed
922
				sheet_foreach_cell_in_range
923 924 925
				(sheet, CELL_ITER_ALL,
				 r->start.col, r->start.row,
				 r->end.col, r->end.row,
Morten Welinder's avatar
Morten Welinder committed
926
				 (CellIterFunc) cmd_set_text_full_check_text,
927
				 (gpointer) corrected);
Morten Welinder's avatar
Morten Welinder committed
928

929 930 931 932
			same_text = (val != VALUE_TERMINATE);
			if (val != NULL && same_text)
				value_release (val);
		}
933

934 935
		if (go_pango_attr_list_is_empty (markup))
			markup = NULL;
936
		if (markup && corrected && corrected[0] == '\'') {
937 938 939
			markup = adj_markup = pango_attr_list_copy (markup);
			go_pango_attr_list_erase (adj_markup, 0, 1);
		}
Morten Welinder's avatar
Morten Welinder committed
940

941 942 943 944
		if (same_text) {
			for (l = selection; l != NULL && same_text; l = l->next) {
				GnmRange *r = l->data;
				GnmValue *val =
Morten Welinder's avatar
Morten Welinder committed
945
					sheet_foreach_cell_in_range
946 947 948
					(sheet, CELL_ITER_IGNORE_BLANK,
					 r->start.col, r->start.row,
					 r->end.col, r->end.row,
Morten Welinder's avatar
Morten Welinder committed
949
					 (CellIterFunc) cmd_set_text_full_check_markup,
950
					 (gpointer) markup);
Morten Welinder's avatar
Morten Welinder committed
951

952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
				same_markup = (val != VALUE_TERMINATE);
				if (val != NULL && same_markup)
					value_release (val);
			}

			if (same_markup) {
				g_free (corrected);
				g_free (name);
				range_fragment_free (selection);
				if (adj_markup)
					pango_attr_list_unref (adj_markup);
				return TRUE;
			}

			text = g_strdup_printf (_("Editing style of %s"), name);
		} else {
			text_str = gnm_cmd_trunc_descriptor (g_string_new (corrected), NULL);
			text = g_strdup_printf (_("Typing \"%s\" in %s"), text_str->str, name);
			g_string_free (text_str, TRUE);
		}
972

973 974
		for (l = selection; l != NULL; l = l->next) {
			GnmSheetRange *sr;
Morten Welinder's avatar
Morten Welinder committed
975
			undo = go_undo_combine
976
				(undo, clipboard_copy_range_undo (sheet, l->data));
977 978
			if (corrected) {
				sr = gnm_sheet_range_new (sheet, l->data);
Morten Welinder's avatar
Morten Welinder committed
979 980
				redo = go_undo_combine
					(redo, sheet_range_set_text_undo
981 982
					 (sr, corrected));
			}
983 984 985
			if (markup) {
				sr = gnm_sheet_range_new (sheet, l->data);
				/* Note: order of combination matters!! */
Morten Welinder's avatar
Morten Welinder committed
986
				redo = go_undo_combine
987 988
					(sheet_range_set_markup_undo (sr, markup), redo);
			}
989
		}
990

991 992
		if (adj_markup)
			pango_attr_list_unref (adj_markup);
993
		g_free (corrected);
994 995

		same_text_and_not_same_markup = (same_text && !same_markup);
996
	}
997
	g_free (name);
Morten Welinder's avatar
Morten Welinder committed
998

999 1000
	/* We are combining this since we don't want to apply and undo twice.*/
	if (same_text_and_not_same_markup || !autofit_col) {
Morten Welinder's avatar
Morten Welinder committed
1001
		GnmCell *cell = sheet_cell_fetch
1002
			(sheet, ep->eval.col, ep->eval.row);
1003 1004
		gboolean nvis;

1005
		go_undo_undo (redo);
1006 1007 1008 1009 1010 1011 1012
		nvis = !VALUE_IS_STRING (cell->value);
		if (!autofit_col)
			autofit_col = nvis;
		if (same_text_and_not_same_markup)
			/* We only have to do something if at least one cell           */
			/* now contains a string, but they contain all the same thing. */
			same_text_and_not_same_markup = nvis;
1013 1014
		go_undo_undo (undo);
	}
1015 1016 1017 1018 1019 1020 1021 1022
	if (same_text_and_not_same_markup) {
		/*We had the same text and differnt markup but we are not entering strings. */
		g_object_unref (G_OBJECT (undo));
		g_object_unref (G_OBJECT (redo));
		g_free (text);
		range_fragment_free (selection);
		return TRUE;
	}
1023 1024
	for (l = selection; l != NULL; l = l->next) {
		GnmRange *r = l->data;
1025 1026 1027
		GnmRange *new_r;

		new_r = g_new (GnmRange, 1);
Morten Welinder's avatar
Morten Welinder committed
1028 1029 1030 1031
		*new_r = *r;
		redo  = go_undo_combine
			(go_undo_binary_new
			 (sheet, new_r,
1032
			  (GOUndoBinaryFunc) colrow_autofit_row,
1033 1034
			  NULL, g_free),
			 redo);
Morten Welinder's avatar
Morten Welinder committed
1035
		cri_row_list = colrow_get_index_list
1036 1037 1038 1039
			(r->start.row, r->end.row, cri_row_list);

		if (autofit_col) {
			new_r = g_new (GnmRange, 1);
Morten Welinder's avatar
Morten Welinder committed
1040 1041 1042 1043
			*new_r = *r;
			redo  = go_undo_combine
				(go_undo_binary_new
				 (sheet, new_r,
1044
				  (GOUndoBinaryFunc) colrow_autofit_col,
1045 1046
				  NULL, g_free),
				 redo);
Morten Welinder's avatar
Morten Welinder committed
1047
			cri_col_list = colrow_get_index_list
1048 1049
				(r->start.col, r->end.col, cri_col_list);
		}
1050 1051
	}
	undo = go_undo_combine (undo,
Morten Welinder's avatar
Morten Welinder committed
1052 1053 1054
				gnm_undo_colrow_restore_state_group_new
				(sheet, TRUE,
				 cri_col_list,
1055
				 colrow_get_sizes (sheet, TRUE,
1056
						   cri_col_list, -1)));
1057
	undo = go_undo_combine (undo,
Morten Welinder's avatar
Morten Welinder committed
1058 1059 1060
				gnm_undo_colrow_restore_state_group_new
				(sheet, FALSE,
				 cri_row_list,
1061
				 colrow_get_sizes (sheet, FALSE,
1062
						   cri_row_list, -1)));
Morten Welinder's avatar
Morten Welinder committed
1063

1064

1065 1066 1067
	result = cmd_generic (wbc, text, undo, redo);
	g_free (text);
	range_fragment_free (selection);
Morten Welinder's avatar
Morten Welinder committed
1068
	return result;
Jody Goldberg's avatar
Jody Goldberg committed
1069 1070
}

1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
/*
 * cmd_area_set_text
 *
 * the caller is expected to have ensured:
 *
 * 1) that no array is being split
 * 2) that the range is not locked.
 *
 */

gboolean
cmd_area_set_text (WorkbookControl *wbc, SheetView *sv,
		   char const *new_text, PangoAttrList *markup)
{
	GnmEvalPos ep;
	gboolean result;
	GSList *selection = selection_get_ranges (sv, FALSE);

	eval_pos_init_editpos (&ep, sv);
	result = cmd_set_text_full (wbc, selection, &ep,
1091
				    new_text, markup, TRUE);
1092 1093 1094 1095 1096 1097 1098
	return result;
}

gboolean
cmd_set_text (WorkbookControl *wbc,
	      Sheet *sheet, GnmCellPos const *pos,
	      char const *new_text,
1099 1100
	      PangoAttrList *markup,
	      gboolean autocorrect)
1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
{
	GnmCell const *cell;
	GnmEvalPos ep;
	gboolean result;
	GSList *selection;
	GnmRange *r;

	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
	g_return_val_if_fail (new_text != NULL, TRUE);

	/* Ensure that we are not splitting up an array */
	cell = sheet_cell_get (sheet, pos->col, pos->row);
	if (gnm_cell_is_nonsingleton_array (cell)) {
		gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc),
			_("Set Text"), NULL);
		return TRUE;
	}

	eval_pos_init_pos (&ep, sheet, pos);
	r = g_new (GnmRange, 1);
	r->start = r->end = *pos;
	selection = g_slist_prepend (NULL, r);
	result = cmd_set_text_full (wbc, selection, &ep,
1124
				    new_text, markup, autocorrect);
1125 1126 1127 1128
	return result;
}


1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
/*
 * cmd_area_set_array_expr
 *
 * the caller is expected to have ensured:
 *
 * 1) that no array is being split
 * 2) that the selection consists of a single range
 * 3) that the range is not locked.
 *
 */

gboolean
cmd_area_set_array_expr (WorkbookControl *wbc, SheetView *sv,
			 GnmExprTop const  *texpr)
{
	GSList	*selection = selection_get_ranges (sv, FALSE);
	GOUndo *undo = NULL;
	GOUndo *redo = NULL;
	gboolean result;
	Sheet *sheet = sv_sheet (sv);
	char *name;
	char *text;
	GnmSheetRange *sr;
1152 1153 1154
	GnmRange *r_1, *r_2, *r;
	ColRowIndexList *cri_col_list;
	ColRowIndexList *cri_row_list;
Morten Welinder's avatar
Morten Welinder committed
1155

1156 1157 1158 1159
	g_return_val_if_fail (selection != NULL , TRUE);
	g_return_val_if_fail (selection->next == NULL , TRUE);

	name = undo_range_list_name (sheet, selection);
1160
	text = g_strdup_printf (_("Inserting array expression in %s"), name);
1161 1162
	g_free (name);

1163
	r = selection->data;
1164

Morten Welinder's avatar
Morten Welinder committed
1165
	cri_row_list = colrow_get_index_list
1166
		(r->start.row, r->end.row, NULL);
Morten Welinder's avatar
Morten Welinder committed
1167
	cri_col_list = colrow_get_index_list
1168 1169 1170
		(r->start.col, r->end.col, NULL);
	undo = clipboard_copy_range_undo (sheet, selection->data);
	undo = go_undo_combine (undo,
Morten Welinder's avatar
Morten Welinder committed
1171 1172 1173
				gnm_undo_colrow_restore_state_group_new
				(sheet, TRUE,
				 cri_col_list,
1174 1175 1176
				 colrow_get_sizes (sheet, TRUE,
						   cri_col_list, -1)));
	undo = go_undo_combine (undo,
Morten Welinder's avatar
Morten Welinder committed
1177 1178 1179
				gnm_undo_colrow_restore_state_group_new
				(sheet, FALSE,
				 cri_row_list,
1180 1181
				 colrow_get_sizes (sheet, FALSE,
						   cri_row_list, -1)));
Morten Welinder's avatar
Morten Welinder committed
1182

1183 1184 1185 1186 1187
	sr = gnm_sheet_range_new (sheet, r);
	r_1 = g_new (GnmRange, 1);
	*r_1 = *r;
	r_2 = g_new (GnmRange, 1);
	*r_2 = *r;
1188
	redo = gnm_cell_set_array_formula_undo (sr, texpr);
Morten Welinder's avatar
Morten Welinder committed
1189 1190 1191
	redo = go_undo_combine
		(go_undo_binary_new
		 (sheet, r_1,
1192 1193 1194
		  (GOUndoBinaryFunc) colrow_autofit_col,
		  NULL, g_free),
		 redo);
Morten Welinder's avatar
Morten Welinder committed
1195 1196 1197
	redo  = go_undo_combine
		(go_undo_binary_new
		 (sheet, r_2,
1198 1199 1200
		  (GOUndoBinaryFunc) colrow_autofit_row,
		  NULL, g_free),
		 redo);
1201 1202 1203 1204 1205 1206 1207

	range_fragment_free (selection);
	result = cmd_generic (wbc, text, undo, redo);
	g_free (text);
	return result;
}

1208 1209 1210 1211 1212 1213 1214 1215 1216
/*
 * cmd_create_data_table
 *
 * the caller is expected to have ensured:
 *
 * 1) that no array is being split
 * 2) that the range is not locked.
 *
 */
1217 1218 1219 1220
gboolean
cmd_create_data_table (WorkbookControl *wbc, Sheet *sheet, GnmRange const *r,
		       char const *col_input, char const *row_input)
{
1221 1222 1223 1224 1225 1226 1227 1228
	GOUndo *undo = NULL;
	GOUndo *redo = NULL;
	gboolean result;
	char *name;
	char *text;
	GnmSheetRange *sr;
	GnmParsePos pp;
	GnmExprTop const  *texpr;
Morten Welinder's avatar
Morten Welinder committed
1229

1230 1231 1232
	name = undo_range_name (sheet, r);
	text = g_strdup_printf (_("Creating a Data Table in %s"), name);
	g_free (name);
1233

1234
	undo = clipboard_copy_range_undo (sheet, r);
1235

1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
	sr = gnm_sheet_range_new (sheet, r);
	parse_pos_init (&pp, NULL, sheet, r->start.col, r->start.row);
	name = g_strdup_printf ("TABLE(%s,%s)", row_input, col_input);
	texpr = gnm_expr_parse_str
			(name, &pp, GNM_EXPR_PARSE_DEFAULT,
			 sheet_get_conventions (sheet), NULL);
	g_free (name);

	if (texpr == NULL) {
		g_object_unref (G_OBJECT (undo));
		g_free (text);
		return TRUE;
	}

	redo = gnm_cell_set_array_formula_undo (sr, texpr);
	gnm_expr_top_unref (texpr);

	result = cmd_generic (wbc, text, undo, redo);
	g_free (text);
	return result;
1256
}
1257

Jody Goldberg's avatar
Jody Goldberg committed
1258 1259
/******************************************************************/

1260
#define CMD_INS_DEL_COLROW_TYPE        (cmd_ins_del_colrow_get_type ())
Jody Goldberg's avatar
Jody Goldberg committed
1261
#define CMD_INS_DEL_COLROW(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_INS_DEL_COLROW_TYPE, CmdInsDelColRow))
Jody Goldberg's avatar
Jody Goldberg committed
1262

1263 1264
typedef struct {
	GnmCommand cmd;
Jody Goldberg's avatar
Jody Goldberg committed
1265

1266
	Sheet		*sheet;
Jody Goldberg's avatar
Jody Goldberg committed
1267
	gboolean	 is_insert;
1268
	gboolean	 is_cols;
1269
	gboolean         is_cut;
1270 1271
	int		 index;
	int		 count;
1272
	GnmRange        *cutcopied;
1273
	SheetView	*cut_copy_view;
1274