GnmAction: introspection fixes.
[gnumeric.git] / src / commands.c
blobd55a413ad0327faa8abfd97ba7385dbf304476f9
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 gboolean
193 cmd_cell_range_is_locked_effective (Sheet *sheet, GnmRange *range,
194 WorkbookControl *wbc, char const *cmd_name)
196 int i, j;
197 WorkbookView *wbv = wb_control_view (wbc);
199 if (wbv->is_protected || sheet->is_protected)
200 for (i = range->start.row; i <= range->end.row; i++)
201 for (j = range->start.col; j <= range->end.col; j++)
202 if (gnm_style_get_contents_locked (sheet_style_get (sheet, j, i))) {
203 char *r = global_range_name (sheet, range);
204 char *text = g_strdup_printf (wbv->is_protected ?
205 _("%s is locked. Unprotect the workbook to enable editing.") :
206 _("%s is locked. Unprotect the sheet to enable editing."),
208 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
209 cmd_name, text);
210 g_free (text);
211 g_free (r);
212 return TRUE;
214 return FALSE;
218 * checks whether the cells are effectively locked
220 * static gboolean cmd_dao_is_locked_effective
223 * Do not use this function unless the sheet is part of the
224 * workbook with the given wbcg (otherwise the results may be strange)
228 static gboolean
229 cmd_dao_is_locked_effective (data_analysis_output_t *dao,
230 WorkbookControl *wbc, char const *cmd_name)
232 GnmRange range;
233 range_init (&range, dao->start_col, dao->start_row,
234 dao->start_col + dao->cols - 1, dao->start_row + dao->rows - 1);
235 return (dao->type != NewWorkbookOutput &&
236 cmd_cell_range_is_locked_effective (dao->sheet, &range, wbc, cmd_name));
240 * cmd_selection_is_locked_effective: (skip)
241 * checks whether the selection is effectively locked
243 * static gboolean cmd_selection_is_locked_effective
246 * Do not use this function unless the sheet is part of the
247 * workbook with the given wbcg (otherwise the results may be strange)
250 gboolean
251 cmd_selection_is_locked_effective (Sheet *sheet, GSList *selection,
252 WorkbookControl *wbc, char const *cmd_name)
254 for (; selection; selection = selection->next) {
255 GnmRange *range = selection->data;
256 if (cmd_cell_range_is_locked_effective (sheet, range, wbc, cmd_name))
257 return TRUE;
259 return FALSE;
263 * A helper routine to select a range and make sure the top-left
264 * is visible.
266 static void
267 select_range (Sheet *sheet, const GnmRange *r, WorkbookControl *wbc)
269 SheetView *sv;
271 if (sheet->workbook != wb_control_get_workbook (wbc)) {
273 * We could try to pick a random wbc for the sheet's
274 * workbook. But not right now.
276 return;
279 wb_control_sheet_focus (wbc, sheet);
280 sv = sheet_get_view (sheet, wb_control_view (wbc));
281 sv_selection_reset (sv);
282 sv_selection_add_range (sv, r);
283 gnm_sheet_view_make_cell_visible (sv, r->start.col, r->start.row, FALSE);
287 * A helper routine to select a list of ranges and make sure the top-left
288 * corner of the last is visible.
290 static void
291 select_selection (Sheet *sheet, GSList *selection, WorkbookControl *wbc)
293 SheetView *sv = sheet_get_view (sheet, wb_control_view (wbc));
294 const GnmRange *r0 = NULL;
295 GSList *l;
297 g_return_if_fail (selection != NULL);
299 wb_control_sheet_focus (wbc, sheet);
300 sv_selection_reset (sv);
301 for (l = selection; l; l = l->next) {
302 GnmRange const *r = l->data;
303 sv_selection_add_range (sv, r);
304 r0 = r;
306 gnm_sheet_view_make_cell_visible (sv, r0->start.col, r0->start.row, FALSE);
310 * get_menu_label:
311 * with a list of commands.
312 * @cmd_list: The command list to check.
314 * Utility routine to get the descriptor associated
315 * Returns : A static reference to a descriptor. DO NOT free this.
317 static char const *
318 get_menu_label (GSList *cmd_list)
320 if (cmd_list != NULL) {
321 GnmCommand *cmd = GNM_COMMAND (cmd_list->data);
322 return cmd->cmd_descriptor;
325 return NULL;
329 * undo_redo_menu_labels:
330 * @wb: The book whose undo/redo queues we are modifying
332 * Another utility to set the menus correctly.
334 static void
335 undo_redo_menu_labels (Workbook *wb)
337 char const *undo_label = get_menu_label (wb->undo_commands);
338 char const *redo_label = get_menu_label (wb->redo_commands);
340 WORKBOOK_FOREACH_CONTROL (wb, view, control,
341 wb_control_undo_redo_labels (control, undo_label, redo_label);
345 static void
346 update_after_action (Sheet *sheet, WorkbookControl *wbc)
348 gnm_app_recalc ();
350 if (sheet != NULL) {
351 g_return_if_fail (IS_SHEET (sheet));
353 sheet_mark_dirty (sheet);
354 sheet_update (sheet);
356 if (sheet->workbook == wb_control_get_workbook (wbc))
357 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
358 wb_control_sheet_focus (control, sheet););
359 } else if (wbc != NULL) {
360 Sheet *sheet = wb_control_cur_sheet (wbc);
361 if (sheet)
362 sheet_update (sheet);
368 * command_undo:
369 * @wbc: The workbook control which issued the request.
370 * Any user level errors generated by undoing will be reported
371 * here.
373 * Undo the last command executed.
375 void
376 command_undo (WorkbookControl *wbc)
378 GnmCommand *cmd;
379 GnmCommandClass *klass;
380 Workbook *wb = wb_control_get_workbook (wbc);
382 g_return_if_fail (wb != NULL);
383 g_return_if_fail (wb->undo_commands != NULL);
385 cmd = GNM_COMMAND (wb->undo_commands->data);
386 g_return_if_fail (cmd != NULL);
388 klass = CMD_CLASS (cmd);
389 g_return_if_fail (klass != NULL);
391 g_object_ref (cmd);
393 /* TRUE indicates a failure to undo. Leave the command where it is */
394 if (!klass->undo_cmd (cmd, wbc)) {
395 gboolean undo_cleared;
397 update_after_action (cmd->sheet, wbc);
399 if (!cmd->workbook_modified_before_do)
400 go_doc_set_dirty (GO_DOC (wb), FALSE);
403 * A few commands clear the undo queue. For those, we do not
404 * want to stuff the cmd object on the redo queue.
406 undo_cleared = (wb->undo_commands == NULL);
408 if (!undo_cleared) {
409 wb->undo_commands = g_slist_remove (wb->undo_commands, cmd);
410 wb->redo_commands = g_slist_prepend (wb->redo_commands, cmd);
412 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
413 wb_control_undo_redo_pop (control, TRUE);
414 wb_control_undo_redo_push (control, FALSE, cmd->cmd_descriptor, cmd);
416 undo_redo_menu_labels (wb);
417 /* TODO : Should we mark the workbook as clean or pristine too */
421 g_object_unref (cmd);
425 * command_redo:
426 * @wbc: The workbook control which issued the request.
428 * Redo the last command that was undone.
429 * Any user level errors generated by redoing will be reported
430 * here.
432 void
433 command_redo (WorkbookControl *wbc)
435 GnmCommand *cmd;
436 GnmCommandClass *klass;
437 Workbook *wb = wb_control_get_workbook (wbc);
439 g_return_if_fail (wb);
440 g_return_if_fail (wb->redo_commands);
442 cmd = GNM_COMMAND (wb->redo_commands->data);
443 g_return_if_fail (cmd != NULL);
445 klass = CMD_CLASS (cmd);
446 g_return_if_fail (klass != NULL);
448 g_object_ref (cmd);
450 cmd->workbook_modified_before_do =
451 go_doc_is_dirty (wb_control_get_doc (wbc));
453 /* TRUE indicates a failure to redo. Leave the command where it is */
454 if (!klass->redo_cmd (cmd, wbc)) {
455 gboolean redo_cleared;
457 update_after_action (cmd->sheet, wbc);
460 * A few commands clear the undo queue. For those, we do not
461 * want to stuff the cmd object on the redo queue.
463 redo_cleared = (wb->redo_commands == NULL);
465 if (!redo_cleared) {
466 wb->redo_commands = g_slist_remove (wb->redo_commands, cmd);
467 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
469 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
470 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
471 wb_control_undo_redo_pop (control, FALSE);
473 undo_redo_menu_labels (wb);
477 g_object_unref (cmd);
481 * command_repeat:
482 * @wbc: The workbook control which issued the request.
484 * Repeat the last command (if possible)
486 * Any user level errors generated by redoing will be reported
487 * here.
489 void
490 command_repeat (WorkbookControl *wbc)
492 GnmCommand *cmd;
493 GnmCommandClass *klass;
494 Workbook *wb = wb_control_get_workbook (wbc);
496 g_return_if_fail (wb);
497 g_return_if_fail (wb->undo_commands);
499 cmd = GNM_COMMAND (wb->undo_commands->data);
500 g_return_if_fail (cmd != NULL);
502 klass = CMD_CLASS (cmd);
503 g_return_if_fail (klass != NULL);
505 if (klass->repeat_cmd != NULL)
506 (*klass->repeat_cmd) (cmd, wbc);
510 * command_setup_combos:
511 * @wbc:
513 * Initialize the combos to correspond to the current undo/redo state.
515 void
516 command_setup_combos (WorkbookControl *wbc)
518 char const *undo_label = NULL, *redo_label = NULL;
519 GSList *ptr, *tmp;
520 Workbook *wb = wb_control_get_workbook (wbc);
522 g_return_if_fail (wb);
524 wb_control_undo_redo_truncate (wbc, 0, TRUE);
525 tmp = g_slist_reverse (wb->undo_commands);
526 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
527 undo_label = get_menu_label (ptr);
528 wb_control_undo_redo_push (wbc, TRUE, undo_label, ptr->data);
530 if (g_slist_reverse (tmp)) {} /* ignore, list is in undo_commands */
532 wb_control_undo_redo_truncate (wbc, 0, FALSE);
533 tmp = g_slist_reverse (wb->redo_commands);
534 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
535 redo_label = get_menu_label (ptr);
536 wb_control_undo_redo_push (wbc, FALSE, redo_label, ptr->data);
538 if (g_slist_reverse (tmp)) {} /* ignore, list is in redo_commands */
540 /* update the menus too */
541 wb_control_undo_redo_labels (wbc, undo_label, redo_label);
545 * command_list_release:
546 * @cmds: (element-type GObject): the set of commands to free.
548 * command_list_release : utility routine to free the resources associated
549 * with a list of commands.
551 * NOTE : remember to NULL the list when you are done.
553 void
554 command_list_release (GSList *cmd_list)
556 while (cmd_list != NULL) {
557 GObject *cmd = G_OBJECT (cmd_list->data);
559 g_return_if_fail (cmd != NULL);
561 g_object_unref (cmd);
562 cmd_list = g_slist_remove (cmd_list, cmd_list->data);
567 * Each undo item has a certain size. The size of typing a value into
568 * a cell is the unit size. A large autoformat could have a size of
569 * hundreds or even thousands.
571 * We wish to have the same undo behaviour across platforms, so please
572 * don't use sizeof in computing the undo size.
575 #undef DEBUG_TRUNCATE_UNDO
578 * Truncate the undo list if it is too big.
580 * Returns -1 if no truncation was done, or else the number of elements
581 * left.
583 static int
584 truncate_undo_info (Workbook *wb)
586 int size_left;
587 int max_num;
588 int ok_count;
589 GSList *l, *prev;
591 size_left = gnm_conf_get_undo_size ();
592 max_num = gnm_conf_get_undo_maxnum ();
594 #ifdef DEBUG_TRUNCATE_UNDO
595 g_printerr ("Undo sizes:");
596 #endif
598 for (l = wb->undo_commands, prev = NULL, ok_count = 0;
600 prev = l, l = l->next, ok_count++) {
601 int min_leave;
602 GnmCommand *cmd = GNM_COMMAND (l->data);
603 int size = cmd->size;
605 if (size < 1) {
607 * We could g_assert, but that would cause data loss.
608 * Instead, just continue.
610 g_warning ("Faulty undo_size_func, please report.");
611 size = 1;
614 #ifdef DEBUG_TRUNCATE_UNDO
615 g_printerr (" %d", size);
616 #endif
618 /* Keep at least one undo item. */
619 if (ok_count >= max_num || (size > size_left && ok_count >= 1)) {
620 /* Current item is too big; truncate list here. */
621 command_list_release (l);
622 if (prev)
623 prev->next = NULL;
624 else
625 wb->undo_commands = NULL;
626 #ifdef DEBUG_TRUNCATE_UNDO
627 g_printerr ("[trunc]\n");
628 #endif
629 return ok_count;
633 * In order to allow a series of useful small items behind
634 * a big item, leave at least 10% of current item's size.
636 min_leave = size / 10;
637 size_left = MAX (size_left - size, min_leave);
640 #ifdef DEBUG_TRUNCATE_UNDO
641 g_printerr ("\n");
642 #endif
643 return -1;
648 * command_register_undo:
649 * @wbc: The workbook control that issued the command.
650 * @cmd: The new command to add.
652 * An internal utility to tack a new command
653 * onto the undo list.
655 static void
656 command_register_undo (WorkbookControl *wbc, GObject *obj)
658 Workbook *wb;
659 GnmCommand *cmd;
660 int undo_trunc;
662 g_return_if_fail (wbc != NULL);
663 wb = wb_control_get_workbook (wbc);
665 cmd = GNM_COMMAND (obj);
666 g_return_if_fail (cmd != NULL);
668 command_list_release (wb->redo_commands);
669 wb->redo_commands = NULL;
671 g_object_ref (obj); /* keep a ref in case it gets truncated away */
672 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
673 undo_trunc = truncate_undo_info (wb);
675 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
676 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
677 if (undo_trunc >= 0)
678 wb_control_undo_redo_truncate (control, undo_trunc, TRUE);
679 wb_control_undo_redo_truncate (control, 0, FALSE);
681 undo_redo_menu_labels (wb);
682 g_object_unref (obj);
687 * gnm_command_push_undo:
688 * @wbc: The workbook control that issued the command.
689 * @obj: The new command to add.
691 * An internal utility to tack a new command
692 * onto the undo list.
694 * returns : TRUE if there was an error.
696 gboolean
697 gnm_command_push_undo (WorkbookControl *wbc, GObject *obj)
699 gboolean trouble;
700 GnmCommand *cmd;
701 GnmCommandClass *klass;
703 g_return_val_if_fail (wbc != NULL, TRUE);
705 cmd = GNM_COMMAND (obj);
706 cmd->workbook_modified_before_do =
707 go_doc_is_dirty (wb_control_get_doc (wbc));
709 g_return_val_if_fail (cmd != NULL, TRUE);
711 klass = CMD_CLASS (cmd);
712 g_return_val_if_fail (klass != NULL, TRUE);
714 /* TRUE indicates a failure to do the command */
715 trouble = klass->redo_cmd (cmd, wbc);
716 update_after_action (cmd->sheet, wbc);
718 if (!trouble)
719 command_register_undo (wbc, obj);
720 else
721 g_object_unref (obj);
723 return trouble;
727 * command_undo_sheet_delete deletes the sheet without deleting the current cmd.
728 * returns true if is indeed deleted the sheet.
729 * Note: only call this for a sheet of your current workbook from the undo procedure
732 static gboolean
733 command_undo_sheet_delete (Sheet* sheet)
735 Workbook *wb = sheet->workbook;
737 g_return_val_if_fail (IS_SHEET (sheet), FALSE);
739 if (wb->redo_commands != NULL) {
740 command_list_release (wb->redo_commands);
741 wb->redo_commands = NULL;
742 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
743 wb_control_undo_redo_truncate (ctl, 0, FALSE););
744 undo_redo_menu_labels (wb);
747 workbook_sheet_delete (sheet);
749 return (TRUE);
752 /******************************************************************/
754 static GnmValue *
755 cmd_set_text_full_check_texpr (GnmCellIter const *iter, GnmExprTop const *texpr)
757 if (iter->cell == NULL ||
758 !gnm_expr_top_equal (iter->cell->base.texpr, texpr))
759 return VALUE_TERMINATE;
760 return NULL;
763 static GnmValue *
764 cmd_set_text_full_check_text (GnmCellIter const *iter, char *text)
766 char *old_text;
767 gboolean same;
768 gboolean quoted = FALSE;
770 if (gnm_cell_is_blank (iter->cell))
771 return ((text == NULL || text[0] == '\0') ? NULL : VALUE_TERMINATE);
773 if (text == NULL || text[0] == '\0')
774 return VALUE_TERMINATE;
776 old_text = gnm_cell_get_text_for_editing (iter->cell, NULL, &quoted);
777 same = g_strcmp0 (old_text, text) == 0;
779 if (!same && !quoted && iter->cell->value && VALUE_IS_STRING (iter->cell->value)
780 && text[0] == '\'')
781 same = g_strcmp0 (old_text, text + 1) == 0;
783 g_free (old_text);
785 return (same ? NULL : VALUE_TERMINATE);
788 static GnmValue *
789 cmd_set_text_full_check_markup (GnmCellIter const *iter, PangoAttrList *markup)
791 PangoAttrList const *old_markup = NULL;
792 gboolean same_markup;
794 g_return_val_if_fail (iter->cell != NULL, NULL);
796 if (iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
797 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
798 if (fmt && go_format_is_markup (fmt)) {
799 old_markup = go_format_get_markup (fmt);
800 if (go_pango_attr_list_is_empty (old_markup))
801 old_markup = NULL;
805 same_markup = gnm_pango_attr_list_equal (old_markup, markup);
807 return same_markup ? NULL : VALUE_TERMINATE;
811 * cmd_set_text_full
813 * the caller is expected to have ensured:
815 * 1) that no array is being split
816 * 2) that the range is not locked.
818 * Note:
819 * We will free the selection but nothing else.
823 static gboolean
824 cmd_set_text_full (WorkbookControl *wbc, GSList *selection, GnmEvalPos *ep,
825 char const *new_text, PangoAttrList *markup,
826 gboolean autocorrect)
828 GSList *l;
829 char const *expr_txt;
830 GnmExprTop const *texpr = NULL;
831 GOUndo *undo = NULL;
832 GOUndo *redo = NULL;
833 gboolean result, autofit_col = FALSE, same_text_and_not_same_markup = FALSE;
834 char *text = NULL;
835 char *name;
836 Sheet *sheet = ep->sheet;
837 GnmParsePos pp;
838 ColRowIndexList *cri_col_list = NULL, *cri_row_list = NULL;
839 GOFormat const *format = gnm_style_get_format
840 (sheet_style_get (sheet, ep->eval.col, ep->eval.row));
842 g_return_val_if_fail (selection != NULL , TRUE);
844 parse_pos_init_evalpos (&pp, ep);
845 name = undo_range_list_name (sheet, selection);
847 if ((format == NULL) || !go_format_is_text (format)) {
848 expr_txt = gnm_expr_char_start_p (new_text);
849 if (expr_txt != NULL)
850 texpr = gnm_expr_parse_str
851 (expr_txt, &pp, GNM_EXPR_PARSE_DEFAULT,
852 sheet_get_conventions (sheet), NULL);
855 if (texpr != NULL) {
856 GOFormat const *sf;
857 GnmStyle *new_style = NULL;
858 gboolean same_texpr = TRUE;
860 /* We should check whether we are in fact changing anything: */
861 for (l = selection; l != NULL && same_texpr; l = l->next) {
862 GnmRange *r = l->data;
863 GnmValue *val =
864 sheet_foreach_cell_in_range
865 (sheet, CELL_ITER_ALL, r,
866 (CellIterFunc) cmd_set_text_full_check_texpr,
867 (gpointer) texpr);
869 same_texpr = (val != VALUE_TERMINATE);
870 if (val != NULL && same_texpr)
871 value_release (val);
874 if (same_texpr) {
875 gnm_expr_top_unref (texpr);
876 g_free (name);
877 range_fragment_free (selection);
878 return TRUE;
881 text = g_strdup_printf (_("Inserting expression in %s"), name);
883 if (go_format_is_general (format)) {
884 sf = gnm_auto_style_format_suggest (texpr, ep);
885 if (sf != NULL) {
886 new_style = gnm_style_new ();
887 gnm_style_set_format (new_style, sf);
888 go_format_unref (sf);
892 for (l = selection; l != NULL; l = l->next) {
893 GnmSheetRange *sr;
894 undo = go_undo_combine
895 (undo, clipboard_copy_range_undo (sheet, l->data));
896 sr = gnm_sheet_range_new (sheet, l->data);
897 redo = go_undo_combine
898 (redo, sheet_range_set_expr_undo (sr, texpr));
899 if (new_style) {
900 sr = gnm_sheet_range_new (sheet, l->data);
901 redo = go_undo_combine
902 (redo, sheet_apply_style_undo (sr, new_style));
905 if (new_style)
906 gnm_style_unref (new_style);
907 gnm_expr_top_unref (texpr);
908 autofit_col = TRUE;
909 } else {
910 GString *text_str;
911 PangoAttrList *adj_markup = NULL;
912 char *corrected;
913 gboolean same_text = TRUE;
914 gboolean same_markup = TRUE;
916 if (new_text == NULL)
917 corrected = NULL;
918 else if (autocorrect)
919 corrected = autocorrect_tool (new_text);
920 else
921 corrected = g_strdup (new_text);
923 if (corrected && (corrected[0] == '\'') && corrected[1] == '\0') {
924 g_free (corrected);
925 corrected = g_strdup ("");
928 /* We should check whether we are in fact changing anything: */
929 /* We'll handle */
930 for (l = selection; l != NULL && same_text; l = l->next) {
931 GnmRange *r = l->data;
932 GnmValue *val =
933 sheet_foreach_cell_in_range
934 (sheet, CELL_ITER_ALL, r,
935 (CellIterFunc) cmd_set_text_full_check_text,
936 (gpointer) corrected);
938 same_text = (val != VALUE_TERMINATE);
939 if (val != NULL && same_text)
940 value_release (val);
943 if (go_pango_attr_list_is_empty (markup))
944 markup = NULL;
945 if (markup && corrected && corrected[0] == '\'') {
946 markup = adj_markup = pango_attr_list_copy (markup);
947 go_pango_attr_list_erase (adj_markup, 0, 1);
950 if (same_text) {
951 for (l = selection; l != NULL && same_text; l = l->next) {
952 GnmRange *r = l->data;
953 GnmValue *val =
954 sheet_foreach_cell_in_range
955 (sheet, CELL_ITER_IGNORE_BLANK, r,
956 (CellIterFunc) cmd_set_text_full_check_markup,
957 (gpointer) markup);
959 same_markup = (val != VALUE_TERMINATE);
960 if (val != NULL && same_markup)
961 value_release (val);
964 if (same_markup) {
965 g_free (corrected);
966 g_free (name);
967 range_fragment_free (selection);
968 if (adj_markup)
969 pango_attr_list_unref (adj_markup);
970 return TRUE;
973 text = g_strdup_printf (_("Editing style of %s"), name);
974 } else {
975 text_str = gnm_cmd_trunc_descriptor (g_string_new (corrected), NULL);
976 text = g_strdup_printf (_("Typing \"%s\" in %s"), text_str->str, name);
977 g_string_free (text_str, TRUE);
980 for (l = selection; l != NULL; l = l->next) {
981 GnmSheetRange *sr;
982 undo = go_undo_combine
983 (undo, clipboard_copy_range_undo (sheet, l->data));
984 if (corrected) {
985 sr = gnm_sheet_range_new (sheet, l->data);
986 redo = go_undo_combine
987 (redo, sheet_range_set_text_undo
988 (sr, corrected));
990 if (markup) {
991 sr = gnm_sheet_range_new (sheet, l->data);
992 /* Note: order of combination matters!! */
993 redo = go_undo_combine
994 (sheet_range_set_markup_undo (sr, markup), redo);
998 if (adj_markup)
999 pango_attr_list_unref (adj_markup);
1000 g_free (corrected);
1002 same_text_and_not_same_markup = (same_text && !same_markup);
1004 g_free (name);
1006 /* We are combining this since we don't want to apply and undo twice.*/
1007 if (same_text_and_not_same_markup || !autofit_col) {
1008 GnmCell *cell = sheet_cell_fetch
1009 (sheet, ep->eval.col, ep->eval.row);
1010 gboolean nvis;
1012 go_undo_undo (redo);
1013 nvis = !VALUE_IS_STRING (cell->value);
1014 if (!autofit_col)
1015 autofit_col = nvis;
1016 if (same_text_and_not_same_markup)
1017 /* We only have to do something if at least one cell */
1018 /* now contains a string, but they contain all the same thing. */
1019 same_text_and_not_same_markup = nvis;
1020 go_undo_undo (undo);
1022 if (same_text_and_not_same_markup) {
1023 /*We had the same text and different markup but we are not entering strings. */
1024 g_object_unref (undo);
1025 g_object_unref (redo);
1026 g_free (text);
1027 range_fragment_free (selection);
1028 return TRUE;
1030 for (l = selection; l != NULL; l = l->next) {
1031 GnmRange *r = l->data;
1032 GnmRange *new_r;
1034 new_r = g_new (GnmRange, 1);
1035 *new_r = *r;
1036 redo = go_undo_combine
1037 (go_undo_binary_new
1038 (sheet, new_r,
1039 (GOUndoBinaryFunc) colrow_autofit_row,
1040 NULL, g_free),
1041 redo);
1042 cri_row_list = colrow_get_index_list
1043 (r->start.row, r->end.row, cri_row_list);
1045 if (autofit_col) {
1046 new_r = g_new (GnmRange, 1);
1047 *new_r = *r;
1048 redo = go_undo_combine
1049 (go_undo_binary_new
1050 (sheet, new_r,
1051 (GOUndoBinaryFunc) colrow_autofit_col,
1052 NULL, g_free),
1053 redo);
1054 cri_col_list = colrow_get_index_list
1055 (r->start.col, r->end.col, cri_col_list);
1059 undo = go_undo_combine (undo,
1060 gnm_undo_colrow_restore_state_group_new
1061 (sheet, TRUE,
1062 cri_col_list,
1063 colrow_get_sizes (sheet, TRUE,
1064 cri_col_list, -1)));
1065 undo = go_undo_combine (undo,
1066 gnm_undo_colrow_restore_state_group_new
1067 (sheet, FALSE,
1068 cri_row_list,
1069 colrow_get_sizes (sheet, FALSE,
1070 cri_row_list, -1)));
1072 result = cmd_generic (wbc, text, undo, redo);
1073 g_free (text);
1074 range_fragment_free (selection);
1075 return result;
1079 * cmd_area_set_text
1081 * the caller is expected to have ensured:
1083 * 1) that no array is being split
1084 * 2) that the range is not locked.
1088 gboolean
1089 cmd_area_set_text (WorkbookControl *wbc, SheetView *sv,
1090 char const *new_text, PangoAttrList *markup)
1092 GnmEvalPos ep;
1093 gboolean result;
1094 GSList *selection = selection_get_ranges (sv, FALSE);
1096 eval_pos_init_editpos (&ep, sv);
1097 result = cmd_set_text_full (wbc, selection, &ep,
1098 new_text, markup, TRUE);
1099 return result;
1102 gboolean
1103 cmd_set_text (WorkbookControl *wbc,
1104 Sheet *sheet, GnmCellPos const *pos,
1105 char const *new_text,
1106 PangoAttrList *markup,
1107 gboolean autocorrect)
1109 GnmCell const *cell;
1110 GnmEvalPos ep;
1111 gboolean result;
1112 GSList *selection;
1113 GnmRange *r;
1115 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1116 g_return_val_if_fail (new_text != NULL, TRUE);
1118 /* Ensure that we are not splitting up an array */
1119 cell = sheet_cell_get (sheet, pos->col, pos->row);
1120 if (gnm_cell_is_nonsingleton_array (cell)) {
1121 gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc),
1122 _("Set Text"), NULL);
1123 return TRUE;
1126 eval_pos_init_pos (&ep, sheet, pos);
1127 r = g_new (GnmRange, 1);
1128 r->start = r->end = *pos;
1129 selection = g_slist_prepend (NULL, r);
1130 result = cmd_set_text_full (wbc, selection, &ep,
1131 new_text, markup, autocorrect);
1132 return result;
1137 * cmd_area_set_array_expr
1139 * the caller is expected to have ensured:
1141 * 1) that no array is being split
1142 * 2) that the selection consists of a single range
1143 * 3) that the range is not locked.
1147 gboolean
1148 cmd_area_set_array_expr (WorkbookControl *wbc, SheetView *sv,
1149 GnmExprTop const *texpr)
1151 GSList *selection = selection_get_ranges (sv, FALSE);
1152 GOUndo *undo = NULL;
1153 GOUndo *redo = NULL;
1154 gboolean result;
1155 Sheet *sheet = sv_sheet (sv);
1156 char *name;
1157 char *text;
1158 GnmSheetRange *sr;
1159 GnmRange *r;
1161 g_return_val_if_fail (selection != NULL , TRUE);
1162 g_return_val_if_fail (selection->next == NULL , TRUE);
1164 name = undo_range_list_name (sheet, selection);
1165 text = g_strdup_printf (_("Inserting array expression in %s"), name);
1166 g_free (name);
1168 r = selection->data;
1170 undo = clipboard_copy_range_undo (sheet, selection->data);
1172 sr = gnm_sheet_range_new (sheet, r);
1173 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1174 redo = go_undo_combine
1175 (go_undo_binary_new
1176 (sheet, g_memdup (r, sizeof (*r)),
1177 (GOUndoBinaryFunc) colrow_autofit_col,
1178 NULL, g_free),
1179 redo);
1180 redo = go_undo_combine
1181 (go_undo_binary_new
1182 (sheet, g_memdup (r, sizeof (*r)),
1183 (GOUndoBinaryFunc) colrow_autofit_row,
1184 NULL, g_free),
1185 redo);
1187 range_fragment_free (selection);
1188 result = cmd_generic (wbc, text, undo, redo);
1189 g_free (text);
1190 return result;
1194 * cmd_create_data_table
1196 * the caller is expected to have ensured:
1198 * 1) that no array is being split
1199 * 2) that the range is not locked.
1202 gboolean
1203 cmd_create_data_table (WorkbookControl *wbc, Sheet *sheet, GnmRange const *r,
1204 char const *col_input, char const *row_input)
1206 GOUndo *undo = NULL;
1207 GOUndo *redo = NULL;
1208 gboolean result;
1209 char *name;
1210 char *text;
1211 GnmSheetRange *sr;
1212 GnmParsePos pp;
1213 GnmExprTop const *texpr;
1215 name = undo_range_name (sheet, r);
1216 text = g_strdup_printf (_("Creating a Data Table in %s"), name);
1217 g_free (name);
1219 undo = clipboard_copy_range_undo (sheet, r);
1221 sr = gnm_sheet_range_new (sheet, r);
1222 parse_pos_init (&pp, NULL, sheet, r->start.col, r->start.row);
1223 name = g_strdup_printf ("TABLE(%s,%s)", row_input, col_input);
1224 texpr = gnm_expr_parse_str
1225 (name, &pp, GNM_EXPR_PARSE_DEFAULT,
1226 sheet_get_conventions (sheet), NULL);
1227 g_free (name);
1229 if (texpr == NULL) {
1230 g_object_unref (undo);
1231 g_free (text);
1232 return TRUE;
1235 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1236 gnm_expr_top_unref (texpr);
1238 result = cmd_generic (wbc, text, undo, redo);
1239 g_free (text);
1240 return result;
1243 /******************************************************************/
1245 #define CMD_INS_DEL_COLROW_TYPE (cmd_ins_del_colrow_get_type ())
1246 #define CMD_INS_DEL_COLROW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_INS_DEL_COLROW_TYPE, CmdInsDelColRow))
1248 typedef struct {
1249 GnmCommand cmd;
1251 Sheet *sheet;
1252 gboolean is_insert;
1253 gboolean is_cols;
1254 gboolean is_cut;
1255 int index;
1256 int count;
1257 GnmRange *cutcopied;
1258 SheetView *cut_copy_view;
1260 gboolean (*redo_action) (Sheet *sheet, int col, int count,
1261 GOUndo **pundo, GOCmdContext *cc);
1263 gboolean (*repeat_action) (WorkbookControl *wbc, Sheet *sheet,
1264 int start, int count);
1266 GOUndo *undo;
1267 } CmdInsDelColRow;
1269 static void
1270 cmd_ins_del_colrow_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1272 CmdInsDelColRow const *orig = (CmdInsDelColRow const *) cmd;
1273 SheetView *sv = wb_control_cur_sheet_view (wbc);
1274 Sheet *sheet = sv_sheet (sv);
1275 GnmRange const *r = selection_first_range (sv,
1276 GO_CMD_CONTEXT (wbc), _("Ins/Del Column/Row"));
1277 int start, count;
1279 if (r == NULL)
1280 return;
1282 if (orig->is_cols)
1283 start = r->start.col, count = range_width (r);
1284 else
1285 start = r->start.row, count = range_height (r);
1287 orig->repeat_action (wbc, sheet, start, count);
1290 MAKE_GNM_COMMAND (CmdInsDelColRow, cmd_ins_del_colrow, cmd_ins_del_colrow_repeat)
1292 static gboolean
1293 cmd_ins_del_colrow_undo (GnmCommand *cmd, WorkbookControl *wbc)
1295 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1297 if (me->undo) {
1298 go_undo_undo (me->undo);
1299 g_object_unref (me->undo);
1300 me->undo = NULL;
1303 /* Ins/Del Row/Col re-ants things completely to account
1304 * for the shift of col/rows.
1306 if (me->cutcopied != NULL && me->cut_copy_view != NULL)
1307 gnm_app_clipboard_cut_copy (wbc, me->is_cut, me->cut_copy_view,
1308 me->cutcopied, FALSE);
1310 return FALSE;
1313 static gboolean
1314 cmd_ins_del_colrow_redo (GnmCommand *cmd, WorkbookControl *wbc)
1316 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1317 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
1318 int idx = me->index;
1319 int count = me->count;
1321 if (me->redo_action (me->sheet, idx, count, &me->undo, cc)) {
1322 /* Trouble. */
1323 return TRUE;
1326 /* Ins/Del Row/Col re-ants things completely to account
1327 * for the shift of col/rows. */
1328 if (me->cutcopied != NULL && me->cut_copy_view != NULL) {
1329 if (me->is_cut) {
1330 GnmRange s = *me->cutcopied;
1331 int key = me->is_insert ? count : -count;
1332 int threshold = me->is_insert ? idx : idx + 1;
1335 * Really only applies if the regions that are
1336 * inserted/deleted are above the cut/copied region.
1338 if (me->is_cols) {
1339 if (threshold <= s.start.col) {
1340 s.start.col += key;
1341 s.end.col += key;
1343 } else if (threshold <= s.start.row) {
1344 s.start.row += key;
1345 s.end.row += key;
1348 gnm_app_clipboard_cut_copy (wbc, me->is_cut,
1349 me->cut_copy_view,
1350 &s, FALSE);
1351 } else
1352 gnm_app_clipboard_unant ();
1355 return FALSE;
1358 static void
1359 cmd_ins_del_colrow_finalize (GObject *cmd)
1361 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1363 if (me->undo)
1364 g_object_unref (me->undo);
1366 g_free (me->cutcopied);
1368 gnm_sheet_view_weak_unref (&(me->cut_copy_view));
1370 gnm_command_finalize (cmd);
1373 static gboolean
1374 cmd_ins_del_colrow (WorkbookControl *wbc,
1375 Sheet *sheet,
1376 gboolean is_cols, gboolean is_insert,
1377 char const *descriptor, int index, int count)
1379 CmdInsDelColRow *me;
1380 int first, last;
1381 GnmRange r;
1383 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1384 g_return_val_if_fail (count > 0, TRUE);
1386 me = g_object_new (CMD_INS_DEL_COLROW_TYPE, NULL);
1388 me->sheet = sheet;
1389 me->is_cols = is_cols;
1390 me->is_insert = is_insert;
1391 me->index = index;
1392 me->count = count;
1393 me->redo_action = me->is_insert
1394 ? (me->is_cols ? sheet_insert_cols : sheet_insert_rows)
1395 : (me->is_cols ? sheet_delete_cols : sheet_delete_rows);
1396 me->repeat_action = me->is_insert
1397 ? (me->is_cols ? cmd_insert_cols : cmd_insert_rows)
1398 : (me->is_cols ? cmd_delete_cols : cmd_delete_rows);
1400 /* Range that will get deleted. */
1401 first = me->is_insert
1402 ? colrow_max (is_cols, sheet) - count
1403 : index;
1404 last = first + count - 1;
1405 (is_cols ? range_init_cols : range_init_rows) (&r, sheet, first, last);
1407 /* Note: redo_action checks for array subdivision. */
1409 /* Check for locks */
1410 if (cmd_cell_range_is_locked_effective (sheet, &r, wbc, descriptor)) {
1411 g_object_unref (me);
1412 return TRUE;
1415 /* We store the cut or/copied range if applicable */
1416 if (!gnm_app_clipboard_is_empty () &&
1417 gnm_app_clipboard_area_get () &&
1418 sheet == gnm_app_clipboard_sheet_get ()) {
1419 me->cutcopied = gnm_range_dup (gnm_app_clipboard_area_get ());
1420 me->is_cut = gnm_app_clipboard_is_cut ();
1421 gnm_sheet_view_weak_ref (gnm_app_clipboard_sheet_view_get (),
1422 &(me->cut_copy_view));
1423 } else
1424 me->cutcopied = NULL;
1426 me->cmd.sheet = sheet;
1427 me->cmd.size = count * 10; /* FIXME? */
1428 me->cmd.cmd_descriptor = descriptor;
1430 return gnm_command_push_undo (wbc, G_OBJECT (me));
1433 gboolean
1434 cmd_insert_cols (WorkbookControl *wbc,
1435 Sheet *sheet, int start_col, int count)
1437 char *mesg;
1438 GnmRange r;
1440 range_init_full_sheet (&r, sheet);
1441 r.start.col = r.end.col - count + 1;
1443 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1444 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1445 ngettext ("Inserting %i column before column %s would push data off the sheet. "
1446 "Please enlarge the sheet first.",
1447 "Inserting %i columns before column %s would push data off the sheet. "
1448 "Please enlarge the sheet first.",
1449 count),
1450 count, col_name (start_col));
1451 return TRUE;
1454 mesg = g_strdup_printf
1455 (ngettext ("Inserting %d column before %s",
1456 "Inserting %d columns before %s",
1457 count),
1458 count, col_name (start_col));
1459 return cmd_ins_del_colrow (wbc, sheet, TRUE, TRUE, mesg, start_col, count);
1462 gboolean
1463 cmd_insert_rows (WorkbookControl *wbc,
1464 Sheet *sheet, int start_row, int count)
1466 char *mesg;
1467 GnmRange r;
1469 range_init_full_sheet (&r, sheet);
1470 r.start.row = r.end.row - count + 1;
1472 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1473 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1474 ngettext ("Inserting %i row before row %s would push data off the sheet. "
1475 "Please enlarge the sheet first.",
1476 "Inserting %i rows before row %s would push data off the sheet. "
1477 "Please enlarge the sheet first.",
1478 count),
1479 count, row_name (start_row));
1480 return TRUE;
1483 mesg = g_strdup_printf
1484 (ngettext ("Inserting %d row before %s",
1485 "Inserting %d rows before %s",
1486 count),
1487 count, row_name (start_row));
1488 return cmd_ins_del_colrow (wbc, sheet, FALSE, TRUE, mesg, start_row, count);
1491 gboolean
1492 cmd_delete_cols (WorkbookControl *wbc,
1493 Sheet *sheet, int start_col, int count)
1495 char *mesg = g_strdup_printf ((count > 1)
1496 ? _("Deleting columns %s")
1497 : _("Deleting column %s"),
1498 cols_name (start_col, start_col + count - 1));
1499 return cmd_ins_del_colrow (wbc, sheet, TRUE, FALSE, mesg, start_col, count);
1502 gboolean
1503 cmd_delete_rows (WorkbookControl *wbc,
1504 Sheet *sheet, int start_row, int count)
1506 char *mesg = g_strdup_printf ((count > 1)
1507 ? _("Deleting rows %s")
1508 : _("Deleting row %s"),
1509 rows_name (start_row, start_row + count - 1));
1510 return cmd_ins_del_colrow (wbc, sheet, FALSE, FALSE, mesg, start_row, count);
1513 /******************************************************************/
1515 typedef struct {
1516 GSList *selection;
1517 GnmRange const *r;
1518 } cmd_selection_clear_row_handler_t;
1520 static gboolean
1521 cmd_selection_clear_row_handler (GnmColRowIter const *iter,
1522 cmd_selection_clear_row_handler_t *data)
1524 if ((!iter->cri->in_filter) || iter->cri->visible) {
1525 GnmRange *r = gnm_range_dup (data->r);
1526 r->start.row = r->end.row = iter->pos;
1527 data->selection = g_slist_prepend (data->selection, r);
1529 return FALSE;
1532 gboolean
1533 cmd_selection_clear (WorkbookControl *wbc, int clear_flags)
1535 char *names, *descriptor;
1536 GString *types;
1537 SheetView *sv = wb_control_cur_sheet_view (wbc);
1538 GSList *selection = selection_get_ranges (sv, FALSE /* No intersection */);
1539 Sheet *sheet = sv_sheet (sv);
1540 gboolean result;
1541 int size;
1542 GOUndo *undo = NULL;
1543 GOUndo *redo = NULL;
1544 GSList *ranges;
1546 if ((clear_flags & CLEAR_FILTERED_ONLY) != 0 && sheet->filters != NULL) {
1547 /* We need to modify the selection to only include filtered rows. */
1548 cmd_selection_clear_row_handler_t data;
1549 data.selection = selection;
1550 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1551 GnmFilter *filter;
1552 data.r = ranges->data;
1553 filter = gnm_sheet_filter_intersect_rows
1554 (sheet, data.r->start.row, data.r->end.row);
1555 if (filter) {
1556 sheet_colrow_foreach (sheet, FALSE,
1557 data.r->start.row,
1558 data.r->end.row,
1559 (ColRowHandler) cmd_selection_clear_row_handler,
1560 &data);
1561 g_free (ranges->data);
1562 ranges->data = NULL;
1565 selection = g_slist_remove_all (data.selection, NULL);
1568 /* We should first determine whether we break anything by clearing */
1569 /* Check for array subdivision *//* Check for locked cells */
1570 if (sheet_ranges_split_region (sheet, selection,
1571 GO_CMD_CONTEXT (wbc), _("Clear")) ||
1572 cmd_selection_is_locked_effective (sheet, selection, wbc, _("Clear"))) {
1573 range_fragment_free (selection);
1574 return TRUE;
1578 /* We now need to build the descriptor */
1579 /* Collect clear types for descriptor */
1580 if (clear_flags != (CLEAR_VALUES | CLEAR_FORMATS | CLEAR_COMMENTS)) {
1581 GSList *m, *l = NULL;
1582 types = g_string_new (NULL);
1583 if (clear_flags & CLEAR_VALUES)
1584 l = g_slist_append (l, g_string_new (_("contents")));
1585 if (clear_flags & CLEAR_FORMATS)
1586 l = g_slist_append (l, g_string_new (_("formats")));
1587 if (clear_flags & CLEAR_COMMENTS)
1588 l = g_slist_append (l, g_string_new (_("comments")));
1589 /* Using a list for this may seem overkill, but is really the only
1590 * right way to do this
1592 for (m = l; m != NULL; m = m->next) {
1593 GString *s = m->data;
1595 g_string_append_len (types, s->str, s->len);
1596 g_string_free (s, TRUE);
1598 if (m->next)
1599 g_string_append (types, ", ");
1601 g_slist_free (l);
1602 } else
1603 types = g_string_new (_("all"));
1604 /* The range name string will automatically be truncated, we don't
1605 * need to truncate the "types" list because it will not grow
1606 * indefinitely
1608 names = undo_range_list_name (sheet, selection);
1609 descriptor = g_strdup_printf (_("Clearing %s in %s"), types->str, names);
1610 g_free (names);
1611 g_string_free (types, TRUE);
1612 size = g_slist_length (selection);
1614 clear_flags |= CLEAR_NOCHECKARRAY;
1616 if (clear_flags & (CLEAR_VALUES | CLEAR_FORMATS))
1617 clear_flags |= CLEAR_RECALC_DEPS;
1619 /* We are now ready to build the redo and undo items */
1620 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1621 GnmRange const *r = ranges->data;
1622 GnmSheetRange *sr = gnm_sheet_range_new (sheet, r);
1624 undo = go_undo_combine (undo, clipboard_copy_range_undo (sheet, r));
1625 redo = go_undo_combine
1626 (redo, sheet_clear_region_undo
1627 (sr, clear_flags));
1630 range_fragment_free (selection);
1632 result = cmd_generic_with_size (wbc, descriptor, size, undo, redo);
1633 g_free (descriptor);
1635 return result;
1638 /******************************************************************/
1640 #define CMD_FORMAT_TYPE (cmd_format_get_type ())
1641 #define CMD_FORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FORMAT_TYPE, CmdFormat))
1643 typedef struct {
1644 GnmCellPos pos;
1645 GnmStyleList *styles;
1646 ColRowIndexList *rows;
1647 ColRowStateGroup *old_heights;
1648 } CmdFormatOldStyle;
1650 typedef struct {
1651 GnmCommand cmd;
1652 GSList *selection;
1653 GSList *old_styles;
1654 GnmStyle *new_style;
1655 GnmBorder **borders;
1656 } CmdFormat;
1658 static void
1659 cmd_format_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1661 CmdFormat const *orig = (CmdFormat const *) cmd;
1662 int i;
1664 if (orig->new_style)
1665 gnm_style_ref (orig->new_style);
1666 if (orig->borders)
1667 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1668 gnm_style_border_ref (orig->borders [i]);
1670 cmd_selection_format (wbc, orig->new_style, orig->borders, NULL);
1672 MAKE_GNM_COMMAND (CmdFormat, cmd_format, cmd_format_repeat)
1674 static gboolean
1675 cmd_format_undo (GnmCommand *cmd,
1676 G_GNUC_UNUSED WorkbookControl *wbc)
1678 CmdFormat *me = CMD_FORMAT (cmd);
1680 g_return_val_if_fail (me != NULL, TRUE);
1682 if (me->old_styles) {
1683 GSList *rstyles = g_slist_reverse (g_slist_copy (me->old_styles));
1684 GSList *rsel = g_slist_reverse (g_slist_copy (me->selection));
1685 GSList *l1, *l2;
1687 for (l1 = rstyles, l2 = rsel; l1; l1 = l1->next, l2 = l2->next) {
1688 CmdFormatOldStyle *os = l1->data;
1689 GnmRange const *r = l2->data;
1690 GnmSpanCalcFlags flags = sheet_style_set_list
1691 (me->cmd.sheet,
1692 &os->pos, os->styles, NULL, NULL);
1694 if (os->old_heights) {
1695 colrow_restore_state_group (me->cmd.sheet, FALSE,
1696 os->rows,
1697 os->old_heights);
1698 colrow_state_group_destroy (os->old_heights);
1699 os->old_heights = NULL;
1700 colrow_index_list_destroy (os->rows);
1701 os->rows = NULL;
1704 sheet_range_calc_spans (me->cmd.sheet, r, flags);
1705 sheet_flag_style_update_range (me->cmd.sheet, r);
1708 sheet_redraw_all (me->cmd.sheet, FALSE);
1709 g_slist_free (rstyles);
1710 g_slist_free (rsel);
1713 select_selection (me->cmd.sheet, me->selection, wbc);
1715 return FALSE;
1718 static gboolean
1719 cmd_format_redo (GnmCommand *cmd, WorkbookControl *wbc)
1721 CmdFormat *me = CMD_FORMAT (cmd);
1722 GSList *l1, *l2;
1723 gboolean re_fit_height;
1725 g_return_val_if_fail (me != NULL, TRUE);
1727 /* Check for locked cells */
1728 if (cmd_selection_is_locked_effective (me->cmd.sheet, me->selection,
1729 wbc, _("Changing Format")))
1730 return TRUE;
1732 re_fit_height = me->new_style &&
1733 (GNM_SPANCALC_ROW_HEIGHT & gnm_style_required_spanflags (me->new_style));
1735 for (l1 = me->old_styles, l2 = me->selection; l2; l1 = l1->next, l2 = l2->next) {
1736 CmdFormatOldStyle *os = l1->data;
1737 GnmRange const *r = l2->data;
1739 if (me->borders)
1740 sheet_apply_border (me->cmd.sheet, r, me->borders);
1741 if (me->new_style) {
1742 gnm_style_ref (me->new_style);
1743 sheet_apply_style (me->cmd.sheet, r, me->new_style);
1744 if (re_fit_height)
1745 colrow_autofit (me->cmd.sheet, r, FALSE, FALSE,
1746 TRUE, FALSE,
1747 &os->rows, &os->old_heights);
1750 sheet_flag_style_update_range (me->cmd.sheet, r);
1752 sheet_redraw_all (me->cmd.sheet, FALSE);
1753 sheet_mark_dirty (me->cmd.sheet);
1755 select_selection (me->cmd.sheet, me->selection, wbc);
1757 return FALSE;
1760 static void
1761 cmd_format_finalize (GObject *cmd)
1763 CmdFormat *me = CMD_FORMAT (cmd);
1764 int i;
1766 if (me->new_style)
1767 gnm_style_unref (me->new_style);
1768 me->new_style = NULL;
1770 if (me->borders) {
1771 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1772 gnm_style_border_unref (me->borders [i]);
1773 g_free (me->borders);
1774 me->borders = NULL;
1777 if (me->old_styles != NULL) {
1778 GSList *l;
1780 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
1781 CmdFormatOldStyle *os = l->data;
1783 style_list_free (os->styles);
1784 colrow_index_list_destroy (os->rows);
1785 colrow_state_group_destroy (os->old_heights);
1786 g_free (os);
1788 me->old_styles = NULL;
1791 range_fragment_free (me->selection);
1792 me->selection = NULL;
1794 gnm_command_finalize (cmd);
1798 * cmd_format:
1799 * @wbc: the workbook control.
1800 * @sheet: the sheet
1801 * @style: style to apply to the selection
1802 * @borders: borders to apply to the selection
1803 * @opt_translated_name: An optional name to use in place of 'Format Cells'
1805 * If borders is non NULL, then the GnmBorder references are passed,
1806 * the GnmStyle reference is also passed.
1808 * It absorbs the reference to the style.
1810 * Return value: TRUE if there was a problem
1812 gboolean
1813 cmd_selection_format (WorkbookControl *wbc,
1814 GnmStyle *style, GnmBorder **borders,
1815 char const *opt_translated_name)
1817 CmdFormat *me;
1818 GSList *l;
1819 SheetView *sv = wb_control_cur_sheet_view (wbc);
1821 me = g_object_new (CMD_FORMAT_TYPE, NULL);
1823 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
1824 me->new_style = style;
1826 me->cmd.sheet = sv_sheet (sv);
1827 me->cmd.size = 1; /* Updated below. */
1829 me->old_styles = NULL;
1830 for (l = me->selection; l; l = l->next) {
1831 GnmRange const *sel_r = l->data;
1832 GnmRange range = *sel_r;
1833 CmdFormatOldStyle *os;
1835 /* Store the containing range to handle borders */
1836 if (borders != NULL) {
1837 if (range.start.col > 0) range.start.col--;
1838 if (range.start.row > 0) range.start.row--;
1839 if (range.end.col < gnm_sheet_get_last_col (me->cmd.sheet)) range.end.col++;
1840 if (range.end.row < gnm_sheet_get_last_row (me->cmd.sheet)) range.end.row++;
1843 os = g_new (CmdFormatOldStyle, 1);
1845 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
1846 os->pos = range.start;
1847 os->rows = NULL;
1848 os->old_heights = NULL;
1850 me->cmd.size += g_slist_length (os->styles);
1851 me->old_styles = g_slist_append (me->old_styles, os);
1854 if (borders) {
1855 int i;
1857 me->borders = g_new (GnmBorder *, GNM_STYLE_BORDER_EDGE_MAX);
1858 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1859 me->borders [i] = borders [i];
1860 } else
1861 me->borders = NULL;
1863 if (opt_translated_name == NULL) {
1864 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
1866 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing format of %s"), names);
1867 g_free (names);
1868 } else
1869 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
1871 return gnm_command_push_undo (wbc, G_OBJECT (me));
1874 /******************************************************************/
1876 static gboolean
1877 cmd_selection_format_toggle_font_style_filter (PangoAttribute *attribute, PangoAttrType *pt)
1879 return ((attribute->klass->type == *pt) ||
1880 ((PANGO_ATTR_RISE == *pt) && (attribute->klass->type == PANGO_ATTR_SCALE)));
1883 typedef struct {
1884 GOUndo *undo;
1885 PangoAttrType pt;
1886 } csftfs;
1888 static GnmValue *
1889 cmd_selection_format_toggle_font_style_cb (GnmCellIter const *iter, csftfs *closure)
1891 if (iter->cell && iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
1892 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
1893 if (fmt && go_format_is_markup (fmt)) {
1894 const PangoAttrList *old_markup =
1895 go_format_get_markup (fmt);
1896 PangoAttrList *new_markup = pango_attr_list_copy ((PangoAttrList *)old_markup);
1897 PangoAttrList *other = pango_attr_list_filter
1898 (new_markup,
1899 (PangoAttrFilterFunc) cmd_selection_format_toggle_font_style_filter,
1900 &closure->pt);
1901 if (other != NULL) {
1902 GnmSheetRange *sr;
1903 GnmRange r;
1904 range_init_cellpos (&r, &iter->pp.eval);
1905 sr = gnm_sheet_range_new (iter->pp.sheet, &r);
1906 closure->undo = go_undo_combine (closure->undo,
1907 sheet_range_set_markup_undo (sr, new_markup));
1909 pango_attr_list_unref (new_markup);
1910 pango_attr_list_unref (other);
1913 return NULL;
1916 gboolean
1917 cmd_selection_format_toggle_font_style (WorkbookControl *wbc,
1918 GnmStyle *style, GnmStyleElement t)
1920 SheetView *sv = wb_control_cur_sheet_view (wbc);
1921 Sheet *sheet = sv->sheet;
1922 GSList *selection = selection_get_ranges (sv, FALSE), *l;
1923 gboolean result;
1924 char *text, *name;
1925 GOUndo *undo = NULL;
1926 GOUndo *redo = NULL;
1927 PangoAttrType pt;
1930 switch (t) {
1931 case MSTYLE_FONT_BOLD:
1932 pt = PANGO_ATTR_WEIGHT;
1933 break;
1934 case MSTYLE_FONT_ITALIC:
1935 pt = PANGO_ATTR_STYLE;
1936 break;
1937 case MSTYLE_FONT_UNDERLINE:
1938 pt = PANGO_ATTR_UNDERLINE;
1939 break;
1940 case MSTYLE_FONT_STRIKETHROUGH:
1941 pt = PANGO_ATTR_STRIKETHROUGH;
1942 break;
1943 case MSTYLE_FONT_SCRIPT:
1944 pt = PANGO_ATTR_RISE; /* and PANGO_ATTR_SCALE (see ) */
1945 break;
1946 default:
1947 pt = PANGO_ATTR_INVALID;
1948 break;
1952 name = undo_range_list_name (sheet, selection);
1953 text = g_strdup_printf (_("Setting Font Style of %s"), name);
1954 g_free (name);
1956 for (l = selection; l != NULL; l = l->next) {
1957 GnmSheetRange *sr;
1958 undo = go_undo_combine
1959 (undo, clipboard_copy_range_undo (sheet, l->data));
1960 sr = gnm_sheet_range_new (sheet, l->data);
1961 redo = go_undo_combine
1962 (redo, sheet_apply_style_undo (sr, style));
1963 if (pt != PANGO_ATTR_INVALID) {
1964 csftfs closure;
1965 closure.undo = NULL;
1966 closure.pt = pt;
1967 sheet_foreach_cell_in_range
1968 (sheet, CELL_ITER_IGNORE_BLANK, &sr->range,
1969 (CellIterFunc) cmd_selection_format_toggle_font_style_cb,
1970 &closure);
1971 redo = go_undo_combine (redo, closure.undo);
1974 gnm_style_unref (style);
1975 result = cmd_generic (wbc, text, undo, redo);
1976 g_free (text);
1977 range_fragment_free (selection);
1979 return result;
1983 /******************************************************************/
1986 gboolean
1987 cmd_resize_colrow (WorkbookControl *wbc, Sheet *sheet,
1988 gboolean is_cols, ColRowIndexList *selection,
1989 int new_size)
1991 int size = 1;
1992 char *text;
1993 GOUndo *undo = NULL;
1994 GOUndo *redo = NULL;
1995 gboolean is_single, result;
1996 GString *list;
1997 ColRowStateGroup *saved_state;
1999 list = colrow_index_list_to_string (selection, is_cols, &is_single);
2000 gnm_cmd_trunc_descriptor (list, NULL);
2002 if (is_single) {
2003 if (new_size < 0)
2004 text = is_cols
2005 ? g_strdup_printf (_("Autofitting column %s"), list->str)
2006 : g_strdup_printf (_("Autofitting row %s"), list->str);
2007 else if (new_size > 0)
2008 text = is_cols
2009 ? g_strdup_printf (ngettext ("Setting width of column %s to %d pixel",
2010 "Setting width of column %s to %d pixels",
2011 new_size),
2012 list->str, new_size)
2013 : g_strdup_printf (ngettext ("Setting height of row %s to %d pixel",
2014 "Setting height of row %s to %d pixels",
2015 new_size),
2016 list->str, new_size);
2017 else text = is_cols
2018 ? g_strdup_printf (_("Setting width of column %s to default"),
2019 list->str)
2020 : g_strdup_printf (
2021 _("Setting height of row %s to default"), list->str);
2022 } else {
2023 if (new_size < 0)
2024 text = is_cols
2025 ? g_strdup_printf (_("Autofitting columns %s"), list->str)
2026 : g_strdup_printf (_("Autofitting rows %s"), list->str);
2027 else if (new_size > 0)
2028 text = is_cols
2029 ? g_strdup_printf (ngettext("Setting width of columns %s to %d pixel",
2030 "Setting width of columns %s to %d pixels",
2031 new_size),
2032 list->str, new_size)
2033 : g_strdup_printf (ngettext("Setting height of rows %s to %d pixel",
2034 "Setting height of rows %s to %d pixels",
2035 new_size),
2036 list->str, new_size);
2037 else text = is_cols
2038 ? g_strdup_printf (
2039 _("Setting width of columns %s to default"), list->str)
2040 : g_strdup_printf (
2041 _("Setting height of rows %s to default"), list->str);
2043 g_string_free (list, TRUE);
2045 saved_state = colrow_get_sizes (sheet, is_cols, selection, new_size);
2046 undo = gnm_undo_colrow_restore_state_group_new
2047 (sheet, is_cols, colrow_index_list_copy (selection), saved_state);
2049 redo = gnm_undo_colrow_set_sizes_new (sheet, is_cols, selection, new_size, NULL);
2051 result = cmd_generic_with_size (wbc, text, size, undo, redo);
2052 g_free (text);
2054 return result;
2057 gboolean
2058 cmd_autofit_selection (WorkbookControl *wbc, SheetView *sv, Sheet *sheet, gboolean fit_width,
2059 ColRowIndexList *selectionlist)
2061 GOUndo *undo = NULL;
2062 GOUndo *redo = NULL;
2063 gboolean result;
2064 ColRowStateGroup *saved_state;
2065 GSList *l, *selection = selection_get_ranges (sv, TRUE);
2066 gchar *names = undo_range_list_name (sheet, selection);
2067 gchar const *format = fit_width ?
2068 N_("Autofitting width of %s") : N_("Autofitting height of %s");
2069 gchar *text = g_strdup_printf (_(format), names);
2071 g_free (names);
2073 saved_state = colrow_get_sizes (sheet, fit_width, selectionlist, -1);
2074 undo = gnm_undo_colrow_restore_state_group_new
2075 (sheet, fit_width, colrow_index_list_copy (selectionlist), saved_state);
2077 for (l = selection; l != NULL; l = l->next)
2078 redo = go_undo_combine
2079 (redo, gnm_undo_colrow_set_sizes_new
2080 (sheet, fit_width, NULL, -1, l->data));
2082 result = cmd_generic (wbc, text, undo, redo);
2083 g_free (text);
2084 return result;
2088 /******************************************************************/
2090 #define CMD_SORT_TYPE (cmd_sort_get_type ())
2091 #define CMD_SORT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SORT_TYPE, CmdSort))
2093 typedef struct {
2094 GnmCommand cmd;
2096 GnmSortData *data;
2097 int *perm;
2098 GnmCellRegion *old_contents;
2099 } CmdSort;
2101 MAKE_GNM_COMMAND (CmdSort, cmd_sort, NULL)
2103 static void
2104 cmd_sort_finalize (GObject *cmd)
2106 CmdSort *me = CMD_SORT (cmd);
2108 if (me->data != NULL)
2109 gnm_sort_data_destroy (me->data);
2110 g_free (me->perm);
2111 if (me->old_contents != NULL)
2112 cellregion_unref (me->old_contents);
2114 gnm_command_finalize (cmd);
2117 static gboolean
2118 cmd_sort_undo (GnmCommand *cmd, WorkbookControl *wbc)
2120 CmdSort *me = CMD_SORT (cmd);
2121 GnmSortData *data = me->data;
2122 GnmPasteTarget pt;
2124 paste_target_init (&pt, data->sheet, data->range,
2125 PASTE_CONTENTS | PASTE_FORMATS |
2126 (data->retain_formats ? PASTE_FORMATS : 0));
2127 clipboard_paste_region (me->old_contents,
2128 &pt,
2129 GO_CMD_CONTEXT (wbc));
2131 return FALSE;
2134 static gboolean
2135 cmd_sort_redo (GnmCommand *cmd, WorkbookControl *wbc)
2137 CmdSort *me = CMD_SORT (cmd);
2138 GnmSortData *data = me->data;
2140 /* Check for locks */
2141 if (cmd_cell_range_is_locked_effective
2142 (data->sheet, data->range, wbc, _("Sorting")))
2143 return TRUE;
2145 if (me->perm)
2146 gnm_sort_position (data, me->perm, GO_CMD_CONTEXT (wbc));
2147 else {
2148 me->old_contents =
2149 clipboard_copy_range (data->sheet, data->range);
2150 me->cmd.size = cellregion_cmd_size (me->old_contents);
2151 me->perm = gnm_sort_contents (data, GO_CMD_CONTEXT (wbc));
2154 return FALSE;
2157 gboolean
2158 cmd_sort (WorkbookControl *wbc, GnmSortData *data)
2160 CmdSort *me;
2161 char *desc;
2163 g_return_val_if_fail (data != NULL, TRUE);
2165 desc = g_strdup_printf (_("Sorting %s"), range_as_string (data->range));
2166 if (sheet_range_contains_merges_or_arrays (data->sheet, data->range, GO_CMD_CONTEXT (wbc), desc, TRUE, TRUE)) {
2167 gnm_sort_data_destroy (data);
2168 g_free (desc);
2169 return TRUE;
2172 me = g_object_new (CMD_SORT_TYPE, NULL);
2174 me->data = data;
2175 me->perm = NULL;
2176 me->cmd.sheet = data->sheet;
2177 me->cmd.size = 1; /* Changed in initial redo. */
2178 me->cmd.cmd_descriptor = desc;
2180 return gnm_command_push_undo (wbc, G_OBJECT (me));
2183 /******************************************************************/
2185 #define CMD_COLROW_HIDE_TYPE (cmd_colrow_hide_get_type ())
2186 #define CMD_COLROW_HIDE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_HIDE_TYPE, CmdColRowHide))
2188 typedef struct {
2189 GnmCommand cmd;
2191 gboolean is_cols;
2192 ColRowVisList *hide, *show;
2193 } CmdColRowHide;
2195 static void
2196 cmd_colrow_hide_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2198 CmdColRowHide const *orig = (CmdColRowHide const *) cmd;
2199 cmd_selection_colrow_hide (wbc, orig->is_cols, orig->show != NULL);
2201 MAKE_GNM_COMMAND (CmdColRowHide, cmd_colrow_hide, cmd_colrow_hide_repeat)
2204 * cmd_colrow_hide_correct_selection:
2206 * Try to ensure that the selection/cursor is set to a visible row/col
2208 * Added to fix bug 38179
2209 * Removed because the result is irritating and the bug is actually XL
2210 * compatibile
2212 static void
2213 cmd_colrow_hide_correct_selection (G_GNUC_UNUSED CmdColRowHide *me, G_GNUC_UNUSED WorkbookControl *wbc)
2215 #if 0
2216 int x, y, index;
2217 SheetView *sv = sheet_get_view (me->cmd.sheet,
2218 wb_control_view (wbc));
2220 index = colrow_find_adjacent_visible (me->cmd.sheet, me->is_cols,
2221 me->is_cols ? sv->edit_pos.col : sv->edit_pos.row,
2222 TRUE);
2224 x = me->is_cols ? sv->edit_pos.row : index;
2225 y = me->is_cols ? index : sv->edit_pos.col;
2227 if (index >= 0) {
2228 sv_selection_reset (sv);
2229 if (me->is_cols)
2230 sv_selection_add_full (sv, y, x, y, 0,
2231 y, gnm_sheet_get_last_row (sheet),
2232 GNM_SELECTION_MODE_ADD);
2233 else
2234 sv_selection_add_full (sv, y, x, 0, x,
2235 gnm_sheet_get_last_col (sheet), x,
2236 GNM_SELECTION_MODE_ADD);
2238 #endif
2241 static gboolean
2242 cmd_colrow_hide_undo (GnmCommand *cmd, WorkbookControl *wbc)
2244 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2246 g_return_val_if_fail (me != NULL, TRUE);
2248 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2249 TRUE, me->hide);
2250 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2251 FALSE, me->show);
2253 if (me->show != NULL)
2254 cmd_colrow_hide_correct_selection (me, wbc);
2256 return FALSE;
2259 static gboolean
2260 cmd_colrow_hide_redo (GnmCommand *cmd, WorkbookControl *wbc)
2262 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2264 g_return_val_if_fail (me != NULL, TRUE);
2266 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2267 FALSE, me->hide);
2268 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2269 TRUE, me->show);
2271 if (me->hide != NULL)
2272 cmd_colrow_hide_correct_selection (me, wbc);
2274 return FALSE;
2277 static void
2278 cmd_colrow_hide_finalize (GObject *cmd)
2280 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2281 colrow_vis_list_destroy (me->hide);
2282 me->hide = NULL;
2283 colrow_vis_list_destroy (me->show);
2284 me->show = NULL;
2285 gnm_command_finalize (cmd);
2288 gboolean
2289 cmd_selection_colrow_hide (WorkbookControl *wbc,
2290 gboolean is_cols, gboolean visible)
2292 CmdColRowHide *me;
2293 SheetView *sv = wb_control_cur_sheet_view (wbc);
2294 int n;
2295 Sheet *sheet;
2296 GSList *show = NULL, *hide = NULL;
2298 if (visible)
2299 show = colrow_get_visibility_toggle (sv, is_cols, TRUE);
2300 else
2301 hide = colrow_get_visibility_toggle (sv, is_cols, FALSE);
2302 n = colrow_vis_list_length (hide) + colrow_vis_list_length (show);
2303 sheet = sv_sheet (sv);
2305 if (!visible) {
2306 /* If these are the last colrows to hide, check with the user */
2307 int count = 0;
2308 if (is_cols) {
2309 int i, max = gnm_sheet_get_max_cols (sheet);
2310 ColRowInfo *ci;
2311 for (i = 0 ; i < max ; i++)
2312 if (NULL ==
2313 (ci = sheet_col_get (sheet, i)) ||
2314 (ci->visible))
2315 count++;
2316 } else {
2317 int i, max = gnm_sheet_get_max_rows (sheet);
2318 ColRowInfo *ci;
2319 for (i = 0 ; i < max ; i++)
2320 if (NULL ==
2321 (ci = sheet_row_get (sheet, i)) ||
2322 (ci->visible))
2323 count++;
2325 if (count <= n) {
2326 gchar const *text = is_cols ?
2327 _("Are you sure that you want to hide all columns? "
2328 "If you do so you can unhide them with the "
2329 "'Format\342\206\222Column\342\206\222Unhide' "
2330 "menu item.") :
2331 _("Are you sure that you want to hide all rows? "
2332 "If you do so you can unhide them with the "
2333 "'Format\342\206\222Row\342\206\222Unhide' "
2334 "menu item.");
2335 if (!go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)),
2336 FALSE, "%s", text)) {
2337 colrow_vis_list_destroy (show);
2338 colrow_vis_list_destroy (hide);
2339 return TRUE;
2344 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2345 me->show = show;
2346 me->hide = hide;
2347 me->is_cols = is_cols;
2348 me->cmd.sheet = sheet;
2349 me->cmd.size = 1 + g_slist_length (hide) + g_slist_length (show);
2350 me->cmd.cmd_descriptor = g_strdup (is_cols
2351 ? (visible ? _("Unhide columns") : _("Hide columns"))
2352 : (visible ? _("Unhide rows") : _("Hide rows")));
2354 return gnm_command_push_undo (wbc, G_OBJECT (me));
2357 gboolean
2358 cmd_selection_outline_change (WorkbookControl *wbc,
2359 gboolean is_cols, int index, int depth)
2361 CmdColRowHide *me;
2362 ColRowInfo const *cri;
2363 int first = -1, last = -1;
2364 gboolean visible = FALSE;
2365 int d;
2366 Sheet *sheet = wb_control_cur_sheet (wbc);
2367 SheetView *sv = wb_control_cur_sheet_view (wbc);
2369 cri = sheet_colrow_get_info (sheet, index, is_cols);
2371 d = cri->outline_level;
2372 if (depth > d)
2373 depth = d;
2375 /* Nodes only collapse when selected directly, selecting at a lower
2376 * level is a standard toggle. */
2377 if (depth == d) {
2378 if ((is_cols ? sheet->outline_symbols_right : sheet->outline_symbols_below)) {
2379 if (index > 0) {
2380 ColRowInfo const *prev =
2381 sheet_colrow_get (sheet, index-1, is_cols);
2383 if (prev != NULL && prev->outline_level > d) {
2384 visible = (depth == d && cri->is_collapsed);
2385 last = index - 1;
2386 first = colrow_find_outline_bound (sheet, is_cols,
2387 last, d+1, FALSE);
2390 } else if (index+1 < colrow_max (is_cols, sheet)) {
2391 ColRowInfo const *next =
2392 sheet_colrow_get (sheet, index+1, is_cols);
2394 if (next != NULL && next->outline_level > d) {
2395 visible = (depth == d && cri->is_collapsed);
2396 first = index + 1;
2397 last = colrow_find_outline_bound (sheet, is_cols,
2398 first, d+1, TRUE);
2403 /* If nothing done yet do a simple collapse */
2404 if (first < 0 && cri->outline_level > 0) {
2405 if (depth < d)
2406 ++depth;
2407 first = colrow_find_outline_bound (sheet, is_cols, index, depth, FALSE);
2408 last = colrow_find_outline_bound (sheet, is_cols, index, depth, TRUE);
2409 visible = FALSE;
2411 if (first == last && depth > cri->outline_level)
2412 return TRUE;
2415 if (first < 0 || last < 0)
2416 return TRUE;
2418 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2420 me->is_cols = is_cols;
2421 me->hide = me->show = NULL;
2422 if (visible)
2423 me->show = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2424 TRUE, first, last);
2425 else
2426 me->hide = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2427 FALSE, first, last);
2429 me->cmd.sheet = sv_sheet (sv);
2430 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2431 me->cmd.cmd_descriptor = g_strdup (is_cols
2432 ? (visible ? _("Expand columns") : _("Collapse columns"))
2433 : (visible ? _("Expand rows") : _("Collapse rows")));
2435 return gnm_command_push_undo (wbc, G_OBJECT (me));
2438 gboolean
2439 cmd_global_outline_change (WorkbookControl *wbc, gboolean is_cols, int depth)
2441 CmdColRowHide *me;
2442 ColRowVisList *hide, *show;
2443 SheetView *sv = wb_control_cur_sheet_view (wbc);
2445 colrow_get_global_outline (sv_sheet (sv), is_cols, depth, &show, &hide);
2447 if (show == NULL && hide == NULL)
2448 return TRUE;
2450 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2451 me->is_cols = is_cols;
2452 me->hide = hide;
2453 me->show = show;
2454 me->cmd.sheet = sv_sheet (sv);
2455 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2456 me->cmd.cmd_descriptor = g_strdup_printf (is_cols
2457 ? _("Show column outline %d") : _("Show row outline %d"), depth);
2459 return gnm_command_push_undo (wbc, G_OBJECT (me));
2462 /******************************************************************/
2464 #define CMD_GROUP_TYPE (cmd_group_get_type ())
2465 #define CMD_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GROUP_TYPE, CmdGroup))
2467 typedef struct {
2468 GnmCommand cmd;
2470 GnmRange range;
2471 gboolean is_cols;
2472 gboolean group;
2473 } CmdGroup;
2475 static void
2476 cmd_group_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2478 CmdGroup const *orig = (CmdGroup const *) cmd;
2479 cmd_selection_group (wbc, orig->is_cols, orig->group);
2481 MAKE_GNM_COMMAND (CmdGroup, cmd_group, cmd_group_repeat)
2483 static gboolean
2484 cmd_group_undo (GnmCommand *cmd,
2485 G_GNUC_UNUSED WorkbookControl *wbc)
2487 CmdGroup const *me = CMD_GROUP (cmd);
2488 sheet_colrow_group_ungroup (me->cmd.sheet,
2489 &me->range, me->is_cols, !me->group);
2490 return FALSE;
2493 static gboolean
2494 cmd_group_redo (GnmCommand *cmd,
2495 G_GNUC_UNUSED WorkbookControl *wbc)
2497 CmdGroup const *me = CMD_GROUP (cmd);
2498 sheet_colrow_group_ungroup (me->cmd.sheet,
2499 &me->range, me->is_cols, me->group);
2500 return FALSE;
2503 static void
2504 cmd_group_finalize (GObject *cmd)
2506 gnm_command_finalize (cmd);
2509 gboolean
2510 cmd_selection_group (WorkbookControl *wbc,
2511 gboolean is_cols, gboolean group)
2513 CmdGroup *me;
2514 SheetView *sv;
2515 GnmRange r;
2517 g_return_val_if_fail (wbc != NULL, TRUE);
2519 sv = wb_control_cur_sheet_view (wbc);
2520 r = *selection_first_range (sv, NULL, NULL);
2522 /* Check if this really is possible and display an error if it's not */
2523 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2524 if (group) {
2525 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2526 ? _("Those columns are already grouped")
2527 : _("Those rows are already grouped"));
2528 return TRUE;
2531 /* see if the user selected the col/row with the marker too */
2532 if (is_cols) {
2533 if (r.start.col != r.end.col) {
2534 if (sv->sheet->outline_symbols_right)
2535 r.end.col--;
2536 else
2537 r.start.col++;
2539 } else {
2540 if (r.start.row != r.end.row) {
2541 if (sv->sheet->outline_symbols_below)
2542 r.end.row--;
2543 else
2544 r.start.row++;
2548 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2549 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2550 ? _("Those columns are not grouped, you can't ungroup them")
2551 : _("Those rows are not grouped, you can't ungroup them"));
2552 return TRUE;
2556 me = g_object_new (CMD_GROUP_TYPE, NULL);
2557 me->is_cols = is_cols;
2558 me->group = group;
2559 me->range = r;
2561 me->cmd.sheet = sv->sheet;
2562 me->cmd.size = 1;
2563 me->cmd.cmd_descriptor = is_cols
2564 ? g_strdup_printf (group ? _("Group columns %s") : _("Ungroup columns %s"),
2565 cols_name (me->range.start.col, me->range.end.col))
2566 : g_strdup_printf (group ? _("Group rows %d:%d") : _("Ungroup rows %d:%d"),
2567 me->range.start.row + 1, me->range.end.row + 1);
2569 return gnm_command_push_undo (wbc, G_OBJECT (me));
2572 /******************************************************************/
2574 #define CMD_PASTE_CUT_TYPE (cmd_paste_cut_get_type ())
2575 #define CMD_PASTE_CUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_CUT_TYPE, CmdPasteCut))
2577 typedef struct {
2578 GnmCommand cmd;
2580 GnmExprRelocateInfo info;
2581 GSList *paste_contents;
2582 GOUndo *reloc_undo;
2583 gboolean move_selection;
2584 ColRowStateList *saved_sizes;
2586 /* handle redo-ing an undo with contents from a deleted sheet */
2587 GnmCellRegion *deleted_sheet_contents;
2588 } CmdPasteCut;
2590 MAKE_GNM_COMMAND (CmdPasteCut, cmd_paste_cut, NULL)
2592 typedef struct {
2593 GnmPasteTarget pt;
2594 GnmCellRegion *contents;
2595 } PasteContent;
2598 * cmd_paste_cut_update:
2600 * Utility routine to update things when we are transfering between sheets and
2601 * workbooks.
2603 static void
2604 cmd_paste_cut_update (GnmExprRelocateInfo const *info,
2605 G_GNUC_UNUSED WorkbookControl *wbc)
2607 Sheet *o = info->origin_sheet;
2608 Sheet *t = info->target_sheet;
2610 /* Dirty and update both sheets */
2611 sheet_mark_dirty (t);
2612 sheet_update (t);
2614 if (IS_SHEET (o) && o != t) {
2615 sheet_mark_dirty (o);
2616 sheet_update (o);
2620 static gboolean
2621 cmd_paste_cut_undo (GnmCommand *cmd, WorkbookControl *wbc)
2623 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2624 GnmExprRelocateInfo reverse;
2626 g_return_val_if_fail (me != NULL, TRUE);
2627 g_return_val_if_fail (me->paste_contents != NULL, TRUE);
2628 g_return_val_if_fail (me->deleted_sheet_contents == NULL, TRUE);
2630 reverse.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
2631 reverse.target_sheet = me->info.origin_sheet;
2632 reverse.origin_sheet = me->info.target_sheet;
2633 reverse.origin = me->info.origin;
2634 range_translate (&reverse.origin,
2635 me->info.origin_sheet, /* FIXME: What sheet? */
2636 me->info.col_offset,
2637 me->info.row_offset);
2638 reverse.col_offset = -me->info.col_offset;
2639 reverse.row_offset = -me->info.row_offset;
2641 /* Move things back being careful NOT to invalidate the src region */
2642 if (IS_SHEET (me->info.origin_sheet))
2643 sheet_move_range (&reverse, NULL, GO_CMD_CONTEXT (wbc));
2644 else
2645 me->deleted_sheet_contents = clipboard_copy_range (
2646 reverse.origin_sheet, &reverse.origin);
2648 /* Restore the original row heights */
2649 colrow_set_states (me->info.target_sheet, FALSE,
2650 reverse.origin.start.row, me->saved_sizes);
2651 colrow_state_list_destroy (me->saved_sizes);
2652 me->saved_sizes = NULL;
2654 if (me->reloc_undo) {
2655 go_undo_undo (me->reloc_undo);
2656 g_object_unref (me->reloc_undo);
2657 me->reloc_undo = NULL;
2660 while (me->paste_contents) {
2661 PasteContent *pc = me->paste_contents->data;
2662 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2664 clipboard_paste_region (pc->contents, &pc->pt, GO_CMD_CONTEXT (wbc));
2665 cellregion_unref (pc->contents);
2666 g_free (pc);
2669 /* Force update of the status area */
2670 sheet_flag_status_update_range (me->info.target_sheet, NULL);
2672 cmd_paste_cut_update (&me->info, wbc);
2674 /* Select the original region */
2675 if (me->move_selection && IS_SHEET (me->info.origin_sheet))
2676 select_range (me->info.origin_sheet,
2677 &me->info.origin,
2678 wbc);
2680 return FALSE;
2683 static gboolean
2684 cmd_paste_cut_redo (GnmCommand *cmd, WorkbookControl *wbc)
2686 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2687 GnmRange tmp;
2689 g_return_val_if_fail (me != NULL, TRUE);
2690 g_return_val_if_fail (me->paste_contents == NULL, TRUE);
2692 tmp = me->info.origin;
2693 range_translate (&tmp, me->info.origin_sheet, /* FIXME: What sheet? */
2694 me->info.col_offset, me->info.row_offset);
2695 range_normalize (&tmp);
2697 g_return_val_if_fail (range_is_sane (&tmp), TRUE);
2699 if (me->info.origin_sheet != me->info.target_sheet ||
2700 !range_overlap (&me->info.origin, &tmp)) {
2701 PasteContent *pc = g_new (PasteContent, 1);
2702 paste_target_init (&pc->pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2703 pc->contents = clipboard_copy_range (me->info.target_sheet, &tmp);
2704 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2705 } else {
2706 /* need to store any portions of the paste target
2707 * that do not overlap with the source.
2709 GSList *ptr, *frag = range_split_ranges (&me->info.origin, &tmp);
2710 for (ptr = frag ; ptr != NULL ; ptr = ptr->next) {
2711 GnmRange *r = ptr->data;
2713 if (!range_overlap (&me->info.origin, r)) {
2714 PasteContent *pc = g_new (PasteContent, 1);
2715 paste_target_init (&pc->pt, me->info.target_sheet, r, PASTE_ALL_SHEET);
2716 pc->contents = clipboard_copy_range (me->info.target_sheet, r);
2717 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2719 g_free (r);
2721 g_slist_free (frag);
2724 /* rare corner case. If the origin sheet has been deleted */
2725 if (!IS_SHEET (me->info.origin_sheet)) {
2726 GnmPasteTarget pt;
2727 paste_target_init (&pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2728 sheet_clear_region (pt.sheet,
2729 tmp.start.col, tmp.start.row, tmp.end.col, tmp.end.row,
2730 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
2731 GO_CMD_CONTEXT (wbc));
2732 clipboard_paste_region (me->deleted_sheet_contents,
2733 &pt, GO_CMD_CONTEXT (wbc));
2734 cellregion_unref (me->deleted_sheet_contents);
2735 me->deleted_sheet_contents = NULL;
2736 } else
2737 sheet_move_range (&me->info, &me->reloc_undo, GO_CMD_CONTEXT (wbc));
2739 cmd_paste_cut_update (&me->info, wbc);
2741 /* Backup row heights and adjust row heights to fit */
2742 me->saved_sizes = colrow_get_states (me->info.target_sheet, FALSE, tmp.start.row, tmp.end.row);
2743 rows_height_update (me->info.target_sheet, &tmp, FALSE);
2745 /* Make sure the destination is selected */
2746 if (me->move_selection)
2747 select_range (me->info.target_sheet, &tmp, wbc);
2749 return FALSE;
2752 static void
2753 cmd_paste_cut_finalize (GObject *cmd)
2755 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2757 if (me->saved_sizes)
2758 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
2759 while (me->paste_contents) {
2760 PasteContent *pc = me->paste_contents->data;
2761 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2762 cellregion_unref (pc->contents);
2763 g_free (pc);
2765 if (me->reloc_undo) {
2766 g_object_unref (me->reloc_undo);
2767 me->reloc_undo = NULL;
2769 if (me->deleted_sheet_contents) {
2770 cellregion_unref (me->deleted_sheet_contents);
2771 me->deleted_sheet_contents = NULL;
2774 gnm_command_finalize (cmd);
2777 gboolean
2778 cmd_paste_cut (WorkbookControl *wbc, GnmExprRelocateInfo const *info,
2779 gboolean move_selection, char *descriptor)
2781 CmdPasteCut *me;
2782 GnmRange r;
2783 char *where;
2785 g_return_val_if_fail (info != NULL, TRUE);
2787 /* This is vacuous */
2788 if (info->origin_sheet == info->target_sheet &&
2789 info->col_offset == 0 && info->row_offset == 0)
2790 return TRUE;
2792 /* FIXME: Do we want to show the destination range as well ? */
2793 where = undo_range_name (info->origin_sheet, &info->origin);
2794 if (descriptor == NULL)
2795 descriptor = g_strdup_printf (_("Moving %s"), where);
2796 g_free (where);
2798 g_return_val_if_fail (info != NULL, TRUE);
2800 r = info->origin;
2801 if (range_translate (&r, info->target_sheet,
2802 info->col_offset, info->row_offset)) {
2804 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), descriptor,
2805 _("is beyond sheet boundaries"));
2806 g_free (descriptor);
2807 return TRUE;
2810 /* Check array subdivision & merged regions */
2811 if (sheet_range_splits_region (info->target_sheet, &r,
2812 (info->origin_sheet == info->target_sheet)
2813 ? &info->origin : NULL, GO_CMD_CONTEXT (wbc), descriptor)) {
2814 g_free (descriptor);
2815 return TRUE;
2818 me = g_object_new (CMD_PASTE_CUT_TYPE, NULL);
2820 me->info = *info;
2821 me->paste_contents = NULL;
2822 me->deleted_sheet_contents = NULL;
2823 me->reloc_undo = NULL;
2824 me->move_selection = move_selection;
2825 me->saved_sizes = NULL;
2827 me->cmd.sheet = NULL; /* we have potentially two different. */
2828 me->cmd.size = 1; /* FIXME? */
2829 me->cmd.cmd_descriptor = descriptor;
2831 /* NOTE : if the destination workbook is different from the source
2832 * workbook should we have undo elements in both menus ?? It seems
2833 * poor form to hit undo in 1 window and effect another...
2835 * Maybe queue it as two different commands, as a clear in one book
2836 * and a paste in the other. This is not symmetric though. What
2837 * happens to the cells in the original sheet that now reference the
2838 * cells in the other? When do they reset to the original?
2840 * Probably when the clear in the original is undone.
2843 return gnm_command_push_undo (wbc, G_OBJECT (me));
2846 /******************************************************************/
2848 static void
2849 warn_if_date_trouble (WorkbookControl *wbc, GnmCellRegion *cr)
2851 Workbook *wb = wb_control_get_workbook (wbc);
2852 const GODateConventions *wb_date_conv = workbook_date_conv (wb);
2854 if (cr->date_conv == NULL)
2855 return;
2856 if (go_date_conv_equal (cr->date_conv, wb_date_conv))
2857 return;
2859 /* We would like to show a warning, but it seems we cannot via a context. */
2861 GError *err;
2862 err = g_error_new (go_error_invalid(), 0,
2863 _("Copying between files with different date conventions.\n"
2864 "It is possible that some dates could be copied\n"
2865 "incorrectly."));
2866 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
2867 g_error_free (err);
2872 #define CMD_PASTE_COPY_TYPE (cmd_paste_copy_get_type ())
2873 #define CMD_PASTE_COPY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_COPY_TYPE, CmdPasteCopy))
2875 typedef struct {
2876 GnmCommand cmd;
2878 GnmCellRegion *contents;
2879 GSList *pasted_objects,*orig_contents_objects;
2880 GnmPasteTarget dst;
2881 gboolean has_been_through_cycle;
2882 gboolean only_objects;
2883 gboolean single_merge_to_single_merge;
2884 } CmdPasteCopy;
2886 static void
2887 cmd_paste_copy_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2889 CmdPasteCopy const *orig = (CmdPasteCopy const *) cmd;
2890 GnmPasteTarget new_dst;
2891 SheetView *sv = wb_control_cur_sheet_view (wbc);
2892 GnmRange const *r = selection_first_range (sv,
2893 GO_CMD_CONTEXT (wbc), _("Paste Copy"));
2894 GnmCellRegion *newcr;
2896 if (r == NULL)
2897 return;
2899 paste_target_init (&new_dst, sv_sheet (sv), r, orig->dst.paste_flags);
2900 newcr = clipboard_copy_range (orig->dst.sheet, &orig->dst.range);
2901 cmd_paste_copy (wbc, &new_dst, newcr);
2902 cellregion_unref (newcr);
2904 MAKE_GNM_COMMAND (CmdPasteCopy, cmd_paste_copy, cmd_paste_copy_repeat)
2906 static int
2907 by_addr (gconstpointer a, gconstpointer b)
2909 if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b))
2910 return -1;
2911 if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b))
2912 return +1;
2913 return 0;
2917 * get_new_objects:
2918 * @sheet: #Sheet to query
2919 * @old: (element-type SheetObject): list of objects to disregard
2921 * Returns: (transfer full) (element-type SheetObject): A list of new objects
2922 * in sheet since @old was collected.
2924 static GSList *
2925 get_new_objects (Sheet *sheet, GSList *old)
2927 GSList *objs =
2928 g_slist_sort (g_slist_copy_deep (sheet->sheet_objects,
2929 (GCopyFunc)g_object_ref,
2930 NULL),
2931 by_addr);
2932 GSList *p = objs, *last = NULL;
2934 while (old) {
2935 int c = -1;
2936 while (p && (c = by_addr (p->data, old->data)) < 0) {
2937 last = p;
2938 p = p->next;
2941 old = old->next;
2943 if (c == 0) {
2944 GSList *next = p->next;
2945 if (last)
2946 last->next = next;
2947 else
2948 objs = next;
2949 g_slist_free_1 (p);
2950 p = next;
2954 return objs;
2957 static void
2958 cmd_paste_copy_select_obj (SheetObject *so, SheetControlGUI *scg)
2960 scg_object_select (scg, so);
2963 static gboolean
2964 cmd_paste_copy_impl (GnmCommand *cmd, WorkbookControl *wbc,
2965 gboolean is_undo)
2967 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
2968 GnmCellRegion *contents;
2969 GSList *old_objects;
2971 g_return_val_if_fail (me != NULL, TRUE);
2972 g_return_val_if_fail (me->contents != NULL, TRUE);
2974 g_slist_foreach (me->pasted_objects,
2975 (GFunc)sheet_object_clear_sheet,
2976 NULL);
2977 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
2978 me->pasted_objects = NULL;
2979 old_objects = get_new_objects (me->dst.sheet, NULL);
2981 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
2982 if (me->has_been_through_cycle)
2983 me->dst.paste_flags =
2984 PASTE_CONTENTS |
2985 PASTE_COLUMN_WIDTHS | PASTE_ROW_HEIGHTS |
2986 (me->dst.paste_flags & PASTE_ALL_SHEET);
2988 if (clipboard_paste_region (me->contents, &me->dst,
2989 GO_CMD_CONTEXT (wbc))) {
2990 /* There was a problem, avoid leaking */
2991 cellregion_unref (contents);
2992 g_slist_free_full (old_objects, g_object_unref);
2993 return TRUE;
2996 me->pasted_objects = get_new_objects (me->dst.sheet, old_objects);
2997 g_slist_free_full (old_objects, g_object_unref);
2999 if (!is_undo && !me->has_been_through_cycle) {
3000 colrow_autofit (me->dst.sheet, &me->dst.range, FALSE, FALSE,
3001 TRUE, FALSE,
3002 NULL, NULL);
3003 colrow_autofit (me->dst.sheet, &me->dst.range, TRUE, TRUE,
3004 TRUE, FALSE,
3005 NULL, NULL);
3008 if (is_undo) {
3009 // We cannot use the random set of objects at the target
3010 // location. http://bugzilla.gnome.org/show_bug.cgi?id=308300
3011 g_slist_free_full (contents->objects, g_object_unref);
3012 contents->objects = g_slist_copy_deep
3013 (me->orig_contents_objects,
3014 (GCopyFunc)sheet_object_dup, NULL);
3015 } else {
3016 GSList *l;
3017 for (l = contents->objects; l; l = l->next) {
3018 SheetObject *so = l->data;
3019 if (sheet_object_get_sheet (so)) {
3020 g_object_unref (so);
3021 l->data = NULL;
3022 } else {
3023 // Object got deleted by paste, so keep it for
3024 // undo. See bugzilla 732653
3027 contents->objects =
3028 g_slist_remove_all (contents->objects, NULL);
3031 cellregion_unref (me->contents);
3032 me->contents = contents;
3033 me->has_been_through_cycle = TRUE;
3035 /* Select the newly pasted contents (this queues a redraw) */
3036 if (me->only_objects && GNM_IS_WBC_GTK (wbc)) {
3037 SheetControlGUI *scg =
3038 wbcg_get_nth_scg (WBC_GTK (wbc),
3039 cmd->sheet->index_in_wb);
3040 scg_object_unselect (scg, NULL);
3041 g_slist_foreach (me->pasted_objects,
3042 (GFunc) cmd_paste_copy_select_obj, scg);
3044 select_range (me->dst.sheet, &me->dst.range, wbc);
3046 return FALSE;
3049 static gboolean
3050 cmd_paste_copy_undo (GnmCommand *cmd, WorkbookControl *wbc)
3052 return cmd_paste_copy_impl (cmd, wbc, TRUE);
3055 static gboolean
3056 cmd_paste_copy_redo (GnmCommand *cmd, WorkbookControl *wbc)
3058 return cmd_paste_copy_impl (cmd, wbc, FALSE);
3061 static void
3062 cmd_paste_copy_finalize (GObject *cmd)
3064 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
3066 if (me->contents) {
3067 cellregion_unref (me->contents);
3068 me->contents = NULL;
3070 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
3071 g_slist_free_full (me->orig_contents_objects, (GDestroyNotify)g_object_unref);
3072 gnm_command_finalize (cmd);
3076 * cmd_paste_copy will ref cr as needed.
3078 gboolean
3079 cmd_paste_copy (WorkbookControl *wbc,
3080 GnmPasteTarget const *pt, GnmCellRegion *cr)
3082 CmdPasteCopy *me;
3083 int n_r = 1, n_c = 1;
3084 char *range_name;
3085 GnmRange const *merge_src;
3087 g_return_val_if_fail (pt != NULL, TRUE);
3088 g_return_val_if_fail (IS_SHEET (pt->sheet), TRUE);
3089 g_return_val_if_fail (cr != NULL, TRUE);
3091 cellregion_ref (cr);
3093 me = g_object_new (CMD_PASTE_COPY_TYPE, NULL);
3095 me->cmd.sheet = pt->sheet;
3096 me->cmd.size = 1; /* FIXME? */
3098 range_name = undo_range_name (pt->sheet, &pt->range);
3099 me->cmd.cmd_descriptor = g_strdup_printf (_("Pasting into %s"),
3100 range_name);
3101 g_free (range_name);
3103 me->dst = *pt;
3104 me->contents = cr;
3105 me->has_been_through_cycle = FALSE;
3106 me->only_objects = (cr->cols < 1 || cr->rows < 1);
3107 me->pasted_objects = NULL;
3108 me->orig_contents_objects =
3109 g_slist_copy_deep (cr->objects,
3110 (GCopyFunc)sheet_object_dup, NULL);
3111 me->single_merge_to_single_merge = FALSE;
3113 /* If the input is only objects ignore all this range stuff */
3114 if (!me->only_objects) {
3115 /* see if we need to do any tiling */
3116 GnmRange *r = &me->dst.range;
3117 if (g_slist_length (cr->merged) == 1 &&
3118 (NULL != (merge_src = cr->merged->data)) &&
3119 range_height (merge_src) == cr->rows &&
3120 range_width (merge_src) == cr->cols) {
3121 /* We are copying from a single merge */
3122 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3123 if (merge != NULL && range_equal (r, merge)) {
3124 /* To a single merge */
3125 me->single_merge_to_single_merge = TRUE;
3126 n_c = n_r = 1;
3127 me->dst.paste_flags |= PASTE_DONT_MERGE;
3128 goto copy_ready;
3132 if (pt->paste_flags & PASTE_TRANSPOSE) {
3133 n_c = range_width (r) / cr->rows;
3134 if (n_c < 1) n_c = 1;
3135 r->end.col = r->start.col + n_c * cr->rows - 1;
3137 n_r = range_height (r) / cr->cols;
3138 if (n_r < 1) n_r = 1;
3139 r->end.row = r->start.row + n_r * cr->cols - 1;
3140 } else {
3141 /* Before looking for tiling if we are not transposing,
3142 * allow pasting a full col or row from a single cell */
3143 n_c = range_width (r);
3144 if (n_c == 1 && cr->cols == gnm_sheet_get_max_cols (me->cmd.sheet)) {
3145 r->start.col = 0;
3146 r->end.col = gnm_sheet_get_last_col (me->cmd.sheet);
3147 } else {
3148 n_c /= cr->cols;
3149 if (n_c < 1) n_c = 1;
3150 r->end.col = r->start.col + n_c * cr->cols - 1;
3153 n_r = range_height (r);
3154 if (n_r == 1 && cr->rows == gnm_sheet_get_max_rows (me->cmd.sheet)) {
3155 r->start.row = 0;
3156 r->end.row = gnm_sheet_get_last_row (me->cmd.sheet);
3157 } else {
3158 n_r /= cr->rows;
3159 if (n_r < 1) n_r = 1;
3160 r->end.row = r->start.row + n_r * cr->rows - 1;
3164 if (cr->cols != 1 || cr->rows != 1) {
3165 /* Note: when the source is a single cell, a single target merge is special */
3166 /* see clipboard.c (clipboard_paste_region) */
3167 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3168 if (merge != NULL && range_equal (r, merge)) {
3169 /* destination is a single merge */
3170 /* enlarge it such that the source fits */
3171 if (pt->paste_flags & PASTE_TRANSPOSE) {
3172 if ((r->end.col - r->start.col + 1) < cr->rows)
3173 r->end.col = r->start.col + cr->rows - 1;
3174 if ((r->end.row - r->start.row + 1) < cr->cols)
3175 r->end.row = r->start.row + cr->cols - 1;
3176 } else {
3177 if ((r->end.col - r->start.col + 1) < cr->cols)
3178 r->end.col = r->start.col + cr->cols - 1;
3179 if ((r->end.row - r->start.row + 1) < cr->rows)
3180 r->end.row = r->start.row + cr->rows - 1;
3186 if (n_c * (gnm_float)n_r > 10000.) {
3187 char *number = g_strdup_printf ("%0.0" GNM_FORMAT_f,
3188 (gnm_float)n_c * (gnm_float)n_r);
3189 gboolean result = go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)), FALSE,
3190 _("Do you really want to paste "
3191 "%s copies?"), number);
3192 g_free (number);
3193 if (!result) {
3194 g_object_unref (me);
3195 return TRUE;
3199 copy_ready:
3200 /* Use translate to do a quiet sanity check */
3201 if (range_translate (&me->dst.range, pt->sheet, 0, 0)) {
3202 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
3203 me->cmd.cmd_descriptor,
3204 _("is beyond sheet boundaries"));
3205 g_object_unref (me);
3206 return TRUE;
3209 /* no need to test if all we have are objects or are copying from */
3210 /*a single merge to a single merge*/
3211 if ((!me->only_objects) && (!me->single_merge_to_single_merge)&&
3212 sheet_range_splits_region (pt->sheet, &me->dst.range,
3213 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
3214 g_object_unref (me);
3215 return TRUE;
3218 warn_if_date_trouble (wbc, cr);
3220 return gnm_command_push_undo (wbc, G_OBJECT (me));
3223 /******************************************************************/
3225 #define CMD_AUTOFILL_TYPE (cmd_autofill_get_type ())
3226 #define CMD_AUTOFILL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFILL_TYPE, CmdAutofill))
3228 typedef struct {
3229 GnmCommand cmd;
3231 GnmCellRegion *contents;
3232 GnmPasteTarget dst;
3233 GnmRange src;
3234 int base_col, base_row, w, h, end_col, end_row;
3235 gboolean default_increment;
3236 gboolean inverse_autofill;
3237 ColRowIndexList *columns;
3238 ColRowStateGroup *old_widths;
3239 } CmdAutofill;
3241 static void
3242 cmd_autofill_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3244 CmdAutofill const *orig = (CmdAutofill const *) cmd;
3245 SheetView *sv = wb_control_cur_sheet_view (wbc);
3246 GnmRange const *r = selection_first_range (sv,
3247 GO_CMD_CONTEXT (wbc), _("Autofill"));
3249 if (r == NULL)
3250 return;
3252 cmd_autofill (wbc, sv_sheet (sv), orig->default_increment,
3253 r->start.col, r->start.row, range_width (r), range_height (r),
3254 r->start.col + (orig->end_col - orig->base_col),
3255 r->start.row + (orig->end_row - orig->base_row),
3256 orig->inverse_autofill);
3258 MAKE_GNM_COMMAND (CmdAutofill, cmd_autofill, cmd_autofill_repeat)
3260 static gboolean
3261 cmd_autofill_undo (GnmCommand *cmd, WorkbookControl *wbc)
3263 CmdAutofill *me = CMD_AUTOFILL (cmd);
3264 gboolean res;
3266 g_return_val_if_fail (wbc != NULL, TRUE);
3267 g_return_val_if_fail (me != NULL, TRUE);
3268 g_return_val_if_fail (me->contents != NULL, TRUE);
3270 res = clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc));
3271 cellregion_unref (me->contents);
3272 me->contents = NULL;
3274 if (me->old_widths) {
3275 colrow_restore_state_group (me->cmd.sheet, TRUE,
3276 me->columns,
3277 me->old_widths);
3278 colrow_state_group_destroy (me->old_widths);
3279 me->old_widths = NULL;
3280 colrow_index_list_destroy (me->columns);
3281 me->columns = NULL;
3284 if (res)
3285 return TRUE;
3287 select_range (me->dst.sheet, &me->src, wbc);
3289 return FALSE;
3292 static gboolean
3293 cmd_autofill_redo (GnmCommand *cmd, WorkbookControl *wbc)
3295 CmdAutofill *me = CMD_AUTOFILL (cmd);
3296 GnmRange r;
3298 g_return_val_if_fail (me != NULL, TRUE);
3299 g_return_val_if_fail (me->contents == NULL, TRUE);
3301 me->contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
3303 g_return_val_if_fail (me->contents != NULL, TRUE);
3305 /* FIXME : when we split autofill to support hints and better validation
3306 * move this in there.
3308 /* MW: May 2006: we support hints now. What's this about? */
3309 sheet_clear_region (me->dst.sheet,
3310 me->dst.range.start.col, me->dst.range.start.row,
3311 me->dst.range.end.col, me->dst.range.end.row,
3312 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3313 GO_CMD_CONTEXT (wbc));
3315 if (me->cmd.size == 1)
3316 me->cmd.size += cellregion_cmd_size (me->contents);
3317 if (me->inverse_autofill)
3318 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3319 me->end_col, me->end_row, me->w, me->h,
3320 me->base_col, me->base_row);
3321 else
3322 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3323 me->base_col, me->base_row, me->w, me->h,
3324 me->end_col, me->end_row);
3326 colrow_autofit (me->cmd.sheet, &me->dst.range, TRUE, TRUE,
3327 TRUE, FALSE,
3328 &me->columns, &me->old_widths);
3330 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3331 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3332 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3334 r = range_union (&me->dst.range, &me->src);
3335 select_range (me->dst.sheet, &r, wbc);
3337 return FALSE;
3340 static void
3341 cmd_autofill_finalize (GObject *cmd)
3343 CmdAutofill *me = CMD_AUTOFILL (cmd);
3345 if (me->contents) {
3346 cellregion_unref (me->contents);
3347 me->contents = NULL;
3349 colrow_index_list_destroy (me->columns);
3350 colrow_state_group_destroy (me->old_widths);
3351 gnm_command_finalize (cmd);
3354 gboolean
3355 cmd_autofill (WorkbookControl *wbc, Sheet *sheet,
3356 gboolean default_increment,
3357 int base_col, int base_row,
3358 int w, int h, int end_col, int end_row,
3359 gboolean inverse_autofill)
3361 CmdAutofill *me;
3362 GnmRange target, src;
3364 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3366 /* This would be meaningless */
3367 if (base_col+w-1 == end_col && base_row+h-1 == end_row)
3368 return FALSE;
3370 if (inverse_autofill) {
3371 if (end_col != base_col + w - 1) {
3372 range_init (&target, base_col, base_row,
3373 end_col - w, end_row);
3374 range_init (&src, end_col - w + 1, base_row,
3375 end_col, end_row);
3376 } else {
3377 range_init (&target, base_col, base_row,
3378 end_col, end_row - h);
3379 range_init (&src, base_col, end_row - h + 1,
3380 end_col, end_row);
3382 } else {
3383 if (end_col != base_col + w - 1) {
3384 range_init (&target, base_col + w, base_row,
3385 end_col, end_row);
3386 range_init (&src, base_col, base_row,
3387 base_col + w - 1, end_row);
3388 } else {
3389 range_init (&target, base_col, base_row + h,
3390 end_col, end_row);
3391 range_init (&src, base_col, base_row,
3392 end_col, base_row + h - 1);
3396 /* We don't support clearing regions, when a user uses the autofill
3397 * cursor to 'shrink' a selection
3399 if (target.start.col > target.end.col || target.start.row > target.end.row)
3400 return TRUE;
3402 /* Check arrays or merged regions in src or target regions */
3403 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")) ||
3404 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")))
3405 return TRUE;
3407 me = g_object_new (CMD_AUTOFILL_TYPE, NULL);
3409 me->contents = NULL;
3410 me->dst.sheet = sheet;
3411 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3412 me->dst.range = target;
3413 me->src = src;
3415 me->base_col = base_col;
3416 me->base_row = base_row,
3417 me->w = w;
3418 me->h = h;
3419 me->end_col = end_col;
3420 me->end_row = end_row;
3421 me->default_increment = default_increment;
3422 me->inverse_autofill = inverse_autofill;
3424 me->cmd.sheet = sheet;
3425 me->cmd.size = 1; /* Changed in initial redo. */
3426 me->cmd.cmd_descriptor = g_strdup_printf (_("Autofilling %s"),
3427 range_as_string (&me->dst.range));
3429 return gnm_command_push_undo (wbc, G_OBJECT (me));
3432 /******************************************************************/
3434 #define CMD_COPYREL_TYPE (cmd_copyrel_get_type ())
3435 #define CMD_COPYREL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COPYREL_TYPE, CmdCopyRel))
3437 typedef struct {
3438 GnmCommand cmd;
3440 GOUndo *undo;
3441 GnmPasteTarget dst, src;
3442 int dx, dy;
3443 char const *name;
3444 } CmdCopyRel;
3446 static void
3447 cmd_copyrel_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3449 CmdCopyRel const *orig = (CmdCopyRel const *) cmd;
3450 cmd_copyrel (wbc, orig->dx, orig->dy, orig->name);
3452 MAKE_GNM_COMMAND (CmdCopyRel, cmd_copyrel, cmd_copyrel_repeat)
3454 static gboolean
3455 cmd_copyrel_undo (GnmCommand *cmd, WorkbookControl *wbc)
3457 CmdCopyRel *me = CMD_COPYREL (cmd);
3459 g_return_val_if_fail (wbc != NULL, TRUE);
3460 g_return_val_if_fail (me != NULL, TRUE);
3461 g_return_val_if_fail (me->undo != NULL, TRUE);
3463 go_undo_undo (me->undo);
3465 /* Select the newly pasted contents (this queues a redraw) */
3466 select_range (me->dst.sheet, &me->dst.range, wbc);
3468 return FALSE;
3471 static gboolean
3472 cmd_copyrel_redo (GnmCommand *cmd, WorkbookControl *wbc)
3474 CmdCopyRel *me = CMD_COPYREL (cmd);
3475 GnmCellRegion *contents;
3476 gboolean res;
3478 g_return_val_if_fail (me != NULL, TRUE);
3480 sheet_clear_region (me->dst.sheet,
3481 me->dst.range.start.col, me->dst.range.start.row,
3482 me->dst.range.end.col, me->dst.range.end.row,
3483 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3484 GO_CMD_CONTEXT (wbc));
3486 contents = clipboard_copy_range (me->src.sheet, &me->src.range);
3487 res = clipboard_paste_region (contents, &me->dst, GO_CMD_CONTEXT (wbc));
3488 cellregion_unref (contents);
3489 if (res)
3490 return TRUE;
3492 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3493 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3494 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3496 /* Select the newly pasted contents (this queues a redraw) */
3497 select_range (me->dst.sheet, &me->dst.range, wbc);
3499 return FALSE;
3502 static void
3503 cmd_copyrel_finalize (GObject *cmd)
3505 CmdCopyRel *me = CMD_COPYREL (cmd);
3507 if (me->undo)
3508 g_object_unref (me->undo);
3510 gnm_command_finalize (cmd);
3513 gboolean
3514 cmd_copyrel (WorkbookControl *wbc,
3515 int dx, int dy,
3516 char const *name)
3518 CmdCopyRel *me;
3519 GnmRange target, src;
3520 SheetView *sv = wb_control_cur_sheet_view (wbc);
3521 Sheet *sheet = sv->sheet;
3522 GnmRange const *selr =
3523 selection_first_range (sv, GO_CMD_CONTEXT (wbc), name);
3525 g_return_val_if_fail (dx == 0 || dy == 0, TRUE);
3527 if (!selr)
3528 return FALSE;
3530 target = *selr;
3531 range_normalize (&target);
3532 src.start = src.end = target.start;
3534 if (dy) {
3535 src.end.col = target.end.col;
3536 if (target.start.row != target.end.row)
3537 target.start.row++;
3538 else
3539 src.start.row = src.end.row = (target.start.row + dy);
3542 if (dx) {
3543 src.end.row = target.end.row;
3544 if (target.start.col != target.end.col)
3545 target.start.col++;
3546 else
3547 src.start.col = src.end.col = (target.start.col + dx);
3550 if (src.start.col < 0 || src.start.col >= gnm_sheet_get_max_cols (sheet) ||
3551 src.start.row < 0 || src.start.row >= gnm_sheet_get_max_rows (sheet))
3552 return FALSE;
3554 /* Check arrays or merged regions in src or target regions */
3555 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), name) ||
3556 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), name))
3557 return TRUE;
3559 me = g_object_new (CMD_COPYREL_TYPE, NULL);
3561 me->dst.sheet = sheet;
3562 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3563 me->dst.range = target;
3564 me->src.sheet = sheet;
3565 me->src.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3566 me->src.range = src;
3567 me->dx = dx;
3568 me->dy = dy;
3569 me->name = name;
3570 me->undo = clipboard_copy_range_undo (me->dst.sheet, &me->dst.range);
3572 me->cmd.sheet = sheet;
3573 me->cmd.size = 1;
3574 me->cmd.cmd_descriptor = g_strdup (name);
3576 return gnm_command_push_undo (wbc, G_OBJECT (me));
3579 /******************************************************************/
3582 #define CMD_AUTOFORMAT_TYPE (cmd_autoformat_get_type ())
3583 #define CMD_AUTOFORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFORMAT_TYPE, CmdAutoFormat))
3585 typedef struct {
3586 GnmCellPos pos;
3587 GnmStyleList *styles;
3588 } CmdAutoFormatOldStyle;
3590 typedef struct {
3591 GnmCommand cmd;
3593 GSList *selection; /* Selections on the sheet */
3594 GSList *old_styles; /* Older styles, one style_list per selection range*/
3596 GnmFT *ft; /* Template that has been applied */
3597 } CmdAutoFormat;
3599 static void
3600 cmd_autoformat_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3602 CmdAutoFormat const *orig = (CmdAutoFormat const *) cmd;
3603 cmd_selection_autoformat (wbc, gnm_ft_clone (orig->ft));
3605 MAKE_GNM_COMMAND (CmdAutoFormat, cmd_autoformat, cmd_autoformat_repeat)
3607 static gboolean
3608 cmd_autoformat_undo (GnmCommand *cmd,
3609 G_GNUC_UNUSED WorkbookControl *wbc)
3611 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3613 g_return_val_if_fail (me != NULL, TRUE);
3615 if (me->old_styles) {
3616 GSList *l1 = me->old_styles;
3617 GSList *l2 = me->selection;
3619 for (; l1; l1 = l1->next, l2 = l2->next) {
3620 GnmRange *r;
3621 CmdAutoFormatOldStyle *os = l1->data;
3622 GnmSpanCalcFlags flags = sheet_style_set_list (me->cmd.sheet,
3623 &os->pos, os->styles, NULL, NULL);
3625 g_return_val_if_fail (l2 && l2->data, TRUE);
3627 r = l2->data;
3628 sheet_range_calc_spans (me->cmd.sheet, r, flags);
3629 if (flags != GNM_SPANCALC_SIMPLE)
3630 rows_height_update (me->cmd.sheet, r, TRUE);
3634 return FALSE;
3637 static gboolean
3638 cmd_autoformat_redo (GnmCommand *cmd,
3639 G_GNUC_UNUSED WorkbookControl *wbc)
3641 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3643 g_return_val_if_fail (me != NULL, TRUE);
3645 gnm_ft_apply_to_sheet_regions (me->ft,
3646 me->cmd.sheet, me->selection);
3648 return FALSE;
3651 static void
3652 cmd_autoformat_finalize (GObject *cmd)
3654 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3656 if (me->old_styles != NULL) {
3657 GSList *l;
3659 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
3660 CmdAutoFormatOldStyle *os = l->data;
3662 if (os->styles)
3663 style_list_free (os->styles);
3665 g_free (os);
3668 me->old_styles = NULL;
3671 range_fragment_free (me->selection);
3672 me->selection = NULL;
3674 gnm_ft_free (me->ft);
3676 gnm_command_finalize (cmd);
3680 * cmd_selection_autoformat:
3681 * @wbc: the context.
3682 * @ft: The format template that was applied
3684 * Return value: TRUE if there was a problem
3686 gboolean
3687 cmd_selection_autoformat (WorkbookControl *wbc, GnmFT *ft)
3689 CmdAutoFormat *me;
3690 char *names;
3691 GSList *l;
3692 SheetView *sv = wb_control_cur_sheet_view (wbc);
3694 me = g_object_new (CMD_AUTOFORMAT_TYPE, NULL);
3696 me->selection = selection_get_ranges (sv, FALSE); /* Regions may overlap */
3697 me->ft = ft;
3698 me->cmd.sheet = sv_sheet (sv);
3699 me->cmd.size = 1; /* FIXME? */
3701 if (!gnm_ft_check_valid (ft, me->selection, GO_CMD_CONTEXT (wbc))) {
3702 g_object_unref (me);
3703 return TRUE;
3706 me->old_styles = NULL;
3707 for (l = me->selection; l; l = l->next) {
3708 CmdFormatOldStyle *os;
3709 GnmRange range = *((GnmRange const *) l->data);
3711 /* Store the containing range to handle borders */
3712 if (range.start.col > 0) range.start.col--;
3713 if (range.start.row > 0) range.start.row--;
3714 if (range.end.col < gnm_sheet_get_last_col (sv->sheet)) range.end.col++;
3715 if (range.end.row < gnm_sheet_get_last_row (sv->sheet)) range.end.row++;
3717 os = g_new (CmdFormatOldStyle, 1);
3719 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
3720 os->pos = range.start;
3722 me->old_styles = g_slist_append (me->old_styles, os);
3725 names = undo_range_list_name (me->cmd.sheet, me->selection);
3726 me->cmd.cmd_descriptor = g_strdup_printf (_("Autoformatting %s"),
3727 names);
3728 g_free (names);
3730 return gnm_command_push_undo (wbc, G_OBJECT (me));
3733 /******************************************************************/
3735 #define CMD_UNMERGE_CELLS_TYPE (cmd_unmerge_cells_get_type ())
3736 #define CMD_UNMERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_UNMERGE_CELLS_TYPE, CmdUnmergeCells))
3738 typedef struct {
3739 GnmCommand cmd;
3741 Sheet *sheet;
3742 GArray *unmerged_regions;
3743 GArray *ranges;
3744 } CmdUnmergeCells;
3746 static void
3747 cmd_unmerge_cells_repeat (G_GNUC_UNUSED GnmCommand const *cmd, WorkbookControl *wbc)
3749 SheetView *sv = wb_control_cur_sheet_view (wbc);
3750 GSList *range_list = selection_get_ranges (sv, FALSE);
3751 cmd_unmerge_cells (wbc, sv_sheet (sv), range_list);
3752 range_fragment_free (range_list);
3754 MAKE_GNM_COMMAND (CmdUnmergeCells, cmd_unmerge_cells, cmd_unmerge_cells_repeat)
3756 static gboolean
3757 cmd_unmerge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3759 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3760 unsigned i;
3762 g_return_val_if_fail (me != NULL, TRUE);
3763 g_return_val_if_fail (me->unmerged_regions != NULL, TRUE);
3765 for (i = 0 ; i < me->unmerged_regions->len ; ++i) {
3766 GnmRange const *tmp = &(g_array_index (me->unmerged_regions, GnmRange, i));
3767 sheet_redraw_range (me->cmd.sheet, tmp);
3768 gnm_sheet_merge_add (me->cmd.sheet, tmp, TRUE, GO_CMD_CONTEXT (wbc));
3769 sheet_range_calc_spans (me->cmd.sheet, tmp, GNM_SPANCALC_RE_RENDER);
3772 g_array_free (me->unmerged_regions, TRUE);
3773 me->unmerged_regions = NULL;
3775 return FALSE;
3778 static gboolean
3779 cmd_unmerge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3781 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3782 unsigned i;
3784 g_return_val_if_fail (me != NULL, TRUE);
3785 g_return_val_if_fail (me->unmerged_regions == NULL, TRUE);
3787 me->unmerged_regions = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3788 for (i = 0 ; i < me->ranges->len ; ++i) {
3789 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (me->cmd.sheet,
3790 &(g_array_index (me->ranges, GnmRange, i)));
3791 for (ptr = merged ; ptr != NULL ; ptr = ptr->next) {
3792 GnmRange const *pr = ptr->data;
3793 GnmRange const tmp = *pr;
3794 g_array_append_val (me->unmerged_regions, tmp);
3795 gnm_sheet_merge_remove (me->cmd.sheet, &tmp);
3796 sheet_range_calc_spans (me->cmd.sheet, &tmp,
3797 GNM_SPANCALC_RE_RENDER);
3799 g_slist_free (merged);
3802 return FALSE;
3805 static void
3806 cmd_unmerge_cells_finalize (GObject *cmd)
3808 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3810 if (me->unmerged_regions != NULL) {
3811 g_array_free (me->unmerged_regions, TRUE);
3812 me->unmerged_regions = NULL;
3814 if (me->ranges != NULL) {
3815 g_array_free (me->ranges, TRUE);
3816 me->ranges = NULL;
3819 gnm_command_finalize (cmd);
3823 * cmd_unmerge_cells:
3824 * @wbc: the context.
3825 * @sheet: #Sheet
3826 * @selection: (element-type GnmRange): selection.
3828 * Return value: TRUE if there was a problem
3830 gboolean
3831 cmd_unmerge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection)
3833 CmdUnmergeCells *me;
3834 char *names;
3836 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3838 me = g_object_new (CMD_UNMERGE_CELLS_TYPE, NULL);
3840 me->cmd.sheet = sheet;
3841 me->cmd.size = 1;
3843 names = undo_range_list_name (sheet, selection);
3844 me->cmd.cmd_descriptor = g_strdup_printf (_("Unmerging %s"), names);
3845 g_free (names);
3847 me->unmerged_regions = NULL;
3848 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3849 for ( ; selection != NULL ; selection = selection->next) {
3850 GSList *merged = gnm_sheet_merge_get_overlap (sheet, selection->data);
3851 if (merged != NULL) {
3852 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
3853 g_slist_free (merged);
3857 if (me->ranges->len <= 0) {
3858 g_object_unref (me);
3859 return TRUE;
3862 return gnm_command_push_undo (wbc, G_OBJECT (me));
3865 /******************************************************************/
3867 #define CMD_MERGE_CELLS_TYPE (cmd_merge_cells_get_type ())
3868 #define CMD_MERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_CELLS_TYPE, CmdMergeCells))
3870 typedef struct {
3871 GnmCommand cmd;
3872 GArray *ranges;
3873 GSList *old_contents;
3874 gboolean center;
3875 } CmdMergeCells;
3877 static void
3878 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc);
3880 MAKE_GNM_COMMAND (CmdMergeCells, cmd_merge_cells, cmd_merge_cells_repeat)
3882 static void
3883 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3885 SheetView *sv = wb_control_cur_sheet_view (wbc);
3886 GSList *range_list = selection_get_ranges (sv, FALSE);
3887 cmd_merge_cells (wbc, sv_sheet (sv), range_list,
3888 CMD_MERGE_CELLS (cmd)->center);
3889 range_fragment_free (range_list);
3892 static gboolean
3893 cmd_merge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3895 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3896 unsigned i, flags;
3898 g_return_val_if_fail (me != NULL, TRUE);
3900 for (i = 0 ; i < me->ranges->len ; ++i) {
3901 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3902 gnm_sheet_merge_remove (me->cmd.sheet, r);
3905 /* Avoid pasting comments that are at 0,0. Redo copies the target
3906 * region (including all comments) . If there was a comment in the top
3907 * left we would end up duplicating it. */
3908 flags = PASTE_CONTENTS | PASTE_FORMATS | PASTE_COMMENTS |
3909 PASTE_IGNORE_COMMENTS_AT_ORIGIN;
3910 if (me->center)
3911 flags |= PASTE_FORMATS;
3912 for (i = 0 ; i < me->ranges->len ; ++i) {
3913 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3914 GnmPasteTarget pt;
3915 GnmCellRegion * c;
3917 g_return_val_if_fail (me->old_contents != NULL, TRUE);
3919 c = me->old_contents->data;
3920 clipboard_paste_region (c,
3921 paste_target_init (&pt, me->cmd.sheet, r, flags),
3922 GO_CMD_CONTEXT (wbc));
3923 cellregion_unref (c);
3924 me->old_contents = g_slist_remove (me->old_contents, c);
3926 g_return_val_if_fail (me->old_contents == NULL, TRUE);
3928 return FALSE;
3931 static gboolean
3932 cmd_merge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3934 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3935 GnmStyle *align_center = NULL;
3936 Sheet *sheet;
3937 unsigned i;
3939 g_return_val_if_fail (me != NULL, TRUE);
3941 if (me->center) {
3942 align_center = gnm_style_new ();
3943 gnm_style_set_align_h (align_center, GNM_HALIGN_CENTER);
3945 sheet = me->cmd.sheet;
3946 for (i = 0 ; i < me->ranges->len ; ++i) {
3947 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3948 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (sheet, r);
3950 /* save contents before removing contained merged regions */
3951 me->old_contents = g_slist_prepend (me->old_contents,
3952 clipboard_copy_range (sheet, r));
3953 for (ptr = merged ; ptr != NULL ; ptr = ptr->next)
3954 gnm_sheet_merge_remove (sheet, ptr->data);
3955 g_slist_free (merged);
3957 gnm_sheet_merge_add (sheet, r, TRUE, GO_CMD_CONTEXT (wbc));
3958 if (me->center)
3959 sheet_apply_style (me->cmd.sheet, r, align_center);
3962 if (me->center)
3963 gnm_style_unref (align_center);
3964 me->old_contents = g_slist_reverse (me->old_contents);
3965 return FALSE;
3968 static void
3969 cmd_merge_cells_finalize (GObject *cmd)
3971 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3973 if (me->old_contents != NULL) {
3974 GSList *l;
3975 for (l = me->old_contents ; l != NULL ; l = g_slist_remove (l, l->data))
3976 cellregion_unref (l->data);
3977 me->old_contents = NULL;
3980 if (me->ranges != NULL) {
3981 g_array_free (me->ranges, TRUE);
3982 me->ranges = NULL;
3985 gnm_command_finalize (cmd);
3989 * cmd_merge_cells:
3990 * @wbc: the context.
3991 * @sheet: #Sheet
3992 * @selection: (element-type GnmRange): selection.
3993 * @center:
3995 * Return value: %TRUE if there was a problem
3997 gboolean
3998 cmd_merge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection,
3999 gboolean center)
4001 CmdMergeCells *me;
4002 char *names;
4004 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4006 me = g_object_new (CMD_MERGE_CELLS_TYPE, NULL);
4008 me->cmd.sheet = sheet;
4009 me->cmd.size = 1;
4011 names = undo_range_list_name (sheet, selection);
4012 me->cmd.cmd_descriptor =
4013 g_strdup_printf ((center ? _("Merge and Center %s") :_("Merging %s")), names);
4014 g_free (names);
4016 me->center = center;
4017 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
4018 for ( ; selection != NULL ; selection = selection->next) {
4019 GnmRange const *exist;
4020 GnmRange const *r = selection->data;
4021 if (range_is_singleton (selection->data))
4022 continue;
4023 if (NULL != (exist = gnm_sheet_merge_is_corner (sheet, &r->start)) &&
4024 range_equal (r, exist))
4025 continue;
4026 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
4029 if (me->ranges->len <= 0) {
4030 g_object_unref (me);
4031 return TRUE;
4034 return gnm_command_push_undo (wbc, G_OBJECT (me));
4037 /******************************************************************/
4039 #define CMD_SEARCH_REPLACE_TYPE (cmd_search_replace_get_type())
4040 #define CMD_SEARCH_REPLACE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SEARCH_REPLACE_TYPE, CmdSearchReplace))
4042 typedef struct {
4043 GnmCommand cmd;
4044 GnmSearchReplace *sr;
4047 * Undo/redo use this list of SearchReplaceItems to do their
4048 * work. Note, that it is possible for a cell to occur
4049 * multiple times in the list.
4051 GList *cells;
4052 } CmdSearchReplace;
4054 MAKE_GNM_COMMAND (CmdSearchReplace, cmd_search_replace, NULL)
4056 typedef enum { SRI_text, SRI_comment } SearchReplaceItemType;
4058 typedef struct {
4059 GnmEvalPos pos;
4060 SearchReplaceItemType old_type, new_type;
4061 union {
4062 char *text;
4063 char *comment;
4064 } old, new;
4065 } SearchReplaceItem;
4068 static void
4069 cmd_search_replace_update_after_action (CmdSearchReplace *me,
4070 WorkbookControl *wbc)
4072 GList *tmp;
4073 Sheet *last_sheet = NULL;
4075 for (tmp = me->cells; tmp; tmp = tmp->next) {
4076 SearchReplaceItem *sri = tmp->data;
4077 if (sri->pos.sheet != last_sheet) {
4078 last_sheet = sri->pos.sheet;
4079 update_after_action (last_sheet, wbc);
4085 static gboolean
4086 cmd_search_replace_undo (GnmCommand *cmd,
4087 WorkbookControl *wbc)
4089 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4090 GList *tmp;
4092 /* Undo does replacements backwards. */
4093 for (tmp = g_list_last (me->cells); tmp; tmp = tmp->prev) {
4094 SearchReplaceItem *sri = tmp->data;
4095 switch (sri->old_type) {
4096 case SRI_text:
4098 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4099 sri->pos.eval.col,
4100 sri->pos.eval.row);
4101 sheet_cell_set_text (cell, sri->old.text, NULL);
4102 break;
4104 case SRI_comment:
4106 GnmComment *comment =
4107 sheet_get_comment (sri->pos.sheet,
4108 &sri->pos.eval);
4109 if (comment) {
4110 cell_comment_text_set (comment, sri->old.comment);
4111 } else {
4112 g_warning ("Undo/redo broken.");
4115 break;
4118 cmd_search_replace_update_after_action (me, wbc);
4120 return FALSE;
4123 static gboolean
4124 cmd_search_replace_redo (GnmCommand *cmd,
4125 WorkbookControl *wbc)
4127 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4128 GList *tmp;
4130 /* Redo does replacements forward. */
4131 for (tmp = me->cells; tmp; tmp = tmp->next) {
4132 SearchReplaceItem *sri = tmp->data;
4133 switch (sri->new_type) {
4134 case SRI_text:
4136 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4137 sri->pos.eval.col,
4138 sri->pos.eval.row);
4139 sheet_cell_set_text (cell, sri->new.text, NULL);
4140 break;
4142 case SRI_comment:
4144 GnmComment *comment =
4145 sheet_get_comment (sri->pos.sheet,
4146 &sri->pos.eval);
4147 if (comment) {
4148 cell_comment_text_set (comment, sri->new.comment);
4149 } else {
4150 g_warning ("Undo/redo broken.");
4153 break;
4156 cmd_search_replace_update_after_action (me, wbc);
4158 return FALSE;
4161 static gboolean
4162 cmd_search_replace_do_cell (CmdSearchReplace *me, GnmEvalPos *ep,
4163 gboolean test_run)
4165 GnmSearchReplace *sr = me->sr;
4167 GnmSearchReplaceCellResult cell_res;
4168 GnmSearchReplaceCommentResult comment_res;
4170 if (gnm_search_replace_cell (sr, ep, TRUE, &cell_res)) {
4171 GnmExprTop const *texpr;
4172 GnmValue *val;
4173 gboolean err;
4174 GnmParsePos pp;
4176 parse_pos_init_evalpos (&pp, ep);
4177 parse_text_value_or_expr (&pp, cell_res.new_text, &val, &texpr);
4180 * FIXME: this is a hack, but parse_text_value_or_expr
4181 * does not have a better way of signaling an error.
4183 err = (val &&
4184 gnm_expr_char_start_p (cell_res.new_text) &&
4185 !go_format_is_text (gnm_cell_get_format (cell_res.cell)));
4186 value_release (val);
4187 if (texpr) gnm_expr_top_unref (texpr);
4189 if (err) {
4190 if (test_run) {
4191 gnm_search_replace_query_fail (sr, &cell_res);
4192 g_free (cell_res.old_text);
4193 g_free (cell_res.new_text);
4194 return TRUE;
4195 } else {
4196 switch (sr->error_behaviour) {
4197 case GNM_SRE_ERROR: {
4198 GnmExprTop const *ee =
4199 gnm_expr_top_new
4200 (gnm_expr_new_funcall1
4201 (gnm_func_lookup ("ERROR", NULL),
4202 gnm_expr_new_constant
4203 (value_new_string_nocopy (cell_res.new_text))));
4204 GnmConventionsOut out;
4206 out.accum = g_string_new ("=");
4207 out.pp = &pp;
4208 out.convs = pp.sheet->convs;
4209 gnm_expr_top_as_gstring (ee, &out);
4210 gnm_expr_top_unref (ee);
4211 cell_res.new_text = g_string_free (out.accum, FALSE);
4212 err = FALSE;
4213 break;
4215 case GNM_SRE_STRING: {
4216 GString *s = g_string_new ("'");
4217 g_string_append (s, cell_res.new_text);
4218 g_free (cell_res.new_text);
4219 cell_res.new_text = g_string_free (s, FALSE);
4220 err = FALSE;
4221 break;
4223 case GNM_SRE_FAIL:
4224 g_assert_not_reached ();
4225 case GNM_SRE_SKIP:
4226 default:
4227 ; /* Nothing */
4232 if (!err && !test_run) {
4233 int res = gnm_search_replace_query_cell
4234 (sr, &cell_res);
4235 gboolean doit = (res == GTK_RESPONSE_YES);
4237 if (res == GTK_RESPONSE_CANCEL) {
4238 g_free (cell_res.old_text);
4239 g_free (cell_res.new_text);
4240 return TRUE;
4243 if (doit) {
4244 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4246 sheet_cell_set_text (cell_res.cell, cell_res.new_text, NULL);
4248 sri->pos = *ep;
4249 sri->old_type = sri->new_type = SRI_text;
4250 sri->old.text = cell_res.old_text;
4251 sri->new.text = cell_res.new_text;
4252 me->cells = g_list_prepend (me->cells, sri);
4254 cell_res.old_text = cell_res.new_text = NULL;
4258 g_free (cell_res.new_text);
4259 g_free (cell_res.old_text);
4262 if (!test_run &&
4263 gnm_search_replace_comment (sr, ep, TRUE, &comment_res)) {
4264 int res = gnm_search_replace_query_comment
4265 (sr, ep, &comment_res);
4266 gboolean doit = (res == GTK_RESPONSE_YES);
4268 if (doit) {
4269 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4270 sri->pos = *ep;
4271 sri->old_type = sri->new_type = SRI_comment;
4272 sri->old.comment = g_strdup (comment_res.old_text);
4273 sri->new.comment = comment_res.new_text;
4274 me->cells = g_list_prepend (me->cells, sri);
4276 cell_comment_text_set (comment_res.comment, comment_res.new_text);
4277 } else {
4278 g_free (comment_res.new_text);
4279 if (res == GTK_RESPONSE_CANCEL)
4280 return TRUE;
4284 return FALSE;
4288 static gboolean
4289 cmd_search_replace_do (CmdSearchReplace *me, gboolean test_run,
4290 WorkbookControl *wbc)
4292 GnmSearchReplace *sr = me->sr;
4293 GPtrArray *cells;
4294 gboolean result = FALSE;
4295 unsigned i;
4297 if (test_run) {
4298 switch (sr->error_behaviour) {
4299 case GNM_SRE_SKIP:
4300 case GNM_SRE_QUERY:
4301 case GNM_SRE_ERROR:
4302 case GNM_SRE_STRING:
4303 /* An error is not a problem. */
4304 return FALSE;
4306 case GNM_SRE_FAIL:
4307 ; /* Nothing. */
4311 cells = gnm_search_collect_cells (sr);
4313 for (i = 0; i < cells->len; i++) {
4314 GnmEvalPos *ep = g_ptr_array_index (cells, i);
4316 if (cmd_search_replace_do_cell (me, ep, test_run)) {
4317 result = TRUE;
4318 break;
4322 gnm_search_collect_cells_free (cells);
4324 if (!test_run) {
4325 /* Cells were added in the wrong order. Correct. */
4326 me->cells = g_list_reverse (me->cells);
4328 cmd_search_replace_update_after_action (me, wbc);
4331 return result;
4335 static void
4336 cmd_search_replace_finalize (GObject *cmd)
4338 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4339 GList *tmp;
4341 for (tmp = me->cells; tmp; tmp = tmp->next) {
4342 SearchReplaceItem *sri = tmp->data;
4343 switch (sri->old_type) {
4344 case SRI_text:
4345 g_free (sri->old.text);
4346 break;
4347 case SRI_comment:
4348 g_free (sri->old.comment);
4349 break;
4351 switch (sri->new_type) {
4352 case SRI_text:
4353 g_free (sri->new.text);
4354 break;
4355 case SRI_comment:
4356 g_free (sri->new.comment);
4357 break;
4359 g_free (sri);
4361 g_list_free (me->cells);
4362 g_object_unref (me->sr);
4364 gnm_command_finalize (cmd);
4367 gboolean
4368 cmd_search_replace (WorkbookControl *wbc, GnmSearchReplace *sr)
4370 CmdSearchReplace *me;
4372 g_return_val_if_fail (sr != NULL, TRUE);
4374 me = g_object_new (CMD_SEARCH_REPLACE_TYPE, NULL);
4376 me->cells = NULL;
4377 me->sr = g_object_ref (sr);
4379 me->cmd.sheet = NULL;
4380 me->cmd.size = 1; /* Corrected below. */
4381 me->cmd.cmd_descriptor = g_strdup (_("Search and Replace"));
4383 if (cmd_search_replace_do (me, TRUE, wbc)) {
4384 /* There was an error and nothing was done. */
4385 g_object_unref (me);
4386 return TRUE;
4389 cmd_search_replace_do (me, FALSE, wbc);
4390 me->cmd.size += g_list_length (me->cells);
4392 command_register_undo (wbc, G_OBJECT (me));
4393 return FALSE;
4396 /******************************************************************/
4398 #define CMD_COLROW_STD_SIZE_TYPE (cmd_colrow_std_size_get_type ())
4399 #define CMD_COLROW_STD_SIZE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_STD_SIZE_TYPE, CmdColRowStdSize))
4401 typedef struct {
4402 GnmCommand cmd;
4404 Sheet *sheet;
4405 gboolean is_cols;
4406 double new_default;
4407 double old_default;
4408 } CmdColRowStdSize;
4410 MAKE_GNM_COMMAND (CmdColRowStdSize, cmd_colrow_std_size, NULL)
4412 static gboolean
4413 cmd_colrow_std_size_undo (GnmCommand *cmd,
4414 G_GNUC_UNUSED WorkbookControl *wbc)
4416 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4418 g_return_val_if_fail (me != NULL, TRUE);
4419 g_return_val_if_fail (me->old_default != 0, TRUE);
4421 if (me->is_cols)
4422 sheet_col_set_default_size_pts (me->sheet, me->old_default);
4423 else
4424 sheet_row_set_default_size_pts (me->sheet, me->old_default);
4426 me->old_default = 0;
4428 return FALSE;
4431 static gboolean
4432 cmd_colrow_std_size_redo (GnmCommand *cmd,
4433 G_GNUC_UNUSED WorkbookControl *wbc)
4435 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4437 g_return_val_if_fail (me != NULL, TRUE);
4438 g_return_val_if_fail (me->old_default == 0, TRUE);
4440 if (me->is_cols) {
4441 me->old_default = sheet_col_get_default_size_pts (me->sheet);
4442 sheet_col_set_default_size_pts (me->sheet, me->new_default);
4443 } else {
4444 me->old_default = sheet_row_get_default_size_pts (me->sheet);
4445 sheet_row_set_default_size_pts (me->sheet, me->new_default);
4448 return FALSE;
4450 static void
4451 cmd_colrow_std_size_finalize (GObject *cmd)
4453 gnm_command_finalize (cmd);
4456 gboolean
4457 cmd_colrow_std_size (WorkbookControl *wbc, Sheet *sheet,
4458 gboolean is_cols, double new_default)
4460 CmdColRowStdSize *me;
4462 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4464 me = g_object_new (CMD_COLROW_STD_SIZE_TYPE, NULL);
4466 me->sheet = sheet;
4467 me->is_cols = is_cols;
4468 me->new_default = new_default;
4469 me->old_default = 0;
4471 me->cmd.sheet = sheet;
4472 me->cmd.size = 1; /* Changed in initial redo. */
4473 me->cmd.cmd_descriptor = is_cols
4474 ? g_strdup_printf (_("Setting default width of columns to %.2fpts"), new_default)
4475 : g_strdup_printf (_("Setting default height of rows to %.2fpts"), new_default);
4477 return gnm_command_push_undo (wbc, G_OBJECT (me));
4480 /******************************************************************/
4482 #define CMD_ZOOM_TYPE (cmd_zoom_get_type ())
4483 #define CMD_ZOOM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ZOOM_TYPE, CmdZoom))
4485 typedef struct {
4486 GnmCommand cmd;
4488 GSList *sheets;
4489 double new_factor;
4490 double *old_factors;
4491 } CmdZoom;
4493 MAKE_GNM_COMMAND (CmdZoom, cmd_zoom, NULL)
4495 static gboolean
4496 cmd_zoom_undo (GnmCommand *cmd,
4497 G_GNUC_UNUSED WorkbookControl *wbc)
4499 CmdZoom *me = CMD_ZOOM (cmd);
4500 GSList *l;
4501 int i;
4503 g_return_val_if_fail (me != NULL, TRUE);
4504 g_return_val_if_fail (me->sheets != NULL, TRUE);
4505 g_return_val_if_fail (me->old_factors != NULL, TRUE);
4507 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4508 Sheet *sheet = l->data;
4509 g_object_set (sheet, "zoom-factor", me->old_factors[i], NULL);
4512 return FALSE;
4515 static gboolean
4516 cmd_zoom_redo (GnmCommand *cmd,
4517 G_GNUC_UNUSED WorkbookControl *wbc)
4519 CmdZoom *me = CMD_ZOOM (cmd);
4520 GSList *l;
4522 g_return_val_if_fail (me != NULL, TRUE);
4523 g_return_val_if_fail (me->sheets != NULL, TRUE);
4525 for (l = me->sheets; l != NULL; l = l->next) {
4526 Sheet *sheet = l->data;
4527 g_object_set (sheet, "zoom-factor", me->new_factor, NULL);
4530 return FALSE;
4533 static void
4534 cmd_zoom_finalize (GObject *cmd)
4536 CmdZoom *me = CMD_ZOOM (cmd);
4538 g_slist_free (me->sheets);
4539 g_free (me->old_factors);
4541 gnm_command_finalize (cmd);
4545 * cmd_zoom:
4546 * @wbc: #WorkbookControl
4547 * @sheets: (element-type Sheet) (transfer container):
4548 * @factor:
4551 gboolean
4552 cmd_zoom (WorkbookControl *wbc, GSList *sheets, double factor)
4554 CmdZoom *me;
4555 GString *namelist;
4556 GSList *l;
4557 int i;
4559 g_return_val_if_fail (wbc != NULL, TRUE);
4560 g_return_val_if_fail (sheets != NULL, TRUE);
4562 me = g_object_new (CMD_ZOOM_TYPE, NULL);
4564 me->sheets = sheets;
4565 me->old_factors = g_new0 (double, g_slist_length (sheets));
4566 me->new_factor = factor;
4568 /* Make a list of all sheets to zoom and save zoom factor for each */
4569 namelist = g_string_new (NULL);
4570 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4571 Sheet *sheet = l->data;
4573 g_string_append (namelist, sheet->name_unquoted);
4574 me->old_factors[i] = sheet->last_zoom_factor_used;
4576 if (l->next)
4577 g_string_append (namelist, ", ");
4580 /* Make sure the string doesn't get overly wide */
4581 gnm_cmd_trunc_descriptor (namelist, NULL);
4583 me->cmd.sheet = NULL;
4584 me->cmd.size = 1;
4585 me->cmd.cmd_descriptor =
4586 g_strdup_printf (_("Zoom %s to %.0f%%"), namelist->str, factor * 100);
4588 g_string_free (namelist, TRUE);
4590 return gnm_command_push_undo (wbc, G_OBJECT (me));
4593 /******************************************************************/
4595 #define CMD_OBJECTS_DELETE_TYPE (cmd_objects_delete_get_type ())
4596 #define CMD_OBJECTS_DELETE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECTS_DELETE_TYPE, CmdObjectsDelete))
4598 typedef struct {
4599 GnmCommand cmd;
4600 GSList *objects;
4601 GArray *location;
4602 } CmdObjectsDelete;
4604 MAKE_GNM_COMMAND (CmdObjectsDelete, cmd_objects_delete, NULL)
4606 static gboolean
4607 cmd_objects_delete_redo (GnmCommand *cmd,
4608 G_GNUC_UNUSED WorkbookControl *wbc)
4610 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4611 g_slist_foreach (me->objects, (GFunc) sheet_object_clear_sheet, NULL);
4612 return FALSE;
4615 static void
4616 cmd_objects_restore_location (SheetObject *so, gint location)
4618 gint loc = sheet_object_get_stacking (so);
4619 if (loc != location)
4620 sheet_object_adjust_stacking(so, location - loc);
4623 static gboolean
4624 cmd_objects_delete_undo (GnmCommand *cmd,
4625 G_GNUC_UNUSED WorkbookControl *wbc)
4627 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4628 GSList *l;
4629 gint i;
4631 g_slist_foreach (me->objects,
4632 (GFunc) sheet_object_set_sheet, me->cmd.sheet);
4634 for (l = me->objects, i = 0; l; l = l->next, i++)
4635 cmd_objects_restore_location (GNM_SO (l->data),
4636 g_array_index(me->location,
4637 gint, i));
4638 return FALSE;
4641 static void
4642 cmd_objects_delete_finalize (GObject *cmd)
4644 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4645 g_slist_free_full (me->objects, g_object_unref);
4646 if (me->location) {
4647 g_array_free (me->location, TRUE);
4648 me->location = NULL;
4650 gnm_command_finalize (cmd);
4653 static void
4654 cmd_objects_store_location (SheetObject *so, GArray *location)
4656 gint loc = sheet_object_get_stacking (so);
4657 g_array_append_val (location, loc);
4661 * cmd_objects_delete:
4662 * @wbc: #WorkbookControl
4663 * @objects: (element-type SheetObject) (transfer container): the objects to
4664 * delete.
4665 * @name:
4667 * Absorbs the list, adding references to the contents.
4669 gboolean
4670 cmd_objects_delete (WorkbookControl *wbc, GSList *objects,
4671 char const *name)
4673 CmdObjectsDelete *me;
4675 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4676 g_return_val_if_fail (objects != NULL, TRUE);
4678 me = g_object_new (CMD_OBJECTS_DELETE_TYPE, NULL);
4680 me->objects = objects;
4681 g_slist_foreach (me->objects, (GFunc) g_object_ref, NULL);
4683 me->location = g_array_new (FALSE, FALSE, sizeof (gint));
4684 g_slist_foreach (me->objects, (GFunc) cmd_objects_store_location,
4685 me->location);
4687 me->cmd.sheet = sheet_object_get_sheet (objects->data);
4688 me->cmd.size = 1;
4689 me->cmd.cmd_descriptor = g_strdup (name ? name : _("Delete Object"));
4691 return gnm_command_push_undo (wbc, G_OBJECT (me));
4694 /******************************************************************/
4697 * cmd_objects_move:
4698 * @wbc: #WorkbookControl
4699 * @objects: (element-type SheetObject) (transfer container): the objects to move.
4700 * @anchors: (element-type SheetObjectAnchor) (transfer full): the anchors for the objects.
4701 * @objects_created:
4702 * @name:
4705 gboolean
4706 cmd_objects_move (WorkbookControl *wbc, GSList *objects, GSList *anchors,
4707 gboolean objects_created, char const *name)
4709 GOUndo *undo = NULL;
4710 GOUndo *redo = NULL;
4711 gboolean result = TRUE;
4713 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4715 undo = sheet_object_move_undo (objects, objects_created);
4716 redo = sheet_object_move_do (objects, anchors, objects_created);
4718 result = cmd_generic (wbc, name, undo, redo);
4720 g_slist_free (objects);
4721 g_slist_free_full (anchors, g_free);
4723 return result;
4726 /******************************************************************/
4728 #define CMD_REORGANIZE_SHEETS_TYPE (cmd_reorganize_sheets_get_type ())
4729 #define CMD_REORGANIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REORGANIZE_SHEETS_TYPE, CmdReorganizeSheets))
4731 typedef struct {
4732 GnmCommand cmd;
4733 Workbook *wb;
4734 WorkbookSheetState *old;
4735 WorkbookSheetState *new;
4736 gboolean first;
4737 Sheet *undo_sheet;
4738 Sheet *redo_sheet;
4739 } CmdReorganizeSheets;
4741 MAKE_GNM_COMMAND (CmdReorganizeSheets, cmd_reorganize_sheets, NULL)
4743 static gboolean
4744 cmd_reorganize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4746 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4747 workbook_sheet_state_restore (me->wb, me->old);
4748 if (me->undo_sheet) {
4749 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4750 wb_control_sheet_focus (control, me->undo_sheet););
4752 return FALSE;
4755 static gboolean
4756 cmd_reorganize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4758 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4760 if (me->first)
4761 me->first = FALSE;
4762 else {
4763 workbook_sheet_state_restore (me->wb, me->new);
4764 if (me->redo_sheet) {
4765 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4766 wb_control_sheet_focus (control, me->redo_sheet););
4770 return FALSE;
4773 static void
4774 cmd_reorganize_sheets_finalize (GObject *cmd)
4776 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4778 if (me->old)
4779 workbook_sheet_state_free (me->old);
4780 if (me->new)
4781 workbook_sheet_state_free (me->new);
4783 gnm_command_finalize (cmd);
4786 gboolean
4787 cmd_reorganize_sheets (WorkbookControl *wbc,
4788 WorkbookSheetState *old_state,
4789 Sheet *undo_sheet)
4791 CmdReorganizeSheets *me;
4792 Workbook *wb = wb_control_get_workbook (wbc);
4794 me = g_object_new (CMD_REORGANIZE_SHEETS_TYPE, NULL);
4795 me->wb = wb;
4796 me->old = old_state;
4797 me->new = workbook_sheet_state_new (me->wb);
4798 me->first = TRUE;
4799 me->undo_sheet = undo_sheet;
4800 me->redo_sheet = wb_control_cur_sheet (wbc);
4802 me->cmd.sheet = NULL;
4803 me->cmd.size = workbook_sheet_state_size (me->old) +
4804 workbook_sheet_state_size (me->new);
4805 me->cmd.cmd_descriptor =
4806 workbook_sheet_state_diff (me->old, me->new);
4808 if (me->cmd.cmd_descriptor)
4809 return gnm_command_push_undo (wbc, G_OBJECT (me));
4811 /* No change. */
4812 g_object_unref (me);
4813 return FALSE;
4816 /******************************************************************/
4818 gboolean
4819 cmd_rename_sheet (WorkbookControl *wbc,
4820 Sheet *sheet,
4821 char const *new_name)
4823 WorkbookSheetState *old_state;
4824 Sheet *collision;
4826 g_return_val_if_fail (new_name != NULL, TRUE);
4827 g_return_val_if_fail (sheet != NULL, TRUE);
4829 if (*new_name == 0) {
4830 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), _("Sheet names must be non-empty."));
4831 return TRUE;
4834 collision = workbook_sheet_by_name (sheet->workbook, new_name);
4835 if (collision && collision != sheet) {
4836 GError *err = g_error_new (go_error_invalid(), 0,
4837 _("A workbook cannot have two sheets with the same name."));
4838 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
4839 g_error_free (err);
4840 return TRUE;
4843 old_state = workbook_sheet_state_new (sheet->workbook);
4844 g_object_set (sheet, "name", new_name, NULL);
4845 return cmd_reorganize_sheets (wbc, old_state, sheet);
4848 /******************************************************************/
4850 #define CMD_RESIZE_SHEETS_TYPE (cmd_resize_sheets_get_type ())
4851 #define CMD_RESIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESIZE_SHEETS_TYPE, CmdResizeSheets))
4853 typedef struct {
4854 GnmCommand cmd;
4855 GSList *sheets;
4856 int cols, rows;
4857 GOUndo *undo;
4858 } CmdResizeSheets;
4860 MAKE_GNM_COMMAND (CmdResizeSheets, cmd_resize_sheets, NULL)
4862 static gboolean
4863 cmd_resize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4865 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4866 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4868 go_undo_undo_with_data (me->undo, cc);
4869 g_object_unref (me->undo);
4870 me->undo = NULL;
4872 return FALSE;
4875 static gboolean
4876 cmd_resize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4878 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4879 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4880 GSList *l;
4882 for (l = me->sheets; l; l = l->next) {
4883 Sheet *sheet = l->data;
4884 gboolean err;
4885 GOUndo *u = gnm_sheet_resize (sheet, me->cols, me->rows,
4886 cc, &err);
4887 me->undo = go_undo_combine (me->undo, u);
4889 if (err) {
4890 if (me->undo)
4891 go_undo_undo_with_data (me->undo, cc);
4892 return TRUE;
4896 return FALSE;
4899 static void
4900 cmd_resize_sheets_finalize (GObject *cmd)
4902 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4904 g_slist_free (me->sheets);
4905 if (me->undo) {
4906 g_object_unref (me->undo);
4907 me->undo = NULL;
4910 gnm_command_finalize (cmd);
4914 * cmd_resize_sheets:
4915 * @wbc: #WorkbookControl
4916 * @sheets: (element-type Sheet) (transfer container): the sheets to resize.
4917 * @cols: new columns number.
4918 * @rows: new rows number.
4921 gboolean
4922 cmd_resize_sheets (WorkbookControl *wbc,
4923 GSList *sheets,
4924 int cols, int rows)
4926 CmdResizeSheets *me;
4928 me = g_object_new (CMD_RESIZE_SHEETS_TYPE, NULL);
4929 me->sheets = sheets;
4930 me->cols = cols;
4931 me->rows = rows;
4932 me->cmd.sheet = sheets ? sheets->data : NULL;
4933 me->cmd.size = 1;
4934 me->cmd.cmd_descriptor = g_strdup (_("Resizing sheet"));
4936 if (sheets &&
4937 gnm_sheet_valid_size (cols, rows))
4938 return gnm_command_push_undo (wbc, G_OBJECT (me));
4940 /* No change. */
4941 g_object_unref (me);
4942 return FALSE;
4945 /******************************************************************/
4947 #define CMD_SET_COMMENT_TYPE (cmd_set_comment_get_type ())
4948 #define CMD_SET_COMMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SET_COMMENT_TYPE, CmdSetComment))
4950 typedef struct {
4951 GnmCommand cmd;
4953 Sheet *sheet;
4954 GnmCellPos pos;
4955 gchar *new_text;
4956 gchar *old_text;
4957 gchar *new_author;
4958 gchar *old_author;
4959 PangoAttrList *old_attributes;
4960 PangoAttrList *new_attributes;
4961 } CmdSetComment;
4963 MAKE_GNM_COMMAND (CmdSetComment, cmd_set_comment, NULL)
4965 static gboolean
4966 cmd_set_comment_apply (Sheet *sheet, GnmCellPos *pos,
4967 char const *text, PangoAttrList *attributes,
4968 char const *author)
4970 GnmComment *comment;
4971 Workbook *wb = sheet->workbook;
4973 comment = sheet_get_comment (sheet, pos);
4974 if (comment) {
4975 if (text)
4976 g_object_set (G_OBJECT (comment), "text", text,
4977 "author", author,
4978 "markup", attributes, NULL);
4979 else {
4980 GnmRange const *mr;
4982 mr = gnm_sheet_merge_contains_pos (sheet, pos);
4984 if (mr)
4985 sheet_objects_clear (sheet, mr, GNM_CELL_COMMENT_TYPE, NULL);
4986 else {
4987 GnmRange r;
4988 r.start = r.end = *pos;
4989 sheet_objects_clear (sheet, &r, GNM_CELL_COMMENT_TYPE, NULL);
4992 } else if (text && (strlen (text) > 0)) {
4993 cell_set_comment (sheet, pos, author, text, attributes);
4995 sheet_mark_dirty (sheet);
4997 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
4998 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
5000 return FALSE;
5003 static gboolean
5004 cmd_set_comment_undo (GnmCommand *cmd,
5005 G_GNUC_UNUSED WorkbookControl *wbc)
5007 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5009 return cmd_set_comment_apply (me->sheet, &me->pos,
5010 me->old_text, me->old_attributes,
5011 me->old_author);
5014 static gboolean
5015 cmd_set_comment_redo (GnmCommand *cmd,
5016 G_GNUC_UNUSED WorkbookControl *wbc)
5018 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5020 return cmd_set_comment_apply (me->sheet, &me->pos,
5021 me->new_text, me->new_attributes,
5022 me->new_author);
5025 static void
5026 cmd_set_comment_finalize (GObject *cmd)
5028 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5030 g_free (me->new_text);
5031 me->new_text = NULL;
5033 g_free (me->old_text);
5034 me->old_text = NULL;
5036 g_free (me->new_author);
5037 me->new_author = NULL;
5039 g_free (me->old_author);
5040 me->old_author = NULL;
5042 if (me->old_attributes != NULL) {
5043 pango_attr_list_unref (me->old_attributes);
5044 me->old_attributes = NULL;
5047 if (me->new_attributes != NULL) {
5048 pango_attr_list_unref (me->new_attributes);
5049 me->new_attributes = NULL;
5052 gnm_command_finalize (cmd);
5055 gboolean
5056 cmd_set_comment (WorkbookControl *wbc,
5057 Sheet *sheet, GnmCellPos const *pos,
5058 char const *new_text,
5059 PangoAttrList *attr,
5060 char const *new_author)
5062 CmdSetComment *me;
5063 GnmComment *comment;
5064 char *where;
5066 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5067 g_return_val_if_fail (new_text != NULL, TRUE);
5069 me = g_object_new (CMD_SET_COMMENT_TYPE, NULL);
5071 me->cmd.sheet = sheet;
5072 me->cmd.size = 1;
5073 if (strlen (new_text) < 1)
5074 me->new_text = NULL;
5075 else
5076 me->new_text = g_strdup (new_text);
5077 if (strlen (new_author) < 1)
5078 me->new_author = NULL;
5079 else
5080 me->new_author = g_strdup (new_author);
5081 if (attr != NULL)
5082 pango_attr_list_ref (attr);
5083 me->new_attributes = attr;
5084 where = undo_cell_pos_name (sheet, pos);
5085 me->cmd.cmd_descriptor =
5086 g_strdup_printf (me->new_text == NULL ?
5087 _("Clearing comment of %s") :
5088 _("Setting comment of %s"),
5089 where);
5090 g_free (where);
5091 me->old_text = NULL;
5092 me->old_author = NULL;
5093 me->old_attributes = NULL;
5094 me->pos = *pos;
5095 me->sheet = sheet;
5096 comment = sheet_get_comment (sheet, pos);
5097 if (comment) {
5098 g_object_get (G_OBJECT (comment),
5099 "text", &(me->old_text),
5100 "author", &(me->old_author),
5101 "markup", &(me->old_attributes), NULL);
5102 if (me->old_attributes != NULL)
5103 pango_attr_list_ref (me->old_attributes);
5104 me->old_text = g_strdup (me->old_text);
5105 me->old_author = g_strdup (me->old_author);
5108 /* Register the command object */
5109 return gnm_command_push_undo (wbc, G_OBJECT (me));
5112 /******************************************************************/
5114 #define CMD_ANALYSIS_TOOL_TYPE (cmd_analysis_tool_get_type ())
5115 #define CMD_ANALYSIS_TOOL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ANALYSIS_TOOL_TYPE, CmdAnalysis_Tool))
5117 typedef struct {
5118 GnmCommand cmd;
5120 data_analysis_output_t *dao;
5121 gpointer specs;
5122 gboolean specs_owned;
5123 analysis_tool_engine engine;
5124 data_analysis_output_type_t type;
5126 ColRowStateList *col_info;
5127 ColRowStateList *row_info;
5128 GnmRange old_range;
5129 GnmCellRegion *old_contents;
5130 GSList *newSheetObjects;
5131 } CmdAnalysis_Tool;
5133 MAKE_GNM_COMMAND (CmdAnalysis_Tool, cmd_analysis_tool, NULL)
5135 static gboolean
5136 cmd_analysis_tool_undo (GnmCommand *cmd, WorkbookControl *wbc)
5138 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5139 GnmPasteTarget pt;
5141 g_return_val_if_fail (me != NULL, TRUE);
5143 /* The old view might not exist anymore */
5144 me->dao->wbc = wbc;
5146 switch (me->type) {
5147 case NewSheetOutput:
5148 if (!command_undo_sheet_delete (me->dao->sheet))
5149 return TRUE;
5150 me->dao->sheet = NULL;
5151 break;
5152 case NewWorkbookOutput:
5153 g_warning ("How did we get here?");
5154 return TRUE;
5155 break;
5156 case RangeOutput:
5157 default:
5158 sheet_clear_region (me->dao->sheet,
5159 me->old_range.start.col, me->old_range.start.row,
5160 me->old_range.end.col, me->old_range.end.row,
5161 CLEAR_COMMENTS | CLEAR_FORMATS | CLEAR_NOCHECKARRAY |
5162 CLEAR_RECALC_DEPS | CLEAR_VALUES | CLEAR_MERGES,
5163 GO_CMD_CONTEXT (wbc));
5164 clipboard_paste_region (me->old_contents,
5165 paste_target_init (&pt, me->dao->sheet, &me->old_range, PASTE_ALL_SHEET),
5166 GO_CMD_CONTEXT (wbc));
5167 cellregion_unref (me->old_contents);
5168 me->old_contents = NULL;
5169 if (me->col_info) {
5170 dao_set_colrow_state_list (me->dao, TRUE, me->col_info);
5171 me->col_info = colrow_state_list_destroy (me->col_info);
5173 if (me->row_info) {
5174 dao_set_colrow_state_list (me->dao, FALSE, me->row_info);
5175 me->row_info = colrow_state_list_destroy (me->row_info);
5177 if (me->newSheetObjects == NULL)
5178 me->newSheetObjects = dao_surrender_so (me->dao);
5179 g_slist_foreach (me->newSheetObjects, (GFunc)sheet_object_clear_sheet, NULL);
5180 sheet_update (me->dao->sheet);
5183 return FALSE;
5186 static void
5187 cmd_analysis_tool_draw_old_so (SheetObject *so, data_analysis_output_t *dao)
5189 g_object_ref (so);
5190 dao_set_sheet_object (dao, 0, 1, so);
5193 static gboolean
5194 cmd_analysis_tool_redo (GnmCommand *cmd, WorkbookControl *wbc)
5196 gpointer continuity = NULL;
5197 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5198 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5200 g_return_val_if_fail (me != NULL, TRUE);
5202 /* The old view might not exist anymore */
5203 me->dao->wbc = wbc;
5205 if (me->col_info)
5206 me->col_info = colrow_state_list_destroy (me->col_info);
5207 me->col_info = dao_get_colrow_state_list (me->dao, TRUE);
5208 if (me->row_info)
5209 me->row_info = colrow_state_list_destroy (me->row_info);
5210 me->row_info = dao_get_colrow_state_list (me->dao, FALSE);
5212 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PREPARE_OUTPUT_RANGE, NULL)
5213 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5214 &me->cmd.cmd_descriptor)
5215 || cmd_dao_is_locked_effective (me->dao, wbc, me->cmd.cmd_descriptor)
5216 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_LAST_VALIDITY_CHECK, &continuity))
5217 return TRUE;
5219 switch (me->type) {
5220 case NewSheetOutput:
5221 me->old_contents = NULL;
5222 break;
5223 case NewWorkbookOutput:
5224 /* No undo in this case (see below) */
5225 me->old_contents = NULL;
5226 break;
5227 case RangeOutput:
5228 default:
5229 range_init (&me->old_range, me->dao->start_col, me->dao->start_row,
5230 me->dao->start_col + me->dao->cols - 1,
5231 me->dao->start_row + me->dao->rows - 1);
5232 me->old_contents = clipboard_copy_range (me->dao->sheet, &me->old_range);
5233 break;
5236 if (me->newSheetObjects != NULL)
5237 dao_set_omit_so (me->dao, TRUE);
5239 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_FORMAT_OUTPUT_RANGE, NULL))
5240 return TRUE;
5242 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PERFORM_CALC, &continuity)) {
5243 if (me->type == RangeOutput) {
5244 g_warning ("This is too late for failure! The target region has "
5245 "already been formatted!");
5246 } else
5247 return TRUE;
5249 if (me->newSheetObjects != NULL)
5251 GSList *l = g_slist_reverse
5252 (g_slist_copy (me->newSheetObjects));
5254 dao_set_omit_so (me->dao, FALSE);
5255 g_slist_foreach (l,
5256 (GFunc) cmd_analysis_tool_draw_old_so,
5257 me->dao);
5258 g_slist_free (l);
5261 if (continuity) {
5262 g_warning ("There shouldn't be any data left in here!");
5265 dao_autofit_columns (me->dao);
5266 sheet_mark_dirty (me->dao->sheet);
5267 sheet_update (me->dao->sheet);
5269 /* The concept of an undo if we create a new worksheet is extremely strange,
5270 * since we have separate undo/redo queues per worksheet.
5271 * Users can simply delete the worksheet if they so desire.
5274 return (me->type == NewWorkbookOutput);
5277 static void
5278 cmd_analysis_tool_finalize (GObject *cmd)
5280 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5282 if (me->col_info)
5283 me->col_info = colrow_state_list_destroy (me->col_info);
5284 if (me->row_info)
5285 me->row_info = colrow_state_list_destroy (me->row_info);
5287 me->engine (NULL, me->dao, me->specs, TOOL_ENGINE_CLEAN_UP, NULL);
5289 if (me->specs_owned) {
5290 g_free (me->specs);
5291 dao_free (me->dao);
5293 if (me->old_contents)
5294 cellregion_unref (me->old_contents);
5296 g_slist_free_full (me->newSheetObjects, g_object_unref);
5298 gnm_command_finalize (cmd);
5302 * cmd_analysis_tool: (skip)
5303 * Note: this takes ownership of specs and dao if and if only the command
5304 * succeeds.
5306 gboolean
5307 cmd_analysis_tool (WorkbookControl *wbc, G_GNUC_UNUSED Sheet *sheet,
5308 data_analysis_output_t *dao, gpointer specs,
5309 analysis_tool_engine engine, gboolean always_take_ownership)
5311 CmdAnalysis_Tool *me;
5312 gboolean trouble;
5313 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5315 g_return_val_if_fail (dao != NULL, TRUE);
5316 g_return_val_if_fail (specs != NULL, TRUE);
5317 g_return_val_if_fail (engine != NULL, TRUE);
5319 me = g_object_new (CMD_ANALYSIS_TOOL_TYPE, NULL);
5321 dao->wbc = wbc;
5323 /* Store the specs for the object */
5324 me->specs = specs;
5325 me->specs_owned = always_take_ownership;
5326 me->dao = dao;
5327 me->engine = engine;
5328 me->cmd.cmd_descriptor = NULL;
5329 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DAO, NULL)) {
5330 g_object_unref (me);
5331 return TRUE;
5333 me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5334 &me->cmd.cmd_descriptor);
5335 me->cmd.sheet = NULL;
5336 me->type = dao->type;
5337 me->row_info = NULL;
5338 me->col_info = NULL;
5340 /* We divide by 2 since many cells will be empty*/
5341 me->cmd.size = 1 + dao->rows * dao->cols / 2;
5343 /* Register the command object */
5344 trouble = gnm_command_push_undo (wbc, G_OBJECT (me));
5346 if (!trouble)
5347 me->specs_owned = TRUE;
5349 return trouble;
5352 /******************************************************************/
5354 #define CMD_MERGE_DATA_TYPE (cmd_merge_data_get_type ())
5355 #define CMD_MERGE_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_DATA_TYPE, CmdMergeData))
5357 typedef struct {
5358 GnmCommand cmd;
5359 GnmValue *merge_zone;
5360 GSList *merge_fields;
5361 GSList *merge_data;
5362 GSList *sheet_list;
5363 Sheet *sheet;
5364 gint n;
5365 } CmdMergeData;
5367 MAKE_GNM_COMMAND (CmdMergeData, cmd_merge_data, NULL)
5369 static void
5370 cmd_merge_data_delete_sheets (gpointer data, gpointer success)
5372 Sheet *sheet = data;
5374 if (!command_undo_sheet_delete (sheet))
5375 *(gboolean *)success = FALSE;
5378 static gboolean
5379 cmd_merge_data_undo (GnmCommand *cmd,
5380 G_GNUC_UNUSED WorkbookControl *wbc)
5382 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5383 gboolean success = TRUE;
5385 g_slist_foreach (me->sheet_list, cmd_merge_data_delete_sheets, &success);
5386 g_slist_free (me->sheet_list);
5387 me->sheet_list = NULL;
5389 return FALSE;
5392 static gboolean
5393 cmd_merge_data_redo (GnmCommand *cmd, WorkbookControl *wbc)
5395 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5396 int i;
5397 GnmCellRegion *merge_contents;
5398 GnmRangeRef *cell = &me->merge_zone->v_range.cell;
5399 GnmPasteTarget pt;
5400 GSList *this_field = me->merge_fields;
5401 GSList *this_data = me->merge_data;
5402 Sheet *source_sheet = cell->a.sheet;
5403 GSList *target_sheet;
5404 GnmRange target_range;
5405 ColRowStateList *state_col;
5406 ColRowStateList *state_row;
5408 range_init (&target_range, cell->a.col, cell->a.row,
5409 cell->b.col, cell->b.row);
5410 merge_contents = clipboard_copy_range (source_sheet, &target_range);
5411 state_col = colrow_get_states (source_sheet, TRUE, target_range.start.col,
5412 target_range.end.col);
5413 state_row = colrow_get_states (source_sheet, FALSE, target_range.start.row,
5414 target_range.end.row);
5416 for (i = 0; i < me->n; i++) {
5417 Sheet *new_sheet;
5419 new_sheet = workbook_sheet_add (me->sheet->workbook, -1,
5420 gnm_sheet_get_max_cols (me->sheet),
5421 gnm_sheet_get_max_rows (me->sheet));
5422 me->sheet_list = g_slist_prepend (me->sheet_list, new_sheet);
5424 colrow_set_states (new_sheet, TRUE, target_range.start.col, state_col);
5425 colrow_set_states (new_sheet, FALSE, target_range.start.row, state_row);
5426 sheet_objects_dup (source_sheet, new_sheet, &target_range);
5427 clipboard_paste_region (merge_contents,
5428 paste_target_init (&pt, new_sheet, &target_range, PASTE_ALL_SHEET),
5429 GO_CMD_CONTEXT (wbc));
5431 cellregion_unref (merge_contents);
5432 me->sheet_list = g_slist_reverse (me->sheet_list);
5433 colrow_state_list_destroy (state_col);
5434 colrow_state_list_destroy (state_row);
5436 while (this_field) {
5437 int col_source, row_source;
5438 int col_target, row_target;
5440 g_return_val_if_fail (this_data != NULL, TRUE);
5441 cell = &((GnmValue *)this_field->data)->v_range.cell;
5442 col_target = cell->a.col;
5443 row_target = cell->a.row;
5445 cell = &((GnmValue *)this_data->data)->v_range.cell;
5446 col_source = cell->a.col;
5447 row_source = cell->a.row;
5448 source_sheet = cell->a.sheet;
5450 target_sheet = me->sheet_list;
5451 while (target_sheet) {
5452 GnmCell *source_cell = sheet_cell_get (source_sheet,
5453 col_source, row_source);
5454 if (source_cell == NULL) {
5455 GnmCell *target_cell = sheet_cell_get ((Sheet *)target_sheet->data,
5456 col_target, row_target);
5457 if (target_cell != NULL)
5458 gnm_cell_set_value (target_cell,
5459 value_new_empty ());
5460 } else {
5461 GnmCell *target_cell = sheet_cell_fetch ((Sheet *)target_sheet->data,
5462 col_target, row_target);
5463 gnm_cell_set_value (target_cell,
5464 value_dup (source_cell->value));
5466 target_sheet = target_sheet->next;
5467 row_source++;
5470 this_field = this_field->next;
5471 this_data = this_data->next;
5474 return FALSE;
5477 static void
5478 cmd_merge_data_finalize (GObject *cmd)
5480 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5482 value_release (me->merge_zone);
5483 me->merge_zone = NULL;
5484 range_list_destroy (me->merge_data);
5485 me->merge_data = NULL;
5486 range_list_destroy (me->merge_fields);
5487 me->merge_fields = NULL;
5488 g_slist_free (me->sheet_list);
5489 me->sheet_list = NULL;
5490 me->n = 0;
5492 gnm_command_finalize (cmd);
5496 * cmd_merge_data:
5497 * @wbc: #WorkbookControl
5498 * @sheet: #Sheet
5499 * @merge_zone: (transfer full): #GnmValue
5500 * @merge_fields: (element-type GnmRange) (transfer full):
5501 * @merge_data: (element-type GnmRange) (transfer full):
5504 gboolean
5505 cmd_merge_data (WorkbookControl *wbc, Sheet *sheet,
5506 GnmValue *merge_zone, GSList *merge_fields, GSList *merge_data)
5508 CmdMergeData *me;
5509 GnmRangeRef *cell;
5511 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5512 g_return_val_if_fail (merge_zone != NULL, TRUE);
5513 g_return_val_if_fail (merge_fields != NULL, TRUE);
5514 g_return_val_if_fail (merge_data != NULL, TRUE);
5516 me = g_object_new (CMD_MERGE_DATA_TYPE, NULL);
5518 me->cmd.sheet = sheet;
5519 me->sheet = sheet;
5520 me->cmd.size = 1 + g_slist_length (merge_fields);
5521 me->cmd.cmd_descriptor =
5522 g_strdup_printf (_("Merging data into %s"), value_peek_string (merge_zone));
5524 me->merge_zone = merge_zone;
5525 me->merge_fields = merge_fields;
5526 me->merge_data = merge_data;
5527 me->sheet_list = NULL;
5529 cell = &((GnmValue *)merge_data->data)->v_range.cell;
5530 me->n = cell->b.row - cell->a.row + 1;
5532 /* Register the command object */
5533 return gnm_command_push_undo (wbc, G_OBJECT (me));
5536 /******************************************************************/
5538 #define CMD_CHANGE_META_DATA_TYPE (cmd_change_summary_get_type ())
5539 #define CMD_CHANGE_META_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_CHANGE_META_DATA_TYPE, CmdChangeMetaData))
5541 typedef struct {
5542 GnmCommand cmd;
5543 GSList *changed_props;
5544 GSList *removed_names;
5545 } CmdChangeMetaData;
5547 MAKE_GNM_COMMAND (CmdChangeMetaData, cmd_change_summary, NULL)
5549 static gboolean
5550 cmd_change_summary_undo (GnmCommand *cmd, WorkbookControl *wbc)
5552 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5553 GsfDocMetaData *meta = go_doc_get_meta_data (wb_control_get_doc (wbc));
5554 GSList *ptr, *old_vals = NULL, *dropped = NULL;
5555 GsfDocProp *prop;
5556 char const *name;
5558 for (ptr = me->removed_names; ptr != NULL ; ptr = ptr->next) {
5559 if (NULL != (prop = gsf_doc_meta_data_steal (meta, ptr->data)))
5560 old_vals = g_slist_prepend (old_vals, prop);
5561 g_free (ptr->data);
5563 g_slist_free (me->removed_names);
5565 for (ptr = me->changed_props; ptr != NULL ; ptr = ptr->next) {
5566 name = gsf_doc_prop_get_name (ptr->data);
5567 if (NULL != (prop = gsf_doc_meta_data_steal (meta, name)))
5568 old_vals = g_slist_prepend (old_vals, prop);
5569 else
5570 dropped = g_slist_prepend (old_vals, g_strdup (name));
5571 gsf_doc_meta_data_store (meta, ptr->data);
5573 g_slist_free (me->changed_props);
5575 me->removed_names = dropped;
5576 me->changed_props = old_vals;
5577 go_doc_update_meta_data (wb_control_get_doc (wbc));
5579 return FALSE;
5582 static gboolean
5583 cmd_change_summary_redo (GnmCommand *cmd, WorkbookControl *wbc)
5585 return cmd_change_summary_undo (cmd, wbc);
5588 static void
5589 cmd_change_summary_finalize (GObject *cmd)
5591 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5593 g_slist_free_full (me->changed_props, (GDestroyNotify)gsf_doc_prop_free);
5594 me->changed_props = NULL;
5595 g_slist_free_full (me->removed_names, g_free);
5596 me->removed_names = NULL;
5598 gnm_command_finalize (cmd);
5602 * cmd_change_meta_data:
5603 * @wbc: #WorkbookControl
5604 * @changes: (element-type GsfDocMetaData) (transfer full): the changed metadata.
5605 * @removed: (element-type GsfDocMetaData) (transfer full): the removed metadata.
5608 gboolean
5609 cmd_change_meta_data (WorkbookControl *wbc, GSList *changes, GSList *removed)
5611 CmdChangeMetaData *me = g_object_new (CMD_CHANGE_META_DATA_TYPE, NULL);
5613 me->changed_props = changes;
5614 me->removed_names = removed;
5615 me->cmd.sheet = NULL;
5617 me->cmd.size = g_slist_length (changes) + g_slist_length (removed);
5618 me->cmd.cmd_descriptor = g_strdup_printf (
5619 _("Changing workbook properties"));
5620 return gnm_command_push_undo (wbc, G_OBJECT (me));
5623 /******************************************************************/
5625 #define CMD_OBJECT_RAISE_TYPE (cmd_object_raise_get_type ())
5626 #define CMD_OBJECT_RAISE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECT_RAISE_TYPE, CmdObjectRaise))
5628 typedef struct {
5629 GnmCommand cmd;
5630 SheetObject *so;
5631 CmdObjectRaiseSelector dir;
5632 gint changed_positions;
5633 } CmdObjectRaise;
5635 MAKE_GNM_COMMAND (CmdObjectRaise, cmd_object_raise, NULL)
5637 static gboolean
5638 cmd_object_raise_redo (GnmCommand *cmd,
5639 G_GNUC_UNUSED WorkbookControl *wbc)
5641 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5642 switch (me->dir) {
5643 case cmd_object_pull_to_front:
5644 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MAXINT/2);
5645 break;
5646 case cmd_object_pull_forward:
5647 me->changed_positions = sheet_object_adjust_stacking (me->so, 1);
5648 break;
5649 case cmd_object_push_backward:
5650 me->changed_positions = sheet_object_adjust_stacking (me->so, -1);
5651 break;
5652 case cmd_object_push_to_back:
5653 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MININT/2);
5654 break;
5656 return FALSE;
5659 static gboolean
5660 cmd_object_raise_undo (GnmCommand *cmd,
5661 G_GNUC_UNUSED WorkbookControl *wbc)
5663 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5664 if (me->changed_positions != 0)
5665 sheet_object_adjust_stacking (me->so, - me->changed_positions);
5666 return FALSE;
5669 static void
5670 cmd_object_raise_finalize (GObject *cmd)
5672 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5673 g_object_unref (me->so);
5674 gnm_command_finalize (cmd);
5677 gboolean
5678 cmd_object_raise (WorkbookControl *wbc, SheetObject *so, CmdObjectRaiseSelector dir)
5680 CmdObjectRaise *me;
5682 g_return_val_if_fail (GNM_IS_SO (so), TRUE);
5684 me = g_object_new (CMD_OBJECT_RAISE_TYPE, NULL);
5686 me->so = so;
5687 g_object_ref (so);
5689 me->cmd.sheet = sheet_object_get_sheet (so);
5690 me->cmd.size = 1;
5691 switch (dir) {
5692 case cmd_object_pull_to_front:
5693 me->cmd.cmd_descriptor = g_strdup (_("Pull Object to the Front"));
5694 break;
5695 case cmd_object_pull_forward:
5696 me->cmd.cmd_descriptor = g_strdup (_("Pull Object Forward"));
5697 break;
5698 case cmd_object_push_backward:
5699 me->cmd.cmd_descriptor = g_strdup (_("Push Object Backward"));
5700 break;
5701 case cmd_object_push_to_back:
5702 me->cmd.cmd_descriptor = g_strdup (_("Push Object to the Back"));
5703 break;
5705 me->dir = dir;
5706 me->changed_positions = 0;
5708 return gnm_command_push_undo (wbc, G_OBJECT (me));
5711 /******************************************************************/
5713 #define CMD_PRINT_SETUP_TYPE (cmd_print_setup_get_type ())
5714 #define CMD_PRINT_SETUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PRINT_SETUP_TYPE, CmdPrintSetup))
5716 typedef struct {
5717 GnmCommand cmd;
5719 GSList *old_pi;
5720 GnmPrintInformation *new_pi;
5721 } CmdPrintSetup;
5723 MAKE_GNM_COMMAND (CmdPrintSetup, cmd_print_setup, NULL)
5725 static void
5726 update_sheet_graph_cb (Sheet *sheet)
5728 g_return_if_fail (IS_SHEET (sheet) && sheet->sheet_type == GNM_SHEET_OBJECT);
5730 sheet_object_graph_ensure_size (GNM_SO (sheet->sheet_objects->data));
5733 static gboolean
5734 cmd_print_setup_undo (GnmCommand *cmd, WorkbookControl *wbc)
5736 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5737 guint n, i;
5738 Workbook *book;
5739 GSList *infos;
5741 g_return_val_if_fail (me->old_pi != NULL, TRUE);
5743 if (me->cmd.sheet) {
5744 GnmPrintInformation *pi = me->old_pi->data;
5745 gnm_print_info_free (me->cmd.sheet->print_info);
5746 me->cmd.sheet->print_info = gnm_print_info_dup (pi);
5747 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5748 update_sheet_graph_cb (me->cmd.sheet);
5749 } else {
5750 book = wb_control_get_workbook(wbc);
5751 n = workbook_sheet_count (book);
5752 infos = me->old_pi;
5753 g_return_val_if_fail (g_slist_length (infos) == n, TRUE);
5755 for (i = 0 ; i < n ; i++) {
5756 GnmPrintInformation *pi = infos->data;
5757 Sheet *sheet = workbook_sheet_by_index (book, i);
5759 g_return_val_if_fail (infos != NULL, TRUE);
5761 gnm_print_info_free (sheet->print_info);
5762 sheet->print_info = gnm_print_info_dup (pi);
5763 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5764 update_sheet_graph_cb (sheet);
5765 infos = infos->next;
5768 return FALSE;
5771 static gboolean
5772 cmd_print_setup_redo (GnmCommand *cmd, WorkbookControl *wbc)
5774 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5775 int n, i;
5776 Workbook *book;
5777 gboolean save_pis = (me->old_pi == NULL);
5779 if (me->cmd.sheet) {
5780 if (save_pis)
5781 me->old_pi = g_slist_append (me->old_pi, me->cmd.sheet->print_info);
5782 else
5783 gnm_print_info_free (me->cmd.sheet->print_info);
5784 me->cmd.sheet->print_info = gnm_print_info_dup (me->new_pi);
5785 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5786 update_sheet_graph_cb (me->cmd.sheet);
5787 } else {
5788 book = wb_control_get_workbook(wbc);
5789 n = workbook_sheet_count (book);
5790 for (i = 0 ; i < n ; i++) {
5791 Sheet *sheet = workbook_sheet_by_index (book, i);
5792 sheet_mark_dirty (sheet);
5793 if (save_pis)
5794 me->old_pi = g_slist_prepend (me->old_pi, sheet->print_info);
5795 else
5796 gnm_print_info_free (sheet->print_info);
5797 sheet->print_info = gnm_print_info_dup (me->new_pi);
5798 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5799 update_sheet_graph_cb (sheet);
5801 if (save_pis)
5802 me->old_pi = g_slist_reverse (me->old_pi);
5804 return FALSE;
5807 static void
5808 cmd_print_setup_finalize (GObject *cmd)
5810 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5811 GSList *list = me->old_pi;
5813 if (me->new_pi)
5814 gnm_print_info_free (me->new_pi);
5815 for (; list; list = list->next)
5816 gnm_print_info_free ((GnmPrintInformation *) list->data);
5817 g_slist_free (me->old_pi);
5818 gnm_command_finalize (cmd);
5821 gboolean
5822 cmd_print_setup (WorkbookControl *wbc, Sheet *sheet, GnmPrintInformation const *pi)
5824 CmdPrintSetup *me;
5826 me = g_object_new (CMD_PRINT_SETUP_TYPE, NULL);
5828 me->cmd.sheet = sheet;
5829 me->cmd.size = 10;
5830 if (sheet)
5831 me->cmd.cmd_descriptor =
5832 g_strdup_printf (_("Page Setup For %s"), sheet->name_unquoted);
5833 else
5834 me->cmd.cmd_descriptor = g_strdup (_("Page Setup For All Sheets"));
5835 me->old_pi = NULL;
5836 me->new_pi = gnm_print_info_dup (pi);
5838 return gnm_command_push_undo (wbc, G_OBJECT (me));
5841 /******************************************************************/
5843 #define CMD_DEFINE_NAME_TYPE (cmd_define_name_get_type ())
5844 #define CMD_DEFINE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DEFINE_NAME_TYPE, CmdDefineName))
5846 typedef struct {
5847 GnmCommand cmd;
5849 GnmParsePos pp;
5850 char *name;
5851 GnmExprTop const *texpr;
5852 gboolean new_name;
5853 gboolean placeholder;
5854 } CmdDefineName;
5856 MAKE_GNM_COMMAND (CmdDefineName, cmd_define_name, NULL)
5858 static gboolean
5859 cmd_define_name_undo (GnmCommand *cmd,
5860 WorkbookControl *wbc)
5862 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5863 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5864 GnmExprTop const *texpr = nexpr->texpr;
5866 gnm_expr_top_ref (texpr);
5867 if (me->new_name)
5868 expr_name_remove (nexpr);
5869 else if (me->placeholder)
5870 expr_name_downgrade_to_placeholder (nexpr);
5871 else
5872 expr_name_set_expr (nexpr, me->texpr); /* restore old def */
5874 me->texpr = texpr;
5876 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5877 wb_view_menus_update (each_wbv);
5879 return FALSE;
5882 static gboolean
5883 cmd_define_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
5885 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5886 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5888 me->new_name = (nexpr == NULL);
5889 me->placeholder = (nexpr != NULL)
5890 && expr_name_is_placeholder (nexpr);
5892 if (me->new_name || me->placeholder) {
5893 char *err = NULL;
5894 nexpr = expr_name_add (&me->pp, me->name, me->texpr, &err, TRUE, NULL);
5895 if (nexpr == NULL) {
5896 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), err);
5897 g_free (err);
5898 return TRUE;
5900 me->texpr = NULL;
5901 } else { /* changing the definition */
5902 GnmExprTop const *tmp = nexpr->texpr;
5903 gnm_expr_top_ref (tmp);
5904 expr_name_set_expr (nexpr, me->texpr);
5905 me->texpr = tmp; /* store the old definition */
5907 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5908 wb_view_menus_update (each_wbv);
5911 return FALSE;
5914 static void
5915 cmd_define_name_finalize (GObject *cmd)
5917 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5919 g_free (me->name); me->name = NULL;
5921 if (me->texpr) {
5922 gnm_expr_top_unref (me->texpr);
5923 me->texpr = NULL;
5926 gnm_command_finalize (cmd);
5930 * cmd_define_name:
5931 * @wbc:
5932 * @name:
5933 * @pp:
5934 * @texpr: absorbs a ref to the texpr.
5935 * @descriptor: optional descriptor.
5937 * If the @name has never been defined in context @pp create a new name
5938 * If its a placeholder assign @texpr to it and make it real
5939 * If it already exists as a real name just assign @expr.
5941 * Returns TRUE on error
5943 gboolean
5944 cmd_define_name (WorkbookControl *wbc, char const *name,
5945 GnmParsePos const *pp, GnmExprTop const *texpr,
5946 char const *descriptor)
5948 CmdDefineName *me;
5949 GnmNamedExpr *nexpr;
5950 Sheet *sheet;
5952 g_return_val_if_fail (name != NULL, TRUE);
5953 g_return_val_if_fail (pp != NULL, TRUE);
5954 g_return_val_if_fail (texpr != NULL, TRUE);
5956 if (name[0] == '\0') {
5957 go_cmd_context_error_invalid
5958 (GO_CMD_CONTEXT (wbc), _("Defined Name"),
5959 _("An empty string is not allowed as defined name."));
5960 gnm_expr_top_unref (texpr);
5961 return TRUE;
5964 sheet = wb_control_cur_sheet (wbc);
5965 if (!expr_name_validate (name)) {
5966 gchar *err = g_strdup_printf
5967 (_("'%s' is not allowed as defined name."), name);
5968 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
5969 _("Defined Name"), err);
5970 g_free (err);
5971 gnm_expr_top_unref (texpr);
5972 return TRUE;
5975 if (expr_name_check_for_loop (name, texpr)) {
5976 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), name,
5977 _("has a circular reference"));
5978 gnm_expr_top_unref (texpr);
5979 return TRUE;
5981 nexpr = expr_name_lookup (pp, name);
5982 if (nexpr != NULL && !expr_name_is_placeholder (nexpr) &&
5983 gnm_expr_top_equal (texpr, nexpr->texpr)) {
5984 gnm_expr_top_unref (texpr);
5985 return FALSE; /* expr is not changing, do nothing */
5988 me = g_object_new (CMD_DEFINE_NAME_TYPE, NULL);
5989 me->name = g_strdup (name);
5990 me->pp = *pp;
5991 me->texpr = texpr;
5993 me->cmd.sheet = sheet;
5994 me->cmd.size = 1;
5996 if (descriptor == NULL) {
5997 char const *tmp;
5998 GString *res;
6000 /* Underscores need to be doubled. */
6001 res = g_string_new (NULL);
6002 for (tmp = name; *tmp; tmp++) {
6003 if (*tmp == '_')
6004 g_string_append_c (res, '_');
6005 g_string_append_c (res, *tmp);
6008 nexpr = expr_name_lookup (pp, name);
6009 if (nexpr == NULL || expr_name_is_placeholder (nexpr))
6010 me->cmd.cmd_descriptor =
6011 g_strdup_printf (_("Define Name %s"), res->str);
6012 else
6013 me->cmd.cmd_descriptor =
6014 g_strdup_printf (_("Update Name %s"), res->str);
6015 g_string_free (res, TRUE);
6016 } else
6017 me->cmd.cmd_descriptor = g_strdup (descriptor);
6019 return gnm_command_push_undo (wbc, G_OBJECT (me));
6022 /******************************************************************/
6024 #define CMD_REMOVE_NAME_TYPE (cmd_remove_name_get_type ())
6025 #define CMD_REMOVE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REMOVE_NAME_TYPE, CmdRemoveName))
6027 typedef struct {
6028 GnmCommand cmd;
6030 GnmParsePos pp;
6031 GnmNamedExpr *nexpr;
6032 const GnmExprTop *texpr;
6033 } CmdRemoveName;
6035 MAKE_GNM_COMMAND (CmdRemoveName, cmd_remove_name, NULL)
6037 static gboolean
6038 cmd_remove_name_undo (GnmCommand *cmd,
6039 G_GNUC_UNUSED WorkbookControl *wbc)
6041 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6042 GnmNamedExpr *nexpr =
6043 expr_name_add (&me->nexpr->pos, expr_name_name (me->nexpr),
6044 me->texpr, NULL, TRUE, NULL);
6045 if (nexpr) {
6046 me->texpr = NULL;
6047 expr_name_ref (nexpr);
6048 expr_name_unref (me->nexpr);
6049 me->nexpr = nexpr;
6050 return FALSE;
6051 } else {
6052 g_warning ("Redefining name failed.");
6053 return TRUE;
6057 static gboolean
6058 cmd_remove_name_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6060 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6062 me->texpr = me->nexpr->texpr;
6063 gnm_expr_top_ref (me->texpr);
6064 expr_name_downgrade_to_placeholder (me->nexpr);
6066 return FALSE;
6069 static void
6070 cmd_remove_name_finalize (GObject *cmd)
6072 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6074 expr_name_unref (me->nexpr);
6076 if (me->texpr) {
6077 gnm_expr_top_unref (me->texpr);
6078 me->texpr = NULL;
6081 gnm_command_finalize (cmd);
6085 * cmd_remove_name:
6086 * @wbc:
6087 * @nexpr: name to remove.
6089 * Returns TRUE on error
6091 gboolean
6092 cmd_remove_name (WorkbookControl *wbc, GnmNamedExpr *nexpr)
6094 CmdRemoveName *me;
6096 g_return_val_if_fail (wbc != NULL, TRUE);
6097 g_return_val_if_fail (nexpr != NULL, TRUE);
6098 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6100 expr_name_ref (nexpr);
6102 me = g_object_new (CMD_REMOVE_NAME_TYPE, NULL);
6103 me->nexpr = nexpr;
6104 me->texpr = NULL;
6105 me->cmd.sheet = wb_control_cur_sheet (wbc);
6106 me->cmd.size = 1;
6107 me->cmd.cmd_descriptor = g_strdup_printf (_("Remove Name %s"),
6108 expr_name_name (nexpr));
6110 return gnm_command_push_undo (wbc, G_OBJECT (me));
6113 /******************************************************************/
6115 #define CMD_RESCOPE_NAME_TYPE (cmd_rescope_name_get_type ())
6116 #define CMD_RESCOPE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESCOPE_NAME_TYPE, CmdRescopeName))
6118 typedef struct {
6119 GnmCommand cmd;
6120 GnmNamedExpr *nexpr;
6121 Sheet *scope;
6122 } CmdRescopeName;
6124 MAKE_GNM_COMMAND (CmdRescopeName, cmd_rescope_name, NULL)
6126 static gboolean
6127 cmd_rescope_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
6129 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6130 Sheet *old_scope = me->nexpr->pos.sheet;
6131 char *err;
6132 GnmParsePos pp = me->nexpr->pos;
6134 pp.sheet = me->scope;
6135 err = expr_name_set_pos (me->nexpr, &pp);
6137 if (err != NULL) {
6138 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Change Scope of Name"), err);
6139 g_free (err);
6140 return TRUE;
6143 me->scope = old_scope;
6144 return FALSE;
6147 static gboolean
6148 cmd_rescope_name_undo (GnmCommand *cmd, WorkbookControl *wbc)
6150 return cmd_rescope_name_redo (cmd, wbc);
6154 static void
6155 cmd_rescope_name_finalize (GObject *cmd)
6157 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6159 expr_name_unref (me->nexpr);
6160 gnm_command_finalize (cmd);
6164 * cmd_rescope_name:
6165 * @wbc:
6166 * @nexpr: name to rescope.
6168 * Returns TRUE on error
6170 gboolean
6171 cmd_rescope_name (WorkbookControl *wbc, GnmNamedExpr *nexpr, Sheet *scope)
6173 CmdRescopeName *me;
6175 g_return_val_if_fail (wbc != NULL, TRUE);
6176 g_return_val_if_fail (nexpr != NULL, TRUE);
6177 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6179 expr_name_ref (nexpr);
6181 me = g_object_new (CMD_RESCOPE_NAME_TYPE, NULL);
6182 me->nexpr = nexpr;
6183 me->scope = scope;
6184 me->cmd.sheet = wb_control_cur_sheet (wbc);
6185 me->cmd.size = 1;
6186 me->cmd.cmd_descriptor = g_strdup_printf (_("Change Scope of Name %s"),
6187 expr_name_name (nexpr));
6189 return gnm_command_push_undo (wbc, G_OBJECT (me));
6191 /******************************************************************/
6193 #define CMD_SCENARIO_ADD_TYPE (cmd_scenario_add_get_type ())
6194 #define CMD_SCENARIO_ADD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_ADD_TYPE, CmdScenarioAdd))
6196 typedef struct {
6197 GnmCommand cmd;
6198 GnmScenario *scenario;
6199 } CmdScenarioAdd;
6201 MAKE_GNM_COMMAND (CmdScenarioAdd, cmd_scenario_add, NULL)
6203 static gboolean
6204 cmd_scenario_add_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6206 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6207 GnmScenario *sc = g_object_ref (me->scenario);
6208 gnm_sheet_scenario_add (sc->sheet, sc);
6209 return FALSE;
6212 static gboolean
6213 cmd_scenario_add_undo (GnmCommand *cmd,
6214 G_GNUC_UNUSED WorkbookControl *wbc)
6216 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6217 GnmScenario *sc = me->scenario;
6218 gnm_sheet_scenario_remove (sc->sheet, sc);
6219 return FALSE;
6222 static void
6223 cmd_scenario_add_finalize (GObject *cmd)
6225 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6227 g_object_unref (me->scenario);
6228 gnm_command_finalize (cmd);
6231 gboolean
6232 cmd_scenario_add (WorkbookControl *wbc, GnmScenario *s, Sheet *sheet)
6234 CmdScenarioAdd *me;
6236 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6237 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6239 me = g_object_new (CMD_SCENARIO_ADD_TYPE, NULL);
6241 me->scenario = s; /* Take ownership */
6242 me->cmd.sheet = sheet;
6243 me->cmd.size = 1;
6244 me->cmd.cmd_descriptor = g_strdup (_("Add scenario"));
6246 return gnm_command_push_undo (wbc, G_OBJECT (me));
6249 /******************************************************************/
6251 #define CMD_SCENARIO_MNGR_TYPE (cmd_scenario_mngr_get_type ())
6252 #define CMD_SCENARIO_MNGR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_MNGR_TYPE, CmdScenarioMngr))
6254 typedef struct {
6255 GnmCommand cmd;
6256 GnmScenario *sc;
6257 GOUndo *undo;
6258 } CmdScenarioMngr;
6260 MAKE_GNM_COMMAND (CmdScenarioMngr, cmd_scenario_mngr, NULL)
6262 static gboolean
6263 cmd_scenario_mngr_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6265 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6266 if (!me->undo)
6267 me->undo = gnm_scenario_apply (me->sc);
6268 return FALSE;
6271 static gboolean
6272 cmd_scenario_mngr_undo (GnmCommand *cmd,
6273 G_GNUC_UNUSED WorkbookControl *wbc)
6275 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6276 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6277 g_object_unref (me->undo);
6278 me->undo = NULL;
6279 return FALSE;
6282 static void
6283 cmd_scenario_mngr_finalize (GObject *cmd)
6285 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6287 g_object_unref (me->sc);
6288 if (me->undo)
6289 g_object_unref (me->undo);
6291 gnm_command_finalize (cmd);
6294 gboolean
6295 cmd_scenario_mngr (WorkbookControl *wbc, GnmScenario *sc, GOUndo *undo)
6297 CmdScenarioMngr *me;
6299 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6300 g_return_val_if_fail (GNM_IS_SCENARIO (sc), TRUE);
6302 me = g_object_new (CMD_SCENARIO_MNGR_TYPE, NULL);
6304 me->sc = g_object_ref (sc);
6305 me->undo = g_object_ref (undo);
6306 me->cmd.sheet = sc->sheet;
6307 me->cmd.size = 1;
6308 me->cmd.cmd_descriptor = g_strdup (_("Scenario Show"));
6310 return gnm_command_push_undo (wbc, G_OBJECT (me));
6313 /******************************************************************/
6315 #define CMD_DATA_SHUFFLE_TYPE (cmd_data_shuffle_get_type ())
6316 #define CMD_DATA_SHUFFLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DATA_SHUFFLE_TYPE, CmdDataShuffle))
6318 typedef struct {
6319 GnmCommand cmd;
6320 data_shuffling_t *ds;
6321 } CmdDataShuffle;
6323 MAKE_GNM_COMMAND (CmdDataShuffle, cmd_data_shuffle, NULL)
6325 static gboolean
6326 cmd_data_shuffle_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6328 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6330 data_shuffling_redo (me->ds);
6331 return FALSE;
6334 static gboolean
6335 cmd_data_shuffle_undo (GnmCommand *cmd,
6336 G_GNUC_UNUSED WorkbookControl *wbc)
6338 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6340 data_shuffling_redo (me->ds);
6341 return FALSE;
6344 static void
6345 cmd_data_shuffle_finalize (GObject *cmd)
6347 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6349 data_shuffling_free (me->ds);
6350 gnm_command_finalize (cmd);
6353 gboolean
6354 cmd_data_shuffle (WorkbookControl *wbc, data_shuffling_t *sc, Sheet *sheet)
6356 CmdDataShuffle *me;
6358 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6359 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6361 me = g_object_new (CMD_DATA_SHUFFLE_TYPE, NULL);
6363 me->ds = sc;
6364 me->cmd.sheet = sheet;
6365 me->cmd.size = 1;
6366 me->cmd.cmd_descriptor = g_strdup (_("Shuffle Data"));
6368 return gnm_command_push_undo (wbc, G_OBJECT (me));
6371 /******************************************************************/
6373 #define CMD_TEXT_TO_COLUMNS_TYPE (cmd_text_to_columns_get_type ())
6374 #define CMD_TEXT_TO_COLUMNS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TEXT_TO_COLUMNS_TYPE, CmdTextToColumns))
6376 typedef struct {
6377 GnmCommand cmd;
6379 GnmCellRegion *contents;
6380 GnmPasteTarget dst;
6381 GnmRange src;
6382 Sheet *src_sheet;
6383 ColRowStateList *saved_sizes;
6384 } CmdTextToColumns;
6386 MAKE_GNM_COMMAND (CmdTextToColumns, cmd_text_to_columns, NULL)
6388 static gboolean
6389 cmd_text_to_columns_impl (GnmCommand *cmd, WorkbookControl *wbc,
6390 gboolean is_undo)
6392 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6393 GnmCellRegion *contents;
6395 g_return_val_if_fail (me != NULL, TRUE);
6396 g_return_val_if_fail (me->contents != NULL, TRUE);
6398 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
6399 if (clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc))) {
6400 /* There was a problem, avoid leaking */
6401 cellregion_unref (contents);
6402 return TRUE;
6405 cellregion_unref (me->contents);
6407 if (is_undo) {
6408 colrow_set_states (me->dst.sheet, FALSE,
6409 me->dst.range.start.row, me->saved_sizes);
6410 colrow_state_list_destroy (me->saved_sizes);
6411 me->saved_sizes = NULL;
6412 } else {
6413 me->saved_sizes = colrow_get_states (me->dst.sheet,
6414 FALSE, me->dst.range.start.row, me->dst.range.end.row);
6415 rows_height_update (me->dst.sheet, &me->dst.range, FALSE);
6418 me->contents = contents;
6420 /* Select the newly pasted contents (this queues a redraw) */
6421 select_range (me->dst.sheet, &me->dst.range, wbc);
6423 return FALSE;
6426 static gboolean
6427 cmd_text_to_columns_undo (GnmCommand *cmd, WorkbookControl *wbc)
6429 return cmd_text_to_columns_impl (cmd, wbc, TRUE);
6432 static gboolean
6433 cmd_text_to_columns_redo (GnmCommand *cmd, WorkbookControl *wbc)
6435 return cmd_text_to_columns_impl (cmd, wbc, FALSE);
6438 static void
6439 cmd_text_to_columns_finalize (GObject *cmd)
6441 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6443 if (me->saved_sizes)
6444 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
6445 if (me->contents) {
6446 cellregion_unref (me->contents);
6447 me->contents = NULL;
6449 gnm_command_finalize (cmd);
6452 gboolean
6453 cmd_text_to_columns (WorkbookControl *wbc,
6454 GnmRange const *src, Sheet *src_sheet,
6455 GnmRange const *target, Sheet *target_sheet,
6456 GnmCellRegion *contents)
6458 CmdTextToColumns *me;
6459 char *src_range_name, *target_range_name;
6461 g_return_val_if_fail (contents != NULL, TRUE);
6463 src_range_name = undo_range_name (src_sheet, src);
6464 target_range_name = undo_range_name (target_sheet, target);
6466 me = g_object_new (CMD_TEXT_TO_COLUMNS_TYPE, NULL);
6468 me->cmd.sheet = (src_sheet == target_sheet ? src_sheet : NULL);
6469 me->cmd.size = 1; /* FIXME? */
6470 me->cmd.cmd_descriptor = g_strdup_printf (_("Text (%s) to Columns (%s)"),
6471 src_range_name,
6472 target_range_name);
6473 me->dst.range = *target;
6474 me->dst.sheet = target_sheet;
6475 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
6476 me->src = *src;
6477 me->src_sheet = src_sheet;
6478 me->contents = contents;
6479 me->saved_sizes = NULL;
6481 g_free (src_range_name);
6482 g_free (target_range_name);
6484 /* Check array subdivision & merged regions */
6485 if (sheet_range_splits_region (target_sheet, &me->dst.range,
6486 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
6487 g_object_unref (me);
6488 return TRUE;
6491 return gnm_command_push_undo (wbc, G_OBJECT (me));
6494 /******************************************************************/
6496 #define CMD_GENERIC_TYPE (cmd_generic_get_type ())
6497 #define CMD_GENERIC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GENERIC_TYPE, CmdGeneric))
6499 typedef struct {
6500 GnmCommand cmd;
6501 GOUndo *undo, *redo;
6502 } CmdGeneric;
6504 MAKE_GNM_COMMAND (CmdGeneric, cmd_generic, NULL)
6506 static gboolean
6507 cmd_generic_undo (GnmCommand *cmd, WorkbookControl *wbc)
6509 CmdGeneric *me = CMD_GENERIC (cmd);
6510 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6511 return FALSE;
6514 static gboolean
6515 cmd_generic_redo (GnmCommand *cmd, WorkbookControl *wbc)
6517 CmdGeneric *me = CMD_GENERIC (cmd);
6518 go_undo_undo_with_data (me->redo, GO_CMD_CONTEXT (wbc));
6519 return FALSE;
6522 static void
6523 cmd_generic_finalize (GObject *cmd)
6525 CmdGeneric *me = CMD_GENERIC (cmd);
6527 g_object_unref (me->undo);
6528 g_object_unref (me->redo);
6530 gnm_command_finalize (cmd);
6533 gboolean
6534 cmd_generic_with_size (WorkbookControl *wbc, const char *txt,
6535 int size,
6536 GOUndo *undo, GOUndo *redo)
6538 CmdGeneric *me;
6540 g_return_val_if_fail (GO_IS_UNDO (undo), TRUE);
6541 g_return_val_if_fail (GO_IS_UNDO (redo), TRUE);
6543 me = g_object_new (CMD_GENERIC_TYPE, NULL);
6545 me->cmd.sheet = wb_control_cur_sheet (wbc);
6546 me->cmd.size = size;
6547 me->cmd.cmd_descriptor = g_strdup (txt);
6549 me->undo = undo;
6550 me->redo = redo;
6552 return gnm_command_push_undo (wbc, G_OBJECT (me));
6555 gboolean
6556 cmd_generic (WorkbookControl *wbc, const char *txt, GOUndo *undo, GOUndo *redo)
6558 return cmd_generic_with_size (wbc, txt, 1, undo, redo);
6561 /******************************************************************/
6563 #define CMD_GOAL_SEEK_TYPE (cmd_goal_seek_get_type ())
6564 #define CMD_GOAL_SEEK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GOAL_SEEK_TYPE, CmdGoalSeek))
6566 typedef struct {
6567 GnmCommand cmd;
6569 GnmCell *cell;
6570 GnmValue *ov;
6571 GnmValue *nv;
6572 } CmdGoalSeek;
6574 MAKE_GNM_COMMAND (CmdGoalSeek, cmd_goal_seek, NULL)
6576 static gboolean
6577 cmd_goal_seek_impl (GnmCell *cell, GnmValue *value)
6579 sheet_cell_set_value (cell, value_dup(value));
6580 return FALSE;
6584 static gboolean
6585 cmd_goal_seek_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6587 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6589 return cmd_goal_seek_impl (me->cell, me->ov);
6592 static gboolean
6593 cmd_goal_seek_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6595 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6597 return cmd_goal_seek_impl (me->cell, me->nv);
6600 static void
6601 cmd_goal_seek_finalize (GObject *cmd)
6603 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6605 value_release (me->ov);
6606 me->ov = NULL;
6607 value_release (me->nv);
6608 me->nv = NULL;
6610 gnm_command_finalize (cmd);
6613 gboolean
6614 cmd_goal_seek (WorkbookControl *wbc, GnmCell *cell, GnmValue *ov, GnmValue *nv)
6616 CmdGoalSeek *me;
6617 GnmRange range;
6619 g_return_val_if_fail (cell != NULL, TRUE);
6620 g_return_val_if_fail (ov != NULL || nv != NULL, TRUE);
6622 me = g_object_new (CMD_GOAL_SEEK_TYPE, NULL);
6624 me->cmd.sheet = cell->base.sheet;
6625 me->cmd.size = 1;
6626 range_init_cellpos (&range, &cell->pos);
6627 me->cmd.cmd_descriptor = g_strdup_printf
6628 (_("Goal Seek (%s)"), undo_range_name (cell->base.sheet, &range));
6630 me->cell = cell;
6631 me->ov = ov;
6632 me->nv = nv;
6634 if (me->ov == NULL)
6635 me->ov = value_dup (cell->value);
6636 if (me->nv == NULL)
6637 me->nv = value_dup (cell->value);
6639 return gnm_command_push_undo (wbc, G_OBJECT (me));
6642 /******************************************************************/
6644 #if 0
6645 #define CMD_FREEZE_PANES_TYPE (cmd_freeze_panes_get_type ())
6646 #define CMD_FREEZE_PANES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FREEZE_PANES_TYPE, CmdFreezePanes))
6648 typedef struct {
6649 GnmCommand cmd;
6651 SheetView *sv;
6652 GnmCellPos pos;
6653 } CmdFreezePanes;
6655 MAKE_GNM_COMMAND (CmdFreezePanes, cmd_freeze_panes, NULL)
6657 static gboolean
6658 cmd_freeze_panes_undo (GnmCommand *cmd, WorkbookControl *wbc)
6660 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6662 return FALSE;
6665 static gboolean
6666 cmd_freeze_panes_redo (GnmCommand *cmd, WorkbookControl *wbc)
6668 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6670 return FALSE;
6673 static void
6674 cmd_freeze_panes_finalize (GObject *cmd)
6676 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6678 gnm_command_finalize (cmd);
6682 * cmd_freeze_panes:
6683 * @wbc: where to report errors
6684 * @sv: the view to freeze
6685 * @frozen:
6686 * @unfrozen:
6688 * Returns TRUE on error
6690 gboolean
6691 cmd_freeze_panes (WorkbookControl *wbc, SheetView *sv,
6692 GnmCellPos const *frozen, GnmCellPos const *unfrozen)
6694 CmdFreezePanes *me;
6696 g_return_val_if_fail (name != NULL, TRUE);
6697 g_return_val_if_fail (pp != NULL, TRUE);
6698 g_return_val_if_fail (expr != NULL, TRUE);
6700 me = g_object_new (CMD_FREEZE_PANES_TYPE, NULL);
6701 me->sv = sv;
6702 me->frozen = f;
6703 me->unfrozen = expr;
6704 return gnm_command_push_undo (wbc, G_OBJECT (me));
6707 #endif
6710 /******************************************************************/
6713 #define CMD_TABULATE_TYPE (cmd_tabulate_get_type ())
6714 #define CMD_TABULATE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TABULATE_TYPE, CmdTabulate))
6716 typedef struct {
6717 GnmCommand cmd;
6718 GSList *sheet_idx;
6719 GnmTabulateInfo *data;
6720 } CmdTabulate;
6722 MAKE_GNM_COMMAND (CmdTabulate, cmd_tabulate, NULL)
6724 static gint
6725 cmd_tabulate_cmp_f (gconstpointer a,
6726 gconstpointer b)
6728 guint const a_val = GPOINTER_TO_INT (a);
6729 guint const b_val = GPOINTER_TO_INT (b);
6731 if (a_val > b_val)
6732 return -1;
6733 if (a_val < b_val)
6734 return 1;
6735 return 0;
6738 static gboolean
6739 cmd_tabulate_undo (GnmCommand *cmd, WorkbookControl *wbc)
6741 CmdTabulate *me = CMD_TABULATE (cmd);
6742 GSList *l;
6743 gboolean res = TRUE;
6745 me->sheet_idx = g_slist_sort (me->sheet_idx,
6746 cmd_tabulate_cmp_f);
6748 for (l = me->sheet_idx; l != NULL; l = l->next) {
6749 int i = GPOINTER_TO_INT (l->data);
6750 Sheet *new_sheet =
6751 workbook_sheet_by_index (wb_control_get_workbook (wbc),
6753 res = res && command_undo_sheet_delete (new_sheet);
6755 return !res;
6758 static gboolean
6759 cmd_tabulate_redo (GnmCommand *cmd, WorkbookControl *wbc)
6761 CmdTabulate *me = CMD_TABULATE (cmd);
6763 g_slist_free (me->sheet_idx);
6764 me->sheet_idx = do_tabulation (wbc, me->data);
6766 return (me->sheet_idx == NULL);
6769 static void
6770 cmd_tabulate_finalize (GObject *cmd)
6772 CmdTabulate *me = CMD_TABULATE (cmd);
6774 g_free (me->data->cells);
6775 g_free (me->data->minima);
6776 g_free (me->data->maxima);
6777 g_free (me->data->steps);
6778 g_free (me->data);
6779 gnm_command_finalize (cmd);
6782 gboolean
6783 cmd_tabulate (WorkbookControl *wbc, gpointer data)
6785 CmdTabulate *me;
6787 g_return_val_if_fail (data != NULL, TRUE);
6789 me = g_object_new (CMD_TABULATE_TYPE, NULL);
6791 me->cmd.sheet = NULL;
6792 me->cmd.size = 1;
6793 me->cmd.cmd_descriptor =
6794 g_strdup_printf (_("Tabulating Dependencies"));
6795 me->data = data;
6796 me->sheet_idx = NULL;
6798 return gnm_command_push_undo (wbc, G_OBJECT (me));
6801 /******************************************************************/
6803 #define CMD_SO_GRAPH_CONFIG_TYPE (cmd_so_graph_config_get_type ())
6804 #define CMD_SO_GRAPH_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_GRAPH_CONFIG_TYPE, CmdSOGraphConfig))
6806 typedef struct {
6807 GnmCommand cmd;
6808 SheetObject *so;
6809 GogGraph *new_graph;
6810 GogGraph *old_graph;
6811 } CmdSOGraphConfig;
6813 MAKE_GNM_COMMAND (CmdSOGraphConfig, cmd_so_graph_config, NULL)
6815 static gboolean
6816 cmd_so_graph_config_redo (GnmCommand *cmd,
6817 G_GNUC_UNUSED WorkbookControl *wbc)
6819 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6820 sheet_object_graph_set_gog (me->so, me->new_graph);
6821 return FALSE;
6824 static gboolean
6825 cmd_so_graph_config_undo (GnmCommand *cmd,
6826 G_GNUC_UNUSED WorkbookControl *wbc)
6828 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6829 sheet_object_graph_set_gog (me->so, me->old_graph);
6830 return FALSE;
6833 static void
6834 cmd_so_graph_config_finalize (GObject *cmd)
6836 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6838 g_object_unref (me->so);
6839 g_object_unref (me->new_graph);
6840 g_object_unref (me->old_graph);
6842 gnm_command_finalize (cmd);
6845 gboolean
6846 cmd_so_graph_config (WorkbookControl *wbc, SheetObject *so,
6847 GObject *n_graph, GObject *o_graph)
6849 CmdSOGraphConfig *me;
6851 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6852 g_return_val_if_fail (GNM_IS_SO_GRAPH (so), TRUE);
6853 g_return_val_if_fail (GOG_IS_GRAPH (n_graph), TRUE);
6854 g_return_val_if_fail (GOG_IS_GRAPH (o_graph), TRUE);
6856 me = g_object_new (CMD_SO_GRAPH_CONFIG_TYPE, NULL);
6858 me->so = so;
6859 g_object_ref (so);
6861 me->new_graph = GOG_GRAPH (n_graph);
6862 g_object_ref (me->new_graph);
6863 me->old_graph = GOG_GRAPH (o_graph);
6864 g_object_ref (me->old_graph);
6866 me->cmd.sheet = sheet_object_get_sheet (so);
6867 me->cmd.size = 10;
6868 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Graph"));
6870 return gnm_command_push_undo (wbc, G_OBJECT (me));
6873 /******************************************************************/
6875 #define CMD_SO_COMPONENT_CONFIG_TYPE (cmd_so_component_config_get_type ())
6876 #define CMD_SO_COMPONENT_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_COMPONENT_CONFIG_TYPE, CmdSOComponentConfig))
6878 typedef struct {
6879 GnmCommand cmd;
6880 SheetObject *so;
6881 GOComponent *new_obj;
6882 GOComponent *old_obj;
6883 } CmdSOComponentConfig;
6885 MAKE_GNM_COMMAND (CmdSOComponentConfig, cmd_so_component_config, NULL)
6887 static gboolean
6888 cmd_so_component_config_redo (GnmCommand *cmd,
6889 G_GNUC_UNUSED WorkbookControl *wbc)
6891 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6892 sheet_object_component_set_component (me->so, me->new_obj);
6893 return FALSE;
6896 static gboolean
6897 cmd_so_component_config_undo (GnmCommand *cmd,
6898 G_GNUC_UNUSED WorkbookControl *wbc)
6900 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6901 sheet_object_component_set_component (me->so, me->old_obj);
6902 return FALSE;
6905 static void
6906 cmd_so_component_config_finalize (GObject *cmd)
6908 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6910 g_object_unref (me->so);
6911 g_object_unref (me->new_obj);
6912 g_object_unref (me->old_obj);
6914 gnm_command_finalize (cmd);
6917 gboolean
6918 cmd_so_component_config (WorkbookControl *wbc, SheetObject *so,
6919 GObject *n_obj, GObject *o_obj)
6921 CmdSOComponentConfig *me;
6923 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6924 g_return_val_if_fail (GNM_IS_SO_COMPONENT (so), TRUE);
6925 g_return_val_if_fail (GO_IS_COMPONENT (n_obj), TRUE);
6926 g_return_val_if_fail (GO_IS_COMPONENT (o_obj), TRUE);
6928 me = g_object_new (CMD_SO_COMPONENT_CONFIG_TYPE, NULL);
6930 me->so = so;
6931 g_object_ref (so);
6933 me->new_obj = GO_COMPONENT (g_object_ref (n_obj));
6934 me->old_obj = GO_COMPONENT (g_object_ref (o_obj));
6936 me->cmd.sheet = sheet_object_get_sheet (so);
6937 me->cmd.size = 10;
6938 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Object"));
6940 return gnm_command_push_undo (wbc, G_OBJECT (me));
6943 /******************************************************************/
6945 #define CMD_TOGGLE_RTL_TYPE (cmd_toggle_rtl_get_type ())
6946 #define CMD_TOGGLE_RTL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TOGGLE_RTL_TYPE, CmdToggleRTL))
6948 typedef GnmCommand CmdToggleRTL;
6950 MAKE_GNM_COMMAND (CmdToggleRTL, cmd_toggle_rtl, NULL)
6952 static gboolean
6953 cmd_toggle_rtl_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6955 go_object_toggle (cmd->sheet, "text-is-rtl");
6956 return FALSE;
6959 static gboolean
6960 cmd_toggle_rtl_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6962 return cmd_toggle_rtl_redo (cmd, wbc);
6965 static void
6966 cmd_toggle_rtl_finalize (GObject *cmd)
6968 gnm_command_finalize (cmd);
6971 gboolean
6972 cmd_toggle_rtl (WorkbookControl *wbc, Sheet *sheet)
6974 CmdToggleRTL *me;
6976 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6977 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6979 me = g_object_new (CMD_TOGGLE_RTL_TYPE, NULL);
6980 me->sheet = sheet;
6981 me->size = 1;
6982 me->cmd_descriptor = g_strdup (sheet->text_is_rtl ? _("Left to Right") : _("Right to Left"));
6984 return gnm_command_push_undo (wbc, G_OBJECT (me));
6987 /******************************************************************/
6989 #define CMD_SO_SET_VALUE_TYPE (cmd_so_set_value_get_type ())
6990 #define CMD_SO_SET_VALUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_VALUE_TYPE, CmdSOSetValue))
6992 typedef struct {
6993 GnmCommand cmd;
6994 GnmCellRef ref;
6995 GnmValue *val;
6996 GOUndo *undo;
6997 } CmdSOSetValue;
6999 MAKE_GNM_COMMAND (CmdSOSetValue, cmd_so_set_value, NULL)
7001 static gboolean
7002 cmd_so_set_value_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7004 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7005 Sheet *sheet = me->ref.sheet;
7006 GnmCell *cell = sheet_cell_fetch (sheet, me->ref.col, me->ref.row);
7008 sheet_cell_set_value (cell, value_dup (me->val));
7009 sheet_update (sheet);
7011 return FALSE;
7014 static gboolean
7015 cmd_so_set_value_undo (GnmCommand *cmd, WorkbookControl *wbc)
7017 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7019 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
7021 return FALSE;
7024 static void
7025 cmd_so_set_value_finalize (GObject *cmd)
7027 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7029 value_release (me->val);
7030 g_object_unref (me->undo);
7032 gnm_command_finalize (cmd);
7035 gboolean
7036 cmd_so_set_value (WorkbookControl *wbc,
7037 const char *text,
7038 const GnmCellRef *pref,
7039 GnmValue *new_val,
7040 Sheet *sheet)
7042 CmdSOSetValue *me;
7043 GnmRange r;
7045 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7047 r.start.col = r.end.col = pref->col;
7048 r.start.row = r.end.row = pref->row;
7050 me = g_object_new (CMD_SO_SET_VALUE_TYPE, NULL);
7051 me->cmd.sheet = sheet;
7052 me->cmd.size = 1;
7053 me->cmd.cmd_descriptor = g_strdup (text);
7054 me->ref = *pref;
7055 me->val = new_val;
7056 me->undo = clipboard_copy_range_undo (pref->sheet, &r);
7058 return gnm_command_push_undo (wbc, G_OBJECT (me));
7061 /******************************************************************/
7063 #define CMD_HYPERLINK_TYPE (cmd_hyperlink_get_type ())
7064 #define CMD_HYPERLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_HYPERLINK_TYPE, CmdHyperlink))
7066 typedef struct {
7067 GnmCommand cmd;
7068 GSList *selection;
7069 GnmStyle *new_style;
7070 char *opt_content;
7071 GOUndo *undo;
7072 gboolean update_size;
7073 } CmdHyperlink;
7075 static void
7076 cmd_hyperlink_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
7078 CmdHyperlink const *orig = (CmdHyperlink const *) cmd;
7080 if (orig->new_style)
7081 gnm_style_ref (orig->new_style);
7083 cmd_selection_hyperlink (wbc, orig->new_style, NULL,
7084 g_strdup (orig->opt_content));
7086 MAKE_GNM_COMMAND (CmdHyperlink, cmd_hyperlink, cmd_hyperlink_repeat)
7088 static gboolean
7089 cmd_hyperlink_undo (GnmCommand *cmd, WorkbookControl *wbc)
7091 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7092 Workbook *wb = wb_control_get_workbook (wbc);
7094 if (me->undo) {
7095 go_undo_undo (me->undo);
7096 g_clear_object (&me->undo);
7099 select_selection (me->cmd.sheet, me->selection, wbc);
7101 WORKBOOK_FOREACH_CONTROL (wb, view, ctl, {
7102 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS);
7105 return FALSE;
7108 static GnmValue *
7109 cb_hyperlink_set_text (GnmCellIter const *iter, gpointer user)
7111 CmdHyperlink *me = user;
7112 GnmCell *cell = iter->cell;
7114 if (cell == NULL)
7115 cell = sheet_cell_fetch (iter->pp.sheet,
7116 iter->pp.eval.col,
7117 iter->pp.eval.row);
7119 /* We skip non-empty cells. */
7120 if (gnm_cell_is_empty (cell) &&
7121 !gnm_cell_is_nonsingleton_array (cell)) {
7122 sheet_cell_set_value (cell, value_new_string (me->opt_content));
7123 if (me->update_size)
7124 me->cmd.size++;
7127 return NULL;
7130 static gboolean
7131 cmd_hyperlink_redo (GnmCommand *cmd, WorkbookControl *wbc)
7133 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7134 GSList *l;
7135 Workbook *wb = wb_control_get_workbook (wbc);
7136 Sheet *sheet;
7138 g_return_val_if_fail (me != NULL, TRUE);
7140 sheet = me->cmd.sheet;
7142 /* Check for locked cells */
7143 if (cmd_selection_is_locked_effective (sheet, me->selection,
7144 wbc, _("Changing Hyperlink")))
7145 return TRUE;
7147 me->undo = clipboard_copy_ranges_undo (sheet, me->selection);
7149 for (l = me->selection; l; l = l->next) {
7150 GnmRange const *r = l->data;
7152 if (me->new_style) {
7153 gnm_style_ref (me->new_style);
7154 sheet_apply_style (sheet, r, me->new_style);
7155 sheet_flag_style_update_range (sheet, r);
7158 if (me->opt_content) {
7159 sheet_foreach_cell_in_range (sheet, CELL_ITER_ALL, r,
7160 cb_hyperlink_set_text,
7161 me);
7164 me->update_size = FALSE;
7166 sheet_redraw_all (sheet, FALSE);
7167 sheet_mark_dirty (sheet);
7169 select_selection (sheet, me->selection, wbc);
7171 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
7172 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
7174 return FALSE;
7177 static void
7178 cmd_hyperlink_finalize (GObject *cmd)
7180 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7182 g_clear_object (&me->undo);
7184 if (me->new_style)
7185 gnm_style_unref (me->new_style);
7186 me->new_style = NULL;
7188 range_fragment_free (me->selection);
7189 me->selection = NULL;
7191 g_free (me->opt_content);
7193 gnm_command_finalize (cmd);
7197 * cmd_selection_hyperlink:
7198 * @wbc: the workbook control.
7199 * @style: style to apply to the selection
7200 * @opt_translated_name: An optional name to use in place of 'Hyperlink Cells'
7201 * @opt_content: optional content for otherwise empty cells.
7203 * If borders is non NULL, then the GnmBorder references are passed,
7204 * the GnmStyle reference is also passed.
7206 * It absorbs the reference to the style.
7208 * Return value: TRUE if there was a problem
7210 gboolean
7211 cmd_selection_hyperlink (WorkbookControl *wbc,
7212 GnmStyle *style,
7213 char const *opt_translated_name,
7214 char *opt_content)
7216 CmdHyperlink *me;
7217 SheetView *sv = wb_control_cur_sheet_view (wbc);
7219 me = g_object_new (CMD_HYPERLINK_TYPE, NULL);
7221 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
7222 me->new_style = style;
7224 me->cmd.sheet = sv_sheet (sv);
7225 me->cmd.size = 1; /* Updated later. */
7226 me->update_size = TRUE;
7228 me->opt_content = opt_content;
7230 if (opt_translated_name == NULL) {
7231 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
7233 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing hyperlink of %s"), names);
7234 g_free (names);
7235 } else
7236 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
7239 return gnm_command_push_undo (wbc, G_OBJECT (me));
7242 /******************************************************************/
7245 #define CMD_SO_SET_LINKS_TYPE (cmd_so_set_links_get_type ())
7246 #define CMD_SO_SET_LINKS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_LINKS_TYPE, CmdSOSetLink))
7248 typedef struct {
7249 GnmCommand cmd;
7250 SheetObject *so;
7251 GnmExprTop const *output;
7252 GnmExprTop const *content;
7253 gboolean as_index;
7254 } CmdSOSetLink;
7256 MAKE_GNM_COMMAND (CmdSOSetLink, cmd_so_set_links, NULL)
7258 static gboolean
7259 cmd_so_set_links_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7261 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7262 GnmExprTop const *old_output;
7263 GnmExprTop const *old_content;
7264 gboolean old_as_index;
7266 old_content = sheet_widget_list_base_get_content_link (me->so);
7267 old_output = sheet_widget_list_base_get_result_link (me->so);
7268 old_as_index = sheet_widget_list_base_result_type_is_index (me->so);
7270 sheet_widget_list_base_set_links
7271 (me->so, me->output, me->content);
7272 if (old_as_index != me->as_index) {
7273 sheet_widget_list_base_set_result_type (me->so, me->as_index);
7274 me->as_index = old_as_index;
7276 if (me->output)
7277 gnm_expr_top_unref (me->output);
7278 if (me->content)
7279 gnm_expr_top_unref (me->content);
7280 me->output = old_output;
7281 me->content = old_content;
7283 return FALSE;
7286 static gboolean
7287 cmd_so_set_links_undo (GnmCommand *cmd, WorkbookControl *wbc)
7289 return cmd_so_set_links_redo (cmd, wbc);
7292 static void
7293 cmd_so_set_links_finalize (GObject *cmd)
7295 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7297 if (me->output)
7298 gnm_expr_top_unref (me->output);
7299 if (me->content)
7300 gnm_expr_top_unref (me->content);
7301 gnm_command_finalize (cmd);
7304 gboolean
7305 cmd_so_set_links (WorkbookControl *wbc,
7306 SheetObject *so,
7307 GnmExprTop const *output,
7308 GnmExprTop const *content,
7309 gboolean as_index)
7311 CmdSOSetLink *me;
7313 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7315 me = g_object_new (CMD_SO_SET_LINKS_TYPE, NULL);
7316 me->cmd.sheet = sheet_object_get_sheet (so);
7317 me->cmd.size = 1;
7318 me->cmd.cmd_descriptor = g_strdup (_("Configure List"));
7319 me->so = so;
7320 me->output = output;
7321 me->content = content;
7322 me->as_index = as_index;
7324 return gnm_command_push_undo (wbc, G_OBJECT (me));
7327 /******************************************************************/
7331 #define CMD_SO_SET_FRAME_LABEL_TYPE (cmd_so_set_frame_label_get_type ())
7332 #define CMD_SO_SET_FRAME_LABEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_FRAME_LABEL_TYPE, CmdSOSetFrameLabel))
7334 typedef struct {
7335 GnmCommand cmd;
7336 SheetObject *so;
7337 char *old_label;
7338 char *new_label;
7339 } CmdSOSetFrameLabel;
7341 MAKE_GNM_COMMAND (CmdSOSetFrameLabel, cmd_so_set_frame_label, NULL)
7343 static gboolean
7344 cmd_so_set_frame_label_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7346 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7348 sheet_widget_frame_set_label (me->so, me->new_label);
7350 return FALSE;
7353 static gboolean
7354 cmd_so_set_frame_label_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7356 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7358 sheet_widget_frame_set_label (me->so, me->old_label);
7360 return FALSE;
7363 static void
7364 cmd_so_set_frame_label_finalize (GObject *cmd)
7366 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7368 g_free (me->old_label);
7369 me->old_label = NULL;
7371 g_free (me->new_label);
7372 me->new_label = NULL;
7374 gnm_command_finalize (cmd);
7377 gboolean
7378 cmd_so_set_frame_label (WorkbookControl *wbc,
7379 SheetObject *so,
7380 char *old_label, char *new_label )
7382 CmdSOSetFrameLabel *me;
7384 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7386 me = g_object_new (CMD_SO_SET_FRAME_LABEL_TYPE, NULL);
7387 me->cmd.sheet = sheet_object_get_sheet (so);
7388 me->cmd.size = 1;
7389 me->cmd.cmd_descriptor = g_strdup (_("Set Frame Label"));
7390 me->so = so;
7391 me->old_label = old_label;
7392 me->new_label = new_label;
7394 return gnm_command_push_undo (wbc, G_OBJECT (me));
7397 /******************************************************************/
7398 #define CMD_SO_SET_BUTTON_TYPE (cmd_so_set_button_get_type ())
7399 #define CMD_SO_SET_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_BUTTON_TYPE, CmdSOSetButton))
7401 typedef struct {
7402 GnmCommand cmd;
7403 SheetObject *so;
7404 GnmExprTop const *new_link;
7405 GnmExprTop const *old_link;
7406 char *old_label;
7407 char *new_label;
7408 } CmdSOSetButton;
7410 MAKE_GNM_COMMAND (CmdSOSetButton, cmd_so_set_button, NULL)
7412 static gboolean
7413 cmd_so_set_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7415 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7417 sheet_widget_button_set_link (me->so, me->new_link);
7418 sheet_widget_button_set_label (me->so, me->new_label);
7420 return FALSE;
7423 static gboolean
7424 cmd_so_set_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7426 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7428 sheet_widget_button_set_link (me->so, me->old_link);
7429 sheet_widget_button_set_label (me->so, me->old_label);
7431 return FALSE;
7434 static void
7435 cmd_so_set_button_finalize (GObject *cmd)
7437 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7439 if (me->new_link)
7440 gnm_expr_top_unref (me->new_link);
7441 if (me->old_link)
7442 gnm_expr_top_unref (me->old_link);
7443 g_free (me->old_label);
7444 g_free (me->new_label);
7445 gnm_command_finalize (cmd);
7448 gboolean
7449 cmd_so_set_button (WorkbookControl *wbc,
7450 SheetObject *so, GnmExprTop const *lnk,
7451 char *old_label, char *new_label)
7453 CmdSOSetButton *me;
7455 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7457 me = g_object_new (CMD_SO_SET_BUTTON_TYPE, NULL);
7458 me->cmd.sheet = sheet_object_get_sheet (so);
7459 me->cmd.size = 1;
7460 me->cmd.cmd_descriptor = g_strdup (_("Configure Button"));
7461 me->so = so;
7462 me->new_link = lnk;
7463 me->old_label = old_label;
7464 me->new_label = new_label;
7466 me->old_link = sheet_widget_button_get_link (so);
7468 return gnm_command_push_undo (wbc, G_OBJECT (me));
7471 /******************************************************************/
7472 #define CMD_SO_SET_RADIO_BUTTON_TYPE (cmd_so_set_radio_button_get_type ())
7473 #define CMD_SO_SET_RADIO_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_RADIO_BUTTON_TYPE, CmdSOSetRadioButton))
7475 typedef struct {
7476 GnmCommand cmd;
7477 SheetObject *so;
7478 GnmExprTop const *new_link;
7479 GnmExprTop const *old_link;
7480 char *old_label;
7481 char *new_label;
7482 GnmValue *old_value;
7483 GnmValue *new_value;
7484 } CmdSOSetRadioButton;
7486 MAKE_GNM_COMMAND (CmdSOSetRadioButton, cmd_so_set_radio_button, NULL)
7488 static gboolean
7489 cmd_so_set_radio_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7491 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7493 sheet_widget_radio_button_set_link (me->so, me->new_link);
7494 sheet_widget_radio_button_set_label (me->so, me->new_label);
7495 sheet_widget_radio_button_set_value (me->so, me->new_value);
7497 return FALSE;
7500 static gboolean
7501 cmd_so_set_radio_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7503 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7505 sheet_widget_radio_button_set_link (me->so, me->old_link);
7506 sheet_widget_radio_button_set_label (me->so, me->old_label);
7507 sheet_widget_radio_button_set_value (me->so, me->old_value);
7509 return FALSE;
7512 static void
7513 cmd_so_set_radio_button_finalize (GObject *cmd)
7515 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7517 if (me->new_link)
7518 gnm_expr_top_unref (me->new_link);
7519 if (me->old_link)
7520 gnm_expr_top_unref (me->old_link);
7521 g_free (me->old_label);
7522 g_free (me->new_label);
7523 value_release (me->old_value);
7524 value_release (me->new_value);
7525 gnm_command_finalize (cmd);
7528 gboolean
7529 cmd_so_set_radio_button (WorkbookControl *wbc,
7530 SheetObject *so, GnmExprTop const *lnk,
7531 char *old_label, char *new_label,
7532 GnmValue *old_value, GnmValue *new_value)
7534 CmdSOSetRadioButton *me;
7536 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7538 me = g_object_new (CMD_SO_SET_RADIO_BUTTON_TYPE, NULL);
7539 me->cmd.sheet = sheet_object_get_sheet (so);
7540 me->cmd.size = 1;
7541 me->cmd.cmd_descriptor = g_strdup (_("Configure Radio Button"));
7542 me->so = so;
7543 me->new_link = lnk;
7544 me->old_label = old_label;
7545 me->new_label = new_label;
7546 me->old_value = old_value;
7547 me->new_value = new_value;
7549 me->old_link = sheet_widget_radio_button_get_link (so);
7551 return gnm_command_push_undo (wbc, G_OBJECT (me));
7554 /******************************************************************/
7555 #define CMD_SO_SET_CHECKBOX_TYPE (cmd_so_set_checkbox_get_type ())
7556 #define CMD_SO_SET_CHECKBOX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_CHECKBOX_TYPE, CmdSOSetCheckbox))
7558 typedef struct {
7559 GnmCommand cmd;
7560 SheetObject *so;
7561 GnmExprTop const *new_link;
7562 GnmExprTop const *old_link;
7563 char *old_label;
7564 char *new_label;
7565 } CmdSOSetCheckbox;
7567 MAKE_GNM_COMMAND (CmdSOSetCheckbox, cmd_so_set_checkbox, NULL)
7569 static gboolean
7570 cmd_so_set_checkbox_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7572 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7574 sheet_widget_checkbox_set_link (me->so, me->new_link);
7575 sheet_widget_checkbox_set_label (me->so, me->new_label);
7577 return FALSE;
7580 static gboolean
7581 cmd_so_set_checkbox_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7583 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7585 sheet_widget_checkbox_set_link (me->so, me->old_link);
7586 sheet_widget_checkbox_set_label (me->so, me->old_label);
7588 return FALSE;
7591 static void
7592 cmd_so_set_checkbox_finalize (GObject *cmd)
7594 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7596 if (me->new_link)
7597 gnm_expr_top_unref (me->new_link);
7598 if (me->old_link)
7599 gnm_expr_top_unref (me->old_link);
7600 g_free (me->old_label);
7601 g_free (me->new_label);
7602 gnm_command_finalize (cmd);
7605 gboolean
7606 cmd_so_set_checkbox (WorkbookControl *wbc,
7607 SheetObject *so, GnmExprTop const *lnk,
7608 char *old_label, char *new_label)
7610 CmdSOSetCheckbox *me;
7612 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7614 me = g_object_new (CMD_SO_SET_CHECKBOX_TYPE, NULL);
7615 me->cmd.sheet = sheet_object_get_sheet (so);
7616 me->cmd.size = 1;
7617 me->cmd.cmd_descriptor = g_strdup (_("Configure Checkbox"));
7618 me->so = so;
7619 me->new_link = lnk;
7620 me->old_label = old_label;
7621 me->new_label = new_label;
7623 me->old_link = sheet_widget_checkbox_get_link (so);
7625 return gnm_command_push_undo (wbc, G_OBJECT (me));
7628 /******************************************************************/
7630 #define CMD_SO_SET_ADJUSTMENT_TYPE (cmd_so_set_adjustment_get_type ())
7631 #define CMD_SO_SET_ADJUSTMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_ADJUSTMENT_TYPE, CmdSOSetAdjustment))
7633 typedef struct {
7634 GnmCommand cmd;
7635 SheetObject *so;
7636 GnmExprTop const *new_link;
7637 GnmExprTop const *old_link;
7638 double old_lower;
7639 double old_upper;
7640 double old_step;
7641 double old_page;
7642 gboolean old_horizontal;
7643 } CmdSOSetAdjustment;
7645 MAKE_GNM_COMMAND (CmdSOSetAdjustment, cmd_so_set_adjustment, NULL)
7647 static void
7648 cmd_so_set_adjustment_adj (CmdSOSetAdjustment *me)
7650 GtkAdjustment *adj = sheet_widget_adjustment_get_adjustment (me->so);
7652 double old_lower = gtk_adjustment_get_lower (adj);
7653 double old_upper = gtk_adjustment_get_upper (adj);
7654 double old_step = gtk_adjustment_get_step_increment (adj);
7655 double old_page = gtk_adjustment_get_page_increment (adj);
7656 gboolean old_horizontal;
7657 g_object_get (G_OBJECT (me->so), "horizontal", &old_horizontal, NULL);
7659 gtk_adjustment_configure (adj,
7660 gtk_adjustment_get_value (adj),
7661 me->old_lower,
7662 me->old_upper,
7663 me->old_step,
7664 me->old_page,
7665 gtk_adjustment_get_page_size (adj));
7666 g_object_set (G_OBJECT (me->so), "horizontal", me->old_horizontal, NULL);
7668 me->old_lower = old_lower;
7669 me->old_upper = old_upper;
7670 me->old_step = old_step;
7671 me->old_page = old_page;
7672 me->old_horizontal = old_horizontal;
7675 static gboolean
7676 cmd_so_set_adjustment_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7678 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7680 sheet_widget_adjustment_set_link (me->so, me->new_link);
7681 cmd_so_set_adjustment_adj (me);
7682 return FALSE;
7685 static gboolean
7686 cmd_so_set_adjustment_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7688 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7690 sheet_widget_adjustment_set_link (me->so, me->old_link);
7691 cmd_so_set_adjustment_adj (me);
7693 return FALSE;
7696 static void
7697 cmd_so_set_adjustment_finalize (GObject *cmd)
7699 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7701 if (me->new_link)
7702 gnm_expr_top_unref (me->new_link);
7703 if (me->old_link)
7704 gnm_expr_top_unref (me->old_link);
7705 gnm_command_finalize (cmd);
7708 gboolean
7709 cmd_so_set_adjustment (WorkbookControl *wbc,
7710 SheetObject *so, GnmExprTop const *lnk,
7711 gboolean horizontal,
7712 int lower, int upper,
7713 int step, int page,
7714 char const *undo_label)
7716 CmdSOSetAdjustment *me;
7718 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7720 me = g_object_new (CMD_SO_SET_ADJUSTMENT_TYPE, NULL);
7721 me->cmd.sheet = sheet_object_get_sheet (so);
7722 me->cmd.size = 1;
7723 me->cmd.cmd_descriptor = g_strdup ((undo_label == NULL) ?
7724 _("Configure Adjustment") : _(undo_label));
7725 me->so = so;
7726 me->new_link = lnk;
7727 me->old_lower = lower;
7728 me->old_upper = upper;
7729 me->old_step = step;
7730 me->old_page = page;
7731 me->old_horizontal = horizontal;
7733 me->old_link = sheet_widget_adjustment_get_link (so);
7735 return gnm_command_push_undo (wbc, G_OBJECT (me));
7738 /******************************************************************/
7740 gboolean
7741 cmd_autofilter_add_remove (WorkbookControl *wbc)
7743 SheetView *sv = wb_control_cur_sheet_view (wbc);
7744 GnmFilter *f = gnm_sheet_view_editpos_in_filter (sv);
7745 gboolean add = (f == NULL);
7746 char *descr = NULL, *name = NULL;
7747 GOUndo *undo = NULL;
7748 GOUndo *redo = NULL;
7749 gboolean result;
7752 if (add) {
7753 GnmRange region;
7754 GnmRange const *src = selection_first_range (sv,
7755 GO_CMD_CONTEXT (wbc), _("Add Filter"));
7756 GnmFilter *f_old = NULL;
7758 if (src == NULL)
7759 return TRUE;
7761 f_old = gnm_sheet_filter_intersect_rows
7762 (sv->sheet, src->start.row, src->end.row);
7764 if (f_old != NULL) {
7765 GnmRange *r = gnm_sheet_filter_can_be_extended
7766 (sv->sheet, f_old, src);
7767 if (r == NULL) {
7768 char *error;
7769 name = undo_range_name (sv->sheet, &(f_old->r));
7770 error = g_strdup_printf
7771 (_("Auto Filter blocked by %s"),
7772 name);
7773 g_free(name);
7774 go_cmd_context_error_invalid
7775 (GO_CMD_CONTEXT (wbc),
7776 _("AutoFilter"), error);
7777 g_free (error);
7778 return TRUE;
7780 /* extending existing filter. */
7781 undo = go_undo_binary_new
7782 (gnm_filter_ref (f_old), sv->sheet,
7783 (GOUndoBinaryFunc) gnm_filter_attach,
7784 (GFreeFunc) gnm_filter_unref,
7785 NULL);
7786 redo = go_undo_unary_new
7787 (gnm_filter_ref (f_old),
7788 (GOUndoUnaryFunc) gnm_filter_remove,
7789 (GFreeFunc) gnm_filter_unref);
7790 gnm_filter_remove (f_old);
7791 region = *r;
7792 g_free (r);
7793 } else {
7794 /* if only one row is selected
7795 * assume that the user wants to
7796 * filter the region below this row. */
7797 region = *src;
7798 if (src->start.row == src->end.row)
7799 gnm_sheet_guess_region (sv->sheet, &region);
7800 if (region.start.row == region.end.row) {
7801 go_cmd_context_error_invalid
7802 (GO_CMD_CONTEXT (wbc),
7803 _("AutoFilter"),
7804 _("Requires more than 1 row"));
7805 return TRUE;
7808 f = gnm_filter_new (sv->sheet, &region);
7809 if (f == NULL) {
7810 go_cmd_context_error_invalid
7811 (GO_CMD_CONTEXT (wbc),
7812 _("AutoFilter"),
7813 _("Unable to create Autofilter"));
7814 if (f_old)
7815 gnm_filter_attach (f_old, sv->sheet);
7816 return TRUE;
7819 gnm_filter_remove (f);
7820 if (f_old)
7821 gnm_filter_attach (f_old, sv->sheet);
7823 redo = go_undo_combine (go_undo_binary_new
7824 (gnm_filter_ref (f), sv->sheet,
7825 (GOUndoBinaryFunc) gnm_filter_attach,
7826 (GFreeFunc) gnm_filter_unref,
7827 NULL), redo);
7828 undo = go_undo_combine (undo,
7829 go_undo_unary_new
7831 (GOUndoUnaryFunc) gnm_filter_remove,
7832 (GFreeFunc) gnm_filter_unref));
7834 name = undo_range_name (sv->sheet, &(f->r));
7835 descr = g_strdup_printf
7836 ((f_old == NULL) ? _("Add Autofilter to %s")
7837 : _("Extend Autofilter to %s"),
7838 name);
7839 } else {
7840 undo = go_undo_binary_new
7841 (gnm_filter_ref (f), sv->sheet,
7842 (GOUndoBinaryFunc) gnm_filter_attach,
7843 (GFreeFunc) gnm_filter_unref,
7844 NULL);
7845 redo = go_undo_unary_new
7846 (gnm_filter_ref (f),
7847 (GOUndoUnaryFunc) gnm_filter_remove,
7848 (GFreeFunc) gnm_filter_unref);
7849 name = undo_range_name (sv->sheet, &(f->r));
7850 descr = g_strdup_printf (_("Remove Autofilter from %s"),
7851 name);
7853 result = cmd_generic (wbc, descr, undo, redo);
7854 g_free (name);
7855 g_free (descr);
7857 return result;
7861 /******************************************************************/
7863 gboolean cmd_autofilter_set_condition (WorkbookControl *wbc,
7864 GnmFilter *filter, unsigned i,
7865 GnmFilterCondition *cond)
7867 char *descr = NULL, *name = NULL;
7868 GOUndo *undo = NULL;
7869 GOUndo *redo = NULL;
7870 gboolean result;
7872 undo = gnm_undo_filter_set_condition_new (filter, i,
7873 NULL, TRUE);
7874 g_return_val_if_fail (undo != NULL, TRUE);
7875 redo = gnm_undo_filter_set_condition_new (filter, i,
7876 cond, FALSE);
7877 g_return_val_if_fail (redo != NULL, TRUE);
7879 name = undo_range_name (filter->sheet, &(filter->r));
7880 descr = g_strdup_printf (_("Change filter condition for %s"),
7881 name);
7883 result = cmd_generic (wbc, descr, undo, redo);
7884 g_free (name);
7885 g_free (descr);
7887 return result;
7891 /******************************************************************/
7893 static void
7894 cmd_page_breaks_set_breaks (Sheet *sheet,
7895 GnmPageBreaks const *breaks)
7897 print_info_set_breaks (sheet->print_info, gnm_page_breaks_dup (breaks));
7899 SHEET_FOREACH_CONTROL (sheet, sv, sc, wb_control_menu_state_update (sc_wbc (sc), MS_PAGE_BREAKS););
7902 gboolean
7903 cmd_page_breaks_clear (WorkbookControl *wbc, Sheet *sheet)
7905 GOUndo *undo = NULL;
7906 GOUndo *redo = NULL;
7908 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7909 g_return_val_if_fail (sheet != NULL, TRUE);
7911 if (sheet->print_info->page_breaks.v != NULL) {
7912 redo = go_undo_binary_new
7913 (sheet,
7914 gnm_page_breaks_new (TRUE),
7915 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7916 NULL,
7917 (GFreeFunc) gnm_page_breaks_free);
7918 undo = go_undo_binary_new
7919 (sheet,
7920 gnm_page_breaks_dup
7921 (sheet->print_info->page_breaks.v),
7922 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7923 NULL,
7924 (GFreeFunc) gnm_page_breaks_free);
7927 if (sheet->print_info->page_breaks.h != NULL) {
7928 redo = go_undo_combine
7929 (redo,
7930 go_undo_binary_new
7931 (sheet,
7932 gnm_page_breaks_new (FALSE),
7933 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7934 NULL,
7935 (GFreeFunc) gnm_page_breaks_free));
7937 undo = go_undo_combine
7938 (undo,
7939 go_undo_binary_new
7940 (sheet,
7941 gnm_page_breaks_dup
7942 (sheet->print_info->page_breaks.h),
7943 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7944 NULL,
7945 (GFreeFunc) gnm_page_breaks_free));
7948 if (undo != NULL)
7949 return cmd_generic (wbc, _("Clear All Page Breaks"), undo, redo);
7950 else
7951 return TRUE;
7954 gboolean
7955 cmd_page_break_toggle (WorkbookControl *wbc, Sheet *sheet, gboolean is_vert)
7957 SheetView const *sv = wb_control_cur_sheet_view (wbc);
7958 gint col = sv->edit_pos.col;
7959 gint row = sv->edit_pos.row;
7960 int rc = is_vert ? col : row;
7961 GnmPageBreaks *old, *new, *target;
7962 GnmPageBreakType type;
7963 char const *label;
7964 GOUndo *undo;
7965 GOUndo *redo;
7967 target = is_vert ? sheet->print_info->page_breaks.v
7968 : sheet->print_info->page_breaks.h;
7970 old = (target == NULL) ? gnm_page_breaks_new (is_vert)
7971 : gnm_page_breaks_dup (target);
7972 new = gnm_page_breaks_dup (old);
7974 if (gnm_page_breaks_get_break (new, rc) != GNM_PAGE_BREAK_MANUAL) {
7975 type = GNM_PAGE_BREAK_MANUAL;
7976 label = is_vert ? _("Remove Column Page Break") : _("Remove Row Page Break");
7977 } else {
7978 type = GNM_PAGE_BREAK_NONE;
7979 label = is_vert ? _("Add Column Page Break") : _("Add Row Page Break");
7982 gnm_page_breaks_set_break (new, rc, type);
7984 redo = go_undo_binary_new
7985 (sheet, new,
7986 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7987 NULL,
7988 (GFreeFunc) gnm_page_breaks_free);
7989 undo = go_undo_binary_new
7990 (sheet, old,
7991 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7992 NULL,
7993 (GFreeFunc) gnm_page_breaks_free);
7995 return cmd_generic (wbc, label, undo, redo);
7998 /******************************************************************/