Introspection fixes
[gnumeric.git] / src / commands.c
blobc451487a68df825999e9212300285d42dcdf68a7
2 /*
3 * commands.c: Handlers to undo & redo commands
5 * Copyright (C) 1999-2008 Jody Goldberg (jody@gnome.org)
6 * Copyright (C) 2002-2008 Morten Welinder (terra@gnome.org)
8 * Contributors : Almer S. Tigelaar (almer@gnome.org)
9 * Andreas J. Guelzow (aguelzow@taliesin.ca)
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.
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.
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
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>
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>
82 #include <goffice/goffice.h>
83 #include <gsf/gsf-doc-meta-data.h>
84 #include <string.h>
86 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
90 * There are several distinct stages to wrapping each command.
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.
96 * 2) Copy the boiler plate code into place and implement the descriptor.
98 * 3) Implement the guts of the support functions.
100 * That way undo redo just become applications of the old or the new styles.
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.
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.
114 * FIXME: Filter the list of commands when a sheet is deleted.
116 * TODO : Possibly clear lists on save.
118 * TODO : Reqs for selective undo
120 * Future thoughts
121 * - undoable preference setting ? XL does not have this. Do we want it ?
123 /******************************************************************/
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))
130 GSF_CLASS (GnmCommand, gnm_command, NULL, NULL, G_TYPE_OBJECT)
132 void
133 gnm_command_finalize (GObject *obj)
135 GnmCommand *cmd = GNM_COMMAND (obj);
136 GObjectClass *parent;
138 /* The const was to avoid accidental changes elsewhere */
139 g_free ((gchar *)cmd->cmd_descriptor);
140 cmd->cmd_descriptor = NULL;
142 parent = g_type_class_peek (g_type_parent(G_TYPE_FROM_INSTANCE (obj)));
143 (*parent->finalize) (obj);
146 /******************************************************************/
148 GString *
149 gnm_cmd_trunc_descriptor (GString *src, gboolean *truncated)
151 int max_len = gnm_conf_get_undo_max_descriptor_width ();
152 glong len;
153 char *pos;
155 if (max_len < 5)
156 max_len = 5;
158 while ((pos = strchr(src->str, '\n')) != NULL ||
159 (pos = strchr(src->str, '\r')) != NULL)
160 *pos = ' ';
162 len = g_utf8_strlen (src->str, -1);
164 if (truncated)
165 *truncated = (len > max_len);
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);
173 return src;
178 * cmd_cell_range_is_locked_effective:
179 * @sheet: #Sheet
180 * @range: #GnmRange
181 * @wbc: #WorkbookControl
182 * @cmd_name: the command name.
184 * checks whether the cells are effectively locked
186 * static gboolean cmd_cell_range_is_locked_effective
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)
192 * Returns: %TRUE if there was a problem, %FALSE otherwise.
194 gboolean
195 cmd_cell_range_is_locked_effective (Sheet *sheet, GnmRange *range,
196 WorkbookControl *wbc, char const *cmd_name)
198 int i, j;
199 WorkbookView *wbv = wb_control_view (wbc);
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."),
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;
216 return FALSE;
220 * checks whether the cells are effectively locked
222 * static gboolean cmd_dao_is_locked_effective
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)
228 * Returns: %TRUE if there was a problem, %FALSE otherwise.
230 static gboolean
231 cmd_dao_is_locked_effective (data_analysis_output_t *dao,
232 WorkbookControl *wbc, char const *cmd_name)
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));
242 * cmd_selection_is_locked_effective: (skip)
243 * checks whether the selection is effectively locked
245 * static gboolean cmd_selection_is_locked_effective
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)
250 * Returns: %TRUE if there was a problem, %FALSE otherwise.
252 gboolean
253 cmd_selection_is_locked_effective (Sheet *sheet, GSList *selection,
254 WorkbookControl *wbc, char const *cmd_name)
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;
261 return FALSE;
265 * A helper routine to select a range and make sure the top-left
266 * is visible.
268 static void
269 select_range (Sheet *sheet, const GnmRange *r, WorkbookControl *wbc)
271 SheetView *sv;
273 if (sheet->workbook != wb_control_get_workbook (wbc)) {
275 * We could try to pick a random wbc for the sheet's
276 * workbook. But not right now.
278 return;
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);
289 * A helper routine to select a list of ranges and make sure the top-left
290 * corner of the last is visible.
292 static void
293 select_selection (Sheet *sheet, GSList *selection, WorkbookControl *wbc)
295 SheetView *sv = sheet_get_view (sheet, wb_control_view (wbc));
296 const GnmRange *r0 = NULL;
297 GSList *l;
299 g_return_if_fail (selection != NULL);
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;
308 gnm_sheet_view_make_cell_visible (sv, r0->start.col, r0->start.row, FALSE);
312 * get_menu_label:
313 * with a list of commands.
314 * @cmd_list: The command list to check.
316 * Utility routine to get the descriptor associated
317 * Returns : A static reference to a descriptor. DO NOT free this.
319 static char const *
320 get_menu_label (GSList *cmd_list)
322 if (cmd_list != NULL) {
323 GnmCommand *cmd = GNM_COMMAND (cmd_list->data);
324 return cmd->cmd_descriptor;
327 return NULL;
331 * undo_redo_menu_labels:
332 * @wb: The book whose undo/redo queues we are modifying
334 * Another utility to set the menus correctly.
336 static void
337 undo_redo_menu_labels (Workbook *wb)
339 char const *undo_label = get_menu_label (wb->undo_commands);
340 char const *redo_label = get_menu_label (wb->redo_commands);
342 WORKBOOK_FOREACH_CONTROL (wb, view, control,
343 wb_control_undo_redo_labels (control, undo_label, redo_label);
347 static void
348 update_after_action (Sheet *sheet, WorkbookControl *wbc)
350 gnm_app_recalc ();
352 if (sheet != NULL) {
353 g_return_if_fail (IS_SHEET (sheet));
355 sheet_mark_dirty (sheet);
356 sheet_update (sheet);
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);
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.
375 * Undo the last command executed.
377 void
378 command_undo (WorkbookControl *wbc)
380 GnmCommand *cmd;
381 GnmCommandClass *klass;
382 Workbook *wb = wb_control_get_workbook (wbc);
384 g_return_if_fail (wb != NULL);
385 g_return_if_fail (wb->undo_commands != NULL);
387 cmd = GNM_COMMAND (wb->undo_commands->data);
388 g_return_if_fail (cmd != NULL);
390 klass = CMD_CLASS (cmd);
391 g_return_if_fail (klass != NULL);
393 g_object_ref (cmd);
395 /* TRUE indicates a failure to undo. Leave the command where it is */
396 if (!klass->undo_cmd (cmd, wbc)) {
397 gboolean undo_cleared;
399 update_after_action (cmd->sheet, wbc);
401 if (!cmd->workbook_modified_before_do)
402 go_doc_set_dirty (GO_DOC (wb), FALSE);
405 * A few commands clear the undo queue. For those, we do not
406 * want to stuff the cmd object on the redo queue.
408 undo_cleared = (wb->undo_commands == NULL);
410 if (!undo_cleared) {
411 wb->undo_commands = g_slist_remove (wb->undo_commands, cmd);
412 wb->redo_commands = g_slist_prepend (wb->redo_commands, cmd);
414 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
415 wb_control_undo_redo_pop (control, TRUE);
416 wb_control_undo_redo_push (control, FALSE, cmd->cmd_descriptor, cmd);
418 undo_redo_menu_labels (wb);
419 /* TODO : Should we mark the workbook as clean or pristine too */
423 g_object_unref (cmd);
427 * command_redo:
428 * @wbc: The workbook control which issued the request.
430 * Redo the last command that was undone.
431 * Any user level errors generated by redoing will be reported
432 * here.
434 void
435 command_redo (WorkbookControl *wbc)
437 GnmCommand *cmd;
438 GnmCommandClass *klass;
439 Workbook *wb = wb_control_get_workbook (wbc);
441 g_return_if_fail (wb);
442 g_return_if_fail (wb->redo_commands);
444 cmd = GNM_COMMAND (wb->redo_commands->data);
445 g_return_if_fail (cmd != NULL);
447 klass = CMD_CLASS (cmd);
448 g_return_if_fail (klass != NULL);
450 g_object_ref (cmd);
452 cmd->workbook_modified_before_do =
453 go_doc_is_dirty (wb_control_get_doc (wbc));
455 /* TRUE indicates a failure to redo. Leave the command where it is */
456 if (!klass->redo_cmd (cmd, wbc)) {
457 gboolean redo_cleared;
459 update_after_action (cmd->sheet, wbc);
462 * A few commands clear the undo queue. For those, we do not
463 * want to stuff the cmd object on the redo queue.
465 redo_cleared = (wb->redo_commands == NULL);
467 if (!redo_cleared) {
468 wb->redo_commands = g_slist_remove (wb->redo_commands, cmd);
469 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
471 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
472 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
473 wb_control_undo_redo_pop (control, FALSE);
475 undo_redo_menu_labels (wb);
479 g_object_unref (cmd);
483 * command_repeat:
484 * @wbc: The workbook control which issued the request.
486 * Repeat the last command (if possible)
488 * Any user level errors generated by redoing will be reported
489 * here.
491 void
492 command_repeat (WorkbookControl *wbc)
494 GnmCommand *cmd;
495 GnmCommandClass *klass;
496 Workbook *wb = wb_control_get_workbook (wbc);
498 g_return_if_fail (wb);
499 g_return_if_fail (wb->undo_commands);
501 cmd = GNM_COMMAND (wb->undo_commands->data);
502 g_return_if_fail (cmd != NULL);
504 klass = CMD_CLASS (cmd);
505 g_return_if_fail (klass != NULL);
507 if (klass->repeat_cmd != NULL)
508 (*klass->repeat_cmd) (cmd, wbc);
512 * command_setup_combos:
513 * @wbc:
515 * Initialize the combos to correspond to the current undo/redo state.
517 void
518 command_setup_combos (WorkbookControl *wbc)
520 char const *undo_label = NULL, *redo_label = NULL;
521 GSList *ptr, *tmp;
522 Workbook *wb = wb_control_get_workbook (wbc);
524 g_return_if_fail (wb);
526 wb_control_undo_redo_truncate (wbc, 0, TRUE);
527 tmp = g_slist_reverse (wb->undo_commands);
528 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
529 undo_label = get_menu_label (ptr);
530 wb_control_undo_redo_push (wbc, TRUE, undo_label, ptr->data);
532 if (g_slist_reverse (tmp)) {} /* ignore, list is in undo_commands */
534 wb_control_undo_redo_truncate (wbc, 0, FALSE);
535 tmp = g_slist_reverse (wb->redo_commands);
536 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
537 redo_label = get_menu_label (ptr);
538 wb_control_undo_redo_push (wbc, FALSE, redo_label, ptr->data);
540 if (g_slist_reverse (tmp)) {} /* ignore, list is in redo_commands */
542 /* update the menus too */
543 wb_control_undo_redo_labels (wbc, undo_label, redo_label);
547 * command_list_release:
548 * @cmds: (element-type GObject): the set of commands to free.
550 * command_list_release : utility routine to free the resources associated
551 * with a list of commands.
553 * NOTE : remember to NULL the list when you are done.
555 void
556 command_list_release (GSList *cmd_list)
558 while (cmd_list != NULL) {
559 GObject *cmd = G_OBJECT (cmd_list->data);
561 g_return_if_fail (cmd != NULL);
563 g_object_unref (cmd);
564 cmd_list = g_slist_remove (cmd_list, cmd_list->data);
569 * Each undo item has a certain size. The size of typing a value into
570 * a cell is the unit size. A large autoformat could have a size of
571 * hundreds or even thousands.
573 * We wish to have the same undo behaviour across platforms, so please
574 * don't use sizeof in computing the undo size.
577 #undef DEBUG_TRUNCATE_UNDO
580 * Truncate the undo list if it is too big.
582 * Returns -1 if no truncation was done, or else the number of elements
583 * left.
585 static int
586 truncate_undo_info (Workbook *wb)
588 int size_left;
589 int max_num;
590 int ok_count;
591 GSList *l, *prev;
593 size_left = gnm_conf_get_undo_size ();
594 max_num = gnm_conf_get_undo_maxnum ();
596 #ifdef DEBUG_TRUNCATE_UNDO
597 g_printerr ("Undo sizes:");
598 #endif
600 for (l = wb->undo_commands, prev = NULL, ok_count = 0;
602 prev = l, l = l->next, ok_count++) {
603 int min_leave;
604 GnmCommand *cmd = GNM_COMMAND (l->data);
605 int size = cmd->size;
607 if (size < 1) {
609 * We could g_assert, but that would cause data loss.
610 * Instead, just continue.
612 g_warning ("Faulty undo_size_func, please report.");
613 size = 1;
616 #ifdef DEBUG_TRUNCATE_UNDO
617 g_printerr (" %d", size);
618 #endif
620 /* Keep at least one undo item. */
621 if (ok_count >= max_num || (size > size_left && ok_count >= 1)) {
622 /* Current item is too big; truncate list here. */
623 command_list_release (l);
624 if (prev)
625 prev->next = NULL;
626 else
627 wb->undo_commands = NULL;
628 #ifdef DEBUG_TRUNCATE_UNDO
629 g_printerr ("[trunc]\n");
630 #endif
631 return ok_count;
635 * In order to allow a series of useful small items behind
636 * a big item, leave at least 10% of current item's size.
638 min_leave = size / 10;
639 size_left = MAX (size_left - size, min_leave);
642 #ifdef DEBUG_TRUNCATE_UNDO
643 g_printerr ("\n");
644 #endif
645 return -1;
650 * command_register_undo:
651 * @wbc: The workbook control that issued the command.
652 * @cmd: The new command to add.
654 * An internal utility to tack a new command
655 * onto the undo list.
657 static void
658 command_register_undo (WorkbookControl *wbc, GObject *obj)
660 Workbook *wb;
661 GnmCommand *cmd;
662 int undo_trunc;
664 g_return_if_fail (wbc != NULL);
665 wb = wb_control_get_workbook (wbc);
667 cmd = GNM_COMMAND (obj);
668 g_return_if_fail (cmd != NULL);
670 command_list_release (wb->redo_commands);
671 wb->redo_commands = NULL;
673 g_object_ref (obj); /* keep a ref in case it gets truncated away */
674 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
675 undo_trunc = truncate_undo_info (wb);
677 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
678 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
679 if (undo_trunc >= 0)
680 wb_control_undo_redo_truncate (control, undo_trunc, TRUE);
681 wb_control_undo_redo_truncate (control, 0, FALSE);
683 undo_redo_menu_labels (wb);
684 g_object_unref (obj);
689 * gnm_command_push_undo:
690 * @wbc: The workbook control that issued the command.
691 * @obj: The new command to add.
693 * An internal utility to tack a new command
694 * onto the undo list.
696 * returns : TRUE if there was an error.
698 gboolean
699 gnm_command_push_undo (WorkbookControl *wbc, GObject *obj)
701 gboolean trouble;
702 GnmCommand *cmd;
703 GnmCommandClass *klass;
705 g_return_val_if_fail (wbc != NULL, TRUE);
707 cmd = GNM_COMMAND (obj);
708 cmd->workbook_modified_before_do =
709 go_doc_is_dirty (wb_control_get_doc (wbc));
711 g_return_val_if_fail (cmd != NULL, TRUE);
713 klass = CMD_CLASS (cmd);
714 g_return_val_if_fail (klass != NULL, TRUE);
716 /* TRUE indicates a failure to do the command */
717 trouble = klass->redo_cmd (cmd, wbc);
718 update_after_action (cmd->sheet, wbc);
720 if (!trouble)
721 command_register_undo (wbc, obj);
722 else
723 g_object_unref (obj);
725 return trouble;
729 * command_undo_sheet_delete deletes the sheet without deleting the current cmd.
730 * returns true if is indeed deleted the sheet.
731 * Note: only call this for a sheet of your current workbook from the undo procedure
734 static gboolean
735 command_undo_sheet_delete (Sheet* sheet)
737 Workbook *wb = sheet->workbook;
739 g_return_val_if_fail (IS_SHEET (sheet), FALSE);
741 if (wb->redo_commands != NULL) {
742 command_list_release (wb->redo_commands);
743 wb->redo_commands = NULL;
744 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
745 wb_control_undo_redo_truncate (ctl, 0, FALSE););
746 undo_redo_menu_labels (wb);
749 workbook_sheet_delete (sheet);
751 return (TRUE);
754 /******************************************************************/
756 static GnmValue *
757 cmd_set_text_full_check_texpr (GnmCellIter const *iter, GnmExprTop const *texpr)
759 if (iter->cell == NULL ||
760 !gnm_expr_top_equal (iter->cell->base.texpr, texpr))
761 return VALUE_TERMINATE;
762 return NULL;
765 static GnmValue *
766 cmd_set_text_full_check_text (GnmCellIter const *iter, char *text)
768 char *old_text;
769 gboolean same;
770 gboolean quoted = FALSE;
772 if (gnm_cell_is_blank (iter->cell))
773 return ((text == NULL || text[0] == '\0') ? NULL : VALUE_TERMINATE);
775 if (text == NULL || text[0] == '\0')
776 return VALUE_TERMINATE;
778 old_text = gnm_cell_get_text_for_editing (iter->cell, NULL, &quoted);
779 same = g_strcmp0 (old_text, text) == 0;
781 if (!same && !quoted && iter->cell->value && VALUE_IS_STRING (iter->cell->value)
782 && text[0] == '\'')
783 same = g_strcmp0 (old_text, text + 1) == 0;
785 g_free (old_text);
787 return (same ? NULL : VALUE_TERMINATE);
790 static GnmValue *
791 cmd_set_text_full_check_markup (GnmCellIter const *iter, PangoAttrList *markup)
793 PangoAttrList const *old_markup = NULL;
794 gboolean same_markup;
796 g_return_val_if_fail (iter->cell != NULL, NULL);
798 if (iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
799 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
800 if (fmt && go_format_is_markup (fmt)) {
801 old_markup = go_format_get_markup (fmt);
802 if (go_pango_attr_list_is_empty (old_markup))
803 old_markup = NULL;
807 same_markup = gnm_pango_attr_list_equal (old_markup, markup);
809 return same_markup ? NULL : VALUE_TERMINATE;
813 * cmd_set_text_full
815 * the caller is expected to have ensured:
817 * 1) that no array is being split
818 * 2) that the range is not locked.
820 * Note:
821 * We will free the selection but nothing else.
823 * Returns: %TRUE if there was a problem, %FALSE otherwise.
825 static gboolean
826 cmd_set_text_full (WorkbookControl *wbc, GSList *selection, GnmEvalPos *ep,
827 char const *new_text, PangoAttrList *markup,
828 gboolean autocorrect)
830 GSList *l;
831 char const *expr_txt;
832 GnmExprTop const *texpr = NULL;
833 GOUndo *undo = NULL;
834 GOUndo *redo = NULL;
835 gboolean result, autofit_col = FALSE, same_text_and_not_same_markup = FALSE;
836 char *text = NULL;
837 char *name;
838 Sheet *sheet = ep->sheet;
839 GnmParsePos pp;
840 ColRowIndexList *cri_col_list = NULL, *cri_row_list = NULL;
841 GOFormat const *format = gnm_style_get_format
842 (sheet_style_get (sheet, ep->eval.col, ep->eval.row));
844 g_return_val_if_fail (selection != NULL , TRUE);
846 parse_pos_init_evalpos (&pp, ep);
847 name = undo_range_list_name (sheet, selection);
849 if ((format == NULL) || !go_format_is_text (format)) {
850 expr_txt = gnm_expr_char_start_p (new_text);
851 if (expr_txt != NULL)
852 texpr = gnm_expr_parse_str
853 (expr_txt, &pp, GNM_EXPR_PARSE_DEFAULT,
854 sheet_get_conventions (sheet), NULL);
857 if (texpr != NULL) {
858 GOFormat const *sf;
859 GnmStyle *new_style = NULL;
860 gboolean same_texpr = TRUE;
862 /* We should check whether we are in fact changing anything: */
863 for (l = selection; l != NULL && same_texpr; l = l->next) {
864 GnmRange *r = l->data;
865 GnmValue *val =
866 sheet_foreach_cell_in_range
867 (sheet, CELL_ITER_ALL, r,
868 (CellIterFunc) cmd_set_text_full_check_texpr,
869 (gpointer) texpr);
871 same_texpr = (val != VALUE_TERMINATE);
872 if (val != NULL && same_texpr)
873 value_release (val);
876 if (same_texpr) {
877 gnm_expr_top_unref (texpr);
878 g_free (name);
879 range_fragment_free (selection);
880 return TRUE;
883 text = g_strdup_printf (_("Inserting expression in %s"), name);
885 if (go_format_is_general (format)) {
886 sf = gnm_auto_style_format_suggest (texpr, ep);
887 if (sf != NULL) {
888 new_style = gnm_style_new ();
889 gnm_style_set_format (new_style, sf);
890 go_format_unref (sf);
894 for (l = selection; l != NULL; l = l->next) {
895 GnmSheetRange *sr;
896 undo = go_undo_combine
897 (undo, clipboard_copy_range_undo (sheet, l->data));
898 sr = gnm_sheet_range_new (sheet, l->data);
899 redo = go_undo_combine
900 (redo, sheet_range_set_expr_undo (sr, texpr));
901 if (new_style) {
902 sr = gnm_sheet_range_new (sheet, l->data);
903 redo = go_undo_combine
904 (redo, sheet_apply_style_undo (sr, new_style));
907 if (new_style)
908 gnm_style_unref (new_style);
909 gnm_expr_top_unref (texpr);
910 autofit_col = TRUE;
911 } else {
912 GString *text_str;
913 PangoAttrList *adj_markup = NULL;
914 char *corrected;
915 gboolean same_text = TRUE;
916 gboolean same_markup = TRUE;
918 if (new_text == NULL)
919 corrected = NULL;
920 else if (autocorrect)
921 corrected = autocorrect_tool (new_text);
922 else
923 corrected = g_strdup (new_text);
925 if (corrected && (corrected[0] == '\'') && corrected[1] == '\0') {
926 g_free (corrected);
927 corrected = g_strdup ("");
930 /* We should check whether we are in fact changing anything: */
931 /* We'll handle */
932 for (l = selection; l != NULL && same_text; l = l->next) {
933 GnmRange *r = l->data;
934 GnmValue *val =
935 sheet_foreach_cell_in_range
936 (sheet, CELL_ITER_ALL, r,
937 (CellIterFunc) cmd_set_text_full_check_text,
938 (gpointer) corrected);
940 same_text = (val != VALUE_TERMINATE);
941 if (val != NULL && same_text)
942 value_release (val);
945 if (go_pango_attr_list_is_empty (markup))
946 markup = NULL;
947 if (markup && corrected && corrected[0] == '\'') {
948 markup = adj_markup = pango_attr_list_copy (markup);
949 go_pango_attr_list_erase (adj_markup, 0, 1);
952 if (same_text) {
953 for (l = selection; l != NULL && same_text; l = l->next) {
954 GnmRange *r = l->data;
955 GnmValue *val =
956 sheet_foreach_cell_in_range
957 (sheet, CELL_ITER_IGNORE_BLANK, r,
958 (CellIterFunc) cmd_set_text_full_check_markup,
959 (gpointer) markup);
961 same_markup = (val != VALUE_TERMINATE);
962 if (val != NULL && same_markup)
963 value_release (val);
966 if (same_markup) {
967 g_free (corrected);
968 g_free (name);
969 range_fragment_free (selection);
970 if (adj_markup)
971 pango_attr_list_unref (adj_markup);
972 return TRUE;
975 text = g_strdup_printf (_("Editing style of %s"), name);
976 } else {
977 text_str = gnm_cmd_trunc_descriptor (g_string_new (corrected), NULL);
978 text = g_strdup_printf (_("Typing \"%s\" in %s"), text_str->str, name);
979 g_string_free (text_str, TRUE);
982 for (l = selection; l != NULL; l = l->next) {
983 GnmSheetRange *sr;
984 undo = go_undo_combine
985 (undo, clipboard_copy_range_undo (sheet, l->data));
986 if (corrected) {
987 sr = gnm_sheet_range_new (sheet, l->data);
988 redo = go_undo_combine
989 (redo, sheet_range_set_text_undo
990 (sr, corrected));
992 if (markup) {
993 sr = gnm_sheet_range_new (sheet, l->data);
994 /* Note: order of combination matters!! */
995 redo = go_undo_combine
996 (sheet_range_set_markup_undo (sr, markup), redo);
1000 if (adj_markup)
1001 pango_attr_list_unref (adj_markup);
1002 g_free (corrected);
1004 same_text_and_not_same_markup = (same_text && !same_markup);
1006 g_free (name);
1008 /* We are combining this since we don't want to apply and undo twice.*/
1009 if (same_text_and_not_same_markup || !autofit_col) {
1010 GnmCell *cell = sheet_cell_fetch
1011 (sheet, ep->eval.col, ep->eval.row);
1012 gboolean nvis;
1014 go_undo_undo (redo);
1015 nvis = !VALUE_IS_STRING (cell->value);
1016 if (!autofit_col)
1017 autofit_col = nvis;
1018 if (same_text_and_not_same_markup)
1019 /* We only have to do something if at least one cell */
1020 /* now contains a string, but they contain all the same thing. */
1021 same_text_and_not_same_markup = nvis;
1022 go_undo_undo (undo);
1024 if (same_text_and_not_same_markup) {
1025 /*We had the same text and different markup but we are not entering strings. */
1026 g_object_unref (undo);
1027 g_object_unref (redo);
1028 g_free (text);
1029 range_fragment_free (selection);
1030 return TRUE;
1032 for (l = selection; l != NULL; l = l->next) {
1033 GnmRange *r = l->data;
1034 GnmRange *new_r;
1036 new_r = g_new (GnmRange, 1);
1037 *new_r = *r;
1038 redo = go_undo_combine
1039 (go_undo_binary_new
1040 (sheet, new_r,
1041 (GOUndoBinaryFunc) colrow_autofit_row,
1042 NULL, g_free),
1043 redo);
1044 cri_row_list = colrow_get_index_list
1045 (r->start.row, r->end.row, cri_row_list);
1047 if (autofit_col) {
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_col,
1054 NULL, g_free),
1055 redo);
1056 cri_col_list = colrow_get_index_list
1057 (r->start.col, r->end.col, cri_col_list);
1061 undo = go_undo_combine (undo,
1062 gnm_undo_colrow_restore_state_group_new
1063 (sheet, TRUE,
1064 cri_col_list,
1065 colrow_get_sizes (sheet, TRUE,
1066 cri_col_list, -1)));
1067 undo = go_undo_combine (undo,
1068 gnm_undo_colrow_restore_state_group_new
1069 (sheet, FALSE,
1070 cri_row_list,
1071 colrow_get_sizes (sheet, FALSE,
1072 cri_row_list, -1)));
1074 result = cmd_generic (wbc, text, undo, redo);
1075 g_free (text);
1076 range_fragment_free (selection);
1077 return result;
1081 * cmd_area_set_text
1083 * the caller is expected to have ensured:
1085 * 1) that no array is being split
1086 * 2) that the range is not locked.
1088 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1090 gboolean
1091 cmd_area_set_text (WorkbookControl *wbc, SheetView *sv,
1092 char const *new_text, PangoAttrList *markup)
1094 GnmEvalPos ep;
1095 gboolean result;
1096 GSList *selection = selection_get_ranges (sv, FALSE);
1098 eval_pos_init_editpos (&ep, sv);
1099 result = cmd_set_text_full (wbc, selection, &ep,
1100 new_text, markup, TRUE);
1101 return result;
1104 gboolean
1105 cmd_set_text (WorkbookControl *wbc,
1106 Sheet *sheet, GnmCellPos const *pos,
1107 char const *new_text,
1108 PangoAttrList *markup,
1109 gboolean autocorrect)
1111 GnmCell const *cell;
1112 GnmEvalPos ep;
1113 gboolean result;
1114 GSList *selection;
1115 GnmRange *r;
1117 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1118 g_return_val_if_fail (new_text != NULL, TRUE);
1120 /* Ensure that we are not splitting up an array */
1121 cell = sheet_cell_get (sheet, pos->col, pos->row);
1122 if (gnm_cell_is_nonsingleton_array (cell)) {
1123 gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc),
1124 _("Set Text"), NULL);
1125 return TRUE;
1128 eval_pos_init_pos (&ep, sheet, pos);
1129 r = g_new (GnmRange, 1);
1130 r->start = r->end = *pos;
1131 selection = g_slist_prepend (NULL, r);
1132 result = cmd_set_text_full (wbc, selection, &ep,
1133 new_text, markup, autocorrect);
1134 return result;
1139 * cmd_area_set_array_expr
1141 * the caller is expected to have ensured:
1143 * 1) that no array is being split
1144 * 2) that the selection consists of a single range
1145 * 3) that the range is not locked.
1147 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1149 gboolean
1150 cmd_area_set_array_expr (WorkbookControl *wbc, SheetView *sv,
1151 GnmExprTop const *texpr)
1153 GSList *selection = selection_get_ranges (sv, FALSE);
1154 GOUndo *undo = NULL;
1155 GOUndo *redo = NULL;
1156 gboolean result;
1157 Sheet *sheet = sv_sheet (sv);
1158 char *name;
1159 char *text;
1160 GnmSheetRange *sr;
1161 GnmRange *r;
1163 g_return_val_if_fail (selection != NULL , TRUE);
1164 g_return_val_if_fail (selection->next == NULL , TRUE);
1166 name = undo_range_list_name (sheet, selection);
1167 text = g_strdup_printf (_("Inserting array expression in %s"), name);
1168 g_free (name);
1170 r = selection->data;
1172 undo = clipboard_copy_range_undo (sheet, selection->data);
1174 sr = gnm_sheet_range_new (sheet, r);
1175 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1176 redo = go_undo_combine
1177 (go_undo_binary_new
1178 (sheet, g_memdup (r, sizeof (*r)),
1179 (GOUndoBinaryFunc) colrow_autofit_col,
1180 NULL, g_free),
1181 redo);
1182 redo = go_undo_combine
1183 (go_undo_binary_new
1184 (sheet, g_memdup (r, sizeof (*r)),
1185 (GOUndoBinaryFunc) colrow_autofit_row,
1186 NULL, g_free),
1187 redo);
1189 range_fragment_free (selection);
1190 result = cmd_generic (wbc, text, undo, redo);
1191 g_free (text);
1192 return result;
1196 * cmd_create_data_table
1198 * the caller is expected to have ensured:
1200 * 1) that no array is being split
1201 * 2) that the range is not locked.
1203 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1205 gboolean
1206 cmd_create_data_table (WorkbookControl *wbc, Sheet *sheet, GnmRange const *r,
1207 char const *col_input, char const *row_input)
1209 GOUndo *undo = NULL;
1210 GOUndo *redo = NULL;
1211 gboolean result;
1212 char *name;
1213 char *text;
1214 GnmSheetRange *sr;
1215 GnmParsePos pp;
1216 GnmExprTop const *texpr;
1218 name = undo_range_name (sheet, r);
1219 text = g_strdup_printf (_("Creating a Data Table in %s"), name);
1220 g_free (name);
1222 undo = clipboard_copy_range_undo (sheet, r);
1224 sr = gnm_sheet_range_new (sheet, r);
1225 parse_pos_init (&pp, NULL, sheet, r->start.col, r->start.row);
1226 name = g_strdup_printf ("TABLE(%s,%s)", row_input, col_input);
1227 texpr = gnm_expr_parse_str
1228 (name, &pp, GNM_EXPR_PARSE_DEFAULT,
1229 sheet_get_conventions (sheet), NULL);
1230 g_free (name);
1232 if (texpr == NULL) {
1233 g_object_unref (undo);
1234 g_free (text);
1235 return TRUE;
1238 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1239 gnm_expr_top_unref (texpr);
1241 result = cmd_generic (wbc, text, undo, redo);
1242 g_free (text);
1243 return result;
1246 /******************************************************************/
1248 #define CMD_INS_DEL_COLROW_TYPE (cmd_ins_del_colrow_get_type ())
1249 #define CMD_INS_DEL_COLROW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_INS_DEL_COLROW_TYPE, CmdInsDelColRow))
1251 typedef struct {
1252 GnmCommand cmd;
1254 Sheet *sheet;
1255 gboolean is_insert;
1256 gboolean is_cols;
1257 gboolean is_cut;
1258 int index;
1259 int count;
1260 GnmRange *cutcopied;
1261 SheetView *cut_copy_view;
1263 gboolean (*redo_action) (Sheet *sheet, int col, int count,
1264 GOUndo **pundo, GOCmdContext *cc);
1266 gboolean (*repeat_action) (WorkbookControl *wbc, Sheet *sheet,
1267 int start, int count);
1269 GOUndo *undo;
1270 } CmdInsDelColRow;
1272 static void
1273 cmd_ins_del_colrow_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1275 CmdInsDelColRow const *orig = (CmdInsDelColRow const *) cmd;
1276 SheetView *sv = wb_control_cur_sheet_view (wbc);
1277 Sheet *sheet = sv_sheet (sv);
1278 GnmRange const *r = selection_first_range (sv,
1279 GO_CMD_CONTEXT (wbc), _("Ins/Del Column/Row"));
1280 int start, count;
1282 if (r == NULL)
1283 return;
1285 if (orig->is_cols)
1286 start = r->start.col, count = range_width (r);
1287 else
1288 start = r->start.row, count = range_height (r);
1290 orig->repeat_action (wbc, sheet, start, count);
1293 MAKE_GNM_COMMAND (CmdInsDelColRow, cmd_ins_del_colrow, cmd_ins_del_colrow_repeat)
1295 static gboolean
1296 cmd_ins_del_colrow_undo (GnmCommand *cmd, WorkbookControl *wbc)
1298 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1300 if (me->undo) {
1301 go_undo_undo (me->undo);
1302 g_object_unref (me->undo);
1303 me->undo = NULL;
1306 /* Ins/Del Row/Col re-ants things completely to account
1307 * for the shift of col/rows.
1309 if (me->cutcopied != NULL && me->cut_copy_view != NULL)
1310 gnm_app_clipboard_cut_copy (wbc, me->is_cut, me->cut_copy_view,
1311 me->cutcopied, FALSE);
1313 return FALSE;
1316 static gboolean
1317 cmd_ins_del_colrow_redo (GnmCommand *cmd, WorkbookControl *wbc)
1319 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1320 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
1321 int idx = me->index;
1322 int count = me->count;
1324 if (me->redo_action (me->sheet, idx, count, &me->undo, cc)) {
1325 /* Trouble. */
1326 return TRUE;
1329 /* Ins/Del Row/Col re-ants things completely to account
1330 * for the shift of col/rows. */
1331 if (me->cutcopied != NULL && me->cut_copy_view != NULL) {
1332 if (me->is_cut) {
1333 GnmRange s = *me->cutcopied;
1334 int key = me->is_insert ? count : -count;
1335 int threshold = me->is_insert ? idx : idx + 1;
1338 * Really only applies if the regions that are
1339 * inserted/deleted are above the cut/copied region.
1341 if (me->is_cols) {
1342 if (threshold <= s.start.col) {
1343 s.start.col += key;
1344 s.end.col += key;
1346 } else if (threshold <= s.start.row) {
1347 s.start.row += key;
1348 s.end.row += key;
1351 gnm_app_clipboard_cut_copy (wbc, me->is_cut,
1352 me->cut_copy_view,
1353 &s, FALSE);
1354 } else
1355 gnm_app_clipboard_unant ();
1358 return FALSE;
1361 static void
1362 cmd_ins_del_colrow_finalize (GObject *cmd)
1364 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1366 if (me->undo)
1367 g_object_unref (me->undo);
1369 g_free (me->cutcopied);
1371 gnm_sheet_view_weak_unref (&(me->cut_copy_view));
1373 gnm_command_finalize (cmd);
1376 static gboolean
1377 cmd_ins_del_colrow (WorkbookControl *wbc,
1378 Sheet *sheet,
1379 gboolean is_cols, gboolean is_insert,
1380 char const *descriptor, int index, int count)
1382 CmdInsDelColRow *me;
1383 int first, last;
1384 GnmRange r;
1386 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1387 g_return_val_if_fail (count > 0, TRUE);
1389 me = g_object_new (CMD_INS_DEL_COLROW_TYPE, NULL);
1391 me->sheet = sheet;
1392 me->is_cols = is_cols;
1393 me->is_insert = is_insert;
1394 me->index = index;
1395 me->count = count;
1396 me->redo_action = me->is_insert
1397 ? (me->is_cols ? sheet_insert_cols : sheet_insert_rows)
1398 : (me->is_cols ? sheet_delete_cols : sheet_delete_rows);
1399 me->repeat_action = me->is_insert
1400 ? (me->is_cols ? cmd_insert_cols : cmd_insert_rows)
1401 : (me->is_cols ? cmd_delete_cols : cmd_delete_rows);
1403 /* Range that will get deleted. */
1404 first = me->is_insert
1405 ? colrow_max (is_cols, sheet) - count
1406 : index;
1407 last = first + count - 1;
1408 (is_cols ? range_init_cols : range_init_rows) (&r, sheet, first, last);
1410 /* Note: redo_action checks for array subdivision. */
1412 /* Check for locks */
1413 if (cmd_cell_range_is_locked_effective (sheet, &r, wbc, descriptor)) {
1414 g_object_unref (me);
1415 return TRUE;
1418 /* We store the cut or/copied range if applicable */
1419 if (!gnm_app_clipboard_is_empty () &&
1420 gnm_app_clipboard_area_get () &&
1421 sheet == gnm_app_clipboard_sheet_get ()) {
1422 me->cutcopied = gnm_range_dup (gnm_app_clipboard_area_get ());
1423 me->is_cut = gnm_app_clipboard_is_cut ();
1424 gnm_sheet_view_weak_ref (gnm_app_clipboard_sheet_view_get (),
1425 &(me->cut_copy_view));
1426 } else
1427 me->cutcopied = NULL;
1429 me->cmd.sheet = sheet;
1430 me->cmd.size = count * 10; /* FIXME? */
1431 me->cmd.cmd_descriptor = descriptor;
1433 return gnm_command_push_undo (wbc, G_OBJECT (me));
1436 gboolean
1437 cmd_insert_cols (WorkbookControl *wbc,
1438 Sheet *sheet, int start_col, int count)
1440 char *mesg;
1441 GnmRange r;
1443 range_init_full_sheet (&r, sheet);
1444 r.start.col = r.end.col - count + 1;
1446 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1447 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1448 ngettext ("Inserting %i column before column %s would push data off the sheet. "
1449 "Please enlarge the sheet first.",
1450 "Inserting %i columns before column %s would push data off the sheet. "
1451 "Please enlarge the sheet first.",
1452 count),
1453 count, col_name (start_col));
1454 return TRUE;
1457 mesg = g_strdup_printf
1458 (ngettext ("Inserting %d column before %s",
1459 "Inserting %d columns before %s",
1460 count),
1461 count, col_name (start_col));
1462 return cmd_ins_del_colrow (wbc, sheet, TRUE, TRUE, mesg, start_col, count);
1465 gboolean
1466 cmd_insert_rows (WorkbookControl *wbc,
1467 Sheet *sheet, int start_row, int count)
1469 char *mesg;
1470 GnmRange r;
1472 range_init_full_sheet (&r, sheet);
1473 r.start.row = r.end.row - count + 1;
1475 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1476 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1477 ngettext ("Inserting %i row before row %s would push data off the sheet. "
1478 "Please enlarge the sheet first.",
1479 "Inserting %i rows before row %s would push data off the sheet. "
1480 "Please enlarge the sheet first.",
1481 count),
1482 count, row_name (start_row));
1483 return TRUE;
1486 mesg = g_strdup_printf
1487 (ngettext ("Inserting %d row before %s",
1488 "Inserting %d rows before %s",
1489 count),
1490 count, row_name (start_row));
1491 return cmd_ins_del_colrow (wbc, sheet, FALSE, TRUE, mesg, start_row, count);
1494 gboolean
1495 cmd_delete_cols (WorkbookControl *wbc,
1496 Sheet *sheet, int start_col, int count)
1498 char *mesg = g_strdup_printf ((count > 1)
1499 ? _("Deleting columns %s")
1500 : _("Deleting column %s"),
1501 cols_name (start_col, start_col + count - 1));
1502 return cmd_ins_del_colrow (wbc, sheet, TRUE, FALSE, mesg, start_col, count);
1505 gboolean
1506 cmd_delete_rows (WorkbookControl *wbc,
1507 Sheet *sheet, int start_row, int count)
1509 char *mesg = g_strdup_printf ((count > 1)
1510 ? _("Deleting rows %s")
1511 : _("Deleting row %s"),
1512 rows_name (start_row, start_row + count - 1));
1513 return cmd_ins_del_colrow (wbc, sheet, FALSE, FALSE, mesg, start_row, count);
1516 /******************************************************************/
1518 typedef struct {
1519 GSList *selection;
1520 GnmRange const *r;
1521 } cmd_selection_clear_row_handler_t;
1523 static gboolean
1524 cmd_selection_clear_row_handler (GnmColRowIter const *iter,
1525 cmd_selection_clear_row_handler_t *data)
1527 if ((!iter->cri->in_filter) || iter->cri->visible) {
1528 GnmRange *r = gnm_range_dup (data->r);
1529 r->start.row = r->end.row = iter->pos;
1530 data->selection = g_slist_prepend (data->selection, r);
1532 return FALSE;
1535 gboolean
1536 cmd_selection_clear (WorkbookControl *wbc, int clear_flags)
1538 char *names, *descriptor;
1539 GString *types;
1540 SheetView *sv = wb_control_cur_sheet_view (wbc);
1541 GSList *selection = selection_get_ranges (sv, FALSE /* No intersection */);
1542 Sheet *sheet = sv_sheet (sv);
1543 gboolean result;
1544 int size;
1545 GOUndo *undo = NULL;
1546 GOUndo *redo = NULL;
1547 GSList *ranges;
1549 if ((clear_flags & CLEAR_FILTERED_ONLY) != 0 && sheet->filters != NULL) {
1550 /* We need to modify the selection to only include filtered rows. */
1551 cmd_selection_clear_row_handler_t data;
1552 data.selection = selection;
1553 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1554 GnmFilter *filter;
1555 data.r = ranges->data;
1556 filter = gnm_sheet_filter_intersect_rows
1557 (sheet, data.r->start.row, data.r->end.row);
1558 if (filter) {
1559 sheet_colrow_foreach (sheet, FALSE,
1560 data.r->start.row,
1561 data.r->end.row,
1562 (ColRowHandler) cmd_selection_clear_row_handler,
1563 &data);
1564 g_free (ranges->data);
1565 ranges->data = NULL;
1568 selection = g_slist_remove_all (data.selection, NULL);
1571 /* We should first determine whether we break anything by clearing */
1572 /* Check for array subdivision *//* Check for locked cells */
1573 if (sheet_ranges_split_region (sheet, selection,
1574 GO_CMD_CONTEXT (wbc), _("Clear")) ||
1575 cmd_selection_is_locked_effective (sheet, selection, wbc, _("Clear"))) {
1576 range_fragment_free (selection);
1577 return TRUE;
1581 /* We now need to build the descriptor */
1582 /* Collect clear types for descriptor */
1583 if (clear_flags != (CLEAR_VALUES | CLEAR_FORMATS | CLEAR_COMMENTS)) {
1584 GSList *m, *l = NULL;
1585 types = g_string_new (NULL);
1586 if (clear_flags & CLEAR_VALUES)
1587 l = g_slist_append (l, g_string_new (_("contents")));
1588 if (clear_flags & CLEAR_FORMATS)
1589 l = g_slist_append (l, g_string_new (_("formats")));
1590 if (clear_flags & CLEAR_COMMENTS)
1591 l = g_slist_append (l, g_string_new (_("comments")));
1592 /* Using a list for this may seem overkill, but is really the only
1593 * right way to do this
1595 for (m = l; m != NULL; m = m->next) {
1596 GString *s = m->data;
1598 g_string_append_len (types, s->str, s->len);
1599 g_string_free (s, TRUE);
1601 if (m->next)
1602 g_string_append (types, ", ");
1604 g_slist_free (l);
1605 } else
1606 types = g_string_new (_("all"));
1607 /* The range name string will automatically be truncated, we don't
1608 * need to truncate the "types" list because it will not grow
1609 * indefinitely
1611 names = undo_range_list_name (sheet, selection);
1612 descriptor = g_strdup_printf (_("Clearing %s in %s"), types->str, names);
1613 g_free (names);
1614 g_string_free (types, TRUE);
1615 size = g_slist_length (selection);
1617 clear_flags |= CLEAR_NOCHECKARRAY;
1619 if (clear_flags & (CLEAR_VALUES | CLEAR_FORMATS))
1620 clear_flags |= CLEAR_RECALC_DEPS;
1622 /* We are now ready to build the redo and undo items */
1623 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1624 GnmRange const *r = ranges->data;
1625 GnmSheetRange *sr = gnm_sheet_range_new (sheet, r);
1627 undo = go_undo_combine (undo, clipboard_copy_range_undo (sheet, r));
1628 redo = go_undo_combine
1629 (redo, sheet_clear_region_undo
1630 (sr, clear_flags));
1633 range_fragment_free (selection);
1635 result = cmd_generic_with_size (wbc, descriptor, size, undo, redo);
1636 g_free (descriptor);
1638 return result;
1641 /******************************************************************/
1643 #define CMD_FORMAT_TYPE (cmd_format_get_type ())
1644 #define CMD_FORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FORMAT_TYPE, CmdFormat))
1646 typedef struct {
1647 GnmCellPos pos;
1648 GnmStyleList *styles;
1649 ColRowIndexList *rows;
1650 ColRowStateGroup *old_heights;
1651 } CmdFormatOldStyle;
1653 typedef struct {
1654 GnmCommand cmd;
1655 GSList *selection;
1656 GSList *old_styles;
1657 GnmStyle *new_style;
1658 GnmBorder **borders;
1659 } CmdFormat;
1661 static void
1662 cmd_format_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1664 CmdFormat const *orig = (CmdFormat const *) cmd;
1665 int i;
1667 if (orig->new_style)
1668 gnm_style_ref (orig->new_style);
1669 if (orig->borders)
1670 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1671 gnm_style_border_ref (orig->borders [i]);
1673 cmd_selection_format (wbc, orig->new_style, orig->borders, NULL);
1675 MAKE_GNM_COMMAND (CmdFormat, cmd_format, cmd_format_repeat)
1677 static gboolean
1678 cmd_format_undo (GnmCommand *cmd,
1679 G_GNUC_UNUSED WorkbookControl *wbc)
1681 CmdFormat *me = CMD_FORMAT (cmd);
1683 g_return_val_if_fail (me != NULL, TRUE);
1685 if (me->old_styles) {
1686 GSList *rstyles = g_slist_reverse (g_slist_copy (me->old_styles));
1687 GSList *rsel = g_slist_reverse (g_slist_copy (me->selection));
1688 GSList *l1, *l2;
1690 for (l1 = rstyles, l2 = rsel; l1; l1 = l1->next, l2 = l2->next) {
1691 CmdFormatOldStyle *os = l1->data;
1692 GnmRange const *r = l2->data;
1693 GnmSpanCalcFlags flags = sheet_style_set_list
1694 (me->cmd.sheet,
1695 &os->pos, os->styles, NULL, NULL);
1697 if (os->old_heights) {
1698 colrow_restore_state_group (me->cmd.sheet, FALSE,
1699 os->rows,
1700 os->old_heights);
1701 colrow_state_group_destroy (os->old_heights);
1702 os->old_heights = NULL;
1703 colrow_index_list_destroy (os->rows);
1704 os->rows = NULL;
1707 sheet_range_calc_spans (me->cmd.sheet, r, flags);
1708 sheet_flag_style_update_range (me->cmd.sheet, r);
1711 sheet_redraw_all (me->cmd.sheet, FALSE);
1712 g_slist_free (rstyles);
1713 g_slist_free (rsel);
1716 select_selection (me->cmd.sheet, me->selection, wbc);
1718 return FALSE;
1721 static gboolean
1722 cmd_format_redo (GnmCommand *cmd, WorkbookControl *wbc)
1724 CmdFormat *me = CMD_FORMAT (cmd);
1725 GSList *l1, *l2;
1726 gboolean re_fit_height;
1728 g_return_val_if_fail (me != NULL, TRUE);
1730 /* Check for locked cells */
1731 if (cmd_selection_is_locked_effective (me->cmd.sheet, me->selection,
1732 wbc, _("Changing Format")))
1733 return TRUE;
1735 re_fit_height = me->new_style &&
1736 (GNM_SPANCALC_ROW_HEIGHT & gnm_style_required_spanflags (me->new_style));
1738 for (l1 = me->old_styles, l2 = me->selection; l2; l1 = l1->next, l2 = l2->next) {
1739 CmdFormatOldStyle *os = l1->data;
1740 GnmRange const *r = l2->data;
1742 if (me->borders)
1743 sheet_apply_border (me->cmd.sheet, r, me->borders);
1744 if (me->new_style) {
1745 gnm_style_ref (me->new_style);
1746 sheet_apply_style (me->cmd.sheet, r, me->new_style);
1747 if (re_fit_height)
1748 colrow_autofit (me->cmd.sheet, r, FALSE, FALSE,
1749 TRUE, FALSE,
1750 &os->rows, &os->old_heights);
1753 sheet_flag_style_update_range (me->cmd.sheet, r);
1755 sheet_redraw_all (me->cmd.sheet, FALSE);
1756 sheet_mark_dirty (me->cmd.sheet);
1758 select_selection (me->cmd.sheet, me->selection, wbc);
1760 return FALSE;
1763 static void
1764 cmd_format_finalize (GObject *cmd)
1766 CmdFormat *me = CMD_FORMAT (cmd);
1767 int i;
1769 if (me->new_style)
1770 gnm_style_unref (me->new_style);
1771 me->new_style = NULL;
1773 if (me->borders) {
1774 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1775 gnm_style_border_unref (me->borders [i]);
1776 g_free (me->borders);
1777 me->borders = NULL;
1780 if (me->old_styles != NULL) {
1781 GSList *l;
1783 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
1784 CmdFormatOldStyle *os = l->data;
1786 style_list_free (os->styles);
1787 colrow_index_list_destroy (os->rows);
1788 colrow_state_group_destroy (os->old_heights);
1789 g_free (os);
1791 me->old_styles = NULL;
1794 range_fragment_free (me->selection);
1795 me->selection = NULL;
1797 gnm_command_finalize (cmd);
1801 * cmd_format: (skip)
1802 * @wbc: the workbook control.
1803 * @sheet: the sheet
1804 * @style: (transfer full): style to apply to the selection
1805 * @borders: (nullable) (transfer full): borders to apply to the selection
1806 * @opt_translated_name: An optional name to use in place of 'Format Cells'
1808 * If borders is non-%NULL, then the GnmBorder references are passed,
1809 * the GnmStyle reference is also passed.
1811 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1813 gboolean
1814 cmd_selection_format (WorkbookControl *wbc,
1815 GnmStyle *style, GnmBorder **borders,
1816 char const *opt_translated_name)
1818 CmdFormat *me;
1819 GSList *l;
1820 SheetView *sv = wb_control_cur_sheet_view (wbc);
1822 me = g_object_new (CMD_FORMAT_TYPE, NULL);
1824 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
1825 me->new_style = style;
1827 me->cmd.sheet = sv_sheet (sv);
1828 me->cmd.size = 1; /* Updated below. */
1830 me->old_styles = NULL;
1831 for (l = me->selection; l; l = l->next) {
1832 GnmRange const *sel_r = l->data;
1833 GnmRange range = *sel_r;
1834 CmdFormatOldStyle *os;
1836 /* Store the containing range to handle borders */
1837 if (borders != NULL) {
1838 if (range.start.col > 0) range.start.col--;
1839 if (range.start.row > 0) range.start.row--;
1840 if (range.end.col < gnm_sheet_get_last_col (me->cmd.sheet)) range.end.col++;
1841 if (range.end.row < gnm_sheet_get_last_row (me->cmd.sheet)) range.end.row++;
1844 os = g_new (CmdFormatOldStyle, 1);
1846 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
1847 os->pos = range.start;
1848 os->rows = NULL;
1849 os->old_heights = NULL;
1851 me->cmd.size += g_slist_length (os->styles);
1852 me->old_styles = g_slist_append (me->old_styles, os);
1855 if (borders) {
1856 int i;
1858 me->borders = g_new (GnmBorder *, GNM_STYLE_BORDER_EDGE_MAX);
1859 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1860 me->borders [i] = borders [i];
1861 } else
1862 me->borders = NULL;
1864 if (opt_translated_name == NULL) {
1865 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
1867 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing format of %s"), names);
1868 g_free (names);
1869 } else
1870 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
1872 return gnm_command_push_undo (wbc, G_OBJECT (me));
1875 /******************************************************************/
1877 static gboolean
1878 cmd_selection_format_toggle_font_style_filter (PangoAttribute *attribute, PangoAttrType *pt)
1880 return ((attribute->klass->type == *pt) ||
1881 ((PANGO_ATTR_RISE == *pt) && (attribute->klass->type == PANGO_ATTR_SCALE)));
1884 typedef struct {
1885 GOUndo *undo;
1886 PangoAttrType pt;
1887 } csftfs;
1889 static GnmValue *
1890 cmd_selection_format_toggle_font_style_cb (GnmCellIter const *iter, csftfs *closure)
1892 if (iter->cell && iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
1893 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
1894 if (fmt && go_format_is_markup (fmt)) {
1895 const PangoAttrList *old_markup =
1896 go_format_get_markup (fmt);
1897 PangoAttrList *new_markup = pango_attr_list_copy ((PangoAttrList *)old_markup);
1898 PangoAttrList *other = pango_attr_list_filter
1899 (new_markup,
1900 (PangoAttrFilterFunc) cmd_selection_format_toggle_font_style_filter,
1901 &closure->pt);
1902 if (other != NULL) {
1903 GnmSheetRange *sr;
1904 GnmRange r;
1905 range_init_cellpos (&r, &iter->pp.eval);
1906 sr = gnm_sheet_range_new (iter->pp.sheet, &r);
1907 closure->undo = go_undo_combine (closure->undo,
1908 sheet_range_set_markup_undo (sr, new_markup));
1910 pango_attr_list_unref (new_markup);
1911 pango_attr_list_unref (other);
1914 return NULL;
1917 gboolean
1918 cmd_selection_format_toggle_font_style (WorkbookControl *wbc,
1919 GnmStyle *style, GnmStyleElement t)
1921 SheetView *sv = wb_control_cur_sheet_view (wbc);
1922 Sheet *sheet = sv->sheet;
1923 GSList *selection = selection_get_ranges (sv, FALSE), *l;
1924 gboolean result;
1925 char *text, *name;
1926 GOUndo *undo = NULL;
1927 GOUndo *redo = NULL;
1928 PangoAttrType pt;
1931 switch (t) {
1932 case MSTYLE_FONT_BOLD:
1933 pt = PANGO_ATTR_WEIGHT;
1934 break;
1935 case MSTYLE_FONT_ITALIC:
1936 pt = PANGO_ATTR_STYLE;
1937 break;
1938 case MSTYLE_FONT_UNDERLINE:
1939 pt = PANGO_ATTR_UNDERLINE;
1940 break;
1941 case MSTYLE_FONT_STRIKETHROUGH:
1942 pt = PANGO_ATTR_STRIKETHROUGH;
1943 break;
1944 case MSTYLE_FONT_SCRIPT:
1945 pt = PANGO_ATTR_RISE; /* and PANGO_ATTR_SCALE (see ) */
1946 break;
1947 default:
1948 pt = PANGO_ATTR_INVALID;
1949 break;
1953 name = undo_range_list_name (sheet, selection);
1954 text = g_strdup_printf (_("Setting Font Style of %s"), name);
1955 g_free (name);
1957 for (l = selection; l != NULL; l = l->next) {
1958 GnmSheetRange *sr;
1959 undo = go_undo_combine
1960 (undo, clipboard_copy_range_undo (sheet, l->data));
1961 sr = gnm_sheet_range_new (sheet, l->data);
1962 redo = go_undo_combine
1963 (redo, sheet_apply_style_undo (sr, style));
1964 if (pt != PANGO_ATTR_INVALID) {
1965 csftfs closure;
1966 closure.undo = NULL;
1967 closure.pt = pt;
1968 sheet_foreach_cell_in_range
1969 (sheet, CELL_ITER_IGNORE_BLANK, &sr->range,
1970 (CellIterFunc) cmd_selection_format_toggle_font_style_cb,
1971 &closure);
1972 redo = go_undo_combine (redo, closure.undo);
1975 gnm_style_unref (style);
1976 result = cmd_generic (wbc, text, undo, redo);
1977 g_free (text);
1978 range_fragment_free (selection);
1980 return result;
1984 /******************************************************************/
1987 gboolean
1988 cmd_resize_colrow (WorkbookControl *wbc, Sheet *sheet,
1989 gboolean is_cols, ColRowIndexList *selection,
1990 int new_size)
1992 int size = 1;
1993 char *text;
1994 GOUndo *undo = NULL;
1995 GOUndo *redo = NULL;
1996 gboolean is_single, result;
1997 GString *list;
1998 ColRowStateGroup *saved_state;
2000 list = colrow_index_list_to_string (selection, is_cols, &is_single);
2001 gnm_cmd_trunc_descriptor (list, NULL);
2003 if (is_single) {
2004 if (new_size < 0)
2005 text = is_cols
2006 ? g_strdup_printf (_("Autofitting column %s"), list->str)
2007 : g_strdup_printf (_("Autofitting row %s"), list->str);
2008 else if (new_size > 0)
2009 text = is_cols
2010 ? g_strdup_printf (ngettext ("Setting width of column %s to %d pixel",
2011 "Setting width of column %s to %d pixels",
2012 new_size),
2013 list->str, new_size)
2014 : g_strdup_printf (ngettext ("Setting height of row %s to %d pixel",
2015 "Setting height of row %s to %d pixels",
2016 new_size),
2017 list->str, new_size);
2018 else text = is_cols
2019 ? g_strdup_printf (_("Setting width of column %s to default"),
2020 list->str)
2021 : g_strdup_printf (
2022 _("Setting height of row %s to default"), list->str);
2023 } else {
2024 if (new_size < 0)
2025 text = is_cols
2026 ? g_strdup_printf (_("Autofitting columns %s"), list->str)
2027 : g_strdup_printf (_("Autofitting rows %s"), list->str);
2028 else if (new_size > 0)
2029 text = is_cols
2030 ? g_strdup_printf (ngettext("Setting width of columns %s to %d pixel",
2031 "Setting width of columns %s to %d pixels",
2032 new_size),
2033 list->str, new_size)
2034 : g_strdup_printf (ngettext("Setting height of rows %s to %d pixel",
2035 "Setting height of rows %s to %d pixels",
2036 new_size),
2037 list->str, new_size);
2038 else text = is_cols
2039 ? g_strdup_printf (
2040 _("Setting width of columns %s to default"), list->str)
2041 : g_strdup_printf (
2042 _("Setting height of rows %s to default"), list->str);
2044 g_string_free (list, TRUE);
2046 saved_state = colrow_get_sizes (sheet, is_cols, selection, new_size);
2047 undo = gnm_undo_colrow_restore_state_group_new
2048 (sheet, is_cols, colrow_index_list_copy (selection), saved_state);
2050 redo = gnm_undo_colrow_set_sizes_new (sheet, is_cols, selection, new_size, NULL);
2052 result = cmd_generic_with_size (wbc, text, size, undo, redo);
2053 g_free (text);
2055 return result;
2058 gboolean
2059 cmd_autofit_selection (WorkbookControl *wbc, SheetView *sv, Sheet *sheet, gboolean fit_width,
2060 ColRowIndexList *selectionlist)
2062 GOUndo *undo = NULL;
2063 GOUndo *redo = NULL;
2064 gboolean result;
2065 ColRowStateGroup *saved_state;
2066 GSList *l, *selection = selection_get_ranges (sv, TRUE);
2067 gchar *names = undo_range_list_name (sheet, selection);
2068 gchar const *format = fit_width ?
2069 N_("Autofitting width of %s") : N_("Autofitting height of %s");
2070 gchar *text = g_strdup_printf (_(format), names);
2072 g_free (names);
2074 saved_state = colrow_get_sizes (sheet, fit_width, selectionlist, -1);
2075 undo = gnm_undo_colrow_restore_state_group_new
2076 (sheet, fit_width, colrow_index_list_copy (selectionlist), saved_state);
2078 for (l = selection; l != NULL; l = l->next)
2079 redo = go_undo_combine
2080 (redo, gnm_undo_colrow_set_sizes_new
2081 (sheet, fit_width, NULL, -1, l->data));
2083 result = cmd_generic (wbc, text, undo, redo);
2084 g_free (text);
2085 return result;
2089 /******************************************************************/
2091 #define CMD_SORT_TYPE (cmd_sort_get_type ())
2092 #define CMD_SORT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SORT_TYPE, CmdSort))
2094 typedef struct {
2095 GnmCommand cmd;
2097 GnmSortData *data;
2098 int *perm;
2099 GnmCellRegion *old_contents;
2100 } CmdSort;
2102 MAKE_GNM_COMMAND (CmdSort, cmd_sort, NULL)
2104 static void
2105 cmd_sort_finalize (GObject *cmd)
2107 CmdSort *me = CMD_SORT (cmd);
2109 if (me->data != NULL)
2110 gnm_sort_data_destroy (me->data);
2111 g_free (me->perm);
2112 if (me->old_contents != NULL)
2113 cellregion_unref (me->old_contents);
2115 gnm_command_finalize (cmd);
2118 static gboolean
2119 cmd_sort_undo (GnmCommand *cmd, WorkbookControl *wbc)
2121 CmdSort *me = CMD_SORT (cmd);
2122 GnmSortData *data = me->data;
2123 GnmPasteTarget pt;
2125 paste_target_init (&pt, data->sheet, data->range,
2126 PASTE_CONTENTS | PASTE_FORMATS |
2127 (data->retain_formats ? PASTE_FORMATS : 0));
2128 clipboard_paste_region (me->old_contents,
2129 &pt,
2130 GO_CMD_CONTEXT (wbc));
2132 return FALSE;
2135 static gboolean
2136 cmd_sort_redo (GnmCommand *cmd, WorkbookControl *wbc)
2138 CmdSort *me = CMD_SORT (cmd);
2139 GnmSortData *data = me->data;
2141 /* Check for locks */
2142 if (cmd_cell_range_is_locked_effective
2143 (data->sheet, data->range, wbc, _("Sorting")))
2144 return TRUE;
2146 if (me->perm)
2147 gnm_sort_position (data, me->perm, GO_CMD_CONTEXT (wbc));
2148 else {
2149 me->old_contents =
2150 clipboard_copy_range (data->sheet, data->range);
2151 me->cmd.size = cellregion_cmd_size (me->old_contents);
2152 me->perm = gnm_sort_contents (data, GO_CMD_CONTEXT (wbc));
2155 return FALSE;
2158 gboolean
2159 cmd_sort (WorkbookControl *wbc, GnmSortData *data)
2161 CmdSort *me;
2162 char *desc;
2164 g_return_val_if_fail (data != NULL, TRUE);
2166 desc = g_strdup_printf (_("Sorting %s"), range_as_string (data->range));
2167 if (sheet_range_contains_merges_or_arrays (data->sheet, data->range, GO_CMD_CONTEXT (wbc), desc, TRUE, TRUE)) {
2168 gnm_sort_data_destroy (data);
2169 g_free (desc);
2170 return TRUE;
2173 me = g_object_new (CMD_SORT_TYPE, NULL);
2175 me->data = data;
2176 me->perm = NULL;
2177 me->cmd.sheet = data->sheet;
2178 me->cmd.size = 1; /* Changed in initial redo. */
2179 me->cmd.cmd_descriptor = desc;
2181 return gnm_command_push_undo (wbc, G_OBJECT (me));
2184 /******************************************************************/
2186 #define CMD_COLROW_HIDE_TYPE (cmd_colrow_hide_get_type ())
2187 #define CMD_COLROW_HIDE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_HIDE_TYPE, CmdColRowHide))
2189 typedef struct {
2190 GnmCommand cmd;
2192 gboolean is_cols;
2193 ColRowVisList *hide, *show;
2194 } CmdColRowHide;
2196 static void
2197 cmd_colrow_hide_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2199 CmdColRowHide const *orig = (CmdColRowHide const *) cmd;
2200 cmd_selection_colrow_hide (wbc, orig->is_cols, orig->show != NULL);
2202 MAKE_GNM_COMMAND (CmdColRowHide, cmd_colrow_hide, cmd_colrow_hide_repeat)
2205 * cmd_colrow_hide_correct_selection:
2207 * Try to ensure that the selection/cursor is set to a visible row/col
2209 * Added to fix bug 38179
2210 * Removed because the result is irritating and the bug is actually XL
2211 * compatibile
2213 static void
2214 cmd_colrow_hide_correct_selection (G_GNUC_UNUSED CmdColRowHide *me, G_GNUC_UNUSED WorkbookControl *wbc)
2216 #if 0
2217 int x, y, index;
2218 SheetView *sv = sheet_get_view (me->cmd.sheet,
2219 wb_control_view (wbc));
2221 index = colrow_find_adjacent_visible (me->cmd.sheet, me->is_cols,
2222 me->is_cols ? sv->edit_pos.col : sv->edit_pos.row,
2223 TRUE);
2225 x = me->is_cols ? sv->edit_pos.row : index;
2226 y = me->is_cols ? index : sv->edit_pos.col;
2228 if (index >= 0) {
2229 sv_selection_reset (sv);
2230 if (me->is_cols)
2231 sv_selection_add_full (sv, y, x, y, 0,
2232 y, gnm_sheet_get_last_row (sheet),
2233 GNM_SELECTION_MODE_ADD);
2234 else
2235 sv_selection_add_full (sv, y, x, 0, x,
2236 gnm_sheet_get_last_col (sheet), x,
2237 GNM_SELECTION_MODE_ADD);
2239 #endif
2242 static gboolean
2243 cmd_colrow_hide_undo (GnmCommand *cmd, WorkbookControl *wbc)
2245 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2247 g_return_val_if_fail (me != NULL, TRUE);
2249 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2250 TRUE, me->hide);
2251 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2252 FALSE, me->show);
2254 if (me->show != NULL)
2255 cmd_colrow_hide_correct_selection (me, wbc);
2257 return FALSE;
2260 static gboolean
2261 cmd_colrow_hide_redo (GnmCommand *cmd, WorkbookControl *wbc)
2263 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2265 g_return_val_if_fail (me != NULL, TRUE);
2267 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2268 FALSE, me->hide);
2269 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2270 TRUE, me->show);
2272 if (me->hide != NULL)
2273 cmd_colrow_hide_correct_selection (me, wbc);
2275 return FALSE;
2278 static void
2279 cmd_colrow_hide_finalize (GObject *cmd)
2281 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2282 colrow_vis_list_destroy (me->hide);
2283 me->hide = NULL;
2284 colrow_vis_list_destroy (me->show);
2285 me->show = NULL;
2286 gnm_command_finalize (cmd);
2289 gboolean
2290 cmd_selection_colrow_hide (WorkbookControl *wbc,
2291 gboolean is_cols, gboolean visible)
2293 CmdColRowHide *me;
2294 SheetView *sv = wb_control_cur_sheet_view (wbc);
2295 int n;
2296 Sheet *sheet;
2297 GSList *show = NULL, *hide = NULL;
2299 if (visible)
2300 show = colrow_get_visibility_toggle (sv, is_cols, TRUE);
2301 else
2302 hide = colrow_get_visibility_toggle (sv, is_cols, FALSE);
2303 n = colrow_vis_list_length (hide) + colrow_vis_list_length (show);
2304 sheet = sv_sheet (sv);
2306 if (!visible) {
2307 /* If these are the last colrows to hide, check with the user */
2308 int count = 0;
2309 if (is_cols) {
2310 int i, max = gnm_sheet_get_max_cols (sheet);
2311 ColRowInfo *ci;
2312 for (i = 0 ; i < max ; i++)
2313 if (NULL ==
2314 (ci = sheet_col_get (sheet, i)) ||
2315 (ci->visible))
2316 count++;
2317 } else {
2318 int i, max = gnm_sheet_get_max_rows (sheet);
2319 ColRowInfo *ci;
2320 for (i = 0 ; i < max ; i++)
2321 if (NULL ==
2322 (ci = sheet_row_get (sheet, i)) ||
2323 (ci->visible))
2324 count++;
2326 if (count <= n) {
2327 gchar const *text = is_cols ?
2328 _("Are you sure that you want to hide all columns? "
2329 "If you do so you can unhide them with the "
2330 "'Format\342\206\222Column\342\206\222Unhide' "
2331 "menu item.") :
2332 _("Are you sure that you want to hide all rows? "
2333 "If you do so you can unhide them with the "
2334 "'Format\342\206\222Row\342\206\222Unhide' "
2335 "menu item.");
2336 if (!go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)),
2337 FALSE, "%s", text)) {
2338 colrow_vis_list_destroy (show);
2339 colrow_vis_list_destroy (hide);
2340 return TRUE;
2345 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2346 me->show = show;
2347 me->hide = hide;
2348 me->is_cols = is_cols;
2349 me->cmd.sheet = sheet;
2350 me->cmd.size = 1 + g_slist_length (hide) + g_slist_length (show);
2351 me->cmd.cmd_descriptor = g_strdup (is_cols
2352 ? (visible ? _("Unhide columns") : _("Hide columns"))
2353 : (visible ? _("Unhide rows") : _("Hide rows")));
2355 return gnm_command_push_undo (wbc, G_OBJECT (me));
2358 gboolean
2359 cmd_selection_outline_change (WorkbookControl *wbc,
2360 gboolean is_cols, int index, int depth)
2362 CmdColRowHide *me;
2363 ColRowInfo const *cri;
2364 int first = -1, last = -1;
2365 gboolean visible = FALSE;
2366 int d;
2367 Sheet *sheet = wb_control_cur_sheet (wbc);
2368 SheetView *sv = wb_control_cur_sheet_view (wbc);
2370 cri = sheet_colrow_get_info (sheet, index, is_cols);
2372 d = cri->outline_level;
2373 if (depth > d)
2374 depth = d;
2376 /* Nodes only collapse when selected directly, selecting at a lower
2377 * level is a standard toggle. */
2378 if (depth == d) {
2379 if ((is_cols ? sheet->outline_symbols_right : sheet->outline_symbols_below)) {
2380 if (index > 0) {
2381 ColRowInfo const *prev =
2382 sheet_colrow_get (sheet, index-1, is_cols);
2384 if (prev != NULL && prev->outline_level > d) {
2385 visible = (depth == d && cri->is_collapsed);
2386 last = index - 1;
2387 first = colrow_find_outline_bound (sheet, is_cols,
2388 last, d+1, FALSE);
2391 } else if (index+1 < colrow_max (is_cols, sheet)) {
2392 ColRowInfo const *next =
2393 sheet_colrow_get (sheet, index+1, is_cols);
2395 if (next != NULL && next->outline_level > d) {
2396 visible = (depth == d && cri->is_collapsed);
2397 first = index + 1;
2398 last = colrow_find_outline_bound (sheet, is_cols,
2399 first, d+1, TRUE);
2404 /* If nothing done yet do a simple collapse */
2405 if (first < 0 && cri->outline_level > 0) {
2406 if (depth < d)
2407 ++depth;
2408 first = colrow_find_outline_bound (sheet, is_cols, index, depth, FALSE);
2409 last = colrow_find_outline_bound (sheet, is_cols, index, depth, TRUE);
2410 visible = FALSE;
2412 if (first == last && depth > cri->outline_level)
2413 return TRUE;
2416 if (first < 0 || last < 0)
2417 return TRUE;
2419 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2421 me->is_cols = is_cols;
2422 me->hide = me->show = NULL;
2423 if (visible)
2424 me->show = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2425 TRUE, first, last);
2426 else
2427 me->hide = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2428 FALSE, first, last);
2430 me->cmd.sheet = sv_sheet (sv);
2431 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2432 me->cmd.cmd_descriptor = g_strdup (is_cols
2433 ? (visible ? _("Expand columns") : _("Collapse columns"))
2434 : (visible ? _("Expand rows") : _("Collapse rows")));
2436 return gnm_command_push_undo (wbc, G_OBJECT (me));
2439 gboolean
2440 cmd_global_outline_change (WorkbookControl *wbc, gboolean is_cols, int depth)
2442 CmdColRowHide *me;
2443 ColRowVisList *hide, *show;
2444 SheetView *sv = wb_control_cur_sheet_view (wbc);
2446 colrow_get_global_outline (sv_sheet (sv), is_cols, depth, &show, &hide);
2448 if (show == NULL && hide == NULL)
2449 return TRUE;
2451 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2452 me->is_cols = is_cols;
2453 me->hide = hide;
2454 me->show = show;
2455 me->cmd.sheet = sv_sheet (sv);
2456 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2457 me->cmd.cmd_descriptor = g_strdup_printf (is_cols
2458 ? _("Show column outline %d") : _("Show row outline %d"), depth);
2460 return gnm_command_push_undo (wbc, G_OBJECT (me));
2463 /******************************************************************/
2465 #define CMD_GROUP_TYPE (cmd_group_get_type ())
2466 #define CMD_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GROUP_TYPE, CmdGroup))
2468 typedef struct {
2469 GnmCommand cmd;
2471 GnmRange range;
2472 gboolean is_cols;
2473 gboolean group;
2474 } CmdGroup;
2476 static void
2477 cmd_group_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2479 CmdGroup const *orig = (CmdGroup const *) cmd;
2480 cmd_selection_group (wbc, orig->is_cols, orig->group);
2482 MAKE_GNM_COMMAND (CmdGroup, cmd_group, cmd_group_repeat)
2484 static gboolean
2485 cmd_group_undo (GnmCommand *cmd,
2486 G_GNUC_UNUSED WorkbookControl *wbc)
2488 CmdGroup const *me = CMD_GROUP (cmd);
2489 sheet_colrow_group_ungroup (me->cmd.sheet,
2490 &me->range, me->is_cols, !me->group);
2491 return FALSE;
2494 static gboolean
2495 cmd_group_redo (GnmCommand *cmd,
2496 G_GNUC_UNUSED WorkbookControl *wbc)
2498 CmdGroup const *me = CMD_GROUP (cmd);
2499 sheet_colrow_group_ungroup (me->cmd.sheet,
2500 &me->range, me->is_cols, me->group);
2501 return FALSE;
2504 static void
2505 cmd_group_finalize (GObject *cmd)
2507 gnm_command_finalize (cmd);
2510 gboolean
2511 cmd_selection_group (WorkbookControl *wbc,
2512 gboolean is_cols, gboolean group)
2514 CmdGroup *me;
2515 SheetView *sv;
2516 GnmRange r;
2518 g_return_val_if_fail (wbc != NULL, TRUE);
2520 sv = wb_control_cur_sheet_view (wbc);
2521 r = *selection_first_range (sv, NULL, NULL);
2523 /* Check if this really is possible and display an error if it's not */
2524 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2525 if (group) {
2526 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2527 ? _("Those columns are already grouped")
2528 : _("Those rows are already grouped"));
2529 return TRUE;
2532 /* see if the user selected the col/row with the marker too */
2533 if (is_cols) {
2534 if (r.start.col != r.end.col) {
2535 if (sv->sheet->outline_symbols_right)
2536 r.end.col--;
2537 else
2538 r.start.col++;
2540 } else {
2541 if (r.start.row != r.end.row) {
2542 if (sv->sheet->outline_symbols_below)
2543 r.end.row--;
2544 else
2545 r.start.row++;
2549 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2550 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2551 ? _("Those columns are not grouped, you can't ungroup them")
2552 : _("Those rows are not grouped, you can't ungroup them"));
2553 return TRUE;
2557 me = g_object_new (CMD_GROUP_TYPE, NULL);
2558 me->is_cols = is_cols;
2559 me->group = group;
2560 me->range = r;
2562 me->cmd.sheet = sv->sheet;
2563 me->cmd.size = 1;
2564 me->cmd.cmd_descriptor = is_cols
2565 ? g_strdup_printf (group ? _("Group columns %s") : _("Ungroup columns %s"),
2566 cols_name (me->range.start.col, me->range.end.col))
2567 : g_strdup_printf (group ? _("Group rows %d:%d") : _("Ungroup rows %d:%d"),
2568 me->range.start.row + 1, me->range.end.row + 1);
2570 return gnm_command_push_undo (wbc, G_OBJECT (me));
2573 /******************************************************************/
2575 #define CMD_PASTE_CUT_TYPE (cmd_paste_cut_get_type ())
2576 #define CMD_PASTE_CUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_CUT_TYPE, CmdPasteCut))
2578 typedef struct {
2579 GnmCommand cmd;
2581 GnmExprRelocateInfo info;
2582 GSList *paste_contents;
2583 GOUndo *reloc_undo;
2584 gboolean move_selection;
2585 ColRowStateList *saved_sizes;
2587 /* handle redo-ing an undo with contents from a deleted sheet */
2588 GnmCellRegion *deleted_sheet_contents;
2589 } CmdPasteCut;
2591 MAKE_GNM_COMMAND (CmdPasteCut, cmd_paste_cut, NULL)
2593 typedef struct {
2594 GnmPasteTarget pt;
2595 GnmCellRegion *contents;
2596 } PasteContent;
2599 * cmd_paste_cut_update:
2601 * Utility routine to update things when we are transfering between sheets and
2602 * workbooks.
2604 static void
2605 cmd_paste_cut_update (GnmExprRelocateInfo const *info,
2606 G_GNUC_UNUSED WorkbookControl *wbc)
2608 Sheet *o = info->origin_sheet;
2609 Sheet *t = info->target_sheet;
2611 /* Dirty and update both sheets */
2612 sheet_mark_dirty (t);
2613 sheet_update (t);
2615 if (IS_SHEET (o) && o != t) {
2616 sheet_mark_dirty (o);
2617 sheet_update (o);
2621 static gboolean
2622 cmd_paste_cut_undo (GnmCommand *cmd, WorkbookControl *wbc)
2624 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2625 GnmExprRelocateInfo reverse;
2627 g_return_val_if_fail (me != NULL, TRUE);
2628 g_return_val_if_fail (me->paste_contents != NULL, TRUE);
2629 g_return_val_if_fail (me->deleted_sheet_contents == NULL, TRUE);
2631 reverse.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
2632 reverse.target_sheet = me->info.origin_sheet;
2633 reverse.origin_sheet = me->info.target_sheet;
2634 reverse.origin = me->info.origin;
2635 range_translate (&reverse.origin,
2636 me->info.origin_sheet, /* FIXME: What sheet? */
2637 me->info.col_offset,
2638 me->info.row_offset);
2639 reverse.col_offset = -me->info.col_offset;
2640 reverse.row_offset = -me->info.row_offset;
2642 /* Move things back being careful NOT to invalidate the src region */
2643 if (IS_SHEET (me->info.origin_sheet))
2644 sheet_move_range (&reverse, NULL, GO_CMD_CONTEXT (wbc));
2645 else
2646 me->deleted_sheet_contents = clipboard_copy_range (
2647 reverse.origin_sheet, &reverse.origin);
2649 /* Restore the original row heights */
2650 colrow_set_states (me->info.target_sheet, FALSE,
2651 reverse.origin.start.row, me->saved_sizes);
2652 colrow_state_list_destroy (me->saved_sizes);
2653 me->saved_sizes = NULL;
2655 if (me->reloc_undo) {
2656 go_undo_undo (me->reloc_undo);
2657 g_object_unref (me->reloc_undo);
2658 me->reloc_undo = NULL;
2661 while (me->paste_contents) {
2662 PasteContent *pc = me->paste_contents->data;
2663 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2665 clipboard_paste_region (pc->contents, &pc->pt, GO_CMD_CONTEXT (wbc));
2666 cellregion_unref (pc->contents);
2667 g_free (pc);
2670 /* Force update of the status area */
2671 sheet_flag_status_update_range (me->info.target_sheet, NULL);
2673 cmd_paste_cut_update (&me->info, wbc);
2675 /* Select the original region */
2676 if (me->move_selection && IS_SHEET (me->info.origin_sheet))
2677 select_range (me->info.origin_sheet,
2678 &me->info.origin,
2679 wbc);
2681 return FALSE;
2684 static gboolean
2685 cmd_paste_cut_redo (GnmCommand *cmd, WorkbookControl *wbc)
2687 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2688 GnmRange tmp;
2690 g_return_val_if_fail (me != NULL, TRUE);
2691 g_return_val_if_fail (me->paste_contents == NULL, TRUE);
2693 tmp = me->info.origin;
2694 range_translate (&tmp, me->info.origin_sheet, /* FIXME: What sheet? */
2695 me->info.col_offset, me->info.row_offset);
2696 range_normalize (&tmp);
2698 g_return_val_if_fail (range_is_sane (&tmp), TRUE);
2700 if (me->info.origin_sheet != me->info.target_sheet ||
2701 !range_overlap (&me->info.origin, &tmp)) {
2702 PasteContent *pc = g_new (PasteContent, 1);
2703 paste_target_init (&pc->pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2704 pc->contents = clipboard_copy_range (me->info.target_sheet, &tmp);
2705 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2706 } else {
2707 /* need to store any portions of the paste target
2708 * that do not overlap with the source.
2710 GSList *ptr, *frag = range_split_ranges (&me->info.origin, &tmp);
2711 for (ptr = frag ; ptr != NULL ; ptr = ptr->next) {
2712 GnmRange *r = ptr->data;
2714 if (!range_overlap (&me->info.origin, r)) {
2715 PasteContent *pc = g_new (PasteContent, 1);
2716 paste_target_init (&pc->pt, me->info.target_sheet, r, PASTE_ALL_SHEET);
2717 pc->contents = clipboard_copy_range (me->info.target_sheet, r);
2718 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2720 g_free (r);
2722 g_slist_free (frag);
2725 /* rare corner case. If the origin sheet has been deleted */
2726 if (!IS_SHEET (me->info.origin_sheet)) {
2727 GnmPasteTarget pt;
2728 paste_target_init (&pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2729 sheet_clear_region (pt.sheet,
2730 tmp.start.col, tmp.start.row, tmp.end.col, tmp.end.row,
2731 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
2732 GO_CMD_CONTEXT (wbc));
2733 clipboard_paste_region (me->deleted_sheet_contents,
2734 &pt, GO_CMD_CONTEXT (wbc));
2735 cellregion_unref (me->deleted_sheet_contents);
2736 me->deleted_sheet_contents = NULL;
2737 } else
2738 sheet_move_range (&me->info, &me->reloc_undo, GO_CMD_CONTEXT (wbc));
2740 cmd_paste_cut_update (&me->info, wbc);
2742 /* Backup row heights and adjust row heights to fit */
2743 me->saved_sizes = colrow_get_states (me->info.target_sheet, FALSE, tmp.start.row, tmp.end.row);
2744 rows_height_update (me->info.target_sheet, &tmp, FALSE);
2746 /* Make sure the destination is selected */
2747 if (me->move_selection)
2748 select_range (me->info.target_sheet, &tmp, wbc);
2750 return FALSE;
2753 static void
2754 cmd_paste_cut_finalize (GObject *cmd)
2756 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2758 if (me->saved_sizes)
2759 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
2760 while (me->paste_contents) {
2761 PasteContent *pc = me->paste_contents->data;
2762 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2763 cellregion_unref (pc->contents);
2764 g_free (pc);
2766 if (me->reloc_undo) {
2767 g_object_unref (me->reloc_undo);
2768 me->reloc_undo = NULL;
2770 if (me->deleted_sheet_contents) {
2771 cellregion_unref (me->deleted_sheet_contents);
2772 me->deleted_sheet_contents = NULL;
2775 gnm_command_finalize (cmd);
2778 gboolean
2779 cmd_paste_cut (WorkbookControl *wbc, GnmExprRelocateInfo const *info,
2780 gboolean move_selection, char *descriptor)
2782 CmdPasteCut *me;
2783 GnmRange r;
2784 char *where;
2786 g_return_val_if_fail (info != NULL, TRUE);
2788 /* This is vacuous */
2789 if (info->origin_sheet == info->target_sheet &&
2790 info->col_offset == 0 && info->row_offset == 0)
2791 return TRUE;
2793 /* FIXME: Do we want to show the destination range as well ? */
2794 where = undo_range_name (info->origin_sheet, &info->origin);
2795 if (descriptor == NULL)
2796 descriptor = g_strdup_printf (_("Moving %s"), where);
2797 g_free (where);
2799 g_return_val_if_fail (info != NULL, TRUE);
2801 r = info->origin;
2802 if (range_translate (&r, info->target_sheet,
2803 info->col_offset, info->row_offset)) {
2805 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), descriptor,
2806 _("is beyond sheet boundaries"));
2807 g_free (descriptor);
2808 return TRUE;
2811 /* Check array subdivision & merged regions */
2812 if (sheet_range_splits_region (info->target_sheet, &r,
2813 (info->origin_sheet == info->target_sheet)
2814 ? &info->origin : NULL, GO_CMD_CONTEXT (wbc), descriptor)) {
2815 g_free (descriptor);
2816 return TRUE;
2819 me = g_object_new (CMD_PASTE_CUT_TYPE, NULL);
2821 me->info = *info;
2822 me->paste_contents = NULL;
2823 me->deleted_sheet_contents = NULL;
2824 me->reloc_undo = NULL;
2825 me->move_selection = move_selection;
2826 me->saved_sizes = NULL;
2828 me->cmd.sheet = NULL; /* we have potentially two different. */
2829 me->cmd.size = 1; /* FIXME? */
2830 me->cmd.cmd_descriptor = descriptor;
2832 /* NOTE : if the destination workbook is different from the source
2833 * workbook should we have undo elements in both menus ?? It seems
2834 * poor form to hit undo in 1 window and effect another...
2836 * Maybe queue it as two different commands, as a clear in one book
2837 * and a paste in the other. This is not symmetric though. What
2838 * happens to the cells in the original sheet that now reference the
2839 * cells in the other? When do they reset to the original?
2841 * Probably when the clear in the original is undone.
2844 return gnm_command_push_undo (wbc, G_OBJECT (me));
2847 /******************************************************************/
2849 static void
2850 warn_if_date_trouble (WorkbookControl *wbc, GnmCellRegion *cr)
2852 Workbook *wb = wb_control_get_workbook (wbc);
2853 const GODateConventions *wb_date_conv = workbook_date_conv (wb);
2855 if (cr->date_conv == NULL)
2856 return;
2857 if (go_date_conv_equal (cr->date_conv, wb_date_conv))
2858 return;
2860 /* We would like to show a warning, but it seems we cannot via a context. */
2862 GError *err;
2863 err = g_error_new (go_error_invalid(), 0,
2864 _("Copying between files with different date conventions.\n"
2865 "It is possible that some dates could be copied\n"
2866 "incorrectly."));
2867 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
2868 g_error_free (err);
2873 #define CMD_PASTE_COPY_TYPE (cmd_paste_copy_get_type ())
2874 #define CMD_PASTE_COPY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_COPY_TYPE, CmdPasteCopy))
2876 typedef struct {
2877 GnmCommand cmd;
2879 GnmCellRegion *contents;
2880 GSList *pasted_objects,*orig_contents_objects;
2881 GnmPasteTarget dst;
2882 gboolean has_been_through_cycle;
2883 gboolean only_objects;
2884 gboolean single_merge_to_single_merge;
2885 } CmdPasteCopy;
2887 static void
2888 cmd_paste_copy_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2890 CmdPasteCopy const *orig = (CmdPasteCopy const *) cmd;
2891 GnmPasteTarget new_dst;
2892 SheetView *sv = wb_control_cur_sheet_view (wbc);
2893 GnmRange const *r = selection_first_range (sv,
2894 GO_CMD_CONTEXT (wbc), _("Paste Copy"));
2895 GnmCellRegion *newcr;
2897 if (r == NULL)
2898 return;
2900 paste_target_init (&new_dst, sv_sheet (sv), r, orig->dst.paste_flags);
2901 newcr = clipboard_copy_range (orig->dst.sheet, &orig->dst.range);
2902 cmd_paste_copy (wbc, &new_dst, newcr);
2903 cellregion_unref (newcr);
2905 MAKE_GNM_COMMAND (CmdPasteCopy, cmd_paste_copy, cmd_paste_copy_repeat)
2907 static int
2908 by_addr (gconstpointer a, gconstpointer b)
2910 if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b))
2911 return -1;
2912 if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b))
2913 return +1;
2914 return 0;
2918 * get_new_objects:
2919 * @sheet: #Sheet to query
2920 * @old: (element-type SheetObject): list of objects to disregard
2922 * Returns: (transfer full) (element-type SheetObject): A list of new objects
2923 * in sheet since @old was collected.
2925 static GSList *
2926 get_new_objects (Sheet *sheet, GSList *old)
2928 GSList *objs =
2929 g_slist_sort (g_slist_copy_deep (sheet->sheet_objects,
2930 (GCopyFunc)g_object_ref,
2931 NULL),
2932 by_addr);
2933 GSList *p = objs, *last = NULL;
2935 while (old) {
2936 int c = -1;
2937 while (p && (c = by_addr (p->data, old->data)) < 0) {
2938 last = p;
2939 p = p->next;
2942 old = old->next;
2944 if (c == 0) {
2945 GSList *next = p->next;
2946 if (last)
2947 last->next = next;
2948 else
2949 objs = next;
2950 g_slist_free_1 (p);
2951 p = next;
2955 return objs;
2958 static void
2959 cmd_paste_copy_select_obj (SheetObject *so, SheetControlGUI *scg)
2961 scg_object_select (scg, so);
2964 static gboolean
2965 cmd_paste_copy_impl (GnmCommand *cmd, WorkbookControl *wbc,
2966 gboolean is_undo)
2968 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
2969 GnmCellRegion *contents;
2970 GSList *old_objects;
2972 g_return_val_if_fail (me != NULL, TRUE);
2973 g_return_val_if_fail (me->contents != NULL, TRUE);
2975 g_slist_foreach (me->pasted_objects,
2976 (GFunc)sheet_object_clear_sheet,
2977 NULL);
2978 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
2979 me->pasted_objects = NULL;
2980 old_objects = get_new_objects (me->dst.sheet, NULL);
2982 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
2983 if (me->has_been_through_cycle)
2984 me->dst.paste_flags =
2985 PASTE_CONTENTS |
2986 PASTE_COLUMN_WIDTHS | PASTE_ROW_HEIGHTS |
2987 (me->dst.paste_flags & PASTE_ALL_SHEET);
2989 if (clipboard_paste_region (me->contents, &me->dst,
2990 GO_CMD_CONTEXT (wbc))) {
2991 /* There was a problem, avoid leaking */
2992 cellregion_unref (contents);
2993 g_slist_free_full (old_objects, g_object_unref);
2994 return TRUE;
2997 me->pasted_objects = get_new_objects (me->dst.sheet, old_objects);
2998 g_slist_free_full (old_objects, g_object_unref);
3000 if (!is_undo && !me->has_been_through_cycle) {
3001 colrow_autofit (me->dst.sheet, &me->dst.range, FALSE, FALSE,
3002 TRUE, FALSE,
3003 NULL, NULL);
3004 colrow_autofit (me->dst.sheet, &me->dst.range, TRUE, TRUE,
3005 TRUE, FALSE,
3006 NULL, NULL);
3009 if (is_undo) {
3010 // We cannot use the random set of objects at the target
3011 // location. http://bugzilla.gnome.org/show_bug.cgi?id=308300
3012 g_slist_free_full (contents->objects, g_object_unref);
3013 contents->objects = g_slist_copy_deep
3014 (me->orig_contents_objects,
3015 (GCopyFunc)sheet_object_dup, NULL);
3016 } else {
3017 GSList *l;
3018 for (l = contents->objects; l; l = l->next) {
3019 SheetObject *so = l->data;
3020 if (sheet_object_get_sheet (so)) {
3021 g_object_unref (so);
3022 l->data = NULL;
3023 } else {
3024 // Object got deleted by paste, so keep it for
3025 // undo. See bugzilla 732653
3028 contents->objects =
3029 g_slist_remove_all (contents->objects, NULL);
3032 cellregion_unref (me->contents);
3033 me->contents = contents;
3034 me->has_been_through_cycle = TRUE;
3036 /* Select the newly pasted contents (this queues a redraw) */
3037 if (me->only_objects && GNM_IS_WBC_GTK (wbc)) {
3038 SheetControlGUI *scg =
3039 wbcg_get_nth_scg (WBC_GTK (wbc),
3040 cmd->sheet->index_in_wb);
3041 scg_object_unselect (scg, NULL);
3042 g_slist_foreach (me->pasted_objects,
3043 (GFunc) cmd_paste_copy_select_obj, scg);
3045 select_range (me->dst.sheet, &me->dst.range, wbc);
3047 return FALSE;
3050 static gboolean
3051 cmd_paste_copy_undo (GnmCommand *cmd, WorkbookControl *wbc)
3053 return cmd_paste_copy_impl (cmd, wbc, TRUE);
3056 static gboolean
3057 cmd_paste_copy_redo (GnmCommand *cmd, WorkbookControl *wbc)
3059 return cmd_paste_copy_impl (cmd, wbc, FALSE);
3062 static void
3063 cmd_paste_copy_finalize (GObject *cmd)
3065 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
3067 if (me->contents) {
3068 cellregion_unref (me->contents);
3069 me->contents = NULL;
3071 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
3072 g_slist_free_full (me->orig_contents_objects, (GDestroyNotify)g_object_unref);
3073 gnm_command_finalize (cmd);
3077 * cmd_paste_copy:
3078 * @wbc:
3079 * @pt:
3080 * @cr: (transfer none):
3082 * Returns: %TRUE if there was a problem, %FALSE otherwise.
3084 gboolean
3085 cmd_paste_copy (WorkbookControl *wbc,
3086 GnmPasteTarget const *pt, GnmCellRegion *cr)
3088 CmdPasteCopy *me;
3089 int n_r = 1, n_c = 1;
3090 char *range_name;
3091 GnmRange const *merge_src;
3093 g_return_val_if_fail (pt != NULL, TRUE);
3094 g_return_val_if_fail (IS_SHEET (pt->sheet), TRUE);
3095 g_return_val_if_fail (cr != NULL, TRUE);
3097 cellregion_ref (cr);
3099 me = g_object_new (CMD_PASTE_COPY_TYPE, NULL);
3101 me->cmd.sheet = pt->sheet;
3102 me->cmd.size = 1; /* FIXME? */
3104 range_name = undo_range_name (pt->sheet, &pt->range);
3105 me->cmd.cmd_descriptor = g_strdup_printf (_("Pasting into %s"),
3106 range_name);
3107 g_free (range_name);
3109 me->dst = *pt;
3110 me->contents = cr;
3111 me->has_been_through_cycle = FALSE;
3112 me->only_objects = (cr->cols < 1 || cr->rows < 1);
3113 me->pasted_objects = NULL;
3114 me->orig_contents_objects =
3115 g_slist_copy_deep (cr->objects,
3116 (GCopyFunc)sheet_object_dup, NULL);
3117 me->single_merge_to_single_merge = FALSE;
3119 /* If the input is only objects ignore all this range stuff */
3120 if (!me->only_objects) {
3121 /* see if we need to do any tiling */
3122 GnmRange *r = &me->dst.range;
3123 if (g_slist_length (cr->merged) == 1 &&
3124 (NULL != (merge_src = cr->merged->data)) &&
3125 range_height (merge_src) == cr->rows &&
3126 range_width (merge_src) == cr->cols) {
3127 /* We are copying from a single merge */
3128 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3129 if (merge != NULL && range_equal (r, merge)) {
3130 /* To a single merge */
3131 me->single_merge_to_single_merge = TRUE;
3132 n_c = n_r = 1;
3133 me->dst.paste_flags |= PASTE_DONT_MERGE;
3134 goto copy_ready;
3138 if (pt->paste_flags & PASTE_TRANSPOSE) {
3139 n_c = range_width (r) / cr->rows;
3140 if (n_c < 1) n_c = 1;
3141 r->end.col = r->start.col + n_c * cr->rows - 1;
3143 n_r = range_height (r) / cr->cols;
3144 if (n_r < 1) n_r = 1;
3145 r->end.row = r->start.row + n_r * cr->cols - 1;
3146 } else {
3147 /* Before looking for tiling if we are not transposing,
3148 * allow pasting a full col or row from a single cell */
3149 n_c = range_width (r);
3150 if (n_c == 1 && cr->cols == gnm_sheet_get_max_cols (me->cmd.sheet)) {
3151 r->start.col = 0;
3152 r->end.col = gnm_sheet_get_last_col (me->cmd.sheet);
3153 } else {
3154 n_c /= cr->cols;
3155 if (n_c < 1) n_c = 1;
3156 r->end.col = r->start.col + n_c * cr->cols - 1;
3159 n_r = range_height (r);
3160 if (n_r == 1 && cr->rows == gnm_sheet_get_max_rows (me->cmd.sheet)) {
3161 r->start.row = 0;
3162 r->end.row = gnm_sheet_get_last_row (me->cmd.sheet);
3163 } else {
3164 n_r /= cr->rows;
3165 if (n_r < 1) n_r = 1;
3166 r->end.row = r->start.row + n_r * cr->rows - 1;
3170 if (cr->cols != 1 || cr->rows != 1) {
3171 /* Note: when the source is a single cell, a single target merge is special */
3172 /* see clipboard.c (clipboard_paste_region) */
3173 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3174 if (merge != NULL && range_equal (r, merge)) {
3175 /* destination is a single merge */
3176 /* enlarge it such that the source fits */
3177 if (pt->paste_flags & PASTE_TRANSPOSE) {
3178 if ((r->end.col - r->start.col + 1) < cr->rows)
3179 r->end.col = r->start.col + cr->rows - 1;
3180 if ((r->end.row - r->start.row + 1) < cr->cols)
3181 r->end.row = r->start.row + cr->cols - 1;
3182 } else {
3183 if ((r->end.col - r->start.col + 1) < cr->cols)
3184 r->end.col = r->start.col + cr->cols - 1;
3185 if ((r->end.row - r->start.row + 1) < cr->rows)
3186 r->end.row = r->start.row + cr->rows - 1;
3192 if (n_c * (gnm_float)n_r > 10000.) {
3193 char *number = g_strdup_printf ("%0.0" GNM_FORMAT_f,
3194 (gnm_float)n_c * (gnm_float)n_r);
3195 gboolean result = go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)), FALSE,
3196 _("Do you really want to paste "
3197 "%s copies?"), number);
3198 g_free (number);
3199 if (!result) {
3200 g_object_unref (me);
3201 return TRUE;
3205 copy_ready:
3206 /* Use translate to do a quiet sanity check */
3207 if (range_translate (&me->dst.range, pt->sheet, 0, 0)) {
3208 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
3209 me->cmd.cmd_descriptor,
3210 _("is beyond sheet boundaries"));
3211 g_object_unref (me);
3212 return TRUE;
3215 /* no need to test if all we have are objects or are copying from */
3216 /*a single merge to a single merge*/
3217 if ((!me->only_objects) && (!me->single_merge_to_single_merge)&&
3218 sheet_range_splits_region (pt->sheet, &me->dst.range,
3219 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
3220 g_object_unref (me);
3221 return TRUE;
3224 warn_if_date_trouble (wbc, cr);
3226 return gnm_command_push_undo (wbc, G_OBJECT (me));
3229 /******************************************************************/
3231 #define CMD_AUTOFILL_TYPE (cmd_autofill_get_type ())
3232 #define CMD_AUTOFILL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFILL_TYPE, CmdAutofill))
3234 typedef struct {
3235 GnmCommand cmd;
3237 GnmCellRegion *contents;
3238 GnmPasteTarget dst;
3239 GnmRange src;
3240 int base_col, base_row, w, h, end_col, end_row;
3241 gboolean default_increment;
3242 gboolean inverse_autofill;
3243 ColRowIndexList *columns;
3244 ColRowStateGroup *old_widths;
3245 } CmdAutofill;
3247 static void
3248 cmd_autofill_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3250 CmdAutofill const *orig = (CmdAutofill const *) cmd;
3251 SheetView *sv = wb_control_cur_sheet_view (wbc);
3252 GnmRange const *r = selection_first_range (sv,
3253 GO_CMD_CONTEXT (wbc), _("Autofill"));
3255 if (r == NULL)
3256 return;
3258 cmd_autofill (wbc, sv_sheet (sv), orig->default_increment,
3259 r->start.col, r->start.row, range_width (r), range_height (r),
3260 r->start.col + (orig->end_col - orig->base_col),
3261 r->start.row + (orig->end_row - orig->base_row),
3262 orig->inverse_autofill);
3264 MAKE_GNM_COMMAND (CmdAutofill, cmd_autofill, cmd_autofill_repeat)
3266 static gboolean
3267 cmd_autofill_undo (GnmCommand *cmd, WorkbookControl *wbc)
3269 CmdAutofill *me = CMD_AUTOFILL (cmd);
3270 gboolean res;
3272 g_return_val_if_fail (wbc != NULL, TRUE);
3273 g_return_val_if_fail (me != NULL, TRUE);
3274 g_return_val_if_fail (me->contents != NULL, TRUE);
3276 res = clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc));
3277 cellregion_unref (me->contents);
3278 me->contents = NULL;
3280 if (me->old_widths) {
3281 colrow_restore_state_group (me->cmd.sheet, TRUE,
3282 me->columns,
3283 me->old_widths);
3284 colrow_state_group_destroy (me->old_widths);
3285 me->old_widths = NULL;
3286 colrow_index_list_destroy (me->columns);
3287 me->columns = NULL;
3290 if (res)
3291 return TRUE;
3293 select_range (me->dst.sheet, &me->src, wbc);
3295 return FALSE;
3298 static gboolean
3299 cmd_autofill_redo (GnmCommand *cmd, WorkbookControl *wbc)
3301 CmdAutofill *me = CMD_AUTOFILL (cmd);
3302 GnmRange r;
3304 g_return_val_if_fail (me != NULL, TRUE);
3305 g_return_val_if_fail (me->contents == NULL, TRUE);
3307 me->contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
3309 g_return_val_if_fail (me->contents != NULL, TRUE);
3311 /* FIXME : when we split autofill to support hints and better validation
3312 * move this in there.
3314 /* MW: May 2006: we support hints now. What's this about? */
3315 sheet_clear_region (me->dst.sheet,
3316 me->dst.range.start.col, me->dst.range.start.row,
3317 me->dst.range.end.col, me->dst.range.end.row,
3318 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3319 GO_CMD_CONTEXT (wbc));
3321 if (me->cmd.size == 1)
3322 me->cmd.size += cellregion_cmd_size (me->contents);
3323 if (me->inverse_autofill)
3324 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3325 me->end_col, me->end_row, me->w, me->h,
3326 me->base_col, me->base_row);
3327 else
3328 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3329 me->base_col, me->base_row, me->w, me->h,
3330 me->end_col, me->end_row);
3332 colrow_autofit (me->cmd.sheet, &me->dst.range, TRUE, TRUE,
3333 TRUE, FALSE,
3334 &me->columns, &me->old_widths);
3336 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3337 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3338 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3340 r = range_union (&me->dst.range, &me->src);
3341 select_range (me->dst.sheet, &r, wbc);
3343 return FALSE;
3346 static void
3347 cmd_autofill_finalize (GObject *cmd)
3349 CmdAutofill *me = CMD_AUTOFILL (cmd);
3351 if (me->contents) {
3352 cellregion_unref (me->contents);
3353 me->contents = NULL;
3355 colrow_index_list_destroy (me->columns);
3356 colrow_state_group_destroy (me->old_widths);
3357 gnm_command_finalize (cmd);
3360 gboolean
3361 cmd_autofill (WorkbookControl *wbc, Sheet *sheet,
3362 gboolean default_increment,
3363 int base_col, int base_row,
3364 int w, int h, int end_col, int end_row,
3365 gboolean inverse_autofill)
3367 CmdAutofill *me;
3368 GnmRange target, src;
3370 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3372 /* This would be meaningless */
3373 if (base_col+w-1 == end_col && base_row+h-1 == end_row)
3374 return FALSE;
3376 if (inverse_autofill) {
3377 if (end_col != base_col + w - 1) {
3378 range_init (&target, base_col, base_row,
3379 end_col - w, end_row);
3380 range_init (&src, end_col - w + 1, base_row,
3381 end_col, end_row);
3382 } else {
3383 range_init (&target, base_col, base_row,
3384 end_col, end_row - h);
3385 range_init (&src, base_col, end_row - h + 1,
3386 end_col, end_row);
3388 } else {
3389 if (end_col != base_col + w - 1) {
3390 range_init (&target, base_col + w, base_row,
3391 end_col, end_row);
3392 range_init (&src, base_col, base_row,
3393 base_col + w - 1, end_row);
3394 } else {
3395 range_init (&target, base_col, base_row + h,
3396 end_col, end_row);
3397 range_init (&src, base_col, base_row,
3398 end_col, base_row + h - 1);
3402 /* We don't support clearing regions, when a user uses the autofill
3403 * cursor to 'shrink' a selection
3405 if (target.start.col > target.end.col || target.start.row > target.end.row)
3406 return TRUE;
3408 /* Check arrays or merged regions in src or target regions */
3409 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")) ||
3410 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")))
3411 return TRUE;
3413 me = g_object_new (CMD_AUTOFILL_TYPE, NULL);
3415 me->contents = NULL;
3416 me->dst.sheet = sheet;
3417 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3418 me->dst.range = target;
3419 me->src = src;
3421 me->base_col = base_col;
3422 me->base_row = base_row,
3423 me->w = w;
3424 me->h = h;
3425 me->end_col = end_col;
3426 me->end_row = end_row;
3427 me->default_increment = default_increment;
3428 me->inverse_autofill = inverse_autofill;
3430 me->cmd.sheet = sheet;
3431 me->cmd.size = 1; /* Changed in initial redo. */
3432 me->cmd.cmd_descriptor = g_strdup_printf (_("Autofilling %s"),
3433 range_as_string (&me->dst.range));
3435 return gnm_command_push_undo (wbc, G_OBJECT (me));
3438 /******************************************************************/
3440 #define CMD_COPYREL_TYPE (cmd_copyrel_get_type ())
3441 #define CMD_COPYREL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COPYREL_TYPE, CmdCopyRel))
3443 typedef struct {
3444 GnmCommand cmd;
3446 GOUndo *undo;
3447 GnmPasteTarget dst, src;
3448 int dx, dy;
3449 char const *name;
3450 } CmdCopyRel;
3452 static void
3453 cmd_copyrel_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3455 CmdCopyRel const *orig = (CmdCopyRel const *) cmd;
3456 cmd_copyrel (wbc, orig->dx, orig->dy, orig->name);
3458 MAKE_GNM_COMMAND (CmdCopyRel, cmd_copyrel, cmd_copyrel_repeat)
3460 static gboolean
3461 cmd_copyrel_undo (GnmCommand *cmd, WorkbookControl *wbc)
3463 CmdCopyRel *me = CMD_COPYREL (cmd);
3465 g_return_val_if_fail (wbc != NULL, TRUE);
3466 g_return_val_if_fail (me != NULL, TRUE);
3467 g_return_val_if_fail (me->undo != NULL, TRUE);
3469 go_undo_undo (me->undo);
3471 /* Select the newly pasted contents (this queues a redraw) */
3472 select_range (me->dst.sheet, &me->dst.range, wbc);
3474 return FALSE;
3477 static gboolean
3478 cmd_copyrel_redo (GnmCommand *cmd, WorkbookControl *wbc)
3480 CmdCopyRel *me = CMD_COPYREL (cmd);
3481 GnmCellRegion *contents;
3482 gboolean res;
3484 g_return_val_if_fail (me != NULL, TRUE);
3486 sheet_clear_region (me->dst.sheet,
3487 me->dst.range.start.col, me->dst.range.start.row,
3488 me->dst.range.end.col, me->dst.range.end.row,
3489 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3490 GO_CMD_CONTEXT (wbc));
3492 contents = clipboard_copy_range (me->src.sheet, &me->src.range);
3493 res = clipboard_paste_region (contents, &me->dst, GO_CMD_CONTEXT (wbc));
3494 cellregion_unref (contents);
3495 if (res)
3496 return TRUE;
3498 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3499 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3500 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3502 /* Select the newly pasted contents (this queues a redraw) */
3503 select_range (me->dst.sheet, &me->dst.range, wbc);
3505 return FALSE;
3508 static void
3509 cmd_copyrel_finalize (GObject *cmd)
3511 CmdCopyRel *me = CMD_COPYREL (cmd);
3513 if (me->undo)
3514 g_object_unref (me->undo);
3516 gnm_command_finalize (cmd);
3519 gboolean
3520 cmd_copyrel (WorkbookControl *wbc,
3521 int dx, int dy,
3522 char const *name)
3524 CmdCopyRel *me;
3525 GnmRange target, src;
3526 SheetView *sv = wb_control_cur_sheet_view (wbc);
3527 Sheet *sheet = sv->sheet;
3528 GnmRange const *selr =
3529 selection_first_range (sv, GO_CMD_CONTEXT (wbc), name);
3531 g_return_val_if_fail (dx == 0 || dy == 0, TRUE);
3533 if (!selr)
3534 return FALSE;
3536 target = *selr;
3537 range_normalize (&target);
3538 src.start = src.end = target.start;
3540 if (dy) {
3541 src.end.col = target.end.col;
3542 if (target.start.row != target.end.row)
3543 target.start.row++;
3544 else
3545 src.start.row = src.end.row = (target.start.row + dy);
3548 if (dx) {
3549 src.end.row = target.end.row;
3550 if (target.start.col != target.end.col)
3551 target.start.col++;
3552 else
3553 src.start.col = src.end.col = (target.start.col + dx);
3556 if (src.start.col < 0 || src.start.col >= gnm_sheet_get_max_cols (sheet) ||
3557 src.start.row < 0 || src.start.row >= gnm_sheet_get_max_rows (sheet))
3558 return FALSE;
3560 /* Check arrays or merged regions in src or target regions */
3561 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), name) ||
3562 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), name))
3563 return TRUE;
3565 me = g_object_new (CMD_COPYREL_TYPE, NULL);
3567 me->dst.sheet = sheet;
3568 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3569 me->dst.range = target;
3570 me->src.sheet = sheet;
3571 me->src.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3572 me->src.range = src;
3573 me->dx = dx;
3574 me->dy = dy;
3575 me->name = name;
3576 me->undo = clipboard_copy_range_undo (me->dst.sheet, &me->dst.range);
3578 me->cmd.sheet = sheet;
3579 me->cmd.size = 1;
3580 me->cmd.cmd_descriptor = g_strdup (name);
3582 return gnm_command_push_undo (wbc, G_OBJECT (me));
3585 /******************************************************************/
3588 #define CMD_AUTOFORMAT_TYPE (cmd_autoformat_get_type ())
3589 #define CMD_AUTOFORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFORMAT_TYPE, CmdAutoFormat))
3591 typedef struct {
3592 GnmCellPos pos;
3593 GnmStyleList *styles;
3594 } CmdAutoFormatOldStyle;
3596 typedef struct {
3597 GnmCommand cmd;
3599 GSList *selection; /* Selections on the sheet */
3600 GSList *old_styles; /* Older styles, one style_list per selection range*/
3602 GnmFT *ft; /* Template that has been applied */
3603 } CmdAutoFormat;
3605 static void
3606 cmd_autoformat_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3608 CmdAutoFormat const *orig = (CmdAutoFormat const *) cmd;
3609 cmd_selection_autoformat (wbc, gnm_ft_clone (orig->ft));
3611 MAKE_GNM_COMMAND (CmdAutoFormat, cmd_autoformat, cmd_autoformat_repeat)
3613 static gboolean
3614 cmd_autoformat_undo (GnmCommand *cmd,
3615 G_GNUC_UNUSED WorkbookControl *wbc)
3617 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3619 g_return_val_if_fail (me != NULL, TRUE);
3621 if (me->old_styles) {
3622 GSList *l1 = me->old_styles;
3623 GSList *l2 = me->selection;
3625 for (; l1; l1 = l1->next, l2 = l2->next) {
3626 GnmRange *r;
3627 CmdAutoFormatOldStyle *os = l1->data;
3628 GnmSpanCalcFlags flags = sheet_style_set_list (me->cmd.sheet,
3629 &os->pos, os->styles, NULL, NULL);
3631 g_return_val_if_fail (l2 && l2->data, TRUE);
3633 r = l2->data;
3634 sheet_range_calc_spans (me->cmd.sheet, r, flags);
3635 if (flags != GNM_SPANCALC_SIMPLE)
3636 rows_height_update (me->cmd.sheet, r, TRUE);
3640 return FALSE;
3643 static gboolean
3644 cmd_autoformat_redo (GnmCommand *cmd,
3645 G_GNUC_UNUSED WorkbookControl *wbc)
3647 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3649 g_return_val_if_fail (me != NULL, TRUE);
3651 gnm_ft_apply_to_sheet_regions (me->ft,
3652 me->cmd.sheet, me->selection);
3654 return FALSE;
3657 static void
3658 cmd_autoformat_finalize (GObject *cmd)
3660 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3662 if (me->old_styles != NULL) {
3663 GSList *l;
3665 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
3666 CmdAutoFormatOldStyle *os = l->data;
3668 if (os->styles)
3669 style_list_free (os->styles);
3671 g_free (os);
3674 me->old_styles = NULL;
3677 range_fragment_free (me->selection);
3678 me->selection = NULL;
3680 gnm_ft_free (me->ft);
3682 gnm_command_finalize (cmd);
3686 * cmd_selection_autoformat:
3687 * @wbc: the context.
3688 * @ft: The format template that was applied
3690 * Returns: %TRUE if there was a problem, %FALSE otherwise.
3692 gboolean
3693 cmd_selection_autoformat (WorkbookControl *wbc, GnmFT *ft)
3695 CmdAutoFormat *me;
3696 char *names;
3697 GSList *l;
3698 SheetView *sv = wb_control_cur_sheet_view (wbc);
3700 me = g_object_new (CMD_AUTOFORMAT_TYPE, NULL);
3702 me->selection = selection_get_ranges (sv, FALSE); /* Regions may overlap */
3703 me->ft = ft;
3704 me->cmd.sheet = sv_sheet (sv);
3705 me->cmd.size = 1; /* FIXME? */
3707 if (!gnm_ft_check_valid (ft, me->selection, GO_CMD_CONTEXT (wbc))) {
3708 g_object_unref (me);
3709 return TRUE;
3712 me->old_styles = NULL;
3713 for (l = me->selection; l; l = l->next) {
3714 CmdFormatOldStyle *os;
3715 GnmRange range = *((GnmRange const *) l->data);
3717 /* Store the containing range to handle borders */
3718 if (range.start.col > 0) range.start.col--;
3719 if (range.start.row > 0) range.start.row--;
3720 if (range.end.col < gnm_sheet_get_last_col (sv->sheet)) range.end.col++;
3721 if (range.end.row < gnm_sheet_get_last_row (sv->sheet)) range.end.row++;
3723 os = g_new (CmdFormatOldStyle, 1);
3725 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
3726 os->pos = range.start;
3728 me->old_styles = g_slist_append (me->old_styles, os);
3731 names = undo_range_list_name (me->cmd.sheet, me->selection);
3732 me->cmd.cmd_descriptor = g_strdup_printf (_("Autoformatting %s"),
3733 names);
3734 g_free (names);
3736 return gnm_command_push_undo (wbc, G_OBJECT (me));
3739 /******************************************************************/
3741 #define CMD_UNMERGE_CELLS_TYPE (cmd_unmerge_cells_get_type ())
3742 #define CMD_UNMERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_UNMERGE_CELLS_TYPE, CmdUnmergeCells))
3744 typedef struct {
3745 GnmCommand cmd;
3747 Sheet *sheet;
3748 GArray *unmerged_regions;
3749 GArray *ranges;
3750 } CmdUnmergeCells;
3752 static void
3753 cmd_unmerge_cells_repeat (G_GNUC_UNUSED GnmCommand const *cmd, WorkbookControl *wbc)
3755 SheetView *sv = wb_control_cur_sheet_view (wbc);
3756 GSList *range_list = selection_get_ranges (sv, FALSE);
3757 cmd_unmerge_cells (wbc, sv_sheet (sv), range_list);
3758 range_fragment_free (range_list);
3760 MAKE_GNM_COMMAND (CmdUnmergeCells, cmd_unmerge_cells, cmd_unmerge_cells_repeat)
3762 static gboolean
3763 cmd_unmerge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3765 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3766 unsigned i;
3768 g_return_val_if_fail (me != NULL, TRUE);
3769 g_return_val_if_fail (me->unmerged_regions != NULL, TRUE);
3771 for (i = 0 ; i < me->unmerged_regions->len ; ++i) {
3772 GnmRange const *tmp = &(g_array_index (me->unmerged_regions, GnmRange, i));
3773 sheet_redraw_range (me->cmd.sheet, tmp);
3774 gnm_sheet_merge_add (me->cmd.sheet, tmp, TRUE, GO_CMD_CONTEXT (wbc));
3775 sheet_range_calc_spans (me->cmd.sheet, tmp, GNM_SPANCALC_RE_RENDER);
3778 g_array_free (me->unmerged_regions, TRUE);
3779 me->unmerged_regions = NULL;
3781 return FALSE;
3784 static gboolean
3785 cmd_unmerge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3787 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3788 unsigned i;
3790 g_return_val_if_fail (me != NULL, TRUE);
3791 g_return_val_if_fail (me->unmerged_regions == NULL, TRUE);
3793 me->unmerged_regions = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3794 for (i = 0 ; i < me->ranges->len ; ++i) {
3795 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (me->cmd.sheet,
3796 &(g_array_index (me->ranges, GnmRange, i)));
3797 for (ptr = merged ; ptr != NULL ; ptr = ptr->next) {
3798 GnmRange const *pr = ptr->data;
3799 GnmRange const tmp = *pr;
3800 g_array_append_val (me->unmerged_regions, tmp);
3801 gnm_sheet_merge_remove (me->cmd.sheet, &tmp);
3802 sheet_range_calc_spans (me->cmd.sheet, &tmp,
3803 GNM_SPANCALC_RE_RENDER);
3805 g_slist_free (merged);
3808 return FALSE;
3811 static void
3812 cmd_unmerge_cells_finalize (GObject *cmd)
3814 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3816 if (me->unmerged_regions != NULL) {
3817 g_array_free (me->unmerged_regions, TRUE);
3818 me->unmerged_regions = NULL;
3820 if (me->ranges != NULL) {
3821 g_array_free (me->ranges, TRUE);
3822 me->ranges = NULL;
3825 gnm_command_finalize (cmd);
3829 * cmd_unmerge_cells:
3830 * @wbc: the context.
3831 * @sheet: #Sheet
3832 * @selection: (element-type GnmRange): selection.
3834 * Returns: %TRUE if there was a problem, %FALSE otherwise.
3836 gboolean
3837 cmd_unmerge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection)
3839 CmdUnmergeCells *me;
3840 char *names;
3842 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3844 me = g_object_new (CMD_UNMERGE_CELLS_TYPE, NULL);
3846 me->cmd.sheet = sheet;
3847 me->cmd.size = 1;
3849 names = undo_range_list_name (sheet, selection);
3850 me->cmd.cmd_descriptor = g_strdup_printf (_("Unmerging %s"), names);
3851 g_free (names);
3853 me->unmerged_regions = NULL;
3854 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3855 for ( ; selection != NULL ; selection = selection->next) {
3856 GSList *merged = gnm_sheet_merge_get_overlap (sheet, selection->data);
3857 if (merged != NULL) {
3858 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
3859 g_slist_free (merged);
3863 if (me->ranges->len <= 0) {
3864 g_object_unref (me);
3865 return TRUE;
3868 return gnm_command_push_undo (wbc, G_OBJECT (me));
3871 /******************************************************************/
3873 #define CMD_MERGE_CELLS_TYPE (cmd_merge_cells_get_type ())
3874 #define CMD_MERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_CELLS_TYPE, CmdMergeCells))
3876 typedef struct {
3877 GnmCommand cmd;
3878 GArray *ranges;
3879 GSList *old_contents;
3880 gboolean center;
3881 } CmdMergeCells;
3883 static void
3884 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc);
3886 MAKE_GNM_COMMAND (CmdMergeCells, cmd_merge_cells, cmd_merge_cells_repeat)
3888 static void
3889 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3891 SheetView *sv = wb_control_cur_sheet_view (wbc);
3892 GSList *range_list = selection_get_ranges (sv, FALSE);
3893 cmd_merge_cells (wbc, sv_sheet (sv), range_list,
3894 CMD_MERGE_CELLS (cmd)->center);
3895 range_fragment_free (range_list);
3898 static gboolean
3899 cmd_merge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3901 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3902 unsigned i, flags;
3904 g_return_val_if_fail (me != NULL, TRUE);
3906 for (i = 0 ; i < me->ranges->len ; ++i) {
3907 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3908 gnm_sheet_merge_remove (me->cmd.sheet, r);
3911 /* Avoid pasting comments that are at 0,0. Redo copies the target
3912 * region (including all comments) . If there was a comment in the top
3913 * left we would end up duplicating it. */
3914 flags = PASTE_CONTENTS | PASTE_FORMATS | PASTE_COMMENTS |
3915 PASTE_IGNORE_COMMENTS_AT_ORIGIN;
3916 if (me->center)
3917 flags |= PASTE_FORMATS;
3918 for (i = 0 ; i < me->ranges->len ; ++i) {
3919 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3920 GnmPasteTarget pt;
3921 GnmCellRegion * c;
3923 g_return_val_if_fail (me->old_contents != NULL, TRUE);
3925 c = me->old_contents->data;
3926 clipboard_paste_region (c,
3927 paste_target_init (&pt, me->cmd.sheet, r, flags),
3928 GO_CMD_CONTEXT (wbc));
3929 cellregion_unref (c);
3930 me->old_contents = g_slist_remove (me->old_contents, c);
3932 g_return_val_if_fail (me->old_contents == NULL, TRUE);
3934 return FALSE;
3937 static gboolean
3938 cmd_merge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3940 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3941 GnmStyle *align_center = NULL;
3942 Sheet *sheet;
3943 unsigned i;
3945 g_return_val_if_fail (me != NULL, TRUE);
3947 if (me->center) {
3948 align_center = gnm_style_new ();
3949 gnm_style_set_align_h (align_center, GNM_HALIGN_CENTER);
3951 sheet = me->cmd.sheet;
3952 for (i = 0 ; i < me->ranges->len ; ++i) {
3953 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3954 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (sheet, r);
3956 /* save contents before removing contained merged regions */
3957 me->old_contents = g_slist_prepend (me->old_contents,
3958 clipboard_copy_range (sheet, r));
3959 for (ptr = merged ; ptr != NULL ; ptr = ptr->next)
3960 gnm_sheet_merge_remove (sheet, ptr->data);
3961 g_slist_free (merged);
3963 gnm_sheet_merge_add (sheet, r, TRUE, GO_CMD_CONTEXT (wbc));
3964 if (me->center)
3965 sheet_apply_style (me->cmd.sheet, r, align_center);
3968 if (me->center)
3969 gnm_style_unref (align_center);
3970 me->old_contents = g_slist_reverse (me->old_contents);
3971 return FALSE;
3974 static void
3975 cmd_merge_cells_finalize (GObject *cmd)
3977 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3979 if (me->old_contents != NULL) {
3980 GSList *l;
3981 for (l = me->old_contents ; l != NULL ; l = g_slist_remove (l, l->data))
3982 cellregion_unref (l->data);
3983 me->old_contents = NULL;
3986 if (me->ranges != NULL) {
3987 g_array_free (me->ranges, TRUE);
3988 me->ranges = NULL;
3991 gnm_command_finalize (cmd);
3995 * cmd_merge_cells:
3996 * @wbc: the context.
3997 * @sheet: #Sheet
3998 * @selection: (element-type GnmRange): selection.
3999 * @center:
4001 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4003 gboolean
4004 cmd_merge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection,
4005 gboolean center)
4007 CmdMergeCells *me;
4008 char *names;
4010 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4012 me = g_object_new (CMD_MERGE_CELLS_TYPE, NULL);
4014 me->cmd.sheet = sheet;
4015 me->cmd.size = 1;
4017 names = undo_range_list_name (sheet, selection);
4018 me->cmd.cmd_descriptor =
4019 g_strdup_printf ((center ? _("Merge and Center %s") :_("Merging %s")), names);
4020 g_free (names);
4022 me->center = center;
4023 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
4024 for ( ; selection != NULL ; selection = selection->next) {
4025 GnmRange const *exist;
4026 GnmRange const *r = selection->data;
4027 if (range_is_singleton (selection->data))
4028 continue;
4029 if (NULL != (exist = gnm_sheet_merge_is_corner (sheet, &r->start)) &&
4030 range_equal (r, exist))
4031 continue;
4032 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
4035 if (me->ranges->len <= 0) {
4036 g_object_unref (me);
4037 return TRUE;
4040 return gnm_command_push_undo (wbc, G_OBJECT (me));
4043 /******************************************************************/
4045 #define CMD_SEARCH_REPLACE_TYPE (cmd_search_replace_get_type())
4046 #define CMD_SEARCH_REPLACE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SEARCH_REPLACE_TYPE, CmdSearchReplace))
4048 typedef struct {
4049 GnmCommand cmd;
4050 GnmSearchReplace *sr;
4053 * Undo/redo use this list of SearchReplaceItems to do their
4054 * work. Note, that it is possible for a cell to occur
4055 * multiple times in the list.
4057 GList *cells;
4058 } CmdSearchReplace;
4060 MAKE_GNM_COMMAND (CmdSearchReplace, cmd_search_replace, NULL)
4062 typedef enum { SRI_text, SRI_comment } SearchReplaceItemType;
4064 typedef struct {
4065 GnmEvalPos pos;
4066 SearchReplaceItemType old_type, new_type;
4067 union {
4068 char *text;
4069 char *comment;
4070 } old, new;
4071 } SearchReplaceItem;
4074 static void
4075 cmd_search_replace_update_after_action (CmdSearchReplace *me,
4076 WorkbookControl *wbc)
4078 GList *tmp;
4079 Sheet *last_sheet = NULL;
4081 for (tmp = me->cells; tmp; tmp = tmp->next) {
4082 SearchReplaceItem *sri = tmp->data;
4083 if (sri->pos.sheet != last_sheet) {
4084 last_sheet = sri->pos.sheet;
4085 update_after_action (last_sheet, wbc);
4091 static gboolean
4092 cmd_search_replace_undo (GnmCommand *cmd,
4093 WorkbookControl *wbc)
4095 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4096 GList *tmp;
4098 /* Undo does replacements backwards. */
4099 for (tmp = g_list_last (me->cells); tmp; tmp = tmp->prev) {
4100 SearchReplaceItem *sri = tmp->data;
4101 switch (sri->old_type) {
4102 case SRI_text:
4104 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4105 sri->pos.eval.col,
4106 sri->pos.eval.row);
4107 sheet_cell_set_text (cell, sri->old.text, NULL);
4108 break;
4110 case SRI_comment:
4112 GnmComment *comment =
4113 sheet_get_comment (sri->pos.sheet,
4114 &sri->pos.eval);
4115 if (comment) {
4116 cell_comment_text_set (comment, sri->old.comment);
4117 } else {
4118 g_warning ("Undo/redo broken.");
4121 break;
4124 cmd_search_replace_update_after_action (me, wbc);
4126 return FALSE;
4129 static gboolean
4130 cmd_search_replace_redo (GnmCommand *cmd,
4131 WorkbookControl *wbc)
4133 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4134 GList *tmp;
4136 /* Redo does replacements forward. */
4137 for (tmp = me->cells; tmp; tmp = tmp->next) {
4138 SearchReplaceItem *sri = tmp->data;
4139 switch (sri->new_type) {
4140 case SRI_text:
4142 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4143 sri->pos.eval.col,
4144 sri->pos.eval.row);
4145 sheet_cell_set_text (cell, sri->new.text, NULL);
4146 break;
4148 case SRI_comment:
4150 GnmComment *comment =
4151 sheet_get_comment (sri->pos.sheet,
4152 &sri->pos.eval);
4153 if (comment) {
4154 cell_comment_text_set (comment, sri->new.comment);
4155 } else {
4156 g_warning ("Undo/redo broken.");
4159 break;
4162 cmd_search_replace_update_after_action (me, wbc);
4164 return FALSE;
4167 static gboolean
4168 cmd_search_replace_do_cell (CmdSearchReplace *me, GnmEvalPos *ep,
4169 gboolean test_run)
4171 GnmSearchReplace *sr = me->sr;
4173 GnmSearchReplaceCellResult cell_res;
4174 GnmSearchReplaceCommentResult comment_res;
4176 if (gnm_search_replace_cell (sr, ep, TRUE, &cell_res)) {
4177 GnmExprTop const *texpr;
4178 GnmValue *val;
4179 gboolean err;
4180 GnmParsePos pp;
4182 parse_pos_init_evalpos (&pp, ep);
4183 parse_text_value_or_expr (&pp, cell_res.new_text, &val, &texpr);
4186 * FIXME: this is a hack, but parse_text_value_or_expr
4187 * does not have a better way of signaling an error.
4189 err = (val &&
4190 gnm_expr_char_start_p (cell_res.new_text) &&
4191 !go_format_is_text (gnm_cell_get_format (cell_res.cell)));
4192 value_release (val);
4193 if (texpr) gnm_expr_top_unref (texpr);
4195 if (err) {
4196 if (test_run) {
4197 gnm_search_replace_query_fail (sr, &cell_res);
4198 g_free (cell_res.old_text);
4199 g_free (cell_res.new_text);
4200 return TRUE;
4201 } else {
4202 switch (sr->error_behaviour) {
4203 case GNM_SRE_ERROR: {
4204 GnmExprTop const *ee =
4205 gnm_expr_top_new
4206 (gnm_expr_new_funcall1
4207 (gnm_func_lookup ("ERROR", NULL),
4208 gnm_expr_new_constant
4209 (value_new_string_nocopy (cell_res.new_text))));
4210 GnmConventionsOut out;
4212 out.accum = g_string_new ("=");
4213 out.pp = &pp;
4214 out.convs = pp.sheet->convs;
4215 gnm_expr_top_as_gstring (ee, &out);
4216 gnm_expr_top_unref (ee);
4217 cell_res.new_text = g_string_free (out.accum, FALSE);
4218 err = FALSE;
4219 break;
4221 case GNM_SRE_STRING: {
4222 GString *s = g_string_new ("'");
4223 g_string_append (s, cell_res.new_text);
4224 g_free (cell_res.new_text);
4225 cell_res.new_text = g_string_free (s, FALSE);
4226 err = FALSE;
4227 break;
4229 case GNM_SRE_FAIL:
4230 g_assert_not_reached ();
4231 case GNM_SRE_SKIP:
4232 default:
4233 ; /* Nothing */
4238 if (!err && !test_run) {
4239 int res = gnm_search_replace_query_cell
4240 (sr, &cell_res);
4241 gboolean doit = (res == GTK_RESPONSE_YES);
4243 if (res == GTK_RESPONSE_CANCEL) {
4244 g_free (cell_res.old_text);
4245 g_free (cell_res.new_text);
4246 return TRUE;
4249 if (doit) {
4250 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4252 sheet_cell_set_text (cell_res.cell, cell_res.new_text, NULL);
4254 sri->pos = *ep;
4255 sri->old_type = sri->new_type = SRI_text;
4256 sri->old.text = cell_res.old_text;
4257 sri->new.text = cell_res.new_text;
4258 me->cells = g_list_prepend (me->cells, sri);
4260 cell_res.old_text = cell_res.new_text = NULL;
4264 g_free (cell_res.new_text);
4265 g_free (cell_res.old_text);
4268 if (!test_run &&
4269 gnm_search_replace_comment (sr, ep, TRUE, &comment_res)) {
4270 int res = gnm_search_replace_query_comment
4271 (sr, ep, &comment_res);
4272 gboolean doit = (res == GTK_RESPONSE_YES);
4274 if (doit) {
4275 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4276 sri->pos = *ep;
4277 sri->old_type = sri->new_type = SRI_comment;
4278 sri->old.comment = g_strdup (comment_res.old_text);
4279 sri->new.comment = comment_res.new_text;
4280 me->cells = g_list_prepend (me->cells, sri);
4282 cell_comment_text_set (comment_res.comment, comment_res.new_text);
4283 } else {
4284 g_free (comment_res.new_text);
4285 if (res == GTK_RESPONSE_CANCEL)
4286 return TRUE;
4290 return FALSE;
4294 static gboolean
4295 cmd_search_replace_do (CmdSearchReplace *me, gboolean test_run,
4296 WorkbookControl *wbc)
4298 GnmSearchReplace *sr = me->sr;
4299 GPtrArray *cells;
4300 gboolean result = FALSE;
4301 unsigned i;
4303 if (test_run) {
4304 switch (sr->error_behaviour) {
4305 case GNM_SRE_SKIP:
4306 case GNM_SRE_QUERY:
4307 case GNM_SRE_ERROR:
4308 case GNM_SRE_STRING:
4309 /* An error is not a problem. */
4310 return FALSE;
4312 case GNM_SRE_FAIL:
4313 ; /* Nothing. */
4317 cells = gnm_search_collect_cells (sr);
4319 for (i = 0; i < cells->len; i++) {
4320 GnmEvalPos *ep = g_ptr_array_index (cells, i);
4322 if (cmd_search_replace_do_cell (me, ep, test_run)) {
4323 result = TRUE;
4324 break;
4328 gnm_search_collect_cells_free (cells);
4330 if (!test_run) {
4331 /* Cells were added in the wrong order. Correct. */
4332 me->cells = g_list_reverse (me->cells);
4334 cmd_search_replace_update_after_action (me, wbc);
4337 return result;
4341 static void
4342 cmd_search_replace_finalize (GObject *cmd)
4344 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4345 GList *tmp;
4347 for (tmp = me->cells; tmp; tmp = tmp->next) {
4348 SearchReplaceItem *sri = tmp->data;
4349 switch (sri->old_type) {
4350 case SRI_text:
4351 g_free (sri->old.text);
4352 break;
4353 case SRI_comment:
4354 g_free (sri->old.comment);
4355 break;
4357 switch (sri->new_type) {
4358 case SRI_text:
4359 g_free (sri->new.text);
4360 break;
4361 case SRI_comment:
4362 g_free (sri->new.comment);
4363 break;
4365 g_free (sri);
4367 g_list_free (me->cells);
4368 g_object_unref (me->sr);
4370 gnm_command_finalize (cmd);
4373 gboolean
4374 cmd_search_replace (WorkbookControl *wbc, GnmSearchReplace *sr)
4376 CmdSearchReplace *me;
4378 g_return_val_if_fail (sr != NULL, TRUE);
4380 me = g_object_new (CMD_SEARCH_REPLACE_TYPE, NULL);
4382 me->cells = NULL;
4383 me->sr = g_object_ref (sr);
4385 me->cmd.sheet = NULL;
4386 me->cmd.size = 1; /* Corrected below. */
4387 me->cmd.cmd_descriptor = g_strdup (_("Search and Replace"));
4389 if (cmd_search_replace_do (me, TRUE, wbc)) {
4390 /* There was an error and nothing was done. */
4391 g_object_unref (me);
4392 return TRUE;
4395 cmd_search_replace_do (me, FALSE, wbc);
4396 me->cmd.size += g_list_length (me->cells);
4398 command_register_undo (wbc, G_OBJECT (me));
4399 return FALSE;
4402 /******************************************************************/
4404 #define CMD_COLROW_STD_SIZE_TYPE (cmd_colrow_std_size_get_type ())
4405 #define CMD_COLROW_STD_SIZE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_STD_SIZE_TYPE, CmdColRowStdSize))
4407 typedef struct {
4408 GnmCommand cmd;
4410 Sheet *sheet;
4411 gboolean is_cols;
4412 double new_default;
4413 double old_default;
4414 } CmdColRowStdSize;
4416 MAKE_GNM_COMMAND (CmdColRowStdSize, cmd_colrow_std_size, NULL)
4418 static gboolean
4419 cmd_colrow_std_size_undo (GnmCommand *cmd,
4420 G_GNUC_UNUSED WorkbookControl *wbc)
4422 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4424 g_return_val_if_fail (me != NULL, TRUE);
4425 g_return_val_if_fail (me->old_default != 0, TRUE);
4427 if (me->is_cols)
4428 sheet_col_set_default_size_pts (me->sheet, me->old_default);
4429 else
4430 sheet_row_set_default_size_pts (me->sheet, me->old_default);
4432 me->old_default = 0;
4434 return FALSE;
4437 static gboolean
4438 cmd_colrow_std_size_redo (GnmCommand *cmd,
4439 G_GNUC_UNUSED WorkbookControl *wbc)
4441 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4443 g_return_val_if_fail (me != NULL, TRUE);
4444 g_return_val_if_fail (me->old_default == 0, TRUE);
4446 if (me->is_cols) {
4447 me->old_default = sheet_col_get_default_size_pts (me->sheet);
4448 sheet_col_set_default_size_pts (me->sheet, me->new_default);
4449 } else {
4450 me->old_default = sheet_row_get_default_size_pts (me->sheet);
4451 sheet_row_set_default_size_pts (me->sheet, me->new_default);
4454 return FALSE;
4456 static void
4457 cmd_colrow_std_size_finalize (GObject *cmd)
4459 gnm_command_finalize (cmd);
4462 gboolean
4463 cmd_colrow_std_size (WorkbookControl *wbc, Sheet *sheet,
4464 gboolean is_cols, double new_default)
4466 CmdColRowStdSize *me;
4468 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4470 me = g_object_new (CMD_COLROW_STD_SIZE_TYPE, NULL);
4472 me->sheet = sheet;
4473 me->is_cols = is_cols;
4474 me->new_default = new_default;
4475 me->old_default = 0;
4477 me->cmd.sheet = sheet;
4478 me->cmd.size = 1; /* Changed in initial redo. */
4479 me->cmd.cmd_descriptor = is_cols
4480 ? g_strdup_printf (_("Setting default width of columns to %.2fpts"), new_default)
4481 : g_strdup_printf (_("Setting default height of rows to %.2fpts"), new_default);
4483 return gnm_command_push_undo (wbc, G_OBJECT (me));
4486 /******************************************************************/
4488 #define CMD_ZOOM_TYPE (cmd_zoom_get_type ())
4489 #define CMD_ZOOM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ZOOM_TYPE, CmdZoom))
4491 typedef struct {
4492 GnmCommand cmd;
4494 GSList *sheets;
4495 double new_factor;
4496 double *old_factors;
4497 } CmdZoom;
4499 MAKE_GNM_COMMAND (CmdZoom, cmd_zoom, NULL)
4501 static gboolean
4502 cmd_zoom_undo (GnmCommand *cmd,
4503 G_GNUC_UNUSED WorkbookControl *wbc)
4505 CmdZoom *me = CMD_ZOOM (cmd);
4506 GSList *l;
4507 int i;
4509 g_return_val_if_fail (me != NULL, TRUE);
4510 g_return_val_if_fail (me->sheets != NULL, TRUE);
4511 g_return_val_if_fail (me->old_factors != NULL, TRUE);
4513 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4514 Sheet *sheet = l->data;
4515 g_object_set (sheet, "zoom-factor", me->old_factors[i], NULL);
4518 return FALSE;
4521 static gboolean
4522 cmd_zoom_redo (GnmCommand *cmd,
4523 G_GNUC_UNUSED WorkbookControl *wbc)
4525 CmdZoom *me = CMD_ZOOM (cmd);
4526 GSList *l;
4528 g_return_val_if_fail (me != NULL, TRUE);
4529 g_return_val_if_fail (me->sheets != NULL, TRUE);
4531 for (l = me->sheets; l != NULL; l = l->next) {
4532 Sheet *sheet = l->data;
4533 g_object_set (sheet, "zoom-factor", me->new_factor, NULL);
4536 return FALSE;
4539 static void
4540 cmd_zoom_finalize (GObject *cmd)
4542 CmdZoom *me = CMD_ZOOM (cmd);
4544 g_slist_free (me->sheets);
4545 g_free (me->old_factors);
4547 gnm_command_finalize (cmd);
4551 * cmd_zoom:
4552 * @wbc: #WorkbookControl
4553 * @sheets: (element-type Sheet) (transfer container):
4554 * @factor:
4556 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4558 gboolean
4559 cmd_zoom (WorkbookControl *wbc, GSList *sheets, double factor)
4561 CmdZoom *me;
4562 GString *namelist;
4563 GSList *l;
4564 int i;
4566 g_return_val_if_fail (wbc != NULL, TRUE);
4567 g_return_val_if_fail (sheets != NULL, TRUE);
4569 me = g_object_new (CMD_ZOOM_TYPE, NULL);
4571 me->sheets = sheets;
4572 me->old_factors = g_new0 (double, g_slist_length (sheets));
4573 me->new_factor = factor;
4575 /* Make a list of all sheets to zoom and save zoom factor for each */
4576 namelist = g_string_new (NULL);
4577 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4578 Sheet *sheet = l->data;
4580 g_string_append (namelist, sheet->name_unquoted);
4581 me->old_factors[i] = sheet->last_zoom_factor_used;
4583 if (l->next)
4584 g_string_append (namelist, ", ");
4587 /* Make sure the string doesn't get overly wide */
4588 gnm_cmd_trunc_descriptor (namelist, NULL);
4590 me->cmd.sheet = NULL;
4591 me->cmd.size = 1;
4592 me->cmd.cmd_descriptor =
4593 g_strdup_printf (_("Zoom %s to %.0f%%"), namelist->str, factor * 100);
4595 g_string_free (namelist, TRUE);
4597 return gnm_command_push_undo (wbc, G_OBJECT (me));
4600 /******************************************************************/
4602 #define CMD_OBJECTS_DELETE_TYPE (cmd_objects_delete_get_type ())
4603 #define CMD_OBJECTS_DELETE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECTS_DELETE_TYPE, CmdObjectsDelete))
4605 typedef struct {
4606 GnmCommand cmd;
4607 GSList *objects;
4608 GArray *location;
4609 } CmdObjectsDelete;
4611 MAKE_GNM_COMMAND (CmdObjectsDelete, cmd_objects_delete, NULL)
4613 static gboolean
4614 cmd_objects_delete_redo (GnmCommand *cmd,
4615 G_GNUC_UNUSED WorkbookControl *wbc)
4617 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4618 g_slist_foreach (me->objects, (GFunc) sheet_object_clear_sheet, NULL);
4619 return FALSE;
4622 static void
4623 cmd_objects_restore_location (SheetObject *so, gint location)
4625 gint loc = sheet_object_get_stacking (so);
4626 if (loc != location)
4627 sheet_object_adjust_stacking(so, location - loc);
4630 static gboolean
4631 cmd_objects_delete_undo (GnmCommand *cmd,
4632 G_GNUC_UNUSED WorkbookControl *wbc)
4634 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4635 GSList *l;
4636 gint i;
4638 g_slist_foreach (me->objects,
4639 (GFunc) sheet_object_set_sheet, me->cmd.sheet);
4641 for (l = me->objects, i = 0; l; l = l->next, i++)
4642 cmd_objects_restore_location (GNM_SO (l->data),
4643 g_array_index(me->location,
4644 gint, i));
4645 return FALSE;
4648 static void
4649 cmd_objects_delete_finalize (GObject *cmd)
4651 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4652 g_slist_free_full (me->objects, g_object_unref);
4653 if (me->location) {
4654 g_array_free (me->location, TRUE);
4655 me->location = NULL;
4657 gnm_command_finalize (cmd);
4660 static void
4661 cmd_objects_store_location (SheetObject *so, GArray *location)
4663 gint loc = sheet_object_get_stacking (so);
4664 g_array_append_val (location, loc);
4668 * cmd_objects_delete:
4669 * @wbc: #WorkbookControl
4670 * @objects: (element-type SheetObject) (transfer container): the objects to
4671 * delete.
4672 * @name:
4674 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4676 gboolean
4677 cmd_objects_delete (WorkbookControl *wbc, GSList *objects,
4678 char const *name)
4680 CmdObjectsDelete *me;
4682 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4683 g_return_val_if_fail (objects != NULL, TRUE);
4685 me = g_object_new (CMD_OBJECTS_DELETE_TYPE, NULL);
4687 me->objects = objects;
4688 g_slist_foreach (me->objects, (GFunc) g_object_ref, NULL);
4690 me->location = g_array_new (FALSE, FALSE, sizeof (gint));
4691 g_slist_foreach (me->objects, (GFunc) cmd_objects_store_location,
4692 me->location);
4694 me->cmd.sheet = sheet_object_get_sheet (objects->data);
4695 me->cmd.size = 1;
4696 me->cmd.cmd_descriptor = g_strdup (name ? name : _("Delete Object"));
4698 return gnm_command_push_undo (wbc, G_OBJECT (me));
4701 /******************************************************************/
4704 * cmd_objects_move:
4705 * @wbc: #WorkbookControl
4706 * @objects: (element-type SheetObject) (transfer container): the objects to move.
4707 * @anchors: (element-type SheetObjectAnchor) (transfer full): the anchors for the objects.
4708 * @objects_created:
4709 * @name:
4711 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4713 gboolean
4714 cmd_objects_move (WorkbookControl *wbc, GSList *objects, GSList *anchors,
4715 gboolean objects_created, char const *name)
4717 GOUndo *undo = NULL;
4718 GOUndo *redo = NULL;
4719 gboolean result = TRUE;
4721 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4723 undo = sheet_object_move_undo (objects, objects_created);
4724 redo = sheet_object_move_do (objects, anchors, objects_created);
4726 result = cmd_generic (wbc, name, undo, redo);
4728 g_slist_free (objects);
4729 g_slist_free_full (anchors, g_free);
4731 return result;
4734 /******************************************************************/
4736 #define CMD_REORGANIZE_SHEETS_TYPE (cmd_reorganize_sheets_get_type ())
4737 #define CMD_REORGANIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REORGANIZE_SHEETS_TYPE, CmdReorganizeSheets))
4739 typedef struct {
4740 GnmCommand cmd;
4741 Workbook *wb;
4742 WorkbookSheetState *old;
4743 WorkbookSheetState *new;
4744 gboolean first;
4745 Sheet *undo_sheet;
4746 Sheet *redo_sheet;
4747 } CmdReorganizeSheets;
4749 MAKE_GNM_COMMAND (CmdReorganizeSheets, cmd_reorganize_sheets, NULL)
4751 static gboolean
4752 cmd_reorganize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4754 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4755 workbook_sheet_state_restore (me->wb, me->old);
4756 if (me->undo_sheet) {
4757 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4758 wb_control_sheet_focus (control, me->undo_sheet););
4760 return FALSE;
4763 static gboolean
4764 cmd_reorganize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4766 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4768 if (me->first)
4769 me->first = FALSE;
4770 else {
4771 workbook_sheet_state_restore (me->wb, me->new);
4772 if (me->redo_sheet) {
4773 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4774 wb_control_sheet_focus (control, me->redo_sheet););
4778 return FALSE;
4781 static void
4782 cmd_reorganize_sheets_finalize (GObject *cmd)
4784 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4786 if (me->old)
4787 workbook_sheet_state_free (me->old);
4788 if (me->new)
4789 workbook_sheet_state_free (me->new);
4791 gnm_command_finalize (cmd);
4794 gboolean
4795 cmd_reorganize_sheets (WorkbookControl *wbc,
4796 WorkbookSheetState *old_state,
4797 Sheet *undo_sheet)
4799 CmdReorganizeSheets *me;
4800 Workbook *wb = wb_control_get_workbook (wbc);
4802 me = g_object_new (CMD_REORGANIZE_SHEETS_TYPE, NULL);
4803 me->wb = wb;
4804 me->old = old_state;
4805 me->new = workbook_sheet_state_new (me->wb);
4806 me->first = TRUE;
4807 me->undo_sheet = undo_sheet;
4808 me->redo_sheet = wb_control_cur_sheet (wbc);
4810 me->cmd.sheet = NULL;
4811 me->cmd.size = workbook_sheet_state_size (me->old) +
4812 workbook_sheet_state_size (me->new);
4813 me->cmd.cmd_descriptor =
4814 workbook_sheet_state_diff (me->old, me->new);
4816 if (me->cmd.cmd_descriptor)
4817 return gnm_command_push_undo (wbc, G_OBJECT (me));
4819 /* No change. */
4820 g_object_unref (me);
4821 return FALSE;
4824 /******************************************************************/
4826 gboolean
4827 cmd_rename_sheet (WorkbookControl *wbc,
4828 Sheet *sheet,
4829 char const *new_name)
4831 WorkbookSheetState *old_state;
4832 Sheet *collision;
4834 g_return_val_if_fail (new_name != NULL, TRUE);
4835 g_return_val_if_fail (sheet != NULL, TRUE);
4837 if (*new_name == 0) {
4838 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), _("Sheet names must be non-empty."));
4839 return TRUE;
4842 collision = workbook_sheet_by_name (sheet->workbook, new_name);
4843 if (collision && collision != sheet) {
4844 GError *err = g_error_new (go_error_invalid(), 0,
4845 _("A workbook cannot have two sheets with the same name."));
4846 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
4847 g_error_free (err);
4848 return TRUE;
4851 old_state = workbook_sheet_state_new (sheet->workbook);
4852 g_object_set (sheet, "name", new_name, NULL);
4853 return cmd_reorganize_sheets (wbc, old_state, sheet);
4856 /******************************************************************/
4858 #define CMD_RESIZE_SHEETS_TYPE (cmd_resize_sheets_get_type ())
4859 #define CMD_RESIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESIZE_SHEETS_TYPE, CmdResizeSheets))
4861 typedef struct {
4862 GnmCommand cmd;
4863 GSList *sheets;
4864 int cols, rows;
4865 GOUndo *undo;
4866 } CmdResizeSheets;
4868 MAKE_GNM_COMMAND (CmdResizeSheets, cmd_resize_sheets, NULL)
4870 static gboolean
4871 cmd_resize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4873 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4874 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4876 go_undo_undo_with_data (me->undo, cc);
4877 g_object_unref (me->undo);
4878 me->undo = NULL;
4880 return FALSE;
4883 static gboolean
4884 cmd_resize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4886 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4887 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4888 GSList *l;
4890 for (l = me->sheets; l; l = l->next) {
4891 Sheet *sheet = l->data;
4892 gboolean err;
4893 GOUndo *u = gnm_sheet_resize (sheet, me->cols, me->rows,
4894 cc, &err);
4895 me->undo = go_undo_combine (me->undo, u);
4897 if (err) {
4898 if (me->undo)
4899 go_undo_undo_with_data (me->undo, cc);
4900 return TRUE;
4904 return FALSE;
4907 static void
4908 cmd_resize_sheets_finalize (GObject *cmd)
4910 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4912 g_slist_free (me->sheets);
4913 if (me->undo) {
4914 g_object_unref (me->undo);
4915 me->undo = NULL;
4918 gnm_command_finalize (cmd);
4922 * cmd_resize_sheets:
4923 * @wbc: #WorkbookControl
4924 * @sheets: (element-type Sheet) (transfer container): the sheets to resize.
4925 * @cols: new columns number.
4926 * @rows: new rows number.
4928 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4930 gboolean
4931 cmd_resize_sheets (WorkbookControl *wbc,
4932 GSList *sheets,
4933 int cols, int rows)
4935 CmdResizeSheets *me;
4937 me = g_object_new (CMD_RESIZE_SHEETS_TYPE, NULL);
4938 me->sheets = sheets;
4939 me->cols = cols;
4940 me->rows = rows;
4941 me->cmd.sheet = sheets ? sheets->data : NULL;
4942 me->cmd.size = 1;
4943 me->cmd.cmd_descriptor = g_strdup (_("Resizing sheet"));
4945 if (sheets &&
4946 gnm_sheet_valid_size (cols, rows))
4947 return gnm_command_push_undo (wbc, G_OBJECT (me));
4949 /* No change. */
4950 g_object_unref (me);
4951 return FALSE;
4954 /******************************************************************/
4956 #define CMD_SET_COMMENT_TYPE (cmd_set_comment_get_type ())
4957 #define CMD_SET_COMMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SET_COMMENT_TYPE, CmdSetComment))
4959 typedef struct {
4960 GnmCommand cmd;
4962 Sheet *sheet;
4963 GnmCellPos pos;
4964 gchar *new_text;
4965 gchar *old_text;
4966 gchar *new_author;
4967 gchar *old_author;
4968 PangoAttrList *old_attributes;
4969 PangoAttrList *new_attributes;
4970 } CmdSetComment;
4972 MAKE_GNM_COMMAND (CmdSetComment, cmd_set_comment, NULL)
4974 static gboolean
4975 cmd_set_comment_apply (Sheet *sheet, GnmCellPos *pos,
4976 char const *text, PangoAttrList *attributes,
4977 char const *author)
4979 GnmComment *comment;
4980 Workbook *wb = sheet->workbook;
4982 comment = sheet_get_comment (sheet, pos);
4983 if (comment) {
4984 if (text)
4985 g_object_set (G_OBJECT (comment), "text", text,
4986 "author", author,
4987 "markup", attributes, NULL);
4988 else {
4989 GnmRange const *mr;
4991 mr = gnm_sheet_merge_contains_pos (sheet, pos);
4993 if (mr)
4994 sheet_objects_clear (sheet, mr, GNM_CELL_COMMENT_TYPE, NULL);
4995 else {
4996 GnmRange r;
4997 r.start = r.end = *pos;
4998 sheet_objects_clear (sheet, &r, GNM_CELL_COMMENT_TYPE, NULL);
5001 } else if (text && (strlen (text) > 0)) {
5002 cell_set_comment (sheet, pos, author, text, attributes);
5004 sheet_mark_dirty (sheet);
5006 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
5007 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
5009 return FALSE;
5012 static gboolean
5013 cmd_set_comment_undo (GnmCommand *cmd,
5014 G_GNUC_UNUSED WorkbookControl *wbc)
5016 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5018 return cmd_set_comment_apply (me->sheet, &me->pos,
5019 me->old_text, me->old_attributes,
5020 me->old_author);
5023 static gboolean
5024 cmd_set_comment_redo (GnmCommand *cmd,
5025 G_GNUC_UNUSED WorkbookControl *wbc)
5027 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5029 return cmd_set_comment_apply (me->sheet, &me->pos,
5030 me->new_text, me->new_attributes,
5031 me->new_author);
5034 static void
5035 cmd_set_comment_finalize (GObject *cmd)
5037 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5039 g_free (me->new_text);
5040 me->new_text = NULL;
5042 g_free (me->old_text);
5043 me->old_text = NULL;
5045 g_free (me->new_author);
5046 me->new_author = NULL;
5048 g_free (me->old_author);
5049 me->old_author = NULL;
5051 if (me->old_attributes != NULL) {
5052 pango_attr_list_unref (me->old_attributes);
5053 me->old_attributes = NULL;
5056 if (me->new_attributes != NULL) {
5057 pango_attr_list_unref (me->new_attributes);
5058 me->new_attributes = NULL;
5061 gnm_command_finalize (cmd);
5064 gboolean
5065 cmd_set_comment (WorkbookControl *wbc,
5066 Sheet *sheet, GnmCellPos const *pos,
5067 char const *new_text,
5068 PangoAttrList *attr,
5069 char const *new_author)
5071 CmdSetComment *me;
5072 GnmComment *comment;
5073 char *where;
5075 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5076 g_return_val_if_fail (new_text != NULL, TRUE);
5078 me = g_object_new (CMD_SET_COMMENT_TYPE, NULL);
5080 me->cmd.sheet = sheet;
5081 me->cmd.size = 1;
5082 if (strlen (new_text) < 1)
5083 me->new_text = NULL;
5084 else
5085 me->new_text = g_strdup (new_text);
5086 if (strlen (new_author) < 1)
5087 me->new_author = NULL;
5088 else
5089 me->new_author = g_strdup (new_author);
5090 if (attr != NULL)
5091 pango_attr_list_ref (attr);
5092 me->new_attributes = attr;
5093 where = undo_cell_pos_name (sheet, pos);
5094 me->cmd.cmd_descriptor =
5095 g_strdup_printf (me->new_text == NULL ?
5096 _("Clearing comment of %s") :
5097 _("Setting comment of %s"),
5098 where);
5099 g_free (where);
5100 me->old_text = NULL;
5101 me->old_author = NULL;
5102 me->old_attributes = NULL;
5103 me->pos = *pos;
5104 me->sheet = sheet;
5105 comment = sheet_get_comment (sheet, pos);
5106 if (comment) {
5107 g_object_get (G_OBJECT (comment),
5108 "text", &(me->old_text),
5109 "author", &(me->old_author),
5110 "markup", &(me->old_attributes), NULL);
5111 if (me->old_attributes != NULL)
5112 pango_attr_list_ref (me->old_attributes);
5113 me->old_text = g_strdup (me->old_text);
5114 me->old_author = g_strdup (me->old_author);
5117 /* Register the command object */
5118 return gnm_command_push_undo (wbc, G_OBJECT (me));
5121 /******************************************************************/
5123 #define CMD_ANALYSIS_TOOL_TYPE (cmd_analysis_tool_get_type ())
5124 #define CMD_ANALYSIS_TOOL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ANALYSIS_TOOL_TYPE, CmdAnalysis_Tool))
5126 typedef struct {
5127 GnmCommand cmd;
5129 data_analysis_output_t *dao;
5130 gpointer specs;
5131 gboolean specs_owned;
5132 analysis_tool_engine engine;
5133 data_analysis_output_type_t type;
5135 ColRowStateList *col_info;
5136 ColRowStateList *row_info;
5137 GnmRange old_range;
5138 GnmCellRegion *old_contents;
5139 GSList *newSheetObjects;
5140 } CmdAnalysis_Tool;
5142 MAKE_GNM_COMMAND (CmdAnalysis_Tool, cmd_analysis_tool, NULL)
5144 static gboolean
5145 cmd_analysis_tool_undo (GnmCommand *cmd, WorkbookControl *wbc)
5147 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5148 GnmPasteTarget pt;
5150 g_return_val_if_fail (me != NULL, TRUE);
5152 /* The old view might not exist anymore */
5153 me->dao->wbc = wbc;
5155 switch (me->type) {
5156 case NewSheetOutput:
5157 if (!command_undo_sheet_delete (me->dao->sheet))
5158 return TRUE;
5159 me->dao->sheet = NULL;
5160 break;
5161 case NewWorkbookOutput:
5162 g_warning ("How did we get here?");
5163 return TRUE;
5164 break;
5165 case RangeOutput:
5166 default:
5167 sheet_clear_region (me->dao->sheet,
5168 me->old_range.start.col, me->old_range.start.row,
5169 me->old_range.end.col, me->old_range.end.row,
5170 CLEAR_COMMENTS | CLEAR_FORMATS | CLEAR_NOCHECKARRAY |
5171 CLEAR_RECALC_DEPS | CLEAR_VALUES | CLEAR_MERGES,
5172 GO_CMD_CONTEXT (wbc));
5173 clipboard_paste_region (me->old_contents,
5174 paste_target_init (&pt, me->dao->sheet, &me->old_range, PASTE_ALL_SHEET),
5175 GO_CMD_CONTEXT (wbc));
5176 cellregion_unref (me->old_contents);
5177 me->old_contents = NULL;
5178 if (me->col_info) {
5179 dao_set_colrow_state_list (me->dao, TRUE, me->col_info);
5180 me->col_info = colrow_state_list_destroy (me->col_info);
5182 if (me->row_info) {
5183 dao_set_colrow_state_list (me->dao, FALSE, me->row_info);
5184 me->row_info = colrow_state_list_destroy (me->row_info);
5186 if (me->newSheetObjects == NULL)
5187 me->newSheetObjects = dao_surrender_so (me->dao);
5188 g_slist_foreach (me->newSheetObjects, (GFunc)sheet_object_clear_sheet, NULL);
5189 sheet_update (me->dao->sheet);
5192 return FALSE;
5195 static void
5196 cmd_analysis_tool_draw_old_so (SheetObject *so, data_analysis_output_t *dao)
5198 g_object_ref (so);
5199 dao_set_sheet_object (dao, 0, 1, so);
5202 static gboolean
5203 cmd_analysis_tool_redo (GnmCommand *cmd, WorkbookControl *wbc)
5205 gpointer continuity = NULL;
5206 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5207 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5209 g_return_val_if_fail (me != NULL, TRUE);
5211 /* The old view might not exist anymore */
5212 me->dao->wbc = wbc;
5214 if (me->col_info)
5215 me->col_info = colrow_state_list_destroy (me->col_info);
5216 me->col_info = dao_get_colrow_state_list (me->dao, TRUE);
5217 if (me->row_info)
5218 me->row_info = colrow_state_list_destroy (me->row_info);
5219 me->row_info = dao_get_colrow_state_list (me->dao, FALSE);
5221 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PREPARE_OUTPUT_RANGE, NULL)
5222 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5223 &me->cmd.cmd_descriptor)
5224 || cmd_dao_is_locked_effective (me->dao, wbc, me->cmd.cmd_descriptor)
5225 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_LAST_VALIDITY_CHECK, &continuity))
5226 return TRUE;
5228 switch (me->type) {
5229 case NewSheetOutput:
5230 me->old_contents = NULL;
5231 break;
5232 case NewWorkbookOutput:
5233 /* No undo in this case (see below) */
5234 me->old_contents = NULL;
5235 break;
5236 case RangeOutput:
5237 default:
5238 range_init (&me->old_range, me->dao->start_col, me->dao->start_row,
5239 me->dao->start_col + me->dao->cols - 1,
5240 me->dao->start_row + me->dao->rows - 1);
5241 me->old_contents = clipboard_copy_range (me->dao->sheet, &me->old_range);
5242 break;
5245 if (me->newSheetObjects != NULL)
5246 dao_set_omit_so (me->dao, TRUE);
5248 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_FORMAT_OUTPUT_RANGE, NULL))
5249 return TRUE;
5251 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PERFORM_CALC, &continuity)) {
5252 if (me->type == RangeOutput) {
5253 g_warning ("This is too late for failure! The target region has "
5254 "already been formatted!");
5255 } else
5256 return TRUE;
5258 if (me->newSheetObjects != NULL)
5260 GSList *l = g_slist_reverse
5261 (g_slist_copy (me->newSheetObjects));
5263 dao_set_omit_so (me->dao, FALSE);
5264 g_slist_foreach (l,
5265 (GFunc) cmd_analysis_tool_draw_old_so,
5266 me->dao);
5267 g_slist_free (l);
5270 if (continuity) {
5271 g_warning ("There shouldn't be any data left in here!");
5274 dao_autofit_columns (me->dao);
5275 sheet_mark_dirty (me->dao->sheet);
5276 sheet_update (me->dao->sheet);
5278 /* The concept of an undo if we create a new worksheet is extremely strange,
5279 * since we have separate undo/redo queues per worksheet.
5280 * Users can simply delete the worksheet if they so desire.
5283 return (me->type == NewWorkbookOutput);
5286 static void
5287 cmd_analysis_tool_finalize (GObject *cmd)
5289 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5291 if (me->col_info)
5292 me->col_info = colrow_state_list_destroy (me->col_info);
5293 if (me->row_info)
5294 me->row_info = colrow_state_list_destroy (me->row_info);
5296 me->engine (NULL, me->dao, me->specs, TOOL_ENGINE_CLEAN_UP, NULL);
5298 if (me->specs_owned) {
5299 g_free (me->specs);
5300 dao_free (me->dao);
5302 if (me->old_contents)
5303 cellregion_unref (me->old_contents);
5305 g_slist_free_full (me->newSheetObjects, g_object_unref);
5307 gnm_command_finalize (cmd);
5311 * cmd_analysis_tool: (skip)
5312 * Note: this takes ownership of specs and dao if the command
5313 * succeeds.
5315 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5317 gboolean
5318 cmd_analysis_tool (WorkbookControl *wbc, G_GNUC_UNUSED Sheet *sheet,
5319 data_analysis_output_t *dao, gpointer specs,
5320 analysis_tool_engine engine, gboolean always_take_ownership)
5322 CmdAnalysis_Tool *me;
5323 gboolean trouble;
5324 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5326 g_return_val_if_fail (dao != NULL, TRUE);
5327 g_return_val_if_fail (specs != NULL, TRUE);
5328 g_return_val_if_fail (engine != NULL, TRUE);
5330 me = g_object_new (CMD_ANALYSIS_TOOL_TYPE, NULL);
5332 dao->wbc = wbc;
5334 /* Store the specs for the object */
5335 me->specs = specs;
5336 me->specs_owned = always_take_ownership;
5337 me->dao = dao;
5338 me->engine = engine;
5339 me->cmd.cmd_descriptor = NULL;
5340 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DAO, NULL)) {
5341 g_object_unref (me);
5342 return TRUE;
5344 me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5345 &me->cmd.cmd_descriptor);
5346 me->cmd.sheet = NULL;
5347 me->type = dao->type;
5348 me->row_info = NULL;
5349 me->col_info = NULL;
5351 /* We divide by 2 since many cells will be empty*/
5352 me->cmd.size = 1 + dao->rows * dao->cols / 2;
5354 /* Register the command object */
5355 trouble = gnm_command_push_undo (wbc, G_OBJECT (me));
5357 if (!trouble)
5358 me->specs_owned = TRUE;
5360 return trouble;
5363 /******************************************************************/
5365 #define CMD_MERGE_DATA_TYPE (cmd_merge_data_get_type ())
5366 #define CMD_MERGE_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_DATA_TYPE, CmdMergeData))
5368 typedef struct {
5369 GnmCommand cmd;
5370 GnmValue *merge_zone;
5371 GSList *merge_fields;
5372 GSList *merge_data;
5373 GSList *sheet_list;
5374 Sheet *sheet;
5375 gint n;
5376 } CmdMergeData;
5378 MAKE_GNM_COMMAND (CmdMergeData, cmd_merge_data, NULL)
5380 static void
5381 cmd_merge_data_delete_sheets (gpointer data, gpointer success)
5383 Sheet *sheet = data;
5385 if (!command_undo_sheet_delete (sheet))
5386 *(gboolean *)success = FALSE;
5389 static gboolean
5390 cmd_merge_data_undo (GnmCommand *cmd,
5391 G_GNUC_UNUSED WorkbookControl *wbc)
5393 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5394 gboolean success = TRUE;
5396 g_slist_foreach (me->sheet_list, cmd_merge_data_delete_sheets, &success);
5397 g_slist_free (me->sheet_list);
5398 me->sheet_list = NULL;
5400 return FALSE;
5403 static gboolean
5404 cmd_merge_data_redo (GnmCommand *cmd, WorkbookControl *wbc)
5406 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5407 int i;
5408 GnmCellRegion *merge_contents;
5409 GnmRangeRef *cell = &me->merge_zone->v_range.cell;
5410 GnmPasteTarget pt;
5411 GSList *this_field = me->merge_fields;
5412 GSList *this_data = me->merge_data;
5413 Sheet *source_sheet = cell->a.sheet;
5414 GSList *target_sheet;
5415 GnmRange target_range;
5416 ColRowStateList *state_col;
5417 ColRowStateList *state_row;
5419 range_init (&target_range, cell->a.col, cell->a.row,
5420 cell->b.col, cell->b.row);
5421 merge_contents = clipboard_copy_range (source_sheet, &target_range);
5422 state_col = colrow_get_states (source_sheet, TRUE, target_range.start.col,
5423 target_range.end.col);
5424 state_row = colrow_get_states (source_sheet, FALSE, target_range.start.row,
5425 target_range.end.row);
5427 for (i = 0; i < me->n; i++) {
5428 Sheet *new_sheet;
5430 new_sheet = workbook_sheet_add (me->sheet->workbook, -1,
5431 gnm_sheet_get_max_cols (me->sheet),
5432 gnm_sheet_get_max_rows (me->sheet));
5433 me->sheet_list = g_slist_prepend (me->sheet_list, new_sheet);
5435 colrow_set_states (new_sheet, TRUE, target_range.start.col, state_col);
5436 colrow_set_states (new_sheet, FALSE, target_range.start.row, state_row);
5437 sheet_objects_dup (source_sheet, new_sheet, &target_range);
5438 clipboard_paste_region (merge_contents,
5439 paste_target_init (&pt, new_sheet, &target_range, PASTE_ALL_SHEET),
5440 GO_CMD_CONTEXT (wbc));
5442 cellregion_unref (merge_contents);
5443 me->sheet_list = g_slist_reverse (me->sheet_list);
5444 colrow_state_list_destroy (state_col);
5445 colrow_state_list_destroy (state_row);
5447 while (this_field) {
5448 int col_source, row_source;
5449 int col_target, row_target;
5451 g_return_val_if_fail (this_data != NULL, TRUE);
5452 cell = &((GnmValue *)this_field->data)->v_range.cell;
5453 col_target = cell->a.col;
5454 row_target = cell->a.row;
5456 cell = &((GnmValue *)this_data->data)->v_range.cell;
5457 col_source = cell->a.col;
5458 row_source = cell->a.row;
5459 source_sheet = cell->a.sheet;
5461 target_sheet = me->sheet_list;
5462 while (target_sheet) {
5463 GnmCell *source_cell = sheet_cell_get (source_sheet,
5464 col_source, row_source);
5465 if (source_cell == NULL) {
5466 GnmCell *target_cell = sheet_cell_get ((Sheet *)target_sheet->data,
5467 col_target, row_target);
5468 if (target_cell != NULL)
5469 gnm_cell_set_value (target_cell,
5470 value_new_empty ());
5471 } else {
5472 GnmCell *target_cell = sheet_cell_fetch ((Sheet *)target_sheet->data,
5473 col_target, row_target);
5474 gnm_cell_set_value (target_cell,
5475 value_dup (source_cell->value));
5477 target_sheet = target_sheet->next;
5478 row_source++;
5481 this_field = this_field->next;
5482 this_data = this_data->next;
5485 return FALSE;
5488 static void
5489 cmd_merge_data_finalize (GObject *cmd)
5491 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5493 value_release (me->merge_zone);
5494 me->merge_zone = NULL;
5495 range_list_destroy (me->merge_data);
5496 me->merge_data = NULL;
5497 range_list_destroy (me->merge_fields);
5498 me->merge_fields = NULL;
5499 g_slist_free (me->sheet_list);
5500 me->sheet_list = NULL;
5501 me->n = 0;
5503 gnm_command_finalize (cmd);
5507 * cmd_merge_data:
5508 * @wbc: #WorkbookControl
5509 * @sheet: #Sheet
5510 * @merge_zone: (transfer full): #GnmValue
5511 * @merge_fields: (element-type GnmRange) (transfer full):
5512 * @merge_data: (element-type GnmRange) (transfer full):
5514 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5516 gboolean
5517 cmd_merge_data (WorkbookControl *wbc, Sheet *sheet,
5518 GnmValue *merge_zone, GSList *merge_fields, GSList *merge_data)
5520 CmdMergeData *me;
5521 GnmRangeRef *cell;
5523 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5524 g_return_val_if_fail (merge_zone != NULL, TRUE);
5525 g_return_val_if_fail (merge_fields != NULL, TRUE);
5526 g_return_val_if_fail (merge_data != NULL, TRUE);
5528 me = g_object_new (CMD_MERGE_DATA_TYPE, NULL);
5530 me->cmd.sheet = sheet;
5531 me->sheet = sheet;
5532 me->cmd.size = 1 + g_slist_length (merge_fields);
5533 me->cmd.cmd_descriptor =
5534 g_strdup_printf (_("Merging data into %s"), value_peek_string (merge_zone));
5536 me->merge_zone = merge_zone;
5537 me->merge_fields = merge_fields;
5538 me->merge_data = merge_data;
5539 me->sheet_list = NULL;
5541 cell = &((GnmValue *)merge_data->data)->v_range.cell;
5542 me->n = cell->b.row - cell->a.row + 1;
5544 /* Register the command object */
5545 return gnm_command_push_undo (wbc, G_OBJECT (me));
5548 /******************************************************************/
5550 #define CMD_CHANGE_META_DATA_TYPE (cmd_change_summary_get_type ())
5551 #define CMD_CHANGE_META_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_CHANGE_META_DATA_TYPE, CmdChangeMetaData))
5553 typedef struct {
5554 GnmCommand cmd;
5555 GSList *changed_props;
5556 GSList *removed_names;
5557 } CmdChangeMetaData;
5559 MAKE_GNM_COMMAND (CmdChangeMetaData, cmd_change_summary, NULL)
5561 static gboolean
5562 cmd_change_summary_undo (GnmCommand *cmd, WorkbookControl *wbc)
5564 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5565 GsfDocMetaData *meta = go_doc_get_meta_data (wb_control_get_doc (wbc));
5566 GSList *ptr, *old_vals = NULL, *dropped = NULL;
5567 GsfDocProp *prop;
5568 char const *name;
5570 for (ptr = me->removed_names; ptr != NULL ; ptr = ptr->next) {
5571 if (NULL != (prop = gsf_doc_meta_data_steal (meta, ptr->data)))
5572 old_vals = g_slist_prepend (old_vals, prop);
5573 g_free (ptr->data);
5575 g_slist_free (me->removed_names);
5577 for (ptr = me->changed_props; ptr != NULL ; ptr = ptr->next) {
5578 name = gsf_doc_prop_get_name (ptr->data);
5579 if (NULL != (prop = gsf_doc_meta_data_steal (meta, name)))
5580 old_vals = g_slist_prepend (old_vals, prop);
5581 else
5582 dropped = g_slist_prepend (old_vals, g_strdup (name));
5583 gsf_doc_meta_data_store (meta, ptr->data);
5585 g_slist_free (me->changed_props);
5587 me->removed_names = dropped;
5588 me->changed_props = old_vals;
5589 go_doc_update_meta_data (wb_control_get_doc (wbc));
5591 return FALSE;
5594 static gboolean
5595 cmd_change_summary_redo (GnmCommand *cmd, WorkbookControl *wbc)
5597 return cmd_change_summary_undo (cmd, wbc);
5600 static void
5601 cmd_change_summary_finalize (GObject *cmd)
5603 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5605 g_slist_free_full (me->changed_props, (GDestroyNotify)gsf_doc_prop_free);
5606 me->changed_props = NULL;
5607 g_slist_free_full (me->removed_names, g_free);
5608 me->removed_names = NULL;
5610 gnm_command_finalize (cmd);
5614 * cmd_change_meta_data:
5615 * @wbc: #WorkbookControl
5616 * @changes: (element-type GsfDocMetaData) (transfer full): the changed metadata.
5617 * @removed: (element-type GsfDocMetaData) (transfer full): the removed metadata.
5619 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5621 gboolean
5622 cmd_change_meta_data (WorkbookControl *wbc, GSList *changes, GSList *removed)
5624 CmdChangeMetaData *me = g_object_new (CMD_CHANGE_META_DATA_TYPE, NULL);
5626 me->changed_props = changes;
5627 me->removed_names = removed;
5628 me->cmd.sheet = NULL;
5630 me->cmd.size = g_slist_length (changes) + g_slist_length (removed);
5631 me->cmd.cmd_descriptor = g_strdup_printf (
5632 _("Changing workbook properties"));
5633 return gnm_command_push_undo (wbc, G_OBJECT (me));
5636 /******************************************************************/
5638 #define CMD_OBJECT_RAISE_TYPE (cmd_object_raise_get_type ())
5639 #define CMD_OBJECT_RAISE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECT_RAISE_TYPE, CmdObjectRaise))
5641 typedef struct {
5642 GnmCommand cmd;
5643 SheetObject *so;
5644 CmdObjectRaiseSelector dir;
5645 gint changed_positions;
5646 } CmdObjectRaise;
5648 MAKE_GNM_COMMAND (CmdObjectRaise, cmd_object_raise, NULL)
5650 static gboolean
5651 cmd_object_raise_redo (GnmCommand *cmd,
5652 G_GNUC_UNUSED WorkbookControl *wbc)
5654 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5655 switch (me->dir) {
5656 case cmd_object_pull_to_front:
5657 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MAXINT/2);
5658 break;
5659 case cmd_object_pull_forward:
5660 me->changed_positions = sheet_object_adjust_stacking (me->so, 1);
5661 break;
5662 case cmd_object_push_backward:
5663 me->changed_positions = sheet_object_adjust_stacking (me->so, -1);
5664 break;
5665 case cmd_object_push_to_back:
5666 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MININT/2);
5667 break;
5669 return FALSE;
5672 static gboolean
5673 cmd_object_raise_undo (GnmCommand *cmd,
5674 G_GNUC_UNUSED WorkbookControl *wbc)
5676 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5677 if (me->changed_positions != 0)
5678 sheet_object_adjust_stacking (me->so, - me->changed_positions);
5679 return FALSE;
5682 static void
5683 cmd_object_raise_finalize (GObject *cmd)
5685 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5686 g_object_unref (me->so);
5687 gnm_command_finalize (cmd);
5690 gboolean
5691 cmd_object_raise (WorkbookControl *wbc, SheetObject *so, CmdObjectRaiseSelector dir)
5693 CmdObjectRaise *me;
5695 g_return_val_if_fail (GNM_IS_SO (so), TRUE);
5697 me = g_object_new (CMD_OBJECT_RAISE_TYPE, NULL);
5699 me->so = so;
5700 g_object_ref (so);
5702 me->cmd.sheet = sheet_object_get_sheet (so);
5703 me->cmd.size = 1;
5704 switch (dir) {
5705 case cmd_object_pull_to_front:
5706 me->cmd.cmd_descriptor = g_strdup (_("Pull Object to the Front"));
5707 break;
5708 case cmd_object_pull_forward:
5709 me->cmd.cmd_descriptor = g_strdup (_("Pull Object Forward"));
5710 break;
5711 case cmd_object_push_backward:
5712 me->cmd.cmd_descriptor = g_strdup (_("Push Object Backward"));
5713 break;
5714 case cmd_object_push_to_back:
5715 me->cmd.cmd_descriptor = g_strdup (_("Push Object to the Back"));
5716 break;
5718 me->dir = dir;
5719 me->changed_positions = 0;
5721 return gnm_command_push_undo (wbc, G_OBJECT (me));
5724 /******************************************************************/
5726 #define CMD_PRINT_SETUP_TYPE (cmd_print_setup_get_type ())
5727 #define CMD_PRINT_SETUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PRINT_SETUP_TYPE, CmdPrintSetup))
5729 typedef struct {
5730 GnmCommand cmd;
5732 GSList *old_pi;
5733 GnmPrintInformation *new_pi;
5734 } CmdPrintSetup;
5736 MAKE_GNM_COMMAND (CmdPrintSetup, cmd_print_setup, NULL)
5738 static void
5739 update_sheet_graph_cb (Sheet *sheet)
5741 g_return_if_fail (IS_SHEET (sheet) && sheet->sheet_type == GNM_SHEET_OBJECT);
5743 sheet_object_graph_ensure_size (GNM_SO (sheet->sheet_objects->data));
5746 static gboolean
5747 cmd_print_setup_undo (GnmCommand *cmd, WorkbookControl *wbc)
5749 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5750 guint n, i;
5751 Workbook *book;
5752 GSList *infos;
5754 g_return_val_if_fail (me->old_pi != NULL, TRUE);
5756 if (me->cmd.sheet) {
5757 GnmPrintInformation *pi = me->old_pi->data;
5758 gnm_print_info_free (me->cmd.sheet->print_info);
5759 me->cmd.sheet->print_info = gnm_print_info_dup (pi);
5760 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5761 update_sheet_graph_cb (me->cmd.sheet);
5762 } else {
5763 book = wb_control_get_workbook(wbc);
5764 n = workbook_sheet_count (book);
5765 infos = me->old_pi;
5766 g_return_val_if_fail (g_slist_length (infos) == n, TRUE);
5768 for (i = 0 ; i < n ; i++) {
5769 GnmPrintInformation *pi = infos->data;
5770 Sheet *sheet = workbook_sheet_by_index (book, i);
5772 g_return_val_if_fail (infos != NULL, TRUE);
5774 gnm_print_info_free (sheet->print_info);
5775 sheet->print_info = gnm_print_info_dup (pi);
5776 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5777 update_sheet_graph_cb (sheet);
5778 infos = infos->next;
5781 return FALSE;
5784 static gboolean
5785 cmd_print_setup_redo (GnmCommand *cmd, WorkbookControl *wbc)
5787 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5788 int n, i;
5789 Workbook *book;
5790 gboolean save_pis = (me->old_pi == NULL);
5792 if (me->cmd.sheet) {
5793 if (save_pis)
5794 me->old_pi = g_slist_append (me->old_pi, me->cmd.sheet->print_info);
5795 else
5796 gnm_print_info_free (me->cmd.sheet->print_info);
5797 me->cmd.sheet->print_info = gnm_print_info_dup (me->new_pi);
5798 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5799 update_sheet_graph_cb (me->cmd.sheet);
5800 } else {
5801 book = wb_control_get_workbook(wbc);
5802 n = workbook_sheet_count (book);
5803 for (i = 0 ; i < n ; i++) {
5804 Sheet *sheet = workbook_sheet_by_index (book, i);
5805 sheet_mark_dirty (sheet);
5806 if (save_pis)
5807 me->old_pi = g_slist_prepend (me->old_pi, sheet->print_info);
5808 else
5809 gnm_print_info_free (sheet->print_info);
5810 sheet->print_info = gnm_print_info_dup (me->new_pi);
5811 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5812 update_sheet_graph_cb (sheet);
5814 if (save_pis)
5815 me->old_pi = g_slist_reverse (me->old_pi);
5817 return FALSE;
5820 static void
5821 cmd_print_setup_finalize (GObject *cmd)
5823 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5824 GSList *list = me->old_pi;
5826 if (me->new_pi)
5827 gnm_print_info_free (me->new_pi);
5828 for (; list; list = list->next)
5829 gnm_print_info_free ((GnmPrintInformation *) list->data);
5830 g_slist_free (me->old_pi);
5831 gnm_command_finalize (cmd);
5834 gboolean
5835 cmd_print_setup (WorkbookControl *wbc, Sheet *sheet, GnmPrintInformation const *pi)
5837 CmdPrintSetup *me;
5839 me = g_object_new (CMD_PRINT_SETUP_TYPE, NULL);
5841 me->cmd.sheet = sheet;
5842 me->cmd.size = 10;
5843 if (sheet)
5844 me->cmd.cmd_descriptor =
5845 g_strdup_printf (_("Page Setup For %s"), sheet->name_unquoted);
5846 else
5847 me->cmd.cmd_descriptor = g_strdup (_("Page Setup For All Sheets"));
5848 me->old_pi = NULL;
5849 me->new_pi = gnm_print_info_dup (pi);
5851 return gnm_command_push_undo (wbc, G_OBJECT (me));
5854 /******************************************************************/
5856 #define CMD_DEFINE_NAME_TYPE (cmd_define_name_get_type ())
5857 #define CMD_DEFINE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DEFINE_NAME_TYPE, CmdDefineName))
5859 typedef struct {
5860 GnmCommand cmd;
5862 GnmParsePos pp;
5863 char *name;
5864 GnmExprTop const *texpr;
5865 gboolean new_name;
5866 gboolean placeholder;
5867 } CmdDefineName;
5869 MAKE_GNM_COMMAND (CmdDefineName, cmd_define_name, NULL)
5871 static gboolean
5872 cmd_define_name_undo (GnmCommand *cmd,
5873 WorkbookControl *wbc)
5875 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5876 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5877 GnmExprTop const *texpr = nexpr->texpr;
5879 gnm_expr_top_ref (texpr);
5880 if (me->new_name)
5881 expr_name_remove (nexpr);
5882 else if (me->placeholder)
5883 expr_name_downgrade_to_placeholder (nexpr);
5884 else
5885 expr_name_set_expr (nexpr, me->texpr); /* restore old def */
5887 me->texpr = texpr;
5889 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5890 wb_view_menus_update (each_wbv);
5892 return FALSE;
5895 static gboolean
5896 cmd_define_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
5898 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5899 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5901 me->new_name = (nexpr == NULL);
5902 me->placeholder = (nexpr != NULL)
5903 && expr_name_is_placeholder (nexpr);
5905 if (me->new_name || me->placeholder) {
5906 char *err = NULL;
5907 nexpr = expr_name_add (&me->pp, me->name, me->texpr, &err, TRUE, NULL);
5908 if (nexpr == NULL) {
5909 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), err);
5910 g_free (err);
5911 return TRUE;
5913 me->texpr = NULL;
5914 } else { /* changing the definition */
5915 GnmExprTop const *tmp = nexpr->texpr;
5916 gnm_expr_top_ref (tmp);
5917 expr_name_set_expr (nexpr, me->texpr);
5918 me->texpr = tmp; /* store the old definition */
5920 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5921 wb_view_menus_update (each_wbv);
5924 return FALSE;
5927 static void
5928 cmd_define_name_finalize (GObject *cmd)
5930 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5932 g_free (me->name); me->name = NULL;
5934 if (me->texpr) {
5935 gnm_expr_top_unref (me->texpr);
5936 me->texpr = NULL;
5939 gnm_command_finalize (cmd);
5943 * cmd_define_name:
5944 * @wbc:
5945 * @name:
5946 * @pp:
5947 * @texpr: (transfer full): #GnmExprTop
5948 * @descriptor: optional descriptor.
5950 * If the @name has never been defined in context @pp create a new name
5951 * If its a placeholder assign @texpr to it and make it real
5952 * If it already exists as a real name just assign @expr.
5954 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5956 gboolean
5957 cmd_define_name (WorkbookControl *wbc, char const *name,
5958 GnmParsePos const *pp, GnmExprTop const *texpr,
5959 char const *descriptor)
5961 CmdDefineName *me;
5962 GnmNamedExpr *nexpr;
5963 Sheet *sheet;
5965 g_return_val_if_fail (name != NULL, TRUE);
5966 g_return_val_if_fail (pp != NULL, TRUE);
5967 g_return_val_if_fail (texpr != NULL, TRUE);
5969 if (name[0] == '\0') {
5970 go_cmd_context_error_invalid
5971 (GO_CMD_CONTEXT (wbc), _("Defined Name"),
5972 _("An empty string is not allowed as defined name."));
5973 gnm_expr_top_unref (texpr);
5974 return TRUE;
5977 sheet = wb_control_cur_sheet (wbc);
5978 if (!expr_name_validate (name)) {
5979 gchar *err = g_strdup_printf
5980 (_("'%s' is not allowed as defined name."), name);
5981 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
5982 _("Defined Name"), err);
5983 g_free (err);
5984 gnm_expr_top_unref (texpr);
5985 return TRUE;
5988 if (expr_name_check_for_loop (name, texpr)) {
5989 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), name,
5990 _("has a circular reference"));
5991 gnm_expr_top_unref (texpr);
5992 return TRUE;
5994 nexpr = expr_name_lookup (pp, name);
5995 if (nexpr != NULL && !expr_name_is_placeholder (nexpr) &&
5996 gnm_expr_top_equal (texpr, nexpr->texpr)) {
5997 gnm_expr_top_unref (texpr);
5998 return FALSE; /* expr is not changing, do nothing */
6001 me = g_object_new (CMD_DEFINE_NAME_TYPE, NULL);
6002 me->name = g_strdup (name);
6003 me->pp = *pp;
6004 me->texpr = texpr;
6006 me->cmd.sheet = sheet;
6007 me->cmd.size = 1;
6009 if (descriptor == NULL) {
6010 char const *tmp;
6011 GString *res;
6013 /* Underscores need to be doubled. */
6014 res = g_string_new (NULL);
6015 for (tmp = name; *tmp; tmp++) {
6016 if (*tmp == '_')
6017 g_string_append_c (res, '_');
6018 g_string_append_c (res, *tmp);
6021 nexpr = expr_name_lookup (pp, name);
6022 if (nexpr == NULL || expr_name_is_placeholder (nexpr))
6023 me->cmd.cmd_descriptor =
6024 g_strdup_printf (_("Define Name %s"), res->str);
6025 else
6026 me->cmd.cmd_descriptor =
6027 g_strdup_printf (_("Update Name %s"), res->str);
6028 g_string_free (res, TRUE);
6029 } else
6030 me->cmd.cmd_descriptor = g_strdup (descriptor);
6032 return gnm_command_push_undo (wbc, G_OBJECT (me));
6035 /******************************************************************/
6037 #define CMD_REMOVE_NAME_TYPE (cmd_remove_name_get_type ())
6038 #define CMD_REMOVE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REMOVE_NAME_TYPE, CmdRemoveName))
6040 typedef struct {
6041 GnmCommand cmd;
6043 GnmParsePos pp;
6044 GnmNamedExpr *nexpr;
6045 const GnmExprTop *texpr;
6046 } CmdRemoveName;
6048 MAKE_GNM_COMMAND (CmdRemoveName, cmd_remove_name, NULL)
6050 static gboolean
6051 cmd_remove_name_undo (GnmCommand *cmd,
6052 G_GNUC_UNUSED WorkbookControl *wbc)
6054 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6055 GnmNamedExpr *nexpr =
6056 expr_name_add (&me->nexpr->pos, expr_name_name (me->nexpr),
6057 me->texpr, NULL, TRUE, NULL);
6058 if (nexpr) {
6059 me->texpr = NULL;
6060 expr_name_ref (nexpr);
6061 expr_name_unref (me->nexpr);
6062 me->nexpr = nexpr;
6063 return FALSE;
6064 } else {
6065 g_warning ("Redefining name failed.");
6066 return TRUE;
6070 static gboolean
6071 cmd_remove_name_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6073 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6075 me->texpr = me->nexpr->texpr;
6076 gnm_expr_top_ref (me->texpr);
6077 expr_name_downgrade_to_placeholder (me->nexpr);
6079 return FALSE;
6082 static void
6083 cmd_remove_name_finalize (GObject *cmd)
6085 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6087 expr_name_unref (me->nexpr);
6089 if (me->texpr) {
6090 gnm_expr_top_unref (me->texpr);
6091 me->texpr = NULL;
6094 gnm_command_finalize (cmd);
6098 * cmd_remove_name:
6099 * @wbc:
6100 * @nexpr: name to remove.
6102 * Returns TRUE on error
6104 gboolean
6105 cmd_remove_name (WorkbookControl *wbc, GnmNamedExpr *nexpr)
6107 CmdRemoveName *me;
6109 g_return_val_if_fail (wbc != NULL, TRUE);
6110 g_return_val_if_fail (nexpr != NULL, TRUE);
6111 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6113 expr_name_ref (nexpr);
6115 me = g_object_new (CMD_REMOVE_NAME_TYPE, NULL);
6116 me->nexpr = nexpr;
6117 me->texpr = NULL;
6118 me->cmd.sheet = wb_control_cur_sheet (wbc);
6119 me->cmd.size = 1;
6120 me->cmd.cmd_descriptor = g_strdup_printf (_("Remove Name %s"),
6121 expr_name_name (nexpr));
6123 return gnm_command_push_undo (wbc, G_OBJECT (me));
6126 /******************************************************************/
6128 #define CMD_RESCOPE_NAME_TYPE (cmd_rescope_name_get_type ())
6129 #define CMD_RESCOPE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESCOPE_NAME_TYPE, CmdRescopeName))
6131 typedef struct {
6132 GnmCommand cmd;
6133 GnmNamedExpr *nexpr;
6134 Sheet *scope;
6135 } CmdRescopeName;
6137 MAKE_GNM_COMMAND (CmdRescopeName, cmd_rescope_name, NULL)
6139 static gboolean
6140 cmd_rescope_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
6142 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6143 Sheet *old_scope = me->nexpr->pos.sheet;
6144 char *err;
6145 GnmParsePos pp = me->nexpr->pos;
6147 pp.sheet = me->scope;
6148 err = expr_name_set_pos (me->nexpr, &pp);
6150 if (err != NULL) {
6151 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Change Scope of Name"), err);
6152 g_free (err);
6153 return TRUE;
6156 me->scope = old_scope;
6157 return FALSE;
6160 static gboolean
6161 cmd_rescope_name_undo (GnmCommand *cmd, WorkbookControl *wbc)
6163 return cmd_rescope_name_redo (cmd, wbc);
6167 static void
6168 cmd_rescope_name_finalize (GObject *cmd)
6170 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6172 expr_name_unref (me->nexpr);
6173 gnm_command_finalize (cmd);
6177 * cmd_rescope_name:
6178 * @wbc:
6179 * @nexpr: name to rescope.
6180 * @scope:
6182 * Returns: %TRUE if there was a problem, %FALSE otherwise.
6184 gboolean
6185 cmd_rescope_name (WorkbookControl *wbc, GnmNamedExpr *nexpr, Sheet *scope)
6187 CmdRescopeName *me;
6189 g_return_val_if_fail (wbc != NULL, TRUE);
6190 g_return_val_if_fail (nexpr != NULL, TRUE);
6191 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6193 expr_name_ref (nexpr);
6195 me = g_object_new (CMD_RESCOPE_NAME_TYPE, NULL);
6196 me->nexpr = nexpr;
6197 me->scope = scope;
6198 me->cmd.sheet = wb_control_cur_sheet (wbc);
6199 me->cmd.size = 1;
6200 me->cmd.cmd_descriptor = g_strdup_printf (_("Change Scope of Name %s"),
6201 expr_name_name (nexpr));
6203 return gnm_command_push_undo (wbc, G_OBJECT (me));
6205 /******************************************************************/
6207 #define CMD_SCENARIO_ADD_TYPE (cmd_scenario_add_get_type ())
6208 #define CMD_SCENARIO_ADD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_ADD_TYPE, CmdScenarioAdd))
6210 typedef struct {
6211 GnmCommand cmd;
6212 GnmScenario *scenario;
6213 } CmdScenarioAdd;
6215 MAKE_GNM_COMMAND (CmdScenarioAdd, cmd_scenario_add, NULL)
6217 static gboolean
6218 cmd_scenario_add_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6220 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6221 GnmScenario *sc = g_object_ref (me->scenario);
6222 gnm_sheet_scenario_add (sc->sheet, sc);
6223 return FALSE;
6226 static gboolean
6227 cmd_scenario_add_undo (GnmCommand *cmd,
6228 G_GNUC_UNUSED WorkbookControl *wbc)
6230 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6231 GnmScenario *sc = me->scenario;
6232 gnm_sheet_scenario_remove (sc->sheet, sc);
6233 return FALSE;
6236 static void
6237 cmd_scenario_add_finalize (GObject *cmd)
6239 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6241 g_object_unref (me->scenario);
6242 gnm_command_finalize (cmd);
6246 * cmd_scenario_add: (skip)
6247 * @wbc:
6248 * @s: (transfer full):
6249 * @sheet:
6251 * Returns: %TRUE if there was a problem, %FALSE otherwise.
6253 gboolean
6254 cmd_scenario_add (WorkbookControl *wbc, GnmScenario *s, Sheet *sheet)
6256 CmdScenarioAdd *me;
6258 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6259 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6261 me = g_object_new (CMD_SCENARIO_ADD_TYPE, NULL);
6263 me->scenario = s; /* Take ownership */
6264 me->cmd.sheet = sheet;
6265 me->cmd.size = 1;
6266 me->cmd.cmd_descriptor = g_strdup (_("Add scenario"));
6268 return gnm_command_push_undo (wbc, G_OBJECT (me));
6271 /******************************************************************/
6273 #define CMD_SCENARIO_MNGR_TYPE (cmd_scenario_mngr_get_type ())
6274 #define CMD_SCENARIO_MNGR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_MNGR_TYPE, CmdScenarioMngr))
6276 typedef struct {
6277 GnmCommand cmd;
6278 GnmScenario *sc;
6279 GOUndo *undo;
6280 } CmdScenarioMngr;
6282 MAKE_GNM_COMMAND (CmdScenarioMngr, cmd_scenario_mngr, NULL)
6284 static gboolean
6285 cmd_scenario_mngr_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6287 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6288 if (!me->undo)
6289 me->undo = gnm_scenario_apply (me->sc);
6290 return FALSE;
6293 static gboolean
6294 cmd_scenario_mngr_undo (GnmCommand *cmd,
6295 G_GNUC_UNUSED WorkbookControl *wbc)
6297 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6298 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6299 g_object_unref (me->undo);
6300 me->undo = NULL;
6301 return FALSE;
6304 static void
6305 cmd_scenario_mngr_finalize (GObject *cmd)
6307 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6309 g_object_unref (me->sc);
6310 if (me->undo)
6311 g_object_unref (me->undo);
6313 gnm_command_finalize (cmd);
6316 gboolean
6317 cmd_scenario_mngr (WorkbookControl *wbc, GnmScenario *sc, GOUndo *undo)
6319 CmdScenarioMngr *me;
6321 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6322 g_return_val_if_fail (GNM_IS_SCENARIO (sc), TRUE);
6324 me = g_object_new (CMD_SCENARIO_MNGR_TYPE, NULL);
6326 me->sc = g_object_ref (sc);
6327 me->undo = g_object_ref (undo);
6328 me->cmd.sheet = sc->sheet;
6329 me->cmd.size = 1;
6330 me->cmd.cmd_descriptor = g_strdup (_("Scenario Show"));
6332 return gnm_command_push_undo (wbc, G_OBJECT (me));
6335 /******************************************************************/
6337 #define CMD_DATA_SHUFFLE_TYPE (cmd_data_shuffle_get_type ())
6338 #define CMD_DATA_SHUFFLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DATA_SHUFFLE_TYPE, CmdDataShuffle))
6340 typedef struct {
6341 GnmCommand cmd;
6342 data_shuffling_t *ds;
6343 } CmdDataShuffle;
6345 MAKE_GNM_COMMAND (CmdDataShuffle, cmd_data_shuffle, NULL)
6347 static gboolean
6348 cmd_data_shuffle_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6350 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6352 data_shuffling_redo (me->ds);
6353 return FALSE;
6356 static gboolean
6357 cmd_data_shuffle_undo (GnmCommand *cmd,
6358 G_GNUC_UNUSED WorkbookControl *wbc)
6360 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6362 data_shuffling_redo (me->ds);
6363 return FALSE;
6366 static void
6367 cmd_data_shuffle_finalize (GObject *cmd)
6369 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6371 data_shuffling_free (me->ds);
6372 gnm_command_finalize (cmd);
6375 gboolean
6376 cmd_data_shuffle (WorkbookControl *wbc, data_shuffling_t *sc, Sheet *sheet)
6378 CmdDataShuffle *me;
6380 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6381 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6383 me = g_object_new (CMD_DATA_SHUFFLE_TYPE, NULL);
6385 me->ds = sc;
6386 me->cmd.sheet = sheet;
6387 me->cmd.size = 1;
6388 me->cmd.cmd_descriptor = g_strdup (_("Shuffle Data"));
6390 return gnm_command_push_undo (wbc, G_OBJECT (me));
6393 /******************************************************************/
6395 #define CMD_TEXT_TO_COLUMNS_TYPE (cmd_text_to_columns_get_type ())
6396 #define CMD_TEXT_TO_COLUMNS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TEXT_TO_COLUMNS_TYPE, CmdTextToColumns))
6398 typedef struct {
6399 GnmCommand cmd;
6401 GnmCellRegion *contents;
6402 GnmPasteTarget dst;
6403 GnmRange src;
6404 Sheet *src_sheet;
6405 ColRowStateList *saved_sizes;
6406 } CmdTextToColumns;
6408 MAKE_GNM_COMMAND (CmdTextToColumns, cmd_text_to_columns, NULL)
6410 static gboolean
6411 cmd_text_to_columns_impl (GnmCommand *cmd, WorkbookControl *wbc,
6412 gboolean is_undo)
6414 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6415 GnmCellRegion *contents;
6417 g_return_val_if_fail (me != NULL, TRUE);
6418 g_return_val_if_fail (me->contents != NULL, TRUE);
6420 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
6421 if (clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc))) {
6422 /* There was a problem, avoid leaking */
6423 cellregion_unref (contents);
6424 return TRUE;
6427 cellregion_unref (me->contents);
6429 if (is_undo) {
6430 colrow_set_states (me->dst.sheet, FALSE,
6431 me->dst.range.start.row, me->saved_sizes);
6432 colrow_state_list_destroy (me->saved_sizes);
6433 me->saved_sizes = NULL;
6434 } else {
6435 me->saved_sizes = colrow_get_states (me->dst.sheet,
6436 FALSE, me->dst.range.start.row, me->dst.range.end.row);
6437 rows_height_update (me->dst.sheet, &me->dst.range, FALSE);
6440 me->contents = contents;
6442 /* Select the newly pasted contents (this queues a redraw) */
6443 select_range (me->dst.sheet, &me->dst.range, wbc);
6445 return FALSE;
6448 static gboolean
6449 cmd_text_to_columns_undo (GnmCommand *cmd, WorkbookControl *wbc)
6451 return cmd_text_to_columns_impl (cmd, wbc, TRUE);
6454 static gboolean
6455 cmd_text_to_columns_redo (GnmCommand *cmd, WorkbookControl *wbc)
6457 return cmd_text_to_columns_impl (cmd, wbc, FALSE);
6460 static void
6461 cmd_text_to_columns_finalize (GObject *cmd)
6463 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6465 if (me->saved_sizes)
6466 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
6467 if (me->contents) {
6468 cellregion_unref (me->contents);
6469 me->contents = NULL;
6471 gnm_command_finalize (cmd);
6474 gboolean
6475 cmd_text_to_columns (WorkbookControl *wbc,
6476 GnmRange const *src, Sheet *src_sheet,
6477 GnmRange const *target, Sheet *target_sheet,
6478 GnmCellRegion *contents)
6480 CmdTextToColumns *me;
6481 char *src_range_name, *target_range_name;
6483 g_return_val_if_fail (contents != NULL, TRUE);
6485 src_range_name = undo_range_name (src_sheet, src);
6486 target_range_name = undo_range_name (target_sheet, target);
6488 me = g_object_new (CMD_TEXT_TO_COLUMNS_TYPE, NULL);
6490 me->cmd.sheet = (src_sheet == target_sheet ? src_sheet : NULL);
6491 me->cmd.size = 1; /* FIXME? */
6492 me->cmd.cmd_descriptor = g_strdup_printf (_("Text (%s) to Columns (%s)"),
6493 src_range_name,
6494 target_range_name);
6495 me->dst.range = *target;
6496 me->dst.sheet = target_sheet;
6497 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
6498 me->src = *src;
6499 me->src_sheet = src_sheet;
6500 me->contents = contents;
6501 me->saved_sizes = NULL;
6503 g_free (src_range_name);
6504 g_free (target_range_name);
6506 /* Check array subdivision & merged regions */
6507 if (sheet_range_splits_region (target_sheet, &me->dst.range,
6508 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
6509 g_object_unref (me);
6510 return TRUE;
6513 return gnm_command_push_undo (wbc, G_OBJECT (me));
6516 /******************************************************************/
6518 #define CMD_GENERIC_TYPE (cmd_generic_get_type ())
6519 #define CMD_GENERIC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GENERIC_TYPE, CmdGeneric))
6521 typedef struct {
6522 GnmCommand cmd;
6523 GOUndo *undo, *redo;
6524 } CmdGeneric;
6526 MAKE_GNM_COMMAND (CmdGeneric, cmd_generic, NULL)
6528 static gboolean
6529 cmd_generic_undo (GnmCommand *cmd, WorkbookControl *wbc)
6531 CmdGeneric *me = CMD_GENERIC (cmd);
6532 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6533 return FALSE;
6536 static gboolean
6537 cmd_generic_redo (GnmCommand *cmd, WorkbookControl *wbc)
6539 CmdGeneric *me = CMD_GENERIC (cmd);
6540 go_undo_undo_with_data (me->redo, GO_CMD_CONTEXT (wbc));
6541 return FALSE;
6544 static void
6545 cmd_generic_finalize (GObject *cmd)
6547 CmdGeneric *me = CMD_GENERIC (cmd);
6549 g_object_unref (me->undo);
6550 g_object_unref (me->redo);
6552 gnm_command_finalize (cmd);
6555 gboolean
6556 cmd_generic_with_size (WorkbookControl *wbc, const char *txt,
6557 int size,
6558 GOUndo *undo, GOUndo *redo)
6560 CmdGeneric *me;
6562 g_return_val_if_fail (GO_IS_UNDO (undo), TRUE);
6563 g_return_val_if_fail (GO_IS_UNDO (redo), TRUE);
6565 me = g_object_new (CMD_GENERIC_TYPE, NULL);
6567 me->cmd.sheet = wb_control_cur_sheet (wbc);
6568 me->cmd.size = size;
6569 me->cmd.cmd_descriptor = g_strdup (txt);
6571 me->undo = undo;
6572 me->redo = redo;
6574 return gnm_command_push_undo (wbc, G_OBJECT (me));
6577 gboolean
6578 cmd_generic (WorkbookControl *wbc, const char *txt, GOUndo *undo, GOUndo *redo)
6580 return cmd_generic_with_size (wbc, txt, 1, undo, redo);
6583 /******************************************************************/
6585 #define CMD_GOAL_SEEK_TYPE (cmd_goal_seek_get_type ())
6586 #define CMD_GOAL_SEEK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GOAL_SEEK_TYPE, CmdGoalSeek))
6588 typedef struct {
6589 GnmCommand cmd;
6591 GnmCell *cell;
6592 GnmValue *ov;
6593 GnmValue *nv;
6594 } CmdGoalSeek;
6596 MAKE_GNM_COMMAND (CmdGoalSeek, cmd_goal_seek, NULL)
6598 static gboolean
6599 cmd_goal_seek_impl (GnmCell *cell, GnmValue *value)
6601 sheet_cell_set_value (cell, value_dup(value));
6602 return FALSE;
6606 static gboolean
6607 cmd_goal_seek_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6609 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6611 return cmd_goal_seek_impl (me->cell, me->ov);
6614 static gboolean
6615 cmd_goal_seek_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6617 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6619 return cmd_goal_seek_impl (me->cell, me->nv);
6622 static void
6623 cmd_goal_seek_finalize (GObject *cmd)
6625 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6627 value_release (me->ov);
6628 me->ov = NULL;
6629 value_release (me->nv);
6630 me->nv = NULL;
6632 gnm_command_finalize (cmd);
6635 gboolean
6636 cmd_goal_seek (WorkbookControl *wbc, GnmCell *cell, GnmValue *ov, GnmValue *nv)
6638 CmdGoalSeek *me;
6639 GnmRange range;
6641 g_return_val_if_fail (cell != NULL, TRUE);
6642 g_return_val_if_fail (ov != NULL || nv != NULL, TRUE);
6644 me = g_object_new (CMD_GOAL_SEEK_TYPE, NULL);
6646 me->cmd.sheet = cell->base.sheet;
6647 me->cmd.size = 1;
6648 range_init_cellpos (&range, &cell->pos);
6649 me->cmd.cmd_descriptor = g_strdup_printf
6650 (_("Goal Seek (%s)"), undo_range_name (cell->base.sheet, &range));
6652 me->cell = cell;
6653 me->ov = ov;
6654 me->nv = nv;
6656 if (me->ov == NULL)
6657 me->ov = value_dup (cell->value);
6658 if (me->nv == NULL)
6659 me->nv = value_dup (cell->value);
6661 return gnm_command_push_undo (wbc, G_OBJECT (me));
6664 /******************************************************************/
6666 #if 0
6667 #define CMD_FREEZE_PANES_TYPE (cmd_freeze_panes_get_type ())
6668 #define CMD_FREEZE_PANES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FREEZE_PANES_TYPE, CmdFreezePanes))
6670 typedef struct {
6671 GnmCommand cmd;
6673 SheetView *sv;
6674 GnmCellPos pos;
6675 } CmdFreezePanes;
6677 MAKE_GNM_COMMAND (CmdFreezePanes, cmd_freeze_panes, NULL)
6679 static gboolean
6680 cmd_freeze_panes_undo (GnmCommand *cmd, WorkbookControl *wbc)
6682 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6684 return FALSE;
6687 static gboolean
6688 cmd_freeze_panes_redo (GnmCommand *cmd, WorkbookControl *wbc)
6690 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6692 return FALSE;
6695 static void
6696 cmd_freeze_panes_finalize (GObject *cmd)
6698 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6700 gnm_command_finalize (cmd);
6704 * cmd_freeze_panes:
6705 * @wbc: where to report errors
6706 * @sv: the view to freeze
6707 * @frozen:
6708 * @unfrozen:
6710 * Returns: %TRUE if there was a problem, %FALSE otherwise.
6712 gboolean
6713 cmd_freeze_panes (WorkbookControl *wbc, SheetView *sv,
6714 GnmCellPos const *frozen, GnmCellPos const *unfrozen)
6716 CmdFreezePanes *me;
6718 g_return_val_if_fail (name != NULL, TRUE);
6719 g_return_val_if_fail (pp != NULL, TRUE);
6720 g_return_val_if_fail (expr != NULL, TRUE);
6722 me = g_object_new (CMD_FREEZE_PANES_TYPE, NULL);
6723 me->sv = sv;
6724 me->frozen = f;
6725 me->unfrozen = expr;
6726 return gnm_command_push_undo (wbc, G_OBJECT (me));
6729 #endif
6732 /******************************************************************/
6735 #define CMD_TABULATE_TYPE (cmd_tabulate_get_type ())
6736 #define CMD_TABULATE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TABULATE_TYPE, CmdTabulate))
6738 typedef struct {
6739 GnmCommand cmd;
6740 GSList *sheet_idx;
6741 GnmTabulateInfo *data;
6742 } CmdTabulate;
6744 MAKE_GNM_COMMAND (CmdTabulate, cmd_tabulate, NULL)
6746 static gint
6747 cmd_tabulate_cmp_f (gconstpointer a,
6748 gconstpointer b)
6750 guint const a_val = GPOINTER_TO_INT (a);
6751 guint const b_val = GPOINTER_TO_INT (b);
6753 if (a_val > b_val)
6754 return -1;
6755 if (a_val < b_val)
6756 return 1;
6757 return 0;
6760 static gboolean
6761 cmd_tabulate_undo (GnmCommand *cmd, WorkbookControl *wbc)
6763 CmdTabulate *me = CMD_TABULATE (cmd);
6764 GSList *l;
6765 gboolean res = TRUE;
6767 me->sheet_idx = g_slist_sort (me->sheet_idx,
6768 cmd_tabulate_cmp_f);
6770 for (l = me->sheet_idx; l != NULL; l = l->next) {
6771 int i = GPOINTER_TO_INT (l->data);
6772 Sheet *new_sheet =
6773 workbook_sheet_by_index (wb_control_get_workbook (wbc),
6775 res = res && command_undo_sheet_delete (new_sheet);
6777 return !res;
6780 static gboolean
6781 cmd_tabulate_redo (GnmCommand *cmd, WorkbookControl *wbc)
6783 CmdTabulate *me = CMD_TABULATE (cmd);
6785 g_slist_free (me->sheet_idx);
6786 me->sheet_idx = do_tabulation (wbc, me->data);
6788 return (me->sheet_idx == NULL);
6791 static void
6792 cmd_tabulate_finalize (GObject *cmd)
6794 CmdTabulate *me = CMD_TABULATE (cmd);
6796 g_free (me->data->cells);
6797 g_free (me->data->minima);
6798 g_free (me->data->maxima);
6799 g_free (me->data->steps);
6800 g_free (me->data);
6801 gnm_command_finalize (cmd);
6804 gboolean
6805 cmd_tabulate (WorkbookControl *wbc, gpointer data)
6807 CmdTabulate *me;
6809 g_return_val_if_fail (data != NULL, TRUE);
6811 me = g_object_new (CMD_TABULATE_TYPE, NULL);
6813 me->cmd.sheet = NULL;
6814 me->cmd.size = 1;
6815 me->cmd.cmd_descriptor =
6816 g_strdup_printf (_("Tabulating Dependencies"));
6817 me->data = data;
6818 me->sheet_idx = NULL;
6820 return gnm_command_push_undo (wbc, G_OBJECT (me));
6823 /******************************************************************/
6825 #define CMD_SO_GRAPH_CONFIG_TYPE (cmd_so_graph_config_get_type ())
6826 #define CMD_SO_GRAPH_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_GRAPH_CONFIG_TYPE, CmdSOGraphConfig))
6828 typedef struct {
6829 GnmCommand cmd;
6830 SheetObject *so;
6831 GogGraph *new_graph;
6832 GogGraph *old_graph;
6833 } CmdSOGraphConfig;
6835 MAKE_GNM_COMMAND (CmdSOGraphConfig, cmd_so_graph_config, NULL)
6837 static gboolean
6838 cmd_so_graph_config_redo (GnmCommand *cmd,
6839 G_GNUC_UNUSED WorkbookControl *wbc)
6841 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6842 sheet_object_graph_set_gog (me->so, me->new_graph);
6843 return FALSE;
6846 static gboolean
6847 cmd_so_graph_config_undo (GnmCommand *cmd,
6848 G_GNUC_UNUSED WorkbookControl *wbc)
6850 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6851 sheet_object_graph_set_gog (me->so, me->old_graph);
6852 return FALSE;
6855 static void
6856 cmd_so_graph_config_finalize (GObject *cmd)
6858 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6860 g_object_unref (me->so);
6861 g_object_unref (me->new_graph);
6862 g_object_unref (me->old_graph);
6864 gnm_command_finalize (cmd);
6867 gboolean
6868 cmd_so_graph_config (WorkbookControl *wbc, SheetObject *so,
6869 GObject *n_graph, GObject *o_graph)
6871 CmdSOGraphConfig *me;
6873 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6874 g_return_val_if_fail (GNM_IS_SO_GRAPH (so), TRUE);
6875 g_return_val_if_fail (GOG_IS_GRAPH (n_graph), TRUE);
6876 g_return_val_if_fail (GOG_IS_GRAPH (o_graph), TRUE);
6878 me = g_object_new (CMD_SO_GRAPH_CONFIG_TYPE, NULL);
6880 me->so = so;
6881 g_object_ref (so);
6883 me->new_graph = GOG_GRAPH (n_graph);
6884 g_object_ref (me->new_graph);
6885 me->old_graph = GOG_GRAPH (o_graph);
6886 g_object_ref (me->old_graph);
6888 me->cmd.sheet = sheet_object_get_sheet (so);
6889 me->cmd.size = 10;
6890 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Graph"));
6892 return gnm_command_push_undo (wbc, G_OBJECT (me));
6895 /******************************************************************/
6897 #define CMD_SO_COMPONENT_CONFIG_TYPE (cmd_so_component_config_get_type ())
6898 #define CMD_SO_COMPONENT_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_COMPONENT_CONFIG_TYPE, CmdSOComponentConfig))
6900 typedef struct {
6901 GnmCommand cmd;
6902 SheetObject *so;
6903 GOComponent *new_obj;
6904 GOComponent *old_obj;
6905 } CmdSOComponentConfig;
6907 MAKE_GNM_COMMAND (CmdSOComponentConfig, cmd_so_component_config, NULL)
6909 static gboolean
6910 cmd_so_component_config_redo (GnmCommand *cmd,
6911 G_GNUC_UNUSED WorkbookControl *wbc)
6913 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6914 sheet_object_component_set_component (me->so, me->new_obj);
6915 return FALSE;
6918 static gboolean
6919 cmd_so_component_config_undo (GnmCommand *cmd,
6920 G_GNUC_UNUSED WorkbookControl *wbc)
6922 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6923 sheet_object_component_set_component (me->so, me->old_obj);
6924 return FALSE;
6927 static void
6928 cmd_so_component_config_finalize (GObject *cmd)
6930 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6932 g_object_unref (me->so);
6933 g_object_unref (me->new_obj);
6934 g_object_unref (me->old_obj);
6936 gnm_command_finalize (cmd);
6939 gboolean
6940 cmd_so_component_config (WorkbookControl *wbc, SheetObject *so,
6941 GObject *n_obj, GObject *o_obj)
6943 CmdSOComponentConfig *me;
6945 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6946 g_return_val_if_fail (GNM_IS_SO_COMPONENT (so), TRUE);
6947 g_return_val_if_fail (GO_IS_COMPONENT (n_obj), TRUE);
6948 g_return_val_if_fail (GO_IS_COMPONENT (o_obj), TRUE);
6950 me = g_object_new (CMD_SO_COMPONENT_CONFIG_TYPE, NULL);
6952 me->so = so;
6953 g_object_ref (so);
6955 me->new_obj = GO_COMPONENT (g_object_ref (n_obj));
6956 me->old_obj = GO_COMPONENT (g_object_ref (o_obj));
6958 me->cmd.sheet = sheet_object_get_sheet (so);
6959 me->cmd.size = 10;
6960 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Object"));
6962 return gnm_command_push_undo (wbc, G_OBJECT (me));
6965 /******************************************************************/
6967 #define CMD_TOGGLE_RTL_TYPE (cmd_toggle_rtl_get_type ())
6968 #define CMD_TOGGLE_RTL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TOGGLE_RTL_TYPE, CmdToggleRTL))
6970 typedef GnmCommand CmdToggleRTL;
6972 MAKE_GNM_COMMAND (CmdToggleRTL, cmd_toggle_rtl, NULL)
6974 static gboolean
6975 cmd_toggle_rtl_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6977 go_object_toggle (cmd->sheet, "text-is-rtl");
6978 return FALSE;
6981 static gboolean
6982 cmd_toggle_rtl_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6984 return cmd_toggle_rtl_redo (cmd, wbc);
6987 static void
6988 cmd_toggle_rtl_finalize (GObject *cmd)
6990 gnm_command_finalize (cmd);
6993 gboolean
6994 cmd_toggle_rtl (WorkbookControl *wbc, Sheet *sheet)
6996 CmdToggleRTL *me;
6998 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6999 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
7001 me = g_object_new (CMD_TOGGLE_RTL_TYPE, NULL);
7002 me->sheet = sheet;
7003 me->size = 1;
7004 me->cmd_descriptor = g_strdup (sheet->text_is_rtl ? _("Left to Right") : _("Right to Left"));
7006 return gnm_command_push_undo (wbc, G_OBJECT (me));
7009 /******************************************************************/
7011 #define CMD_SO_SET_VALUE_TYPE (cmd_so_set_value_get_type ())
7012 #define CMD_SO_SET_VALUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_VALUE_TYPE, CmdSOSetValue))
7014 typedef struct {
7015 GnmCommand cmd;
7016 GnmCellRef ref;
7017 GnmValue *val;
7018 GOUndo *undo;
7019 } CmdSOSetValue;
7021 MAKE_GNM_COMMAND (CmdSOSetValue, cmd_so_set_value, NULL)
7023 static gboolean
7024 cmd_so_set_value_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7026 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7027 Sheet *sheet = me->ref.sheet;
7028 GnmCell *cell = sheet_cell_fetch (sheet, me->ref.col, me->ref.row);
7030 sheet_cell_set_value (cell, value_dup (me->val));
7031 sheet_update (sheet);
7033 return FALSE;
7036 static gboolean
7037 cmd_so_set_value_undo (GnmCommand *cmd, WorkbookControl *wbc)
7039 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7041 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
7043 return FALSE;
7046 static void
7047 cmd_so_set_value_finalize (GObject *cmd)
7049 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7051 value_release (me->val);
7052 g_object_unref (me->undo);
7054 gnm_command_finalize (cmd);
7057 gboolean
7058 cmd_so_set_value (WorkbookControl *wbc,
7059 const char *text,
7060 const GnmCellRef *pref,
7061 GnmValue *new_val,
7062 Sheet *sheet)
7064 CmdSOSetValue *me;
7065 GnmRange r;
7067 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7069 r.start.col = r.end.col = pref->col;
7070 r.start.row = r.end.row = pref->row;
7072 me = g_object_new (CMD_SO_SET_VALUE_TYPE, NULL);
7073 me->cmd.sheet = sheet;
7074 me->cmd.size = 1;
7075 me->cmd.cmd_descriptor = g_strdup (text);
7076 me->ref = *pref;
7077 me->val = new_val;
7078 me->undo = clipboard_copy_range_undo (pref->sheet, &r);
7080 return gnm_command_push_undo (wbc, G_OBJECT (me));
7083 /******************************************************************/
7085 #define CMD_HYPERLINK_TYPE (cmd_hyperlink_get_type ())
7086 #define CMD_HYPERLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_HYPERLINK_TYPE, CmdHyperlink))
7088 typedef struct {
7089 GnmCommand cmd;
7090 GSList *selection;
7091 GnmStyle *new_style;
7092 char *opt_content;
7093 GOUndo *undo;
7094 gboolean update_size;
7095 } CmdHyperlink;
7097 static void
7098 cmd_hyperlink_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
7100 CmdHyperlink const *orig = (CmdHyperlink const *) cmd;
7102 if (orig->new_style)
7103 gnm_style_ref (orig->new_style);
7105 cmd_selection_hyperlink (wbc, orig->new_style, NULL,
7106 g_strdup (orig->opt_content));
7108 MAKE_GNM_COMMAND (CmdHyperlink, cmd_hyperlink, cmd_hyperlink_repeat)
7110 static gboolean
7111 cmd_hyperlink_undo (GnmCommand *cmd, WorkbookControl *wbc)
7113 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7114 Workbook *wb = wb_control_get_workbook (wbc);
7116 if (me->undo) {
7117 go_undo_undo (me->undo);
7118 g_clear_object (&me->undo);
7121 select_selection (me->cmd.sheet, me->selection, wbc);
7123 WORKBOOK_FOREACH_CONTROL (wb, view, ctl, {
7124 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS);
7127 return FALSE;
7130 static GnmValue *
7131 cb_hyperlink_set_text (GnmCellIter const *iter, gpointer user)
7133 CmdHyperlink *me = user;
7134 GnmCell *cell = iter->cell;
7136 if (cell == NULL)
7137 cell = sheet_cell_fetch (iter->pp.sheet,
7138 iter->pp.eval.col,
7139 iter->pp.eval.row);
7141 /* We skip non-empty cells. */
7142 if (gnm_cell_is_empty (cell) &&
7143 !gnm_cell_is_nonsingleton_array (cell)) {
7144 sheet_cell_set_value (cell, value_new_string (me->opt_content));
7145 if (me->update_size)
7146 me->cmd.size++;
7149 return NULL;
7152 static gboolean
7153 cmd_hyperlink_redo (GnmCommand *cmd, WorkbookControl *wbc)
7155 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7156 GSList *l;
7157 Workbook *wb = wb_control_get_workbook (wbc);
7158 Sheet *sheet;
7160 g_return_val_if_fail (me != NULL, TRUE);
7162 sheet = me->cmd.sheet;
7164 /* Check for locked cells */
7165 if (cmd_selection_is_locked_effective (sheet, me->selection,
7166 wbc, _("Changing Hyperlink")))
7167 return TRUE;
7169 me->undo = clipboard_copy_ranges_undo (sheet, me->selection);
7171 for (l = me->selection; l; l = l->next) {
7172 GnmRange const *r = l->data;
7174 if (me->new_style) {
7175 gnm_style_ref (me->new_style);
7176 sheet_apply_style (sheet, r, me->new_style);
7177 sheet_flag_style_update_range (sheet, r);
7180 if (me->opt_content) {
7181 sheet_foreach_cell_in_range (sheet, CELL_ITER_ALL, r,
7182 cb_hyperlink_set_text,
7183 me);
7186 me->update_size = FALSE;
7188 sheet_redraw_all (sheet, FALSE);
7189 sheet_mark_dirty (sheet);
7191 select_selection (sheet, me->selection, wbc);
7193 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
7194 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
7196 return FALSE;
7199 static void
7200 cmd_hyperlink_finalize (GObject *cmd)
7202 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7204 g_clear_object (&me->undo);
7206 if (me->new_style)
7207 gnm_style_unref (me->new_style);
7208 me->new_style = NULL;
7210 range_fragment_free (me->selection);
7211 me->selection = NULL;
7213 g_free (me->opt_content);
7215 gnm_command_finalize (cmd);
7219 * cmd_selection_hyperlink:
7220 * @wbc: the workbook control.
7221 * @style: (transfer full): style to apply to the selection
7222 * @opt_translated_name: An optional name to use in place of 'Hyperlink Cells'
7223 * @opt_content: optional content for otherwise empty cells.
7225 * Returns: %TRUE if there was a problem, %FALSE otherwise.
7227 gboolean
7228 cmd_selection_hyperlink (WorkbookControl *wbc,
7229 GnmStyle *style,
7230 char const *opt_translated_name,
7231 char *opt_content)
7233 CmdHyperlink *me;
7234 SheetView *sv = wb_control_cur_sheet_view (wbc);
7236 me = g_object_new (CMD_HYPERLINK_TYPE, NULL);
7238 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
7239 me->new_style = style;
7241 me->cmd.sheet = sv_sheet (sv);
7242 me->cmd.size = 1; /* Updated later. */
7243 me->update_size = TRUE;
7245 me->opt_content = opt_content;
7247 if (opt_translated_name == NULL) {
7248 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
7250 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing hyperlink of %s"), names);
7251 g_free (names);
7252 } else
7253 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
7256 return gnm_command_push_undo (wbc, G_OBJECT (me));
7259 /******************************************************************/
7262 #define CMD_SO_SET_LINKS_TYPE (cmd_so_set_links_get_type ())
7263 #define CMD_SO_SET_LINKS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_LINKS_TYPE, CmdSOSetLink))
7265 typedef struct {
7266 GnmCommand cmd;
7267 SheetObject *so;
7268 GnmExprTop const *output;
7269 GnmExprTop const *content;
7270 gboolean as_index;
7271 } CmdSOSetLink;
7273 MAKE_GNM_COMMAND (CmdSOSetLink, cmd_so_set_links, NULL)
7275 static gboolean
7276 cmd_so_set_links_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7278 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7279 GnmExprTop const *old_output;
7280 GnmExprTop const *old_content;
7281 gboolean old_as_index;
7283 old_content = sheet_widget_list_base_get_content_link (me->so);
7284 old_output = sheet_widget_list_base_get_result_link (me->so);
7285 old_as_index = sheet_widget_list_base_result_type_is_index (me->so);
7287 sheet_widget_list_base_set_links
7288 (me->so, me->output, me->content);
7289 if (old_as_index != me->as_index) {
7290 sheet_widget_list_base_set_result_type (me->so, me->as_index);
7291 me->as_index = old_as_index;
7293 if (me->output)
7294 gnm_expr_top_unref (me->output);
7295 if (me->content)
7296 gnm_expr_top_unref (me->content);
7297 me->output = old_output;
7298 me->content = old_content;
7300 return FALSE;
7303 static gboolean
7304 cmd_so_set_links_undo (GnmCommand *cmd, WorkbookControl *wbc)
7306 return cmd_so_set_links_redo (cmd, wbc);
7309 static void
7310 cmd_so_set_links_finalize (GObject *cmd)
7312 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7314 if (me->output)
7315 gnm_expr_top_unref (me->output);
7316 if (me->content)
7317 gnm_expr_top_unref (me->content);
7318 gnm_command_finalize (cmd);
7321 gboolean
7322 cmd_so_set_links (WorkbookControl *wbc,
7323 SheetObject *so,
7324 GnmExprTop const *output,
7325 GnmExprTop const *content,
7326 gboolean as_index)
7328 CmdSOSetLink *me;
7330 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7332 me = g_object_new (CMD_SO_SET_LINKS_TYPE, NULL);
7333 me->cmd.sheet = sheet_object_get_sheet (so);
7334 me->cmd.size = 1;
7335 me->cmd.cmd_descriptor = g_strdup (_("Configure List"));
7336 me->so = so;
7337 me->output = output;
7338 me->content = content;
7339 me->as_index = as_index;
7341 return gnm_command_push_undo (wbc, G_OBJECT (me));
7344 /******************************************************************/
7348 #define CMD_SO_SET_FRAME_LABEL_TYPE (cmd_so_set_frame_label_get_type ())
7349 #define CMD_SO_SET_FRAME_LABEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_FRAME_LABEL_TYPE, CmdSOSetFrameLabel))
7351 typedef struct {
7352 GnmCommand cmd;
7353 SheetObject *so;
7354 char *old_label;
7355 char *new_label;
7356 } CmdSOSetFrameLabel;
7358 MAKE_GNM_COMMAND (CmdSOSetFrameLabel, cmd_so_set_frame_label, NULL)
7360 static gboolean
7361 cmd_so_set_frame_label_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7363 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7365 sheet_widget_frame_set_label (me->so, me->new_label);
7367 return FALSE;
7370 static gboolean
7371 cmd_so_set_frame_label_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7373 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7375 sheet_widget_frame_set_label (me->so, me->old_label);
7377 return FALSE;
7380 static void
7381 cmd_so_set_frame_label_finalize (GObject *cmd)
7383 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7385 g_free (me->old_label);
7386 me->old_label = NULL;
7388 g_free (me->new_label);
7389 me->new_label = NULL;
7391 gnm_command_finalize (cmd);
7394 gboolean
7395 cmd_so_set_frame_label (WorkbookControl *wbc,
7396 SheetObject *so,
7397 char *old_label, char *new_label )
7399 CmdSOSetFrameLabel *me;
7401 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7403 me = g_object_new (CMD_SO_SET_FRAME_LABEL_TYPE, NULL);
7404 me->cmd.sheet = sheet_object_get_sheet (so);
7405 me->cmd.size = 1;
7406 me->cmd.cmd_descriptor = g_strdup (_("Set Frame Label"));
7407 me->so = so;
7408 me->old_label = old_label;
7409 me->new_label = new_label;
7411 return gnm_command_push_undo (wbc, G_OBJECT (me));
7414 /******************************************************************/
7415 #define CMD_SO_SET_BUTTON_TYPE (cmd_so_set_button_get_type ())
7416 #define CMD_SO_SET_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_BUTTON_TYPE, CmdSOSetButton))
7418 typedef struct {
7419 GnmCommand cmd;
7420 SheetObject *so;
7421 GnmExprTop const *new_link;
7422 GnmExprTop const *old_link;
7423 char *old_label;
7424 char *new_label;
7425 } CmdSOSetButton;
7427 MAKE_GNM_COMMAND (CmdSOSetButton, cmd_so_set_button, NULL)
7429 static gboolean
7430 cmd_so_set_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7432 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7434 sheet_widget_button_set_link (me->so, me->new_link);
7435 sheet_widget_button_set_label (me->so, me->new_label);
7437 return FALSE;
7440 static gboolean
7441 cmd_so_set_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7443 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7445 sheet_widget_button_set_link (me->so, me->old_link);
7446 sheet_widget_button_set_label (me->so, me->old_label);
7448 return FALSE;
7451 static void
7452 cmd_so_set_button_finalize (GObject *cmd)
7454 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7456 if (me->new_link)
7457 gnm_expr_top_unref (me->new_link);
7458 if (me->old_link)
7459 gnm_expr_top_unref (me->old_link);
7460 g_free (me->old_label);
7461 g_free (me->new_label);
7462 gnm_command_finalize (cmd);
7465 gboolean
7466 cmd_so_set_button (WorkbookControl *wbc,
7467 SheetObject *so, GnmExprTop const *lnk,
7468 char *old_label, char *new_label)
7470 CmdSOSetButton *me;
7472 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7474 me = g_object_new (CMD_SO_SET_BUTTON_TYPE, NULL);
7475 me->cmd.sheet = sheet_object_get_sheet (so);
7476 me->cmd.size = 1;
7477 me->cmd.cmd_descriptor = g_strdup (_("Configure Button"));
7478 me->so = so;
7479 me->new_link = lnk;
7480 me->old_label = old_label;
7481 me->new_label = new_label;
7483 me->old_link = sheet_widget_button_get_link (so);
7485 return gnm_command_push_undo (wbc, G_OBJECT (me));
7488 /******************************************************************/
7489 #define CMD_SO_SET_RADIO_BUTTON_TYPE (cmd_so_set_radio_button_get_type ())
7490 #define CMD_SO_SET_RADIO_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_RADIO_BUTTON_TYPE, CmdSOSetRadioButton))
7492 typedef struct {
7493 GnmCommand cmd;
7494 SheetObject *so;
7495 GnmExprTop const *new_link;
7496 GnmExprTop const *old_link;
7497 char *old_label;
7498 char *new_label;
7499 GnmValue *old_value;
7500 GnmValue *new_value;
7501 } CmdSOSetRadioButton;
7503 MAKE_GNM_COMMAND (CmdSOSetRadioButton, cmd_so_set_radio_button, NULL)
7505 static gboolean
7506 cmd_so_set_radio_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7508 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7510 sheet_widget_radio_button_set_link (me->so, me->new_link);
7511 sheet_widget_radio_button_set_label (me->so, me->new_label);
7512 sheet_widget_radio_button_set_value (me->so, me->new_value);
7514 return FALSE;
7517 static gboolean
7518 cmd_so_set_radio_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7520 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7522 sheet_widget_radio_button_set_link (me->so, me->old_link);
7523 sheet_widget_radio_button_set_label (me->so, me->old_label);
7524 sheet_widget_radio_button_set_value (me->so, me->old_value);
7526 return FALSE;
7529 static void
7530 cmd_so_set_radio_button_finalize (GObject *cmd)
7532 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7534 if (me->new_link)
7535 gnm_expr_top_unref (me->new_link);
7536 if (me->old_link)
7537 gnm_expr_top_unref (me->old_link);
7538 g_free (me->old_label);
7539 g_free (me->new_label);
7540 value_release (me->old_value);
7541 value_release (me->new_value);
7542 gnm_command_finalize (cmd);
7545 gboolean
7546 cmd_so_set_radio_button (WorkbookControl *wbc,
7547 SheetObject *so, GnmExprTop const *lnk,
7548 char *old_label, char *new_label,
7549 GnmValue *old_value, GnmValue *new_value)
7551 CmdSOSetRadioButton *me;
7553 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7555 me = g_object_new (CMD_SO_SET_RADIO_BUTTON_TYPE, NULL);
7556 me->cmd.sheet = sheet_object_get_sheet (so);
7557 me->cmd.size = 1;
7558 me->cmd.cmd_descriptor = g_strdup (_("Configure Radio Button"));
7559 me->so = so;
7560 me->new_link = lnk;
7561 me->old_label = old_label;
7562 me->new_label = new_label;
7563 me->old_value = old_value;
7564 me->new_value = new_value;
7566 me->old_link = sheet_widget_radio_button_get_link (so);
7568 return gnm_command_push_undo (wbc, G_OBJECT (me));
7571 /******************************************************************/
7572 #define CMD_SO_SET_CHECKBOX_TYPE (cmd_so_set_checkbox_get_type ())
7573 #define CMD_SO_SET_CHECKBOX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_CHECKBOX_TYPE, CmdSOSetCheckbox))
7575 typedef struct {
7576 GnmCommand cmd;
7577 SheetObject *so;
7578 GnmExprTop const *new_link;
7579 GnmExprTop const *old_link;
7580 char *old_label;
7581 char *new_label;
7582 } CmdSOSetCheckbox;
7584 MAKE_GNM_COMMAND (CmdSOSetCheckbox, cmd_so_set_checkbox, NULL)
7586 static gboolean
7587 cmd_so_set_checkbox_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7589 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7591 sheet_widget_checkbox_set_link (me->so, me->new_link);
7592 sheet_widget_checkbox_set_label (me->so, me->new_label);
7594 return FALSE;
7597 static gboolean
7598 cmd_so_set_checkbox_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7600 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7602 sheet_widget_checkbox_set_link (me->so, me->old_link);
7603 sheet_widget_checkbox_set_label (me->so, me->old_label);
7605 return FALSE;
7608 static void
7609 cmd_so_set_checkbox_finalize (GObject *cmd)
7611 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7613 if (me->new_link)
7614 gnm_expr_top_unref (me->new_link);
7615 if (me->old_link)
7616 gnm_expr_top_unref (me->old_link);
7617 g_free (me->old_label);
7618 g_free (me->new_label);
7619 gnm_command_finalize (cmd);
7622 gboolean
7623 cmd_so_set_checkbox (WorkbookControl *wbc,
7624 SheetObject *so, GnmExprTop const *lnk,
7625 char *old_label, char *new_label)
7627 CmdSOSetCheckbox *me;
7629 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7631 me = g_object_new (CMD_SO_SET_CHECKBOX_TYPE, NULL);
7632 me->cmd.sheet = sheet_object_get_sheet (so);
7633 me->cmd.size = 1;
7634 me->cmd.cmd_descriptor = g_strdup (_("Configure Checkbox"));
7635 me->so = so;
7636 me->new_link = lnk;
7637 me->old_label = old_label;
7638 me->new_label = new_label;
7640 me->old_link = sheet_widget_checkbox_get_link (so);
7642 return gnm_command_push_undo (wbc, G_OBJECT (me));
7645 /******************************************************************/
7647 #define CMD_SO_SET_ADJUSTMENT_TYPE (cmd_so_set_adjustment_get_type ())
7648 #define CMD_SO_SET_ADJUSTMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_ADJUSTMENT_TYPE, CmdSOSetAdjustment))
7650 typedef struct {
7651 GnmCommand cmd;
7652 SheetObject *so;
7653 GnmExprTop const *new_link;
7654 GnmExprTop const *old_link;
7655 double old_lower;
7656 double old_upper;
7657 double old_step;
7658 double old_page;
7659 gboolean old_horizontal;
7660 } CmdSOSetAdjustment;
7662 MAKE_GNM_COMMAND (CmdSOSetAdjustment, cmd_so_set_adjustment, NULL)
7664 static void
7665 cmd_so_set_adjustment_adj (CmdSOSetAdjustment *me)
7667 GtkAdjustment *adj = sheet_widget_adjustment_get_adjustment (me->so);
7669 double old_lower = gtk_adjustment_get_lower (adj);
7670 double old_upper = gtk_adjustment_get_upper (adj);
7671 double old_step = gtk_adjustment_get_step_increment (adj);
7672 double old_page = gtk_adjustment_get_page_increment (adj);
7673 gboolean old_horizontal;
7674 g_object_get (G_OBJECT (me->so), "horizontal", &old_horizontal, NULL);
7676 gtk_adjustment_configure (adj,
7677 gtk_adjustment_get_value (adj),
7678 me->old_lower,
7679 me->old_upper,
7680 me->old_step,
7681 me->old_page,
7682 gtk_adjustment_get_page_size (adj));
7683 g_object_set (G_OBJECT (me->so), "horizontal", me->old_horizontal, NULL);
7685 me->old_lower = old_lower;
7686 me->old_upper = old_upper;
7687 me->old_step = old_step;
7688 me->old_page = old_page;
7689 me->old_horizontal = old_horizontal;
7692 static gboolean
7693 cmd_so_set_adjustment_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7695 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7697 sheet_widget_adjustment_set_link (me->so, me->new_link);
7698 cmd_so_set_adjustment_adj (me);
7699 return FALSE;
7702 static gboolean
7703 cmd_so_set_adjustment_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7705 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7707 sheet_widget_adjustment_set_link (me->so, me->old_link);
7708 cmd_so_set_adjustment_adj (me);
7710 return FALSE;
7713 static void
7714 cmd_so_set_adjustment_finalize (GObject *cmd)
7716 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7718 if (me->new_link)
7719 gnm_expr_top_unref (me->new_link);
7720 if (me->old_link)
7721 gnm_expr_top_unref (me->old_link);
7722 gnm_command_finalize (cmd);
7725 gboolean
7726 cmd_so_set_adjustment (WorkbookControl *wbc,
7727 SheetObject *so, GnmExprTop const *lnk,
7728 gboolean horizontal,
7729 int lower, int upper,
7730 int step, int page,
7731 char const *undo_label)
7733 CmdSOSetAdjustment *me;
7735 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7737 me = g_object_new (CMD_SO_SET_ADJUSTMENT_TYPE, NULL);
7738 me->cmd.sheet = sheet_object_get_sheet (so);
7739 me->cmd.size = 1;
7740 me->cmd.cmd_descriptor = g_strdup ((undo_label == NULL) ?
7741 _("Configure Adjustment") : _(undo_label));
7742 me->so = so;
7743 me->new_link = lnk;
7744 me->old_lower = lower;
7745 me->old_upper = upper;
7746 me->old_step = step;
7747 me->old_page = page;
7748 me->old_horizontal = horizontal;
7750 me->old_link = sheet_widget_adjustment_get_link (so);
7752 return gnm_command_push_undo (wbc, G_OBJECT (me));
7755 /******************************************************************/
7757 gboolean
7758 cmd_autofilter_add_remove (WorkbookControl *wbc)
7760 SheetView *sv = wb_control_cur_sheet_view (wbc);
7761 GnmFilter *f = gnm_sheet_view_editpos_in_filter (sv);
7762 gboolean add = (f == NULL);
7763 char *descr = NULL, *name = NULL;
7764 GOUndo *undo = NULL;
7765 GOUndo *redo = NULL;
7766 gboolean result;
7769 if (add) {
7770 GnmRange region;
7771 GnmRange const *src = selection_first_range (sv,
7772 GO_CMD_CONTEXT (wbc), _("Add Filter"));
7773 GnmFilter *f_old = NULL;
7775 if (src == NULL)
7776 return TRUE;
7778 f_old = gnm_sheet_filter_intersect_rows
7779 (sv->sheet, src->start.row, src->end.row);
7781 if (f_old != NULL) {
7782 GnmRange *r = gnm_sheet_filter_can_be_extended
7783 (sv->sheet, f_old, src);
7784 if (r == NULL) {
7785 char *error;
7786 name = undo_range_name (sv->sheet, &(f_old->r));
7787 error = g_strdup_printf
7788 (_("Auto Filter blocked by %s"),
7789 name);
7790 g_free(name);
7791 go_cmd_context_error_invalid
7792 (GO_CMD_CONTEXT (wbc),
7793 _("AutoFilter"), error);
7794 g_free (error);
7795 return TRUE;
7797 /* extending existing filter. */
7798 undo = go_undo_binary_new
7799 (gnm_filter_ref (f_old), sv->sheet,
7800 (GOUndoBinaryFunc) gnm_filter_attach,
7801 (GFreeFunc) gnm_filter_unref,
7802 NULL);
7803 redo = go_undo_unary_new
7804 (gnm_filter_ref (f_old),
7805 (GOUndoUnaryFunc) gnm_filter_remove,
7806 (GFreeFunc) gnm_filter_unref);
7807 gnm_filter_remove (f_old);
7808 region = *r;
7809 g_free (r);
7810 } else {
7811 /* if only one row is selected
7812 * assume that the user wants to
7813 * filter the region below this row. */
7814 region = *src;
7815 if (src->start.row == src->end.row)
7816 gnm_sheet_guess_region (sv->sheet, &region);
7817 if (region.start.row == region.end.row) {
7818 go_cmd_context_error_invalid
7819 (GO_CMD_CONTEXT (wbc),
7820 _("AutoFilter"),
7821 _("Requires more than 1 row"));
7822 return TRUE;
7825 f = gnm_filter_new (sv->sheet, &region);
7826 if (f == NULL) {
7827 go_cmd_context_error_invalid
7828 (GO_CMD_CONTEXT (wbc),
7829 _("AutoFilter"),
7830 _("Unable to create Autofilter"));
7831 if (f_old)
7832 gnm_filter_attach (f_old, sv->sheet);
7833 return TRUE;
7836 gnm_filter_remove (f);
7837 if (f_old)
7838 gnm_filter_attach (f_old, sv->sheet);
7840 redo = go_undo_combine (go_undo_binary_new
7841 (gnm_filter_ref (f), sv->sheet,
7842 (GOUndoBinaryFunc) gnm_filter_attach,
7843 (GFreeFunc) gnm_filter_unref,
7844 NULL), redo);
7845 undo = go_undo_combine (undo,
7846 go_undo_unary_new
7848 (GOUndoUnaryFunc) gnm_filter_remove,
7849 (GFreeFunc) gnm_filter_unref));
7851 name = undo_range_name (sv->sheet, &(f->r));
7852 descr = g_strdup_printf
7853 ((f_old == NULL) ? _("Add Autofilter to %s")
7854 : _("Extend Autofilter to %s"),
7855 name);
7856 } else {
7857 undo = go_undo_binary_new
7858 (gnm_filter_ref (f), sv->sheet,
7859 (GOUndoBinaryFunc) gnm_filter_attach,
7860 (GFreeFunc) gnm_filter_unref,
7861 NULL);
7862 redo = go_undo_unary_new
7863 (gnm_filter_ref (f),
7864 (GOUndoUnaryFunc) gnm_filter_remove,
7865 (GFreeFunc) gnm_filter_unref);
7866 name = undo_range_name (sv->sheet, &(f->r));
7867 descr = g_strdup_printf (_("Remove Autofilter from %s"),
7868 name);
7870 result = cmd_generic (wbc, descr, undo, redo);
7871 g_free (name);
7872 g_free (descr);
7874 return result;
7878 /******************************************************************/
7880 gboolean cmd_autofilter_set_condition (WorkbookControl *wbc,
7881 GnmFilter *filter, unsigned i,
7882 GnmFilterCondition *cond)
7884 char *descr = NULL, *name = NULL;
7885 GOUndo *undo = NULL;
7886 GOUndo *redo = NULL;
7887 gboolean result;
7889 undo = gnm_undo_filter_set_condition_new (filter, i,
7890 NULL, TRUE);
7891 g_return_val_if_fail (undo != NULL, TRUE);
7892 redo = gnm_undo_filter_set_condition_new (filter, i,
7893 cond, FALSE);
7894 g_return_val_if_fail (redo != NULL, TRUE);
7896 name = undo_range_name (filter->sheet, &(filter->r));
7897 descr = g_strdup_printf (_("Change filter condition for %s"),
7898 name);
7900 result = cmd_generic (wbc, descr, undo, redo);
7901 g_free (name);
7902 g_free (descr);
7904 return result;
7908 /******************************************************************/
7910 static void
7911 cmd_page_breaks_set_breaks (Sheet *sheet,
7912 GnmPageBreaks const *breaks)
7914 print_info_set_breaks (sheet->print_info, gnm_page_breaks_dup (breaks));
7916 SHEET_FOREACH_CONTROL (sheet, sv, sc, wb_control_menu_state_update (sc_wbc (sc), MS_PAGE_BREAKS););
7919 gboolean
7920 cmd_page_breaks_clear (WorkbookControl *wbc, Sheet *sheet)
7922 GOUndo *undo = NULL;
7923 GOUndo *redo = NULL;
7925 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7926 g_return_val_if_fail (sheet != NULL, TRUE);
7928 if (sheet->print_info->page_breaks.v != NULL) {
7929 redo = go_undo_binary_new
7930 (sheet,
7931 gnm_page_breaks_new (TRUE),
7932 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7933 NULL,
7934 (GFreeFunc) gnm_page_breaks_free);
7935 undo = go_undo_binary_new
7936 (sheet,
7937 gnm_page_breaks_dup
7938 (sheet->print_info->page_breaks.v),
7939 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7940 NULL,
7941 (GFreeFunc) gnm_page_breaks_free);
7944 if (sheet->print_info->page_breaks.h != NULL) {
7945 redo = go_undo_combine
7946 (redo,
7947 go_undo_binary_new
7948 (sheet,
7949 gnm_page_breaks_new (FALSE),
7950 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7951 NULL,
7952 (GFreeFunc) gnm_page_breaks_free));
7954 undo = go_undo_combine
7955 (undo,
7956 go_undo_binary_new
7957 (sheet,
7958 gnm_page_breaks_dup
7959 (sheet->print_info->page_breaks.h),
7960 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7961 NULL,
7962 (GFreeFunc) gnm_page_breaks_free));
7965 if (undo != NULL)
7966 return cmd_generic (wbc, _("Clear All Page Breaks"), undo, redo);
7967 else
7968 return TRUE;
7971 gboolean
7972 cmd_page_break_toggle (WorkbookControl *wbc, Sheet *sheet, gboolean is_vert)
7974 SheetView const *sv = wb_control_cur_sheet_view (wbc);
7975 gint col = sv->edit_pos.col;
7976 gint row = sv->edit_pos.row;
7977 int rc = is_vert ? col : row;
7978 GnmPageBreaks *old, *new, *target;
7979 GnmPageBreakType type;
7980 char const *label;
7981 GOUndo *undo;
7982 GOUndo *redo;
7984 target = is_vert ? sheet->print_info->page_breaks.v
7985 : sheet->print_info->page_breaks.h;
7987 old = (target == NULL) ? gnm_page_breaks_new (is_vert)
7988 : gnm_page_breaks_dup (target);
7989 new = gnm_page_breaks_dup (old);
7991 if (gnm_page_breaks_get_break (new, rc) != GNM_PAGE_BREAK_MANUAL) {
7992 type = GNM_PAGE_BREAK_MANUAL;
7993 label = is_vert ? _("Remove Column Page Break") : _("Remove Row Page Break");
7994 } else {
7995 type = GNM_PAGE_BREAK_NONE;
7996 label = is_vert ? _("Add Column Page Break") : _("Add Row Page Break");
7999 gnm_page_breaks_set_break (new, rc, type);
8001 redo = go_undo_binary_new
8002 (sheet, new,
8003 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8004 NULL,
8005 (GFreeFunc) gnm_page_breaks_free);
8006 undo = go_undo_binary_new
8007 (sheet, old,
8008 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8009 NULL,
8010 (GFreeFunc) gnm_page_breaks_free);
8012 return cmd_generic (wbc, label, undo, redo);
8015 /******************************************************************/