commands.c 45.7 KB
Newer Older
Jody Goldberg's avatar
Jody Goldberg committed
1
2
3
4
5
6
7
8
9
10
/*
 * command.c : Handlers to undo & redo commands
 *
 * Author:
 * 	Jody Goldberg <jgoldberg@home.com>
 *
 * (C) 1999, 2000 Jody Goldberg
 */
#include <config.h>
#include "gnumeric-type-util.h"
Jody Goldberg's avatar
Jody Goldberg committed
11
#include "gnumeric-util.h"
Jody Goldberg's avatar
Jody Goldberg committed
12
#include "commands.h"
13
#include "application.h"
Jody Goldberg's avatar
Jody Goldberg committed
14
#include "sheet.h"
15
#include "workbook.h"
Jody Goldberg's avatar
Jody Goldberg committed
16
#include "workbook-view.h"
17
18
#include "ranges.h"
#include "sort.h"
19
#include "parse-util.h"
20
#include "clipboard.h"
Jody Goldberg's avatar
Jody Goldberg committed
21
#include "selection.h"
Morten Welinder's avatar
Morten Welinder committed
22
#include "datetime.h"
23
#include "colrow.h"
24
#include "dialogs.h"
Michael Meeks's avatar
Michael Meeks committed
25
#include "border.h"
Jody Goldberg's avatar
Jody Goldberg committed
26
#include "rendered-value.h"
Jody Goldberg's avatar
Jody Goldberg committed
27
28
29
30
31
32

/*
 * NOTE : This is a work in progress
 *
 * Feel free to lend a hand.  There are several distinct stages to
 * wrapping each command.
33
 *
Jody Goldberg's avatar
Jody Goldberg committed
34
35
36
37
38
39
40
41
42
43
 * 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.
 *
44
45
46
47
48
49
50
51
52
 * 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
 *    duplicating work.
53
 *
54
55
 * FIXME: Filter the list of commands when a sheet is deleted.
 *
Jody Goldberg's avatar
Jody Goldberg committed
56
57
58
59
60
 * TODO : Add user preference for undo buffer size limit (# of commands ?)
 * TODO : Possibly clear lists on save.
 *
 * TODO : Reqs for selective undo
 * TODO : Add Repeat last command
61
62
63
 *
 * Future thoughts
 * - undoable preference setting ?  XL does not have this.  Do we want it ?
Jody Goldberg's avatar
Jody Goldberg committed
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
 */
/******************************************************************/

#define GNUMERIC_COMMAND_TYPE        (gnumeric_command_get_type ())
#define GNUMERIC_COMMAND(o)          (GTK_CHECK_CAST ((o), GNUMERIC_COMMAND_TYPE, GnumericCommand))
#define GNUMERIC_COMMAND_CLASS(k)    (GTK_CHECK_CLASS_CAST((k), GNUMERIC_COMMAND_TYPE, GnumericCommandClass))
#define IS_GNUMERIC_COMMAND(o)       (GTK_CHECK_TYPE ((o), GNUMERIC_COMMAND_TYPE))
#define IS_GNUMERIC_COMMAND_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), GNUMERIC_COMMAND_TYPE))

typedef struct
{
	GtkObject parent;
	char const *cmd_descriptor;	/* A string to put in the menu */
} GnumericCommand;

typedef gboolean (* UndoCmd)(GnumericCommand *this, CommandContext *context);
typedef gboolean (* RedoCmd)(GnumericCommand *this, CommandContext *context);

typedef struct {
	GtkObjectClass parent_class;

	UndoCmd		undo_cmd;
	RedoCmd		redo_cmd;
} GnumericCommandClass;

static GNUMERIC_MAKE_TYPE(gnumeric_command, "GnumericCommand",
			  GnumericCommand, NULL, NULL,
			  gtk_object_get_type());

Morten Welinder's avatar
Morten Welinder committed
93
94
95
/* Store the real GtkObject dtor pointer */
static void (* gtk_object_dtor) (GtkObject *object) = NULL;

Jody Goldberg's avatar
Jody Goldberg committed
96
97
98
static void
gnumeric_command_destroy (GtkObject *obj)
{
Michael Meeks's avatar
Michael Meeks committed
99
	GnumericCommand *cmd = GNUMERIC_COMMAND (obj);
Jody Goldberg's avatar
Jody Goldberg committed
100
101
102
103
104

	g_return_if_fail (cmd != NULL);

	/* The const was to avoid accidental changes elsewhere */
	g_free ((gchar *)cmd->cmd_descriptor);
Morten Welinder's avatar
Morten Welinder committed
105
106
107
108

	/* Call the base class dtor */
	g_return_if_fail (gtk_object_dtor);
	(*gtk_object_dtor) (obj);
Jody Goldberg's avatar
Jody Goldberg committed
109
110
111
112
113
114
115
116
117
118
}

#define GNUMERIC_MAKE_COMMAND(type, func) \
static gboolean \
func ## _undo (GnumericCommand *me, CommandContext *context); \
static gboolean \
func ## _redo (GnumericCommand *me, CommandContext *context); \
static void \
func ## _destroy (GtkObject *object); \
static void \
Morten Welinder's avatar
Morten Welinder committed
119
func ## _class_init (GnumericCommandClass * const parent) \
Jody Goldberg's avatar
Jody Goldberg committed
120
{	\
Morten Welinder's avatar
Morten Welinder committed
121
122
123
124
	parent->undo_cmd = (UndoCmd)& func ## _undo;		\
	parent->redo_cmd = (RedoCmd)& func ## _redo;		\
	if (gtk_object_dtor == NULL)				\
		gtk_object_dtor = parent->parent_class.destroy;	\
Jody Goldberg's avatar
Jody Goldberg committed
125
126
	parent->parent_class.destroy = & func ## _destroy;	\
} \
Morten Welinder's avatar
Morten Welinder committed
127
128
static GNUMERIC_MAKE_TYPE_WITH_PARENT(func, #type, type, GnumericCommandClass, func ## _class_init, NULL, \
				      gnumeric_command_get_type())
Jody Goldberg's avatar
Jody Goldberg committed
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

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

/**
 * 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.
 */
static gchar const *
get_menu_label (GSList *cmd_list)
{
	if (cmd_list != NULL) {
		GnumericCommand *cmd = GNUMERIC_COMMAND (cmd_list->data);
		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)
{
	workbook_view_set_undo_redo_state (wb,
					   get_menu_label (wb->undo_commands),
					   get_menu_label (wb->redo_commands));
}

/*
 * command_undo : Undo the last command executed.
 *
 * @context : The command context which issued the request.
 *            Any user level errors generated by undoing will be reported
 *            here.
 *
 * @wb : The workbook whose commands to undo.
 */
void
command_undo (CommandContext *context, Workbook *wb)
{
	GnumericCommand *cmd;
177
	GnumericCommandClass *klass;
Jody Goldberg's avatar
Jody Goldberg committed
178
179
180
181

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

Michael Meeks's avatar
Michael Meeks committed
182
	cmd = GNUMERIC_COMMAND (wb->undo_commands->data);
Jody Goldberg's avatar
Jody Goldberg committed
183
184
	g_return_if_fail (cmd != NULL);

185
186
187
188
189
190
191
	klass = GNUMERIC_COMMAND_CLASS(cmd->parent.klass);
	g_return_if_fail (klass != NULL);

	/* TRUE indicates a failure to undo.  Leave the command where it is */
	if (klass->undo_cmd (cmd, context))
		return;

Jody Goldberg's avatar
Jody Goldberg committed
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
	wb->undo_commands = g_slist_remove (wb->undo_commands,
					    wb->undo_commands->data);
	wb->redo_commands = g_slist_prepend (wb->redo_commands, cmd);
	undo_redo_menu_labels (wb);
	/* TODO : Should we mark the workbook as clean or pristine too */
}

/*
 * command_redo : Redo the last command that was undone.
 *
 * @context : The command context which issued the request.
 *            Any user level errors generated by redoing will be reported
 *            here.
 *
 * @wb : The workbook whose commands to redo.
 */
void
command_redo (CommandContext *context, Workbook *wb)
{
	GnumericCommand *cmd;
212
	GnumericCommandClass *klass;
Jody Goldberg's avatar
Jody Goldberg committed
213
214
215
216

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

Michael Meeks's avatar
Michael Meeks committed
217
	cmd = GNUMERIC_COMMAND (wb->redo_commands->data);
Jody Goldberg's avatar
Jody Goldberg committed
218
219
	g_return_if_fail (cmd != NULL);

220
221
222
	klass = GNUMERIC_COMMAND_CLASS(cmd->parent.klass);
	g_return_if_fail (klass != NULL);

223
	/* TRUE indicates a failure to redo.  Leave the command where it is */
224
225
226
	if (klass->redo_cmd (cmd, context))
		return;

Jody Goldberg's avatar
Jody Goldberg committed
227
228
229
230
231
232
233
234
235
236
237
238
	/* Remove the command from the undo list */
	wb->redo_commands = g_slist_remove (wb->redo_commands,
					    wb->redo_commands->data);
	wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
	undo_redo_menu_labels (wb);
}

/*
 * command_list_release : utility routine to free the resources associated
 *    with a list of commands.
 *
 * @cmd_list : The set of commands to free.
239
240
 *
 * NOTE : remember to NULL the list when you are done.
Jody Goldberg's avatar
Jody Goldberg committed
241
242
243
244
245
246
 */
void
command_list_release (GSList *cmd_list)
{
	while (cmd_list != NULL) {
		GtkObject *cmd = GTK_OBJECT (cmd_list->data);
Morten Welinder's avatar
Morten Welinder committed
247
248
249

		g_return_if_fail (cmd != NULL);

Jody Goldberg's avatar
Jody Goldberg committed
250
251
252
253
254
255
256
257
258
		gtk_object_unref (cmd);
		cmd_list = g_slist_remove (cmd_list, cmd_list->data);
	}
}

/**
 * command_push_undo : An internal utility to tack a new command
 *    onto the undo list.
 *
259
 * @context : The context that issued the command.
Jody Goldberg's avatar
Jody Goldberg committed
260
261
 * @wb : The workbook the command operated on.
 * @cmd : The new command to add.
262
 *
263
 * returns : TRUE if there was an error.
Jody Goldberg's avatar
Jody Goldberg committed
264
 */
265
static gboolean
266
command_push_undo (CommandContext *context, Workbook *wb, GtkObject *obj)
Jody Goldberg's avatar
Jody Goldberg committed
267
{
268
269
270
271
272
273
274
275
276
277
278
279
280
281
	gboolean trouble;
	GnumericCommand *cmd;
	GnumericCommandClass *klass;

	g_return_val_if_fail (wb, TRUE);

	cmd = GNUMERIC_COMMAND (obj);
	g_return_val_if_fail (cmd != NULL, TRUE);

	klass = GNUMERIC_COMMAND_CLASS(cmd->parent.klass);
	g_return_val_if_fail (klass != NULL, TRUE);

	/* TRUE indicates a failure to do the command */
	trouble = klass->redo_cmd (cmd, context);
Jody Goldberg's avatar
Jody Goldberg committed
282

283
	if  (!trouble) {
284
285
		command_list_release (wb->redo_commands);
		wb->redo_commands = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
286

287
		wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
Jody Goldberg's avatar
Jody Goldberg committed
288

289
290
		undo_redo_menu_labels (wb);
	} else
291
		gtk_object_unref (obj);
292
293

	return trouble;
Jody Goldberg's avatar
Jody Goldberg committed
294
295
296
297
298
299
300
301
302
303
304
}

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

#define CMD_SET_TEXT_TYPE        (cmd_set_text_get_type ())
#define CMD_SET_TEXT(o)          (GTK_CHECK_CAST ((o), CMD_SET_TEXT_TYPE, CmdSetText))

typedef struct
{
	GnumericCommand parent;

305
	EvalPos	 pos;
Jody Goldberg's avatar
Jody Goldberg committed
306
307
308
309
310
311
312
313
314
315
316
317
318
319
	gchar		*text;
} CmdSetText;

GNUMERIC_MAKE_COMMAND (CmdSetText, cmd_set_text);

static gboolean
cmd_set_text_undo (GnumericCommand *cmd, CommandContext *context)
{
	CmdSetText *me = CMD_SET_TEXT(cmd);
	Cell *cell;
	char *new_text;

	g_return_val_if_fail (me != NULL, TRUE);

320
	/* Get the cell */
Jody Goldberg's avatar
Jody Goldberg committed
321
322
323
324
	cell = sheet_cell_get (me->pos.sheet,
			       me->pos.eval.col,
			       me->pos.eval.row);

325
	/* Save the new value so we can redo */
326
	new_text = (cell == NULL || cell->value == NULL || cell->value->type == VALUE_EMPTY)
327
	    ? NULL : cell_get_entered_text (cell);
Jody Goldberg's avatar
Jody Goldberg committed
328

329
	/* Restore the old value if it was not empty */
Morten Welinder's avatar
Morten Welinder committed
330
	if (me->text != NULL) {
331
332
333
334
335
		if (cell == NULL)
			cell = sheet_cell_new (me->pos.sheet,
					       me->pos.eval.col,
					       me->pos.eval.row);
		sheet_cell_set_text (cell, me->text);
Morten Welinder's avatar
Morten Welinder committed
336
		g_free (me->text);
337
	} else if (cell != NULL)
338
		sheet_cell_remove (me->pos.sheet, cell, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
339
340

	me->text = new_text;
341

342
343
	sheet_set_dirty (me->pos.sheet, TRUE);
	workbook_recalc (me->pos.sheet->workbook);
Jody Goldberg's avatar
Jody Goldberg committed
344
	sheet_update (me->pos.sheet);
345

Jody Goldberg's avatar
Jody Goldberg committed
346
347
348
349
350
351
352
353
354
355
356
357
358
359
	return FALSE;
}

static gboolean
cmd_set_text_redo (GnumericCommand *cmd, CommandContext *context)
{
	/* Undo and redo are the same for this case */
	return cmd_set_text_undo (cmd, context);
}

static void
cmd_set_text_destroy (GtkObject *cmd)
{
	CmdSetText *me = CMD_SET_TEXT(cmd);
Jody Goldberg's avatar
Jody Goldberg committed
360
	if (me->text != NULL) {
Jody Goldberg's avatar
Jody Goldberg committed
361
		g_free (me->text);
Jody Goldberg's avatar
Jody Goldberg committed
362
363
		me->text = NULL;
	}
Jody Goldberg's avatar
Jody Goldberg committed
364
365
366
367
368
	gnumeric_command_destroy (cmd);
}

gboolean
cmd_set_text (CommandContext *context,
369
	      Sheet *sheet, CellPos const *pos,
370
	      char *new_text)
Jody Goldberg's avatar
Jody Goldberg committed
371
372
373
374
375
376
{
	static int const max_descriptor_width = 15;

	GtkObject *obj;
	CmdSetText *me;
	gchar *pad = "";
377
	gchar *text;
378
	Cell const *cell;
Jukka-Pekka Iivonen's avatar
Jukka-Pekka Iivonen committed
379

Jody Goldberg's avatar
Jody Goldberg committed
380
381
382
	g_return_val_if_fail (sheet != NULL, TRUE);
	g_return_val_if_fail (new_text != NULL, TRUE);

383
384
385
386
387
388
389
	/* Ensure that we are not splitting up an array */
	cell = sheet_cell_get (sheet, pos->col, pos->row);
	if (cell_is_partial_array (cell)) {
		gnumeric_error_splits_array (context);
		return TRUE;
	}

390
	/* From src/dialogs/dialog-autocorrect.c */
391
	autocorrect_tool (new_text);
392

Jody Goldberg's avatar
Jody Goldberg committed
393
394
395
396
397
398
	obj = gtk_type_new (CMD_SET_TEXT_TYPE);
	me = CMD_SET_TEXT (obj);

	/* Store the specs for the object */
	me->pos.sheet = sheet;
	me->pos.eval = *pos;
Jody Goldberg's avatar
Jody Goldberg committed
399
	me->text = g_strdup (new_text);
Jody Goldberg's avatar
Jody Goldberg committed
400
401
402
403

	/* Limit the size of the descriptor to something reasonable */
	if (strlen(new_text) > max_descriptor_width) {
		pad = "..."; /* length of 3 */
404
405
		text = g_strndup (new_text,
				  max_descriptor_width - 3);
Jody Goldberg's avatar
Jody Goldberg committed
406
	} else
407
		text = (gchar *) new_text;
Jody Goldberg's avatar
Jody Goldberg committed
408
409
410

	me->parent.cmd_descriptor =
	    g_strdup_printf (_("Typing \"%s%s\" in %s"), text, pad,
411
			     cell_pos_name(pos));
Jody Goldberg's avatar
Jody Goldberg committed
412

Jody Goldberg's avatar
Jody Goldberg committed
413
414
	if (*pad)
		g_free (text);
Jody Goldberg's avatar
Jody Goldberg committed
415
416

	/* Register the command object */
417
	return command_push_undo (context, sheet->workbook, obj);
Jody Goldberg's avatar
Jody Goldberg committed
418
419
420
421
422
423
424
425
426
427
428
}

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

#define CMD_AREA_SET_TEXT_TYPE        (cmd_area_set_text_get_type ())
#define CMD_AREA_SET_TEXT(o)          (GTK_CHECK_CAST ((o), CMD_AREA_SET_TEXT_TYPE, CmdAreaSetText))

typedef struct
{
	GnumericCommand parent;

429
	EvalPos	 pos;
Jody Goldberg's avatar
Jody Goldberg committed
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
	char 	*text; 
	gboolean as_array;
	GSList	*old_content;
	GSList	*selection;
} CmdAreaSetText;

GNUMERIC_MAKE_COMMAND (CmdAreaSetText, cmd_area_set_text);

static gboolean
cmd_area_set_text_undo (GnumericCommand *cmd, CommandContext *context)
{
	CmdAreaSetText *me = CMD_AREA_SET_TEXT (cmd);
	GSList *ranges;

	g_return_val_if_fail (me != NULL, TRUE);
	g_return_val_if_fail (me->selection != NULL, TRUE);
	g_return_val_if_fail (me->old_content != NULL, TRUE);

	for (ranges = me->selection; ranges != NULL ; ranges = ranges->next) {
		Range const * const r = ranges->data;
		CellRegion * c;

		g_return_val_if_fail (me->old_content != NULL, TRUE);

		c = me->old_content->data;
455
		clipboard_paste_region (context, c, me->pos.sheet,
Jody Goldberg's avatar
Jody Goldberg committed
456
457
458
459
460
461
462
					r->start.col, r->start.row,
					PASTE_VALUES, GDK_CURRENT_TIME);
		clipboard_release (c);
		me->old_content = g_slist_remove (me->old_content, c);
	}
	g_return_val_if_fail (me->old_content == NULL, TRUE);

463
464
465
	sheet_set_dirty (me->pos.sheet, TRUE);
	workbook_recalc (me->pos.sheet->workbook);
	sheet_update (me->pos.sheet);
Jody Goldberg's avatar
Jody Goldberg committed
466
467
468
469
470
471
472
473
474
475

	return FALSE;
}

static gboolean
cmd_area_set_text_redo (GnumericCommand *cmd, CommandContext *context)
{
	CmdAreaSetText *me = CMD_AREA_SET_TEXT (cmd);
	ExprTree *expr = NULL;
	GSList *l;
476
	char const *start;
Jody Goldberg's avatar
Jody Goldberg committed
477
478
479
480

	g_return_val_if_fail (me != NULL, TRUE);

	/* Check for array subdivision */
481
482
483
	if (selection_check_for_array (me->pos.sheet, me->selection)) {
		gnumeric_error_splits_array (context);
		return TRUE;
Jody Goldberg's avatar
Jody Goldberg committed
484
485
486
487
488
489
490
491
492
	}

	/*
	 * Only enter an array formula if
	 *   1) the text is a formula
	 *   2) It's entered as an array formula
	 *   3) There is only one 1 selection
	 */
	l = me->selection;
493
494
	start = gnumeric_char_start_expr_p (me->text);
	if (start != NULL && me->as_array && l != NULL && l->next == NULL) {
Jody Goldberg's avatar
Jody Goldberg committed
495
		char *error_string = NULL;
496
		ParsePos pp;
497
		expr = expr_parse_string (start,
498
499
		    parse_pos_init_evalpos (&pp, &me->pos),
		    NULL, &error_string);
Jody Goldberg's avatar
Jody Goldberg committed
500
501
502
503
504

		if (expr == NULL)
			return TRUE;
	}

Jody Goldberg's avatar
Jody Goldberg committed
505
	/* Everything is ok. Store previous contents and perform the operation */
Jody Goldberg's avatar
Jody Goldberg committed
506
507
508
	for (l = me->selection ; l != NULL ; l = l->next) {
		Range const * const r = l->data;
		me->old_content = g_slist_prepend (me->old_content,
509
			clipboard_copy_cell_range (me->pos.sheet,
Jody Goldberg's avatar
Jody Goldberg committed
510
511
512
						   r->start.col, r->start.row,
						   r->end.col, r->end.row));

Jody Goldberg's avatar
Jody Goldberg committed
513
		/* If there is an expression then this was an array */
Jody Goldberg's avatar
Jody Goldberg committed
514
		if (expr != NULL) 
515
			cell_set_array_formula (me->pos.sheet,
Jody Goldberg's avatar
Jody Goldberg committed
516
517
						r->start.row, r->start.col,
						r->end.row, r->end.col,
518
						expr, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
519
		else
520
			sheet_range_set_text (&me->pos, r, me->text);
Jody Goldberg's avatar
Jody Goldberg committed
521
522
	}

523
524
	sheet_set_dirty (me->pos.sheet, TRUE);
	workbook_recalc (me->pos.sheet->workbook);
525
526
527
528
529
530
531
532
533
534
535
536
537

	/*
	 * Now that things have been filled in and recalculated we can generate
	 * the spans.  Non expression cells need to be rendered.
	 * TODO : We could be smarter here.  Only the left and
	 * right columns can span,
	 * so there is no need to check the middles.
	 */
	for (l = me->selection ; l != NULL ; l = l->next) {
		Range const * const r = l->data;
		sheet_range_calc_spans (me->pos.sheet, *r, SPANCALC_RENDER);
	}

538
	sheet_update (me->pos.sheet);
Jody Goldberg's avatar
Jody Goldberg committed
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565

	return FALSE;
}
static void
cmd_area_set_text_destroy (GtkObject *cmd)
{
	CmdAreaSetText *me = CMD_AREA_SET_TEXT (cmd);

	g_free (me->text);

	if (me->old_content != NULL) {
		GSList *l;
		for (l = me->old_content ; l != NULL ; l = g_slist_remove (l, l->data))
			clipboard_release (l->data);
		me->old_content = NULL;
	}
	if (me->selection != NULL) {
		GSList *l;
		for (l = me->selection ; l != NULL ; l = g_slist_remove (l, l->data))
			g_free (l->data);
		me->selection = NULL;
	}

	gnumeric_command_destroy (cmd);
}

gboolean
566
cmd_area_set_text (CommandContext *context, EvalPos const *pos,
567
		   char const *new_text, gboolean as_array)
Jody Goldberg's avatar
Jody Goldberg committed
568
569
570
571
572
573
574
575
576
577
578
{
	static int const max_descriptor_width = 15;

	GtkObject *obj;
	CmdAreaSetText *me;
	gchar *text, *pad = "";

	obj = gtk_type_new (CMD_AREA_SET_TEXT_TYPE);
	me = CMD_AREA_SET_TEXT (obj);

	/* Store the specs for the object */
579
580
	me->pos         = *pos;
	me->text        = g_strdup (new_text);
Jody Goldberg's avatar
Jody Goldberg committed
581
	me->as_array    = as_array;
582
	me->selection   = selection_get_ranges (pos->sheet, FALSE /* No intersection */);
Jody Goldberg's avatar
Jody Goldberg committed
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
	me->old_content = NULL;

	if (strlen(new_text) > max_descriptor_width) {
		pad = "..."; /* length of 3 */
		text = g_strndup (new_text,
				  max_descriptor_width - 3);
	} else
		text = (gchar *) new_text;

	me->parent.cmd_descriptor =
	    g_strdup_printf (_("Typing \"%s%s\""), text, pad);

	if (*pad)
		g_free (text);

	/* Register the command object */
599
	return command_push_undo (context, pos->sheet->workbook, obj);
Jody Goldberg's avatar
Jody Goldberg committed
600
601
602
603
604
605
606
607
608
609
610
611
612
}

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

#define CMD_INS_DEL_ROW_COL_TYPE        (cmd_ins_del_row_col_get_type ())
#define CMD_INS_DEL_ROW_COL(o)          (GTK_CHECK_CAST ((o), CMD_INS_DEL_ROW_COL_TYPE, CmdInsDelRowCol))

typedef struct
{
	GnumericCommand parent;

	Sheet		*sheet;
	gboolean	 is_insert;
613
	gboolean	 is_cols;
614
615
616
617
	int		 index;
	int		 count;

	double		*sizes;
618
619
	CellRegion 	*contents;
	GSList		*reloc_storage;
Jody Goldberg's avatar
Jody Goldberg committed
620
621
622
623
624
625
626
627
} CmdInsDelRowCol;

GNUMERIC_MAKE_COMMAND (CmdInsDelRowCol, cmd_ins_del_row_col);

static gboolean
cmd_ins_del_row_col_undo (GnumericCommand *cmd, CommandContext *context)
{
	CmdInsDelRowCol *me = CMD_INS_DEL_ROW_COL(cmd);
628
	int index;
629
630
	GSList *tmp = NULL;
	gboolean trouble;
Jody Goldberg's avatar
Jody Goldberg committed
631
632

	g_return_val_if_fail (me != NULL, TRUE);
633
634
	g_return_val_if_fail (me->sizes != NULL, TRUE);
	g_return_val_if_fail (me->contents != NULL, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
635

636
	if (!me->is_insert) {
637
		index = me->index;
638
		if (me->is_cols)
639
			trouble = sheet_insert_cols (context, me->sheet, me->index, me->count, &tmp);
640
		else
641
			trouble = sheet_insert_rows (context, me->sheet, me->index, me->count, &tmp);
642
	} else {
643
		index = ((me->is_cols) ? SHEET_MAX_COLS : SHEET_MAX_ROWS) - me->count;
644
		if (me->is_cols)
645
			trouble = sheet_delete_cols (context, me->sheet, me->index, me->count, &tmp);
646
		else
647
			trouble = sheet_delete_rows (context, me->sheet, me->index, me->count, &tmp);
648
	}
649
650

	/* restore row/col sizes */
651
652
653
	sheet_restore_row_col_sizes (me->sheet, me->is_cols, index, me->count,
				     me->sizes);
	me->sizes = NULL;
654

655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
	/* restore row/col contents */
	if (me->is_cols)
		clipboard_paste_region (context, me->contents, me->sheet,
					index, 0, PASTE_ALL_TYPES,
					GDK_CURRENT_TIME);
	else
		clipboard_paste_region (context, me->contents, me->sheet,
					0, index, PASTE_ALL_TYPES,
					GDK_CURRENT_TIME);
	clipboard_release (me->contents);
	me->contents = NULL;

	/* Throw away the undo info for the expressions after the action*/
	workbook_expr_unrelocate_free (tmp);

	/* Restore the changed expressions before the action */
	workbook_expr_unrelocate (me->sheet->workbook, me->reloc_storage);
	me->reloc_storage = NULL;

Jody Goldberg's avatar
Jody Goldberg committed
674
	sheet_set_dirty (me->sheet, TRUE);
675
	workbook_recalc (me->sheet->workbook);
676
	sheet_update (me->sheet);
677

678
679
680
	/* Ins/Del Row/Col unants things */
	application_clipboard_unant ();

681
	return trouble;
Jody Goldberg's avatar
Jody Goldberg committed
682
683
684
685
686
687
}

static gboolean
cmd_ins_del_row_col_redo (GnumericCommand *cmd, CommandContext *context)
{
	CmdInsDelRowCol *me = CMD_INS_DEL_ROW_COL(cmd);
688
689
	gboolean trouble;
	int index;
Jody Goldberg's avatar
Jody Goldberg committed
690
691

	g_return_val_if_fail (me != NULL, TRUE);
692
	g_return_val_if_fail (me->sizes == NULL, TRUE);
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
	g_return_val_if_fail (me->contents == NULL, TRUE);

	index = (me->is_insert)
	    ? (((me->is_cols) ? SHEET_MAX_COLS : SHEET_MAX_ROWS) - me->count)
	    : me->index;

	me->sizes = sheet_save_row_col_sizes (me->sheet, me->is_cols,
					      index, me->count);
	me->contents = (me->is_cols)
	    ? clipboard_copy_cell_range (me->sheet,
					 index,			0,
					 index + me->count - 1,	SHEET_MAX_ROWS-1)
	    : clipboard_copy_cell_range  (me->sheet,
					  0,			index,
					  SHEET_MAX_COLS-1,	index + me->count - 1);
Jody Goldberg's avatar
Jody Goldberg committed
708

709
710
	if (me->is_insert) {
		if (me->is_cols)
711
712
			trouble = sheet_insert_cols (context, me->sheet, me->index,
						     me->count, &me->reloc_storage);
713
		else
714
715
			trouble = sheet_insert_rows (context, me->sheet, me->index,
						     me->count, &me->reloc_storage);
716
717
	} else {
		if (me->is_cols)
718
719
			trouble = sheet_delete_cols (context, me->sheet, me->index,
						     me->count, &me->reloc_storage);
720
		else
721
722
			trouble =sheet_delete_rows (context, me->sheet, me->index,
						    me->count, &me->reloc_storage);
723
724
	}

Jody Goldberg's avatar
Jody Goldberg committed
725
	sheet_set_dirty (me->sheet, TRUE);
726
	workbook_recalc (me->sheet->workbook);
727
	sheet_update (me->sheet);
728

729
730
731
	/* Ins/Del Row/Col unants things */
	application_clipboard_unant ();

732
	return trouble;
Jody Goldberg's avatar
Jody Goldberg committed
733
}
734

Jody Goldberg's avatar
Jody Goldberg committed
735
736
737
738
static void
cmd_ins_del_row_col_destroy (GtkObject *cmd)
{
	CmdInsDelRowCol *me = CMD_INS_DEL_ROW_COL(cmd);
739

740
	if (me->sizes) {
741
		g_free (me->sizes);
742
743
744
745
746
747
		me->sizes = NULL;
	}
	if (me->contents) {
		clipboard_release (me->contents);
		me->contents = NULL;
	}
Jody Goldberg's avatar
Jody Goldberg committed
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
	gnumeric_command_destroy (cmd);
}

static gboolean
cmd_ins_del_row_col (CommandContext *context,
		     Sheet *sheet,
		     gboolean is_col, gboolean is_insert,
		     char const * descriptor, int index, int count)
{
	GtkObject *obj;
	CmdInsDelRowCol *me;

	g_return_val_if_fail (sheet != NULL, TRUE);

	obj = gtk_type_new (CMD_INS_DEL_ROW_COL_TYPE);
	me = CMD_INS_DEL_ROW_COL (obj);

	/* Store the specs for the object */
	me->sheet = sheet;
	me->is_cols = is_col;
	me->is_insert = is_insert;
	me->index = index;
	me->count = count;
771
	me->sizes = NULL;
772
	me->contents = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
773
774
775

	me->parent.cmd_descriptor = descriptor;

776
	/* Register the command object */
777
	return command_push_undo (context, sheet->workbook, obj);
Jody Goldberg's avatar
Jody Goldberg committed
778
779
780
781
782
783
}

gboolean
cmd_insert_cols (CommandContext *context,
		 Sheet *sheet, int start_col, int count)
{
784
785
786
787
	char *mesg = g_strdup_printf ((count > 1)
				      ? _("Inserting %d columns before %s")
				      : _("Inserting %d column before %s"), count,
				      col_name(start_col));
788
789
	return cmd_ins_del_row_col (context, sheet, TRUE, TRUE, mesg,
				    start_col, count);
Jody Goldberg's avatar
Jody Goldberg committed
790
791
792
793
794
795
}

gboolean
cmd_insert_rows (CommandContext *context,
		 Sheet *sheet, int start_row, int count)
{
796
797
798
799
	char *mesg = g_strdup_printf ((count > 1)
				      ? _("Inserting %d rows before %d")
				      : _("Inserting %d row before %d"),
				      count, start_row+1);
800
801
	return cmd_ins_del_row_col (context, sheet, FALSE, TRUE, mesg,
				    start_row, count);
Jody Goldberg's avatar
Jody Goldberg committed
802
803
804
805
806
807
808
809
810
}

gboolean
cmd_delete_cols (CommandContext *context,
		 Sheet *sheet, int start_col, int count)
{
	char *mesg;
	if (count > 1) {
		/* col_name uses a static buffer */
811
		char *temp = g_strdup_printf (_("Deleting %d columns %s:"),
Jody Goldberg's avatar
Jody Goldberg committed
812
					      count, col_name(start_col));
813
		mesg = g_strconcat (temp, col_name(start_col+count-1), NULL);
Jody Goldberg's avatar
Jody Goldberg committed
814
815
		g_free (temp);
	} else
Morten Welinder's avatar
Morten Welinder committed
816
		mesg = g_strdup_printf (_("Deleting column %s"), col_name(start_col));
Jody Goldberg's avatar
Jody Goldberg committed
817

818
	return cmd_ins_del_row_col (context, sheet, TRUE, FALSE, mesg, start_col, count);
Jody Goldberg's avatar
Jody Goldberg committed
819
820
821
822
823
824
825
}

gboolean
cmd_delete_rows (CommandContext *context,
		 Sheet *sheet, int start_row, int count)
{
	char *mesg = (count > 1)
826
	    ? g_strdup_printf (_("Deleting %d rows %d:%d"), count, start_row,
Jody Goldberg's avatar
Jody Goldberg committed
827
828
829
			       start_row+count-1)
	    : g_strdup_printf (_("Deleting row %d"), start_row);

830
	return cmd_ins_del_row_col (context, sheet, FALSE, FALSE, mesg, start_row, count);
Jody Goldberg's avatar
Jody Goldberg committed
831
832
833
834
835
836
837
838
839
840
}

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

#define CMD_CLEAR_TYPE        (cmd_clear_get_type ())
#define CMD_CLEAR(o)          (GTK_CHECK_CAST ((o), CMD_CLEAR_TYPE, CmdClear))

typedef struct
{
	GnumericCommand parent;
Jody Goldberg's avatar
Jody Goldberg committed
841
842
843
844
845
846

	int	 clear_flags;
	int	 paste_flags;
	Sheet	*sheet;
	GSList 	*old_content;
	GSList	*selection;
Jody Goldberg's avatar
Jody Goldberg committed
847
848
849
850
851
852
853
854
} CmdClear;

GNUMERIC_MAKE_COMMAND (CmdClear, cmd_clear);

static gboolean
cmd_clear_undo (GnumericCommand *cmd, CommandContext *context)
{
	CmdClear *me = CMD_CLEAR(cmd);
Jody Goldberg's avatar
Jody Goldberg committed
855
	GSList *ranges;
Jody Goldberg's avatar
Jody Goldberg committed
856
857

	g_return_val_if_fail (me != NULL, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
	g_return_val_if_fail (me->selection != NULL, TRUE);
	g_return_val_if_fail (me->old_content != NULL, TRUE);

	for (ranges = me->selection; ranges != NULL ; ranges = ranges->next) {
		Range const * const r = ranges->data;
		CellRegion * c;

		g_return_val_if_fail (me->old_content != NULL, TRUE);

		c = me->old_content->data;
		clipboard_paste_region (context, c, me->sheet,
					r->start.col, r->start.row,
					me->paste_flags,
					GDK_CURRENT_TIME);
		clipboard_release (c);
		me->old_content = g_slist_remove (me->old_content, c);
	}
	g_return_val_if_fail (me->old_content == NULL, TRUE);

Jody Goldberg's avatar
Jody Goldberg committed
877
	sheet_set_dirty (me->sheet, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
878
	workbook_recalc (me->sheet->workbook);
Jody Goldberg's avatar
Jody Goldberg committed
879
	sheet_update (me->sheet);
Jody Goldberg's avatar
Jody Goldberg committed
880
881
882
883
884
885
886
887

	return FALSE;
}

static gboolean
cmd_clear_redo (GnumericCommand *cmd, CommandContext *context)
{
	CmdClear *me = CMD_CLEAR(cmd);
Jody Goldberg's avatar
Jody Goldberg committed
888
	GSList *l;
Jody Goldberg's avatar
Jody Goldberg committed
889
890

	g_return_val_if_fail (me != NULL, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
891
892
893
	g_return_val_if_fail (me->selection != NULL, TRUE);
	g_return_val_if_fail (me->old_content == NULL, TRUE);

894
895
896
897
898
899
	/* Check for array subdivision */
	if (selection_check_for_array (me->sheet, me->selection)) {
		gnumeric_error_splits_array (context);
		return TRUE;
	}

Jody Goldberg's avatar
Jody Goldberg committed
900
901
902
903
904
905
906
907
	for (l = me->selection ; l != NULL ; l = l->next) {
		Range const * const r = l->data;
		me->old_content =
			g_slist_prepend (me->old_content,
				clipboard_copy_cell_range (me->sheet,
							   r->start.col, r->start.row,
							   r->end.col, r->end.row));

908
		/* We have already checked the arrays */
Jody Goldberg's avatar
Jody Goldberg committed
909
910
911
		sheet_clear_region (context, me->sheet,
				    r->start.col, r->start.row,
				    r->end.col, r->end.row,
912
				    me->clear_flags|CLEAR_NOCHECKARRAY);
Jody Goldberg's avatar
Jody Goldberg committed
913
914
	}

Jody Goldberg's avatar
Jody Goldberg committed
915
	sheet_set_dirty (me->sheet, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
916
	workbook_recalc (me->sheet->workbook);
Jody Goldberg's avatar
Jody Goldberg committed
917
	sheet_update (me->sheet);
Jody Goldberg's avatar
Jody Goldberg committed
918
919
920

	return FALSE;
}
Jody Goldberg's avatar
Jody Goldberg committed
921

Jody Goldberg's avatar
Jody Goldberg committed
922
923
924
925
static void
cmd_clear_destroy (GtkObject *cmd)
{
	CmdClear *me = CMD_CLEAR(cmd);
Jody Goldberg's avatar
Jody Goldberg committed
926
927
928
929
930
931
932
933
934
935
936
937
938
939

	if (me->old_content != NULL) {
		GSList *l;
		for (l = me->old_content ; l != NULL ; l = g_slist_remove (l, l->data))
			clipboard_release (l->data);
		me->old_content = NULL;
	}
	if (me->selection != NULL) {
		GSList *l;
		for (l = me->selection ; l != NULL ; l = g_slist_remove (l, l->data))
			g_free (l->data);
		me->selection = NULL;
	}

Jody Goldberg's avatar
Jody Goldberg committed
940
941
942
943
	gnumeric_command_destroy (cmd);
}

gboolean
944
cmd_clear_selection (CommandContext *context, Sheet *sheet, int clear_flags)
Jody Goldberg's avatar
Jody Goldberg committed
945
{
Jody Goldberg's avatar
Jody Goldberg committed
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
	GtkObject *obj;
	CmdClear *me;

	g_return_val_if_fail (sheet != NULL, TRUE);

	obj = gtk_type_new (CMD_CLEAR_TYPE);
	me = CMD_CLEAR (obj);

	/* Store the specs for the object */
	me->sheet = sheet;
	me->clear_flags = clear_flags;
	me->old_content = NULL;
	me->selection = selection_get_ranges (sheet, FALSE /* No intersection */);

	me->paste_flags = 0;
	if (clear_flags & CLEAR_VALUES)
		me->paste_flags |= PASTE_VALUES | PASTE_FORMULAS;
	if (clear_flags & CLEAR_FORMATS)
		me->paste_flags |= PASTE_FORMATS;
	if (clear_flags & CLEAR_COMMENTS)
		g_warning ("Deleted comments can not be restored yet");

	/* TODO : Something more descriptive ? maybe the range name */
	me->parent.cmd_descriptor = g_strdup (_("Clear"));

	/* Register the command object */
972
	return command_push_undo (context, sheet->workbook, obj);
Jody Goldberg's avatar
Jody Goldberg committed
973
974
975
976
977
978
979
}

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

#define CMD_FORMAT_TYPE        (cmd_format_get_type ())
#define CMD_FORMAT(o)          (GTK_CHECK_CAST ((o), CMD_FORMAT_TYPE, CmdFormat))

Michael Meeks's avatar
Michael Meeks committed
980
981
982
983
typedef struct {
	CellPos pos;
	GList  *styles;
} CmdFormatOldStyle;
984

Michael Meeks's avatar
Michael Meeks committed
985
typedef struct {
Jody Goldberg's avatar
Jody Goldberg committed
986
	GnumericCommand parent;
Michael Meeks's avatar
Michael Meeks committed
987
988
989
990
991
992
993
994

	Sheet         *sheet;
	GSList        *selection;

	GSList        *old_styles;

	MStyle        *new_style;
	MStyleBorder **borders;
Jody Goldberg's avatar
Jody Goldberg committed
995
996
997
998
999
1000
1001
} CmdFormat;

GNUMERIC_MAKE_COMMAND (CmdFormat, cmd_format);

static gboolean
cmd_format_undo (GnumericCommand *cmd, CommandContext *context)
{
Michael Meeks's avatar
Michael Meeks committed
1002
	CmdFormat *me = CMD_FORMAT (cmd);
Jody Goldberg's avatar
Jody Goldberg committed
1003
1004
1005

	g_return_val_if_fail (me != NULL, TRUE);

Michael Meeks's avatar
Michael Meeks committed
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
	if (me->old_styles) {
		GSList *l;

		for (l = me->old_styles; l; l = l->next) {
			CmdFormatOldStyle *os = l->data;

			sheet_style_attach_list (me->sheet, os->styles,
						 &os->pos, FALSE);
		}
	}
	
Jody Goldberg's avatar
Jody Goldberg committed
1017
1018
	sheet_set_dirty (me->sheet, TRUE);
	workbook_recalc (me->sheet->workbook);
Jody Goldberg's avatar
Jody Goldberg committed
1019
1020
	sheet_update (me->sheet);

Jody Goldberg's avatar
Jody Goldberg committed
1021
1022
1023
1024
1025
1026
	return FALSE;
}

static gboolean
cmd_format_redo (GnumericCommand *cmd, CommandContext *context)
{
Michael Meeks's avatar
Michael Meeks committed
1027
1028
	CmdFormat *me = CMD_FORMAT (cmd);
	GSList    *l;
Jody Goldberg's avatar
Jody Goldberg committed
1029
1030
1031

	g_return_val_if_fail (me != NULL, TRUE);

Michael Meeks's avatar
Michael Meeks committed
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
	for (l = me->selection; l; l = l->next) {
		if (me->borders)
			sheet_range_set_border (me->sheet, l->data,
						me->borders);
		if (me->new_style) {
			mstyle_ref (me->new_style);
			sheet_range_apply_style (me->sheet, l->data,
						 me->new_style);
		}
	}

	sheet_set_dirty (me->sheet, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
1044
	workbook_recalc (me->sheet->workbook);
Jody Goldberg's avatar
Jody Goldberg committed
1045
	sheet_update (me->sheet);
Michael Meeks's avatar
Michael Meeks committed
1046

Jody Goldberg's avatar
Jody Goldberg committed
1047
1048
	return FALSE;
}
Michael Meeks's avatar
Michael Meeks committed
1049

Jody Goldberg's avatar
Jody Goldberg committed
1050
1051
1052
static void
cmd_format_destroy (GtkObject *cmd)
{
Michael Meeks's avatar
Michael Meeks committed
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
	CmdFormat *me = CMD_FORMAT (cmd);
	int        i;

	if (me->new_style)
		mstyle_unref (me->new_style);
	me->new_style = NULL;

	if (me->borders) {
		for (i = STYLE_BORDER_TOP; i < STYLE_BORDER_EDGE_MAX; i++)
			style_border_unref (me->borders [i]);
		g_free (me->borders);
		me->borders = NULL;
	}

	if (me->old_styles != NULL) {
		GSList *l;

		for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
			CmdFormatOldStyle *os = l->data;

			if (os->styles)
				sheet_style_list_destroy (os->styles);

			g_free (os);
		}
		me->old_styles = NULL;
	}

	if (me->selection != NULL) {
		GSList *l;
		for (l = me->selection ; l != NULL ; l = g_slist_remove (l, l->data))
			g_free (l->data);
		me->selection = NULL;
	}
Jody Goldberg's avatar
Jody Goldberg committed
1087
1088
1089
1090

	gnumeric_command_destroy (cmd);
}

Michael Meeks's avatar
Michael Meeks committed
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
/**
 * cmd_format:
 * @context: the context.
 * @sheet: the sheet
 * @style: style to apply to the selection
 * @borders: borders to apply to the selection
 * 
 *  If borders is non NULL, then the MStyleBorder references are passed,
 * the MStyle reference is also passed.
 * 
1101
 * Return value: TRUE if there was a problem
Michael Meeks's avatar
Michael Meeks committed
1102
 **/
Jody Goldberg's avatar
Jody Goldberg committed
1103
gboolean
Michael Meeks's avatar
Michael Meeks committed
1104
1105
cmd_format (CommandContext *context, Sheet *sheet,
	    MStyle *style, MStyleBorder **borders)
Jody Goldberg's avatar
Jody Goldberg committed
1106
{
Michael Meeks's avatar
Michael Meeks committed
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
	GtkObject *obj;
	CmdFormat *me;
	GSList    *l;

	g_return_val_if_fail (sheet != NULL, TRUE);

	obj = gtk_type_new (CMD_FORMAT_TYPE);
	me = CMD_FORMAT (obj);

	me->sheet      = sheet;
	me->selection  = selection_get_ranges (sheet, FALSE); /* TRUE ? */
	me->new_style  = style;

	me->old_styles = NULL;
	for (l = me->selection; l; l = l->next) {
		CmdFormatOldStyle *os;
Jody Goldberg's avatar
Jody Goldberg committed
1123
1124
1125
1126
1127
1128
1129
		Range range = *((Range const *)l->data);

		/* Store the containing range to handle borders */
		if (range.start.col > 0) range.start.col--;
		if (range.start.row > 0) range.start.row--;
		if (range.end.col < SHEET_MAX_COLS-1) range.end.col++;
		if (range.end.row < SHEET_MAX_ROWS-1) range.end.row++;
Michael Meeks's avatar
Michael Meeks committed
1130
1131
1132

		os = g_new (CmdFormatOldStyle, 1);

Jody Goldberg's avatar
Jody Goldberg committed
1133
1134
		os->styles = sheet_get_styles_in_range (sheet, &range);
		os->pos = range.start;
Michael Meeks's avatar
Michael Meeks committed
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150

		me->old_styles = g_slist_append (me->old_styles, os);
	}

	if (borders) {
		int i;

		me->borders = g_new (MStyleBorder *, STYLE_BORDER_EDGE_MAX);
		for (i = STYLE_BORDER_TOP; i < STYLE_BORDER_EDGE_MAX; i++)
			me->borders [i] = borders [i];
	} else
		me->borders = NULL;

	me->parent.cmd_descriptor = g_strdup (_("Format cells"));

	/* Register the command object */
1151
	return command_push_undo (context, sheet->workbook, obj);
Jody Goldberg's avatar
Jody Goldberg committed
1152
1153
}

1154
1155
1156
1157
1158
1159
1160
1161
1162
/******************************************************************/

#define CMD_RENAME_SHEET_TYPE        (cmd_rename_sheet_get_type ())
#define CMD_RENAME_SHEET(o)          (GTK_CHECK_CAST ((o), CMD_RENAME_SHEET_TYPE, CmdRenameSheet))

typedef struct
{
	GnumericCommand parent;

1163
1164
	Workbook *wb;
	char *old_name, *new_name;
1165
1166
1167
1168
1169
1170
1171
} CmdRenameSheet;

GNUMERIC_MAKE_COMMAND (CmdRenameSheet, cmd_rename_sheet);

static gboolean
cmd_rename_sheet_undo (GnumericCommand *cmd, CommandContext *context)
{
Michael Meeks's avatar
Michael Meeks committed
1172
	CmdRenameSheet *me = CMD_RENAME_SHEET (cmd);
1173
1174
1175

	g_return_val_if_fail (me != NULL, TRUE);

Jody Goldberg's avatar
Jody Goldberg committed
1176
1177
	return workbook_rename_sheet (context, me->wb,
				      me->new_name, me->old_name);
1178
1179
1180
1181
1182
}

static gboolean
cmd_rename_sheet_redo (GnumericCommand *cmd, CommandContext *context)
{
Michael Meeks's avatar
Michael Meeks committed
1183
	CmdRenameSheet *me = CMD_RENAME_SHEET (cmd);
1184
1185
1186

	g_return_val_if_fail (me != NULL, TRUE);

Jody Goldberg's avatar
Jody Goldberg committed
1187
1188
	return workbook_rename_sheet (context, me->wb,
				      me->old_name, me->new_name);
1189
1190
1191
1192
}
static void
cmd_rename_sheet_destroy (GtkObject *cmd)
{
Michael Meeks's avatar
Michael Meeks committed
1193
	CmdRenameSheet *me = CMD_RENAME_SHEET (cmd);
1194

1195
1196
1197
	me->wb = NULL;
	g_free (me->old_name);
	g_free (me->new_name);
1198
1199
1200
1201
1202
1203
1204
	gnumeric_command_destroy (cmd);
}

gboolean
cmd_rename_sheet (CommandContext *context,
		  Workbook *wb, const char *old_name, const char *new_name)
{
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
	GtkObject *obj;
	CmdRenameSheet *me;

	g_return_val_if_fail (wb != NULL, TRUE);

	obj = gtk_type_new (CMD_RENAME_SHEET_TYPE);
	me = CMD_RENAME_SHEET (obj);

	/* Store the specs for the object */
	me->wb = wb;
	me->old_name = g_strdup (old_name);
	me->new_name = g_strdup (new_name);

	me->parent.cmd_descriptor = 
	    g_strdup_printf (_("Rename sheet '%s' '%s'"), old_name, new_name);

	/* Register the command object */
1222
	return command_push_undo (context, wb, obj);
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
}

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

#define CMD_SET_DATE_TIME_TYPE        (cmd_set_date_time_get_type ())
#define CMD_SET_DATE_TIME(o)          (GTK_CHECK_CAST ((o), CMD_SET_DATE_TIME_TYPE, CmdSetDateTime))

typedef struct
{
	GnumericCommand parent;

	gboolean	 is_date;
1235
	EvalPos	 pos;
1236
1237
1238
1239
1240
1241
1242
1243
	gchar		*contents;
} CmdSetDateTime;

GNUMERIC_MAKE_COMMAND (CmdSetDateTime, cmd_set_date_time);

static gboolean
cmd_set_date_time_undo (GnumericCommand *cmd, CommandContext *context)
{
Michael Meeks's avatar
Michael Meeks committed
1244
	CmdSetDateTime *me = CMD_SET_DATE_TIME (cmd);
1245
1246
	Cell  *cell;
	Sheet *sheet;
1247
1248
1249

	g_return_val_if_fail (me != NULL, TRUE);

1250
1251
	sheet = me->pos.sheet;

1252
	/* Get the cell */
1253
	cell = sheet_cell_get (sheet, me->pos.eval.col, me->pos.eval.row);
1254

1255
	/* The cell MUST exist or something is very confused */
1256
1257
1258
1259
	g_return_val_if_fail (cell != NULL, TRUE);

	/* Restore the old value (possibly empty) */
	if (me->contents != NULL) {
1260
		sheet_cell_set_text (cell, me->contents);
1261
1262
1263
		g_free (me->contents);
		me->contents = NULL;
	} else
1264
		sheet_cell_remove (sheet, cell, TRUE);
1265

1266
1267
1268
1269
	/* see if we need to update status */
	sheet_flag_status_update_cell (me->pos.sheet,
				       me->pos.eval.col, me->pos.eval.row);

1270
1271
1272
	sheet_set_dirty (sheet, TRUE);
	workbook_recalc (sheet->workbook);
	sheet_update (sheet);
Jody Goldberg's avatar
Jody Goldberg committed
1273

1274
1275
1276
1277
1278
1279
	return FALSE;
}

static gboolean
cmd_set_date_time_redo (GnumericCommand *cmd, CommandContext *context)
{
Michael Meeks's avatar
Michael Meeks committed
1280
	CmdSetDateTime *me = CMD_SET_DATE_TIME (cmd);
1281
1282
1283
1284
1285
1286
1287
1288
	Value *v;
	Cell *cell;
	char const * prefered_format;

	g_return_val_if_fail (me != NULL, TRUE);
	g_return_val_if_fail (me->contents == NULL, TRUE);

	if (me->is_date) {
Morten Welinder's avatar
Morten Welinder committed
1289
		v = value_new_int (datetime_timet_to_serial (time (NULL)));
1290

1291
		/* FIXME : the '>' prefix is intended to give the translators
1292
		 * a chance to provide a locale specific date format.
1293
		 * This is ugly because the format may not show up in the
1294
1295
1296
		 * list of date formats, and will be marked custom.  In addition
		 * translators should be aware that the leading character of the
		 * result will be ignored.
1297
		 */
1298
1299
		prefered_format = _(">mm/dd/yyyy");
	} else {
Morten Welinder's avatar
Morten Welinder committed
1300
		v = value_new_float (datetime_timet_to_seconds (time (NULL)) / (24.0 * 60 * 60));
1301

1302
		/* FIXME : See comment above */
1303
1304
1305
1306
1307
1308
1309
		prefered_format = _(">hh:mm");
	}

	/* Get the cell (creating it if needed) */
	cell = sheet_cell_fetch (me->pos.sheet, me->pos.eval.col, me->pos.eval.row);

	/* Save contents */
1310
	me->contents = (cell->value) ? cell_get_entered_text (cell) : NULL;
1311

1312
	sheet_cell_set_value (cell, v, prefered_format+1);
Jody Goldberg's avatar
Jody Goldberg committed
1313

1314
1315
1316
1317
	/* see if we need to update status */
	sheet_flag_status_update_cell (me->pos.sheet,
				       me->pos.eval.col, me->pos.eval.row);

Jody Goldberg's avatar
Jody Goldberg committed
1318
	sheet_set_dirty (me->pos.sheet, TRUE);
1319
	workbook_recalc (me->pos.sheet->workbook);
Jody Goldberg's avatar
Jody Goldberg committed
1320
	sheet_update (me->pos.sheet);
Jody Goldberg's avatar
Jody Goldberg committed
1321

1322
1323
1324
1325
1326
	return FALSE;
}
static void
cmd_set_date_time_destroy (GtkObject *cmd)
{
Michael Meeks's avatar
Michael Meeks committed
1327
	CmdSetDateTime *me = CMD_SET_DATE_TIME (cmd);
1328

Jody Goldberg's avatar
Jody Goldberg committed
1329
	if (me->contents) {
1330
		g_free (me->contents);
Jody Goldberg's avatar
Jody Goldberg committed
1331
1332
		me->contents = NULL;
	}
1333
1334
1335
1336
	gnumeric_command_destroy (cmd);
}

gboolean
1337
1338
cmd_set_date_time (CommandContext *context,
		   Sheet *sheet, CellPos const *pos, gboolean is_date)
1339
1340
1341
{
	GtkObject *obj;
	CmdSetDateTime *me;
1342