1 
2 /*
3  * commands.c: Handlers to undo & redo commands
4  *
5  * Copyright (C) 1999-2008 Jody Goldberg (jody@gnome.org)
6  * Copyright (C) 2002-2008 Morten Welinder (terra@gnome.org)
7  *
8  * Contributors : Almer S. Tigelaar (almer@gnome.org)
9  *                Andreas J. Guelzow (aguelzow@taliesin.ca)
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of the
14  * License, or (at your option) version 3.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
24  * USA
25  */
26 #include <gnumeric-config.h>
27 #include <glib/gi18n-lib.h>
28 #include <gnumeric.h>
29 #include <commands.h>
30 #include <gnm-command-impl.h>
31 
32 #include <application.h>
33 #include <sheet.h>
34 #include <sheet-view.h>
35 #include <sheet-style.h>
36 #include <gnm-format.h>
37 #include <format-template.h>
38 #include <command-context.h>
39 #include <workbook-control.h>
40 #include <workbook-view.h>
41 #include <workbook-priv.h> /* For the undo/redo queues and the FOREACH */
42 #include <ranges.h>
43 #include <sort.h>
44 #include <dependent.h>
45 #include <value.h>
46 #include <expr.h>
47 #include <func.h>
48 #include <expr-name.h>
49 #include <cell.h>
50 #include <sheet-merge.h>
51 #include <parse-util.h>
52 #include <print-info.h>
53 #include <clipboard.h>
54 #include <selection.h>
55 #include <colrow.h>
56 #include <style-border.h>
57 #include <tools/auto-correct.h>
58 #include <sheet-autofill.h>
59 #include <mstyle.h>
60 #include <search.h>
61 #include <gutils.h>
62 #include <gui-util.h>
63 #include <sheet-object-cell-comment.h>
64 #include <sheet-object-widget.h>
65 #include <sheet-object.h>
66 #include <sheet-object-component.h>
67 #include <sheet-object-graph.h>
68 #include <sheet-control.h>
69 #include <sheet-control-gui.h>
70 #include <sheet-utils.h>
71 #include <style-color.h>
72 #include <sheet-filter.h>
73 #include <auto-format.h>
74 #include <tools/dao.h>
75 #include <gnumeric-conf.h>
76 #include <tools/scenarios.h>
77 #include <tools/data-shuffling.h>
78 #include <tools/tabulate.h>
79 #include <wbc-gtk.h>
80 #include <undo.h>
81 
82 #include <goffice/goffice.h>
83 #include <gsf/gsf-doc-meta-data.h>
84 #include <string.h>
85 
86 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
87 
88 
89 /*
90  * There are several distinct stages to wrapping each command.
91  *
92  * 1) Find the appropriate place(s) in the catch all calls to activations
93  * of this logical function.  Be careful.  This should only be called by
94  * user initiated actions, not internal commands.
95  *
96  * 2) Copy the boiler plate code into place and implement the descriptor.
97  *
98  * 3) Implement the guts of the support functions.
99  *
100  * That way undo redo just become applications of the old or the new styles.
101  *
102  * Design thoughts:
103  * 1) redo : this should be renamed 'exec' and should be the place that the
104  *    the actual command executes.  This avoid duplicating the code for
105  *    application and re-application.
106  *
107  * 2) The command objects are responsible for generating recalc and redraw
108  *    events.  None of the internal utility routines should do so.  Those are
109  *    expensive events and should only be done once per command to avoid
110  *    duplicating work.  The lower levels can queue redraws if they must, and
111  *    flag state changes but the call to gnm_app_recalc and sheet_update is
112  *    by GnmCommand.
113  *
114  * FIXME: Filter the list of commands when a sheet is deleted.
115  *
116  * TODO : Possibly clear lists on save.
117  *
118  * TODO : Reqs for selective undo
119  *
120  * Future thoughts
121  * - undoable preference setting ?  XL does not have this.  Do we want it ?
122  */
123 /******************************************************************/
124 
125 #define GNM_COMMAND(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_COMMAND_TYPE, GnmCommand))
126 #define GNM_COMMAND_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST ((k), GNM_COMMAND_TYPE, GnmCommandClass))
127 #define GNM_IS_COMMAND(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_COMMAND_TYPE))
128 #define CMD_CLASS(o)		GNM_COMMAND_CLASS (G_OBJECT_GET_CLASS(cmd))
129 
GSF_CLASS(GnmCommand,gnm_command,NULL,NULL,G_TYPE_OBJECT)130 GSF_CLASS (GnmCommand, gnm_command, NULL, NULL, G_TYPE_OBJECT)
131 
132 void
133 gnm_command_finalize (GObject *obj)
134 {
135 	GnmCommand *cmd = GNM_COMMAND (obj);
136 	GObjectClass *parent;
137 
138 	/* The const was to avoid accidental changes elsewhere */
139 	g_free ((gchar *)cmd->cmd_descriptor);
140 	cmd->cmd_descriptor = NULL;
141 
142 	parent = g_type_class_peek (g_type_parent(G_TYPE_FROM_INSTANCE (obj)));
143 	(*parent->finalize) (obj);
144 }
145 
146 /******************************************************************/
147 
148 GString *
gnm_cmd_trunc_descriptor(GString * src,gboolean * truncated)149 gnm_cmd_trunc_descriptor (GString *src, gboolean *truncated)
150 {
151 	int max_len = gnm_conf_get_undo_max_descriptor_width ();
152 	glong len;
153 	char *pos;
154 
155 	if (max_len < 5)
156 		max_len = 5;
157 
158 	while ((pos = strchr(src->str, '\n')) != NULL ||
159 	       (pos = strchr(src->str, '\r')) != NULL)
160 		*pos = ' ';
161 
162 	len = g_utf8_strlen (src->str, -1);
163 
164 	if (truncated)
165 		*truncated = (len > max_len);
166 
167 	if (len > max_len) {
168 		gchar* last = g_utf8_offset_to_pointer (src->str,
169                                                         max_len - 1);
170 		g_string_truncate (src, last - src->str);
171 		g_string_append (src, UNICODE_ELLIPSIS);
172 	}
173 	return src;
174 }
175 
176 
177 /**
178  * cmd_cell_range_is_locked_effective:
179  * @sheet: #Sheet
180  * @range: #GnmRange
181  * @wbc: #WorkbookControl
182  * @cmd_name: the command name.
183  *
184  * checks whether the cells are effectively locked
185  *
186  * static gboolean cmd_cell_range_is_locked_effective
187  *
188  *
189  * Do not use this function unless the sheet is part of the
190  * workbook with the given wbc (otherwise the results may be strange)
191  *
192  * Returns: %TRUE if there was a problem, %FALSE otherwise.
193  */
194 gboolean
cmd_cell_range_is_locked_effective(Sheet * sheet,GnmRange * range,WorkbookControl * wbc,char const * cmd_name)195 cmd_cell_range_is_locked_effective (Sheet *sheet, GnmRange *range,
196 				    WorkbookControl *wbc, char const *cmd_name)
197 {
198 	int i, j;
199 	WorkbookView *wbv = wb_control_view (wbc);
200 
201 	if (wbv->is_protected || sheet->is_protected)
202 		for (i = range->start.row; i <= range->end.row; i++)
203 			for (j = range->start.col; j <= range->end.col; j++)
204 				if (gnm_style_get_contents_locked (sheet_style_get (sheet, j, i))) {
205 					char *r = global_range_name (sheet, range);
206 					char *text = g_strdup_printf (wbv->is_protected  ?
207 						_("%s is locked. Unprotect the workbook to enable editing.") :
208 						_("%s is locked. Unprotect the sheet to enable editing."),
209 						r);
210 					go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
211 						cmd_name, text);
212 					g_free (text);
213 					g_free (r);
214 					return TRUE;
215 				}
216 	return FALSE;
217 }
218 
219 /*
220  * checks whether the cells are effectively locked
221  *
222  * static gboolean cmd_dao_is_locked_effective
223  *
224  *
225  * Do not use this function unless the sheet is part of the
226  * workbook with the given wbcg (otherwise the results may be strange)
227  *
228  * Returns: %TRUE if there was a problem, %FALSE otherwise.
229  */
230 static gboolean
cmd_dao_is_locked_effective(data_analysis_output_t * dao,WorkbookControl * wbc,char const * cmd_name)231 cmd_dao_is_locked_effective (data_analysis_output_t  *dao,
232 			     WorkbookControl *wbc, char const *cmd_name)
233 {
234 	GnmRange range;
235 	range_init (&range, dao->start_col, dao->start_row,
236 		    dao->start_col +  dao->cols - 1,  dao->start_row +  dao->rows - 1);
237 	return (dao->type != NewWorkbookOutput &&
238 		cmd_cell_range_is_locked_effective (dao->sheet, &range, wbc, cmd_name));
239 }
240 
241 /**
242  * cmd_selection_is_locked_effective: (skip)
243  * checks whether the selection is effectively locked
244  *
245  * static gboolean cmd_selection_is_locked_effective
246  *
247  * Do not use this function unless the sheet is part of the
248  * workbook with the given wbcg (otherwise the results may be strange)
249  *
250  * Returns: %TRUE if there was a problem, %FALSE otherwise.
251  */
252 gboolean
cmd_selection_is_locked_effective(Sheet * sheet,GSList * selection,WorkbookControl * wbc,char const * cmd_name)253 cmd_selection_is_locked_effective (Sheet *sheet, GSList *selection,
254 				   WorkbookControl *wbc, char const *cmd_name)
255 {
256 	for (; selection; selection = selection->next) {
257 		GnmRange *range = selection->data;
258 		if (cmd_cell_range_is_locked_effective (sheet, range, wbc, cmd_name))
259 			return TRUE;
260 	}
261 	return FALSE;
262 }
263 
264 /*
265  * A helper routine to select a range and make sure the top-left
266  * is visible.
267  */
268 static void
select_range(Sheet * sheet,const GnmRange * r,WorkbookControl * wbc)269 select_range (Sheet *sheet, const GnmRange *r, WorkbookControl *wbc)
270 {
271 	SheetView *sv;
272 
273 	if (sheet->workbook != wb_control_get_workbook (wbc)) {
274 		/*
275 		 * We could try to pick a random wbc for the sheet's
276 		 * workbook.  But not right now.
277 		 */
278 		return;
279 	}
280 
281 	wb_control_sheet_focus (wbc, sheet);
282 	sv = sheet_get_view (sheet, wb_control_view (wbc));
283 	sv_selection_reset (sv);
284 	sv_selection_add_range (sv, r);
285 	gnm_sheet_view_make_cell_visible (sv, r->start.col, r->start.row, FALSE);
286 }
287 
288 /*
289  * A helper routine to select a list of ranges and make sure the top-left
290  * corner of the last is visible.
291  */
292 static void
select_selection(Sheet * sheet,GSList * selection,WorkbookControl * wbc)293 select_selection (Sheet *sheet, GSList *selection, WorkbookControl *wbc)
294 {
295 	SheetView *sv = sheet_get_view (sheet, wb_control_view (wbc));
296 	const GnmRange *r0 = NULL;
297 	GSList *l;
298 
299 	g_return_if_fail (selection != NULL);
300 
301 	wb_control_sheet_focus (wbc, sheet);
302 	sv_selection_reset (sv);
303 	for (l = selection; l; l = l->next) {
304 		GnmRange const *r = l->data;
305 		sv_selection_add_range (sv, r);
306 		r0 = r;
307 	}
308 	gnm_sheet_view_make_cell_visible (sv, r0->start.col, r0->start.row, FALSE);
309 }
310 
311 /**
312  * get_menu_label:
313  *     with a list of commands.
314  * @cmd_list: The command list to check.
315  *
316  * Utility routine to get the descriptor associated
317  * Returns : A static reference to a descriptor.  DO NOT free this.
318  */
319 static char const *
get_menu_label(GSList * cmd_list)320 get_menu_label (GSList *cmd_list)
321 {
322 	if (cmd_list != NULL) {
323 		GnmCommand *cmd = GNM_COMMAND (cmd_list->data);
324 		return cmd->cmd_descriptor;
325 	}
326 
327 	return NULL;
328 }
329 
330 /**
331  * undo_redo_menu_labels:
332  * @wb: The book whose undo/redo queues we are modifying
333  *
334  * Another utility to set the menus correctly.
335  */
336 static void
undo_redo_menu_labels(Workbook * wb)337 undo_redo_menu_labels (Workbook *wb)
338 {
339 	char const *undo_label = get_menu_label (wb->undo_commands);
340 	char const *redo_label = get_menu_label (wb->redo_commands);
341 
342 	WORKBOOK_FOREACH_CONTROL (wb, view, control,
343 		wb_control_undo_redo_labels (control, undo_label, redo_label);
344 	);
345 }
346 
347 static void
update_after_action(Sheet * sheet,WorkbookControl * wbc)348 update_after_action (Sheet *sheet, WorkbookControl *wbc)
349 {
350 	gnm_app_recalc ();
351 
352 	if (sheet != NULL) {
353 		g_return_if_fail (IS_SHEET (sheet));
354 
355 		sheet_mark_dirty (sheet);
356 		sheet_update (sheet);
357 
358 		if (sheet->workbook == wb_control_get_workbook (wbc))
359 			WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
360 				  wb_control_sheet_focus (control, sheet););
361 	} else if (wbc != NULL) {
362 		Sheet *sheet = wb_control_cur_sheet (wbc);
363 		if (sheet)
364 			sheet_update (sheet);
365 	}
366 }
367 
368 
369 /**
370  * command_undo:
371  * @wbc: The workbook control which issued the request.
372  *        Any user level errors generated by undoing will be reported
373  *        here.
374  *
375  * Undo the last command executed.
376  **/
377 void
command_undo(WorkbookControl * wbc)378 command_undo (WorkbookControl *wbc)
379 {
380 	GnmCommand *cmd;
381 	GnmCommandClass *klass;
382 	Workbook *wb = wb_control_get_workbook (wbc);
383 
384 	g_return_if_fail (wb != NULL);
385 	g_return_if_fail (wb->undo_commands != NULL);
386 
387 	cmd = GNM_COMMAND (wb->undo_commands->data);
388 	g_return_if_fail (cmd != NULL);
389 
390 	klass = CMD_CLASS (cmd);
391 	g_return_if_fail (klass != NULL);
392 
393 	g_object_ref (cmd);
394 
395 	/* TRUE indicates a failure to undo.  Leave the command where it is */
396 	if (!klass->undo_cmd (cmd, wbc)) {
397 		gboolean undo_cleared;
398 
399 		update_after_action (cmd->sheet, wbc);
400 
401 		go_doc_set_state (GO_DOC (wb), cmd->state_before_do);
402 
403 		/*
404 		 * A few commands clear the undo queue.  For those, we do not
405 		 * want to stuff the cmd object on the redo queue.
406 		 */
407 		undo_cleared = (wb->undo_commands == NULL);
408 
409 		if (!undo_cleared) {
410 			wb->undo_commands = g_slist_remove (wb->undo_commands, cmd);
411 			wb->redo_commands = g_slist_prepend (wb->redo_commands, cmd);
412 
413 			WORKBOOK_FOREACH_CONTROL (wb, view, control, {
414 				wb_control_undo_redo_pop (control, TRUE);
415 				wb_control_undo_redo_push (control, FALSE, cmd->cmd_descriptor, cmd);
416 			});
417 			undo_redo_menu_labels (wb);
418 			/* TODO : Should we mark the workbook as clean or pristine too */
419 		}
420 	}
421 
422 	g_object_unref (cmd);
423 }
424 
425 /**
426  * command_redo:
427  * @wbc: The workbook control which issued the request.
428  *
429  *  Redo the last command that was undone.
430  *        Any user level errors generated by redoing will be reported
431  *        here.
432  **/
433 void
command_redo(WorkbookControl * wbc)434 command_redo (WorkbookControl *wbc)
435 {
436 	GnmCommand *cmd;
437 	GnmCommandClass *klass;
438 	Workbook *wb = wb_control_get_workbook (wbc);
439 
440 	g_return_if_fail (wb);
441 	g_return_if_fail (wb->redo_commands);
442 
443 	cmd = GNM_COMMAND (wb->redo_commands->data);
444 	g_return_if_fail (cmd != NULL);
445 
446 	klass = CMD_CLASS (cmd);
447 	g_return_if_fail (klass != NULL);
448 
449 	g_object_ref (cmd);
450 
451 	cmd->state_before_do = go_doc_get_state (wb_control_get_doc (wbc));
452 
453 	/* TRUE indicates a failure to redo.  Leave the command where it is */
454 	if (!klass->redo_cmd (cmd, wbc)) {
455 		gboolean redo_cleared;
456 
457 		update_after_action (cmd->sheet, wbc);
458 
459 		/*
460 		 * A few commands clear the undo queue.  For those, we do not
461 		 * want to stuff the cmd object on the redo queue.
462 		 */
463 		redo_cleared = (wb->redo_commands == NULL);
464 
465 		if (!redo_cleared) {
466 			wb->redo_commands = g_slist_remove (wb->redo_commands, cmd);
467 			wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
468 
469 			WORKBOOK_FOREACH_CONTROL (wb, view, control, {
470 				wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
471 				wb_control_undo_redo_pop (control, FALSE);
472 			});
473 			undo_redo_menu_labels (wb);
474 		}
475 	}
476 
477 	g_object_unref (cmd);
478 }
479 
480 /**
481  * command_repeat:
482  * @wbc: The workbook control which issued the request.
483  *
484  *  Repeat the last command (if possible)
485  *
486  *        Any user level errors generated by redoing will be reported
487  *        here.
488  **/
489 void
command_repeat(WorkbookControl * wbc)490 command_repeat (WorkbookControl *wbc)
491 {
492 	GnmCommand *cmd;
493 	GnmCommandClass *klass;
494 	Workbook *wb = wb_control_get_workbook (wbc);
495 
496 	g_return_if_fail (wb);
497 	g_return_if_fail (wb->undo_commands);
498 
499 	cmd = GNM_COMMAND (wb->undo_commands->data);
500 	g_return_if_fail (cmd != NULL);
501 
502 	klass = CMD_CLASS (cmd);
503 	g_return_if_fail (klass != NULL);
504 
505 	if (klass->repeat_cmd != NULL)
506 		(*klass->repeat_cmd) (cmd, wbc);
507 }
508 
509 /**
510  * command_setup_combos:
511  * @wbc:
512  *
513  * Initialize the combos to correspond to the current undo/redo state.
514  */
515 void
command_setup_combos(WorkbookControl * wbc)516 command_setup_combos (WorkbookControl *wbc)
517 {
518 	char const *undo_label = NULL, *redo_label = NULL;
519 	GSList *ptr, *tmp;
520 	Workbook *wb = wb_control_get_workbook (wbc);
521 
522 	g_return_if_fail (wb);
523 
524 	wb_control_undo_redo_truncate (wbc, 0, TRUE);
525 	tmp = g_slist_reverse (wb->undo_commands);
526 	for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
527 		undo_label = get_menu_label (ptr);
528 		wb_control_undo_redo_push (wbc, TRUE, undo_label, ptr->data);
529 	}
530 	if (g_slist_reverse (tmp)) {}	/* ignore, list is in undo_commands */
531 
532 	wb_control_undo_redo_truncate (wbc, 0, FALSE);
533 	tmp = g_slist_reverse (wb->redo_commands);
534 	for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
535 		redo_label = get_menu_label (ptr);
536 		wb_control_undo_redo_push (wbc, FALSE, redo_label, ptr->data);
537 	}
538 	if (g_slist_reverse (tmp)) {}	/* ignore, list is in redo_commands */
539 
540 	/* update the menus too */
541 	wb_control_undo_redo_labels (wbc, undo_label, redo_label);
542 }
543 
544 /**
545  * command_list_release:
546  * @cmds: (element-type GObject): the set of commands to free.
547  *
548  * command_list_release : utility routine to free the resources associated
549  *    with a list of commands.
550  *
551  * NOTE : remember to NULL the list when you are done.
552  */
553 void
command_list_release(GSList * cmd_list)554 command_list_release (GSList *cmd_list)
555 {
556 	while (cmd_list != NULL) {
557 		GObject *cmd = G_OBJECT (cmd_list->data);
558 
559 		g_return_if_fail (cmd != NULL);
560 
561 		g_object_unref (cmd);
562 		cmd_list = g_slist_remove (cmd_list, cmd_list->data);
563 	}
564 }
565 
566 /*
567  * Each undo item has a certain size.  The size of typing a value into
568  * a cell is the unit size.  A large autoformat could have a size of
569  * hundreds or even thousands.
570  *
571  * We wish to have the same undo behaviour across platforms, so please
572  * don't use sizeof in computing the undo size.
573  */
574 
575 #undef DEBUG_TRUNCATE_UNDO
576 
577 /*
578  * Truncate the undo list if it is too big.
579  *
580  * Returns -1 if no truncation was done, or else the number of elements
581  * left.
582  */
583 static int
truncate_undo_info(Workbook * wb)584 truncate_undo_info (Workbook *wb)
585 {
586 	int size_left;
587 	int max_num;
588 	int ok_count;
589 	GSList *l, *prev;
590 
591 	size_left = gnm_conf_get_undo_size ();
592 	max_num   = gnm_conf_get_undo_maxnum ();
593 
594 #ifdef DEBUG_TRUNCATE_UNDO
595 	g_printerr ("Undo sizes:");
596 #endif
597 
598 	for (l = wb->undo_commands, prev = NULL, ok_count = 0;
599 	     l;
600 	     prev = l, l = l->next, ok_count++) {
601 		int min_leave;
602 		GnmCommand *cmd = GNM_COMMAND (l->data);
603 		int size = cmd->size;
604 
605 		if (size < 1) {
606 				/*
607 				 * We could g_assert, but that would cause data loss.
608 				 * Instead, just continue.
609 				 */
610 			g_warning ("Faulty undo_size_func, please report.");
611 			size = 1;
612 		}
613 
614 #ifdef DEBUG_TRUNCATE_UNDO
615 			g_printerr (" %d", size);
616 #endif
617 
618 		/* Keep at least one undo item.  */
619 		if (ok_count >= max_num || (size > size_left && ok_count >= 1)) {
620 			/* Current item is too big; truncate list here.  */
621 			command_list_release (l);
622 			if (prev)
623 				prev->next = NULL;
624 			else
625 				wb->undo_commands = NULL;
626 #ifdef DEBUG_TRUNCATE_UNDO
627 			g_printerr ("[trunc]\n");
628 #endif
629 			return ok_count;
630 		}
631 
632 		/*
633 		 * In order to allow a series of useful small items behind
634 		 * a big item, leave at least 10% of current item's size.
635 		 */
636 		min_leave = size / 10;
637 		size_left = MAX (size_left - size, min_leave);
638 	}
639 
640 #ifdef DEBUG_TRUNCATE_UNDO
641 	g_printerr ("\n");
642 #endif
643 	return -1;
644 }
645 
646 
647 /**
648  * command_register_undo:
649  * @wbc: The workbook control that issued the command.
650  * @cmd: The new command to add.
651  *
652  * An internal utility to tack a new command
653  *    onto the undo list.
654  */
655 static void
command_register_undo(WorkbookControl * wbc,GObject * obj)656 command_register_undo (WorkbookControl *wbc, GObject *obj)
657 {
658 	Workbook *wb;
659 	GnmCommand *cmd;
660 	int undo_trunc;
661 
662 	g_return_if_fail (wbc != NULL);
663 	wb = wb_control_get_workbook (wbc);
664 
665 	cmd = GNM_COMMAND (obj);
666 	g_return_if_fail (cmd != NULL);
667 
668 	command_list_release (wb->redo_commands);
669 	wb->redo_commands = NULL;
670 
671 	g_object_ref (obj); /* keep a ref in case it gets truncated away */
672 	wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
673 	undo_trunc = truncate_undo_info (wb);
674 
675 	WORKBOOK_FOREACH_CONTROL (wb, view, control, {
676 		wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
677 		if (undo_trunc >= 0)
678 			wb_control_undo_redo_truncate (control, undo_trunc, TRUE);
679 		wb_control_undo_redo_truncate (control, 0, FALSE);
680 	});
681 	undo_redo_menu_labels (wb);
682 	g_object_unref (obj);
683 }
684 
685 
686 /**
687  * gnm_command_push_undo:
688  * @wbc: The workbook control that issued the command.
689  * @obj: The new command to add.
690  *
691  *  An internal utility to tack a new command
692  *    onto the undo list.
693  *
694  * Returns: %TRUE if there was a problem, %FALSE otherwise.
695  */
696 
697 
698 /*
699  * cmd_set_text_full
700  *
701  * the caller is expected to have ensured:
702  *
703  * 1) that no array is being split
704  * 2) that the range is not locked.
705  *
706  * Note:
707  * We will free the selection but nothing else.
708  *
709  * Returns: %TRUE if there was a problem, %FALSE otherwise.
710  */
711 gboolean
gnm_command_push_undo(WorkbookControl * wbc,GObject * obj)712 gnm_command_push_undo (WorkbookControl *wbc, GObject *obj)
713 {
714 	gboolean trouble;
715 	GnmCommand *cmd;
716 	GnmCommandClass *klass;
717 
718 	g_return_val_if_fail (wbc != NULL, TRUE);
719 
720 	cmd = GNM_COMMAND (obj);
721 	cmd->state_before_do = go_doc_get_state (wb_control_get_doc (wbc));
722 
723 	g_return_val_if_fail (cmd != NULL, TRUE);
724 
725 	klass = CMD_CLASS (cmd);
726 	g_return_val_if_fail (klass != NULL, TRUE);
727 
728 	/* TRUE indicates a failure to do the command */
729 	trouble = klass->redo_cmd (cmd, wbc);
730 	update_after_action (cmd->sheet, wbc);
731 
732 	if (!trouble)
733 		command_register_undo (wbc, obj);
734 	else
735 		g_object_unref (obj);
736 
737 	return trouble;
738 }
739 
740 /*
741  * command_undo_sheet_delete deletes the sheet without deleting the current cmd.
742  * returns true if it indeed deleted the sheet.
743  * Note: only call this for a sheet of your current workbook from the undo procedure
744  */
745 
746 static gboolean
command_undo_sheet_delete(Sheet * sheet)747 command_undo_sheet_delete (Sheet* sheet)
748 {
749 	Workbook *wb = sheet->workbook;
750 
751         g_return_val_if_fail (IS_SHEET (sheet), FALSE);
752 
753 	if (wb->redo_commands != NULL) {
754 		command_list_release (wb->redo_commands);
755 		wb->redo_commands = NULL;
756 		WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
757 			wb_control_undo_redo_truncate (ctl, 0, FALSE););
758 		undo_redo_menu_labels (wb);
759 	}
760 
761 	workbook_sheet_delete (sheet);
762 
763 	return (TRUE);
764 }
765 
766 /******************************************************************/
767 
768 static GnmValue *
cmd_set_text_full_check_texpr(GnmCellIter const * iter,GnmExprTop const * texpr)769 cmd_set_text_full_check_texpr (GnmCellIter const *iter, GnmExprTop const  *texpr)
770 {
771 	if (iter->cell == NULL ||
772 	    !gnm_expr_top_equal (iter->cell->base.texpr, texpr))
773 		return VALUE_TERMINATE;
774 	return NULL;
775 }
776 
777 static GnmValue *
cmd_set_text_full_check_text(GnmCellIter const * iter,char * text)778 cmd_set_text_full_check_text (GnmCellIter const *iter, char *text)
779 {
780 	char *old_text;
781 	gboolean same;
782 	gboolean quoted = FALSE;
783 
784 	if (gnm_cell_is_blank (iter->cell))
785 		return ((text == NULL || text[0] == '\0') ? NULL : VALUE_TERMINATE);
786 
787 	if (text == NULL || text[0] == '\0')
788 		return VALUE_TERMINATE;
789 
790 	old_text = gnm_cell_get_text_for_editing (iter->cell, NULL, &quoted);
791 	same = g_strcmp0 (old_text, text) == 0;
792 
793 	if (!same && !quoted && iter->cell->value && VALUE_IS_STRING (iter->cell->value)
794 	    && text[0] == '\'')
795 		same = g_strcmp0 (old_text, text + 1) == 0;
796 
797 	g_free (old_text);
798 
799 	return (same ? NULL : VALUE_TERMINATE);
800 }
801 
802 static GnmValue *
cmd_set_text_full_check_markup(GnmCellIter const * iter,PangoAttrList * markup)803 cmd_set_text_full_check_markup (GnmCellIter const *iter, PangoAttrList *markup)
804 {
805 	PangoAttrList const *old_markup = NULL;
806 	gboolean same_markup;
807 
808 	g_return_val_if_fail (iter->cell != NULL, NULL);
809 
810 	if (iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
811 		const GOFormat *fmt = VALUE_FMT (iter->cell->value);
812 		if (fmt && go_format_is_markup (fmt)) {
813 			old_markup = go_format_get_markup (fmt);
814 			if (go_pango_attr_list_is_empty (old_markup))
815 				old_markup = NULL;
816 		}
817 	}
818 
819 	same_markup = gnm_pango_attr_list_equal (old_markup, markup);
820 
821 	return same_markup ? NULL : VALUE_TERMINATE;
822 }
823 
824 /*
825  * cmd_set_text_full
826  *
827  * the caller is expected to have ensured:
828  *
829  * 1) that no array is being split
830  * 2) that the range is not locked.
831  *
832  * Note:
833  * We will free the selection but nothing else.
834  *
835  * Returns: %TRUE if there was a problem, %FALSE otherwise.
836  */
837 static gboolean
cmd_set_text_full(WorkbookControl * wbc,GSList * selection,GnmEvalPos * ep,char const * new_text,PangoAttrList * markup,gboolean autocorrect)838 cmd_set_text_full (WorkbookControl *wbc, GSList *selection, GnmEvalPos *ep,
839 		   char const *new_text, PangoAttrList *markup,
840 		   gboolean autocorrect)
841 {
842 	GSList	*l;
843 	char const *expr_txt;
844 	GnmExprTop const  *texpr = NULL;
845 	GOUndo *undo = NULL;
846 	GOUndo *redo = NULL;
847 	gboolean result, autofit_col = FALSE, same_text_and_not_same_markup = FALSE;
848 	char *text = NULL;
849 	char *name;
850 	Sheet *sheet = ep->sheet;
851 	GnmParsePos pp;
852 	ColRowIndexList *cri_col_list = NULL, *cri_row_list = NULL;
853 	GOFormat const *format = gnm_style_get_format
854 		(sheet_style_get (sheet, ep->eval.col, ep->eval.row));
855 
856 	g_return_val_if_fail (selection != NULL , TRUE);
857 
858 	parse_pos_init_evalpos (&pp, ep);
859 	name = undo_range_list_name (sheet, selection);
860 
861 	if ((format == NULL) || !go_format_is_text (format)) {
862 		expr_txt = gnm_expr_char_start_p (new_text);
863 		if (expr_txt != NULL)
864 			texpr = gnm_expr_parse_str
865 				(expr_txt, &pp, GNM_EXPR_PARSE_DEFAULT,
866 				 sheet_get_conventions (sheet), NULL);
867 	}
868 
869 	if (texpr != NULL) {
870 		GOFormat const *sf;
871 		GnmStyle *new_style = NULL;
872 		gboolean same_texpr = TRUE;
873 
874 		/* We should check whether we are in fact changing anything: */
875 		for (l = selection; l != NULL && same_texpr; l = l->next) {
876 			GnmRange *r = l->data;
877 			GnmValue *val =
878 				sheet_foreach_cell_in_range
879 				(sheet, CELL_ITER_ALL, r,
880 				 (CellIterFunc) cmd_set_text_full_check_texpr,
881 				 (gpointer) texpr);
882 
883 			same_texpr = (val != VALUE_TERMINATE);
884 			if (val != NULL && same_texpr)
885 				value_release (val);
886 		}
887 
888 		if (same_texpr) {
889 			gnm_expr_top_unref (texpr);
890 			g_free (name);
891 			range_fragment_free (selection);
892 			return TRUE;
893 		}
894 
895 		text = g_strdup_printf (_("Inserting expression in %s"), name);
896 
897 		if (go_format_is_general (format)) {
898 			sf = gnm_auto_style_format_suggest (texpr, ep);
899 			if (sf != NULL) {
900 				new_style = gnm_style_new ();
901 				gnm_style_set_format (new_style, sf);
902 				go_format_unref (sf);
903 			}
904 		}
905 
906 		for (l = selection; l != NULL; l = l->next) {
907 			GnmSheetRange *sr;
908 			undo = go_undo_combine
909 				(undo, clipboard_copy_range_undo (sheet, l->data));
910 			sr = gnm_sheet_range_new (sheet, l->data);
911 			redo = go_undo_combine
912 				(redo, sheet_range_set_expr_undo (sr, texpr));
913 			if (new_style) {
914 				sr = gnm_sheet_range_new (sheet, l->data);
915 				redo = go_undo_combine
916 					(redo, sheet_apply_style_undo (sr, new_style));
917 			}
918 		}
919 		if (new_style)
920 			gnm_style_unref (new_style);
921 		gnm_expr_top_unref (texpr);
922 		autofit_col = TRUE;
923 	} else {
924 		GString *text_str;
925 		PangoAttrList *adj_markup = NULL;
926 		char *corrected;
927 		gboolean same_text = TRUE;
928 		gboolean same_markup = TRUE;
929 
930 		if (new_text == NULL)
931 			corrected = NULL;
932 		else if (autocorrect)
933 			corrected = autocorrect_tool (new_text);
934 		else
935 			corrected = g_strdup (new_text);
936 
937 		if (corrected && (corrected[0] == '\'') && corrected[1] == '\0') {
938 			g_free (corrected);
939 			corrected = g_strdup ("");
940 		}
941 
942 		/* We should check whether we are in fact changing anything: */
943 		/* We'll handle */
944 		for (l = selection; l != NULL && same_text; l = l->next) {
945 			GnmRange *r = l->data;
946 			GnmValue *val =
947 				sheet_foreach_cell_in_range
948 				(sheet, CELL_ITER_ALL, r,
949 				 (CellIterFunc) cmd_set_text_full_check_text,
950 				 (gpointer) corrected);
951 
952 			same_text = (val != VALUE_TERMINATE);
953 			if (val != NULL && same_text)
954 				value_release (val);
955 		}
956 
957 		if (go_pango_attr_list_is_empty (markup))
958 			markup = NULL;
959 		if (markup && corrected && corrected[0] == '\'') {
960 			markup = adj_markup = pango_attr_list_copy (markup);
961 			go_pango_attr_list_erase (adj_markup, 0, 1);
962 		}
963 
964 		if (same_text) {
965 			for (l = selection; l != NULL && same_text; l = l->next) {
966 				GnmRange *r = l->data;
967 				GnmValue *val =
968 					sheet_foreach_cell_in_range
969 					(sheet, CELL_ITER_IGNORE_BLANK, r,
970 					 (CellIterFunc) cmd_set_text_full_check_markup,
971 					 (gpointer) markup);
972 
973 				same_markup = (val != VALUE_TERMINATE);
974 				if (val != NULL && same_markup)
975 					value_release (val);
976 			}
977 
978 			if (same_markup) {
979 				g_free (corrected);
980 				g_free (name);
981 				range_fragment_free (selection);
982 				if (adj_markup)
983 					pango_attr_list_unref (adj_markup);
984 				return TRUE;
985 			}
986 
987 			text = g_strdup_printf (_("Editing style of %s"), name);
988 		} else {
989 			text_str = gnm_cmd_trunc_descriptor (g_string_new (corrected), NULL);
990 			text = g_strdup_printf (_("Typing \"%s\" in %s"), text_str->str, name);
991 			g_string_free (text_str, TRUE);
992 		}
993 
994 		for (l = selection; l != NULL; l = l->next) {
995 			GnmSheetRange *sr;
996 			undo = go_undo_combine
997 				(undo, clipboard_copy_range_undo (sheet, l->data));
998 			if (corrected) {
999 				sr = gnm_sheet_range_new (sheet, l->data);
1000 				redo = go_undo_combine
1001 					(redo, sheet_range_set_text_undo
1002 					 (sr, corrected));
1003 			}
1004 			if (markup) {
1005 				sr = gnm_sheet_range_new (sheet, l->data);
1006 				/* Note: order of combination matters!! */
1007 				redo = go_undo_combine
1008 					(sheet_range_set_markup_undo (sr, markup), redo);
1009 			}
1010 		}
1011 
1012 		if (adj_markup)
1013 			pango_attr_list_unref (adj_markup);
1014 		g_free (corrected);
1015 
1016 		same_text_and_not_same_markup = (same_text && !same_markup);
1017 	}
1018 	g_free (name);
1019 
1020 	/* We are combining this since we don't want to apply and undo twice.*/
1021 	if (same_text_and_not_same_markup || !autofit_col) {
1022 		GnmCell *cell = sheet_cell_fetch
1023 			(sheet, ep->eval.col, ep->eval.row);
1024 		gboolean nvis;
1025 
1026 		go_undo_undo (redo);
1027 		nvis = !VALUE_IS_STRING (cell->value);
1028 		if (!autofit_col)
1029 			autofit_col = nvis;
1030 		if (same_text_and_not_same_markup)
1031 			/* We only have to do something if at least one cell           */
1032 			/* now contains a string, but they contain all the same thing. */
1033 			same_text_and_not_same_markup = nvis;
1034 		go_undo_undo (undo);
1035 	}
1036 	if (same_text_and_not_same_markup) {
1037 		/*We had the same text and different markup but we are not entering strings. */
1038 		g_object_unref (undo);
1039 		g_object_unref (redo);
1040 		g_free (text);
1041 		range_fragment_free (selection);
1042 		return TRUE;
1043 	}
1044 	for (l = selection; l != NULL; l = l->next) {
1045 		GnmRange *r = l->data;
1046 		GnmRange *new_r;
1047 
1048 		new_r = g_new (GnmRange, 1);
1049 		*new_r = *r;
1050 		redo  = go_undo_combine
1051 			(go_undo_binary_new
1052 			 (sheet, new_r,
1053 			  (GOUndoBinaryFunc) colrow_autofit_row,
1054 			  NULL, g_free),
1055 			 redo);
1056 		cri_row_list = colrow_get_index_list
1057 			(r->start.row, r->end.row, cri_row_list);
1058 
1059 		if (autofit_col) {
1060 			new_r = g_new (GnmRange, 1);
1061 			*new_r = *r;
1062 			redo  = go_undo_combine
1063 				(go_undo_binary_new
1064 				 (sheet, new_r,
1065 				  (GOUndoBinaryFunc) colrow_autofit_col,
1066 				  NULL, g_free),
1067 				 redo);
1068 			cri_col_list = colrow_get_index_list
1069 				(r->start.col, r->end.col, cri_col_list);
1070 		}
1071 	}
1072 
1073 	undo = go_undo_combine (undo,
1074 				gnm_undo_colrow_restore_state_group_new
1075 				(sheet, TRUE,
1076 				 cri_col_list,
1077 				 colrow_get_sizes (sheet, TRUE,
1078 						   cri_col_list, -1)));
1079 	undo = go_undo_combine (undo,
1080 				gnm_undo_colrow_restore_state_group_new
1081 				(sheet, FALSE,
1082 				 cri_row_list,
1083 				 colrow_get_sizes (sheet, FALSE,
1084 						   cri_row_list, -1)));
1085 
1086 	result = cmd_generic (wbc, text, undo, redo);
1087 	g_free (text);
1088 	range_fragment_free (selection);
1089 	return result;
1090 }
1091 
1092 /*
1093  * cmd_area_set_text
1094  *
1095  * the caller is expected to have ensured:
1096  *
1097  * 1) that no array is being split
1098  * 2) that the range is not locked.
1099  *
1100  * Returns: %TRUE if there was a problem, %FALSE otherwise.
1101  */
1102 gboolean
cmd_area_set_text(WorkbookControl * wbc,SheetView * sv,char const * new_text,PangoAttrList * markup)1103 cmd_area_set_text (WorkbookControl *wbc, SheetView *sv,
1104 		   char const *new_text, PangoAttrList *markup)
1105 {
1106 	GnmEvalPos ep;
1107 	gboolean result;
1108 	GSList *selection = selection_get_ranges (sv, FALSE);
1109 
1110 	eval_pos_init_editpos (&ep, sv);
1111 	result = cmd_set_text_full (wbc, selection, &ep,
1112 				    new_text, markup, TRUE);
1113 	return result;
1114 }
1115 
1116 gboolean
cmd_set_text(WorkbookControl * wbc,Sheet * sheet,GnmCellPos const * pos,char const * new_text,PangoAttrList * markup,gboolean autocorrect)1117 cmd_set_text (WorkbookControl *wbc,
1118 	      Sheet *sheet, GnmCellPos const *pos,
1119 	      char const *new_text,
1120 	      PangoAttrList *markup,
1121 	      gboolean autocorrect)
1122 {
1123 	GnmCell const *cell;
1124 	GnmEvalPos ep;
1125 	gboolean result;
1126 	GSList *selection;
1127 	GnmRange *r;
1128 
1129 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1130 	g_return_val_if_fail (new_text != NULL, TRUE);
1131 
1132 	/* Ensure that we are not splitting up an array */
1133 	cell = sheet_cell_get (sheet, pos->col, pos->row);
1134 	if (gnm_cell_is_nonsingleton_array (cell)) {
1135 		gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc),
1136 			_("Set Text"), NULL);
1137 		return TRUE;
1138 	}
1139 
1140 	eval_pos_init_pos (&ep, sheet, pos);
1141 	r = g_new (GnmRange, 1);
1142 	r->start = r->end = *pos;
1143 	selection = g_slist_prepend (NULL, r);
1144 	result = cmd_set_text_full (wbc, selection, &ep,
1145 				    new_text, markup, autocorrect);
1146 	return result;
1147 }
1148 
1149 
1150 /*
1151  * cmd_area_set_array_expr
1152  *
1153  * the caller is expected to have ensured:
1154  *
1155  * 1) that no array is being split
1156  * 2) that the selection consists of a single range
1157  * 3) that the range is not locked.
1158  *
1159  * Returns: %TRUE if there was a problem, %FALSE otherwise.
1160  */
1161 gboolean
cmd_area_set_array_expr(WorkbookControl * wbc,SheetView * sv,GnmExprTop const * texpr)1162 cmd_area_set_array_expr (WorkbookControl *wbc, SheetView *sv,
1163 			 GnmExprTop const  *texpr)
1164 {
1165 	GSList	*selection = selection_get_ranges (sv, FALSE);
1166 	GOUndo *undo = NULL;
1167 	GOUndo *redo = NULL;
1168 	gboolean result;
1169 	Sheet *sheet = sv_sheet (sv);
1170 	char *name;
1171 	char *text;
1172 	GnmSheetRange *sr;
1173 	GnmRange *r;
1174 
1175 	g_return_val_if_fail (selection != NULL , TRUE);
1176 	g_return_val_if_fail (selection->next == NULL , TRUE);
1177 
1178 	name = undo_range_list_name (sheet, selection);
1179 	text = g_strdup_printf (_("Inserting array expression in %s"), name);
1180 	g_free (name);
1181 
1182 	r = selection->data;
1183 
1184 	undo = clipboard_copy_range_undo (sheet, selection->data);
1185 
1186 	sr = gnm_sheet_range_new (sheet, r);
1187 	redo = gnm_cell_set_array_formula_undo (sr, texpr);
1188 	redo = go_undo_combine
1189 		(go_undo_binary_new
1190 		 (sheet, g_memdup (r, sizeof (*r)),
1191 		  (GOUndoBinaryFunc) colrow_autofit_col,
1192 		  NULL, g_free),
1193 		 redo);
1194 	redo  = go_undo_combine
1195 		(go_undo_binary_new
1196 		 (sheet, g_memdup (r, sizeof (*r)),
1197 		  (GOUndoBinaryFunc) colrow_autofit_row,
1198 		  NULL, g_free),
1199 		 redo);
1200 
1201 	range_fragment_free (selection);
1202 	result = cmd_generic (wbc, text, undo, redo);
1203 	g_free (text);
1204 	return result;
1205 }
1206 
1207 /*
1208  * cmd_create_data_table
1209  *
1210  * the caller is expected to have ensured:
1211  *
1212  * 1) that no array is being split
1213  * 2) that the range is not locked.
1214  *
1215  * Returns: %TRUE if there was a problem, %FALSE otherwise.
1216  */
1217 gboolean
cmd_create_data_table(WorkbookControl * wbc,Sheet * sheet,GnmRange const * r,char const * col_input,char const * row_input)1218 cmd_create_data_table (WorkbookControl *wbc, Sheet *sheet, GnmRange const *r,
1219 		       char const *col_input, char const *row_input)
1220 {
1221 	GOUndo *undo = NULL;
1222 	GOUndo *redo = NULL;
1223 	gboolean result;
1224 	char *name;
1225 	char *text;
1226 	GnmSheetRange *sr;
1227 	GnmParsePos pp;
1228 	GnmExprTop const  *texpr;
1229 
1230 	name = undo_range_name (sheet, r);
1231 	text = g_strdup_printf (_("Creating a Data Table in %s"), name);
1232 	g_free (name);
1233 
1234 	undo = clipboard_copy_range_undo (sheet, r);
1235 
1236 	sr = gnm_sheet_range_new (sheet, r);
1237 	parse_pos_init (&pp, NULL, sheet, r->start.col, r->start.row);
1238 	name = g_strdup_printf ("TABLE(%s,%s)", row_input, col_input);
1239 	texpr = gnm_expr_parse_str
1240 			(name, &pp, GNM_EXPR_PARSE_DEFAULT,
1241 			 sheet_get_conventions (sheet), NULL);
1242 	g_free (name);
1243 
1244 	if (texpr == NULL) {
1245 		g_object_unref (undo);
1246 		g_free (text);
1247 		return TRUE;
1248 	}
1249 
1250 	redo = gnm_cell_set_array_formula_undo (sr, texpr);
1251 	gnm_expr_top_unref (texpr);
1252 
1253 	result = cmd_generic (wbc, text, undo, redo);
1254 	g_free (text);
1255 	return result;
1256 }
1257 
1258 /******************************************************************/
1259 
1260 #define CMD_INS_DEL_COLROW_TYPE        (cmd_ins_del_colrow_get_type ())
1261 #define CMD_INS_DEL_COLROW(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_INS_DEL_COLROW_TYPE, CmdInsDelColRow))
1262 
1263 typedef struct {
1264 	GnmCommand cmd;
1265 
1266 	Sheet		*sheet;
1267 	gboolean	 is_insert;
1268 	gboolean	 is_cols;
1269 	gboolean         is_cut;
1270 	int		 index;
1271 	int		 count;
1272 	GnmRange        *cutcopied;
1273 	SheetView	*cut_copy_view;
1274 
1275 	gboolean       (*redo_action) (Sheet *sheet, int col, int count,
1276 				       GOUndo **pundo, GOCmdContext *cc);
1277 
1278 	gboolean       (*repeat_action) (WorkbookControl *wbc, Sheet *sheet,
1279 					 int start, int count);
1280 
1281 	GOUndo          *undo;
1282 } CmdInsDelColRow;
1283 
1284 static void
cmd_ins_del_colrow_repeat(GnmCommand const * cmd,WorkbookControl * wbc)1285 cmd_ins_del_colrow_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1286 {
1287 	CmdInsDelColRow const *orig = (CmdInsDelColRow const *) cmd;
1288 	SheetView *sv = wb_control_cur_sheet_view (wbc);
1289 	Sheet *sheet = sv_sheet (sv);
1290 	GnmRange const *r = selection_first_range (sv,
1291 		GO_CMD_CONTEXT (wbc), _("Ins/Del Column/Row"));
1292 	int start, count;
1293 
1294 	if (r == NULL)
1295 		return;
1296 
1297 	if (orig->is_cols)
1298 		start = r->start.col, count = range_width (r);
1299 	else
1300 		start = r->start.row, count = range_height (r);
1301 
1302 	orig->repeat_action (wbc, sheet, start, count);
1303 }
1304 
MAKE_GNM_COMMAND(CmdInsDelColRow,cmd_ins_del_colrow,cmd_ins_del_colrow_repeat)1305 MAKE_GNM_COMMAND (CmdInsDelColRow, cmd_ins_del_colrow, cmd_ins_del_colrow_repeat)
1306 
1307 static gboolean
1308 cmd_ins_del_colrow_undo (GnmCommand *cmd, WorkbookControl *wbc)
1309 {
1310 	CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1311 
1312 	if (me->undo) {
1313 		go_undo_undo (me->undo);
1314 		g_object_unref (me->undo);
1315 		me->undo = NULL;
1316 	}
1317 
1318 	/* Ins/Del Row/Col re-ants things completely to account
1319 	 * for the shift of col/rows.
1320 	 */
1321 	if (me->cutcopied != NULL && me->cut_copy_view != NULL)
1322 		gnm_app_clipboard_cut_copy (wbc, me->is_cut, me->cut_copy_view,
1323 					    me->cutcopied, FALSE);
1324 
1325 	return FALSE;
1326 }
1327 
1328 static gboolean
cmd_ins_del_colrow_redo(GnmCommand * cmd,WorkbookControl * wbc)1329 cmd_ins_del_colrow_redo (GnmCommand *cmd, WorkbookControl *wbc)
1330 {
1331 	CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1332 	GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
1333 	int idx = me->index;
1334 	int count = me->count;
1335 
1336 	if (me->redo_action (me->sheet, idx, count, &me->undo, cc)) {
1337 		/* Trouble.  */
1338 		return TRUE;
1339 	}
1340 
1341 	/* Ins/Del Row/Col re-ants things completely to account
1342 	 * for the shift of col/rows. */
1343 	if (me->cutcopied != NULL && me->cut_copy_view != NULL) {
1344 		if (me->is_cut) {
1345 			GnmRange s = *me->cutcopied;
1346 			int key = me->is_insert ? count : -count;
1347 			int threshold = me->is_insert ? idx : idx + 1;
1348 
1349 			/*
1350 			 * Really only applies if the regions that are
1351 			 * inserted/deleted are above the cut/copied region.
1352 			 */
1353 			if (me->is_cols) {
1354 				if (threshold <= s.start.col) {
1355 					s.start.col += key;
1356 					s.end.col   += key;
1357 				}
1358 			} else if (threshold <= s.start.row) {
1359 				s.start.row += key;
1360 				s.end.row   += key;
1361 			}
1362 
1363 			gnm_app_clipboard_cut_copy (wbc, me->is_cut,
1364 						    me->cut_copy_view,
1365 						    &s, FALSE);
1366 		} else
1367 			gnm_app_clipboard_unant ();
1368 	}
1369 
1370 	return FALSE;
1371 }
1372 
1373 static void
cmd_ins_del_colrow_finalize(GObject * cmd)1374 cmd_ins_del_colrow_finalize (GObject *cmd)
1375 {
1376 	CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1377 
1378 	if (me->undo)
1379 		g_object_unref (me->undo);
1380 
1381 	g_free (me->cutcopied);
1382 
1383 	gnm_sheet_view_weak_unref (&(me->cut_copy_view));
1384 
1385 	gnm_command_finalize (cmd);
1386 }
1387 
1388 static gboolean
cmd_ins_del_colrow(WorkbookControl * wbc,Sheet * sheet,gboolean is_cols,gboolean is_insert,char const * descriptor,int index,int count)1389 cmd_ins_del_colrow (WorkbookControl *wbc,
1390 		    Sheet *sheet,
1391 		    gboolean is_cols, gboolean is_insert,
1392 		    char const *descriptor, int index, int count)
1393 {
1394 	CmdInsDelColRow *me;
1395 	int first, last;
1396 	GnmRange r;
1397 
1398 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1399 	g_return_val_if_fail (count > 0, TRUE);
1400 
1401 	me = g_object_new (CMD_INS_DEL_COLROW_TYPE, NULL);
1402 
1403 	me->sheet = sheet;
1404 	me->is_cols = is_cols;
1405 	me->is_insert = is_insert;
1406 	me->index = index;
1407 	me->count = count;
1408 	me->redo_action = me->is_insert
1409 		? (me->is_cols ? sheet_insert_cols : sheet_insert_rows)
1410 		: (me->is_cols ? sheet_delete_cols : sheet_delete_rows);
1411 	me->repeat_action = me->is_insert
1412 		? (me->is_cols ? cmd_insert_cols : cmd_insert_rows)
1413 		: (me->is_cols ? cmd_delete_cols : cmd_delete_rows);
1414 
1415 	/* Range that will get deleted. */
1416 	first = me->is_insert
1417 		? colrow_max (is_cols, sheet) - count
1418 		: index;
1419 	last = first + count - 1;
1420 	(is_cols ? range_init_cols : range_init_rows) (&r, sheet, first, last);
1421 
1422 	/* Note: redo_action checks for array subdivision.  */
1423 
1424 	/* Check for locks */
1425 	if (cmd_cell_range_is_locked_effective (sheet, &r, wbc, descriptor)) {
1426 		g_object_unref (me);
1427 		return TRUE;
1428 	}
1429 
1430 	/* We store the cut or/copied range if applicable */
1431 	if (!gnm_app_clipboard_is_empty () &&
1432 	    gnm_app_clipboard_area_get () &&
1433 	    sheet == gnm_app_clipboard_sheet_get ()) {
1434 		me->cutcopied = gnm_range_dup (gnm_app_clipboard_area_get ());
1435 		me->is_cut    = gnm_app_clipboard_is_cut ();
1436 		gnm_sheet_view_weak_ref (gnm_app_clipboard_sheet_view_get (),
1437 			&(me->cut_copy_view));
1438 	} else
1439 		me->cutcopied = NULL;
1440 
1441 	me->cmd.sheet = sheet;
1442 	me->cmd.size = count * 10;  /* FIXME?  */
1443 	me->cmd.cmd_descriptor = descriptor;
1444 
1445 	return gnm_command_push_undo (wbc, G_OBJECT (me));
1446 }
1447 
1448 gboolean
cmd_insert_cols(WorkbookControl * wbc,Sheet * sheet,int start_col,int count)1449 cmd_insert_cols (WorkbookControl *wbc,
1450 		 Sheet *sheet, int start_col, int count)
1451 {
1452 	char *mesg;
1453 	GnmRange r;
1454 
1455 	range_init_full_sheet (&r, sheet);
1456 	r.start.col = r.end.col - count + 1;
1457 
1458 	if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1459 		go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1460 				      ngettext ("Inserting %i column before column %s would push data off the sheet. "
1461 						"Please enlarge the sheet first.",
1462 						"Inserting %i columns before column %s would push data off the sheet. "
1463 						"Please enlarge the sheet first.",
1464 						count),
1465 				      count, col_name (start_col));
1466 		return TRUE;
1467 	}
1468 
1469 	mesg  = g_strdup_printf
1470 		(ngettext ("Inserting %d column before %s",
1471 			   "Inserting %d columns before %s",
1472 			   count),
1473 		 count, col_name (start_col));
1474 	return cmd_ins_del_colrow (wbc, sheet, TRUE, TRUE, mesg, start_col, count);
1475 }
1476 
1477 gboolean
cmd_insert_rows(WorkbookControl * wbc,Sheet * sheet,int start_row,int count)1478 cmd_insert_rows (WorkbookControl *wbc,
1479 		 Sheet *sheet, int start_row, int count)
1480 {
1481 	char *mesg;
1482 	GnmRange r;
1483 
1484 	range_init_full_sheet (&r, sheet);
1485 	r.start.row = r.end.row - count + 1;
1486 
1487 	if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1488 		go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1489 				      ngettext ("Inserting %i row before row %s would push data off the sheet. "
1490 						"Please enlarge the sheet first.",
1491 						"Inserting %i rows before row %s would push data off the sheet. "
1492 						"Please enlarge the sheet first.",
1493 						count),
1494 				      count, row_name (start_row));
1495 		return TRUE;
1496 	}
1497 
1498 	mesg = g_strdup_printf
1499 		(ngettext ("Inserting %d row before %s",
1500 			   "Inserting %d rows before %s",
1501 			   count),
1502 		 count, row_name (start_row));
1503 	return cmd_ins_del_colrow (wbc, sheet, FALSE, TRUE, mesg, start_row, count);
1504 }
1505 
1506 gboolean
cmd_delete_cols(WorkbookControl * wbc,Sheet * sheet,int start_col,int count)1507 cmd_delete_cols (WorkbookControl *wbc,
1508 		 Sheet *sheet, int start_col, int count)
1509 {
1510 	char *mesg = g_strdup_printf ((count > 1)
1511 				      ? _("Deleting columns %s")
1512 				      : _("Deleting column %s"),
1513 				      cols_name (start_col, start_col + count - 1));
1514 	return cmd_ins_del_colrow (wbc, sheet, TRUE, FALSE, mesg, start_col, count);
1515 }
1516 
1517 gboolean
cmd_delete_rows(WorkbookControl * wbc,Sheet * sheet,int start_row,int count)1518 cmd_delete_rows (WorkbookControl *wbc,
1519 		 Sheet *sheet, int start_row, int count)
1520 {
1521 	char *mesg = g_strdup_printf ((count > 1)
1522 				      ? _("Deleting rows %s")
1523 				      : _("Deleting row %s"),
1524 				      rows_name (start_row, start_row + count - 1));
1525 	return cmd_ins_del_colrow (wbc, sheet, FALSE, FALSE, mesg, start_row, count);
1526 }
1527 
1528 /******************************************************************/
1529 
1530 typedef struct {
1531 	GSList	  *selection;
1532 	GnmRange const *r;
1533 } cmd_selection_clear_row_handler_t;
1534 
1535 static gboolean
cmd_selection_clear_row_handler(GnmColRowIter const * iter,cmd_selection_clear_row_handler_t * data)1536 cmd_selection_clear_row_handler (GnmColRowIter const *iter,
1537 				 cmd_selection_clear_row_handler_t *data)
1538 {
1539 	if ((!iter->cri->in_filter) || iter->cri->visible) {
1540 		GnmRange *r = gnm_range_dup (data->r);
1541 		r->start.row = r->end.row = iter->pos;
1542 		data->selection = g_slist_prepend (data->selection, r);
1543 	}
1544 	return FALSE;
1545 }
1546 
1547 gboolean
cmd_selection_clear(WorkbookControl * wbc,int clear_flags)1548 cmd_selection_clear (WorkbookControl *wbc, int clear_flags)
1549 {
1550 	char *names, *descriptor;
1551 	GString *types;
1552 	SheetView *sv = wb_control_cur_sheet_view (wbc);
1553 	GSList	  *selection = selection_get_ranges (sv, FALSE /* No intersection */);
1554 	Sheet *sheet = sv_sheet (sv);
1555 	gboolean result;
1556 	int size;
1557 	GOUndo *undo = NULL;
1558 	GOUndo *redo = NULL;
1559 	GSList *ranges;
1560 
1561 	if ((clear_flags & CLEAR_FILTERED_ONLY) != 0 && sheet->filters != NULL) {
1562 		/* We need to modify the selection to only include filtered rows. */
1563 		cmd_selection_clear_row_handler_t data;
1564 		data.selection = selection;
1565 		for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1566 			GnmFilter *filter;
1567 			data.r = ranges->data;
1568 			filter = gnm_sheet_filter_intersect_rows
1569 				(sheet, data.r->start.row, data.r->end.row);
1570 			if (filter) {
1571 				sheet_colrow_foreach (sheet, FALSE,
1572 						      data.r->start.row,
1573 						      data.r->end.row,
1574 						      (ColRowHandler) cmd_selection_clear_row_handler,
1575 						      &data);
1576 				g_free (ranges->data);
1577 				ranges->data = NULL;
1578 			}
1579 		}
1580 		selection = g_slist_remove_all (data.selection, NULL);
1581 	}
1582 
1583 	/* We should first determine whether we break anything by clearing */
1584 	/* Check for array subdivision *//* Check for locked cells */
1585 	if (sheet_ranges_split_region (sheet, selection,
1586 				       GO_CMD_CONTEXT (wbc), _("Clear")) ||
1587 	    cmd_selection_is_locked_effective (sheet, selection, wbc, _("Clear"))) {
1588 		range_fragment_free (selection);
1589 		return TRUE;
1590 	}
1591 
1592 
1593 	/* We now need to build the descriptor */
1594 	/* Collect clear types for descriptor */
1595 	if (clear_flags != (CLEAR_VALUES | CLEAR_FORMATS | CLEAR_COMMENTS)) {
1596 		GSList *m, *l = NULL;
1597 		types = g_string_new (NULL);
1598 		if (clear_flags & CLEAR_VALUES)
1599 			l = g_slist_append (l, g_string_new (_("contents")));
1600 		if (clear_flags & CLEAR_FORMATS)
1601 			l = g_slist_append (l, g_string_new (_("formats")));
1602 		if (clear_flags & CLEAR_COMMENTS)
1603 			l = g_slist_append (l, g_string_new (_("comments")));
1604 		/* Using a list for this may seem overkill, but is really the only
1605 		 * right way to do this
1606 		 */
1607 		for (m = l; m != NULL; m = m->next) {
1608 			GString *s = m->data;
1609 
1610 			g_string_append_len (types, s->str, s->len);
1611 			g_string_free (s, TRUE);
1612 
1613 			if (m->next)
1614 				g_string_append (types, ", ");
1615 		}
1616 		g_slist_free (l);
1617 	} else
1618 		types = g_string_new (_("all"));
1619 	/* The range name string will automatically be truncated, we don't
1620 	 * need to truncate the "types" list because it will not grow
1621 	 * indefinitely
1622 	 */
1623 	names = undo_range_list_name (sheet, selection);
1624 	descriptor = g_strdup_printf (_("Clearing %s in %s"), types->str, names);
1625 	g_free (names);
1626 	g_string_free (types, TRUE);
1627 	size = g_slist_length (selection);
1628 
1629 	clear_flags |= CLEAR_NOCHECKARRAY;
1630 
1631 	if (clear_flags & (CLEAR_VALUES | CLEAR_FORMATS))
1632 		clear_flags |= CLEAR_RECALC_DEPS;
1633 
1634 	/* We are now ready to build the redo and undo items */
1635 	for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1636 		GnmRange const *r = ranges->data;
1637 		GnmSheetRange *sr = gnm_sheet_range_new (sheet, r);
1638 
1639 		undo = go_undo_combine (undo, clipboard_copy_range_undo (sheet, r));
1640 		redo =  go_undo_combine
1641 			(redo, sheet_clear_region_undo
1642 			 (sr, clear_flags));
1643 	}
1644 
1645 	range_fragment_free (selection);
1646 
1647 	result = cmd_generic_with_size (wbc, descriptor, size, undo, redo);
1648 	g_free (descriptor);
1649 
1650 	return result;
1651 }
1652 
1653 /******************************************************************/
1654 
1655 #define CMD_FORMAT_TYPE        (cmd_format_get_type ())
1656 #define CMD_FORMAT(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FORMAT_TYPE, CmdFormat))
1657 
1658 typedef struct {
1659 	GnmCellPos pos;
1660 	GnmStyleList *styles;
1661 	ColRowIndexList *rows;
1662 	ColRowStateGroup *old_heights;
1663 } CmdFormatOldStyle;
1664 
1665 typedef struct {
1666 	GnmCommand cmd;
1667 	GSList	   *selection;
1668 	GSList	   *old_styles;
1669 	GnmStyle   *new_style;
1670 	GnmBorder **borders;
1671 } CmdFormat;
1672 
1673 static void
cmd_format_repeat(GnmCommand const * cmd,WorkbookControl * wbc)1674 cmd_format_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1675 {
1676 	CmdFormat const *orig = (CmdFormat const *) cmd;
1677 	int i;
1678 
1679 	if (orig->new_style)
1680 		gnm_style_ref (orig->new_style);
1681 	if (orig->borders)
1682 		for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1683 			gnm_style_border_ref (orig->borders [i]);
1684 
1685 	cmd_selection_format (wbc, orig->new_style, orig->borders, NULL);
1686 }
MAKE_GNM_COMMAND(CmdFormat,cmd_format,cmd_format_repeat)1687 MAKE_GNM_COMMAND (CmdFormat, cmd_format, cmd_format_repeat)
1688 
1689 static gboolean
1690 cmd_format_undo (GnmCommand *cmd,
1691 		 G_GNUC_UNUSED WorkbookControl *wbc)
1692 {
1693 	CmdFormat *me = CMD_FORMAT (cmd);
1694 
1695 	g_return_val_if_fail (me != NULL, TRUE);
1696 
1697 	if (me->old_styles) {
1698 		GSList *rstyles = g_slist_reverse (g_slist_copy (me->old_styles));
1699 		GSList *rsel = g_slist_reverse (g_slist_copy (me->selection));
1700 		GSList *l1, *l2;
1701 
1702 		for (l1 = rstyles, l2 = rsel; l1; l1 = l1->next, l2 = l2->next) {
1703 			CmdFormatOldStyle *os = l1->data;
1704 			GnmRange const *r = l2->data;
1705 			GnmSpanCalcFlags flags = sheet_style_set_list
1706 				(me->cmd.sheet,
1707 				 &os->pos, os->styles, NULL, NULL);
1708 
1709 			if (os->old_heights) {
1710 				colrow_restore_state_group (me->cmd.sheet, FALSE,
1711 							    os->rows,
1712 							    os->old_heights);
1713 				colrow_state_group_destroy (os->old_heights);
1714 				os->old_heights = NULL;
1715 				colrow_index_list_destroy (os->rows);
1716 				os->rows = NULL;
1717 			}
1718 
1719 			sheet_range_calc_spans (me->cmd.sheet, r, flags);
1720 			sheet_flag_style_update_range (me->cmd.sheet, r);
1721 		}
1722 
1723 		sheet_redraw_all (me->cmd.sheet, FALSE);
1724 		g_slist_free (rstyles);
1725 		g_slist_free (rsel);
1726 	}
1727 
1728 	select_selection (me->cmd.sheet, me->selection, wbc);
1729 
1730 	return FALSE;
1731 }
1732 
1733 static gboolean
cmd_format_redo(GnmCommand * cmd,WorkbookControl * wbc)1734 cmd_format_redo (GnmCommand *cmd, WorkbookControl *wbc)
1735 {
1736 	CmdFormat *me = CMD_FORMAT (cmd);
1737 	GSList *l1, *l2;
1738 	gboolean re_fit_height;
1739 
1740 	g_return_val_if_fail (me != NULL, TRUE);
1741 
1742 	/* Check for locked cells */
1743 	if (cmd_selection_is_locked_effective (me->cmd.sheet, me->selection,
1744 					       wbc, _("Changing Format")))
1745 		return TRUE;
1746 
1747 	re_fit_height =	me->new_style &&
1748 		(GNM_SPANCALC_ROW_HEIGHT & gnm_style_required_spanflags (me->new_style));
1749 
1750 	for (l1 = me->old_styles, l2 = me->selection; l2; l1 = l1->next, l2 = l2->next) {
1751 		CmdFormatOldStyle *os = l1->data;
1752 		GnmRange const *r = l2->data;
1753 
1754 		if (me->borders)
1755 			sheet_apply_border (me->cmd.sheet, r, me->borders);
1756 		if (me->new_style) {
1757 			gnm_style_ref (me->new_style);
1758 			sheet_apply_style (me->cmd.sheet, r, me->new_style);
1759 			if (re_fit_height)
1760 				colrow_autofit (me->cmd.sheet, r, FALSE, FALSE,
1761 						TRUE, FALSE,
1762 						&os->rows, &os->old_heights);
1763 		}
1764 
1765 		sheet_flag_style_update_range (me->cmd.sheet, r);
1766 	}
1767 	sheet_redraw_all (me->cmd.sheet, FALSE);
1768 	sheet_mark_dirty (me->cmd.sheet);
1769 
1770 	select_selection (me->cmd.sheet, me->selection, wbc);
1771 
1772 	return FALSE;
1773 }
1774 
1775 static void
cmd_format_finalize(GObject * cmd)1776 cmd_format_finalize (GObject *cmd)
1777 {
1778 	CmdFormat *me = CMD_FORMAT (cmd);
1779 	int        i;
1780 
1781 	if (me->new_style)
1782 		gnm_style_unref (me->new_style);
1783 	me->new_style = NULL;
1784 
1785 	if (me->borders) {
1786 		for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1787 			gnm_style_border_unref (me->borders [i]);
1788 		g_free (me->borders);
1789 		me->borders = NULL;
1790 	}
1791 
1792 	if (me->old_styles != NULL) {
1793 		GSList *l;
1794 
1795 		for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
1796 			CmdFormatOldStyle *os = l->data;
1797 
1798 			style_list_free (os->styles);
1799 			colrow_index_list_destroy (os->rows);
1800 			colrow_state_group_destroy (os->old_heights);
1801 			g_free (os);
1802 		}
1803 		me->old_styles = NULL;
1804 	}
1805 
1806 	range_fragment_free (me->selection);
1807 	me->selection = NULL;
1808 
1809 	gnm_command_finalize (cmd);
1810 }
1811 
1812 /**
1813  * cmd_format: (skip)
1814  * @wbc: the workbook control.
1815  * @sheet: the sheet
1816  * @style: (transfer full): style to apply to the selection
1817  * @borders: (nullable) (transfer full): borders to apply to the selection
1818  * @opt_translated_name: An optional name to use in place of 'Format Cells'
1819  *
1820  * If borders is non-%NULL, then the GnmBorder references are passed,
1821  * the GnmStyle reference is also passed.
1822  *
1823  * Returns: %TRUE if there was a problem, %FALSE otherwise.
1824  **/
1825 gboolean
cmd_selection_format(WorkbookControl * wbc,GnmStyle * style,GnmBorder ** borders,char const * opt_translated_name)1826 cmd_selection_format (WorkbookControl *wbc,
1827 		      GnmStyle *style, GnmBorder **borders,
1828 		      char const *opt_translated_name)
1829 {
1830 	CmdFormat *me;
1831 	GSList    *l;
1832 	SheetView *sv = wb_control_cur_sheet_view (wbc);
1833 
1834 	me = g_object_new (CMD_FORMAT_TYPE, NULL);
1835 
1836 	me->selection  = selection_get_ranges (sv, FALSE); /* TRUE ? */
1837 	me->new_style  = style;
1838 
1839 	me->cmd.sheet = sv_sheet (sv);
1840 	me->cmd.size = 1;  /* Updated below.  */
1841 
1842 	me->old_styles = NULL;
1843 	for (l = me->selection; l; l = l->next) {
1844 		GnmRange const *sel_r = l->data;
1845 		GnmRange range = *sel_r;
1846 		CmdFormatOldStyle *os;
1847 
1848 		/* Store the containing range to handle borders */
1849 		if (borders != NULL) {
1850 			if (range.start.col > 0) range.start.col--;
1851 			if (range.start.row > 0) range.start.row--;
1852 			if (range.end.col < gnm_sheet_get_last_col (me->cmd.sheet)) range.end.col++;
1853 			if (range.end.row < gnm_sheet_get_last_row (me->cmd.sheet)) range.end.row++;
1854 		}
1855 
1856 		os = g_new (CmdFormatOldStyle, 1);
1857 
1858 		os->styles = sheet_style_get_range (me->cmd.sheet, &range);
1859 		os->pos = range.start;
1860 		os->rows = NULL;
1861 		os->old_heights = NULL;
1862 
1863 		me->cmd.size += g_slist_length (os->styles);
1864 		me->old_styles = g_slist_append (me->old_styles, os);
1865 	}
1866 
1867 	if (borders) {
1868 		int i;
1869 
1870 		me->borders = g_new (GnmBorder *, GNM_STYLE_BORDER_EDGE_MAX);
1871 		for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1872 			me->borders [i] = borders [i];
1873 	} else
1874 		me->borders = NULL;
1875 
1876 	if (opt_translated_name == NULL) {
1877 		char *names = undo_range_list_name (me->cmd.sheet, me->selection);
1878 
1879 		me->cmd.cmd_descriptor = g_strdup_printf (_("Changing format of %s"), names);
1880 		g_free (names);
1881 	} else
1882 		me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
1883 
1884 	return gnm_command_push_undo (wbc, G_OBJECT (me));
1885 }
1886 
1887 /******************************************************************/
1888 
1889 static gboolean
cmd_selection_format_toggle_font_style_filter(PangoAttribute * attribute,PangoAttrType * pt)1890 cmd_selection_format_toggle_font_style_filter (PangoAttribute *attribute, PangoAttrType *pt)
1891 {
1892 	return ((attribute->klass->type == *pt) ||
1893 		((PANGO_ATTR_RISE == *pt) && (attribute->klass->type == PANGO_ATTR_SCALE)));
1894 }
1895 
1896 typedef struct {
1897 	GOUndo *undo;
1898 	PangoAttrType pt;
1899 } csftfs;
1900 
1901 static GnmValue *
cmd_selection_format_toggle_font_style_cb(GnmCellIter const * iter,csftfs * closure)1902 cmd_selection_format_toggle_font_style_cb (GnmCellIter const *iter, csftfs *closure)
1903 {
1904 	if (iter->cell && iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
1905 		const GOFormat *fmt = VALUE_FMT (iter->cell->value);
1906 		if (fmt && go_format_is_markup (fmt)) {
1907 			const PangoAttrList *old_markup =
1908 				go_format_get_markup (fmt);
1909 			PangoAttrList *new_markup = pango_attr_list_copy ((PangoAttrList *)old_markup);
1910 			PangoAttrList *other = pango_attr_list_filter
1911 				(new_markup,
1912 				 (PangoAttrFilterFunc) cmd_selection_format_toggle_font_style_filter,
1913 				 &closure->pt);
1914 			if (other != NULL) {
1915 				GnmSheetRange *sr;
1916 				GnmRange r;
1917 				range_init_cellpos (&r, &iter->pp.eval);
1918 				sr = gnm_sheet_range_new (iter->pp.sheet, &r);
1919 				closure->undo = go_undo_combine (closure->undo,
1920 								 sheet_range_set_markup_undo (sr, new_markup));
1921 			}
1922 			pango_attr_list_unref (new_markup);
1923 			pango_attr_list_unref (other);
1924 		}
1925 	}
1926 	return NULL;
1927 }
1928 
1929 gboolean
cmd_selection_format_toggle_font_style(WorkbookControl * wbc,GnmStyle * style,GnmStyleElement t)1930 cmd_selection_format_toggle_font_style (WorkbookControl *wbc,
1931 					GnmStyle *style, GnmStyleElement t)
1932 {
1933 	SheetView *sv = wb_control_cur_sheet_view (wbc);
1934 	Sheet *sheet = sv->sheet;
1935 	GSList	*selection = selection_get_ranges (sv, FALSE), *l;
1936 	gboolean result;
1937 	char *text, *name;
1938 	GOUndo *undo = NULL;
1939 	GOUndo *redo = NULL;
1940 	PangoAttrType pt;
1941 
1942 
1943 	switch (t) {
1944 	case MSTYLE_FONT_BOLD:
1945 		pt = PANGO_ATTR_WEIGHT;
1946 		break;
1947 	case MSTYLE_FONT_ITALIC:
1948 		pt = PANGO_ATTR_STYLE;
1949 		break;
1950 	case MSTYLE_FONT_UNDERLINE:
1951 		pt = PANGO_ATTR_UNDERLINE;
1952 		break;
1953 	case MSTYLE_FONT_STRIKETHROUGH:
1954 		pt = PANGO_ATTR_STRIKETHROUGH;
1955 		break;
1956 	case MSTYLE_FONT_SCRIPT:
1957 		pt = PANGO_ATTR_RISE; /* and PANGO_ATTR_SCALE (see  ) */
1958 		break;
1959 	default:
1960 		pt = PANGO_ATTR_INVALID;
1961 		break;
1962 	}
1963 
1964 
1965 	name = undo_range_list_name (sheet, selection);
1966 	text = g_strdup_printf (_("Setting Font Style of %s"), name);
1967 	g_free (name);
1968 
1969 	for (l = selection; l != NULL; l = l->next) {
1970 		GnmSheetRange *sr;
1971 		undo = go_undo_combine
1972 			(undo, clipboard_copy_range_undo (sheet, l->data));
1973 		sr = gnm_sheet_range_new (sheet, l->data);
1974 		redo = go_undo_combine
1975 			(redo, sheet_apply_style_undo (sr, style));
1976 		if (pt != PANGO_ATTR_INVALID) {
1977 			csftfs closure;
1978 			closure.undo = NULL;
1979 			closure.pt = pt;
1980 			sheet_foreach_cell_in_range
1981 				(sheet, CELL_ITER_IGNORE_BLANK, &sr->range,
1982 				 (CellIterFunc) cmd_selection_format_toggle_font_style_cb,
1983 				 &closure);
1984 			redo = go_undo_combine (redo, closure.undo);
1985 		}
1986 	}
1987 	gnm_style_unref (style);
1988 	result = cmd_generic (wbc, text, undo, redo);
1989 	g_free (text);
1990 	range_fragment_free (selection);
1991 
1992 	return result;
1993 }
1994 
1995 
1996 /******************************************************************/
1997 
1998 
1999 gboolean
cmd_resize_colrow(WorkbookControl * wbc,Sheet * sheet,gboolean is_cols,ColRowIndexList * selection,int new_size)2000 cmd_resize_colrow (WorkbookControl *wbc, Sheet *sheet,
2001 		   gboolean is_cols, ColRowIndexList *selection,
2002 		   int new_size)
2003 {
2004 	int size = 1;
2005 	char *text;
2006 	GOUndo *undo = NULL;
2007 	GOUndo *redo = NULL;
2008 	gboolean is_single, result;
2009 	GString *list;
2010 	ColRowStateGroup *saved_state;
2011 
2012 	list = colrow_index_list_to_string (selection, is_cols, &is_single);
2013 	gnm_cmd_trunc_descriptor (list, NULL);
2014 
2015 	if (is_single) {
2016 		if (new_size < 0)
2017 			text = is_cols
2018 				? g_strdup_printf (_("Autofitting column %s"), list->str)
2019 				: g_strdup_printf (_("Autofitting row %s"), list->str);
2020 		else if (new_size >  0)
2021 			text = is_cols
2022 				? g_strdup_printf (ngettext ("Setting width of column %s to %d pixel",
2023 							"Setting width of column %s to %d pixels",
2024 							new_size),
2025 						   list->str, new_size)
2026 				: g_strdup_printf (ngettext ("Setting height of row %s to %d pixel",
2027 							"Setting height of row %s to %d pixels",
2028 							new_size),
2029 						   list->str, new_size);
2030 		else text = is_cols
2031 			     ? g_strdup_printf (_("Setting width of column %s to default"),
2032 						list->str)
2033 			     : g_strdup_printf (
2034 				     _("Setting height of row %s to default"), list->str);
2035 	} else {
2036 		if (new_size < 0)
2037 			text = is_cols
2038 				? g_strdup_printf (_("Autofitting columns %s"), list->str)
2039 				: g_strdup_printf (_("Autofitting rows %s"), list->str);
2040 		else if (new_size >  0)
2041 			text = is_cols
2042 				? g_strdup_printf (ngettext("Setting width of columns %s to %d pixel",
2043 							"Setting width of columns %s to %d pixels",
2044 							new_size),
2045 						   list->str, new_size)
2046 				: g_strdup_printf (ngettext("Setting height of rows %s to %d pixel",
2047 							"Setting height of rows %s to %d pixels",
2048 							new_size),
2049 						   list->str, new_size);
2050 		else text = is_cols
2051 			     ? g_strdup_printf (
2052 				     _("Setting width of columns %s to default"), list->str)
2053 			     : g_strdup_printf (
2054 				     _("Setting height of rows %s to default"), list->str);
2055 	}
2056 	g_string_free (list, TRUE);
2057 
2058 	saved_state = colrow_get_sizes (sheet, is_cols, selection, new_size);
2059 	undo = gnm_undo_colrow_restore_state_group_new
2060 		(sheet, is_cols, colrow_index_list_copy (selection), saved_state);
2061 
2062  	redo = gnm_undo_colrow_set_sizes_new (sheet, is_cols, selection, new_size, NULL);
2063 
2064 	result = cmd_generic_with_size (wbc, text, size, undo, redo);
2065 	g_free (text);
2066 
2067 	return result;
2068 }
2069 
2070 gboolean
cmd_autofit_selection(WorkbookControl * wbc,SheetView * sv,Sheet * sheet,gboolean fit_width,ColRowIndexList * selectionlist)2071 cmd_autofit_selection (WorkbookControl *wbc, SheetView *sv, Sheet *sheet, gboolean fit_width,
2072 		       ColRowIndexList *selectionlist)
2073 {
2074 	GOUndo *undo = NULL;
2075 	GOUndo *redo = NULL;
2076 	gboolean result;
2077 	ColRowStateGroup *saved_state;
2078 	GSList *l, *selection = selection_get_ranges (sv, TRUE);
2079 	gchar *names = undo_range_list_name (sheet, selection);
2080 	gchar const *format = fit_width ?
2081 		N_("Autofitting width of %s") : N_("Autofitting height of %s");
2082 	gchar *text = g_strdup_printf (_(format), names);
2083 
2084 	g_free (names);
2085 
2086 	saved_state = colrow_get_sizes (sheet, fit_width, selectionlist, -1);
2087 	undo = gnm_undo_colrow_restore_state_group_new
2088 		(sheet, fit_width, colrow_index_list_copy (selectionlist), saved_state);
2089 
2090 	for (l = selection; l != NULL; l = l->next)
2091 		redo = go_undo_combine
2092 			(redo, gnm_undo_colrow_set_sizes_new
2093 			 (sheet, fit_width, NULL, -1, l->data));
2094 
2095 	result = cmd_generic (wbc, text, undo, redo);
2096 	g_free (text);
2097 	return result;
2098 }
2099 
2100 
2101 /******************************************************************/
2102 
2103 #define CMD_SORT_TYPE        (cmd_sort_get_type ())
2104 #define CMD_SORT(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SORT_TYPE, CmdSort))
2105 
2106 typedef struct {
2107 	GnmCommand cmd;
2108 
2109 	GnmSortData *data;
2110 	int         *perm;
2111 	GnmCellRegion *old_contents;
2112 } CmdSort;
2113 
MAKE_GNM_COMMAND(CmdSort,cmd_sort,NULL)2114 MAKE_GNM_COMMAND (CmdSort, cmd_sort, NULL)
2115 
2116 static void
2117 cmd_sort_finalize (GObject *cmd)
2118 {
2119 	CmdSort *me = CMD_SORT (cmd);
2120 
2121 	if (me->data != NULL)
2122 		gnm_sort_data_destroy (me->data);
2123 	g_free (me->perm);
2124 	if (me->old_contents != NULL)
2125 		cellregion_unref (me->old_contents);
2126 
2127 	gnm_command_finalize (cmd);
2128 }
2129 
2130 static gboolean
cmd_sort_undo(GnmCommand * cmd,WorkbookControl * wbc)2131 cmd_sort_undo (GnmCommand *cmd, WorkbookControl *wbc)
2132 {
2133 	CmdSort *me = CMD_SORT (cmd);
2134 	GnmSortData *data = me->data;
2135 	GnmPasteTarget pt;
2136 
2137 	paste_target_init (&pt, data->sheet, data->range,
2138 			   PASTE_CONTENTS | PASTE_FORMATS | PASTE_COMMENTS |
2139 			   (data->retain_formats ? PASTE_FORMATS : 0));
2140 	clipboard_paste_region (me->old_contents,
2141 				&pt,
2142 				GO_CMD_CONTEXT (wbc));
2143 
2144 	return FALSE;
2145 }
2146 
2147 static gboolean
cmd_sort_redo(GnmCommand * cmd,WorkbookControl * wbc)2148 cmd_sort_redo (GnmCommand *cmd, WorkbookControl *wbc)
2149 {
2150 	CmdSort *me = CMD_SORT (cmd);
2151 	GnmSortData *data = me->data;
2152 
2153 	/* Check for locks */
2154 	if (cmd_cell_range_is_locked_effective
2155 	    (data->sheet, data->range, wbc, _("Sorting")))
2156 		return TRUE;
2157 
2158 	if (me->perm)
2159 		gnm_sort_position (data, me->perm, GO_CMD_CONTEXT (wbc));
2160 	else {
2161 		me->old_contents =
2162 			clipboard_copy_range (data->sheet, data->range);
2163 		me->cmd.size = cellregion_cmd_size (me->old_contents);
2164 		me->perm = gnm_sort_contents (data, GO_CMD_CONTEXT (wbc));
2165 	}
2166 
2167 	return FALSE;
2168 }
2169 
2170 gboolean
cmd_sort(WorkbookControl * wbc,GnmSortData * data)2171 cmd_sort (WorkbookControl *wbc, GnmSortData *data)
2172 {
2173 	CmdSort *me;
2174 	char *desc;
2175 
2176 	g_return_val_if_fail (data != NULL, TRUE);
2177 
2178 	desc = g_strdup_printf (_("Sorting %s"), range_as_string (data->range));
2179 	if (sheet_range_contains_merges_or_arrays (data->sheet, data->range, GO_CMD_CONTEXT (wbc), desc, TRUE, TRUE)) {
2180 		gnm_sort_data_destroy (data);
2181 		g_free (desc);
2182 		return TRUE;
2183 	}
2184 
2185 	me = g_object_new (CMD_SORT_TYPE, NULL);
2186 
2187 	me->data = data;
2188 	me->perm = NULL;
2189 	me->cmd.sheet = data->sheet;
2190 	me->cmd.size = 1;  /* Changed in initial redo.  */
2191 	me->cmd.cmd_descriptor = desc;
2192 
2193 	return gnm_command_push_undo (wbc, G_OBJECT (me));
2194 }
2195 
2196 /******************************************************************/
2197 
2198 #define CMD_COLROW_HIDE_TYPE        (cmd_colrow_hide_get_type ())
2199 #define CMD_COLROW_HIDE(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_HIDE_TYPE, CmdColRowHide))
2200 
2201 typedef struct {
2202 	GnmCommand cmd;
2203 
2204 	gboolean       is_cols;
2205 	ColRowVisList *hide, *show;
2206 } CmdColRowHide;
2207 
2208 static void
cmd_colrow_hide_repeat(GnmCommand const * cmd,WorkbookControl * wbc)2209 cmd_colrow_hide_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2210 {
2211 	CmdColRowHide const *orig = (CmdColRowHide const *) cmd;
2212 	cmd_selection_colrow_hide (wbc, orig->is_cols, orig->show != NULL);
2213 }
MAKE_GNM_COMMAND(CmdColRowHide,cmd_colrow_hide,cmd_colrow_hide_repeat)2214 MAKE_GNM_COMMAND (CmdColRowHide, cmd_colrow_hide, cmd_colrow_hide_repeat)
2215 
2216 /**
2217  * cmd_colrow_hide_correct_selection:
2218  *
2219  * Try to ensure that the selection/cursor is set to a visible row/col
2220  *
2221  * Added to fix bug 38179
2222  * Removed because the result is irritating and the bug is actually XL
2223  * compatibile
2224  **/
2225 static void
2226 cmd_colrow_hide_correct_selection (G_GNUC_UNUSED CmdColRowHide *me, G_GNUC_UNUSED WorkbookControl *wbc)
2227 {
2228 #if 0
2229 	int x, y, index;
2230 	SheetView *sv = sheet_get_view (me->cmd.sheet,
2231 		wb_control_view (wbc));
2232 
2233 	index = colrow_find_adjacent_visible (me->cmd.sheet, me->is_cols,
2234 			me->is_cols ? sv->edit_pos.col : sv->edit_pos.row,
2235 			TRUE);
2236 
2237 	x = me->is_cols ? sv->edit_pos.row : index;
2238 	y = me->is_cols ? index : sv->edit_pos.col;
2239 
2240 	if (index >= 0) {
2241 		sv_selection_reset (sv);
2242 		if (me->is_cols)
2243 			sv_selection_add_full (sv, y, x, y, 0,
2244 					       y, gnm_sheet_get_last_row (sheet),
2245 					       GNM_SELECTION_MODE_ADD);
2246 		else
2247 			sv_selection_add_full (sv, y, x, 0, x,
2248 					       gnm_sheet_get_last_col (sheet), x,
2249 					       GNM_SELECTION_MODE_ADD);
2250 	}
2251 #endif
2252 }
2253 
2254 static gboolean
cmd_colrow_hide_undo(GnmCommand * cmd,WorkbookControl * wbc)2255 cmd_colrow_hide_undo (GnmCommand *cmd, WorkbookControl *wbc)
2256 {
2257 	CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2258 
2259 	g_return_val_if_fail (me != NULL, TRUE);
2260 
2261 	colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2262 				    TRUE, me->hide);
2263 	colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2264 				    FALSE, me->show);
2265 
2266 	if (me->show != NULL)
2267 		cmd_colrow_hide_correct_selection (me, wbc);
2268 
2269 	return FALSE;
2270 }
2271 
2272 static gboolean
cmd_colrow_hide_redo(GnmCommand * cmd,WorkbookControl * wbc)2273 cmd_colrow_hide_redo (GnmCommand *cmd, WorkbookControl *wbc)
2274 {
2275 	CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2276 
2277 	g_return_val_if_fail (me != NULL, TRUE);
2278 
2279 	colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2280 				    FALSE, me->hide);
2281 	colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2282 				    TRUE, me->show);
2283 
2284 	if (me->hide != NULL)
2285 		cmd_colrow_hide_correct_selection (me, wbc);
2286 
2287 	return FALSE;
2288 }
2289 
2290 static void
cmd_colrow_hide_finalize(GObject * cmd)2291 cmd_colrow_hide_finalize (GObject *cmd)
2292 {
2293 	CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2294 	colrow_vis_list_destroy (me->hide);
2295 	me->hide = NULL;
2296 	colrow_vis_list_destroy (me->show);
2297 	me->show = NULL;
2298 	gnm_command_finalize (cmd);
2299 }
2300 
2301 gboolean
cmd_selection_colrow_hide(WorkbookControl * wbc,gboolean is_cols,gboolean visible)2302 cmd_selection_colrow_hide (WorkbookControl *wbc,
2303 			   gboolean is_cols, gboolean visible)
2304 {
2305 	CmdColRowHide *me;
2306 	SheetView *sv = wb_control_cur_sheet_view (wbc);
2307 	int n;
2308 	Sheet *sheet;
2309 	GSList *show = NULL, *hide = NULL;
2310 
2311 	if (visible)
2312 		show = colrow_get_visibility_toggle (sv, is_cols, TRUE);
2313 	else
2314 		hide = colrow_get_visibility_toggle (sv, is_cols, FALSE);
2315 	n = colrow_vis_list_length (hide) + colrow_vis_list_length (show);
2316 	sheet = sv_sheet (sv);
2317 
2318 	if (!visible) {
2319 		/* If these are the last colrows to hide, check with the user */
2320 		int count = 0;
2321 		if (is_cols) {
2322 			int i, max = gnm_sheet_get_max_cols (sheet);
2323 			ColRowInfo *ci;
2324 			for (i = 0 ; i < max ; i++)
2325 				if (NULL ==
2326 				    (ci = sheet_col_get (sheet, i)) ||
2327 				    (ci->visible))
2328 					count++;
2329 		} else {
2330 			int i, max = gnm_sheet_get_max_rows (sheet);
2331 			ColRowInfo *ci;
2332 			for (i = 0 ; i < max ; i++)
2333 				if (NULL ==
2334 				    (ci = sheet_row_get (sheet, i)) ||
2335 				    (ci->visible))
2336 					count++;
2337 		}
2338 		if (count <= n) {
2339 			gchar const *text = is_cols ?
2340 				_("Are you sure that you want to hide all columns? "
2341 				  "If you do so you can unhide them with the "
2342 				  "'Format\342\206\222Column\342\206\222Unhide' "
2343 				  "menu item.") :
2344 				_("Are you sure that you want to hide all rows? "
2345 				  "If you do so you can unhide them with the "
2346 				  "'Format\342\206\222Row\342\206\222Unhide' "
2347 				  "menu item.");
2348 			if (!go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)),
2349 						  FALSE, "%s", text)) {
2350 				colrow_vis_list_destroy (show);
2351 				colrow_vis_list_destroy (hide);
2352 				return TRUE;
2353 			}
2354 		}
2355 	}
2356 
2357 	me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2358 	me->show = show;
2359 	me->hide = hide;
2360 	me->is_cols = is_cols;
2361 	me->cmd.sheet = sheet;
2362 	me->cmd.size = 1 + g_slist_length (hide) + g_slist_length (show);
2363 	me->cmd.cmd_descriptor = g_strdup (is_cols
2364 		? (visible ? _("Unhide columns") : _("Hide columns"))
2365 		: (visible ? _("Unhide rows") : _("Hide rows")));
2366 
2367 	return gnm_command_push_undo (wbc, G_OBJECT (me));
2368 }
2369 
2370 gboolean
cmd_selection_outline_change(WorkbookControl * wbc,gboolean is_cols,int index,int depth)2371 cmd_selection_outline_change (WorkbookControl *wbc,
2372 			      gboolean is_cols, int index, int depth)
2373 {
2374 	CmdColRowHide *me;
2375 	ColRowInfo const *cri;
2376 	int first = -1, last = -1;
2377 	gboolean visible = FALSE;
2378 	int d;
2379 	Sheet	  *sheet = wb_control_cur_sheet (wbc);
2380 	SheetView *sv	 = wb_control_cur_sheet_view (wbc);
2381 
2382 	cri = sheet_colrow_get_info (sheet, index, is_cols);
2383 
2384 	d = cri->outline_level;
2385 	if (depth > d)
2386 		depth = d;
2387 
2388 	/* Nodes only collapse when selected directly, selecting at a lower
2389 	 * level is a standard toggle. */
2390 	if (depth == d) {
2391 		if ((is_cols ? sheet->outline_symbols_right : sheet->outline_symbols_below)) {
2392 			if (index > 0) {
2393 				ColRowInfo const *prev =
2394 					sheet_colrow_get (sheet, index-1, is_cols);
2395 
2396 				if (prev != NULL && prev->outline_level > d) {
2397 					visible = (depth == d && cri->is_collapsed);
2398 					last = index - 1;
2399 					first = colrow_find_outline_bound (sheet, is_cols,
2400 						last, d+1, FALSE);
2401 				}
2402 			}
2403 		} else if (index+1 < colrow_max (is_cols, sheet)) {
2404 			ColRowInfo const *next =
2405 				sheet_colrow_get (sheet, index+1, is_cols);
2406 
2407 			if (next != NULL && next->outline_level > d) {
2408 				visible = (depth == d && cri->is_collapsed);
2409 				first = index + 1;
2410 				last = colrow_find_outline_bound (sheet, is_cols,
2411 					first, d+1, TRUE);
2412 			}
2413 		}
2414 	}
2415 
2416 	/* If nothing done yet do a simple collapse */
2417 	if (first < 0 && cri->outline_level > 0) {
2418 		if (depth < d)
2419 			++depth;
2420 		first = colrow_find_outline_bound (sheet, is_cols, index, depth, FALSE);
2421 		last = colrow_find_outline_bound (sheet, is_cols, index, depth, TRUE);
2422 		visible = FALSE;
2423 
2424 		if (first == last && depth > cri->outline_level)
2425 			return TRUE;
2426 	}
2427 
2428 	if (first < 0 || last < 0)
2429 		return TRUE;
2430 
2431 	me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2432 
2433 	me->is_cols = is_cols;
2434 	me->hide = me->show = NULL;
2435 	if (visible)
2436 		me->show = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2437 						      TRUE, first, last);
2438 	else
2439 		me->hide = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2440 						      FALSE, first, last);
2441 
2442 	me->cmd.sheet = sv_sheet (sv);
2443 	me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2444 	me->cmd.cmd_descriptor = g_strdup (is_cols
2445 		? (visible ? _("Expand columns") : _("Collapse columns"))
2446 		: (visible ? _("Expand rows") : _("Collapse rows")));
2447 
2448 	return gnm_command_push_undo (wbc, G_OBJECT (me));
2449 }
2450 
2451 gboolean
cmd_global_outline_change(WorkbookControl * wbc,gboolean is_cols,int depth)2452 cmd_global_outline_change (WorkbookControl *wbc, gboolean is_cols, int depth)
2453 {
2454 	CmdColRowHide *me;
2455 	ColRowVisList *hide, *show;
2456 	SheetView *sv	 = wb_control_cur_sheet_view (wbc);
2457 
2458 	colrow_get_global_outline (sv_sheet (sv), is_cols, depth, &show, &hide);
2459 
2460 	if (show == NULL && hide == NULL)
2461 		return TRUE;
2462 
2463 	me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2464 	me->is_cols	= is_cols;
2465 	me->hide	= hide;
2466 	me->show	= show;
2467 	me->cmd.sheet	= sv_sheet (sv);
2468 	me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2469 	me->cmd.cmd_descriptor = g_strdup_printf (is_cols
2470 		? _("Show column outline %d") : _("Show row outline %d"), depth);
2471 
2472 	return gnm_command_push_undo (wbc, G_OBJECT (me));
2473 }
2474 
2475 /******************************************************************/
2476 
2477 #define CMD_GROUP_TYPE        (cmd_group_get_type ())
2478 #define CMD_GROUP(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GROUP_TYPE, CmdGroup))
2479 
2480 typedef struct {
2481 	GnmCommand cmd;
2482 
2483 	GnmRange       range;
2484 	gboolean       is_cols;
2485 	gboolean       group;
2486 } CmdGroup;
2487 
2488 static void
cmd_group_repeat(GnmCommand const * cmd,WorkbookControl * wbc)2489 cmd_group_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2490 {
2491 	CmdGroup const *orig = (CmdGroup const *) cmd;
2492 	cmd_selection_group (wbc, orig->is_cols, orig->group);
2493 }
MAKE_GNM_COMMAND(CmdGroup,cmd_group,cmd_group_repeat)2494 MAKE_GNM_COMMAND (CmdGroup, cmd_group, cmd_group_repeat)
2495 
2496 static gboolean
2497 cmd_group_undo (GnmCommand *cmd,
2498 		G_GNUC_UNUSED WorkbookControl *wbc)
2499 {
2500 	CmdGroup const *me = CMD_GROUP (cmd);
2501 	sheet_colrow_group_ungroup (me->cmd.sheet,
2502 		&me->range, me->is_cols, !me->group);
2503 	return FALSE;
2504 }
2505 
2506 static gboolean
cmd_group_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)2507 cmd_group_redo (GnmCommand *cmd,
2508 		G_GNUC_UNUSED WorkbookControl *wbc)
2509 {
2510 	CmdGroup const *me = CMD_GROUP (cmd);
2511 	sheet_colrow_group_ungroup (me->cmd.sheet,
2512 		&me->range, me->is_cols, me->group);
2513 	return FALSE;
2514 }
2515 
2516 static void
cmd_group_finalize(GObject * cmd)2517 cmd_group_finalize (GObject *cmd)
2518 {
2519 	gnm_command_finalize (cmd);
2520 }
2521 
2522 gboolean
cmd_selection_group(WorkbookControl * wbc,gboolean is_cols,gboolean group)2523 cmd_selection_group (WorkbookControl *wbc,
2524 		     gboolean is_cols, gboolean group)
2525 {
2526 	CmdGroup  *me;
2527 	SheetView *sv;
2528 	GnmRange	   r;
2529 
2530 	g_return_val_if_fail (wbc != NULL, TRUE);
2531 
2532 	sv = wb_control_cur_sheet_view (wbc);
2533 	r = *selection_first_range (sv, NULL, NULL);
2534 
2535 	/* Check if this really is possible and display an error if it's not */
2536 	if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2537 		if (group) {
2538 			go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2539 					       ? _("Those columns are already grouped")
2540 					       : _("Those rows are already grouped"));
2541 			return TRUE;
2542 		}
2543 
2544 		/* see if the user selected the col/row with the marker too */
2545 		if (is_cols) {
2546 			if (r.start.col != r.end.col) {
2547 				if (sv->sheet->outline_symbols_right)
2548 					r.end.col--;
2549 				else
2550 					r.start.col++;
2551 			}
2552 		} else {
2553 			if (r.start.row != r.end.row) {
2554 				if (sv->sheet->outline_symbols_below)
2555 					r.end.row--;
2556 				else
2557 					r.start.row++;
2558 			}
2559 		}
2560 
2561 		if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2562 			go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2563 					       ? _("Those columns are not grouped, you can't ungroup them")
2564 					       : _("Those rows are not grouped, you can't ungroup them"));
2565 			return TRUE;
2566 		}
2567 	}
2568 
2569 	me = g_object_new (CMD_GROUP_TYPE, NULL);
2570 	me->is_cols = is_cols;
2571 	me->group = group;
2572 	me->range = r;
2573 
2574 	me->cmd.sheet = sv->sheet;
2575 	me->cmd.size = 1;
2576 	me->cmd.cmd_descriptor = is_cols
2577 		? g_strdup_printf (group ? _("Group columns %s") : _("Ungroup columns %s"),
2578 				   cols_name (me->range.start.col, me->range.end.col))
2579 		: g_strdup_printf (group ? _("Group rows %d:%d") : _("Ungroup rows %d:%d"),
2580 				   me->range.start.row + 1, me->range.end.row + 1);
2581 
2582 	return gnm_command_push_undo (wbc, G_OBJECT (me));
2583 }
2584 
2585 /******************************************************************/
2586 
2587 #define CMD_PASTE_CUT_TYPE        (cmd_paste_cut_get_type ())
2588 #define CMD_PASTE_CUT(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_CUT_TYPE, CmdPasteCut))
2589 
2590 typedef struct {
2591 	GnmCommand cmd;
2592 
2593 	GnmExprRelocateInfo info;
2594 	GSList *paste_contents;
2595 	GOUndo *reloc_undo;
2596 	gboolean move_selection;
2597 	ColRowStateList *saved_sizes;
2598 
2599 	/* handle redo-ing an undo with contents from a deleted sheet */
2600 	GnmCellRegion *deleted_sheet_contents;
2601 } CmdPasteCut;
2602 
2603 MAKE_GNM_COMMAND (CmdPasteCut, cmd_paste_cut, NULL)
2604 
2605 typedef struct {
2606 	GnmPasteTarget pt;
2607 	GnmCellRegion *contents;
2608 } PasteContent;
2609 
2610 /**
2611  * cmd_paste_cut_update:
2612  *
2613  * Utility routine to update things when we are transfering between sheets and
2614  * workbooks.
2615  */
2616 static void
cmd_paste_cut_update(GnmExprRelocateInfo const * info,G_GNUC_UNUSED WorkbookControl * wbc)2617 cmd_paste_cut_update (GnmExprRelocateInfo const *info,
2618 		      G_GNUC_UNUSED WorkbookControl *wbc)
2619 {
2620 	Sheet *o = info->origin_sheet;
2621 	Sheet *t = info->target_sheet;
2622 
2623 	/* Dirty and update both sheets */
2624 	sheet_mark_dirty (t);
2625 	sheet_update (t);
2626 
2627 	if (IS_SHEET (o) && o != t) {
2628 		sheet_mark_dirty (o);
2629 		sheet_update (o);
2630 	}
2631 }
2632 
2633 static gboolean
cmd_paste_cut_undo(GnmCommand * cmd,WorkbookControl * wbc)2634 cmd_paste_cut_undo (GnmCommand *cmd, WorkbookControl *wbc)
2635 {
2636 	CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2637 	GnmExprRelocateInfo reverse;
2638 
2639 	g_return_val_if_fail (me != NULL, TRUE);
2640 	g_return_val_if_fail (me->paste_contents != NULL, TRUE);
2641 	g_return_val_if_fail (me->deleted_sheet_contents == NULL, TRUE);
2642 
2643 	reverse.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
2644 	reverse.target_sheet = me->info.origin_sheet;
2645 	reverse.origin_sheet = me->info.target_sheet;
2646 	reverse.origin = me->info.origin;
2647 	range_translate (&reverse.origin,
2648 			 me->info.origin_sheet,  /* FIXME: What sheet? */
2649 			 me->info.col_offset,
2650 			 me->info.row_offset);
2651 	reverse.col_offset = -me->info.col_offset;
2652 	reverse.row_offset = -me->info.row_offset;
2653 
2654 	/* Move things back being careful NOT to invalidate the src region */
2655 	if (IS_SHEET (me->info.origin_sheet))
2656 		sheet_move_range (&reverse, NULL, GO_CMD_CONTEXT (wbc));
2657 	else
2658 		me->deleted_sheet_contents = clipboard_copy_range (
2659 			reverse.origin_sheet, &reverse.origin);
2660 
2661 	/* Restore the original row heights */
2662 	colrow_set_states (me->info.target_sheet, FALSE,
2663 		reverse.origin.start.row, me->saved_sizes);
2664 	colrow_state_list_destroy (me->saved_sizes);
2665 	me->saved_sizes = NULL;
2666 
2667 	if (me->reloc_undo) {
2668 		go_undo_undo (me->reloc_undo);
2669 		g_object_unref (me->reloc_undo);
2670 		me->reloc_undo = NULL;
2671 	}
2672 
2673 	while (me->paste_contents) {
2674 		PasteContent *pc = me->paste_contents->data;
2675 		me->paste_contents = g_slist_remove (me->paste_contents, pc);
2676 
2677 		clipboard_paste_region (pc->contents, &pc->pt, GO_CMD_CONTEXT (wbc));
2678 		cellregion_unref (pc->contents);
2679 		g_free (pc);
2680 	}
2681 
2682 	/* Force update of the status area */
2683 	sheet_flag_status_update_range (me->info.target_sheet, NULL);
2684 
2685 	cmd_paste_cut_update (&me->info, wbc);
2686 
2687 	/* Select the original region */
2688 	if (me->move_selection && IS_SHEET (me->info.origin_sheet))
2689 		select_range (me->info.origin_sheet,
2690 			      &me->info.origin,
2691 			      wbc);
2692 
2693 	return FALSE;
2694 }
2695 
2696 static gboolean
cmd_paste_cut_redo(GnmCommand * cmd,WorkbookControl * wbc)2697 cmd_paste_cut_redo (GnmCommand *cmd, WorkbookControl *wbc)
2698 {
2699 	CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2700 	GnmRange tmp;
2701 
2702 	g_return_val_if_fail (me != NULL, TRUE);
2703 	g_return_val_if_fail (me->paste_contents == NULL, TRUE);
2704 
2705 	tmp = me->info.origin;
2706 	range_translate (&tmp, me->info.origin_sheet,  /* FIXME: What sheet? */
2707 			 me->info.col_offset, me->info.row_offset);
2708 	range_normalize (&tmp);
2709 
2710 	g_return_val_if_fail (range_is_sane (&tmp), TRUE);
2711 
2712 	if (me->info.origin_sheet != me->info.target_sheet ||
2713 	    !range_overlap (&me->info.origin, &tmp)) {
2714 		PasteContent *pc = g_new (PasteContent, 1);
2715 		paste_target_init (&pc->pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2716 		pc->contents = clipboard_copy_range (me->info.target_sheet, &tmp);
2717 		me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2718 	} else {
2719 		/* need to store any portions of the paste target
2720 		 * that do not overlap with the source.
2721 		 */
2722 		GSList *ptr, *frag = range_split_ranges (&me->info.origin, &tmp);
2723 		for (ptr = frag ; ptr != NULL ; ptr = ptr->next) {
2724 			GnmRange *r = ptr->data;
2725 
2726 			if (!range_overlap (&me->info.origin, r)) {
2727 				PasteContent *pc = g_new (PasteContent, 1);
2728 				paste_target_init (&pc->pt, me->info.target_sheet, r, PASTE_ALL_SHEET);
2729 				pc->contents = clipboard_copy_range (me->info.target_sheet,  r);
2730 				me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2731 			}
2732 			g_free (r);
2733 		}
2734 		g_slist_free (frag);
2735 	}
2736 
2737 	/* rare corner case.  If the origin sheet has been deleted */
2738 	if (!IS_SHEET (me->info.origin_sheet)) {
2739 		GnmPasteTarget pt;
2740 		paste_target_init (&pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2741 		sheet_clear_region (pt.sheet,
2742 			tmp.start.col, tmp.start.row, tmp.end.col,   tmp.end.row,
2743 			CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
2744 			GO_CMD_CONTEXT (wbc));
2745 		clipboard_paste_region (me->deleted_sheet_contents,
2746 			&pt, GO_CMD_CONTEXT (wbc));
2747 		cellregion_unref (me->deleted_sheet_contents);
2748 		me->deleted_sheet_contents = NULL;
2749 	} else
2750 		sheet_move_range (&me->info, &me->reloc_undo, GO_CMD_CONTEXT (wbc));
2751 
2752 	cmd_paste_cut_update (&me->info, wbc);
2753 
2754 	/* Backup row heights and adjust row heights to fit */
2755 	me->saved_sizes = colrow_get_states (me->info.target_sheet, FALSE, tmp.start.row, tmp.end.row);
2756 	rows_height_update (me->info.target_sheet, &tmp, FALSE);
2757 
2758 	/* Make sure the destination is selected */
2759 	if (me->move_selection)
2760 		select_range (me->info.target_sheet, &tmp, wbc);
2761 
2762 	return FALSE;
2763 }
2764 
2765 static void
cmd_paste_cut_finalize(GObject * cmd)2766 cmd_paste_cut_finalize (GObject *cmd)
2767 {
2768 	CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2769 
2770 	if (me->saved_sizes)
2771 		me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
2772 	while (me->paste_contents) {
2773 		PasteContent *pc = me->paste_contents->data;
2774 		me->paste_contents = g_slist_remove (me->paste_contents, pc);
2775 		cellregion_unref (pc->contents);
2776 		g_free (pc);
2777 	}
2778 	if (me->reloc_undo) {
2779 		g_object_unref (me->reloc_undo);
2780 		me->reloc_undo = NULL;
2781 	}
2782 	if (me->deleted_sheet_contents) {
2783 		cellregion_unref (me->deleted_sheet_contents);
2784 		me->deleted_sheet_contents = NULL;
2785 	}
2786 
2787 	gnm_command_finalize (cmd);
2788 }
2789 
2790 gboolean
cmd_paste_cut(WorkbookControl * wbc,GnmExprRelocateInfo const * info,gboolean move_selection,char * descriptor)2791 cmd_paste_cut (WorkbookControl *wbc, GnmExprRelocateInfo const *info,
2792 	       gboolean move_selection, char *descriptor)
2793 {
2794 	CmdPasteCut *me;
2795 	GnmRange r;
2796 	char *where;
2797 
2798 	g_return_val_if_fail (info != NULL, TRUE);
2799 
2800 	/* This is vacuous */
2801 	if (info->origin_sheet == info->target_sheet &&
2802 	    info->col_offset == 0 && info->row_offset == 0)
2803 		return TRUE;
2804 
2805 	/* FIXME: Do we want to show the destination range as well ? */
2806 	where = undo_range_name (info->origin_sheet, &info->origin);
2807 	if (descriptor == NULL)
2808 		descriptor = g_strdup_printf (_("Moving %s"), where);
2809 	g_free (where);
2810 
2811 	g_return_val_if_fail (info != NULL, TRUE);
2812 
2813 	r = info->origin;
2814 	if (range_translate (&r, info->target_sheet,
2815 			     info->col_offset, info->row_offset)) {
2816 
2817 		go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), descriptor,
2818 					_("is beyond sheet boundaries"));
2819 		g_free (descriptor);
2820 		return TRUE;
2821 	}
2822 
2823 	/* Check array subdivision & merged regions */
2824 	if (sheet_range_splits_region (info->target_sheet, &r,
2825 		(info->origin_sheet == info->target_sheet)
2826 		? &info->origin : NULL, GO_CMD_CONTEXT (wbc), descriptor)) {
2827 		g_free (descriptor);
2828 		return TRUE;
2829 	}
2830 
2831 	me = g_object_new (CMD_PASTE_CUT_TYPE, NULL);
2832 
2833 	me->info = *info;
2834 	me->paste_contents  = NULL;
2835 	me->deleted_sheet_contents = NULL;
2836 	me->reloc_undo = NULL;
2837 	me->move_selection = move_selection;
2838 	me->saved_sizes    = NULL;
2839 
2840 	me->cmd.sheet = NULL;  /* we have potentially two different.  */
2841 	me->cmd.size = 1;  /* FIXME?  */
2842 	me->cmd.cmd_descriptor = descriptor;
2843 
2844 	/* NOTE : if the destination workbook is different from the source
2845 	 * workbook should we have undo elements in both menus ??  It seems
2846 	 * poor form to hit undo in 1 window and effect another...
2847 	 *
2848 	 * Maybe queue it as two different commands, as a clear in one book
2849 	 * and a paste in the other.  This is not symmetric though.  What
2850 	 * happens to the cells in the original sheet that now reference the
2851 	 * cells in the other?  When do they reset to the original?
2852 	 *
2853 	 * Probably when the clear in the original is undone.
2854 	 */
2855 
2856 	return gnm_command_push_undo (wbc, G_OBJECT (me));
2857 }
2858 
2859 /******************************************************************/
2860 
2861 static void
warn_if_date_trouble(WorkbookControl * wbc,GnmCellRegion * cr)2862 warn_if_date_trouble (WorkbookControl *wbc, GnmCellRegion *cr)
2863 {
2864 	Workbook *wb = wb_control_get_workbook (wbc);
2865 	const GODateConventions *wb_date_conv = workbook_date_conv (wb);
2866 
2867 	if (cr->date_conv == NULL)
2868 		return;
2869 	if (go_date_conv_equal (cr->date_conv, wb_date_conv))
2870 		return;
2871 
2872 	/* We would like to show a warning, but it seems we cannot via a context.  */
2873 	{
2874 		GError *err;
2875 		err = g_error_new (go_error_invalid(), 0,
2876 				   _("Copying between files with different date conventions.\n"
2877 				     "It is possible that some dates could be copied\n"
2878 				     "incorrectly."));
2879 		go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
2880 		g_error_free (err);
2881 	}
2882 }
2883 
2884 
2885 #define CMD_PASTE_COPY_TYPE        (cmd_paste_copy_get_type ())
2886 #define CMD_PASTE_COPY(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_COPY_TYPE, CmdPasteCopy))
2887 
2888 typedef struct {
2889 	GnmCommand cmd;
2890 
2891 	GnmCellRegion   *contents;
2892 	GSList          *pasted_objects,*orig_contents_objects;
2893 	GnmPasteTarget   dst;
2894 	gboolean         has_been_through_cycle;
2895 	gboolean         only_objects;
2896 	gboolean single_merge_to_single_merge;
2897 } CmdPasteCopy;
2898 
2899 static void
cmd_paste_copy_repeat(GnmCommand const * cmd,WorkbookControl * wbc)2900 cmd_paste_copy_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2901 {
2902 	CmdPasteCopy const *orig = (CmdPasteCopy const *) cmd;
2903 	GnmPasteTarget new_dst;
2904 	SheetView *sv = wb_control_cur_sheet_view (wbc);
2905 	GnmRange const	*r = selection_first_range (sv,
2906 		GO_CMD_CONTEXT (wbc), _("Paste Copy"));
2907 	GnmCellRegion *newcr;
2908 
2909 	if (r == NULL)
2910 		return;
2911 
2912 	paste_target_init (&new_dst, sv_sheet (sv), r, orig->dst.paste_flags);
2913 	newcr = clipboard_copy_range (orig->dst.sheet, &orig->dst.range);
2914 	cmd_paste_copy (wbc, &new_dst, newcr);
2915 	cellregion_unref (newcr);
2916 }
MAKE_GNM_COMMAND(CmdPasteCopy,cmd_paste_copy,cmd_paste_copy_repeat)2917 MAKE_GNM_COMMAND (CmdPasteCopy, cmd_paste_copy, cmd_paste_copy_repeat)
2918 
2919 static int
2920 by_addr (gconstpointer a, gconstpointer b)
2921 {
2922 	if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b))
2923 		return -1;
2924 	if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b))
2925 		return +1;
2926 	return 0;
2927 }
2928 
2929 /**
2930  * get_new_objects:
2931  * @sheet: #Sheet to query
2932  * @old: (element-type SheetObject): list of objects to disregard
2933  *
2934  * Returns: (transfer full) (element-type SheetObject): A list of new objects
2935  * in sheet since @old was collected.
2936  */
2937 static GSList *
get_new_objects(Sheet * sheet,GSList * old)2938 get_new_objects (Sheet *sheet, GSList *old)
2939 {
2940 	GSList *objs =
2941 		g_slist_sort (g_slist_copy_deep (sheet->sheet_objects,
2942 						 (GCopyFunc)g_object_ref,
2943 						 NULL),
2944 			      by_addr);
2945 	GSList *p = objs, *last = NULL;
2946 
2947 	while (old) {
2948 		int c = -1;
2949 		while (p && (c = by_addr (p->data, old->data)) < 0) {
2950 			last = p;
2951 			p = p->next;
2952 		}
2953 
2954 		old = old->next;
2955 
2956 		if (c == 0) {
2957 			GSList *next = p->next;
2958 			if (last)
2959 				last->next = next;
2960 			else
2961 				objs = next;
2962 			g_object_unref (p->data);
2963 			g_slist_free_1 (p);
2964 			p = next;
2965 		}
2966 	}
2967 
2968 	return objs;
2969 }
2970 
2971 static void
cmd_paste_copy_select_obj(SheetObject * so,SheetControlGUI * scg)2972 cmd_paste_copy_select_obj (SheetObject *so, SheetControlGUI *scg)
2973 {
2974 	scg_object_select (scg, so);
2975 }
2976 
2977 static gboolean
cmd_paste_copy_impl(GnmCommand * cmd,WorkbookControl * wbc,gboolean is_undo)2978 cmd_paste_copy_impl (GnmCommand *cmd, WorkbookControl *wbc,
2979 		     gboolean is_undo)
2980 {
2981 	CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
2982 	GnmCellRegion *contents;
2983 	GSList *old_objects;
2984 
2985 	g_return_val_if_fail (me != NULL, TRUE);
2986 	g_return_val_if_fail (me->contents != NULL, TRUE);
2987 
2988 	g_slist_foreach (me->pasted_objects,
2989 			 (GFunc)sheet_object_clear_sheet,
2990 			 NULL);
2991 	g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
2992 	me->pasted_objects = NULL;
2993 	old_objects = get_new_objects (me->dst.sheet, NULL);
2994 
2995 	contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
2996 	if (me->has_been_through_cycle)
2997 		me->dst.paste_flags =
2998 			PASTE_CONTENTS |
2999 			PASTE_COLUMN_WIDTHS | PASTE_ROW_HEIGHTS |
3000 			(me->dst.paste_flags & PASTE_ALL_SHEET);
3001 
3002 	if (clipboard_paste_region (me->contents, &me->dst,
3003 				    GO_CMD_CONTEXT (wbc))) {
3004 		/* There was a problem, avoid leaking */
3005 		cellregion_unref (contents);
3006 		g_slist_free_full (old_objects, g_object_unref);
3007 		return TRUE;
3008 	}
3009 
3010 	me->pasted_objects = get_new_objects (me->dst.sheet, old_objects);
3011 	g_slist_free_full (old_objects, g_object_unref);
3012 
3013 	if (!is_undo && !me->has_been_through_cycle) {
3014 		colrow_autofit (me->dst.sheet, &me->dst.range, FALSE, FALSE,
3015 				TRUE, FALSE,
3016 				NULL, NULL);
3017 		colrow_autofit (me->dst.sheet, &me->dst.range, TRUE, TRUE,
3018 				TRUE, FALSE,
3019 				NULL, NULL);
3020 	}
3021 
3022 	if (is_undo) {
3023 		// We cannot use the random set of objects at the target
3024 		// location.  http://bugzilla.gnome.org/show_bug.cgi?id=308300
3025 		g_slist_free_full (contents->objects, g_object_unref);
3026 		contents->objects = g_slist_copy_deep
3027 			(me->orig_contents_objects,
3028 			 (GCopyFunc)sheet_object_dup, NULL);
3029 	} else {
3030 		GSList *l;
3031 		for (l = contents->objects; l; l = l->next) {
3032 			SheetObject *so = l->data;
3033 			if (sheet_object_get_sheet (so)) {
3034 				g_object_unref (so);
3035 				l->data = NULL;
3036 			} else {
3037 				// Object got deleted by paste, so keep it for
3038 				// undo.  See bugzilla 732653
3039 			}
3040 		}
3041 		contents->objects =
3042 			g_slist_remove_all (contents->objects, NULL);
3043 	}
3044 
3045 	cellregion_unref (me->contents);
3046 	me->contents = contents;
3047 	me->has_been_through_cycle = TRUE;
3048 
3049 	/* Select the newly pasted contents  (this queues a redraw) */
3050 	if (me->only_objects && GNM_IS_WBC_GTK (wbc)) {
3051 		SheetControlGUI *scg =
3052 			wbcg_get_nth_scg (WBC_GTK (wbc),
3053 					  cmd->sheet->index_in_wb);
3054 		scg_object_unselect (scg, NULL);
3055 		g_slist_foreach (me->pasted_objects,
3056 				 (GFunc) cmd_paste_copy_select_obj, scg);
3057 	}
3058 	select_range (me->dst.sheet, &me->dst.range, wbc);
3059 
3060 	return FALSE;
3061 }
3062 
3063 static gboolean
cmd_paste_copy_undo(GnmCommand * cmd,WorkbookControl * wbc)3064 cmd_paste_copy_undo (GnmCommand *cmd, WorkbookControl *wbc)
3065 {
3066 	return cmd_paste_copy_impl (cmd, wbc, TRUE);
3067 }
3068 
3069 static gboolean
cmd_paste_copy_redo(GnmCommand * cmd,WorkbookControl * wbc)3070 cmd_paste_copy_redo (GnmCommand *cmd, WorkbookControl *wbc)
3071 {
3072 	return cmd_paste_copy_impl (cmd, wbc, FALSE);
3073 }
3074 
3075 static void
cmd_paste_copy_finalize(GObject * cmd)3076 cmd_paste_copy_finalize (GObject *cmd)
3077 {
3078 	CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
3079 
3080 	if (me->contents) {
3081 		cellregion_unref (me->contents);
3082 		me->contents = NULL;
3083 	}
3084 	g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
3085 	g_slist_free_full (me->orig_contents_objects, (GDestroyNotify)g_object_unref);
3086 	gnm_command_finalize (cmd);
3087 }
3088 
3089 /*
3090  * cmd_paste_copy:
3091  * @wbc:
3092  * @pt:
3093  * @cr: (transfer none):
3094  *
3095  * Returns: %TRUE if there was a problem, %FALSE otherwise.
3096  */
3097 gboolean
cmd_paste_copy(WorkbookControl * wbc,GnmPasteTarget const * pt,GnmCellRegion * cr)3098 cmd_paste_copy (WorkbookControl *wbc,
3099 		GnmPasteTarget const *pt, GnmCellRegion *cr)
3100 {
3101 	CmdPasteCopy *me;
3102 	int n_r = 1, n_c = 1;
3103 	char *range_name;
3104 	GnmRange const *merge_src;
3105 
3106 	g_return_val_if_fail (pt != NULL, TRUE);
3107 	g_return_val_if_fail (IS_SHEET (pt->sheet), TRUE);
3108 	g_return_val_if_fail (cr != NULL, TRUE);
3109 
3110 	cellregion_ref (cr);
3111 
3112 	me = g_object_new (CMD_PASTE_COPY_TYPE, NULL);
3113 
3114 	me->cmd.sheet = pt->sheet;
3115 	me->cmd.size = 1;  /* FIXME?  */
3116 
3117 	range_name = undo_range_name (pt->sheet, &pt->range);
3118 	me->cmd.cmd_descriptor = g_strdup_printf (_("Pasting into %s"),
3119 						  range_name);
3120 	g_free (range_name);
3121 
3122 	me->dst = *pt;
3123 	me->contents = cr;
3124 	me->has_been_through_cycle = FALSE;
3125 	me->only_objects = (cr->cols < 1 || cr->rows < 1);
3126 	me->pasted_objects = NULL;
3127 	me->orig_contents_objects =
3128 		g_slist_copy_deep (cr->objects,
3129 				   (GCopyFunc)sheet_object_dup, NULL);
3130 	me->single_merge_to_single_merge = FALSE;
3131 
3132 	/* If the input is only objects ignore all this range stuff */
3133 	if (!me->only_objects) {
3134                 /* see if we need to do any tiling */
3135 		GnmRange *r = &me->dst.range;
3136 		if (g_slist_length (cr->merged) == 1 &&
3137 		    (NULL != (merge_src = cr->merged->data)) &&
3138 		    range_height (merge_src) == cr->rows &&
3139 		    range_width (merge_src) == cr->cols) {
3140 			/* We are copying from a single merge */
3141 			GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3142 			if (merge != NULL && range_equal (r, merge)) {
3143 				/* To a single merge */
3144 				me->single_merge_to_single_merge = TRUE;
3145 				n_c = n_r = 1;
3146 				me->dst.paste_flags |= PASTE_DONT_MERGE;
3147 				goto copy_ready;
3148 			}
3149 		}
3150 
3151 		if (pt->paste_flags & PASTE_TRANSPOSE) {
3152 			n_c = range_width (r) / cr->rows;
3153 			if (n_c < 1) n_c = 1;
3154 			r->end.col = r->start.col + n_c * cr->rows - 1;
3155 
3156 			n_r = range_height (r) / cr->cols;
3157 			if (n_r < 1) n_r = 1;
3158 			r->end.row = r->start.row + n_r * cr->cols - 1;
3159 		} else {
3160 			/* Before looking for tiling if we are not transposing,
3161 			 * allow pasting a full col or row from a single cell */
3162 			n_c = range_width (r);
3163 			if (n_c == 1 && cr->cols == gnm_sheet_get_max_cols (me->cmd.sheet)) {
3164 				r->start.col = 0;
3165 				r->end.col = gnm_sheet_get_last_col (me->cmd.sheet);
3166 			} else {
3167 				n_c /= cr->cols;
3168 				if (n_c < 1) n_c = 1;
3169 				r->end.col = r->start.col + n_c * cr->cols - 1;
3170 			}
3171 
3172 			n_r = range_height (r);
3173 			if (n_r == 1 && cr->rows == gnm_sheet_get_max_rows (me->cmd.sheet)) {
3174 				r->start.row = 0;
3175 				r->end.row = gnm_sheet_get_last_row (me->cmd.sheet);
3176 			} else {
3177 				n_r /= cr->rows;
3178 				if (n_r < 1) n_r = 1;
3179 				r->end.row = r->start.row + n_r * cr->rows - 1;
3180 			}
3181 		}
3182 
3183 		if  (cr->cols != 1 || cr->rows != 1) {
3184 			/* Note: when the source is a single cell, a single target merge is special */
3185 			/* see clipboard.c (clipboard_paste_region)                                 */
3186 			GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3187 			if (merge != NULL && range_equal (r, merge)) {
3188 				/* destination is a single merge */
3189 				/* enlarge it such that the source fits */
3190 				if (pt->paste_flags & PASTE_TRANSPOSE) {
3191 					if ((r->end.col - r->start.col + 1) < cr->rows)
3192 						r->end.col = r->start.col + cr->rows - 1;
3193 					if ((r->end.row - r->start.row + 1) < cr->cols)
3194 						r->end.row = r->start.row + cr->cols - 1;
3195 				} else {
3196 					if ((r->end.col - r->start.col + 1) < cr->cols)
3197 						r->end.col = r->start.col + cr->cols - 1;
3198 					if ((r->end.row - r->start.row + 1) < cr->rows)
3199 						r->end.row = r->start.row + cr->rows - 1;
3200 				}
3201 			}
3202 		}
3203 	}
3204 
3205 	if (n_c * (gnm_float)n_r > 10000.) {
3206 		char *number = g_strdup_printf ("%0.0" GNM_FORMAT_f,
3207 						(gnm_float)n_c * (gnm_float)n_r);
3208 		gboolean result = go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)), FALSE,
3209 						       _("Do you really want to paste "
3210 							 "%s copies?"), number);
3211 		g_free (number);
3212 		if (!result) {
3213 			g_object_unref (me);
3214 			return TRUE;
3215 		}
3216 	}
3217 
3218  copy_ready:
3219 	/* Use translate to do a quiet sanity check */
3220 	if (range_translate (&me->dst.range, pt->sheet, 0, 0)) {
3221 		go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
3222 					me->cmd.cmd_descriptor,
3223 					_("is beyond sheet boundaries"));
3224 		g_object_unref (me);
3225 		return TRUE;
3226 	}
3227 
3228 	/* no need to test if all we have are objects or are copying from */
3229 	/*a single merge to a single merge*/
3230 	if ((!me->only_objects) && (!me->single_merge_to_single_merge)&&
3231 	    sheet_range_splits_region (pt->sheet, &me->dst.range,
3232 				       NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
3233 		g_object_unref (me);
3234 		return TRUE;
3235 	}
3236 
3237 	warn_if_date_trouble (wbc, cr);
3238 
3239 	return gnm_command_push_undo (wbc, G_OBJECT (me));
3240 }
3241 
3242 /******************************************************************/
3243 
3244 #define CMD_AUTOFILL_TYPE        (cmd_autofill_get_type ())
3245 #define CMD_AUTOFILL(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFILL_TYPE, CmdAutofill))
3246 
3247 typedef struct {
3248 	GnmCommand cmd;
3249 
3250 	GnmCellRegion *contents;
3251 	GnmPasteTarget dst;
3252 	GnmRange src;
3253 	int base_col, base_row, w, h, end_col, end_row;
3254 	gboolean default_increment;
3255 	gboolean inverse_autofill;
3256 	ColRowIndexList *columns;
3257 	ColRowStateGroup *old_widths;
3258 } CmdAutofill;
3259 
3260 static void
cmd_autofill_repeat(GnmCommand const * cmd,WorkbookControl * wbc)3261 cmd_autofill_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3262 {
3263 	CmdAutofill const *orig = (CmdAutofill const *) cmd;
3264 	SheetView *sv = wb_control_cur_sheet_view (wbc);
3265 	GnmRange const	*r = selection_first_range (sv,
3266 		GO_CMD_CONTEXT (wbc), _("Autofill"));
3267 
3268 	if (r == NULL)
3269 		return;
3270 
3271 	cmd_autofill (wbc, sv_sheet (sv), orig->default_increment,
3272 	      r->start.col, r->start.row, range_width (r), range_height (r),
3273 	      r->start.col + (orig->end_col - orig->base_col),
3274 	      r->start.row + (orig->end_row - orig->base_row),
3275 	      orig->inverse_autofill);
3276 }
MAKE_GNM_COMMAND(CmdAutofill,cmd_autofill,cmd_autofill_repeat)3277 MAKE_GNM_COMMAND (CmdAutofill, cmd_autofill, cmd_autofill_repeat)
3278 
3279 static gboolean
3280 cmd_autofill_undo (GnmCommand *cmd, WorkbookControl *wbc)
3281 {
3282 	CmdAutofill *me = CMD_AUTOFILL (cmd);
3283 	gboolean res;
3284 
3285 	g_return_val_if_fail (wbc != NULL, TRUE);
3286 	g_return_val_if_fail (me != NULL, TRUE);
3287 	g_return_val_if_fail (me->contents != NULL, TRUE);
3288 
3289 	res = clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc));
3290 	cellregion_unref (me->contents);
3291 	me->contents = NULL;
3292 
3293 	if (me->old_widths) {
3294 		colrow_restore_state_group (me->cmd.sheet, TRUE,
3295 					    me->columns,
3296 					    me->old_widths);
3297 		colrow_state_group_destroy (me->old_widths);
3298 		me->old_widths = NULL;
3299 		colrow_index_list_destroy (me->columns);
3300 		me->columns = NULL;
3301 	}
3302 
3303 	if (res)
3304 		return TRUE;
3305 
3306 	select_range (me->dst.sheet, &me->src, wbc);
3307 
3308 	return FALSE;
3309 }
3310 
3311 static gboolean
cmd_autofill_redo(GnmCommand * cmd,WorkbookControl * wbc)3312 cmd_autofill_redo (GnmCommand *cmd, WorkbookControl *wbc)
3313 {
3314 	CmdAutofill *me = CMD_AUTOFILL (cmd);
3315 	GnmRange r;
3316 
3317 	g_return_val_if_fail (me != NULL, TRUE);
3318 	g_return_val_if_fail (me->contents == NULL, TRUE);
3319 
3320 	me->contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
3321 
3322 	g_return_val_if_fail (me->contents != NULL, TRUE);
3323 
3324 	/* FIXME : when we split autofill to support hints and better validation
3325 	 * move this in there.
3326 	 */
3327 	/* MW: May 2006: we support hints now.  What's this about?  */
3328 	sheet_clear_region (me->dst.sheet,
3329 		me->dst.range.start.col, me->dst.range.start.row,
3330 		me->dst.range.end.col,   me->dst.range.end.row,
3331 		CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3332 		GO_CMD_CONTEXT (wbc));
3333 
3334 	if (me->cmd.size == 1)
3335 		me->cmd.size += cellregion_cmd_size (me->contents);
3336 	if (me->inverse_autofill)
3337 		gnm_autofill_fill (me->dst.sheet, me->default_increment,
3338 			me->end_col, me->end_row, me->w, me->h,
3339 			me->base_col, me->base_row);
3340 	else
3341 		gnm_autofill_fill (me->dst.sheet, me->default_increment,
3342 			me->base_col, me->base_row, me->w, me->h,
3343 			me->end_col, me->end_row);
3344 
3345 	colrow_autofit (me->cmd.sheet, &me->dst.range, TRUE, TRUE,
3346 			TRUE, FALSE,
3347 			&me->columns, &me->old_widths);
3348 
3349 	sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3350 	sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3351 	sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3352 
3353 	r = range_union (&me->dst.range, &me->src);
3354 	select_range (me->dst.sheet, &r, wbc);
3355 
3356 	return FALSE;
3357 }
3358 
3359 static void
cmd_autofill_finalize(GObject * cmd)3360 cmd_autofill_finalize (GObject *cmd)
3361 {
3362 	CmdAutofill *me = CMD_AUTOFILL (cmd);
3363 
3364 	if (me->contents) {
3365 		cellregion_unref (me->contents);
3366 		me->contents = NULL;
3367 	}
3368 	colrow_index_list_destroy (me->columns);
3369 	colrow_state_group_destroy (me->old_widths);
3370 	gnm_command_finalize (cmd);
3371 }
3372 
3373 gboolean
cmd_autofill(WorkbookControl * wbc,Sheet * sheet,gboolean default_increment,int base_col,int base_row,int w,int h,int end_col,int end_row,gboolean inverse_autofill)3374 cmd_autofill (WorkbookControl *wbc, Sheet *sheet,
3375 	      gboolean default_increment,
3376 	      int base_col, int base_row,
3377 	      int w, int h, int end_col, int end_row,
3378 	      gboolean inverse_autofill)
3379 {
3380 	CmdAutofill *me;
3381 	GnmRange target, src;
3382 
3383 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3384 
3385 	/* This would be meaningless */
3386 	if (base_col+w-1 == end_col && base_row+h-1 == end_row)
3387 		return FALSE;
3388 
3389 	if (inverse_autofill) {
3390 		if (end_col != base_col + w - 1) {
3391 			range_init (&target, base_col, base_row,
3392 				    end_col - w, end_row);
3393 			range_init (&src, end_col - w + 1, base_row,
3394 				    end_col, end_row);
3395 		} else {
3396 			range_init (&target, base_col, base_row,
3397 				    end_col, end_row - h);
3398 			range_init (&src, base_col, end_row - h + 1,
3399 				    end_col, end_row);
3400 		}
3401 	} else {
3402 		if (end_col != base_col + w - 1) {
3403 			range_init (&target, base_col + w, base_row,
3404 				    end_col, end_row);
3405 			range_init (&src, base_col, base_row,
3406 				    base_col + w - 1, end_row);
3407 		} else {
3408 			range_init (&target, base_col, base_row + h,
3409 				    end_col, end_row);
3410 			range_init (&src, base_col, base_row,
3411 				    end_col, base_row + h - 1);
3412 		}
3413 	}
3414 
3415 	/* We don't support clearing regions, when a user uses the autofill
3416 	 * cursor to 'shrink' a selection
3417 	 */
3418 	if (target.start.col > target.end.col || target.start.row > target.end.row)
3419 		return TRUE;
3420 
3421 	/* Check arrays or merged regions in src or target regions */
3422 	if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")) ||
3423 	    sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")))
3424 		return TRUE;
3425 
3426 	me = g_object_new (CMD_AUTOFILL_TYPE, NULL);
3427 
3428 	me->contents = NULL;
3429 	me->dst.sheet = sheet;
3430 	me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3431 	me->dst.range = target;
3432 	me->src = src;
3433 
3434 	me->base_col = base_col;
3435 	me->base_row = base_row,
3436 	me->w = w;
3437 	me->h = h;
3438 	me->end_col = end_col;
3439 	me->end_row = end_row;
3440 	me->default_increment = default_increment;
3441 	me->inverse_autofill = inverse_autofill;
3442 
3443 	me->cmd.sheet = sheet;
3444 	me->cmd.size = 1;  /* Changed in initial redo.  */
3445 	me->cmd.cmd_descriptor = g_strdup_printf (_("Autofilling %s"),
3446 		range_as_string (&me->dst.range));
3447 
3448 	return gnm_command_push_undo (wbc, G_OBJECT (me));
3449 }
3450 
3451 /******************************************************************/
3452 
3453 #define CMD_COPYREL_TYPE        (cmd_copyrel_get_type ())
3454 #define CMD_COPYREL(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COPYREL_TYPE, CmdCopyRel))
3455 
3456 typedef struct {
3457 	GnmCommand cmd;
3458 
3459 	GOUndo *undo;
3460 	GnmPasteTarget dst, src;
3461 	int dx, dy;
3462 	char const *name;
3463 } CmdCopyRel;
3464 
3465 static void
cmd_copyrel_repeat(GnmCommand const * cmd,WorkbookControl * wbc)3466 cmd_copyrel_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3467 {
3468 	CmdCopyRel const *orig = (CmdCopyRel const *) cmd;
3469 	cmd_copyrel (wbc, orig->dx, orig->dy, orig->name);
3470 }
MAKE_GNM_COMMAND(CmdCopyRel,cmd_copyrel,cmd_copyrel_repeat)3471 MAKE_GNM_COMMAND (CmdCopyRel, cmd_copyrel, cmd_copyrel_repeat)
3472 
3473 static gboolean
3474 cmd_copyrel_undo (GnmCommand *cmd, WorkbookControl *wbc)
3475 {
3476 	CmdCopyRel *me = CMD_COPYREL (cmd);
3477 
3478 	g_return_val_if_fail (wbc != NULL, TRUE);
3479 	g_return_val_if_fail (me != NULL, TRUE);
3480 	g_return_val_if_fail (me->undo != NULL, TRUE);
3481 
3482 	go_undo_undo (me->undo);
3483 
3484 	/* Select the newly pasted contents  (this queues a redraw) */
3485 	select_range (me->dst.sheet, &me->dst.range, wbc);
3486 
3487 	return FALSE;
3488 }
3489 
3490 static gboolean
cmd_copyrel_redo(GnmCommand * cmd,WorkbookControl * wbc)3491 cmd_copyrel_redo (GnmCommand *cmd, WorkbookControl *wbc)
3492 {
3493 	CmdCopyRel *me = CMD_COPYREL (cmd);
3494 	GnmCellRegion *contents;
3495 	gboolean res;
3496 
3497 	g_return_val_if_fail (me != NULL, TRUE);
3498 
3499 	sheet_clear_region (me->dst.sheet,
3500 		me->dst.range.start.col, me->dst.range.start.row,
3501 		me->dst.range.end.col,   me->dst.range.end.row,
3502 		CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3503 		GO_CMD_CONTEXT (wbc));
3504 
3505 	contents = clipboard_copy_range (me->src.sheet, &me->src.range);
3506 	res = clipboard_paste_region (contents, &me->dst, GO_CMD_CONTEXT (wbc));
3507 	cellregion_unref (contents);
3508 	if (res)
3509 		return TRUE;
3510 
3511 	sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3512 	sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3513 	sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3514 
3515 	/* Select the newly pasted contents  (this queues a redraw) */
3516 	select_range (me->dst.sheet, &me->dst.range, wbc);
3517 
3518 	return FALSE;
3519 }
3520 
3521 static void
cmd_copyrel_finalize(GObject * cmd)3522 cmd_copyrel_finalize (GObject *cmd)
3523 {
3524 	CmdCopyRel *me = CMD_COPYREL (cmd);
3525 
3526 	if (me->undo)
3527 		g_object_unref (me->undo);
3528 
3529 	gnm_command_finalize (cmd);
3530 }
3531 
3532 gboolean
cmd_copyrel(WorkbookControl * wbc,int dx,int dy,char const * name)3533 cmd_copyrel (WorkbookControl *wbc,
3534 	     int dx, int dy,
3535 	     char const *name)
3536 {
3537 	CmdCopyRel *me;
3538 	GnmRange target, src;
3539 	SheetView *sv  = wb_control_cur_sheet_view (wbc);
3540 	Sheet *sheet = sv->sheet;
3541 	GnmRange const *selr =
3542 		selection_first_range (sv, GO_CMD_CONTEXT (wbc), name);
3543 
3544 	g_return_val_if_fail (dx == 0 || dy == 0, TRUE);
3545 
3546 	if (!selr)
3547 		return FALSE;
3548 
3549 	target = *selr;
3550 	range_normalize (&target);
3551 	src.start = src.end = target.start;
3552 
3553 	if (dy) {
3554 		src.end.col = target.end.col;
3555 		if (target.start.row != target.end.row)
3556 			target.start.row++;
3557 		else
3558 			src.start.row = src.end.row = (target.start.row + dy);
3559 	}
3560 
3561 	if (dx) {
3562 		src.end.row = target.end.row;
3563 		if (target.start.col != target.end.col)
3564 			target.start.col++;
3565 		else
3566 			src.start.col = src.end.col = (target.start.col + dx);
3567 	}
3568 
3569 	if (src.start.col < 0 || src.start.col >= gnm_sheet_get_max_cols (sheet) ||
3570 	    src.start.row < 0 || src.start.row >= gnm_sheet_get_max_rows (sheet))
3571 		return FALSE;
3572 
3573 	/* Check arrays or merged regions in src or target regions */
3574 	if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), name) ||
3575 	    sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), name))
3576 		return TRUE;
3577 
3578 	me = g_object_new (CMD_COPYREL_TYPE, NULL);
3579 
3580 	me->dst.sheet = sheet;
3581 	me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3582 	me->dst.range = target;
3583 	me->src.sheet = sheet;
3584 	me->src.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3585 	me->src.range = src;
3586 	me->dx = dx;
3587 	me->dy = dy;
3588 	me->name = name;
3589 	me->undo = clipboard_copy_range_undo (me->dst.sheet, &me->dst.range);
3590 
3591 	me->cmd.sheet = sheet;
3592 	me->cmd.size = 1;
3593 	me->cmd.cmd_descriptor = g_strdup (name);
3594 
3595 	return gnm_command_push_undo (wbc, G_OBJECT (me));
3596 }
3597 
3598 /******************************************************************/
3599 
3600 
3601 #define CMD_AUTOFORMAT_TYPE        (cmd_autoformat_get_type ())
3602 #define CMD_AUTOFORMAT(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFORMAT_TYPE, CmdAutoFormat))
3603 
3604 typedef struct {
3605 	GnmCellPos pos;
3606 	GnmStyleList *styles;
3607 } CmdAutoFormatOldStyle;
3608 
3609 typedef struct {
3610 	GnmCommand cmd;
3611 
3612 	GSList         *selection;   /* Selections on the sheet */
3613 	GSList         *old_styles;  /* Older styles, one style_list per selection range*/
3614 
3615 	GnmFT *ft;    /* Template that has been applied */
3616 } CmdAutoFormat;
3617 
3618 static void
cmd_autoformat_repeat(GnmCommand const * cmd,WorkbookControl * wbc)3619 cmd_autoformat_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3620 {
3621 	CmdAutoFormat const *orig = (CmdAutoFormat const *) cmd;
3622 	cmd_selection_autoformat (wbc, gnm_ft_clone (orig->ft));
3623 }
MAKE_GNM_COMMAND(CmdAutoFormat,cmd_autoformat,cmd_autoformat_repeat)3624 MAKE_GNM_COMMAND (CmdAutoFormat, cmd_autoformat, cmd_autoformat_repeat)
3625 
3626 static gboolean
3627 cmd_autoformat_undo (GnmCommand *cmd,
3628 		     G_GNUC_UNUSED WorkbookControl *wbc)
3629 {
3630 	CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3631 
3632 	g_return_val_if_fail (me != NULL, TRUE);
3633 
3634 	if (me->old_styles) {
3635 		GSList *l1 = me->old_styles;
3636 		GSList *l2 = me->selection;
3637 
3638 		for (; l1; l1 = l1->next, l2 = l2->next) {
3639 			GnmRange *r;
3640 			CmdAutoFormatOldStyle *os = l1->data;
3641 			GnmSpanCalcFlags flags = sheet_style_set_list (me->cmd.sheet,
3642 					    &os->pos, os->styles, NULL, NULL);
3643 
3644 			g_return_val_if_fail (l2 && l2->data, TRUE);
3645 
3646 			r = l2->data;
3647 			sheet_range_calc_spans (me->cmd.sheet, r, flags);
3648 			if (flags != GNM_SPANCALC_SIMPLE)
3649 				rows_height_update (me->cmd.sheet, r, TRUE);
3650 		}
3651 	}
3652 
3653 	return FALSE;
3654 }
3655 
3656 static gboolean
cmd_autoformat_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)3657 cmd_autoformat_redo (GnmCommand *cmd,
3658 		     G_GNUC_UNUSED WorkbookControl *wbc)
3659 {
3660 	CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3661 
3662 	g_return_val_if_fail (me != NULL, TRUE);
3663 
3664 	gnm_ft_apply_to_sheet_regions (me->ft,
3665 		me->cmd.sheet, me->selection);
3666 
3667 	return FALSE;
3668 }
3669 
3670 static void
cmd_autoformat_finalize(GObject * cmd)3671 cmd_autoformat_finalize (GObject *cmd)
3672 {
3673 	CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3674 
3675 	if (me->old_styles != NULL) {
3676 		GSList *l;
3677 
3678 		for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
3679 			CmdAutoFormatOldStyle *os = l->data;
3680 
3681 			if (os->styles)
3682 				style_list_free (os->styles);
3683 
3684 			g_free (os);
3685 		}
3686 
3687 		me->old_styles = NULL;
3688 	}
3689 
3690 	range_fragment_free (me->selection);
3691 	me->selection = NULL;
3692 
3693 	gnm_ft_free (me->ft);
3694 
3695 	gnm_command_finalize (cmd);
3696 }
3697 
3698 /**
3699  * cmd_selection_autoformat:
3700  * @wbc: the context.
3701  * @ft: The format template that was applied
3702  *
3703  * Returns: %TRUE if there was a problem, %FALSE otherwise.
3704  **/
3705 gboolean
cmd_selection_autoformat(WorkbookControl * wbc,GnmFT * ft)3706 cmd_selection_autoformat (WorkbookControl *wbc, GnmFT *ft)
3707 {
3708 	CmdAutoFormat *me;
3709 	char      *names;
3710 	GSList    *l;
3711 	SheetView *sv = wb_control_cur_sheet_view (wbc);
3712 
3713 	me = g_object_new (CMD_AUTOFORMAT_TYPE, NULL);
3714 
3715 	me->selection = selection_get_ranges (sv, FALSE); /* Regions may overlap */
3716 	me->ft  = ft;
3717 	me->cmd.sheet = sv_sheet (sv);
3718 	me->cmd.size = 1;  /* FIXME?  */
3719 
3720 	if (!gnm_ft_check_valid (ft, me->selection, GO_CMD_CONTEXT (wbc))) {
3721 		g_object_unref (me);
3722 		return TRUE;
3723 	}
3724 
3725 	me->old_styles = NULL;
3726 	for (l = me->selection; l; l = l->next) {
3727 		CmdFormatOldStyle *os;
3728 		GnmRange range = *((GnmRange const *) l->data);
3729 
3730 		/* Store the containing range to handle borders */
3731 		if (range.start.col > 0) range.start.col--;
3732 		if (range.start.row > 0) range.start.row--;
3733 		if (range.end.col < gnm_sheet_get_last_col (sv->sheet)) range.end.col++;
3734 		if (range.end.row < gnm_sheet_get_last_row (sv->sheet)) range.end.row++;
3735 
3736 		os = g_new (CmdFormatOldStyle, 1);
3737 
3738 		os->styles = sheet_style_get_range (me->cmd.sheet, &range);
3739 		os->pos = range.start;
3740 
3741 		me->old_styles = g_slist_append (me->old_styles, os);
3742 	}
3743 
3744 	names = undo_range_list_name (me->cmd.sheet, me->selection);
3745 	me->cmd.cmd_descriptor = g_strdup_printf (_("Autoformatting %s"),
3746 						     names);
3747 	g_free (names);
3748 
3749 	return gnm_command_push_undo (wbc, G_OBJECT (me));
3750 }
3751 
3752 /******************************************************************/
3753 
3754 #define CMD_UNMERGE_CELLS_TYPE        (cmd_unmerge_cells_get_type ())
3755 #define CMD_UNMERGE_CELLS(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_UNMERGE_CELLS_TYPE, CmdUnmergeCells))
3756 
3757 typedef struct {
3758 	GnmCommand cmd;
3759 
3760 	Sheet	*sheet;
3761 	GArray	*unmerged_regions;
3762 	GArray	*ranges;
3763 } CmdUnmergeCells;
3764 
3765 static void
cmd_unmerge_cells_repeat(G_GNUC_UNUSED GnmCommand const * cmd,WorkbookControl * wbc)3766 cmd_unmerge_cells_repeat (G_GNUC_UNUSED GnmCommand const *cmd, WorkbookControl *wbc)
3767 {
3768 	SheetView *sv = wb_control_cur_sheet_view (wbc);
3769 	GSList *range_list = selection_get_ranges (sv, FALSE);
3770 	cmd_unmerge_cells (wbc, sv_sheet (sv), range_list);
3771 	range_fragment_free (range_list);
3772 }
MAKE_GNM_COMMAND(CmdUnmergeCells,cmd_unmerge_cells,cmd_unmerge_cells_repeat)3773 MAKE_GNM_COMMAND (CmdUnmergeCells, cmd_unmerge_cells, cmd_unmerge_cells_repeat)
3774 
3775 static gboolean
3776 cmd_unmerge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3777 {
3778 	CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3779 	unsigned i;
3780 
3781 	g_return_val_if_fail (me != NULL, TRUE);
3782 	g_return_val_if_fail (me->unmerged_regions != NULL, TRUE);
3783 
3784 	for (i = 0 ; i < me->unmerged_regions->len ; ++i) {
3785 		GnmRange const *tmp = &(g_array_index (me->unmerged_regions, GnmRange, i));
3786 		sheet_redraw_range (me->cmd.sheet, tmp);
3787 		gnm_sheet_merge_add (me->cmd.sheet, tmp, TRUE, GO_CMD_CONTEXT (wbc));
3788 		sheet_range_calc_spans (me->cmd.sheet, tmp, GNM_SPANCALC_RE_RENDER);
3789 	}
3790 
3791 	g_array_free (me->unmerged_regions, TRUE);
3792 	me->unmerged_regions = NULL;
3793 
3794 	return FALSE;
3795 }
3796 
3797 static gboolean
cmd_unmerge_cells_redo(GnmCommand * cmd,WorkbookControl * wbc)3798 cmd_unmerge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3799 {
3800 	CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3801 	unsigned i;
3802 
3803 	g_return_val_if_fail (me != NULL, TRUE);
3804 	g_return_val_if_fail (me->unmerged_regions == NULL, TRUE);
3805 
3806 	me->unmerged_regions = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3807 	for (i = 0 ; i < me->ranges->len ; ++i) {
3808 		GSList *ptr, *merged = gnm_sheet_merge_get_overlap (me->cmd.sheet,
3809 			&(g_array_index (me->ranges, GnmRange, i)));
3810 		for (ptr = merged ; ptr != NULL ; ptr = ptr->next) {
3811 			GnmRange const *pr = ptr->data;
3812 			GnmRange const tmp = *pr;
3813 			g_array_append_val (me->unmerged_regions, tmp);
3814 			gnm_sheet_merge_remove (me->cmd.sheet, &tmp);
3815 			sheet_range_calc_spans (me->cmd.sheet, &tmp,
3816 						GNM_SPANCALC_RE_RENDER);
3817 		}
3818 		g_slist_free (merged);
3819 	}
3820 
3821 	return FALSE;
3822 }
3823 
3824 static void
cmd_unmerge_cells_finalize(GObject * cmd)3825 cmd_unmerge_cells_finalize (GObject *cmd)
3826 {
3827 	CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3828 
3829 	if (me->unmerged_regions != NULL) {
3830 		g_array_free (me->unmerged_regions, TRUE);
3831 		me->unmerged_regions = NULL;
3832 	}
3833 	if (me->ranges != NULL) {
3834 		g_array_free (me->ranges, TRUE);
3835 		me->ranges = NULL;
3836 	}
3837 
3838 	gnm_command_finalize (cmd);
3839 }
3840 
3841 /**
3842  * cmd_unmerge_cells:
3843  * @wbc: the context.
3844  * @sheet: #Sheet
3845  * @selection: (element-type GnmRange): selection.
3846  *
3847  * Returns: %TRUE if there was a problem, %FALSE otherwise.
3848  **/
3849 gboolean
cmd_unmerge_cells(WorkbookControl * wbc,Sheet * sheet,GSList const * selection)3850 cmd_unmerge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection)
3851 {
3852 	CmdUnmergeCells *me;
3853 	char *names;
3854 
3855 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3856 
3857 	me = g_object_new (CMD_UNMERGE_CELLS_TYPE, NULL);
3858 
3859 	me->cmd.sheet = sheet;
3860 	me->cmd.size = 1;
3861 
3862 	names = undo_range_list_name (sheet, selection);
3863 	me->cmd.cmd_descriptor = g_strdup_printf (_("Unmerging %s"), names);
3864 	g_free (names);
3865 
3866 	me->unmerged_regions = NULL;
3867 	me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3868 	for ( ; selection != NULL ; selection = selection->next) {
3869 		GSList *merged = gnm_sheet_merge_get_overlap (sheet, selection->data);
3870 		if (merged != NULL) {
3871 			g_array_append_val (me->ranges, *(GnmRange *)selection->data);
3872 			g_slist_free (merged);
3873 		}
3874 	}
3875 
3876 	if (me->ranges->len <= 0) {
3877 		g_object_unref (me);
3878 		return TRUE;
3879 	}
3880 
3881 	return gnm_command_push_undo (wbc, G_OBJECT (me));
3882 }
3883 
3884 /******************************************************************/
3885 
3886 #define CMD_MERGE_CELLS_TYPE        (cmd_merge_cells_get_type ())
3887 #define CMD_MERGE_CELLS(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_CELLS_TYPE, CmdMergeCells))
3888 
3889 typedef struct {
3890 	GnmCommand cmd;
3891 	GArray	*ranges;
3892 	GSList	*old_contents;
3893 	gboolean center;
3894 } CmdMergeCells;
3895 
3896 static void
3897 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc);
3898 
MAKE_GNM_COMMAND(CmdMergeCells,cmd_merge_cells,cmd_merge_cells_repeat)3899 MAKE_GNM_COMMAND (CmdMergeCells, cmd_merge_cells, cmd_merge_cells_repeat)
3900 
3901 static void
3902 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3903 {
3904 	SheetView *sv = wb_control_cur_sheet_view (wbc);
3905 	GSList *range_list = selection_get_ranges (sv, FALSE);
3906 	cmd_merge_cells (wbc, sv_sheet (sv), range_list,
3907 		CMD_MERGE_CELLS (cmd)->center);
3908 	range_fragment_free (range_list);
3909 }
3910 
3911 static gboolean
cmd_merge_cells_undo(GnmCommand * cmd,WorkbookControl * wbc)3912 cmd_merge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3913 {
3914 	CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3915 	unsigned i, flags;
3916 
3917 	g_return_val_if_fail (me != NULL, TRUE);
3918 
3919 	for (i = 0 ; i < me->ranges->len ; ++i) {
3920 		GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3921 		gnm_sheet_merge_remove (me->cmd.sheet, r);
3922 	}
3923 
3924 	/* Avoid pasting comments that are at 0,0.  Redo copies the target
3925 	 * region (including all comments) .  If there was a comment in the top
3926 	 * left we would end up duplicating it. */
3927 	flags = PASTE_CONTENTS | PASTE_FORMATS | PASTE_COMMENTS |
3928 		PASTE_IGNORE_COMMENTS_AT_ORIGIN;
3929 	if (me->center)
3930 		flags |= PASTE_FORMATS;
3931 	for (i = 0 ; i < me->ranges->len ; ++i) {
3932 		GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3933 		GnmPasteTarget pt;
3934 		GnmCellRegion * c;
3935 
3936 		g_return_val_if_fail (me->old_contents != NULL, TRUE);
3937 
3938 		c = me->old_contents->data;
3939 		clipboard_paste_region (c,
3940 			paste_target_init (&pt, me->cmd.sheet, r, flags),
3941 			GO_CMD_CONTEXT (wbc));
3942 		cellregion_unref (c);
3943 		me->old_contents = g_slist_remove (me->old_contents, c);
3944 	}
3945 	g_return_val_if_fail (me->old_contents == NULL, TRUE);
3946 
3947 	return FALSE;
3948 }
3949 
3950 static gboolean
cmd_merge_cells_redo(GnmCommand * cmd,WorkbookControl * wbc)3951 cmd_merge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3952 {
3953 	CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3954 	GnmStyle *align_center = NULL;
3955 	Sheet *sheet;
3956 	unsigned i;
3957 
3958 	g_return_val_if_fail (me != NULL, TRUE);
3959 
3960 	if (me->center) {
3961 		align_center = gnm_style_new ();
3962 		gnm_style_set_align_h (align_center, GNM_HALIGN_CENTER);
3963 	}
3964 	sheet = me->cmd.sheet;
3965 	for (i = 0 ; i < me->ranges->len ; ++i) {
3966 		GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3967 		GSList *ptr, *merged = gnm_sheet_merge_get_overlap (sheet, r);
3968 
3969 		/* save contents before removing contained merged regions */
3970 		me->old_contents = g_slist_prepend (me->old_contents,
3971 			clipboard_copy_range (sheet, r));
3972 		for (ptr = merged ; ptr != NULL ; ptr = ptr->next)
3973 			gnm_sheet_merge_remove (sheet, ptr->data);
3974 		g_slist_free (merged);
3975 
3976 		gnm_sheet_merge_add (sheet, r, TRUE, GO_CMD_CONTEXT (wbc));
3977 		if (me->center)
3978 			sheet_apply_style (me->cmd.sheet, r, align_center);
3979 	}
3980 
3981 	if (me->center)
3982 		gnm_style_unref (align_center);
3983 	me->old_contents = g_slist_reverse (me->old_contents);
3984 	return FALSE;
3985 }
3986 
3987 static void
cmd_merge_cells_finalize(GObject * cmd)3988 cmd_merge_cells_finalize (GObject *cmd)
3989 {
3990 	CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3991 
3992 	if (me->old_contents != NULL) {
3993 		GSList *l;
3994 		for (l = me->old_contents ; l != NULL ; l = g_slist_remove (l, l->data))
3995 			cellregion_unref (l->data);
3996 		me->old_contents = NULL;
3997 	}
3998 
3999 	if (me->ranges != NULL) {
4000 		g_array_free (me->ranges, TRUE);
4001 		me->ranges = NULL;
4002 	}
4003 
4004 	gnm_command_finalize (cmd);
4005 }
4006 
4007 /**
4008  * cmd_merge_cells:
4009  * @wbc: the context.
4010  * @sheet: #Sheet
4011  * @selection: (element-type GnmRange): selection.
4012  * @center:
4013  *
4014  * Returns: %TRUE if there was a problem, %FALSE otherwise.
4015  **/
4016 gboolean
cmd_merge_cells(WorkbookControl * wbc,Sheet * sheet,GSList const * selection,gboolean center)4017 cmd_merge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection,
4018 		 gboolean center)
4019 {
4020 	CmdMergeCells *me;
4021 	char *names;
4022 
4023 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4024 
4025 	me = g_object_new (CMD_MERGE_CELLS_TYPE, NULL);
4026 
4027 	me->cmd.sheet = sheet;
4028 	me->cmd.size = 1;
4029 
4030 	names = undo_range_list_name (sheet, selection);
4031 	me->cmd.cmd_descriptor =
4032 		g_strdup_printf ((center ? _("Merge and Center %s") :_("Merging %s")), names);
4033 	g_free (names);
4034 
4035 	me->center = center;
4036 	me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
4037 	for ( ; selection != NULL ; selection = selection->next) {
4038 		GnmRange const *exist;
4039 		GnmRange const *r = selection->data;
4040 		if (range_is_singleton (selection->data))
4041 			continue;
4042 		if (NULL != (exist = gnm_sheet_merge_is_corner (sheet, &r->start)) &&
4043 		    range_equal (r, exist))
4044 			continue;
4045 		g_array_append_val (me->ranges, *(GnmRange *)selection->data);
4046 	}
4047 
4048 	if (me->ranges->len <= 0) {
4049 		g_object_unref (me);
4050 		return TRUE;
4051 	}
4052 
4053 	return gnm_command_push_undo (wbc, G_OBJECT (me));
4054 }
4055 
4056 /******************************************************************/
4057 
4058 #define CMD_SEARCH_REPLACE_TYPE		(cmd_search_replace_get_type())
4059 #define CMD_SEARCH_REPLACE(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SEARCH_REPLACE_TYPE, CmdSearchReplace))
4060 
4061 typedef struct {
4062 	GnmCommand cmd;
4063 	GnmSearchReplace *sr;
4064 
4065 	/*
4066 	 * Undo/redo use this list of SearchReplaceItems to do their
4067 	 * work.  Note, that it is possible for a cell to occur
4068 	 * multiple times in the list.
4069 	 */
4070 	GList *cells;
4071 } CmdSearchReplace;
4072 
4073 MAKE_GNM_COMMAND (CmdSearchReplace, cmd_search_replace, NULL)
4074 
4075 typedef enum { SRI_text, SRI_comment } SearchReplaceItemType;
4076 
4077 typedef struct {
4078 	GnmEvalPos pos;
4079 	SearchReplaceItemType old_type, new_type;
4080 	union {
4081 		char *text;
4082 		char *comment;
4083 	} old, new;
4084 } SearchReplaceItem;
4085 
4086 
4087 static void
cmd_search_replace_update_after_action(CmdSearchReplace * me,WorkbookControl * wbc)4088 cmd_search_replace_update_after_action (CmdSearchReplace *me,
4089 					WorkbookControl *wbc)
4090 {
4091 	GList *tmp;
4092 	Sheet *last_sheet = NULL;
4093 
4094 	for (tmp = me->cells; tmp; tmp = tmp->next) {
4095 		SearchReplaceItem *sri = tmp->data;
4096 		if (sri->pos.sheet != last_sheet) {
4097 			last_sheet = sri->pos.sheet;
4098 			update_after_action (last_sheet, wbc);
4099 		}
4100 	}
4101 }
4102 
4103 
4104 static gboolean
cmd_search_replace_undo(GnmCommand * cmd,WorkbookControl * wbc)4105 cmd_search_replace_undo (GnmCommand *cmd,
4106 			 WorkbookControl *wbc)
4107 {
4108 	CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4109 	GList *tmp;
4110 
4111 	/* Undo does replacements backwards.  */
4112 	for (tmp = g_list_last (me->cells); tmp; tmp = tmp->prev) {
4113 		SearchReplaceItem *sri = tmp->data;
4114 		switch (sri->old_type) {
4115 		case SRI_text:
4116 		{
4117 			GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4118 						     sri->pos.eval.col,
4119 						     sri->pos.eval.row);
4120 			sheet_cell_set_text (cell, sri->old.text, NULL);
4121 			break;
4122 		}
4123 		case SRI_comment:
4124 		{
4125 			GnmComment *comment =
4126 				sheet_get_comment (sri->pos.sheet,
4127 						      &sri->pos.eval);
4128 			if (comment) {
4129 				cell_comment_text_set (comment, sri->old.comment);
4130 			} else {
4131 				g_warning ("Undo/redo broken.");
4132 			}
4133 		}
4134 		break;
4135 		}
4136 	}
4137 	cmd_search_replace_update_after_action (me, wbc);
4138 
4139 	return FALSE;
4140 }
4141 
4142 static gboolean
cmd_search_replace_redo(GnmCommand * cmd,WorkbookControl * wbc)4143 cmd_search_replace_redo (GnmCommand *cmd,
4144 			 WorkbookControl *wbc)
4145 {
4146 	CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4147 	GList *tmp;
4148 
4149 	/* Redo does replacements forward.  */
4150 	for (tmp = me->cells; tmp; tmp = tmp->next) {
4151 		SearchReplaceItem *sri = tmp->data;
4152 		switch (sri->new_type) {
4153 		case SRI_text:
4154 		{
4155 			GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4156 						     sri->pos.eval.col,
4157 						     sri->pos.eval.row);
4158 			sheet_cell_set_text (cell, sri->new.text, NULL);
4159 			break;
4160 		}
4161 		case SRI_comment:
4162 		{
4163 			GnmComment *comment =
4164 				sheet_get_comment (sri->pos.sheet,
4165 						      &sri->pos.eval);
4166 			if (comment) {
4167 				cell_comment_text_set (comment, sri->new.comment);
4168 			} else {
4169 				g_warning ("Undo/redo broken.");
4170 			}
4171 		}
4172 		break;
4173 		}
4174 	}
4175 	cmd_search_replace_update_after_action (me, wbc);
4176 
4177 	return FALSE;
4178 }
4179 
4180 static gboolean
cmd_search_replace_do_cell(CmdSearchReplace * me,GnmEvalPos * ep,gboolean test_run)4181 cmd_search_replace_do_cell (CmdSearchReplace *me, GnmEvalPos *ep,
4182 			    gboolean test_run)
4183 {
4184 	GnmSearchReplace *sr = me->sr;
4185 
4186 	GnmSearchReplaceCellResult cell_res;
4187 	GnmSearchReplaceCommentResult comment_res;
4188 
4189 	if (gnm_search_replace_cell (sr, ep, TRUE, &cell_res)) {
4190 		GnmExprTop const *texpr;
4191 		GnmValue *val;
4192 		gboolean err;
4193 		GnmParsePos pp;
4194 
4195 		parse_pos_init_evalpos (&pp, ep);
4196 		parse_text_value_or_expr (&pp, cell_res.new_text, &val, &texpr);
4197 
4198 		/*
4199 		 * FIXME: this is a hack, but parse_text_value_or_expr
4200 		 * does not have a better way of signaling an error.
4201 		 */
4202 		err = (val &&
4203 		       gnm_expr_char_start_p (cell_res.new_text) &&
4204 		       !go_format_is_text (gnm_cell_get_format (cell_res.cell)));
4205 		value_release (val);
4206 		if (texpr) gnm_expr_top_unref (texpr);
4207 
4208 		if (err) {
4209 			if (test_run) {
4210 				gnm_search_replace_query_fail (sr, &cell_res);
4211 				g_free (cell_res.old_text);
4212 				g_free (cell_res.new_text);
4213 				return TRUE;
4214 			} else {
4215 				switch (sr->error_behaviour) {
4216 				case GNM_SRE_ERROR: {
4217 					GnmExprTop const *ee =
4218 						gnm_expr_top_new
4219 						(gnm_expr_new_funcall1
4220 						 (gnm_func_lookup ("ERROR", NULL),
4221 						  gnm_expr_new_constant
4222 						  (value_new_string_nocopy (cell_res.new_text))));
4223 					GnmConventionsOut out;
4224 
4225 					out.accum = g_string_new ("=");
4226 					out.pp = &pp;
4227 					out.convs = pp.sheet->convs;
4228 					gnm_expr_top_as_gstring (ee, &out);
4229 					gnm_expr_top_unref (ee);
4230 					cell_res.new_text = g_string_free (out.accum, FALSE);
4231 					err = FALSE;
4232 					break;
4233 				}
4234 				case GNM_SRE_STRING: {
4235 					GString *s = g_string_new ("'");
4236 					g_string_append (s, cell_res.new_text);
4237 					g_free (cell_res.new_text);
4238 					cell_res.new_text = g_string_free (s, FALSE);
4239 					err = FALSE;
4240 					break;
4241 				}
4242 				case GNM_SRE_FAIL:
4243 					g_assert_not_reached ();
4244 				case GNM_SRE_SKIP:
4245 				default:
4246 					; /* Nothing */
4247 				}
4248 			}
4249 		}
4250 
4251 		if (!err && !test_run) {
4252 			int res = gnm_search_replace_query_cell
4253 				(sr, &cell_res);
4254 			gboolean doit = (res == GTK_RESPONSE_YES);
4255 
4256 			if (res == GTK_RESPONSE_CANCEL) {
4257 				g_free (cell_res.old_text);
4258 				g_free (cell_res.new_text);
4259 				return TRUE;
4260 			}
4261 
4262 			if (doit) {
4263 				SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4264 
4265 				sheet_cell_set_text (cell_res.cell, cell_res.new_text, NULL);
4266 
4267 				sri->pos = *ep;
4268 				sri->old_type = sri->new_type = SRI_text;
4269 				sri->old.text = cell_res.old_text;
4270 				sri->new.text = cell_res.new_text;
4271 				me->cells = g_list_prepend (me->cells, sri);
4272 
4273 				cell_res.old_text = cell_res.new_text = NULL;
4274 			}
4275 		}
4276 
4277 		g_free (cell_res.new_text);
4278 		g_free (cell_res.old_text);
4279 	}
4280 
4281 	if (!test_run &&
4282 	    gnm_search_replace_comment (sr, ep, TRUE, &comment_res)) {
4283 		int res = gnm_search_replace_query_comment
4284 			(sr, ep, &comment_res);
4285 		gboolean doit = (res == GTK_RESPONSE_YES);
4286 
4287 		if (doit) {
4288 			SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4289 			sri->pos = *ep;
4290 			sri->old_type = sri->new_type = SRI_comment;
4291 			sri->old.comment = g_strdup (comment_res.old_text);
4292 			sri->new.comment = comment_res.new_text;
4293 			me->cells = g_list_prepend (me->cells, sri);
4294 
4295 			cell_comment_text_set (comment_res.comment, comment_res.new_text);
4296 		} else {
4297 			g_free (comment_res.new_text);
4298 			if (res == GTK_RESPONSE_CANCEL)
4299 				return TRUE;
4300 		}
4301 	}
4302 
4303 	return FALSE;
4304 }
4305 
4306 
4307 static gboolean
cmd_search_replace_do(CmdSearchReplace * me,gboolean test_run,WorkbookControl * wbc)4308 cmd_search_replace_do (CmdSearchReplace *me, gboolean test_run,
4309 		       WorkbookControl *wbc)
4310 {
4311 	GnmSearchReplace *sr = me->sr;
4312 	GPtrArray *cells;
4313 	gboolean result = FALSE;
4314 	unsigned i;
4315 
4316 	if (test_run) {
4317 		switch (sr->error_behaviour) {
4318 		case GNM_SRE_SKIP:
4319 		case GNM_SRE_QUERY:
4320 		case GNM_SRE_ERROR:
4321 		case GNM_SRE_STRING:
4322 			/* An error is not a problem.  */
4323 			return FALSE;
4324 
4325 		case GNM_SRE_FAIL:
4326 			; /* Nothing.  */
4327 		}
4328 	}
4329 
4330 	cells = gnm_search_collect_cells (sr);
4331 
4332 	for (i = 0; i < cells->len; i++) {
4333 		GnmEvalPos *ep = g_ptr_array_index (cells, i);
4334 
4335 		if (cmd_search_replace_do_cell (me, ep, test_run)) {
4336 			result = TRUE;
4337 			break;
4338 		}
4339 	}
4340 
4341 	gnm_search_collect_cells_free (cells);
4342 
4343 	if (!test_run) {
4344 		/* Cells were added in the wrong order.  Correct.  */
4345 		me->cells = g_list_reverse (me->cells);
4346 
4347 		cmd_search_replace_update_after_action (me, wbc);
4348 	}
4349 
4350 	return result;
4351 }
4352 
4353 
4354 static void
cmd_search_replace_finalize(GObject * cmd)4355 cmd_search_replace_finalize (GObject *cmd)
4356 {
4357 	CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4358 	GList *tmp;
4359 
4360 	for (tmp = me->cells; tmp; tmp = tmp->next) {
4361 		SearchReplaceItem *sri = tmp->data;
4362 		switch (sri->old_type) {
4363 		case SRI_text:
4364 			g_free (sri->old.text);
4365 			break;
4366 		case SRI_comment:
4367 			g_free (sri->old.comment);
4368 			break;
4369 		}
4370 		switch (sri->new_type) {
4371 		case SRI_text:
4372 			g_free (sri->new.text);
4373 			break;
4374 		case SRI_comment:
4375 			g_free (sri->new.comment);
4376 			break;
4377 		}
4378 		g_free (sri);
4379 	}
4380 	g_list_free (me->cells);
4381 	g_object_unref (me->sr);
4382 
4383 	gnm_command_finalize (cmd);
4384 }
4385 
4386 gboolean
cmd_search_replace(WorkbookControl * wbc,GnmSearchReplace * sr)4387 cmd_search_replace (WorkbookControl *wbc, GnmSearchReplace *sr)
4388 {
4389 	CmdSearchReplace *me;
4390 
4391 	g_return_val_if_fail (sr != NULL, TRUE);
4392 
4393 	me = g_object_new (CMD_SEARCH_REPLACE_TYPE, NULL);
4394 
4395 	me->cells = NULL;
4396 	me->sr = g_object_ref (sr);
4397 
4398 	me->cmd.sheet = NULL;
4399 	me->cmd.size = 1;  /* Corrected below. */
4400 	me->cmd.cmd_descriptor = g_strdup (_("Search and Replace"));
4401 
4402 	if (cmd_search_replace_do (me, TRUE, wbc)) {
4403 		/* There was an error and nothing was done.  */
4404 		g_object_unref (me);
4405 		return TRUE;
4406 	}
4407 
4408 	cmd_search_replace_do (me, FALSE, wbc);
4409 	me->cmd.size += g_list_length (me->cells);
4410 
4411 	command_register_undo (wbc, G_OBJECT (me));
4412 	return FALSE;
4413 }
4414 
4415 /******************************************************************/
4416 
4417 #define CMD_COLROW_STD_SIZE_TYPE        (cmd_colrow_std_size_get_type ())
4418 #define CMD_COLROW_STD_SIZE(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_STD_SIZE_TYPE, CmdColRowStdSize))
4419 
4420 typedef struct {
4421 	GnmCommand cmd;
4422 
4423 	Sheet		*sheet;
4424 	gboolean	 is_cols;
4425 	double		 new_default;
4426 	double           old_default;
4427 } CmdColRowStdSize;
4428 
MAKE_GNM_COMMAND(CmdColRowStdSize,cmd_colrow_std_size,NULL)4429 MAKE_GNM_COMMAND (CmdColRowStdSize, cmd_colrow_std_size, NULL)
4430 
4431 static gboolean
4432 cmd_colrow_std_size_undo (GnmCommand *cmd,
4433 			  G_GNUC_UNUSED WorkbookControl *wbc)
4434 {
4435 	CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4436 
4437 	g_return_val_if_fail (me != NULL, TRUE);
4438 	g_return_val_if_fail (me->old_default != 0, TRUE);
4439 
4440 	if (me->is_cols)
4441 		sheet_col_set_default_size_pts (me->sheet, me->old_default);
4442 	else
4443 		sheet_row_set_default_size_pts (me->sheet, me->old_default);
4444 
4445 	me->old_default = 0;
4446 
4447 	return FALSE;
4448 }
4449 
4450 static gboolean
cmd_colrow_std_size_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)4451 cmd_colrow_std_size_redo (GnmCommand *cmd,
4452 			  G_GNUC_UNUSED WorkbookControl *wbc)
4453 {
4454 	CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4455 
4456 	g_return_val_if_fail (me != NULL, TRUE);
4457 	g_return_val_if_fail (me->old_default == 0, TRUE);
4458 
4459 	if (me->is_cols) {
4460 		me->old_default = sheet_col_get_default_size_pts (me->sheet);
4461 		sheet_col_set_default_size_pts (me->sheet, me->new_default);
4462 	} else {
4463 		me->old_default = sheet_row_get_default_size_pts (me->sheet);
4464 		sheet_row_set_default_size_pts (me->sheet, me->new_default);
4465 	}
4466 
4467 	return FALSE;
4468 }
4469 static void
cmd_colrow_std_size_finalize(GObject * cmd)4470 cmd_colrow_std_size_finalize (GObject *cmd)
4471 {
4472 	gnm_command_finalize (cmd);
4473 }
4474 
4475 gboolean
cmd_colrow_std_size(WorkbookControl * wbc,Sheet * sheet,gboolean is_cols,double new_default)4476 cmd_colrow_std_size (WorkbookControl *wbc, Sheet *sheet,
4477 		     gboolean is_cols, double new_default)
4478 {
4479 	CmdColRowStdSize *me;
4480 
4481 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4482 
4483 	me = g_object_new (CMD_COLROW_STD_SIZE_TYPE, NULL);
4484 
4485 	me->sheet = sheet;
4486 	me->is_cols = is_cols;
4487 	me->new_default = new_default;
4488 	me->old_default = 0;
4489 
4490 	me->cmd.sheet = sheet;
4491 	me->cmd.size = 1;  /* Changed in initial redo.  */
4492 	me->cmd.cmd_descriptor = is_cols
4493 		? g_strdup_printf (_("Setting default width of columns to %.2fpts"), new_default)
4494 		: g_strdup_printf (_("Setting default height of rows to %.2fpts"), new_default);
4495 
4496 	return gnm_command_push_undo (wbc, G_OBJECT (me));
4497 }
4498 
4499 /******************************************************************/
4500 
4501 #define CMD_ZOOM_TYPE        (cmd_zoom_get_type ())
4502 #define CMD_ZOOM(o)     (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ZOOM_TYPE, CmdZoom))
4503 
4504 typedef struct {
4505 	GnmCommand cmd;
4506 
4507 	GSList		*sheets;
4508 	double		 new_factor;
4509 	double          *old_factors;
4510 } CmdZoom;
4511 
MAKE_GNM_COMMAND(CmdZoom,cmd_zoom,NULL)4512 MAKE_GNM_COMMAND (CmdZoom, cmd_zoom, NULL)
4513 
4514 static gboolean
4515 cmd_zoom_undo (GnmCommand *cmd,
4516 	       G_GNUC_UNUSED WorkbookControl *wbc)
4517 {
4518 	CmdZoom *me = CMD_ZOOM (cmd);
4519 	GSList *l;
4520 	int i;
4521 
4522 	g_return_val_if_fail (me != NULL, TRUE);
4523 	g_return_val_if_fail (me->sheets != NULL, TRUE);
4524 	g_return_val_if_fail (me->old_factors != NULL, TRUE);
4525 
4526 	for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4527 		Sheet *sheet = l->data;
4528 		g_object_set (sheet, "zoom-factor", me->old_factors[i], NULL);
4529 	}
4530 
4531 	return FALSE;
4532 }
4533 
4534 static gboolean
cmd_zoom_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)4535 cmd_zoom_redo (GnmCommand *cmd,
4536 	       G_GNUC_UNUSED WorkbookControl *wbc)
4537 {
4538 	CmdZoom *me = CMD_ZOOM (cmd);
4539 	GSList *l;
4540 
4541 	g_return_val_if_fail (me != NULL, TRUE);
4542 	g_return_val_if_fail (me->sheets != NULL, TRUE);
4543 
4544 	for (l = me->sheets; l != NULL; l = l->next) {
4545 		Sheet *sheet = l->data;
4546 		g_object_set (sheet, "zoom-factor", me->new_factor, NULL);
4547 	}
4548 
4549 	return FALSE;
4550 }
4551 
4552 static void
cmd_zoom_finalize(GObject * cmd)4553 cmd_zoom_finalize (GObject *cmd)
4554 {
4555 	CmdZoom *me = CMD_ZOOM (cmd);
4556 
4557 	g_slist_free (me->sheets);
4558 	g_free (me->old_factors);
4559 
4560 	gnm_command_finalize (cmd);
4561 }
4562 
4563 /**
4564  * cmd_zoom:
4565  * @wbc: #WorkbookControl
4566  * @sheets: (element-type Sheet) (transfer container):
4567  * @factor:
4568  *
4569  * Returns: %TRUE if there was a problem, %FALSE otherwise.
4570  **/
4571 gboolean
cmd_zoom(WorkbookControl * wbc,GSList * sheets,double factor)4572 cmd_zoom (WorkbookControl *wbc, GSList *sheets, double factor)
4573 {
4574 	CmdZoom *me;
4575 	GString *namelist;
4576 	GSList *l;
4577 	int i;
4578 
4579 	g_return_val_if_fail (wbc != NULL, TRUE);
4580 	g_return_val_if_fail (sheets != NULL, TRUE);
4581 
4582 	me = g_object_new (CMD_ZOOM_TYPE, NULL);
4583 
4584 	me->sheets = sheets;
4585 	me->old_factors = g_new0 (double, g_slist_length (sheets));
4586 	me->new_factor  = factor;
4587 
4588 	/* Make a list of all sheets to zoom and save zoom factor for each */
4589 	namelist = g_string_new (NULL);
4590 	for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4591 		Sheet *sheet = l->data;
4592 
4593 		g_string_append (namelist, sheet->name_unquoted);
4594 		me->old_factors[i] = sheet->last_zoom_factor_used;
4595 
4596 		if (l->next)
4597 			g_string_append (namelist, ", ");
4598 	}
4599 
4600 	/* Make sure the string doesn't get overly wide */
4601 	gnm_cmd_trunc_descriptor (namelist, NULL);
4602 
4603 	me->cmd.sheet = NULL;
4604 	me->cmd.size = 1;
4605 	me->cmd.cmd_descriptor =
4606 		g_strdup_printf (_("Zoom %s to %.0f%%"), namelist->str, factor * 100);
4607 
4608 	g_string_free (namelist, TRUE);
4609 
4610 	return gnm_command_push_undo (wbc, G_OBJECT (me));
4611 }
4612 
4613 /******************************************************************/
4614 
4615 #define CMD_OBJECTS_DELETE_TYPE (cmd_objects_delete_get_type ())
4616 #define CMD_OBJECTS_DELETE(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECTS_DELETE_TYPE, CmdObjectsDelete))
4617 
4618 typedef struct {
4619 	GnmCommand cmd;
4620 	GSList *objects;
4621 	GArray *location;
4622 } CmdObjectsDelete;
4623 
MAKE_GNM_COMMAND(CmdObjectsDelete,cmd_objects_delete,NULL)4624 MAKE_GNM_COMMAND (CmdObjectsDelete, cmd_objects_delete, NULL)
4625 
4626 static gboolean
4627 cmd_objects_delete_redo (GnmCommand *cmd,
4628 			G_GNUC_UNUSED WorkbookControl *wbc)
4629 {
4630 	CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4631 	g_slist_foreach (me->objects, (GFunc) sheet_object_clear_sheet, NULL);
4632 	return FALSE;
4633 }
4634 
4635 static void
cmd_objects_restore_location(SheetObject * so,gint location)4636 cmd_objects_restore_location (SheetObject *so, gint location)
4637 {
4638 	gint loc = sheet_object_get_stacking (so);
4639 	if (loc != location)
4640 		sheet_object_adjust_stacking(so, location - loc);
4641 }
4642 
4643 static gboolean
cmd_objects_delete_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)4644 cmd_objects_delete_undo (GnmCommand *cmd,
4645 			G_GNUC_UNUSED WorkbookControl *wbc)
4646 {
4647 	CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4648 	GSList *l;
4649 	gint i;
4650 
4651 	g_slist_foreach (me->objects,
4652 		(GFunc) sheet_object_set_sheet, me->cmd.sheet);
4653 
4654 	for (l = me->objects, i = 0; l; l = l->next, i++)
4655 		cmd_objects_restore_location (GNM_SO (l->data),
4656 					      g_array_index(me->location,
4657 							    gint, i));
4658 	return FALSE;
4659 }
4660 
4661 static void
cmd_objects_delete_finalize(GObject * cmd)4662 cmd_objects_delete_finalize (GObject *cmd)
4663 {
4664 	CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4665 	g_slist_free_full (me->objects, g_object_unref);
4666 	if (me->location) {
4667 		g_array_free (me->location, TRUE);
4668 		me->location = NULL;
4669 	}
4670 	gnm_command_finalize (cmd);
4671 }
4672 
4673 static void
cmd_objects_store_location(SheetObject * so,GArray * location)4674 cmd_objects_store_location (SheetObject *so, GArray *location)
4675 {
4676 	gint loc = sheet_object_get_stacking (so);
4677 	g_array_append_val (location, loc);
4678 }
4679 
4680 /**
4681  * cmd_objects_delete:
4682  * @wbc: #WorkbookControl
4683  * @objects: (element-type SheetObject) (transfer container): the objects to
4684  * delete.
4685  * @name:
4686  *
4687  * Returns: %TRUE if there was a problem, %FALSE otherwise.
4688  **/
4689 gboolean
cmd_objects_delete(WorkbookControl * wbc,GSList * objects,char const * name)4690 cmd_objects_delete (WorkbookControl *wbc, GSList *objects,
4691 		    char const *name)
4692 {
4693 	CmdObjectsDelete *me;
4694 
4695 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4696 	g_return_val_if_fail (objects != NULL, TRUE);
4697 
4698 	me = g_object_new (CMD_OBJECTS_DELETE_TYPE, NULL);
4699 
4700 	me->objects = objects;
4701 	g_slist_foreach (me->objects, (GFunc) g_object_ref, NULL);
4702 
4703 	me->location = g_array_new (FALSE, FALSE, sizeof (gint));
4704 	g_slist_foreach (me->objects, (GFunc) cmd_objects_store_location,
4705 			 me->location);
4706 
4707 	me->cmd.sheet = sheet_object_get_sheet (objects->data);
4708 	me->cmd.size = 1;
4709 	me->cmd.cmd_descriptor = g_strdup (name ? name : _("Delete Object"));
4710 
4711 	return gnm_command_push_undo (wbc, G_OBJECT (me));
4712 }
4713 
4714 /******************************************************************/
4715 
4716 /**
4717  * cmd_objects_move:
4718  * @wbc: #WorkbookControl
4719  * @objects: (element-type SheetObject) (transfer container): the objects to move.
4720  * @anchors: (element-type SheetObjectAnchor) (transfer full): the anchors for the objects.
4721  * @objects_created:
4722  * @name:
4723  *
4724  * Returns: %TRUE if there was a problem, %FALSE otherwise.
4725  **/
4726 gboolean
cmd_objects_move(WorkbookControl * wbc,GSList * objects,GSList * anchors,gboolean objects_created,char const * name)4727 cmd_objects_move (WorkbookControl *wbc, GSList *objects, GSList *anchors,
4728 		  gboolean objects_created, char const *name)
4729 {
4730 	GOUndo *undo = NULL;
4731 	GOUndo *redo = NULL;
4732 	gboolean result = TRUE;
4733 
4734 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4735 
4736 	undo = sheet_object_move_undo (objects, objects_created);
4737 	redo = sheet_object_move_do (objects, anchors, objects_created);
4738 
4739 	result = cmd_generic (wbc, name, undo, redo);
4740 
4741 	g_slist_free (objects);
4742 	g_slist_free_full (anchors, g_free);
4743 
4744 	return result;
4745 }
4746 
4747 /******************************************************************/
4748 
4749 #define CMD_REORGANIZE_SHEETS_TYPE        (cmd_reorganize_sheets_get_type ())
4750 #define CMD_REORGANIZE_SHEETS(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REORGANIZE_SHEETS_TYPE, CmdReorganizeSheets))
4751 
4752 typedef struct {
4753 	GnmCommand cmd;
4754 	Workbook *wb;
4755 	WorkbookSheetState *old;
4756 	WorkbookSheetState *new;
4757 	gboolean first;
4758 	Sheet *undo_sheet;
4759 	Sheet *redo_sheet;
4760 } CmdReorganizeSheets;
4761 
MAKE_GNM_COMMAND(CmdReorganizeSheets,cmd_reorganize_sheets,NULL)4762 MAKE_GNM_COMMAND (CmdReorganizeSheets, cmd_reorganize_sheets, NULL)
4763 
4764 static gboolean
4765 cmd_reorganize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4766 {
4767 	CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4768 	workbook_sheet_state_restore (me->wb, me->old);
4769 	if (me->undo_sheet) {
4770 		WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4771 			  wb_control_sheet_focus (control, me->undo_sheet););
4772 	}
4773 	return FALSE;
4774 }
4775 
4776 static gboolean
cmd_reorganize_sheets_redo(GnmCommand * cmd,WorkbookControl * wbc)4777 cmd_reorganize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4778 {
4779 	CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4780 
4781 	if (me->first)
4782 		me->first = FALSE;
4783 	else {
4784 		workbook_sheet_state_restore (me->wb, me->new);
4785 		if (me->redo_sheet) {
4786 			WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4787 						       wb_control_sheet_focus (control, me->redo_sheet););
4788 		}
4789 	}
4790 
4791 	return FALSE;
4792 }
4793 
4794 static void
cmd_reorganize_sheets_finalize(GObject * cmd)4795 cmd_reorganize_sheets_finalize (GObject *cmd)
4796 {
4797 	CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4798 
4799 	if (me->old)
4800 		workbook_sheet_state_unref (me->old);
4801 	if (me->new)
4802 		workbook_sheet_state_unref (me->new);
4803 
4804 	gnm_command_finalize (cmd);
4805 }
4806 
4807 gboolean
cmd_reorganize_sheets(WorkbookControl * wbc,WorkbookSheetState * old_state,Sheet * undo_sheet)4808 cmd_reorganize_sheets (WorkbookControl *wbc,
4809 		       WorkbookSheetState *old_state,
4810 		       Sheet *undo_sheet)
4811 {
4812 	CmdReorganizeSheets *me;
4813 	Workbook *wb = wb_control_get_workbook (wbc);
4814 
4815 	me = g_object_new (CMD_REORGANIZE_SHEETS_TYPE, NULL);
4816 	me->wb = wb;
4817 	me->old = old_state;
4818 	me->new = workbook_sheet_state_new (me->wb);
4819 	me->first = TRUE;
4820 	me->undo_sheet = undo_sheet;
4821 	me->redo_sheet = wb_control_cur_sheet (wbc);
4822 
4823 	me->cmd.sheet = NULL;
4824 	me->cmd.size = workbook_sheet_state_size (me->old) +
4825 		workbook_sheet_state_size (me->new);
4826 	me->cmd.cmd_descriptor =
4827 		workbook_sheet_state_diff (me->old, me->new);
4828 
4829 	if (me->cmd.cmd_descriptor)
4830 		return gnm_command_push_undo (wbc, G_OBJECT (me));
4831 
4832 	/* No change.  */
4833 	g_object_unref (me);
4834 	return FALSE;
4835 }
4836 
4837 /******************************************************************/
4838 
4839 gboolean
cmd_rename_sheet(WorkbookControl * wbc,Sheet * sheet,char const * new_name)4840 cmd_rename_sheet (WorkbookControl *wbc,
4841 		  Sheet *sheet,
4842 		  char const *new_name)
4843 {
4844 	WorkbookSheetState *old_state;
4845 	Sheet *collision;
4846 
4847 	g_return_val_if_fail (new_name != NULL, TRUE);
4848 	g_return_val_if_fail (sheet != NULL, TRUE);
4849 
4850 	if (*new_name == 0) {
4851 		go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), _("Sheet names must be non-empty."));
4852 		return TRUE;
4853 	}
4854 
4855 	collision = workbook_sheet_by_name (sheet->workbook, new_name);
4856 	if (collision && collision != sheet) {
4857 		GError *err = g_error_new (go_error_invalid(), 0,
4858 					   _("A workbook cannot have two sheets with the same name."));
4859 		go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
4860 		g_error_free (err);
4861 		return TRUE;
4862 	}
4863 
4864 	old_state = workbook_sheet_state_new (sheet->workbook);
4865 	g_object_set (sheet, "name", new_name, NULL);
4866 	return cmd_reorganize_sheets (wbc, old_state, sheet);
4867 }
4868 
4869 /******************************************************************/
4870 
4871 #define CMD_RESIZE_SHEETS_TYPE        (cmd_resize_sheets_get_type ())
4872 #define CMD_RESIZE_SHEETS(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESIZE_SHEETS_TYPE, CmdResizeSheets))
4873 
4874 typedef struct {
4875 	GnmCommand cmd;
4876 	GSList *sheets;
4877 	int cols, rows;
4878 	GOUndo *undo;
4879 } CmdResizeSheets;
4880 
MAKE_GNM_COMMAND(CmdResizeSheets,cmd_resize_sheets,NULL)4881 MAKE_GNM_COMMAND (CmdResizeSheets, cmd_resize_sheets, NULL)
4882 
4883 static gboolean
4884 cmd_resize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4885 {
4886 	CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4887 	GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4888 
4889 	go_undo_undo_with_data (me->undo, cc);
4890 	g_object_unref (me->undo);
4891 	me->undo = NULL;
4892 
4893 	return FALSE;
4894 }
4895 
4896 static gboolean
cmd_resize_sheets_redo(GnmCommand * cmd,WorkbookControl * wbc)4897 cmd_resize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4898 {
4899 	CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4900 	GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4901 	GSList *l;
4902 
4903 	for (l = me->sheets; l; l = l->next) {
4904 		Sheet *sheet = l->data;
4905 		gboolean err;
4906 		GOUndo *u = gnm_sheet_resize (sheet, me->cols, me->rows,
4907 					      cc, &err);
4908 		me->undo = go_undo_combine (me->undo, u);
4909 
4910 		if (err) {
4911 			if (me->undo)
4912 				go_undo_undo_with_data (me->undo, cc);
4913 			return TRUE;
4914 		}
4915 	}
4916 
4917 	return FALSE;
4918 }
4919 
4920 static void
cmd_resize_sheets_finalize(GObject * cmd)4921 cmd_resize_sheets_finalize (GObject *cmd)
4922 {
4923 	CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4924 
4925 	g_slist_free (me->sheets);
4926 	if (me->undo) {
4927 		g_object_unref (me->undo);
4928 		me->undo = NULL;
4929 	}
4930 
4931 	gnm_command_finalize (cmd);
4932 }
4933 
4934 /**
4935  * cmd_resize_sheets:
4936  * @wbc: #WorkbookControl
4937  * @sheets: (element-type Sheet) (transfer container): the sheets to resize.
4938  * @cols: new columns number.
4939  * @rows: new rows number.
4940  *
4941  * Returns: %TRUE if there was a problem, %FALSE otherwise.
4942  **/
4943 gboolean
cmd_resize_sheets(WorkbookControl * wbc,GSList * sheets,int cols,int rows)4944 cmd_resize_sheets (WorkbookControl *wbc,
4945 		   GSList *sheets,
4946 		   int cols, int rows)
4947 {
4948 	CmdResizeSheets *me;
4949 
4950 	me = g_object_new (CMD_RESIZE_SHEETS_TYPE, NULL);
4951 	me->sheets = sheets;
4952 	me->cols = cols;
4953 	me->rows = rows;
4954 	me->cmd.sheet = sheets ? sheets->data : NULL;
4955 	me->cmd.size = 1;
4956 	me->cmd.cmd_descriptor = g_strdup (_("Resizing sheet"));
4957 
4958 	if (sheets &&
4959 	    gnm_sheet_valid_size (cols, rows))
4960 		return gnm_command_push_undo (wbc, G_OBJECT (me));
4961 
4962 	/* No change.  */
4963 	g_object_unref (me);
4964 	return FALSE;
4965 }
4966 
4967 /******************************************************************/
4968 
4969 #define CMD_SET_COMMENT_TYPE        (cmd_set_comment_get_type ())
4970 #define CMD_SET_COMMENT(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SET_COMMENT_TYPE, CmdSetComment))
4971 
4972 typedef struct {
4973 	GnmCommand cmd;
4974 
4975 	Sheet           *sheet;
4976 	GnmCellPos	        pos;
4977 	gchar		*new_text;
4978 	gchar		*old_text;
4979 	gchar		*new_author;
4980 	gchar		*old_author;
4981 	PangoAttrList   *old_attributes;
4982 	PangoAttrList   *new_attributes;
4983 } CmdSetComment;
4984 
MAKE_GNM_COMMAND(CmdSetComment,cmd_set_comment,NULL)4985 MAKE_GNM_COMMAND (CmdSetComment, cmd_set_comment, NULL)
4986 
4987 static gboolean
4988 cmd_set_comment_apply (Sheet *sheet, GnmCellPos *pos,
4989 		       char const *text, PangoAttrList *attributes,
4990 		       char const *author)
4991 {
4992 	GnmComment   *comment;
4993 	Workbook *wb = sheet->workbook;
4994 
4995 	comment = sheet_get_comment (sheet, pos);
4996 	if (comment) {
4997 		if (text)
4998 			g_object_set (G_OBJECT (comment), "text", text,
4999 				      "author", author,
5000 				      "markup", attributes, NULL);
5001 		else {
5002 			GnmRange const *mr;
5003 
5004 			mr = gnm_sheet_merge_contains_pos (sheet, pos);
5005 
5006 			if (mr)
5007 				sheet_objects_clear (sheet, mr, GNM_CELL_COMMENT_TYPE, NULL);
5008 			else {
5009 				GnmRange r;
5010 				r.start = r.end = *pos;
5011 				sheet_objects_clear (sheet, &r, GNM_CELL_COMMENT_TYPE, NULL);
5012 			}
5013 		}
5014 	} else if (text && (strlen (text) > 0)) {
5015 		cell_set_comment (sheet, pos, author, text, attributes);
5016 	}
5017 	sheet_mark_dirty (sheet);
5018 
5019 	WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
5020 			wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
5021 
5022 	return FALSE;
5023 }
5024 
5025 static gboolean
cmd_set_comment_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)5026 cmd_set_comment_undo (GnmCommand *cmd,
5027 		      G_GNUC_UNUSED WorkbookControl *wbc)
5028 {
5029 	CmdSetComment *me = CMD_SET_COMMENT (cmd);
5030 
5031 	return cmd_set_comment_apply (me->sheet, &me->pos,
5032 				      me->old_text, me->old_attributes,
5033 				      me->old_author);
5034 }
5035 
5036 static gboolean
cmd_set_comment_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)5037 cmd_set_comment_redo (GnmCommand *cmd,
5038 		      G_GNUC_UNUSED WorkbookControl *wbc)
5039 {
5040 	CmdSetComment *me = CMD_SET_COMMENT (cmd);
5041 
5042 	return cmd_set_comment_apply (me->sheet, &me->pos,
5043 				      me->new_text, me->new_attributes,
5044 				      me->new_author);
5045 }
5046 
5047 static void
cmd_set_comment_finalize(GObject * cmd)5048 cmd_set_comment_finalize (GObject *cmd)
5049 {
5050 	CmdSetComment *me = CMD_SET_COMMENT (cmd);
5051 
5052 	g_free (me->new_text);
5053 	me->new_text = NULL;
5054 
5055 	g_free (me->old_text);
5056 	me->old_text = NULL;
5057 
5058 	g_free (me->new_author);
5059 	me->new_author = NULL;
5060 
5061 	g_free (me->old_author);
5062 	me->old_author = NULL;
5063 
5064 	if (me->old_attributes != NULL) {
5065 		pango_attr_list_unref (me->old_attributes);
5066 		me->old_attributes = NULL;
5067 	}
5068 
5069 	if (me->new_attributes != NULL) {
5070 		pango_attr_list_unref (me->new_attributes);
5071 		me->new_attributes = NULL;
5072 	}
5073 
5074 	gnm_command_finalize (cmd);
5075 }
5076 
5077 gboolean
cmd_set_comment(WorkbookControl * wbc,Sheet * sheet,GnmCellPos const * pos,char const * new_text,PangoAttrList * attr,char const * new_author)5078 cmd_set_comment (WorkbookControl *wbc,
5079 		 Sheet *sheet, GnmCellPos const *pos,
5080 		 char const *new_text,
5081 		 PangoAttrList *attr,
5082 		 char const *new_author)
5083 {
5084 	CmdSetComment *me;
5085 	GnmComment   *comment;
5086 	char *where;
5087 
5088 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5089 	g_return_val_if_fail (new_text != NULL, TRUE);
5090 
5091 	me = g_object_new (CMD_SET_COMMENT_TYPE, NULL);
5092 
5093 	me->cmd.sheet = sheet;
5094 	me->cmd.size = 1;
5095 	if (strlen (new_text) < 1)
5096 		me->new_text = NULL;
5097 	else
5098 		me->new_text    = g_strdup (new_text);
5099 	if (strlen (new_author) < 1)
5100 		me->new_author = NULL;
5101 	else
5102 		me->new_author    = g_strdup (new_author);
5103 	if (attr != NULL)
5104 		pango_attr_list_ref (attr);
5105 	me->new_attributes = attr;
5106 	where = undo_cell_pos_name (sheet, pos);
5107 	me->cmd.cmd_descriptor =
5108 		g_strdup_printf (me->new_text == NULL ?
5109 				 _("Clearing comment of %s") :
5110 				 _("Setting comment of %s"),
5111 				 where);
5112 	g_free (where);
5113 	me->old_text    = NULL;
5114 	me->old_author    = NULL;
5115 	me->old_attributes = NULL;
5116 	me->pos         = *pos;
5117 	me->sheet       = sheet;
5118 	comment = sheet_get_comment (sheet, pos);
5119 	if (comment) {
5120 		g_object_get (G_OBJECT (comment),
5121 			      "text", &(me->old_text),
5122 			      "author", &(me->old_author),
5123 			      "markup", &(me->old_attributes), NULL);
5124 		if (me->old_attributes != NULL)
5125 			pango_attr_list_ref (me->old_attributes);
5126 		me->old_text = g_strdup (me->old_text);
5127 		me->old_author = g_strdup (me->old_author);
5128 	}
5129 
5130 	/* Register the command object */
5131 	return gnm_command_push_undo (wbc, G_OBJECT (me));
5132 }
5133 
5134 /******************************************************************/
5135 
5136 #define CMD_ANALYSIS_TOOL_TYPE        (cmd_analysis_tool_get_type ())
5137 #define CMD_ANALYSIS_TOOL(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ANALYSIS_TOOL_TYPE, CmdAnalysis_Tool))
5138 
5139 typedef struct {
5140 	GnmCommand         cmd;
5141 
5142 	data_analysis_output_t  *dao;
5143 	gpointer                specs;
5144 	gboolean                specs_owned;
5145 	analysis_tool_engine    engine;
5146 	data_analysis_output_type_t type;
5147 
5148 	ColRowStateList         *col_info;
5149 	ColRowStateList         *row_info;
5150 	GnmRange                old_range;
5151 	GnmCellRegion           *old_contents;
5152 	GSList                  *newSheetObjects;
5153 } CmdAnalysis_Tool;
5154 
MAKE_GNM_COMMAND(CmdAnalysis_Tool,cmd_analysis_tool,NULL)5155 MAKE_GNM_COMMAND (CmdAnalysis_Tool, cmd_analysis_tool, NULL)
5156 
5157 static gboolean
5158 cmd_analysis_tool_undo (GnmCommand *cmd, WorkbookControl *wbc)
5159 {
5160 	CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5161 	GnmPasteTarget pt;
5162 
5163 	g_return_val_if_fail (me != NULL, TRUE);
5164 
5165 	/* The old view might not exist anymore */
5166 	me->dao->wbc = wbc;
5167 
5168 	switch (me->type) {
5169 	case NewSheetOutput:
5170 		if (!command_undo_sheet_delete (me->dao->sheet))
5171 			return TRUE;
5172 		me->dao->sheet = NULL;
5173 		break;
5174 	case NewWorkbookOutput:
5175 		g_warning ("How did we get here?");
5176 		return TRUE;
5177 		break;
5178 	case RangeOutput:
5179 	default:
5180 		sheet_clear_region (me->dao->sheet,
5181 				    me->old_range.start.col, me->old_range.start.row,
5182 				    me->old_range.end.col, me->old_range.end.row,
5183 				    CLEAR_COMMENTS | CLEAR_FORMATS | CLEAR_NOCHECKARRAY |
5184 				    CLEAR_RECALC_DEPS | CLEAR_VALUES | CLEAR_MERGES,
5185 				    GO_CMD_CONTEXT (wbc));
5186 		clipboard_paste_region (me->old_contents,
5187 			paste_target_init (&pt, me->dao->sheet, &me->old_range, PASTE_ALL_SHEET),
5188 			GO_CMD_CONTEXT (wbc));
5189 		cellregion_unref (me->old_contents);
5190 		me->old_contents = NULL;
5191 		if (me->col_info) {
5192 			dao_set_colrow_state_list (me->dao, TRUE, me->col_info);
5193 			me->col_info = colrow_state_list_destroy (me->col_info);
5194 		}
5195 		if (me->row_info) {
5196 			dao_set_colrow_state_list (me->dao, FALSE, me->row_info);
5197 			me->row_info = colrow_state_list_destroy (me->row_info);
5198 		}
5199 		if (me->newSheetObjects == NULL)
5200 			me->newSheetObjects = dao_surrender_so (me->dao);
5201 		g_slist_foreach (me->newSheetObjects, (GFunc)sheet_object_clear_sheet, NULL);
5202 		sheet_update (me->dao->sheet);
5203 	}
5204 
5205 	return FALSE;
5206 }
5207 
5208 static void
cmd_analysis_tool_draw_old_so(SheetObject * so,data_analysis_output_t * dao)5209 cmd_analysis_tool_draw_old_so (SheetObject *so, data_analysis_output_t *dao)
5210 {
5211 	g_object_ref (so);
5212 	dao_set_sheet_object (dao, 0, 1, so);
5213 }
5214 
5215 static gboolean
cmd_analysis_tool_redo(GnmCommand * cmd,WorkbookControl * wbc)5216 cmd_analysis_tool_redo (GnmCommand *cmd, WorkbookControl *wbc)
5217 {
5218 	gpointer continuity = NULL;
5219 	CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5220 	GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5221 
5222 	g_return_val_if_fail (me != NULL, TRUE);
5223 
5224 	/* The old view might not exist anymore */
5225 	me->dao->wbc = wbc;
5226 
5227 	if (me->col_info)
5228 		me->col_info = colrow_state_list_destroy (me->col_info);
5229 	me->col_info = dao_get_colrow_state_list (me->dao, TRUE);
5230 	if (me->row_info)
5231 		me->row_info = colrow_state_list_destroy (me->row_info);
5232 	me->row_info = dao_get_colrow_state_list (me->dao, FALSE);
5233 
5234 	if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PREPARE_OUTPUT_RANGE, NULL)
5235 	    || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5236 			   &me->cmd.cmd_descriptor)
5237 	    || cmd_dao_is_locked_effective (me->dao, wbc, me->cmd.cmd_descriptor)
5238 	    || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_LAST_VALIDITY_CHECK, &continuity))
5239 		return TRUE;
5240 
5241 	switch (me->type) {
5242 	case NewSheetOutput:
5243 		me->old_contents = NULL;
5244 		break;
5245 	case NewWorkbookOutput:
5246 		/* No undo in this case (see below) */
5247 		me->old_contents = NULL;
5248 		break;
5249 	case RangeOutput:
5250 	default:
5251 		range_init (&me->old_range, me->dao->start_col, me->dao->start_row,
5252 			    me->dao->start_col + me->dao->cols - 1,
5253 			    me->dao->start_row + me->dao->rows - 1);
5254 		me->old_contents = clipboard_copy_range (me->dao->sheet, &me->old_range);
5255 		break;
5256 	}
5257 
5258 	if (me->newSheetObjects != NULL)
5259 		dao_set_omit_so (me->dao, TRUE);
5260 
5261 	if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_FORMAT_OUTPUT_RANGE, NULL))
5262 		return TRUE;
5263 
5264 	if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PERFORM_CALC, &continuity)) {
5265 		if (me->type == RangeOutput) {
5266 			g_warning ("This is too late for failure! The target region has "
5267 				   "already been formatted!");
5268 		} else
5269 			return TRUE;
5270 	}
5271 	if (me->newSheetObjects != NULL)
5272 	{
5273 		GSList *l = g_slist_reverse
5274 			(g_slist_copy (me->newSheetObjects));
5275 
5276 		dao_set_omit_so (me->dao, FALSE);
5277 		g_slist_foreach (l,
5278 				 (GFunc) cmd_analysis_tool_draw_old_so,
5279 				 me->dao);
5280 		g_slist_free (l);
5281 	}
5282 
5283 	if (continuity) {
5284 		g_warning ("There shouldn't be any data left in here!");
5285 	}
5286 
5287 	dao_autofit_columns (me->dao);
5288 	sheet_mark_dirty (me->dao->sheet);
5289 	sheet_update (me->dao->sheet);
5290 
5291 	/* The concept of an undo if we create a new worksheet is extremely strange,
5292 	 * since we have separate undo/redo queues per worksheet.
5293 	 * Users can simply delete the worksheet if they so desire.
5294 	 */
5295 
5296 	return (me->type == NewWorkbookOutput);
5297 }
5298 
5299 static void
cmd_analysis_tool_finalize(GObject * cmd)5300 cmd_analysis_tool_finalize (GObject *cmd)
5301 {
5302 	CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5303 
5304 	if (me->col_info)
5305 		me->col_info = colrow_state_list_destroy (me->col_info);
5306 	if (me->row_info)
5307 		me->row_info = colrow_state_list_destroy (me->row_info);
5308 
5309 	me->engine (NULL, me->dao, me->specs, TOOL_ENGINE_CLEAN_UP, NULL);
5310 
5311 	if (me->specs_owned) {
5312 		g_free (me->specs);
5313 		dao_free (me->dao);
5314 	}
5315 	if (me->old_contents)
5316 		cellregion_unref (me->old_contents);
5317 
5318 	g_slist_free_full (me->newSheetObjects, g_object_unref);
5319 
5320 	gnm_command_finalize (cmd);
5321 }
5322 
5323 /**
5324  * cmd_analysis_tool: (skip)
5325  * Note: this takes ownership of specs and dao if the command
5326  * succeeds.
5327  *
5328  * Returns: %TRUE if there was a problem, %FALSE otherwise.
5329  **/
5330 gboolean
cmd_analysis_tool(WorkbookControl * wbc,G_GNUC_UNUSED Sheet * sheet,data_analysis_output_t * dao,gpointer specs,analysis_tool_engine engine,gboolean always_take_ownership)5331 cmd_analysis_tool (WorkbookControl *wbc, G_GNUC_UNUSED Sheet *sheet,
5332 		   data_analysis_output_t *dao, gpointer specs,
5333 		   analysis_tool_engine engine, gboolean always_take_ownership)
5334 {
5335 	CmdAnalysis_Tool *me;
5336 	gboolean trouble;
5337 	GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5338 
5339 	g_return_val_if_fail (dao != NULL, TRUE);
5340 	g_return_val_if_fail (specs != NULL, TRUE);
5341 	g_return_val_if_fail (engine != NULL, TRUE);
5342 
5343 	me = g_object_new (CMD_ANALYSIS_TOOL_TYPE, NULL);
5344 
5345 	dao->wbc = wbc;
5346 
5347 	/* Store the specs for the object */
5348 	me->specs = specs;
5349 	me->specs_owned = always_take_ownership;
5350 	me->dao = dao;
5351 	me->engine = engine;
5352 	me->cmd.cmd_descriptor = NULL;
5353 	if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DAO, NULL)) {
5354 		g_object_unref (me);
5355 		return TRUE;
5356 	}
5357 	me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5358 		    &me->cmd.cmd_descriptor);
5359 	me->cmd.sheet = NULL;
5360 	me->type = dao->type;
5361 	me->row_info = NULL;
5362 	me->col_info = NULL;
5363 
5364 	/* We divide by 2 since many cells will be empty*/
5365 	me->cmd.size = 1 + dao->rows * dao->cols / 2;
5366 
5367 	/* Register the command object */
5368 	trouble = gnm_command_push_undo (wbc, G_OBJECT (me));
5369 
5370 	if (!trouble)
5371 		me->specs_owned = TRUE;
5372 
5373 	return trouble;
5374 }
5375 
5376 /******************************************************************/
5377 
5378 #define CMD_MERGE_DATA_TYPE        (cmd_merge_data_get_type ())
5379 #define CMD_MERGE_DATA(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_DATA_TYPE, CmdMergeData))
5380 
5381 typedef struct {
5382 	GnmCommand cmd;
5383 	GnmValue *merge_zone;
5384 	GSList *merge_fields;
5385 	GSList *merge_data;
5386 	GSList *sheet_list;
5387 	Sheet *sheet;
5388 	gint n;
5389 } CmdMergeData;
5390 
MAKE_GNM_COMMAND(CmdMergeData,cmd_merge_data,NULL)5391 MAKE_GNM_COMMAND (CmdMergeData, cmd_merge_data, NULL)
5392 
5393 static void
5394 cmd_merge_data_delete_sheets (gpointer data, gpointer success)
5395 {
5396 	Sheet *sheet = data;
5397 
5398 	if (!command_undo_sheet_delete (sheet))
5399 		*(gboolean *)success = FALSE;
5400 }
5401 
5402 static gboolean
cmd_merge_data_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)5403 cmd_merge_data_undo (GnmCommand *cmd,
5404 		     G_GNUC_UNUSED WorkbookControl *wbc)
5405 {
5406 	CmdMergeData *me = CMD_MERGE_DATA (cmd);
5407 	gboolean success = TRUE;
5408 
5409 	g_slist_foreach (me->sheet_list, cmd_merge_data_delete_sheets, &success);
5410 	g_slist_free (me->sheet_list);
5411 	me->sheet_list = NULL;
5412 
5413 	return FALSE;
5414 }
5415 
5416 static gboolean
cmd_merge_data_redo(GnmCommand * cmd,WorkbookControl * wbc)5417 cmd_merge_data_redo (GnmCommand *cmd, WorkbookControl *wbc)
5418 {
5419 	CmdMergeData *me = CMD_MERGE_DATA (cmd);
5420 	int i;
5421 	GnmCellRegion *merge_contents;
5422 	GnmRangeRef *cell = &me->merge_zone->v_range.cell;
5423 	GnmPasteTarget pt;
5424 	GSList *this_field = me->merge_fields;
5425 	GSList *this_data = me->merge_data;
5426 	Sheet *source_sheet = cell->a.sheet;
5427 	GSList *target_sheet;
5428 	GnmRange target_range;
5429 	ColRowStateList *state_col;
5430 	ColRowStateList *state_row;
5431 
5432 	range_init (&target_range, cell->a.col, cell->a.row,
5433 		    cell->b.col, cell->b.row);
5434 	merge_contents = clipboard_copy_range (source_sheet, &target_range);
5435 	state_col = colrow_get_states (source_sheet, TRUE, target_range.start.col,
5436 					   target_range.end.col);
5437 	state_row = colrow_get_states (source_sheet, FALSE, target_range.start.row,
5438 					   target_range.end.row);
5439 
5440 	for (i = 0; i < me->n; i++) {
5441 		Sheet *new_sheet;
5442 
5443 		new_sheet = workbook_sheet_add (me->sheet->workbook, -1,
5444 						gnm_sheet_get_max_cols (me->sheet),
5445 						gnm_sheet_get_max_rows (me->sheet));
5446 		me->sheet_list = g_slist_prepend (me->sheet_list, new_sheet);
5447 
5448 		colrow_set_states (new_sheet, TRUE, target_range.start.col, state_col);
5449 		colrow_set_states (new_sheet, FALSE, target_range.start.row, state_row);
5450 		sheet_objects_dup (source_sheet, new_sheet, &target_range);
5451 		clipboard_paste_region (merge_contents,
5452 			paste_target_init (&pt, new_sheet, &target_range, PASTE_ALL_SHEET),
5453 			GO_CMD_CONTEXT (wbc));
5454 	}
5455 	cellregion_unref (merge_contents);
5456 	me->sheet_list = g_slist_reverse (me->sheet_list);
5457 	colrow_state_list_destroy (state_col);
5458 	colrow_state_list_destroy (state_row);
5459 
5460 	while (this_field) {
5461 		int col_source, row_source;
5462 		int col_target, row_target;
5463 
5464 		g_return_val_if_fail (this_data != NULL, TRUE);
5465 		cell = &((GnmValue *)this_field->data)->v_range.cell;
5466 		col_target = cell->a.col;
5467 		row_target =  cell->a.row;
5468 
5469 		cell = &((GnmValue *)this_data->data)->v_range.cell;
5470 		col_source = cell->a.col;
5471 		row_source =  cell->a.row;
5472 		source_sheet = cell->a.sheet;
5473 
5474 		target_sheet = me->sheet_list;
5475 		while (target_sheet) {
5476 			GnmCell *source_cell = sheet_cell_get (source_sheet,
5477 							      col_source, row_source);
5478 			if (source_cell == NULL) {
5479 				GnmCell *target_cell = sheet_cell_get ((Sheet *)target_sheet->data,
5480 								      col_target, row_target);
5481 				if (target_cell != NULL)
5482 					gnm_cell_set_value (target_cell,
5483 							   value_new_empty ());
5484 			} else {
5485 				GnmCell *target_cell = sheet_cell_fetch ((Sheet *)target_sheet->data,
5486 								      col_target, row_target);
5487 				gnm_cell_set_value (target_cell,
5488 						   value_dup (source_cell->value));
5489 			}
5490 			target_sheet = target_sheet->next;
5491 			row_source++;
5492 		}
5493 
5494 		this_field = this_field->next;
5495 		this_data = this_data->next;
5496 	}
5497 
5498 	return FALSE;
5499 }
5500 
5501 static void
cmd_merge_data_finalize(GObject * cmd)5502 cmd_merge_data_finalize (GObject *cmd)
5503 {
5504 	CmdMergeData *me = CMD_MERGE_DATA (cmd);
5505 
5506 	value_release (me->merge_zone);
5507 	me->merge_zone = NULL;
5508 	range_list_destroy (me->merge_data);
5509 	me->merge_data = NULL;
5510 	range_list_destroy (me->merge_fields);
5511 	me->merge_fields = NULL;
5512 	g_slist_free (me->sheet_list);
5513 	me->sheet_list = NULL;
5514 	me->n = 0;
5515 
5516 	gnm_command_finalize (cmd);
5517 }
5518 
5519 /**
5520  * cmd_merge_data:
5521  * @wbc: #WorkbookControl
5522  * @sheet: #Sheet
5523  * @merge_zone: (transfer full): #GnmValue
5524  * @merge_fields: (element-type GnmRange) (transfer full):
5525  * @merge_data: (element-type GnmRange) (transfer full):
5526  *
5527  * Returns: %TRUE if there was a problem, %FALSE otherwise.
5528  **/
5529 gboolean
cmd_merge_data(WorkbookControl * wbc,Sheet * sheet,GnmValue * merge_zone,GSList * merge_fields,GSList * merge_data)5530 cmd_merge_data (WorkbookControl *wbc, Sheet *sheet,
5531 		GnmValue *merge_zone, GSList *merge_fields, GSList *merge_data)
5532 {
5533 	CmdMergeData *me;
5534 	GnmRangeRef *cell;
5535 
5536 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5537 	g_return_val_if_fail (merge_zone != NULL, TRUE);
5538 	g_return_val_if_fail (merge_fields != NULL, TRUE);
5539 	g_return_val_if_fail (merge_data != NULL, TRUE);
5540 
5541 	me = g_object_new (CMD_MERGE_DATA_TYPE, NULL);
5542 
5543 	me->cmd.sheet = sheet;
5544 	me->sheet = sheet;
5545 	me->cmd.size = 1 + g_slist_length (merge_fields);
5546 	me->cmd.cmd_descriptor =
5547 		g_strdup_printf (_("Merging data into %s"), value_peek_string (merge_zone));
5548 
5549 	me->merge_zone = merge_zone;
5550 	me->merge_fields = merge_fields;
5551 	me->merge_data = merge_data;
5552 	me->sheet_list = NULL;
5553 
5554 	cell = &((GnmValue *)merge_data->data)->v_range.cell;
5555 	me->n = cell->b.row - cell->a.row + 1;
5556 
5557 	/* Register the command object */
5558 	return gnm_command_push_undo (wbc, G_OBJECT (me));
5559 }
5560 
5561 /******************************************************************/
5562 
5563 #define CMD_CHANGE_META_DATA_TYPE        (cmd_change_summary_get_type ())
5564 #define CMD_CHANGE_META_DATA(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_CHANGE_META_DATA_TYPE, CmdChangeMetaData))
5565 
5566 typedef struct {
5567 	GnmCommand cmd;
5568 	GSList *changed_props;
5569 	GSList *removed_names;
5570 } CmdChangeMetaData;
5571 
MAKE_GNM_COMMAND(CmdChangeMetaData,cmd_change_summary,NULL)5572 MAKE_GNM_COMMAND (CmdChangeMetaData, cmd_change_summary, NULL)
5573 
5574 static gboolean
5575 cmd_change_summary_undo (GnmCommand *cmd, WorkbookControl *wbc)
5576 {
5577 	CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5578 	GsfDocMetaData *meta = go_doc_get_meta_data (wb_control_get_doc (wbc));
5579 	GSList *ptr, *old_vals = NULL, *dropped = NULL;
5580 	GsfDocProp *prop;
5581 	char const *name;
5582 
5583 	for (ptr = me->removed_names; ptr != NULL ; ptr = ptr->next) {
5584 		if (NULL != (prop = gsf_doc_meta_data_steal (meta, ptr->data)))
5585 			old_vals = g_slist_prepend (old_vals, prop);
5586 		g_free (ptr->data);
5587 	}
5588 	g_slist_free (me->removed_names);
5589 
5590 	for (ptr = me->changed_props; ptr != NULL ; ptr = ptr->next) {
5591 		name = gsf_doc_prop_get_name (ptr->data);
5592 		if (NULL != (prop = gsf_doc_meta_data_steal (meta, name)))
5593 			old_vals = g_slist_prepend (old_vals, prop);
5594 		else
5595 			dropped = g_slist_prepend (old_vals, g_strdup (name));
5596 		gsf_doc_meta_data_store (meta, ptr->data);
5597 	}
5598 	g_slist_free (me->changed_props);
5599 
5600 	me->removed_names = dropped;
5601 	me->changed_props = old_vals;
5602 	go_doc_update_meta_data (wb_control_get_doc (wbc));
5603 
5604 	return FALSE;
5605 }
5606 
5607 static gboolean
cmd_change_summary_redo(GnmCommand * cmd,WorkbookControl * wbc)5608 cmd_change_summary_redo (GnmCommand *cmd, WorkbookControl *wbc)
5609 {
5610 	return cmd_change_summary_undo (cmd, wbc);
5611 }
5612 
5613 static void
cmd_change_summary_finalize(GObject * cmd)5614 cmd_change_summary_finalize (GObject *cmd)
5615 {
5616 	CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5617 
5618 	g_slist_free_full (me->changed_props, (GDestroyNotify)gsf_doc_prop_free);
5619 	me->changed_props = NULL;
5620 	g_slist_free_full (me->removed_names, g_free);
5621 	me->removed_names = NULL;
5622 
5623 	gnm_command_finalize (cmd);
5624 }
5625 
5626 /**
5627  * cmd_change_meta_data:
5628  * @wbc: #WorkbookControl
5629  * @changes: (element-type GsfDocMetaData) (transfer full): the changed metadata.
5630  * @removed: (element-type GsfDocMetaData) (transfer full): the removed metadata.
5631  *
5632  * Returns: %TRUE if there was a problem, %FALSE otherwise.
5633  **/
5634 gboolean
cmd_change_meta_data(WorkbookControl * wbc,GSList * changes,GSList * removed)5635 cmd_change_meta_data (WorkbookControl *wbc, GSList *changes, GSList *removed)
5636 {
5637 	CmdChangeMetaData *me = g_object_new (CMD_CHANGE_META_DATA_TYPE, NULL);
5638 
5639 	me->changed_props = changes;
5640 	me->removed_names = removed;
5641 	me->cmd.sheet = NULL;
5642 
5643 	me->cmd.size = g_slist_length (changes) + g_slist_length (removed);
5644 	me->cmd.cmd_descriptor = g_strdup_printf (
5645 		_("Changing workbook properties"));
5646 	return gnm_command_push_undo (wbc, G_OBJECT (me));
5647 }
5648 
5649 /******************************************************************/
5650 
5651 #define CMD_OBJECT_RAISE_TYPE (cmd_object_raise_get_type ())
5652 #define CMD_OBJECT_RAISE(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECT_RAISE_TYPE, CmdObjectRaise))
5653 
5654 typedef struct {
5655 	GnmCommand cmd;
5656 	SheetObject *so;
5657 	CmdObjectRaiseSelector dir;
5658 	gint        changed_positions;
5659 } CmdObjectRaise;
5660 
MAKE_GNM_COMMAND(CmdObjectRaise,cmd_object_raise,NULL)5661 MAKE_GNM_COMMAND (CmdObjectRaise, cmd_object_raise, NULL)
5662 
5663 static gboolean
5664 cmd_object_raise_redo (GnmCommand *cmd,
5665 		       G_GNUC_UNUSED WorkbookControl *wbc)
5666 {
5667 	CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5668 	switch (me->dir) {
5669 	case cmd_object_pull_to_front:
5670 		me->changed_positions = sheet_object_adjust_stacking (me->so, G_MAXINT/2);
5671 		break;
5672 	case cmd_object_pull_forward:
5673 		me->changed_positions = sheet_object_adjust_stacking (me->so, 1);
5674 		break;
5675 	case cmd_object_push_backward:
5676 		me->changed_positions = sheet_object_adjust_stacking (me->so, -1);
5677 		break;
5678 	case cmd_object_push_to_back:
5679 		me->changed_positions = sheet_object_adjust_stacking (me->so, G_MININT/2);
5680 		break;
5681 	}
5682 	return FALSE;
5683 }
5684 
5685 static gboolean
cmd_object_raise_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)5686 cmd_object_raise_undo (GnmCommand *cmd,
5687 		       G_GNUC_UNUSED WorkbookControl *wbc)
5688 {
5689 	CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5690 	if (me->changed_positions != 0)
5691 		sheet_object_adjust_stacking (me->so, - me->changed_positions);
5692 	return FALSE;
5693 }
5694 
5695 static void
cmd_object_raise_finalize(GObject * cmd)5696 cmd_object_raise_finalize (GObject *cmd)
5697 {
5698 	CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5699 	g_object_unref (me->so);
5700 	gnm_command_finalize (cmd);
5701 }
5702 
5703 gboolean
cmd_object_raise(WorkbookControl * wbc,SheetObject * so,CmdObjectRaiseSelector dir)5704 cmd_object_raise (WorkbookControl *wbc, SheetObject *so, CmdObjectRaiseSelector dir)
5705 {
5706 	CmdObjectRaise *me;
5707 
5708 	g_return_val_if_fail (GNM_IS_SO (so), TRUE);
5709 
5710 	me = g_object_new (CMD_OBJECT_RAISE_TYPE, NULL);
5711 
5712 	me->so = so;
5713 	g_object_ref (so);
5714 
5715 	me->cmd.sheet = sheet_object_get_sheet (so);
5716 	me->cmd.size = 1;
5717 	switch (dir) {
5718 	case cmd_object_pull_to_front:
5719 		me->cmd.cmd_descriptor = g_strdup (_("Pull Object to the Front"));
5720 		break;
5721 	case cmd_object_pull_forward:
5722 		me->cmd.cmd_descriptor = g_strdup (_("Pull Object Forward"));
5723 		break;
5724 	case cmd_object_push_backward:
5725 		me->cmd.cmd_descriptor = g_strdup (_("Push Object Backward"));
5726 		break;
5727 	case cmd_object_push_to_back:
5728 		me->cmd.cmd_descriptor = g_strdup (_("Push Object to the Back"));
5729 		break;
5730 	}
5731 	me->dir = dir;
5732 	me->changed_positions = 0;
5733 
5734 	return gnm_command_push_undo (wbc, G_OBJECT (me));
5735 }
5736 
5737 /******************************************************************/
5738 
5739 #define CMD_PRINT_SETUP_TYPE        (cmd_print_setup_get_type ())
5740 #define CMD_PRINT_SETUP(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PRINT_SETUP_TYPE, CmdPrintSetup))
5741 
5742 typedef struct {
5743 	GnmCommand cmd;
5744 
5745 	GSList *old_pi;
5746 	GnmPrintInformation *new_pi;
5747 } CmdPrintSetup;
5748 
MAKE_GNM_COMMAND(CmdPrintSetup,cmd_print_setup,NULL)5749 MAKE_GNM_COMMAND (CmdPrintSetup, cmd_print_setup, NULL)
5750 
5751 static void
5752 update_sheet_graph_cb (Sheet *sheet)
5753 {
5754 	g_return_if_fail (IS_SHEET (sheet) && sheet->sheet_type == GNM_SHEET_OBJECT);
5755 
5756 	sheet_object_graph_ensure_size (GNM_SO (sheet->sheet_objects->data));
5757 }
5758 
5759 static gboolean
cmd_print_setup_undo(GnmCommand * cmd,WorkbookControl * wbc)5760 cmd_print_setup_undo (GnmCommand *cmd, WorkbookControl *wbc)
5761 {
5762 	CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5763 	guint n, i;
5764 	Workbook *book;
5765 	GSList *infos;
5766 
5767 	g_return_val_if_fail (me->old_pi != NULL, TRUE);
5768 
5769 	if (me->cmd.sheet) {
5770 		GnmPrintInformation *pi = me->old_pi->data;
5771 		gnm_print_info_free (me->cmd.sheet->print_info);
5772 		me->cmd.sheet->print_info = gnm_print_info_dup (pi);
5773 		if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5774 			update_sheet_graph_cb (me->cmd.sheet);
5775 	} else {
5776 		book = wb_control_get_workbook(wbc);
5777 		n = workbook_sheet_count (book);
5778 		infos = me->old_pi;
5779 		g_return_val_if_fail (g_slist_length (infos) == n, TRUE);
5780 
5781 		for (i = 0 ; i < n ; i++) {
5782 			GnmPrintInformation *pi = infos->data;
5783 			Sheet *sheet = workbook_sheet_by_index (book, i);
5784 
5785 			g_return_val_if_fail (infos != NULL, TRUE);
5786 
5787 			gnm_print_info_free (sheet->print_info);
5788 			sheet->print_info = gnm_print_info_dup (pi);
5789 			if (sheet->sheet_type == GNM_SHEET_OBJECT)
5790 				update_sheet_graph_cb (sheet);
5791 			infos = infos->next;
5792 		}
5793 	}
5794 	return FALSE;
5795 }
5796 
5797 static gboolean
cmd_print_setup_redo(GnmCommand * cmd,WorkbookControl * wbc)5798 cmd_print_setup_redo (GnmCommand *cmd, WorkbookControl *wbc)
5799 {
5800 	CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5801 	int n, i;
5802 	Workbook *book;
5803 	gboolean save_pis = (me->old_pi == NULL);
5804 
5805 	if (me->cmd.sheet) {
5806 		if (save_pis)
5807 			me->old_pi = g_slist_append (me->old_pi, me->cmd.sheet->print_info);
5808 		else
5809 			gnm_print_info_free (me->cmd.sheet->print_info);
5810 		me->cmd.sheet->print_info = gnm_print_info_dup (me->new_pi);
5811 		if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5812 			update_sheet_graph_cb (me->cmd.sheet);
5813 	} else {
5814 		book = wb_control_get_workbook(wbc);
5815 		n = workbook_sheet_count (book);
5816 		for (i = 0 ; i < n ; i++) {
5817 			Sheet *sheet = workbook_sheet_by_index (book, i);
5818 			sheet_mark_dirty (sheet);
5819 			if (save_pis)
5820 				me->old_pi = g_slist_prepend (me->old_pi, sheet->print_info);
5821 			else
5822 				gnm_print_info_free (sheet->print_info);
5823 			sheet->print_info = gnm_print_info_dup (me->new_pi);
5824 			if (sheet->sheet_type == GNM_SHEET_OBJECT)
5825 				update_sheet_graph_cb (sheet);
5826 		}
5827 		if (save_pis)
5828 			me->old_pi = g_slist_reverse (me->old_pi);
5829 	}
5830 	return FALSE;
5831 }
5832 
5833 static void
cmd_print_setup_finalize(GObject * cmd)5834 cmd_print_setup_finalize (GObject *cmd)
5835 {
5836 	CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5837 	GSList *list = me->old_pi;
5838 
5839 	if (me->new_pi)
5840 		gnm_print_info_free (me->new_pi);
5841 	for (; list; list = list->next)
5842 		gnm_print_info_free ((GnmPrintInformation *) list->data);
5843 	g_slist_free (me->old_pi);
5844 	gnm_command_finalize (cmd);
5845 }
5846 
5847 gboolean
cmd_print_setup(WorkbookControl * wbc,Sheet * sheet,GnmPrintInformation const * pi)5848 cmd_print_setup (WorkbookControl *wbc, Sheet *sheet, GnmPrintInformation const *pi)
5849 {
5850 	CmdPrintSetup *me;
5851 
5852 	me = g_object_new (CMD_PRINT_SETUP_TYPE, NULL);
5853 
5854 	me->cmd.sheet = sheet;
5855 	me->cmd.size = 10;
5856 	if (sheet)
5857 		me->cmd.cmd_descriptor =
5858 			g_strdup_printf (_("Page Setup For %s"), sheet->name_unquoted);
5859 	else
5860 		me->cmd.cmd_descriptor = g_strdup (_("Page Setup For All Sheets"));
5861 	me->old_pi = NULL;
5862 	me->new_pi = gnm_print_info_dup (pi);
5863 
5864 	return gnm_command_push_undo (wbc, G_OBJECT (me));
5865 }
5866 
5867 /******************************************************************/
5868 
5869 #define CMD_DEFINE_NAME_TYPE        (cmd_define_name_get_type ())
5870 #define CMD_DEFINE_NAME(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DEFINE_NAME_TYPE, CmdDefineName))
5871 
5872 typedef struct {
5873 	GnmCommand cmd;
5874 
5875 	GnmParsePos	 pp;
5876 	char		*name;
5877 	GnmExprTop const *texpr;
5878 	gboolean	 new_name;
5879 	gboolean	 placeholder;
5880 } CmdDefineName;
5881 
MAKE_GNM_COMMAND(CmdDefineName,cmd_define_name,NULL)5882 MAKE_GNM_COMMAND (CmdDefineName, cmd_define_name, NULL)
5883 
5884 static gboolean
5885 cmd_define_name_undo (GnmCommand *cmd,
5886 		      WorkbookControl *wbc)
5887 {
5888 	CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5889 	GnmNamedExpr  *nexpr = expr_name_lookup (&(me->pp), me->name);
5890 	GnmExprTop const *texpr = nexpr->texpr;
5891 
5892 	gnm_expr_top_ref (texpr);
5893 	if (me->new_name)
5894 		expr_name_remove (nexpr);
5895 	else if (me->placeholder)
5896 		expr_name_downgrade_to_placeholder (nexpr);
5897 	else
5898 		expr_name_set_expr (nexpr, me->texpr); /* restore old def */
5899 
5900 	me->texpr = texpr;
5901 
5902 	WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5903 			wb_view_menus_update (each_wbv);
5904 		});
5905 	return FALSE;
5906 }
5907 
5908 static gboolean
cmd_define_name_redo(GnmCommand * cmd,WorkbookControl * wbc)5909 cmd_define_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
5910 {
5911 	CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5912 	GnmNamedExpr  *nexpr = expr_name_lookup (&(me->pp), me->name);
5913 
5914 	me->new_name = (nexpr == NULL);
5915 	me->placeholder = (nexpr != NULL)
5916 		&& expr_name_is_placeholder (nexpr);
5917 
5918 	if (me->new_name || me->placeholder) {
5919 		char *err = NULL;
5920 		nexpr = expr_name_add (&me->pp, me->name, me->texpr, &err, TRUE, NULL);
5921 		if (nexpr == NULL) {
5922 			go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), err);
5923 			g_free (err);
5924 			return TRUE;
5925 		}
5926 		me->texpr = NULL;
5927 	} else {	/* changing the definition */
5928 		GnmExprTop const *tmp = nexpr->texpr;
5929 		gnm_expr_top_ref (tmp);
5930 		expr_name_set_expr (nexpr, me->texpr);
5931 		me->texpr = tmp; /* store the old definition */
5932 	}
5933 	WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5934 			wb_view_menus_update (each_wbv);
5935 		});
5936 
5937 	return FALSE;
5938 }
5939 
5940 static void
cmd_define_name_finalize(GObject * cmd)5941 cmd_define_name_finalize (GObject *cmd)
5942 {
5943 	CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5944 
5945 	g_free (me->name); me->name = NULL;
5946 
5947 	if (me->texpr) {
5948 		gnm_expr_top_unref (me->texpr);
5949 		me->texpr = NULL;
5950 	}
5951 
5952 	gnm_command_finalize (cmd);
5953 }
5954 
5955 /**
5956  * cmd_define_name:
5957  * @wbc:
5958  * @name:
5959  * @pp:
5960  * @texpr: (transfer full): #GnmExprTop
5961  * @descriptor: optional descriptor.
5962  *
5963  * If the @name has never been defined in context @pp create a new name
5964  * If it is a placeholder, assign @texpr to it and make it real
5965  * If it already exists as a real name just assign @expr.
5966  *
5967  * Returns: %TRUE if there was a problem, %FALSE otherwise.
5968  **/
5969 gboolean
cmd_define_name(WorkbookControl * wbc,char const * name,GnmParsePos const * pp,GnmExprTop const * texpr,char const * descriptor)5970 cmd_define_name (WorkbookControl *wbc, char const *name,
5971 		 GnmParsePos const *pp, GnmExprTop const *texpr,
5972 		 char const *descriptor)
5973 {
5974 	CmdDefineName *me;
5975 	GnmNamedExpr *nexpr;
5976 	Sheet *sheet;
5977 
5978 	g_return_val_if_fail (name != NULL, TRUE);
5979 	g_return_val_if_fail (pp != NULL, TRUE);
5980 	g_return_val_if_fail (texpr != NULL, TRUE);
5981 
5982 	if (name[0] == '\0') {
5983 		go_cmd_context_error_invalid
5984 			(GO_CMD_CONTEXT (wbc), _("Defined Name"),
5985 			 _("An empty string is not allowed as defined name."));
5986 		gnm_expr_top_unref (texpr);
5987 		return TRUE;
5988 	}
5989 
5990 	sheet = wb_control_cur_sheet (wbc);
5991 	if (!expr_name_validate (name)) {
5992 		gchar *err = g_strdup_printf
5993 			(_("'%s' is not allowed as defined name."), name);
5994 		go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
5995 					      _("Defined Name"), err);
5996 		g_free (err);
5997 		gnm_expr_top_unref (texpr);
5998 		return TRUE;
5999 	}
6000 
6001 	if (expr_name_check_for_loop (name, texpr)) {
6002 		go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), name,
6003 					_("has a circular reference"));
6004 		gnm_expr_top_unref (texpr);
6005 		return TRUE;
6006 	}
6007 	nexpr = expr_name_lookup (pp, name);
6008 	if (nexpr != NULL && !expr_name_is_placeholder (nexpr) &&
6009 	    gnm_expr_top_equal (texpr, nexpr->texpr)) {
6010 		gnm_expr_top_unref (texpr);
6011 		return FALSE; /* expr is not changing, do nothing */
6012 	}
6013 
6014 	me = g_object_new (CMD_DEFINE_NAME_TYPE, NULL);
6015 	me->name = g_strdup (name);
6016 	me->pp = *pp;
6017 	me->texpr = texpr;
6018 
6019 	me->cmd.sheet = sheet;
6020 	me->cmd.size = 1;
6021 
6022 	if (descriptor == NULL) {
6023 		char const *tmp;
6024 		GString *res;
6025 
6026 	/* Underscores need to be doubled.  */
6027 		res = g_string_new (NULL);
6028 		for (tmp = name; *tmp; tmp++) {
6029 			if (*tmp == '_')
6030 				g_string_append_c (res, '_');
6031 			g_string_append_c (res, *tmp);
6032 		}
6033 
6034 		nexpr = expr_name_lookup (pp, name);
6035 		if (nexpr == NULL || expr_name_is_placeholder (nexpr))
6036 			me->cmd.cmd_descriptor =
6037 				g_strdup_printf (_("Define Name %s"), res->str);
6038 		else
6039 			me->cmd.cmd_descriptor =
6040 				g_strdup_printf (_("Update Name %s"), res->str);
6041 		g_string_free (res, TRUE);
6042 	} else
6043 		me->cmd.cmd_descriptor = g_strdup (descriptor);
6044 
6045 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6046 }
6047 
6048 /******************************************************************/
6049 
6050 #define CMD_REMOVE_NAME_TYPE        (cmd_remove_name_get_type ())
6051 #define CMD_REMOVE_NAME(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REMOVE_NAME_TYPE, CmdRemoveName))
6052 
6053 typedef struct {
6054 	GnmCommand cmd;
6055 
6056 	GnmParsePos pp;
6057 	GnmNamedExpr *nexpr;
6058 	const GnmExprTop *texpr;
6059 } CmdRemoveName;
6060 
MAKE_GNM_COMMAND(CmdRemoveName,cmd_remove_name,NULL)6061 MAKE_GNM_COMMAND (CmdRemoveName, cmd_remove_name, NULL)
6062 
6063 static gboolean
6064 cmd_remove_name_undo (GnmCommand *cmd,
6065 		      G_GNUC_UNUSED WorkbookControl *wbc)
6066 {
6067 	CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6068 	GnmNamedExpr *nexpr =
6069 		expr_name_add (&me->nexpr->pos, expr_name_name (me->nexpr),
6070 			       me->texpr, NULL, TRUE, NULL);
6071 	if (nexpr) {
6072 		me->texpr = NULL;
6073 		expr_name_ref (nexpr);
6074 		expr_name_unref (me->nexpr);
6075 		me->nexpr = nexpr;
6076 		return FALSE;
6077 	} else {
6078 		g_warning ("Redefining name failed.");
6079 		return TRUE;
6080 	}
6081 }
6082 
6083 static gboolean
cmd_remove_name_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6084 cmd_remove_name_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6085 {
6086 	CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6087 
6088 	me->texpr = me->nexpr->texpr;
6089 	gnm_expr_top_ref (me->texpr);
6090 	expr_name_downgrade_to_placeholder (me->nexpr);
6091 
6092 	return FALSE;
6093 }
6094 
6095 static void
cmd_remove_name_finalize(GObject * cmd)6096 cmd_remove_name_finalize (GObject *cmd)
6097 {
6098 	CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6099 
6100 	expr_name_unref (me->nexpr);
6101 
6102 	if (me->texpr) {
6103 		gnm_expr_top_unref (me->texpr);
6104 		me->texpr = NULL;
6105 	}
6106 
6107 	gnm_command_finalize (cmd);
6108 }
6109 
6110 /**
6111  * cmd_remove_name:
6112  * @wbc:
6113  * @nexpr: name to remove.
6114  *
6115  * Returns TRUE on error
6116  **/
6117 gboolean
cmd_remove_name(WorkbookControl * wbc,GnmNamedExpr * nexpr)6118 cmd_remove_name (WorkbookControl *wbc, GnmNamedExpr *nexpr)
6119 {
6120 	CmdRemoveName *me;
6121 
6122 	g_return_val_if_fail (wbc != NULL, TRUE);
6123 	g_return_val_if_fail (nexpr != NULL, TRUE);
6124 	g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6125 
6126 	expr_name_ref (nexpr);
6127 
6128 	me = g_object_new (CMD_REMOVE_NAME_TYPE, NULL);
6129 	me->nexpr = nexpr;
6130 	me->texpr = NULL;
6131 	me->cmd.sheet = wb_control_cur_sheet (wbc);
6132 	me->cmd.size = 1;
6133 	me->cmd.cmd_descriptor = g_strdup_printf (_("Remove Name %s"),
6134 						  expr_name_name (nexpr));
6135 
6136 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6137 }
6138 
6139 /******************************************************************/
6140 
6141 #define CMD_RESCOPE_NAME_TYPE        (cmd_rescope_name_get_type ())
6142 #define CMD_RESCOPE_NAME(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESCOPE_NAME_TYPE, CmdRescopeName))
6143 
6144 typedef struct {
6145 	GnmCommand cmd;
6146 	GnmNamedExpr *nexpr;
6147 	Sheet *scope;
6148 } CmdRescopeName;
6149 
MAKE_GNM_COMMAND(CmdRescopeName,cmd_rescope_name,NULL)6150 MAKE_GNM_COMMAND (CmdRescopeName, cmd_rescope_name, NULL)
6151 
6152 static gboolean
6153 cmd_rescope_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
6154 {
6155 	CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6156 	Sheet *old_scope = me->nexpr->pos.sheet;
6157 	char *err;
6158 	GnmParsePos pp = me->nexpr->pos;
6159 
6160 	pp.sheet = me->scope;
6161 	err = expr_name_set_pos (me->nexpr, &pp);
6162 
6163 	if (err != NULL) {
6164 		go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Change Scope of Name"), err);
6165 		g_free (err);
6166 		return TRUE;
6167 	}
6168 
6169 	me->scope = old_scope;
6170 	return FALSE;
6171 }
6172 
6173 static gboolean
cmd_rescope_name_undo(GnmCommand * cmd,WorkbookControl * wbc)6174 cmd_rescope_name_undo (GnmCommand *cmd, WorkbookControl *wbc)
6175 {
6176 	return cmd_rescope_name_redo (cmd, wbc);
6177 }
6178 
6179 
6180 static void
cmd_rescope_name_finalize(GObject * cmd)6181 cmd_rescope_name_finalize (GObject *cmd)
6182 {
6183 	CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6184 
6185 	expr_name_unref (me->nexpr);
6186 	gnm_command_finalize (cmd);
6187 }
6188 
6189 /**
6190  * cmd_rescope_name:
6191  * @wbc:
6192  * @nexpr: name to rescope.
6193  * @scope:
6194  *
6195  * Returns: %TRUE if there was a problem, %FALSE otherwise.
6196  **/
6197 gboolean
cmd_rescope_name(WorkbookControl * wbc,GnmNamedExpr * nexpr,Sheet * scope)6198 cmd_rescope_name (WorkbookControl *wbc, GnmNamedExpr *nexpr, Sheet *scope)
6199 {
6200 	CmdRescopeName *me;
6201 
6202 	g_return_val_if_fail (wbc != NULL, TRUE);
6203 	g_return_val_if_fail (nexpr != NULL, TRUE);
6204 	g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6205 
6206 	expr_name_ref (nexpr);
6207 
6208 	me = g_object_new (CMD_RESCOPE_NAME_TYPE, NULL);
6209 	me->nexpr = nexpr;
6210 	me->scope = scope;
6211 	me->cmd.sheet = wb_control_cur_sheet (wbc);
6212 	me->cmd.size = 1;
6213 	me->cmd.cmd_descriptor = g_strdup_printf (_("Change Scope of Name %s"),
6214 						  expr_name_name (nexpr));
6215 
6216 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6217 }
6218 /******************************************************************/
6219 
6220 #define CMD_SCENARIO_ADD_TYPE (cmd_scenario_add_get_type ())
6221 #define CMD_SCENARIO_ADD(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_ADD_TYPE, CmdScenarioAdd))
6222 
6223 typedef struct {
6224 	GnmCommand cmd;
6225 	GnmScenario *scenario;
6226 } CmdScenarioAdd;
6227 
MAKE_GNM_COMMAND(CmdScenarioAdd,cmd_scenario_add,NULL)6228 MAKE_GNM_COMMAND (CmdScenarioAdd, cmd_scenario_add, NULL)
6229 
6230 static gboolean
6231 cmd_scenario_add_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6232 {
6233 	CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6234 	GnmScenario *sc = g_object_ref (me->scenario);
6235 	gnm_sheet_scenario_add (sc->sheet, sc);
6236 	return FALSE;
6237 }
6238 
6239 static gboolean
cmd_scenario_add_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6240 cmd_scenario_add_undo (GnmCommand *cmd,
6241 		       G_GNUC_UNUSED WorkbookControl *wbc)
6242 {
6243 	CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6244 	GnmScenario *sc = me->scenario;
6245 	gnm_sheet_scenario_remove (sc->sheet, sc);
6246 	return FALSE;
6247 }
6248 
6249 static void
cmd_scenario_add_finalize(GObject * cmd)6250 cmd_scenario_add_finalize (GObject *cmd)
6251 {
6252 	CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6253 
6254 	g_object_unref (me->scenario);
6255 	gnm_command_finalize (cmd);
6256 }
6257 
6258 /**
6259  * cmd_scenario_add: (skip)
6260  * @wbc:
6261  * @s: (transfer full):
6262  * @sheet:
6263  *
6264  * Returns: %TRUE if there was a problem, %FALSE otherwise.
6265  */
6266 gboolean
cmd_scenario_add(WorkbookControl * wbc,GnmScenario * s,Sheet * sheet)6267 cmd_scenario_add (WorkbookControl *wbc, GnmScenario *s, Sheet *sheet)
6268 {
6269 	CmdScenarioAdd *me;
6270 
6271 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6272 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6273 
6274 	me = g_object_new (CMD_SCENARIO_ADD_TYPE, NULL);
6275 
6276 	me->scenario  = s; /* Take ownership */
6277 	me->cmd.sheet = sheet;
6278 	me->cmd.size  = 1;
6279 	me->cmd.cmd_descriptor = g_strdup (_("Add scenario"));
6280 
6281 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6282 }
6283 
6284 /******************************************************************/
6285 
6286 #define CMD_SCENARIO_MNGR_TYPE (cmd_scenario_mngr_get_type ())
6287 #define CMD_SCENARIO_MNGR(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_MNGR_TYPE, CmdScenarioMngr))
6288 
6289 typedef struct {
6290 	GnmCommand cmd;
6291 	GnmScenario *sc;
6292 	GOUndo *undo;
6293 } CmdScenarioMngr;
6294 
MAKE_GNM_COMMAND(CmdScenarioMngr,cmd_scenario_mngr,NULL)6295 MAKE_GNM_COMMAND (CmdScenarioMngr, cmd_scenario_mngr, NULL)
6296 
6297 static gboolean
6298 cmd_scenario_mngr_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6299 {
6300 	CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6301 	if (!me->undo)
6302 		me->undo = gnm_scenario_apply (me->sc);
6303 	return FALSE;
6304 }
6305 
6306 static gboolean
cmd_scenario_mngr_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6307 cmd_scenario_mngr_undo (GnmCommand *cmd,
6308 			G_GNUC_UNUSED WorkbookControl *wbc)
6309 {
6310 	CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6311 	go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6312 	g_object_unref (me->undo);
6313 	me->undo = NULL;
6314 	return FALSE;
6315 }
6316 
6317 static void
cmd_scenario_mngr_finalize(GObject * cmd)6318 cmd_scenario_mngr_finalize (GObject *cmd)
6319 {
6320 	CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6321 
6322 	g_object_unref (me->sc);
6323 	if (me->undo)
6324 		g_object_unref (me->undo);
6325 
6326 	gnm_command_finalize (cmd);
6327 }
6328 
6329 gboolean
cmd_scenario_mngr(WorkbookControl * wbc,GnmScenario * sc,GOUndo * undo)6330 cmd_scenario_mngr (WorkbookControl *wbc, GnmScenario *sc, GOUndo *undo)
6331 {
6332 	CmdScenarioMngr *me;
6333 
6334 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6335 	g_return_val_if_fail (GNM_IS_SCENARIO (sc), TRUE);
6336 
6337 	me = g_object_new (CMD_SCENARIO_MNGR_TYPE, NULL);
6338 
6339 	me->sc = g_object_ref (sc);
6340 	me->undo = g_object_ref (undo);
6341 	me->cmd.sheet = sc->sheet;
6342 	me->cmd.size  = 1;
6343 	me->cmd.cmd_descriptor = g_strdup (_("Scenario Show"));
6344 
6345 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6346 }
6347 
6348 /******************************************************************/
6349 
6350 #define CMD_DATA_SHUFFLE_TYPE (cmd_data_shuffle_get_type ())
6351 #define CMD_DATA_SHUFFLE(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DATA_SHUFFLE_TYPE, CmdDataShuffle))
6352 
6353 typedef struct {
6354 	GnmCommand  cmd;
6355 	data_shuffling_t *ds;
6356 } CmdDataShuffle;
6357 
MAKE_GNM_COMMAND(CmdDataShuffle,cmd_data_shuffle,NULL)6358 MAKE_GNM_COMMAND (CmdDataShuffle, cmd_data_shuffle, NULL)
6359 
6360 static gboolean
6361 cmd_data_shuffle_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6362 {
6363 	CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6364 
6365 	data_shuffling_redo (me->ds);
6366 	return FALSE;
6367 }
6368 
6369 static gboolean
cmd_data_shuffle_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6370 cmd_data_shuffle_undo (GnmCommand *cmd,
6371 		       G_GNUC_UNUSED WorkbookControl *wbc)
6372 {
6373 	CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6374 
6375 	data_shuffling_redo (me->ds);
6376 	return FALSE;
6377 }
6378 
6379 static void
cmd_data_shuffle_finalize(GObject * cmd)6380 cmd_data_shuffle_finalize (GObject *cmd)
6381 {
6382 	CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6383 
6384 	data_shuffling_free (me->ds);
6385 	gnm_command_finalize (cmd);
6386 }
6387 
6388 gboolean
cmd_data_shuffle(WorkbookControl * wbc,data_shuffling_t * sc,Sheet * sheet)6389 cmd_data_shuffle (WorkbookControl *wbc, data_shuffling_t *sc, Sheet *sheet)
6390 {
6391 	CmdDataShuffle *me;
6392 
6393 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6394 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6395 
6396 	me = g_object_new (CMD_DATA_SHUFFLE_TYPE, NULL);
6397 
6398 	me->ds        = sc;
6399 	me->cmd.sheet = sheet;
6400 	me->cmd.size  = 1;
6401 	me->cmd.cmd_descriptor = g_strdup (_("Shuffle Data"));
6402 
6403 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6404 }
6405 
6406 /******************************************************************/
6407 
6408 #define CMD_TEXT_TO_COLUMNS_TYPE        (cmd_text_to_columns_get_type ())
6409 #define CMD_TEXT_TO_COLUMNS(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TEXT_TO_COLUMNS_TYPE, CmdTextToColumns))
6410 
6411 typedef struct {
6412 	GnmCommand cmd;
6413 
6414 	GnmCellRegion      *contents;
6415 	GnmPasteTarget      dst;
6416 	GnmRange            src;
6417 	Sheet           *src_sheet;
6418 	ColRowStateList *saved_sizes;
6419 } CmdTextToColumns;
6420 
MAKE_GNM_COMMAND(CmdTextToColumns,cmd_text_to_columns,NULL)6421 MAKE_GNM_COMMAND (CmdTextToColumns, cmd_text_to_columns, NULL)
6422 
6423 static gboolean
6424 cmd_text_to_columns_impl (GnmCommand *cmd, WorkbookControl *wbc,
6425 			  gboolean is_undo)
6426 {
6427 	CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6428 	GnmCellRegion *contents;
6429 
6430 	g_return_val_if_fail (me != NULL, TRUE);
6431 	g_return_val_if_fail (me->contents != NULL, TRUE);
6432 
6433 	contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
6434 	if (clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc))) {
6435 		/* There was a problem, avoid leaking */
6436 		cellregion_unref (contents);
6437 		return TRUE;
6438 	}
6439 
6440 	cellregion_unref (me->contents);
6441 
6442 	if (is_undo) {
6443 		colrow_set_states (me->dst.sheet, FALSE,
6444 			me->dst.range.start.row, me->saved_sizes);
6445 		colrow_state_list_destroy (me->saved_sizes);
6446 		me->saved_sizes = NULL;
6447 	} else {
6448 		me->saved_sizes = colrow_get_states (me->dst.sheet,
6449 			FALSE, me->dst.range.start.row, me->dst.range.end.row);
6450 		rows_height_update (me->dst.sheet, &me->dst.range, FALSE);
6451 	}
6452 
6453 	me->contents = contents;
6454 
6455 	/* Select the newly pasted contents  (this queues a redraw) */
6456 	select_range (me->dst.sheet, &me->dst.range, wbc);
6457 
6458 	return FALSE;
6459 }
6460 
6461 static gboolean
cmd_text_to_columns_undo(GnmCommand * cmd,WorkbookControl * wbc)6462 cmd_text_to_columns_undo (GnmCommand *cmd, WorkbookControl *wbc)
6463 {
6464 	return cmd_text_to_columns_impl (cmd, wbc, TRUE);
6465 }
6466 
6467 static gboolean
cmd_text_to_columns_redo(GnmCommand * cmd,WorkbookControl * wbc)6468 cmd_text_to_columns_redo (GnmCommand *cmd, WorkbookControl *wbc)
6469 {
6470 	return cmd_text_to_columns_impl (cmd, wbc, FALSE);
6471 }
6472 
6473 static void
cmd_text_to_columns_finalize(GObject * cmd)6474 cmd_text_to_columns_finalize (GObject *cmd)
6475 {
6476 	CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6477 
6478 	if (me->saved_sizes)
6479 		me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
6480 	if (me->contents) {
6481 		cellregion_unref (me->contents);
6482 		me->contents = NULL;
6483 	}
6484 	gnm_command_finalize (cmd);
6485 }
6486 
6487 gboolean
cmd_text_to_columns(WorkbookControl * wbc,GnmRange const * src,Sheet * src_sheet,GnmRange const * target,Sheet * target_sheet,GnmCellRegion * contents)6488 cmd_text_to_columns (WorkbookControl *wbc,
6489 		     GnmRange const *src, Sheet *src_sheet,
6490 		     GnmRange const *target, Sheet *target_sheet,
6491 		     GnmCellRegion *contents)
6492 {
6493 	CmdTextToColumns *me;
6494 	char *src_range_name, *target_range_name;
6495 
6496 	g_return_val_if_fail (contents != NULL, TRUE);
6497 
6498 	src_range_name = undo_range_name (src_sheet, src);
6499 	target_range_name = undo_range_name (target_sheet, target);
6500 
6501 	me = g_object_new (CMD_TEXT_TO_COLUMNS_TYPE, NULL);
6502 
6503 	me->cmd.sheet = (src_sheet == target_sheet ? src_sheet : NULL);
6504 	me->cmd.size = 1;  /* FIXME?  */
6505 	me->cmd.cmd_descriptor = g_strdup_printf (_("Text (%s) to Columns (%s)"),
6506 						  src_range_name,
6507 						  target_range_name);
6508 	me->dst.range = *target;
6509 	me->dst.sheet = target_sheet;
6510 	me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
6511 	me->src = *src;
6512 	me->src_sheet = src_sheet;
6513 	me->contents = contents;
6514 	me->saved_sizes = NULL;
6515 
6516 	g_free (src_range_name);
6517 	g_free (target_range_name);
6518 
6519 	/* Check array subdivision & merged regions */
6520 	if (sheet_range_splits_region (target_sheet, &me->dst.range,
6521 				       NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
6522 		g_object_unref (me);
6523 		return TRUE;
6524 	}
6525 
6526 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6527 }
6528 
6529 /******************************************************************/
6530 
6531 #define CMD_GENERIC_TYPE        (cmd_generic_get_type ())
6532 #define CMD_GENERIC(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GENERIC_TYPE, CmdGeneric))
6533 
6534 typedef struct {
6535 	GnmCommand cmd;
6536 	GOUndo *undo, *redo;
6537 } CmdGeneric;
6538 
MAKE_GNM_COMMAND(CmdGeneric,cmd_generic,NULL)6539 MAKE_GNM_COMMAND (CmdGeneric, cmd_generic, NULL)
6540 
6541 static gboolean
6542 cmd_generic_undo (GnmCommand *cmd, WorkbookControl *wbc)
6543 {
6544 	CmdGeneric *me = CMD_GENERIC (cmd);
6545 	go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6546 	return FALSE;
6547 }
6548 
6549 static gboolean
cmd_generic_redo(GnmCommand * cmd,WorkbookControl * wbc)6550 cmd_generic_redo (GnmCommand *cmd, WorkbookControl *wbc)
6551 {
6552 	CmdGeneric *me = CMD_GENERIC (cmd);
6553 	go_undo_undo_with_data (me->redo, GO_CMD_CONTEXT (wbc));
6554 	return FALSE;
6555 }
6556 
6557 static void
cmd_generic_finalize(GObject * cmd)6558 cmd_generic_finalize (GObject *cmd)
6559 {
6560 	CmdGeneric *me = CMD_GENERIC (cmd);
6561 
6562 	g_object_unref (me->undo);
6563 	g_object_unref (me->redo);
6564 
6565 	gnm_command_finalize (cmd);
6566 }
6567 
6568 gboolean
cmd_generic_with_size(WorkbookControl * wbc,const char * txt,int size,GOUndo * undo,GOUndo * redo)6569 cmd_generic_with_size (WorkbookControl *wbc, const char *txt,
6570 		       int size,
6571 		       GOUndo *undo, GOUndo *redo)
6572 {
6573 	CmdGeneric *me;
6574 
6575 	g_return_val_if_fail (GO_IS_UNDO (undo), TRUE);
6576 	g_return_val_if_fail (GO_IS_UNDO (redo), TRUE);
6577 
6578 	me = g_object_new (CMD_GENERIC_TYPE, NULL);
6579 
6580 	me->cmd.sheet = wb_control_cur_sheet (wbc);
6581 	me->cmd.size = size;
6582 	me->cmd.cmd_descriptor = g_strdup (txt);
6583 
6584 	me->undo = undo;
6585 	me->redo = redo;
6586 
6587 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6588 }
6589 
6590 gboolean
cmd_generic(WorkbookControl * wbc,const char * txt,GOUndo * undo,GOUndo * redo)6591 cmd_generic (WorkbookControl *wbc, const char *txt, GOUndo *undo, GOUndo *redo)
6592 {
6593 	return cmd_generic_with_size (wbc, txt, 1, undo, redo);
6594 }
6595 
6596 /******************************************************************/
6597 
6598 #define CMD_GOAL_SEEK_TYPE        (cmd_goal_seek_get_type ())
6599 #define CMD_GOAL_SEEK(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GOAL_SEEK_TYPE, CmdGoalSeek))
6600 
6601 typedef struct {
6602 	GnmCommand cmd;
6603 
6604 	GnmCell	  *cell;
6605 	GnmValue  *ov;
6606 	GnmValue  *nv;
6607 } CmdGoalSeek;
6608 
MAKE_GNM_COMMAND(CmdGoalSeek,cmd_goal_seek,NULL)6609 MAKE_GNM_COMMAND (CmdGoalSeek, cmd_goal_seek, NULL)
6610 
6611 static gboolean
6612 cmd_goal_seek_impl (GnmCell *cell, GnmValue *value)
6613 {
6614 	sheet_cell_set_value (cell, value_dup(value));
6615 	return FALSE;
6616 }
6617 
6618 
6619 static gboolean
cmd_goal_seek_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6620 cmd_goal_seek_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6621 {
6622 	CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6623 
6624 	return cmd_goal_seek_impl (me->cell, me->ov);
6625 }
6626 
6627 static gboolean
cmd_goal_seek_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6628 cmd_goal_seek_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6629 {
6630 	CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6631 
6632 	return cmd_goal_seek_impl (me->cell, me->nv);
6633 }
6634 
6635 static void
cmd_goal_seek_finalize(GObject * cmd)6636 cmd_goal_seek_finalize (GObject *cmd)
6637 {
6638 	CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6639 
6640 	value_release (me->ov);
6641 	me->ov = NULL;
6642 	value_release (me->nv);
6643 	me->nv = NULL;
6644 
6645 	gnm_command_finalize (cmd);
6646 }
6647 
6648 gboolean
cmd_goal_seek(WorkbookControl * wbc,GnmCell * cell,GnmValue * ov,GnmValue * nv)6649 cmd_goal_seek (WorkbookControl *wbc, GnmCell *cell, GnmValue *ov, GnmValue *nv)
6650 {
6651 	CmdGoalSeek *me;
6652 	GnmRange range;
6653 
6654 	g_return_val_if_fail (cell != NULL, TRUE);
6655 	g_return_val_if_fail (ov != NULL || nv != NULL, TRUE);
6656 
6657 	me = g_object_new (CMD_GOAL_SEEK_TYPE, NULL);
6658 
6659 	me->cmd.sheet = cell->base.sheet;
6660 	me->cmd.size = 1;
6661 	range_init_cellpos (&range, &cell->pos);
6662 	me->cmd.cmd_descriptor = g_strdup_printf
6663 		(_("Goal Seek (%s)"), undo_range_name (cell->base.sheet, &range));
6664 
6665 	me->cell = cell;
6666 	me->ov = ov;
6667 	me->nv = nv;
6668 
6669 	if (me->ov == NULL)
6670 		me->ov = value_dup (cell->value);
6671 	if (me->nv == NULL)
6672 		me->nv = value_dup (cell->value);
6673 
6674 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6675 }
6676 
6677 /******************************************************************/
6678 
6679 #if 0
6680 #define CMD_FREEZE_PANES_TYPE        (cmd_freeze_panes_get_type ())
6681 #define CMD_FREEZE_PANES(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FREEZE_PANES_TYPE, CmdFreezePanes))
6682 
6683 typedef struct {
6684 	GnmCommand cmd;
6685 
6686 	SheetView *sv;
6687 	GnmCellPos	   pos;
6688 } CmdFreezePanes;
6689 
6690 MAKE_GNM_COMMAND (CmdFreezePanes, cmd_freeze_panes, NULL)
6691 
6692 static gboolean
6693 cmd_freeze_panes_undo (GnmCommand *cmd, WorkbookControl *wbc)
6694 {
6695 	CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6696 
6697 	return FALSE;
6698 }
6699 
6700 static gboolean
6701 cmd_freeze_panes_redo (GnmCommand *cmd, WorkbookControl *wbc)
6702 {
6703 	CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6704 
6705 	return FALSE;
6706 }
6707 
6708 static void
6709 cmd_freeze_panes_finalize (GObject *cmd)
6710 {
6711 	CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6712 
6713 	gnm_command_finalize (cmd);
6714 }
6715 
6716 /**
6717  * cmd_freeze_panes:
6718  * @wbc: where to report errors
6719  * @sv: the view to freeze
6720  * @frozen:
6721  * @unfrozen:
6722  *
6723  * Returns: %TRUE if there was a problem, %FALSE otherwise.
6724  **/
6725 gboolean
6726 cmd_freeze_panes (WorkbookControl *wbc, SheetView *sv,
6727 		  GnmCellPos const *frozen, GnmCellPos const *unfrozen)
6728 {
6729 	CmdFreezePanes	*me;
6730 
6731 	g_return_val_if_fail (name != NULL, TRUE);
6732 	g_return_val_if_fail (pp != NULL, TRUE);
6733 	g_return_val_if_fail (expr != NULL, TRUE);
6734 
6735 	me = g_object_new (CMD_FREEZE_PANES_TYPE, NULL);
6736 	me->sv = sv;
6737 	me->frozen   = f;
6738 	me->unfrozen = expr;
6739 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6740 }
6741 
6742 #endif
6743 
6744 
6745 /******************************************************************/
6746 
6747 
6748 #define CMD_TABULATE_TYPE        (cmd_tabulate_get_type ())
6749 #define CMD_TABULATE(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TABULATE_TYPE, CmdTabulate))
6750 
6751 typedef struct {
6752 	GnmCommand cmd;
6753 	GSList *sheet_idx;
6754 	GnmTabulateInfo *data;
6755 } CmdTabulate;
6756 
MAKE_GNM_COMMAND(CmdTabulate,cmd_tabulate,NULL)6757 MAKE_GNM_COMMAND (CmdTabulate, cmd_tabulate, NULL)
6758 
6759 static gint
6760 cmd_tabulate_cmp_f (gconstpointer a,
6761 				    gconstpointer b)
6762 {
6763 	guint const a_val = GPOINTER_TO_INT (a);
6764 	guint const b_val = GPOINTER_TO_INT (b);
6765 
6766 	if (a_val > b_val)
6767 		return -1;
6768 	if (a_val < b_val)
6769 		return 1;
6770 	return 0;
6771 }
6772 
6773 static gboolean
cmd_tabulate_undo(GnmCommand * cmd,WorkbookControl * wbc)6774 cmd_tabulate_undo (GnmCommand *cmd, WorkbookControl *wbc)
6775 {
6776 	CmdTabulate *me = CMD_TABULATE (cmd);
6777 	GSList *l;
6778 	gboolean res = TRUE;
6779 
6780 	me->sheet_idx  = g_slist_sort (me->sheet_idx,
6781 				       cmd_tabulate_cmp_f);
6782 
6783 	for (l = me->sheet_idx; l != NULL; l = l->next) {
6784 		int i = GPOINTER_TO_INT (l->data);
6785 		Sheet *new_sheet =
6786 			workbook_sheet_by_index (wb_control_get_workbook (wbc),
6787 						 i);
6788 		res = res && command_undo_sheet_delete (new_sheet);
6789 	}
6790 	return !res;
6791 }
6792 
6793 static gboolean
cmd_tabulate_redo(GnmCommand * cmd,WorkbookControl * wbc)6794 cmd_tabulate_redo (GnmCommand *cmd, WorkbookControl *wbc)
6795 {
6796 	CmdTabulate *me = CMD_TABULATE (cmd);
6797 
6798 	g_slist_free (me->sheet_idx);
6799 	me->sheet_idx = do_tabulation (wbc, me->data);
6800 
6801 	return (me->sheet_idx == NULL);
6802 }
6803 
6804 static void
cmd_tabulate_finalize(GObject * cmd)6805 cmd_tabulate_finalize (GObject *cmd)
6806 {
6807 	CmdTabulate *me = CMD_TABULATE (cmd);
6808 
6809 	g_free (me->data->cells);
6810 	g_free (me->data->minima);
6811 	g_free (me->data->maxima);
6812 	g_free (me->data->steps);
6813 	g_free (me->data);
6814 	gnm_command_finalize (cmd);
6815 }
6816 
6817 gboolean
cmd_tabulate(WorkbookControl * wbc,gpointer data)6818 cmd_tabulate (WorkbookControl *wbc, gpointer data)
6819 {
6820 	CmdTabulate *me;
6821 
6822 	g_return_val_if_fail (data != NULL, TRUE);
6823 
6824 	me = g_object_new (CMD_TABULATE_TYPE, NULL);
6825 
6826 	me->cmd.sheet = NULL;
6827 	me->cmd.size = 1;
6828 	me->cmd.cmd_descriptor =
6829 		g_strdup_printf (_("Tabulating Dependencies"));
6830 	me->data = data;
6831 	me->sheet_idx = NULL;
6832 
6833 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6834 }
6835 
6836 /******************************************************************/
6837 
6838 #define CMD_SO_GRAPH_CONFIG_TYPE (cmd_so_graph_config_get_type ())
6839 #define CMD_SO_GRAPH_CONFIG(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_GRAPH_CONFIG_TYPE, CmdSOGraphConfig))
6840 
6841 typedef struct {
6842 	GnmCommand cmd;
6843 	SheetObject *so;
6844 	GogGraph *new_graph;
6845 	GogGraph *old_graph;
6846 } CmdSOGraphConfig;
6847 
MAKE_GNM_COMMAND(CmdSOGraphConfig,cmd_so_graph_config,NULL)6848 MAKE_GNM_COMMAND (CmdSOGraphConfig, cmd_so_graph_config, NULL)
6849 
6850 static gboolean
6851 cmd_so_graph_config_redo (GnmCommand *cmd,
6852 			  G_GNUC_UNUSED WorkbookControl *wbc)
6853 {
6854 	CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6855 	sheet_object_graph_set_gog (me->so, me->new_graph);
6856 	return FALSE;
6857 }
6858 
6859 static gboolean
cmd_so_graph_config_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6860 cmd_so_graph_config_undo (GnmCommand *cmd,
6861 			  G_GNUC_UNUSED WorkbookControl *wbc)
6862 {
6863 	CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6864 	sheet_object_graph_set_gog (me->so, me->old_graph);
6865 	return FALSE;
6866 }
6867 
6868 static void
cmd_so_graph_config_finalize(GObject * cmd)6869 cmd_so_graph_config_finalize (GObject *cmd)
6870 {
6871 	CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6872 
6873 	g_object_unref (me->so);
6874 	g_object_unref (me->new_graph);
6875 	g_object_unref (me->old_graph);
6876 
6877 	gnm_command_finalize (cmd);
6878 }
6879 
6880 gboolean
cmd_so_graph_config(WorkbookControl * wbc,SheetObject * so,GObject * n_graph,GObject * o_graph)6881 cmd_so_graph_config (WorkbookControl *wbc, SheetObject *so,
6882 		     GObject *n_graph, GObject *o_graph)
6883 {
6884 	CmdSOGraphConfig *me;
6885 
6886 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6887 	g_return_val_if_fail (GNM_IS_SO_GRAPH (so), TRUE);
6888 	g_return_val_if_fail (GOG_IS_GRAPH (n_graph), TRUE);
6889 	g_return_val_if_fail (GOG_IS_GRAPH (o_graph), TRUE);
6890 
6891 	me = g_object_new (CMD_SO_GRAPH_CONFIG_TYPE, NULL);
6892 
6893 	me->so = so;
6894 	g_object_ref (so);
6895 
6896 	me->new_graph = GOG_GRAPH (n_graph);
6897 	g_object_ref (me->new_graph);
6898 	me->old_graph = GOG_GRAPH (o_graph);
6899 	g_object_ref (me->old_graph);
6900 
6901 	me->cmd.sheet = sheet_object_get_sheet (so);
6902 	me->cmd.size = 10;
6903 	me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Graph"));
6904 
6905 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6906 }
6907 
6908 /******************************************************************/
6909 
6910 #define CMD_SO_COMPONENT_CONFIG_TYPE (cmd_so_component_config_get_type ())
6911 #define CMD_SO_COMPONENT_CONFIG(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_COMPONENT_CONFIG_TYPE, CmdSOComponentConfig))
6912 
6913 typedef struct {
6914 	GnmCommand cmd;
6915 	SheetObject *so;
6916 	GOComponent *new_obj;
6917 	GOComponent *old_obj;
6918 } CmdSOComponentConfig;
6919 
MAKE_GNM_COMMAND(CmdSOComponentConfig,cmd_so_component_config,NULL)6920 MAKE_GNM_COMMAND (CmdSOComponentConfig, cmd_so_component_config, NULL)
6921 
6922 static gboolean
6923 cmd_so_component_config_redo (GnmCommand *cmd,
6924 			  G_GNUC_UNUSED WorkbookControl *wbc)
6925 {
6926 	CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6927 	sheet_object_component_set_component (me->so, me->new_obj);
6928 	return FALSE;
6929 }
6930 
6931 static gboolean
cmd_so_component_config_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6932 cmd_so_component_config_undo (GnmCommand *cmd,
6933 			  G_GNUC_UNUSED WorkbookControl *wbc)
6934 {
6935 	CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6936 	sheet_object_component_set_component (me->so, me->old_obj);
6937 	return FALSE;
6938 }
6939 
6940 static void
cmd_so_component_config_finalize(GObject * cmd)6941 cmd_so_component_config_finalize (GObject *cmd)
6942 {
6943 	CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6944 
6945 	g_object_unref (me->so);
6946 	g_object_unref (me->new_obj);
6947 	g_object_unref (me->old_obj);
6948 
6949 	gnm_command_finalize (cmd);
6950 }
6951 
6952 gboolean
cmd_so_component_config(WorkbookControl * wbc,SheetObject * so,GObject * n_obj,GObject * o_obj)6953 cmd_so_component_config (WorkbookControl *wbc, SheetObject *so,
6954 		     GObject *n_obj, GObject *o_obj)
6955 {
6956 	CmdSOComponentConfig *me;
6957 
6958 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6959 	g_return_val_if_fail (GNM_IS_SO_COMPONENT (so), TRUE);
6960 	g_return_val_if_fail (GO_IS_COMPONENT (n_obj), TRUE);
6961 	g_return_val_if_fail (GO_IS_COMPONENT (o_obj), TRUE);
6962 
6963 	me = g_object_new (CMD_SO_COMPONENT_CONFIG_TYPE, NULL);
6964 
6965 	me->so = so;
6966 	g_object_ref (so);
6967 
6968 	me->new_obj = GO_COMPONENT (g_object_ref (n_obj));
6969 	me->old_obj = GO_COMPONENT (g_object_ref (o_obj));
6970 
6971 	me->cmd.sheet = sheet_object_get_sheet (so);
6972 	me->cmd.size = 10;
6973 	me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Object"));
6974 
6975 	return gnm_command_push_undo (wbc, G_OBJECT (me));
6976 }
6977 
6978 /******************************************************************/
6979 
6980 #define CMD_TOGGLE_RTL_TYPE (cmd_toggle_rtl_get_type ())
6981 #define CMD_TOGGLE_RTL(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TOGGLE_RTL_TYPE, CmdToggleRTL))
6982 
6983 typedef GnmCommand CmdToggleRTL;
6984 
MAKE_GNM_COMMAND(CmdToggleRTL,cmd_toggle_rtl,NULL)6985 MAKE_GNM_COMMAND (CmdToggleRTL, cmd_toggle_rtl, NULL)
6986 
6987 static gboolean
6988 cmd_toggle_rtl_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6989 {
6990 	go_object_toggle (cmd->sheet, "text-is-rtl");
6991 	return FALSE;
6992 }
6993 
6994 static gboolean
cmd_toggle_rtl_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6995 cmd_toggle_rtl_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6996 {
6997 	return cmd_toggle_rtl_redo (cmd, wbc);
6998 }
6999 
7000 static void
cmd_toggle_rtl_finalize(GObject * cmd)7001 cmd_toggle_rtl_finalize (GObject *cmd)
7002 {
7003 	gnm_command_finalize (cmd);
7004 }
7005 
7006 gboolean
cmd_toggle_rtl(WorkbookControl * wbc,Sheet * sheet)7007 cmd_toggle_rtl (WorkbookControl *wbc, Sheet *sheet)
7008 {
7009 	CmdToggleRTL *me;
7010 
7011 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7012 	g_return_val_if_fail (IS_SHEET (sheet), TRUE);
7013 
7014 	me = g_object_new (CMD_TOGGLE_RTL_TYPE, NULL);
7015 	me->sheet = sheet;
7016 	me->size = 1;
7017 	me->cmd_descriptor = g_strdup (sheet->text_is_rtl ? _("Left to Right") : _("Right to Left"));
7018 
7019 	return gnm_command_push_undo (wbc, G_OBJECT (me));
7020 }
7021 
7022 /******************************************************************/
7023 
7024 #define CMD_SO_SET_VALUE_TYPE (cmd_so_set_value_get_type ())
7025 #define CMD_SO_SET_VALUE(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_VALUE_TYPE, CmdSOSetValue))
7026 
7027 typedef struct {
7028 	GnmCommand cmd;
7029 	GnmCellRef ref;
7030 	GnmValue *val;
7031 	GOUndo *undo;
7032 } CmdSOSetValue;
7033 
MAKE_GNM_COMMAND(CmdSOSetValue,cmd_so_set_value,NULL)7034 MAKE_GNM_COMMAND (CmdSOSetValue, cmd_so_set_value, NULL)
7035 
7036 static gboolean
7037 cmd_so_set_value_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7038 {
7039 	CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7040 	Sheet *sheet = me->ref.sheet;
7041 	GnmCell *cell = sheet_cell_fetch (sheet, me->ref.col, me->ref.row);
7042 
7043 	sheet_cell_set_value (cell, value_dup (me->val));
7044 	sheet_update (sheet);
7045 
7046 	return FALSE;
7047 }
7048 
7049 static gboolean
cmd_so_set_value_undo(GnmCommand * cmd,WorkbookControl * wbc)7050 cmd_so_set_value_undo (GnmCommand *cmd, WorkbookControl *wbc)
7051 {
7052 	CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7053 
7054 	go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
7055 
7056 	return FALSE;
7057 }
7058 
7059 static void
cmd_so_set_value_finalize(GObject * cmd)7060 cmd_so_set_value_finalize (GObject *cmd)
7061 {
7062 	CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7063 
7064 	value_release (me->val);
7065 	g_object_unref (me->undo);
7066 
7067 	gnm_command_finalize (cmd);
7068 }
7069 
7070 gboolean
cmd_so_set_value(WorkbookControl * wbc,const char * text,const GnmCellRef * pref,GnmValue * new_val,Sheet * sheet)7071 cmd_so_set_value (WorkbookControl *wbc,
7072 		  const char *text,
7073 		  const GnmCellRef *pref,
7074 		  GnmValue *new_val,
7075 		  Sheet *sheet)
7076 {
7077 	CmdSOSetValue *me;
7078 	GnmRange r;
7079 
7080 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7081 
7082 	r.start.col = r.end.col = pref->col;
7083 	r.start.row = r.end.row = pref->row;
7084 
7085 	me = g_object_new (CMD_SO_SET_VALUE_TYPE, NULL);
7086 	me->cmd.sheet = sheet;
7087 	me->cmd.size = 1;
7088 	me->cmd.cmd_descriptor = g_strdup (text);
7089 	me->ref = *pref;
7090 	me->val = new_val;
7091 	me->undo = clipboard_copy_range_undo (pref->sheet, &r);
7092 
7093 	return gnm_command_push_undo (wbc, G_OBJECT (me));
7094 }
7095 
7096 /******************************************************************/
7097 
7098 #define CMD_HYPERLINK_TYPE        (cmd_hyperlink_get_type ())
7099 #define CMD_HYPERLINK(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_HYPERLINK_TYPE, CmdHyperlink))
7100 
7101 typedef struct {
7102 	GnmCommand cmd;
7103 	GSList	   *selection;
7104 	GnmStyle   *new_style;
7105 	char       *opt_content;
7106 	GOUndo     *undo;
7107 	gboolean   update_size;
7108 } CmdHyperlink;
7109 
7110 static void
cmd_hyperlink_repeat(GnmCommand const * cmd,WorkbookControl * wbc)7111 cmd_hyperlink_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
7112 {
7113 	CmdHyperlink const *orig = (CmdHyperlink const *) cmd;
7114 
7115 	if (orig->new_style)
7116 		gnm_style_ref (orig->new_style);
7117 
7118 	cmd_selection_hyperlink (wbc, orig->new_style, NULL,
7119 				 g_strdup (orig->opt_content));
7120 }
MAKE_GNM_COMMAND(CmdHyperlink,cmd_hyperlink,cmd_hyperlink_repeat)7121 MAKE_GNM_COMMAND (CmdHyperlink, cmd_hyperlink, cmd_hyperlink_repeat)
7122 
7123 static gboolean
7124 cmd_hyperlink_undo (GnmCommand *cmd, WorkbookControl *wbc)
7125 {
7126 	CmdHyperlink *me = CMD_HYPERLINK (cmd);
7127 	Workbook *wb = wb_control_get_workbook (wbc);
7128 
7129 	if (me->undo) {
7130 		go_undo_undo (me->undo);
7131 		g_clear_object (&me->undo);
7132 	}
7133 
7134 	select_selection (me->cmd.sheet, me->selection, wbc);
7135 
7136 	WORKBOOK_FOREACH_CONTROL (wb, view, ctl, {
7137 			wb_control_menu_state_update (ctl, MS_COMMENT_LINKS);
7138 		});
7139 
7140 	return FALSE;
7141 }
7142 
7143 static GnmValue *
cb_hyperlink_set_text(GnmCellIter const * iter,gpointer user)7144 cb_hyperlink_set_text (GnmCellIter const *iter, gpointer user)
7145 {
7146 	CmdHyperlink *me = user;
7147 	GnmCell *cell = iter->cell;
7148 
7149 	if (cell == NULL)
7150 		cell = sheet_cell_fetch (iter->pp.sheet,
7151 					 iter->pp.eval.col,
7152 					 iter->pp.eval.row);
7153 
7154 	/* We skip non-empty cells.  */
7155 	if (gnm_cell_is_empty (cell) &&
7156 	    !gnm_cell_is_nonsingleton_array (cell)) {
7157 		sheet_cell_set_value (cell, value_new_string (me->opt_content));
7158 		if (me->update_size)
7159 			me->cmd.size++;
7160 	}
7161 
7162 	return NULL;
7163 }
7164 
7165 static gboolean
cmd_hyperlink_redo(GnmCommand * cmd,WorkbookControl * wbc)7166 cmd_hyperlink_redo (GnmCommand *cmd, WorkbookControl *wbc)
7167 {
7168 	CmdHyperlink *me = CMD_HYPERLINK (cmd);
7169 	GSList *l;
7170 	Workbook *wb = wb_control_get_workbook (wbc);
7171 	Sheet *sheet;
7172 
7173 	g_return_val_if_fail (me != NULL, TRUE);
7174 
7175 	sheet = me->cmd.sheet;
7176 
7177 	/* Check for locked cells */
7178 	if (cmd_selection_is_locked_effective (sheet, me->selection,
7179 					       wbc, _("Changing Hyperlink")))
7180 		return TRUE;
7181 
7182 	me->undo = clipboard_copy_ranges_undo (sheet, me->selection);
7183 
7184 	for (l = me->selection; l; l = l->next) {
7185 		GnmRange const *r = l->data;
7186 
7187 		if (me->new_style) {
7188 			gnm_style_ref (me->new_style);
7189 			sheet_apply_style (sheet, r, me->new_style);
7190 			sheet_flag_style_update_range (sheet, r);
7191 		}
7192 
7193 		if (me->opt_content) {
7194 			sheet_foreach_cell_in_range (sheet, CELL_ITER_ALL, r,
7195 						     cb_hyperlink_set_text,
7196 						     me);
7197 		}
7198 	}
7199 	me->update_size = FALSE;
7200 
7201 	sheet_redraw_all (sheet, FALSE);
7202 	sheet_mark_dirty (sheet);
7203 
7204 	select_selection (sheet, me->selection, wbc);
7205 
7206 	WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
7207 			wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
7208 
7209 	return FALSE;
7210 }
7211 
7212 static void
cmd_hyperlink_finalize(GObject * cmd)7213 cmd_hyperlink_finalize (GObject *cmd)
7214 {
7215 	CmdHyperlink *me = CMD_HYPERLINK (cmd);
7216 
7217 	g_clear_object (&me->undo);
7218 
7219 	if (me->new_style)
7220 		gnm_style_unref (me->new_style);
7221 	me->new_style = NULL;
7222 
7223 	range_fragment_free (me->selection);
7224 	me->selection = NULL;
7225 
7226 	g_free (me->opt_content);
7227 
7228 	gnm_command_finalize (cmd);
7229 }
7230 
7231 /**
7232  * cmd_selection_hyperlink:
7233  * @wbc: the workbook control.
7234  * @style: (transfer full): style to apply to the selection
7235  * @opt_translated_name: An optional name to use in place of 'Hyperlink Cells'
7236  * @opt_content: optional content for otherwise empty cells.
7237  *
7238  * Returns: %TRUE if there was a problem, %FALSE otherwise.
7239  **/
7240 gboolean
cmd_selection_hyperlink(WorkbookControl * wbc,GnmStyle * style,char const * opt_translated_name,char * opt_content)7241 cmd_selection_hyperlink (WorkbookControl *wbc,
7242 			 GnmStyle *style,
7243 			 char const *opt_translated_name,
7244 			 char *opt_content)
7245 {
7246 	CmdHyperlink *me;
7247 	SheetView *sv = wb_control_cur_sheet_view (wbc);
7248 
7249 	me = g_object_new (CMD_HYPERLINK_TYPE, NULL);
7250 
7251 	me->selection  = selection_get_ranges (sv, FALSE); /* TRUE ? */
7252 	me->new_style  = style;
7253 
7254 	me->cmd.sheet = sv_sheet (sv);
7255 	me->cmd.size = 1;  /* Updated later.  */
7256 	me->update_size = TRUE;
7257 
7258 	me->opt_content = opt_content;
7259 
7260 	if (opt_translated_name == NULL) {
7261 		char *names = undo_range_list_name (me->cmd.sheet, me->selection);
7262 
7263 		me->cmd.cmd_descriptor = g_strdup_printf (_("Changing hyperlink of %s"), names);
7264 		g_free (names);
7265 	} else
7266 		me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
7267 
7268 
7269 	return gnm_command_push_undo (wbc, G_OBJECT (me));
7270 }
7271 
7272 /******************************************************************/
7273 
7274 
7275 #define CMD_SO_SET_LINKS_TYPE (cmd_so_set_links_get_type ())
7276 #define CMD_SO_SET_LINKS(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_LINKS_TYPE, CmdSOSetLink))
7277 
7278 typedef struct {
7279 	GnmCommand cmd;
7280 	SheetObject *so;
7281 	GnmExprTop const *output;
7282 	GnmExprTop const *content;
7283 	gboolean as_index;
7284 } CmdSOSetLink;
7285 
MAKE_GNM_COMMAND(CmdSOSetLink,cmd_so_set_links,NULL)7286 MAKE_GNM_COMMAND (CmdSOSetLink, cmd_so_set_links, NULL)
7287 
7288 static gboolean
7289 cmd_so_set_links_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7290 {
7291 	CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7292 	GnmExprTop const *old_output;
7293 	GnmExprTop const *old_content;
7294 	gboolean old_as_index;
7295 
7296 	old_content = sheet_widget_list_base_get_content_link (me->so);
7297 	old_output = sheet_widget_list_base_get_result_link (me->so);
7298 	old_as_index = sheet_widget_list_base_result_type_is_index (me->so);
7299 
7300 	sheet_widget_list_base_set_links
7301 		(me->so, me->output, me->content);
7302 	if (old_as_index != me->as_index) {
7303 		sheet_widget_list_base_set_result_type (me->so, me->as_index);
7304 		me->as_index = old_as_index;
7305 	}
7306 	if (me->output)
7307 		gnm_expr_top_unref (me->output);
7308 	if (me->content)
7309 		gnm_expr_top_unref (me->content);
7310 	me->output = old_output;
7311 	me->content = old_content;
7312 
7313 	return FALSE;
7314 }
7315 
7316 static gboolean
cmd_so_set_links_undo(GnmCommand * cmd,WorkbookControl * wbc)7317 cmd_so_set_links_undo (GnmCommand *cmd, WorkbookControl *wbc)
7318 {
7319 	return cmd_so_set_links_redo (cmd, wbc);
7320 }
7321 
7322 static void
cmd_so_set_links_finalize(GObject * cmd)7323 cmd_so_set_links_finalize (GObject *cmd)
7324 {
7325 	CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7326 
7327 	if (me->output)
7328 		gnm_expr_top_unref (me->output);
7329 	if (me->content)
7330 		gnm_expr_top_unref (me->content);
7331 	gnm_command_finalize (cmd);
7332 }
7333 
7334 gboolean
cmd_so_set_links(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * output,GnmExprTop const * content,gboolean as_index)7335 cmd_so_set_links (WorkbookControl *wbc,
7336 		  SheetObject *so,
7337 		  GnmExprTop const *output,
7338 		  GnmExprTop const *content,
7339 		  gboolean as_index)
7340 {
7341 	CmdSOSetLink *me;
7342 
7343 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7344 
7345 	me = g_object_new (CMD_SO_SET_LINKS_TYPE, NULL);
7346 	me->cmd.sheet = sheet_object_get_sheet (so);
7347 	me->cmd.size = 1;
7348 	me->cmd.cmd_descriptor = g_strdup (_("Configure List"));
7349 	me->so = so;
7350 	me->output = output;
7351 	me->content = content;
7352 	me->as_index = as_index;
7353 
7354 	return gnm_command_push_undo (wbc, G_OBJECT (me));
7355 }
7356 
7357 /******************************************************************/
7358 
7359 
7360 
7361 #define CMD_SO_SET_FRAME_LABEL_TYPE (cmd_so_set_frame_label_get_type ())
7362 #define CMD_SO_SET_FRAME_LABEL(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_FRAME_LABEL_TYPE, CmdSOSetFrameLabel))
7363 
7364 typedef struct {
7365 	GnmCommand cmd;
7366 	SheetObject *so;
7367 	char *old_label;
7368 	char *new_label;
7369 } CmdSOSetFrameLabel;
7370 
MAKE_GNM_COMMAND(CmdSOSetFrameLabel,cmd_so_set_frame_label,NULL)7371 MAKE_GNM_COMMAND (CmdSOSetFrameLabel, cmd_so_set_frame_label, NULL)
7372 
7373 static gboolean
7374 cmd_so_set_frame_label_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7375 {
7376 	CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7377 
7378 	sheet_widget_frame_set_label (me->so, me->new_label);
7379 
7380 	return FALSE;
7381 }
7382 
7383 static gboolean
cmd_so_set_frame_label_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7384 cmd_so_set_frame_label_undo (GnmCommand *cmd, G_GNUC_UNUSED  WorkbookControl *wbc)
7385 {
7386 	CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7387 
7388 	sheet_widget_frame_set_label (me->so, me->old_label);
7389 
7390 	return FALSE;
7391 }
7392 
7393 static void
cmd_so_set_frame_label_finalize(GObject * cmd)7394 cmd_so_set_frame_label_finalize (GObject *cmd)
7395 {
7396 	CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7397 
7398 	g_free (me->old_label);
7399 	me->old_label = NULL;
7400 
7401 	g_free (me->new_label);
7402 	me->new_label = NULL;
7403 
7404 	gnm_command_finalize (cmd);
7405 }
7406 
7407 gboolean
cmd_so_set_frame_label(WorkbookControl * wbc,SheetObject * so,char * old_label,char * new_label)7408 cmd_so_set_frame_label (WorkbookControl *wbc,
7409 			SheetObject *so,
7410 			char *old_label, char *new_label )
7411 {
7412 	CmdSOSetFrameLabel *me;
7413 
7414 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7415 
7416 	me = g_object_new (CMD_SO_SET_FRAME_LABEL_TYPE, NULL);
7417 	me->cmd.sheet = sheet_object_get_sheet (so);
7418 	me->cmd.size = 1;
7419 	me->cmd.cmd_descriptor = g_strdup (_("Set Frame Label"));
7420 	me->so = so;
7421 	me->old_label = old_label;
7422 	me->new_label = new_label;
7423 
7424 	return gnm_command_push_undo (wbc, G_OBJECT (me));
7425 }
7426 
7427 /******************************************************************/
7428 #define CMD_SO_SET_BUTTON_TYPE (cmd_so_set_button_get_type ())
7429 #define CMD_SO_SET_BUTTON(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_BUTTON_TYPE, CmdSOSetButton))
7430 
7431 typedef struct {
7432 	GnmCommand cmd;
7433 	SheetObject *so;
7434 	GnmExprTop const *new_link;
7435 	GnmExprTop const *old_link;
7436 	char *old_label;
7437 	char *new_label;
7438 } CmdSOSetButton;
7439 
MAKE_GNM_COMMAND(CmdSOSetButton,cmd_so_set_button,NULL)7440 MAKE_GNM_COMMAND (CmdSOSetButton, cmd_so_set_button, NULL)
7441 
7442 static gboolean
7443 cmd_so_set_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7444 {
7445 	CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7446 
7447 	sheet_widget_button_set_link (me->so, me->new_link);
7448 	sheet_widget_button_set_label (me->so, me->new_label);
7449 
7450 	return FALSE;
7451 }
7452 
7453 static gboolean
cmd_so_set_button_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7454 cmd_so_set_button_undo (GnmCommand *cmd, G_GNUC_UNUSED  WorkbookControl *wbc)
7455 {
7456 	CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7457 
7458 	sheet_widget_button_set_link (me->so, me->old_link);
7459 	sheet_widget_button_set_label (me->so, me->old_label);
7460 
7461 	return FALSE;
7462 }
7463 
7464 static void
cmd_so_set_button_finalize(GObject * cmd)7465 cmd_so_set_button_finalize (GObject *cmd)
7466 {
7467 	CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7468 
7469 	if (me->new_link)
7470 		gnm_expr_top_unref (me->new_link);
7471 	if (me->old_link)
7472 		gnm_expr_top_unref (me->old_link);
7473 	g_free (me->old_label);
7474 	g_free (me->new_label);
7475 	gnm_command_finalize (cmd);
7476 }
7477 
7478 gboolean
cmd_so_set_button(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * lnk,char * old_label,char * new_label)7479 cmd_so_set_button (WorkbookControl *wbc,
7480 		   SheetObject *so, GnmExprTop const *lnk,
7481 		   char *old_label, char *new_label)
7482 {
7483 	CmdSOSetButton *me;
7484 
7485 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7486 
7487 	me = g_object_new (CMD_SO_SET_BUTTON_TYPE, NULL);
7488 	me->cmd.sheet = sheet_object_get_sheet (so);
7489 	me->cmd.size = 1;
7490 	me->cmd.cmd_descriptor = g_strdup (_("Configure Button"));
7491 	me->so = so;
7492 	me->new_link = lnk;
7493 	me->old_label = old_label;
7494 	me->new_label = new_label;
7495 
7496 	me->old_link = sheet_widget_button_get_link (so);
7497 
7498 	return gnm_command_push_undo (wbc, G_OBJECT (me));
7499 }
7500 
7501 /******************************************************************/
7502 #define CMD_SO_SET_RADIO_BUTTON_TYPE (cmd_so_set_radio_button_get_type ())
7503 #define CMD_SO_SET_RADIO_BUTTON(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_RADIO_BUTTON_TYPE, CmdSOSetRadioButton))
7504 
7505 typedef struct {
7506 	GnmCommand cmd;
7507 	SheetObject *so;
7508 	GnmExprTop const *new_link;
7509 	GnmExprTop const *old_link;
7510 	char *old_label;
7511 	char *new_label;
7512 	GnmValue *old_value;
7513 	GnmValue *new_value;
7514 } CmdSOSetRadioButton;
7515 
MAKE_GNM_COMMAND(CmdSOSetRadioButton,cmd_so_set_radio_button,NULL)7516 MAKE_GNM_COMMAND (CmdSOSetRadioButton, cmd_so_set_radio_button, NULL)
7517 
7518 static gboolean
7519 cmd_so_set_radio_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7520 {
7521 	CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7522 
7523 	sheet_widget_radio_button_set_link (me->so, me->new_link);
7524 	sheet_widget_radio_button_set_label (me->so, me->new_label);
7525 	sheet_widget_radio_button_set_value (me->so, me->new_value);
7526 
7527 	return FALSE;
7528 }
7529 
7530 static gboolean
cmd_so_set_radio_button_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7531 cmd_so_set_radio_button_undo (GnmCommand *cmd, G_GNUC_UNUSED  WorkbookControl *wbc)
7532 {
7533 	CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7534 
7535 	sheet_widget_radio_button_set_link (me->so, me->old_link);
7536 	sheet_widget_radio_button_set_label (me->so, me->old_label);
7537 	sheet_widget_radio_button_set_value (me->so, me->old_value);
7538 
7539 	return FALSE;
7540 }
7541 
7542 static void
cmd_so_set_radio_button_finalize(GObject * cmd)7543 cmd_so_set_radio_button_finalize (GObject *cmd)
7544 {
7545 	CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7546 
7547 	if (me->new_link)
7548 		gnm_expr_top_unref (me->new_link);
7549 	if (me->old_link)
7550 		gnm_expr_top_unref (me->old_link);
7551 	g_free (me->old_label);
7552 	g_free (me->new_label);
7553 	value_release (me->old_value);
7554 	value_release (me->new_value);
7555 	gnm_command_finalize (cmd);
7556 }
7557 
7558 gboolean
cmd_so_set_radio_button(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * lnk,char * old_label,char * new_label,GnmValue * old_value,GnmValue * new_value)7559 cmd_so_set_radio_button (WorkbookControl *wbc,
7560 			 SheetObject *so, GnmExprTop const *lnk,
7561 			 char *old_label, char *new_label,
7562 			 GnmValue *old_value, GnmValue *new_value)
7563 {
7564 	CmdSOSetRadioButton *me;
7565 
7566 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7567 
7568 	me = g_object_new (CMD_SO_SET_RADIO_BUTTON_TYPE, NULL);
7569 	me->cmd.sheet = sheet_object_get_sheet (so);
7570 	me->cmd.size = 1;
7571 	me->cmd.cmd_descriptor = g_strdup (_("Configure Radio Button"));
7572 	me->so = so;
7573 	me->new_link = lnk;
7574 	me->old_label = old_label;
7575 	me->new_label = new_label;
7576 	me->old_value = old_value;
7577 	me->new_value = new_value;
7578 
7579 	me->old_link = sheet_widget_radio_button_get_link (so);
7580 
7581 	return gnm_command_push_undo (wbc, G_OBJECT (me));
7582 }
7583 
7584 /******************************************************************/
7585 #define CMD_SO_SET_CHECKBOX_TYPE (cmd_so_set_checkbox_get_type ())
7586 #define CMD_SO_SET_CHECKBOX(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_CHECKBOX_TYPE, CmdSOSetCheckbox))
7587 
7588 typedef struct {
7589 	GnmCommand cmd;
7590 	SheetObject *so;
7591 	GnmExprTop const *new_link;
7592 	GnmExprTop const *old_link;
7593 	char *old_label;
7594 	char *new_label;
7595 } CmdSOSetCheckbox;
7596 
MAKE_GNM_COMMAND(CmdSOSetCheckbox,cmd_so_set_checkbox,NULL)7597 MAKE_GNM_COMMAND (CmdSOSetCheckbox, cmd_so_set_checkbox, NULL)
7598 
7599 static gboolean
7600 cmd_so_set_checkbox_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7601 {
7602 	CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7603 
7604 	sheet_widget_checkbox_set_link (me->so, me->new_link);
7605 	sheet_widget_checkbox_set_label (me->so, me->new_label);
7606 
7607 	return FALSE;
7608 }
7609 
7610 static gboolean
cmd_so_set_checkbox_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7611 cmd_so_set_checkbox_undo (GnmCommand *cmd, G_GNUC_UNUSED  WorkbookControl *wbc)
7612 {
7613 	CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7614 
7615 	sheet_widget_checkbox_set_link (me->so, me->old_link);
7616 	sheet_widget_checkbox_set_label (me->so, me->old_label);
7617 
7618 	return FALSE;
7619 }
7620 
7621 static void
cmd_so_set_checkbox_finalize(GObject * cmd)7622 cmd_so_set_checkbox_finalize (GObject *cmd)
7623 {
7624 	CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7625 
7626 	if (me->new_link)
7627 		gnm_expr_top_unref (me->new_link);
7628 	if (me->old_link)
7629 		gnm_expr_top_unref (me->old_link);
7630 	g_free (me->old_label);
7631 	g_free (me->new_label);
7632 	gnm_command_finalize (cmd);
7633 }
7634 
7635 gboolean
cmd_so_set_checkbox(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * lnk,char * old_label,char * new_label)7636 cmd_so_set_checkbox (WorkbookControl *wbc,
7637 		     SheetObject *so, GnmExprTop const *lnk,
7638 		     char *old_label, char *new_label)
7639 {
7640 	CmdSOSetCheckbox *me;
7641 
7642 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7643 
7644 	me = g_object_new (CMD_SO_SET_CHECKBOX_TYPE, NULL);
7645 	me->cmd.sheet = sheet_object_get_sheet (so);
7646 	me->cmd.size = 1;
7647 	me->cmd.cmd_descriptor = g_strdup (_("Configure Checkbox"));
7648 	me->so = so;
7649 	me->new_link = lnk;
7650 	me->old_label = old_label;
7651 	me->new_label = new_label;
7652 
7653 	me->old_link = sheet_widget_checkbox_get_link (so);
7654 
7655 	return gnm_command_push_undo (wbc, G_OBJECT (me));
7656 }
7657 
7658 /******************************************************************/
7659 
7660 #define CMD_SO_SET_ADJUSTMENT_TYPE (cmd_so_set_adjustment_get_type ())
7661 #define CMD_SO_SET_ADJUSTMENT(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_ADJUSTMENT_TYPE, CmdSOSetAdjustment))
7662 
7663 typedef struct {
7664 	GnmCommand cmd;
7665 	SheetObject *so;
7666 	GnmExprTop const *new_link;
7667 	GnmExprTop const *old_link;
7668 	double old_lower;
7669 	double old_upper;
7670 	double old_step;
7671 	double old_page;
7672 	gboolean old_horizontal;
7673 } CmdSOSetAdjustment;
7674 
MAKE_GNM_COMMAND(CmdSOSetAdjustment,cmd_so_set_adjustment,NULL)7675 MAKE_GNM_COMMAND (CmdSOSetAdjustment, cmd_so_set_adjustment, NULL)
7676 
7677 static void
7678 cmd_so_set_adjustment_adj (CmdSOSetAdjustment *me)
7679 {
7680 	GtkAdjustment *adj = sheet_widget_adjustment_get_adjustment (me->so);
7681 
7682 	double old_lower = gtk_adjustment_get_lower (adj);
7683 	double old_upper = gtk_adjustment_get_upper (adj);
7684 	double old_step = gtk_adjustment_get_step_increment (adj);
7685 	double old_page = gtk_adjustment_get_page_increment (adj);
7686 	gboolean old_horizontal;
7687 	g_object_get (G_OBJECT (me->so), "horizontal", &old_horizontal, NULL);
7688 
7689 	gtk_adjustment_configure (adj,
7690 				  gtk_adjustment_get_value (adj),
7691 				  me->old_lower,
7692 				  me->old_upper,
7693 				  me->old_step,
7694 				  me->old_page,
7695 				  gtk_adjustment_get_page_size (adj));
7696 	g_object_set (G_OBJECT (me->so), "horizontal", me->old_horizontal, NULL);
7697 
7698 	me->old_lower = old_lower;
7699 	me->old_upper = old_upper;
7700 	me->old_step = old_step;
7701 	me->old_page = old_page;
7702 	me->old_horizontal = old_horizontal;
7703 }
7704 
7705 static gboolean
cmd_so_set_adjustment_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7706 cmd_so_set_adjustment_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7707 {
7708 	CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7709 
7710 	sheet_widget_adjustment_set_link (me->so, me->new_link);
7711 	cmd_so_set_adjustment_adj (me);
7712 	return FALSE;
7713 }
7714 
7715 static gboolean
cmd_so_set_adjustment_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7716 cmd_so_set_adjustment_undo (GnmCommand *cmd, G_GNUC_UNUSED  WorkbookControl *wbc)
7717 {
7718 	CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7719 
7720 	sheet_widget_adjustment_set_link (me->so, me->old_link);
7721 	cmd_so_set_adjustment_adj (me);
7722 
7723 	return FALSE;
7724 }
7725 
7726 static void
cmd_so_set_adjustment_finalize(GObject * cmd)7727 cmd_so_set_adjustment_finalize (GObject *cmd)
7728 {
7729 	CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7730 
7731 	if (me->new_link)
7732 		gnm_expr_top_unref (me->new_link);
7733 	if (me->old_link)
7734 		gnm_expr_top_unref (me->old_link);
7735 	gnm_command_finalize (cmd);
7736 }
7737 
7738 gboolean
cmd_so_set_adjustment(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * lnk,gboolean horizontal,int lower,int upper,int step,int page,char const * undo_label)7739 cmd_so_set_adjustment (WorkbookControl *wbc,
7740 		       SheetObject *so, GnmExprTop const *lnk,
7741 		       gboolean horizontal,
7742 		       int lower, int upper,
7743 		       int step, int page,
7744 		       char const *undo_label)
7745 {
7746 	CmdSOSetAdjustment *me;
7747 
7748 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7749 
7750 	me = g_object_new (CMD_SO_SET_ADJUSTMENT_TYPE, NULL);
7751 	me->cmd.sheet = sheet_object_get_sheet (so);
7752 	me->cmd.size = 1;
7753 	me->cmd.cmd_descriptor = g_strdup ((undo_label == NULL) ?
7754 					   _("Configure Adjustment") : _(undo_label));
7755 	me->so = so;
7756 	me->new_link = lnk;
7757 	me->old_lower = lower;
7758 	me->old_upper = upper;
7759 	me->old_step = step;
7760 	me->old_page = page;
7761 	me->old_horizontal = horizontal;
7762 
7763 	me->old_link = sheet_widget_adjustment_get_link (so);
7764 
7765 	return gnm_command_push_undo (wbc, G_OBJECT (me));
7766 }
7767 
7768 /******************************************************************/
7769 
7770 gboolean
cmd_autofilter_add_remove(WorkbookControl * wbc)7771 cmd_autofilter_add_remove (WorkbookControl *wbc)
7772 {
7773 	SheetView *sv = wb_control_cur_sheet_view (wbc);
7774 	GnmFilter *f = gnm_sheet_view_editpos_in_filter (sv);
7775 	gboolean add = (f == NULL);
7776 	char *descr = NULL, *name = NULL;
7777 	GOUndo *undo = NULL;
7778 	GOUndo *redo = NULL;
7779 	gboolean result;
7780 
7781 
7782 	if (add) {
7783 		GnmRange region;
7784 		GnmRange const *src = selection_first_range (sv,
7785 			GO_CMD_CONTEXT (wbc), _("Add Filter"));
7786 		GnmFilter *f_old = NULL;
7787 
7788 		if (src == NULL)
7789 			return TRUE;
7790 
7791 		f_old = gnm_sheet_filter_intersect_rows
7792 			(sv->sheet, src->start.row, src->end.row);
7793 
7794 		if (f_old != NULL) {
7795 			GnmRange *r = gnm_sheet_filter_can_be_extended
7796 				(sv->sheet, f_old, src);
7797 			if (r == NULL) {
7798 				char *error;
7799 				name = undo_range_name (sv->sheet, &(f_old->r));
7800 				error = g_strdup_printf
7801 					(_("Auto Filter blocked by %s"),
7802 					 name);
7803 				g_free(name);
7804 				go_cmd_context_error_invalid
7805 					(GO_CMD_CONTEXT (wbc),
7806 					 _("AutoFilter"), error);
7807 				g_free (error);
7808 				return TRUE;
7809 			}
7810 			/* extending existing filter. */
7811 			undo = go_undo_binary_new
7812 				(gnm_filter_ref (f_old), sv->sheet,
7813 				 (GOUndoBinaryFunc) gnm_filter_attach,
7814 				 (GFreeFunc) gnm_filter_unref,
7815 				 NULL);
7816 			redo = go_undo_unary_new
7817 				(gnm_filter_ref (f_old),
7818 				 (GOUndoUnaryFunc) gnm_filter_remove,
7819 				 (GFreeFunc) gnm_filter_unref);
7820 			gnm_filter_remove (f_old);
7821 			region = *r;
7822 			g_free (r);
7823 		} else {
7824 			/* if only one row is selected
7825 			 * assume that the user wants to
7826 			 * filter the region below this row. */
7827 			region = *src;
7828 			if (src->start.row == src->end.row)
7829 				gnm_sheet_guess_region  (sv->sheet, &region);
7830 			if (region.start.row == region.end.row) {
7831 				go_cmd_context_error_invalid
7832 					(GO_CMD_CONTEXT (wbc),
7833 					 _("AutoFilter"),
7834 					 _("Requires more than 1 row"));
7835 				return TRUE;
7836 			}
7837 		}
7838 		f = gnm_filter_new (sv->sheet, &region, FALSE);
7839 		if (f == NULL) {
7840 			go_cmd_context_error_invalid
7841 				(GO_CMD_CONTEXT (wbc),
7842 				 _("AutoFilter"),
7843 				 _("Unable to create Autofilter"));
7844 			if (f_old)
7845 				gnm_filter_attach (f_old, sv->sheet);
7846 			return TRUE;
7847 		}
7848 
7849 		if (f_old)
7850 			gnm_filter_attach (f_old, sv->sheet);
7851 
7852 		redo = go_undo_combine (go_undo_binary_new
7853 					(gnm_filter_ref (f), sv->sheet,
7854 					 (GOUndoBinaryFunc) gnm_filter_attach,
7855 					 (GFreeFunc) gnm_filter_unref,
7856 					 NULL), redo);
7857 		undo = go_undo_combine (undo,
7858 					go_undo_unary_new
7859 					(f,
7860 					 (GOUndoUnaryFunc) gnm_filter_remove,
7861 					 (GFreeFunc) gnm_filter_unref));
7862 
7863 		name = undo_range_name (sv->sheet, &(f->r));
7864 		descr = g_strdup_printf
7865 			((f_old == NULL) ? _("Add Autofilter to %s")
7866 			 : _("Extend Autofilter to %s"),
7867 			 name);
7868 	} else {
7869 		undo = go_undo_binary_new
7870 			(gnm_filter_ref (f), sv->sheet,
7871 			 (GOUndoBinaryFunc) gnm_filter_attach,
7872 			 (GFreeFunc) gnm_filter_unref,
7873 			 NULL);
7874 		redo = go_undo_unary_new
7875 			(gnm_filter_ref (f),
7876 			 (GOUndoUnaryFunc) gnm_filter_remove,
7877 			 (GFreeFunc) gnm_filter_unref);
7878 		name = undo_range_name (sv->sheet, &(f->r));
7879 		descr = g_strdup_printf (_("Remove Autofilter from %s"),
7880 					 name);
7881 	}
7882 	result = cmd_generic (wbc, descr, undo, redo);
7883 	g_free (name);
7884 	g_free (descr);
7885 
7886 	return result;
7887 }
7888 
7889 
7890 /******************************************************************/
7891 
cmd_autofilter_set_condition(WorkbookControl * wbc,GnmFilter * filter,unsigned i,GnmFilterCondition * cond)7892 gboolean cmd_autofilter_set_condition (WorkbookControl *wbc,
7893 				       GnmFilter *filter, unsigned i,
7894 				       GnmFilterCondition *cond)
7895 {
7896 	char *descr = NULL, *name = NULL;
7897 	GOUndo *undo = NULL;
7898 	GOUndo *redo = NULL;
7899 	gboolean result;
7900 
7901 	undo = gnm_undo_filter_set_condition_new (filter, i,
7902 						  NULL, TRUE);
7903 	g_return_val_if_fail (undo != NULL, TRUE);
7904 	redo = gnm_undo_filter_set_condition_new (filter, i,
7905 						 cond, FALSE);
7906 	g_return_val_if_fail (redo != NULL, TRUE);
7907 
7908 	name = undo_range_name (filter->sheet, &(filter->r));
7909 	descr = g_strdup_printf (_("Change filter condition for %s"),
7910 				 name);
7911 
7912 	result = cmd_generic (wbc, descr, undo, redo);
7913 	g_free (name);
7914 	g_free (descr);
7915 
7916 	return result;
7917 }
7918 
7919 
7920 /******************************************************************/
7921 
7922 static void
cmd_page_breaks_set_breaks(Sheet * sheet,GnmPageBreaks const * breaks)7923 cmd_page_breaks_set_breaks (Sheet *sheet,
7924 				       GnmPageBreaks const *breaks)
7925 {
7926 	print_info_set_breaks (sheet->print_info, gnm_page_breaks_dup (breaks));
7927 
7928 	SHEET_FOREACH_CONTROL (sheet, sv, sc, wb_control_menu_state_update (sc_wbc (sc), MS_PAGE_BREAKS););
7929 }
7930 
7931 gboolean
cmd_page_breaks_clear(WorkbookControl * wbc,Sheet * sheet)7932 cmd_page_breaks_clear (WorkbookControl *wbc, Sheet *sheet)
7933 {
7934 	GOUndo *undo = NULL;
7935 	GOUndo *redo = NULL;
7936 
7937 	g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7938 	g_return_val_if_fail (sheet != NULL, TRUE);
7939 
7940 	if (sheet->print_info->page_breaks.v != NULL) {
7941 		redo = go_undo_binary_new
7942 			(sheet,
7943 			 gnm_page_breaks_new (TRUE),
7944 			 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7945 			 NULL,
7946 			 (GFreeFunc) gnm_page_breaks_free);
7947 		undo = go_undo_binary_new
7948 			(sheet,
7949 			 gnm_page_breaks_dup
7950 			 (sheet->print_info->page_breaks.v),
7951 			 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7952 			 NULL,
7953 			 (GFreeFunc) gnm_page_breaks_free);
7954 	}
7955 
7956 	if (sheet->print_info->page_breaks.h != NULL) {
7957 		redo = go_undo_combine
7958 			(redo,
7959 			 go_undo_binary_new
7960 			 (sheet,
7961 			  gnm_page_breaks_new (FALSE),
7962 			  (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7963 			  NULL,
7964 			  (GFreeFunc) gnm_page_breaks_free));
7965 
7966 		undo = go_undo_combine
7967 			(undo,
7968 			 go_undo_binary_new
7969 			 (sheet,
7970 			  gnm_page_breaks_dup
7971 			  (sheet->print_info->page_breaks.h),
7972 			  (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7973 			  NULL,
7974 			  (GFreeFunc) gnm_page_breaks_free));
7975 	}
7976 
7977 	if (undo != NULL)
7978 		return cmd_generic (wbc, _("Clear All Page Breaks"), undo, redo);
7979 	else
7980 		return TRUE;
7981 }
7982 
7983 gboolean
cmd_page_break_toggle(WorkbookControl * wbc,Sheet * sheet,gboolean is_vert)7984 cmd_page_break_toggle (WorkbookControl *wbc, Sheet *sheet, gboolean is_vert)
7985 {
7986 	SheetView const *sv  = wb_control_cur_sheet_view (wbc);
7987 	gint col = sv->edit_pos.col;
7988 	gint row = sv->edit_pos.row;
7989 	int rc = is_vert ? col : row;
7990 	GnmPageBreaks *old, *new, *target;
7991 	GnmPageBreakType type;
7992 	char const *label;
7993 	GOUndo *undo;
7994 	GOUndo *redo;
7995 
7996 	target = is_vert ? sheet->print_info->page_breaks.v
7997 		: sheet->print_info->page_breaks.h;
7998 
7999 	old = (target == NULL) ? gnm_page_breaks_new (is_vert)
8000 		: gnm_page_breaks_dup (target);
8001 	new = gnm_page_breaks_dup (old);
8002 
8003 	if (gnm_page_breaks_get_break (new, rc) != GNM_PAGE_BREAK_MANUAL) {
8004 		type = GNM_PAGE_BREAK_MANUAL;
8005 		label = is_vert ? _("Remove Column Page Break") : _("Remove Row Page Break");
8006 	} else {
8007 		type = GNM_PAGE_BREAK_NONE;
8008 		label = is_vert ? _("Add Column Page Break") : _("Add Row Page Break");
8009 	}
8010 
8011 	gnm_page_breaks_set_break (new, rc, type);
8012 
8013 	redo = go_undo_binary_new
8014 		(sheet, new,
8015 		 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8016 		 NULL,
8017 		 (GFreeFunc) gnm_page_breaks_free);
8018 	undo = go_undo_binary_new
8019 		(sheet, old,
8020 		 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8021 		 NULL,
8022 		 (GFreeFunc) gnm_page_breaks_free);
8023 
8024 	return cmd_generic (wbc, label, undo, redo);
8025 }
8026 
8027 /******************************************************************/
8028