Use application properties instead of ugly dual-use global variable
[gnumeric.git] / src / commands.c
blob0a018f31746977171e2c452e754322abbd2367dd
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * commands.c: Handlers to undo & redo commands
6 * Copyright (C) 1999-2008 Jody Goldberg (jody@gnome.org)
7 * Copyright (C) 2002-2008 Morten Welinder (terra@gnome.org)
9 * Contributors : Almer S. Tigelaar (almer@gnome.org)
10 * Andreas J. Guelzow (aguelzow@taliesin.ca)
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) version 3.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
25 * USA
27 #include <gnumeric-config.h>
28 #include <glib/gi18n-lib.h>
29 #include "gnumeric.h"
30 #include "commands.h"
31 #include "gnm-command-impl.h"
33 #include "application.h"
34 #include "sheet.h"
35 #include "sheet-view.h"
36 #include "sheet-style.h"
37 #include "gnm-format.h"
38 #include "format-template.h"
39 #include "command-context.h"
40 #include "workbook-control.h"
41 #include "workbook-view.h"
42 #include "workbook-priv.h" /* For the undo/redo queues and the FOREACH */
43 #include "ranges.h"
44 #include "sort.h"
45 #include "dependent.h"
46 #include "value.h"
47 #include "expr.h"
48 #include "func.h"
49 #include "expr-name.h"
50 #include "cell.h"
51 #include "sheet-merge.h"
52 #include "parse-util.h"
53 #include "print-info.h"
54 #include "clipboard.h"
55 #include "selection.h"
56 #include "colrow.h"
57 #include "style-border.h"
58 #include "auto-correct.h"
59 #include "sheet-autofill.h"
60 #include "mstyle.h"
61 #include "search.h"
62 #include "gutils.h"
63 #include "gui-util.h"
64 #include "sheet-object-cell-comment.h"
65 #include "sheet-object-widget.h"
66 #include "sheet-object.h"
67 #include "sheet-object-component.h"
68 #include "sheet-object-graph.h"
69 #include "sheet-control.h"
70 #include "sheet-control-gui.h"
71 #include "sheet-utils.h"
72 #include "style-color.h"
73 #include "sheet-filter.h"
74 #include "auto-format.h"
75 #include "tools/dao.h"
76 #include "gnumeric-conf.h"
77 #include "scenarios.h"
78 #include "data-shuffling.h"
79 #include "tools/tabulate.h"
80 #include "wbc-gtk.h"
81 #include "undo.h"
83 #include <goffice/goffice.h>
84 #include <gsf/gsf-doc-meta-data.h>
85 #include <string.h>
87 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
91 * There are several distinct stages to wrapping each command.
93 * 1) Find the appropriate place(s) in the catch all calls to activations
94 * of this logical function. Be careful. This should only be called by
95 * user initiated actions, not internal commands.
97 * 2) Copy the boiler plate code into place and implement the descriptor.
99 * 3) Implement the guts of the support functions.
101 * That way undo redo just become applications of the old or the new styles.
103 * Design thoughts:
104 * 1) redo : this should be renamed 'exec' and should be the place that the
105 * the actual command executes. This avoid duplicating the code for
106 * application and re-application.
108 * 2) The command objects are responsible for generating recalc and redraw
109 * events. None of the internal utility routines should do so. Those are
110 * expensive events and should only be done once per command to avoid
111 * duplicating work. The lower levels can queue redraws if they must, and
112 * flag state changes but the call to gnm_app_recalc and sheet_update is
113 * by GnmCommand.
115 * FIXME: Filter the list of commands when a sheet is deleted.
117 * TODO : Possibly clear lists on save.
119 * TODO : Reqs for selective undo
121 * Future thoughts
122 * - undoable preference setting ? XL does not have this. Do we want it ?
124 /******************************************************************/
126 #define GNM_COMMAND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_COMMAND_TYPE, GnmCommand))
127 #define GNM_COMMAND_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_COMMAND_TYPE, GnmCommandClass))
128 #define GNM_IS_COMMAND(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_COMMAND_TYPE))
129 #define CMD_CLASS(o) GNM_COMMAND_CLASS (G_OBJECT_GET_CLASS(cmd))
131 GSF_CLASS (GnmCommand, gnm_command, NULL, NULL, G_TYPE_OBJECT)
133 void
134 gnm_command_finalize (GObject *obj)
136 GnmCommand *cmd = GNM_COMMAND (obj);
137 GObjectClass *parent;
139 /* The const was to avoid accidental changes elsewhere */
140 g_free ((gchar *)cmd->cmd_descriptor);
141 cmd->cmd_descriptor = NULL;
143 parent = g_type_class_peek (g_type_parent(G_TYPE_FROM_INSTANCE (obj)));
144 (*parent->finalize) (obj);
147 /******************************************************************/
149 GString *
150 gnm_cmd_trunc_descriptor (GString *src, gboolean *truncated)
152 int max_len = gnm_conf_get_undo_max_descriptor_width ();
153 glong len;
154 char *pos;
156 if (max_len < 5)
157 max_len = 5;
159 while ((pos = strchr(src->str, '\n')) != NULL ||
160 (pos = strchr(src->str, '\r')) != NULL)
161 *pos = ' ';
163 len = g_utf8_strlen (src->str, -1);
165 if (truncated)
166 *truncated = (len > max_len);
168 if (len > max_len) {
169 gchar* last = g_utf8_offset_to_pointer (src->str,
170 max_len - 1);
171 g_string_truncate (src, last - src->str);
172 g_string_append (src, UNICODE_ELLIPSIS);
174 return src;
179 * cmd_cell_range_is_locked_effective:
180 * @sheet: #Sheet
181 * @range: #GnmRange
182 * @wbc: #WorkbookControl
183 * @cmd_name: the command name.
185 * checks whether the cells are effectively locked
187 * static gboolean cmd_cell_range_is_locked_effective
190 * Do not use this function unless the sheet is part of the
191 * workbook with the given wbc (otherwise the results may be strange)
193 gboolean
194 cmd_cell_range_is_locked_effective (Sheet *sheet, GnmRange *range,
195 WorkbookControl *wbc, char const *cmd_name)
197 int i, j;
198 WorkbookView *wbv = wb_control_view (wbc);
200 if (wbv->is_protected || sheet->is_protected)
201 for (i = range->start.row; i <= range->end.row; i++)
202 for (j = range->start.col; j <= range->end.col; j++)
203 if (gnm_style_get_contents_locked (sheet_style_get (sheet, j, i))) {
204 char *r = global_range_name (sheet, range);
205 char *text = g_strdup_printf (wbv->is_protected ?
206 _("%s is locked. Unprotect the workbook to enable editing.") :
207 _("%s is locked. Unprotect the sheet to enable editing."),
209 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
210 cmd_name, text);
211 g_free (text);
212 g_free (r);
213 return TRUE;
215 return FALSE;
219 * checks whether the cells are effectively locked
221 * static gboolean cmd_dao_is_locked_effective
224 * Do not use this function unless the sheet is part of the
225 * workbook with the given wbcg (otherwise the results may be strange)
229 static gboolean
230 cmd_dao_is_locked_effective (data_analysis_output_t *dao,
231 WorkbookControl *wbc, char const *cmd_name)
233 GnmRange range;
234 range_init (&range, dao->start_col, dao->start_row,
235 dao->start_col + dao->cols - 1, dao->start_row + dao->rows - 1);
236 return (dao->type != NewWorkbookOutput &&
237 cmd_cell_range_is_locked_effective (dao->sheet, &range, wbc, cmd_name));
241 * cmd_selection_is_locked_effective: (skip)
242 * checks whether the selection is effectively locked
244 * static gboolean cmd_selection_is_locked_effective
247 * Do not use this function unless the sheet is part of the
248 * workbook with the given wbcg (otherwise the results may be strange)
251 gboolean
252 cmd_selection_is_locked_effective (Sheet *sheet, GSList *selection,
253 WorkbookControl *wbc, char const *cmd_name)
255 for (; selection; selection = selection->next) {
256 GnmRange *range = selection->data;
257 if (cmd_cell_range_is_locked_effective (sheet, range, wbc, cmd_name))
258 return TRUE;
260 return FALSE;
264 * A helper routine to select a range and make sure the top-left
265 * is visible.
267 static void
268 select_range (Sheet *sheet, const GnmRange *r, WorkbookControl *wbc)
270 SheetView *sv;
272 if (sheet->workbook != wb_control_get_workbook (wbc)) {
274 * We could try to pick a random wbc for the sheet's
275 * workbook. But not right now.
277 return;
280 wb_control_sheet_focus (wbc, sheet);
281 sv = sheet_get_view (sheet, wb_control_view (wbc));
282 sv_selection_reset (sv);
283 sv_selection_add_range (sv, r);
284 gnm_sheet_view_make_cell_visible (sv, r->start.col, r->start.row, FALSE);
288 * A helper routine to select a list of ranges and make sure the top-left
289 * corner of the last is visible.
291 static void
292 select_selection (Sheet *sheet, GSList *selection, WorkbookControl *wbc)
294 SheetView *sv = sheet_get_view (sheet, wb_control_view (wbc));
295 const GnmRange *r0 = NULL;
296 GSList *l;
298 g_return_if_fail (selection != NULL);
300 wb_control_sheet_focus (wbc, sheet);
301 sv_selection_reset (sv);
302 for (l = selection; l; l = l->next) {
303 GnmRange const *r = l->data;
304 sv_selection_add_range (sv, r);
305 r0 = r;
307 gnm_sheet_view_make_cell_visible (sv, r0->start.col, r0->start.row, FALSE);
311 * get_menu_label:
312 * with a list of commands.
313 * @cmd_list: The command list to check.
315 * Utility routine to get the descriptor associated
316 * Returns : A static reference to a descriptor. DO NOT free this.
318 static char const *
319 get_menu_label (GSList *cmd_list)
321 if (cmd_list != NULL) {
322 GnmCommand *cmd = GNM_COMMAND (cmd_list->data);
323 return cmd->cmd_descriptor;
326 return NULL;
330 * undo_redo_menu_labels:
331 * @wb: The book whose undo/redo queues we are modifying
333 * Another utility to set the menus correctly.
335 static void
336 undo_redo_menu_labels (Workbook *wb)
338 char const *undo_label = get_menu_label (wb->undo_commands);
339 char const *redo_label = get_menu_label (wb->redo_commands);
341 WORKBOOK_FOREACH_CONTROL (wb, view, control,
342 wb_control_undo_redo_labels (control, undo_label, redo_label);
346 static void
347 update_after_action (Sheet *sheet, WorkbookControl *wbc)
349 gnm_app_recalc ();
351 if (sheet != NULL) {
352 g_return_if_fail (IS_SHEET (sheet));
354 sheet_mark_dirty (sheet);
355 sheet_update (sheet);
357 if (sheet->workbook == wb_control_get_workbook (wbc))
358 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
359 wb_control_sheet_focus (control, sheet););
360 } else if (wbc != NULL) {
361 Sheet *sheet = wb_control_cur_sheet (wbc);
362 if (sheet)
363 sheet_update (sheet);
369 * command_undo:
370 * @wbc: The workbook control which issued the request.
371 * Any user level errors generated by undoing will be reported
372 * here.
374 * Undo the last command executed.
376 void
377 command_undo (WorkbookControl *wbc)
379 GnmCommand *cmd;
380 GnmCommandClass *klass;
381 Workbook *wb = wb_control_get_workbook (wbc);
383 g_return_if_fail (wb != NULL);
384 g_return_if_fail (wb->undo_commands != NULL);
386 cmd = GNM_COMMAND (wb->undo_commands->data);
387 g_return_if_fail (cmd != NULL);
389 klass = CMD_CLASS (cmd);
390 g_return_if_fail (klass != NULL);
392 g_object_ref (cmd);
394 /* TRUE indicates a failure to undo. Leave the command where it is */
395 if (!klass->undo_cmd (cmd, wbc)) {
396 gboolean undo_cleared;
398 update_after_action (cmd->sheet, wbc);
400 if (!cmd->workbook_modified_before_do)
401 go_doc_set_dirty (GO_DOC (wb), FALSE);
404 * A few commands clear the undo queue. For those, we do not
405 * want to stuff the cmd object on the redo queue.
407 undo_cleared = (wb->undo_commands == NULL);
409 if (!undo_cleared) {
410 wb->undo_commands = g_slist_remove (wb->undo_commands, cmd);
411 wb->redo_commands = g_slist_prepend (wb->redo_commands, cmd);
413 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
414 wb_control_undo_redo_pop (control, TRUE);
415 wb_control_undo_redo_push (control, FALSE, cmd->cmd_descriptor, cmd);
417 undo_redo_menu_labels (wb);
418 /* TODO : Should we mark the workbook as clean or pristine too */
422 g_object_unref (cmd);
426 * command_redo:
427 * @wbc: The workbook control which issued the request.
429 * Redo the last command that was undone.
430 * Any user level errors generated by redoing will be reported
431 * here.
433 void
434 command_redo (WorkbookControl *wbc)
436 GnmCommand *cmd;
437 GnmCommandClass *klass;
438 Workbook *wb = wb_control_get_workbook (wbc);
440 g_return_if_fail (wb);
441 g_return_if_fail (wb->redo_commands);
443 cmd = GNM_COMMAND (wb->redo_commands->data);
444 g_return_if_fail (cmd != NULL);
446 klass = CMD_CLASS (cmd);
447 g_return_if_fail (klass != NULL);
449 g_object_ref (cmd);
451 cmd->workbook_modified_before_do =
452 go_doc_is_dirty (wb_control_get_doc (wbc));
454 /* TRUE indicates a failure to redo. Leave the command where it is */
455 if (!klass->redo_cmd (cmd, wbc)) {
456 gboolean redo_cleared;
458 update_after_action (cmd->sheet, wbc);
461 * A few commands clear the undo queue. For those, we do not
462 * want to stuff the cmd object on the redo queue.
464 redo_cleared = (wb->redo_commands == NULL);
466 if (!redo_cleared) {
467 wb->redo_commands = g_slist_remove (wb->redo_commands, cmd);
468 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
470 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
471 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
472 wb_control_undo_redo_pop (control, FALSE);
474 undo_redo_menu_labels (wb);
478 g_object_unref (cmd);
482 * command_repeat:
483 * @wbc: The workbook control which issued the request.
485 * Repeat the last command (if possible)
487 * Any user level errors generated by redoing will be reported
488 * here.
490 void
491 command_repeat (WorkbookControl *wbc)
493 GnmCommand *cmd;
494 GnmCommandClass *klass;
495 Workbook *wb = wb_control_get_workbook (wbc);
497 g_return_if_fail (wb);
498 g_return_if_fail (wb->undo_commands);
500 cmd = GNM_COMMAND (wb->undo_commands->data);
501 g_return_if_fail (cmd != NULL);
503 klass = CMD_CLASS (cmd);
504 g_return_if_fail (klass != NULL);
506 if (klass->repeat_cmd != NULL)
507 (*klass->repeat_cmd) (cmd, wbc);
511 * command_setup_combos:
512 * @wbc:
514 * Initialize the combos to correspond to the current undo/redo state.
516 void
517 command_setup_combos (WorkbookControl *wbc)
519 char const *undo_label = NULL, *redo_label = NULL;
520 GSList *ptr, *tmp;
521 Workbook *wb = wb_control_get_workbook (wbc);
523 g_return_if_fail (wb);
525 wb_control_undo_redo_truncate (wbc, 0, TRUE);
526 tmp = g_slist_reverse (wb->undo_commands);
527 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
528 undo_label = get_menu_label (ptr);
529 wb_control_undo_redo_push (wbc, TRUE, undo_label, ptr->data);
531 if (g_slist_reverse (tmp)) {} /* ignore, list is in undo_commands */
533 wb_control_undo_redo_truncate (wbc, 0, FALSE);
534 tmp = g_slist_reverse (wb->redo_commands);
535 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
536 redo_label = get_menu_label (ptr);
537 wb_control_undo_redo_push (wbc, FALSE, redo_label, ptr->data);
539 if (g_slist_reverse (tmp)) {} /* ignore, list is in redo_commands */
541 /* update the menus too */
542 wb_control_undo_redo_labels (wbc, undo_label, redo_label);
546 * command_list_release:
547 * @cmds: (element-type GObject): the set of commands to free.
549 * command_list_release : utility routine to free the resources associated
550 * with a list of commands.
552 * NOTE : remember to NULL the list when you are done.
554 void
555 command_list_release (GSList *cmd_list)
557 while (cmd_list != NULL) {
558 GObject *cmd = G_OBJECT (cmd_list->data);
560 g_return_if_fail (cmd != NULL);
562 g_object_unref (cmd);
563 cmd_list = g_slist_remove (cmd_list, cmd_list->data);
568 * Each undo item has a certain size. The size of typing a value into
569 * a cell is the unit size. A large autoformat could have a size of
570 * hundreds or even thousands.
572 * We wish to have the same undo behaviour across platforms, so please
573 * don't use sizeof in computing the undo size.
576 #undef DEBUG_TRUNCATE_UNDO
579 * Truncate the undo list if it is too big.
581 * Returns -1 if no truncation was done, or else the number of elements
582 * left.
584 static int
585 truncate_undo_info (Workbook *wb)
587 int size_left;
588 int max_num;
589 int ok_count;
590 GSList *l, *prev;
592 size_left = gnm_conf_get_undo_size ();
593 max_num = gnm_conf_get_undo_maxnum ();
595 #ifdef DEBUG_TRUNCATE_UNDO
596 g_printerr ("Undo sizes:");
597 #endif
599 for (l = wb->undo_commands, prev = NULL, ok_count = 0;
601 prev = l, l = l->next, ok_count++) {
602 int min_leave;
603 GnmCommand *cmd = GNM_COMMAND (l->data);
604 int size = cmd->size;
606 if (size < 1) {
608 * We could g_assert, but that would cause data loss.
609 * Instead, just continue.
611 g_warning ("Faulty undo_size_func, please report.");
612 size = 1;
615 #ifdef DEBUG_TRUNCATE_UNDO
616 g_printerr (" %d", size);
617 #endif
619 /* Keep at least one undo item. */
620 if (ok_count >= max_num || (size > size_left && ok_count >= 1)) {
621 /* Current item is too big; truncate list here. */
622 command_list_release (l);
623 if (prev)
624 prev->next = NULL;
625 else
626 wb->undo_commands = NULL;
627 #ifdef DEBUG_TRUNCATE_UNDO
628 g_printerr ("[trunc]\n");
629 #endif
630 return ok_count;
634 * In order to allow a series of useful small items behind
635 * a big item, leave at least 10% of current item's size.
637 min_leave = size / 10;
638 size_left = MAX (size_left - size, min_leave);
641 #ifdef DEBUG_TRUNCATE_UNDO
642 g_printerr ("\n");
643 #endif
644 return -1;
649 * command_register_undo:
650 * @wbc: The workbook control that issued the command.
651 * @cmd: The new command to add.
653 * An internal utility to tack a new command
654 * onto the undo list.
656 static void
657 command_register_undo (WorkbookControl *wbc, GObject *obj)
659 Workbook *wb;
660 GnmCommand *cmd;
661 int undo_trunc;
663 g_return_if_fail (wbc != NULL);
664 wb = wb_control_get_workbook (wbc);
666 cmd = GNM_COMMAND (obj);
667 g_return_if_fail (cmd != NULL);
669 command_list_release (wb->redo_commands);
670 wb->redo_commands = NULL;
672 g_object_ref (obj); /* keep a ref in case it gets truncated away */
673 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
674 undo_trunc = truncate_undo_info (wb);
676 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
677 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
678 if (undo_trunc >= 0)
679 wb_control_undo_redo_truncate (control, undo_trunc, TRUE);
680 wb_control_undo_redo_truncate (control, 0, FALSE);
682 undo_redo_menu_labels (wb);
683 g_object_unref (obj);
688 * gnm_command_push_undo:
689 * @wbc: The workbook control that issued the command.
690 * @obj: The new command to add.
692 * An internal utility to tack a new command
693 * onto the undo list.
695 * returns : TRUE if there was an error.
697 gboolean
698 gnm_command_push_undo (WorkbookControl *wbc, GObject *obj)
700 gboolean trouble;
701 GnmCommand *cmd;
702 GnmCommandClass *klass;
704 g_return_val_if_fail (wbc != NULL, TRUE);
706 cmd = GNM_COMMAND (obj);
707 cmd->workbook_modified_before_do =
708 go_doc_is_dirty (wb_control_get_doc (wbc));
710 g_return_val_if_fail (cmd != NULL, TRUE);
712 klass = CMD_CLASS (cmd);
713 g_return_val_if_fail (klass != NULL, TRUE);
715 /* TRUE indicates a failure to do the command */
716 trouble = klass->redo_cmd (cmd, wbc);
717 update_after_action (cmd->sheet, wbc);
719 if (!trouble)
720 command_register_undo (wbc, obj);
721 else
722 g_object_unref (obj);
724 return trouble;
728 * command_undo_sheet_delete deletes the sheet without deleting the current cmd.
729 * returns true if is indeed deleted the sheet.
730 * Note: only call this for a sheet of your current workbook from the undo procedure
733 static gboolean
734 command_undo_sheet_delete (Sheet* sheet)
736 Workbook *wb = sheet->workbook;
738 g_return_val_if_fail (IS_SHEET (sheet), FALSE);
740 if (wb->redo_commands != NULL) {
741 command_list_release (wb->redo_commands);
742 wb->redo_commands = NULL;
743 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
744 wb_control_undo_redo_truncate (ctl, 0, FALSE););
745 undo_redo_menu_labels (wb);
748 workbook_sheet_delete (sheet);
750 return (TRUE);
753 /******************************************************************/
755 static GnmValue *
756 cmd_set_text_full_check_texpr (GnmCellIter const *iter, GnmExprTop const *texpr)
758 if (iter->cell == NULL ||
759 !gnm_expr_top_equal (iter->cell->base.texpr, texpr))
760 return VALUE_TERMINATE;
761 return NULL;
764 static GnmValue *
765 cmd_set_text_full_check_text (GnmCellIter const *iter, char *text)
767 char *old_text;
768 gboolean same;
769 gboolean quoted = FALSE;
771 if (gnm_cell_is_blank (iter->cell))
772 return ((text == NULL || text[0] == '\0') ? NULL : VALUE_TERMINATE);
774 if (text == NULL || text[0] == '\0')
775 return VALUE_TERMINATE;
777 old_text = gnm_cell_get_text_for_editing (iter->cell, NULL, &quoted);
778 same = g_strcmp0 (old_text, text) == 0;
780 if (!same && !quoted && iter->cell->value && VALUE_IS_STRING (iter->cell->value)
781 && text[0] == '\'')
782 same = g_strcmp0 (old_text, text + 1) == 0;
784 g_free (old_text);
786 return (same ? NULL : VALUE_TERMINATE);
789 static GnmValue *
790 cmd_set_text_full_check_markup (GnmCellIter const *iter, PangoAttrList *markup)
792 PangoAttrList const *old_markup = NULL;
793 gboolean same_markup;
795 g_return_val_if_fail (iter->cell != NULL, NULL);
797 if (iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
798 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
799 if (fmt && go_format_is_markup (fmt)) {
800 old_markup = go_format_get_markup (fmt);
801 if (go_pango_attr_list_is_empty (old_markup))
802 old_markup = NULL;
806 same_markup = gnm_pango_attr_list_equal (old_markup, markup);
808 return same_markup ? NULL : VALUE_TERMINATE;
812 * cmd_set_text_full
814 * the caller is expected to have ensured:
816 * 1) that no array is being split
817 * 2) that the range is not locked.
819 * Note:
820 * We will free the selection but nothing else.
824 static gboolean
825 cmd_set_text_full (WorkbookControl *wbc, GSList *selection, GnmEvalPos *ep,
826 char const *new_text, PangoAttrList *markup,
827 gboolean autocorrect)
829 GSList *l;
830 char const *expr_txt;
831 GnmExprTop const *texpr = NULL;
832 GOUndo *undo = NULL;
833 GOUndo *redo = NULL;
834 gboolean result, autofit_col = FALSE, same_text_and_not_same_markup = FALSE;
835 char *text = NULL;
836 char *name;
837 Sheet *sheet = ep->sheet;
838 GnmParsePos pp;
839 ColRowIndexList *cri_col_list = NULL, *cri_row_list = NULL;
840 GOFormat const *format = gnm_style_get_format
841 (sheet_style_get (sheet, ep->eval.col, ep->eval.row));
843 g_return_val_if_fail (selection != NULL , TRUE);
845 parse_pos_init_evalpos (&pp, ep);
846 name = undo_range_list_name (sheet, selection);
848 if ((format == NULL) || !go_format_is_text (format)) {
849 expr_txt = gnm_expr_char_start_p (new_text);
850 if (expr_txt != NULL)
851 texpr = gnm_expr_parse_str
852 (expr_txt, &pp, GNM_EXPR_PARSE_DEFAULT,
853 sheet_get_conventions (sheet), NULL);
856 if (texpr != NULL) {
857 GOFormat const *sf;
858 GnmStyle *new_style = NULL;
859 gboolean same_texpr = TRUE;
861 /* We should check whether we are in fact changing anything: */
862 for (l = selection; l != NULL && same_texpr; l = l->next) {
863 GnmRange *r = l->data;
864 GnmValue *val =
865 sheet_foreach_cell_in_range
866 (sheet, CELL_ITER_ALL, r,
867 (CellIterFunc) cmd_set_text_full_check_texpr,
868 (gpointer) texpr);
870 same_texpr = (val != VALUE_TERMINATE);
871 if (val != NULL && same_texpr)
872 value_release (val);
875 if (same_texpr) {
876 gnm_expr_top_unref (texpr);
877 g_free (name);
878 range_fragment_free (selection);
879 return TRUE;
882 text = g_strdup_printf (_("Inserting expression in %s"), name);
884 if (go_format_is_general (format)) {
885 sf = gnm_auto_style_format_suggest (texpr, ep);
886 if (sf != NULL) {
887 new_style = gnm_style_new ();
888 gnm_style_set_format (new_style, sf);
889 go_format_unref (sf);
893 for (l = selection; l != NULL; l = l->next) {
894 GnmSheetRange *sr;
895 undo = go_undo_combine
896 (undo, clipboard_copy_range_undo (sheet, l->data));
897 sr = gnm_sheet_range_new (sheet, l->data);
898 redo = go_undo_combine
899 (redo, sheet_range_set_expr_undo (sr, texpr));
900 if (new_style) {
901 sr = gnm_sheet_range_new (sheet, l->data);
902 redo = go_undo_combine
903 (redo, sheet_apply_style_undo (sr, new_style));
906 if (new_style)
907 gnm_style_unref (new_style);
908 gnm_expr_top_unref (texpr);
909 autofit_col = TRUE;
910 } else {
911 GString *text_str;
912 PangoAttrList *adj_markup = NULL;
913 char *corrected;
914 gboolean same_text = TRUE;
915 gboolean same_markup = TRUE;
917 if (new_text == NULL)
918 corrected = NULL;
919 else if (autocorrect)
920 corrected = autocorrect_tool (new_text);
921 else
922 corrected = g_strdup (new_text);
924 if (corrected && (corrected[0] == '\'') && corrected[1] == '\0') {
925 g_free (corrected);
926 corrected = g_strdup ("");
929 /* We should check whether we are in fact changing anything: */
930 /* We'll handle */
931 for (l = selection; l != NULL && same_text; l = l->next) {
932 GnmRange *r = l->data;
933 GnmValue *val =
934 sheet_foreach_cell_in_range
935 (sheet, CELL_ITER_ALL, r,
936 (CellIterFunc) cmd_set_text_full_check_text,
937 (gpointer) corrected);
939 same_text = (val != VALUE_TERMINATE);
940 if (val != NULL && same_text)
941 value_release (val);
944 if (go_pango_attr_list_is_empty (markup))
945 markup = NULL;
946 if (markup && corrected && corrected[0] == '\'') {
947 markup = adj_markup = pango_attr_list_copy (markup);
948 go_pango_attr_list_erase (adj_markup, 0, 1);
951 if (same_text) {
952 for (l = selection; l != NULL && same_text; l = l->next) {
953 GnmRange *r = l->data;
954 GnmValue *val =
955 sheet_foreach_cell_in_range
956 (sheet, CELL_ITER_IGNORE_BLANK, r,
957 (CellIterFunc) cmd_set_text_full_check_markup,
958 (gpointer) markup);
960 same_markup = (val != VALUE_TERMINATE);
961 if (val != NULL && same_markup)
962 value_release (val);
965 if (same_markup) {
966 g_free (corrected);
967 g_free (name);
968 range_fragment_free (selection);
969 if (adj_markup)
970 pango_attr_list_unref (adj_markup);
971 return TRUE;
974 text = g_strdup_printf (_("Editing style of %s"), name);
975 } else {
976 text_str = gnm_cmd_trunc_descriptor (g_string_new (corrected), NULL);
977 text = g_strdup_printf (_("Typing \"%s\" in %s"), text_str->str, name);
978 g_string_free (text_str, TRUE);
981 for (l = selection; l != NULL; l = l->next) {
982 GnmSheetRange *sr;
983 undo = go_undo_combine
984 (undo, clipboard_copy_range_undo (sheet, l->data));
985 if (corrected) {
986 sr = gnm_sheet_range_new (sheet, l->data);
987 redo = go_undo_combine
988 (redo, sheet_range_set_text_undo
989 (sr, corrected));
991 if (markup) {
992 sr = gnm_sheet_range_new (sheet, l->data);
993 /* Note: order of combination matters!! */
994 redo = go_undo_combine
995 (sheet_range_set_markup_undo (sr, markup), redo);
999 if (adj_markup)
1000 pango_attr_list_unref (adj_markup);
1001 g_free (corrected);
1003 same_text_and_not_same_markup = (same_text && !same_markup);
1005 g_free (name);
1007 /* We are combining this since we don't want to apply and undo twice.*/
1008 if (same_text_and_not_same_markup || !autofit_col) {
1009 GnmCell *cell = sheet_cell_fetch
1010 (sheet, ep->eval.col, ep->eval.row);
1011 gboolean nvis;
1013 go_undo_undo (redo);
1014 nvis = !VALUE_IS_STRING (cell->value);
1015 if (!autofit_col)
1016 autofit_col = nvis;
1017 if (same_text_and_not_same_markup)
1018 /* We only have to do something if at least one cell */
1019 /* now contains a string, but they contain all the same thing. */
1020 same_text_and_not_same_markup = nvis;
1021 go_undo_undo (undo);
1023 if (same_text_and_not_same_markup) {
1024 /*We had the same text and different markup but we are not entering strings. */
1025 g_object_unref (undo);
1026 g_object_unref (redo);
1027 g_free (text);
1028 range_fragment_free (selection);
1029 return TRUE;
1031 for (l = selection; l != NULL; l = l->next) {
1032 GnmRange *r = l->data;
1033 GnmRange *new_r;
1035 new_r = g_new (GnmRange, 1);
1036 *new_r = *r;
1037 redo = go_undo_combine
1038 (go_undo_binary_new
1039 (sheet, new_r,
1040 (GOUndoBinaryFunc) colrow_autofit_row,
1041 NULL, g_free),
1042 redo);
1043 cri_row_list = colrow_get_index_list
1044 (r->start.row, r->end.row, cri_row_list);
1046 if (autofit_col) {
1047 new_r = g_new (GnmRange, 1);
1048 *new_r = *r;
1049 redo = go_undo_combine
1050 (go_undo_binary_new
1051 (sheet, new_r,
1052 (GOUndoBinaryFunc) colrow_autofit_col,
1053 NULL, g_free),
1054 redo);
1055 cri_col_list = colrow_get_index_list
1056 (r->start.col, r->end.col, cri_col_list);
1060 undo = go_undo_combine (undo,
1061 gnm_undo_colrow_restore_state_group_new
1062 (sheet, TRUE,
1063 cri_col_list,
1064 colrow_get_sizes (sheet, TRUE,
1065 cri_col_list, -1)));
1066 undo = go_undo_combine (undo,
1067 gnm_undo_colrow_restore_state_group_new
1068 (sheet, FALSE,
1069 cri_row_list,
1070 colrow_get_sizes (sheet, FALSE,
1071 cri_row_list, -1)));
1073 result = cmd_generic (wbc, text, undo, redo);
1074 g_free (text);
1075 range_fragment_free (selection);
1076 return result;
1080 * cmd_area_set_text
1082 * the caller is expected to have ensured:
1084 * 1) that no array is being split
1085 * 2) that the range is not locked.
1089 gboolean
1090 cmd_area_set_text (WorkbookControl *wbc, SheetView *sv,
1091 char const *new_text, PangoAttrList *markup)
1093 GnmEvalPos ep;
1094 gboolean result;
1095 GSList *selection = selection_get_ranges (sv, FALSE);
1097 eval_pos_init_editpos (&ep, sv);
1098 result = cmd_set_text_full (wbc, selection, &ep,
1099 new_text, markup, TRUE);
1100 return result;
1103 gboolean
1104 cmd_set_text (WorkbookControl *wbc,
1105 Sheet *sheet, GnmCellPos const *pos,
1106 char const *new_text,
1107 PangoAttrList *markup,
1108 gboolean autocorrect)
1110 GnmCell const *cell;
1111 GnmEvalPos ep;
1112 gboolean result;
1113 GSList *selection;
1114 GnmRange *r;
1116 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1117 g_return_val_if_fail (new_text != NULL, TRUE);
1119 /* Ensure that we are not splitting up an array */
1120 cell = sheet_cell_get (sheet, pos->col, pos->row);
1121 if (gnm_cell_is_nonsingleton_array (cell)) {
1122 gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc),
1123 _("Set Text"), NULL);
1124 return TRUE;
1127 eval_pos_init_pos (&ep, sheet, pos);
1128 r = g_new (GnmRange, 1);
1129 r->start = r->end = *pos;
1130 selection = g_slist_prepend (NULL, r);
1131 result = cmd_set_text_full (wbc, selection, &ep,
1132 new_text, markup, autocorrect);
1133 return result;
1138 * cmd_area_set_array_expr
1140 * the caller is expected to have ensured:
1142 * 1) that no array is being split
1143 * 2) that the selection consists of a single range
1144 * 3) that the range is not locked.
1148 gboolean
1149 cmd_area_set_array_expr (WorkbookControl *wbc, SheetView *sv,
1150 GnmExprTop const *texpr)
1152 GSList *selection = selection_get_ranges (sv, FALSE);
1153 GOUndo *undo = NULL;
1154 GOUndo *redo = NULL;
1155 gboolean result;
1156 Sheet *sheet = sv_sheet (sv);
1157 char *name;
1158 char *text;
1159 GnmSheetRange *sr;
1160 GnmRange *r;
1162 g_return_val_if_fail (selection != NULL , TRUE);
1163 g_return_val_if_fail (selection->next == NULL , TRUE);
1165 name = undo_range_list_name (sheet, selection);
1166 text = g_strdup_printf (_("Inserting array expression in %s"), name);
1167 g_free (name);
1169 r = selection->data;
1171 undo = clipboard_copy_range_undo (sheet, selection->data);
1173 sr = gnm_sheet_range_new (sheet, r);
1174 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1175 redo = go_undo_combine
1176 (go_undo_binary_new
1177 (sheet, g_memdup (r, sizeof (*r)),
1178 (GOUndoBinaryFunc) colrow_autofit_col,
1179 NULL, g_free),
1180 redo);
1181 redo = go_undo_combine
1182 (go_undo_binary_new
1183 (sheet, g_memdup (r, sizeof (*r)),
1184 (GOUndoBinaryFunc) colrow_autofit_row,
1185 NULL, g_free),
1186 redo);
1188 range_fragment_free (selection);
1189 result = cmd_generic (wbc, text, undo, redo);
1190 g_free (text);
1191 return result;
1195 * cmd_create_data_table
1197 * the caller is expected to have ensured:
1199 * 1) that no array is being split
1200 * 2) that the range is not locked.
1203 gboolean
1204 cmd_create_data_table (WorkbookControl *wbc, Sheet *sheet, GnmRange const *r,
1205 char const *col_input, char const *row_input)
1207 GOUndo *undo = NULL;
1208 GOUndo *redo = NULL;
1209 gboolean result;
1210 char *name;
1211 char *text;
1212 GnmSheetRange *sr;
1213 GnmParsePos pp;
1214 GnmExprTop const *texpr;
1216 name = undo_range_name (sheet, r);
1217 text = g_strdup_printf (_("Creating a Data Table in %s"), name);
1218 g_free (name);
1220 undo = clipboard_copy_range_undo (sheet, r);
1222 sr = gnm_sheet_range_new (sheet, r);
1223 parse_pos_init (&pp, NULL, sheet, r->start.col, r->start.row);
1224 name = g_strdup_printf ("TABLE(%s,%s)", row_input, col_input);
1225 texpr = gnm_expr_parse_str
1226 (name, &pp, GNM_EXPR_PARSE_DEFAULT,
1227 sheet_get_conventions (sheet), NULL);
1228 g_free (name);
1230 if (texpr == NULL) {
1231 g_object_unref (undo);
1232 g_free (text);
1233 return TRUE;
1236 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1237 gnm_expr_top_unref (texpr);
1239 result = cmd_generic (wbc, text, undo, redo);
1240 g_free (text);
1241 return result;
1244 /******************************************************************/
1246 #define CMD_INS_DEL_COLROW_TYPE (cmd_ins_del_colrow_get_type ())
1247 #define CMD_INS_DEL_COLROW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_INS_DEL_COLROW_TYPE, CmdInsDelColRow))
1249 typedef struct {
1250 GnmCommand cmd;
1252 Sheet *sheet;
1253 gboolean is_insert;
1254 gboolean is_cols;
1255 gboolean is_cut;
1256 int index;
1257 int count;
1258 GnmRange *cutcopied;
1259 SheetView *cut_copy_view;
1261 gboolean (*redo_action) (Sheet *sheet, int col, int count,
1262 GOUndo **pundo, GOCmdContext *cc);
1264 gboolean (*repeat_action) (WorkbookControl *wbc, Sheet *sheet,
1265 int start, int count);
1267 GOUndo *undo;
1268 } CmdInsDelColRow;
1270 static void
1271 cmd_ins_del_colrow_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1273 CmdInsDelColRow const *orig = (CmdInsDelColRow const *) cmd;
1274 SheetView *sv = wb_control_cur_sheet_view (wbc);
1275 Sheet *sheet = sv_sheet (sv);
1276 GnmRange const *r = selection_first_range (sv,
1277 GO_CMD_CONTEXT (wbc), _("Ins/Del Column/Row"));
1278 int start, count;
1280 if (r == NULL)
1281 return;
1283 if (orig->is_cols)
1284 start = r->start.col, count = range_width (r);
1285 else
1286 start = r->start.row, count = range_height (r);
1288 orig->repeat_action (wbc, sheet, start, count);
1291 MAKE_GNM_COMMAND (CmdInsDelColRow, cmd_ins_del_colrow, cmd_ins_del_colrow_repeat)
1293 static gboolean
1294 cmd_ins_del_colrow_undo (GnmCommand *cmd, WorkbookControl *wbc)
1296 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1298 if (me->undo) {
1299 go_undo_undo (me->undo);
1300 g_object_unref (me->undo);
1301 me->undo = NULL;
1304 /* Ins/Del Row/Col re-ants things completely to account
1305 * for the shift of col/rows.
1307 if (me->cutcopied != NULL && me->cut_copy_view != NULL)
1308 gnm_app_clipboard_cut_copy (wbc, me->is_cut, me->cut_copy_view,
1309 me->cutcopied, FALSE);
1311 return FALSE;
1314 static gboolean
1315 cmd_ins_del_colrow_redo (GnmCommand *cmd, WorkbookControl *wbc)
1317 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1318 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
1319 int idx = me->index;
1320 int count = me->count;
1322 if (me->redo_action (me->sheet, idx, count, &me->undo, cc)) {
1323 /* Trouble. */
1324 return TRUE;
1327 /* Ins/Del Row/Col re-ants things completely to account
1328 * for the shift of col/rows. */
1329 if (me->cutcopied != NULL && me->cut_copy_view != NULL) {
1330 if (me->is_cut) {
1331 GnmRange s = *me->cutcopied;
1332 int key = me->is_insert ? count : -count;
1333 int threshold = me->is_insert ? idx : idx + 1;
1336 * Really only applies if the regions that are
1337 * inserted/deleted are above the cut/copied region.
1339 if (me->is_cols) {
1340 if (threshold <= s.start.col) {
1341 s.start.col += key;
1342 s.end.col += key;
1344 } else if (threshold <= s.start.row) {
1345 s.start.row += key;
1346 s.end.row += key;
1349 gnm_app_clipboard_cut_copy (wbc, me->is_cut,
1350 me->cut_copy_view,
1351 &s, FALSE);
1352 } else
1353 gnm_app_clipboard_unant ();
1356 return FALSE;
1359 static void
1360 cmd_ins_del_colrow_finalize (GObject *cmd)
1362 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1364 if (me->undo)
1365 g_object_unref (me->undo);
1367 g_free (me->cutcopied);
1369 gnm_sheet_view_weak_unref (&(me->cut_copy_view));
1371 gnm_command_finalize (cmd);
1374 static gboolean
1375 cmd_ins_del_colrow (WorkbookControl *wbc,
1376 Sheet *sheet,
1377 gboolean is_cols, gboolean is_insert,
1378 char const *descriptor, int index, int count)
1380 CmdInsDelColRow *me;
1381 int first, last;
1382 GnmRange r;
1384 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1385 g_return_val_if_fail (count > 0, TRUE);
1387 me = g_object_new (CMD_INS_DEL_COLROW_TYPE, NULL);
1389 me->sheet = sheet;
1390 me->is_cols = is_cols;
1391 me->is_insert = is_insert;
1392 me->index = index;
1393 me->count = count;
1394 me->redo_action = me->is_insert
1395 ? (me->is_cols ? sheet_insert_cols : sheet_insert_rows)
1396 : (me->is_cols ? sheet_delete_cols : sheet_delete_rows);
1397 me->repeat_action = me->is_insert
1398 ? (me->is_cols ? cmd_insert_cols : cmd_insert_rows)
1399 : (me->is_cols ? cmd_delete_cols : cmd_delete_rows);
1401 /* Range that will get deleted. */
1402 first = me->is_insert
1403 ? colrow_max (is_cols, sheet) - count
1404 : index;
1405 last = first + count - 1;
1406 (is_cols ? range_init_cols : range_init_rows) (&r, sheet, first, last);
1408 /* Note: redo_action checks for array subdivision. */
1410 /* Check for locks */
1411 if (cmd_cell_range_is_locked_effective (sheet, &r, wbc, descriptor)) {
1412 g_object_unref (me);
1413 return TRUE;
1416 /* We store the cut or/copied range if applicable */
1417 if (!gnm_app_clipboard_is_empty () &&
1418 gnm_app_clipboard_area_get () &&
1419 sheet == gnm_app_clipboard_sheet_get ()) {
1420 me->cutcopied = gnm_range_dup (gnm_app_clipboard_area_get ());
1421 me->is_cut = gnm_app_clipboard_is_cut ();
1422 gnm_sheet_view_weak_ref (gnm_app_clipboard_sheet_view_get (),
1423 &(me->cut_copy_view));
1424 } else
1425 me->cutcopied = NULL;
1427 me->cmd.sheet = sheet;
1428 me->cmd.size = count * 10; /* FIXME? */
1429 me->cmd.cmd_descriptor = descriptor;
1431 return gnm_command_push_undo (wbc, G_OBJECT (me));
1434 gboolean
1435 cmd_insert_cols (WorkbookControl *wbc,
1436 Sheet *sheet, int start_col, int count)
1438 char *mesg;
1439 GnmRange r;
1441 range_init_full_sheet (&r, sheet);
1442 r.start.col = r.end.col - count + 1;
1444 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1445 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1446 ngettext ("Inserting %i column before column %s would push data off the sheet. "
1447 "Please enlarge the sheet first.",
1448 "Inserting %i columns before column %s would push data off the sheet. "
1449 "Please enlarge the sheet first.",
1450 count),
1451 count, col_name (start_col));
1452 return TRUE;
1455 mesg = g_strdup_printf
1456 (ngettext ("Inserting %d column before %s",
1457 "Inserting %d columns before %s",
1458 count),
1459 count, col_name (start_col));
1460 return cmd_ins_del_colrow (wbc, sheet, TRUE, TRUE, mesg, start_col, count);
1463 gboolean
1464 cmd_insert_rows (WorkbookControl *wbc,
1465 Sheet *sheet, int start_row, int count)
1467 char *mesg;
1468 GnmRange r;
1470 range_init_full_sheet (&r, sheet);
1471 r.start.row = r.end.row - count + 1;
1473 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1474 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1475 ngettext ("Inserting %i row before row %s would push data off the sheet. "
1476 "Please enlarge the sheet first.",
1477 "Inserting %i rows before row %s would push data off the sheet. "
1478 "Please enlarge the sheet first.",
1479 count),
1480 count, row_name (start_row));
1481 return TRUE;
1484 mesg = g_strdup_printf
1485 (ngettext ("Inserting %d row before %s",
1486 "Inserting %d rows before %s",
1487 count),
1488 count, row_name (start_row));
1489 return cmd_ins_del_colrow (wbc, sheet, FALSE, TRUE, mesg, start_row, count);
1492 gboolean
1493 cmd_delete_cols (WorkbookControl *wbc,
1494 Sheet *sheet, int start_col, int count)
1496 char *mesg = g_strdup_printf ((count > 1)
1497 ? _("Deleting columns %s")
1498 : _("Deleting column %s"),
1499 cols_name (start_col, start_col + count - 1));
1500 return cmd_ins_del_colrow (wbc, sheet, TRUE, FALSE, mesg, start_col, count);
1503 gboolean
1504 cmd_delete_rows (WorkbookControl *wbc,
1505 Sheet *sheet, int start_row, int count)
1507 char *mesg = g_strdup_printf ((count > 1)
1508 ? _("Deleting rows %s")
1509 : _("Deleting row %s"),
1510 rows_name (start_row, start_row + count - 1));
1511 return cmd_ins_del_colrow (wbc, sheet, FALSE, FALSE, mesg, start_row, count);
1514 /******************************************************************/
1516 typedef struct {
1517 GSList *selection;
1518 GnmRange const *r;
1519 } cmd_selection_clear_row_handler_t;
1521 static gboolean
1522 cmd_selection_clear_row_handler (GnmColRowIter const *iter,
1523 cmd_selection_clear_row_handler_t *data)
1525 if ((!iter->cri->in_filter) || iter->cri->visible) {
1526 GnmRange *r = gnm_range_dup (data->r);
1527 r->start.row = r->end.row = iter->pos;
1528 data->selection = g_slist_prepend (data->selection, r);
1530 return FALSE;
1533 gboolean
1534 cmd_selection_clear (WorkbookControl *wbc, int clear_flags)
1536 char *names, *descriptor;
1537 GString *types;
1538 SheetView *sv = wb_control_cur_sheet_view (wbc);
1539 GSList *selection = selection_get_ranges (sv, FALSE /* No intersection */);
1540 Sheet *sheet = sv_sheet (sv);
1541 gboolean result;
1542 int size;
1543 GOUndo *undo = NULL;
1544 GOUndo *redo = NULL;
1545 GSList *ranges;
1547 if ((clear_flags & CLEAR_FILTERED_ONLY) != 0 && sheet->filters != NULL) {
1548 /* We need to modify the selection to only include filtered rows. */
1549 cmd_selection_clear_row_handler_t data;
1550 data.selection = selection;
1551 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1552 GnmFilter *filter;
1553 data.r = ranges->data;
1554 filter = gnm_sheet_filter_intersect_rows
1555 (sheet, data.r->start.row, data.r->end.row);
1556 if (filter) {
1557 sheet_colrow_foreach (sheet, FALSE,
1558 data.r->start.row,
1559 data.r->end.row,
1560 (ColRowHandler) cmd_selection_clear_row_handler,
1561 &data);
1562 g_free (ranges->data);
1563 ranges->data = NULL;
1566 selection = g_slist_remove_all (data.selection, NULL);
1569 /* We should first determine whether we break anything by clearing */
1570 /* Check for array subdivision *//* Check for locked cells */
1571 if (sheet_ranges_split_region (sheet, selection,
1572 GO_CMD_CONTEXT (wbc), _("Clear")) ||
1573 cmd_selection_is_locked_effective (sheet, selection, wbc, _("Clear"))) {
1574 range_fragment_free (selection);
1575 return TRUE;
1579 /* We now need to build the descriptor */
1580 /* Collect clear types for descriptor */
1581 if (clear_flags != (CLEAR_VALUES | CLEAR_FORMATS | CLEAR_COMMENTS)) {
1582 GSList *m, *l = NULL;
1583 types = g_string_new (NULL);
1584 if (clear_flags & CLEAR_VALUES)
1585 l = g_slist_append (l, g_string_new (_("contents")));
1586 if (clear_flags & CLEAR_FORMATS)
1587 l = g_slist_append (l, g_string_new (_("formats")));
1588 if (clear_flags & CLEAR_COMMENTS)
1589 l = g_slist_append (l, g_string_new (_("comments")));
1590 /* Using a list for this may seem overkill, but is really the only
1591 * right way to do this
1593 for (m = l; m != NULL; m = m->next) {
1594 GString *s = m->data;
1596 g_string_append_len (types, s->str, s->len);
1597 g_string_free (s, TRUE);
1599 if (m->next)
1600 g_string_append (types, ", ");
1602 g_slist_free (l);
1603 } else
1604 types = g_string_new (_("all"));
1605 /* The range name string will automatically be truncated, we don't
1606 * need to truncate the "types" list because it will not grow
1607 * indefinitely
1609 names = undo_range_list_name (sheet, selection);
1610 descriptor = g_strdup_printf (_("Clearing %s in %s"), types->str, names);
1611 g_free (names);
1612 g_string_free (types, TRUE);
1613 size = g_slist_length (selection);
1615 clear_flags |= CLEAR_NOCHECKARRAY;
1617 if (clear_flags & (CLEAR_VALUES | CLEAR_FORMATS))
1618 clear_flags |= CLEAR_RECALC_DEPS;
1620 /* We are now ready to build the redo and undo items */
1621 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1622 GnmRange const *r = ranges->data;
1623 GnmSheetRange *sr = gnm_sheet_range_new (sheet, r);
1625 undo = go_undo_combine (undo, clipboard_copy_range_undo (sheet, r));
1626 redo = go_undo_combine
1627 (redo, sheet_clear_region_undo
1628 (sr, clear_flags));
1631 range_fragment_free (selection);
1633 result = cmd_generic_with_size (wbc, descriptor, size, undo, redo);
1634 g_free (descriptor);
1636 return result;
1639 /******************************************************************/
1641 #define CMD_FORMAT_TYPE (cmd_format_get_type ())
1642 #define CMD_FORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FORMAT_TYPE, CmdFormat))
1644 typedef struct {
1645 GnmCellPos pos;
1646 GnmStyleList *styles;
1647 ColRowIndexList *rows;
1648 ColRowStateGroup *old_heights;
1649 } CmdFormatOldStyle;
1651 typedef struct {
1652 GnmCommand cmd;
1653 GSList *selection;
1654 GSList *old_styles;
1655 GnmStyle *new_style;
1656 GnmBorder **borders;
1657 } CmdFormat;
1659 static void
1660 cmd_format_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1662 CmdFormat const *orig = (CmdFormat const *) cmd;
1663 int i;
1665 if (orig->new_style)
1666 gnm_style_ref (orig->new_style);
1667 if (orig->borders)
1668 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1669 gnm_style_border_ref (orig->borders [i]);
1671 cmd_selection_format (wbc, orig->new_style, orig->borders, NULL);
1673 MAKE_GNM_COMMAND (CmdFormat, cmd_format, cmd_format_repeat)
1675 static gboolean
1676 cmd_format_undo (GnmCommand *cmd,
1677 G_GNUC_UNUSED WorkbookControl *wbc)
1679 CmdFormat *me = CMD_FORMAT (cmd);
1681 g_return_val_if_fail (me != NULL, TRUE);
1683 if (me->old_styles) {
1684 GSList *rstyles = g_slist_reverse (g_slist_copy (me->old_styles));
1685 GSList *rsel = g_slist_reverse (g_slist_copy (me->selection));
1686 GSList *l1, *l2;
1688 for (l1 = rstyles, l2 = rsel; l1; l1 = l1->next, l2 = l2->next) {
1689 CmdFormatOldStyle *os = l1->data;
1690 GnmRange const *r = l2->data;
1691 GnmSpanCalcFlags flags = sheet_style_set_list
1692 (me->cmd.sheet,
1693 &os->pos, os->styles, NULL, NULL);
1695 if (os->old_heights) {
1696 colrow_restore_state_group (me->cmd.sheet, FALSE,
1697 os->rows,
1698 os->old_heights);
1699 colrow_state_group_destroy (os->old_heights);
1700 os->old_heights = NULL;
1701 colrow_index_list_destroy (os->rows);
1702 os->rows = NULL;
1705 sheet_range_calc_spans (me->cmd.sheet, r, flags);
1706 sheet_flag_style_update_range (me->cmd.sheet, r);
1709 sheet_redraw_all (me->cmd.sheet, FALSE);
1710 g_slist_free (rstyles);
1711 g_slist_free (rsel);
1714 select_selection (me->cmd.sheet, me->selection, wbc);
1716 return FALSE;
1719 static gboolean
1720 cmd_format_redo (GnmCommand *cmd, WorkbookControl *wbc)
1722 CmdFormat *me = CMD_FORMAT (cmd);
1723 GSList *l1, *l2;
1724 gboolean re_fit_height;
1726 g_return_val_if_fail (me != NULL, TRUE);
1728 /* Check for locked cells */
1729 if (cmd_selection_is_locked_effective (me->cmd.sheet, me->selection,
1730 wbc, _("Changing Format")))
1731 return TRUE;
1733 re_fit_height = me->new_style &&
1734 (GNM_SPANCALC_ROW_HEIGHT & gnm_style_required_spanflags (me->new_style));
1736 for (l1 = me->old_styles, l2 = me->selection; l2; l1 = l1->next, l2 = l2->next) {
1737 CmdFormatOldStyle *os = l1->data;
1738 GnmRange const *r = l2->data;
1740 if (me->borders)
1741 sheet_apply_border (me->cmd.sheet, r, me->borders);
1742 if (me->new_style) {
1743 gnm_style_ref (me->new_style);
1744 sheet_apply_style (me->cmd.sheet, r, me->new_style);
1745 if (re_fit_height)
1746 colrow_autofit (me->cmd.sheet, r, FALSE, FALSE,
1747 TRUE, FALSE,
1748 &os->rows, &os->old_heights);
1751 sheet_flag_style_update_range (me->cmd.sheet, r);
1753 sheet_redraw_all (me->cmd.sheet, FALSE);
1754 sheet_mark_dirty (me->cmd.sheet);
1756 select_selection (me->cmd.sheet, me->selection, wbc);
1758 return FALSE;
1761 static void
1762 cmd_format_finalize (GObject *cmd)
1764 CmdFormat *me = CMD_FORMAT (cmd);
1765 int i;
1767 if (me->new_style)
1768 gnm_style_unref (me->new_style);
1769 me->new_style = NULL;
1771 if (me->borders) {
1772 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1773 gnm_style_border_unref (me->borders [i]);
1774 g_free (me->borders);
1775 me->borders = NULL;
1778 if (me->old_styles != NULL) {
1779 GSList *l;
1781 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
1782 CmdFormatOldStyle *os = l->data;
1784 style_list_free (os->styles);
1785 colrow_index_list_destroy (os->rows);
1786 colrow_state_group_destroy (os->old_heights);
1787 g_free (os);
1789 me->old_styles = NULL;
1792 range_fragment_free (me->selection);
1793 me->selection = NULL;
1795 gnm_command_finalize (cmd);
1799 * cmd_format:
1800 * @wbc: the workbook control.
1801 * @sheet: the sheet
1802 * @style: style to apply to the selection
1803 * @borders: borders to apply to the selection
1804 * @opt_translated_name: An optional name to use in place of 'Format Cells'
1806 * If borders is non NULL, then the GnmBorder references are passed,
1807 * the GnmStyle reference is also passed.
1809 * It absorbs the reference to the style.
1811 * Return value: TRUE if there was a problem
1813 gboolean
1814 cmd_selection_format (WorkbookControl *wbc,
1815 GnmStyle *style, GnmBorder **borders,
1816 char const *opt_translated_name)
1818 CmdFormat *me;
1819 GSList *l;
1820 SheetView *sv = wb_control_cur_sheet_view (wbc);
1822 me = g_object_new (CMD_FORMAT_TYPE, NULL);
1824 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
1825 me->new_style = style;
1827 me->cmd.sheet = sv_sheet (sv);
1828 me->cmd.size = 1; /* Updated below. */
1830 me->old_styles = NULL;
1831 for (l = me->selection; l; l = l->next) {
1832 GnmRange const *sel_r = l->data;
1833 GnmRange range = *sel_r;
1834 CmdFormatOldStyle *os;
1836 /* Store the containing range to handle borders */
1837 if (borders != NULL) {
1838 if (range.start.col > 0) range.start.col--;
1839 if (range.start.row > 0) range.start.row--;
1840 if (range.end.col < gnm_sheet_get_last_col (me->cmd.sheet)) range.end.col++;
1841 if (range.end.row < gnm_sheet_get_last_row (me->cmd.sheet)) range.end.row++;
1844 os = g_new (CmdFormatOldStyle, 1);
1846 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
1847 os->pos = range.start;
1848 os->rows = NULL;
1849 os->old_heights = NULL;
1851 me->cmd.size += g_slist_length (os->styles);
1852 me->old_styles = g_slist_append (me->old_styles, os);
1855 if (borders) {
1856 int i;
1858 me->borders = g_new (GnmBorder *, GNM_STYLE_BORDER_EDGE_MAX);
1859 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1860 me->borders [i] = borders [i];
1861 } else
1862 me->borders = NULL;
1864 if (opt_translated_name == NULL) {
1865 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
1867 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing format of %s"), names);
1868 g_free (names);
1869 } else
1870 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
1872 return gnm_command_push_undo (wbc, G_OBJECT (me));
1875 /******************************************************************/
1877 static gboolean
1878 cmd_selection_format_toggle_font_style_filter (PangoAttribute *attribute, PangoAttrType *pt)
1880 return ((attribute->klass->type == *pt) ||
1881 ((PANGO_ATTR_RISE == *pt) && (attribute->klass->type == PANGO_ATTR_SCALE)));
1884 typedef struct {
1885 GOUndo *undo;
1886 PangoAttrType pt;
1887 } csftfs;
1889 static GnmValue *
1890 cmd_selection_format_toggle_font_style_cb (GnmCellIter const *iter, csftfs *closure)
1892 if (iter->cell && iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
1893 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
1894 if (fmt && go_format_is_markup (fmt)) {
1895 const PangoAttrList *old_markup =
1896 go_format_get_markup (fmt);
1897 PangoAttrList *new_markup = pango_attr_list_copy ((PangoAttrList *)old_markup);
1898 PangoAttrList *other = pango_attr_list_filter
1899 (new_markup,
1900 (PangoAttrFilterFunc) cmd_selection_format_toggle_font_style_filter,
1901 &closure->pt);
1902 if (other != NULL) {
1903 GnmSheetRange *sr;
1904 GnmRange r;
1905 range_init_cellpos (&r, &iter->pp.eval);
1906 sr = gnm_sheet_range_new (iter->pp.sheet, &r);
1907 closure->undo = go_undo_combine (closure->undo,
1908 sheet_range_set_markup_undo (sr, new_markup));
1910 pango_attr_list_unref (new_markup);
1911 pango_attr_list_unref (other);
1914 return NULL;
1917 gboolean
1918 cmd_selection_format_toggle_font_style (WorkbookControl *wbc,
1919 GnmStyle *style, GnmStyleElement t)
1921 SheetView *sv = wb_control_cur_sheet_view (wbc);
1922 Sheet *sheet = sv->sheet;
1923 GSList *selection = selection_get_ranges (sv, FALSE), *l;
1924 gboolean result;
1925 char *text, *name;
1926 GOUndo *undo = NULL;
1927 GOUndo *redo = NULL;
1928 PangoAttrType pt;
1931 switch (t) {
1932 case MSTYLE_FONT_BOLD:
1933 pt = PANGO_ATTR_WEIGHT;
1934 break;
1935 case MSTYLE_FONT_ITALIC:
1936 pt = PANGO_ATTR_STYLE;
1937 break;
1938 case MSTYLE_FONT_UNDERLINE:
1939 pt = PANGO_ATTR_UNDERLINE;
1940 break;
1941 case MSTYLE_FONT_STRIKETHROUGH:
1942 pt = PANGO_ATTR_STRIKETHROUGH;
1943 break;
1944 case MSTYLE_FONT_SCRIPT:
1945 pt = PANGO_ATTR_RISE; /* and PANGO_ATTR_SCALE (see ) */
1946 break;
1947 default:
1948 pt = PANGO_ATTR_INVALID;
1949 break;
1953 name = undo_range_list_name (sheet, selection);
1954 text = g_strdup_printf (_("Setting Font Style of %s"), name);
1955 g_free (name);
1957 for (l = selection; l != NULL; l = l->next) {
1958 GnmSheetRange *sr;
1959 undo = go_undo_combine
1960 (undo, clipboard_copy_range_undo (sheet, l->data));
1961 sr = gnm_sheet_range_new (sheet, l->data);
1962 redo = go_undo_combine
1963 (redo, sheet_apply_style_undo (sr, style));
1964 if (pt != PANGO_ATTR_INVALID) {
1965 csftfs closure;
1966 closure.undo = NULL;
1967 closure.pt = pt;
1968 sheet_foreach_cell_in_range
1969 (sheet, CELL_ITER_IGNORE_BLANK, &sr->range,
1970 (CellIterFunc) cmd_selection_format_toggle_font_style_cb,
1971 &closure);
1972 redo = go_undo_combine (redo, closure.undo);
1975 gnm_style_unref (style);
1976 result = cmd_generic (wbc, text, undo, redo);
1977 g_free (text);
1978 range_fragment_free (selection);
1980 return result;
1984 /******************************************************************/
1987 gboolean
1988 cmd_resize_colrow (WorkbookControl *wbc, Sheet *sheet,
1989 gboolean is_cols, ColRowIndexList *selection,
1990 int new_size)
1992 int size = 1;
1993 char *text;
1994 GOUndo *undo = NULL;
1995 GOUndo *redo = NULL;
1996 gboolean is_single, result;
1997 GString *list;
1998 ColRowStateGroup *saved_state;
2000 list = colrow_index_list_to_string (selection, is_cols, &is_single);
2001 gnm_cmd_trunc_descriptor (list, NULL);
2003 if (is_single) {
2004 if (new_size < 0)
2005 text = is_cols
2006 ? g_strdup_printf (_("Autofitting column %s"), list->str)
2007 : g_strdup_printf (_("Autofitting row %s"), list->str);
2008 else if (new_size > 0)
2009 text = is_cols
2010 ? g_strdup_printf (ngettext ("Setting width of column %s to %d pixel",
2011 "Setting width of column %s to %d pixels",
2012 new_size),
2013 list->str, new_size)
2014 : g_strdup_printf (ngettext ("Setting height of row %s to %d pixel",
2015 "Setting height of row %s to %d pixels",
2016 new_size),
2017 list->str, new_size);
2018 else text = is_cols
2019 ? g_strdup_printf (_("Setting width of column %s to default"),
2020 list->str)
2021 : g_strdup_printf (
2022 _("Setting height of row %s to default"), list->str);
2023 } else {
2024 if (new_size < 0)
2025 text = is_cols
2026 ? g_strdup_printf (_("Autofitting columns %s"), list->str)
2027 : g_strdup_printf (_("Autofitting rows %s"), list->str);
2028 else if (new_size > 0)
2029 text = is_cols
2030 ? g_strdup_printf (ngettext("Setting width of columns %s to %d pixel",
2031 "Setting width of columns %s to %d pixels",
2032 new_size),
2033 list->str, new_size)
2034 : g_strdup_printf (ngettext("Setting height of rows %s to %d pixel",
2035 "Setting height of rows %s to %d pixels",
2036 new_size),
2037 list->str, new_size);
2038 else text = is_cols
2039 ? g_strdup_printf (
2040 _("Setting width of columns %s to default"), list->str)
2041 : g_strdup_printf (
2042 _("Setting height of rows %s to default"), list->str);
2044 g_string_free (list, TRUE);
2046 saved_state = colrow_get_sizes (sheet, is_cols, selection, new_size);
2047 undo = gnm_undo_colrow_restore_state_group_new
2048 (sheet, is_cols, colrow_index_list_copy (selection), saved_state);
2050 redo = gnm_undo_colrow_set_sizes_new (sheet, is_cols, selection, new_size, NULL);
2052 result = cmd_generic_with_size (wbc, text, size, undo, redo);
2053 g_free (text);
2055 return result;
2058 gboolean
2059 cmd_autofit_selection (WorkbookControl *wbc, SheetView *sv, Sheet *sheet, gboolean fit_width,
2060 ColRowIndexList *selectionlist)
2062 GOUndo *undo = NULL;
2063 GOUndo *redo = NULL;
2064 gboolean result;
2065 ColRowStateGroup *saved_state;
2066 GSList *l, *selection = selection_get_ranges (sv, TRUE);
2067 gchar *names = undo_range_list_name (sheet, selection);
2068 gchar const *format = fit_width ?
2069 N_("Autofitting width of %s") : N_("Autofitting height of %s");
2070 gchar *text = g_strdup_printf (_(format), names);
2072 g_free (names);
2074 saved_state = colrow_get_sizes (sheet, fit_width, selectionlist, -1);
2075 undo = gnm_undo_colrow_restore_state_group_new
2076 (sheet, fit_width, colrow_index_list_copy (selectionlist), saved_state);
2078 for (l = selection; l != NULL; l = l->next)
2079 redo = go_undo_combine
2080 (redo, gnm_undo_colrow_set_sizes_new
2081 (sheet, fit_width, NULL, -1, l->data));
2083 result = cmd_generic (wbc, text, undo, redo);
2084 g_free (text);
2085 return result;
2089 /******************************************************************/
2091 #define CMD_SORT_TYPE (cmd_sort_get_type ())
2092 #define CMD_SORT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SORT_TYPE, CmdSort))
2094 typedef struct {
2095 GnmCommand cmd;
2097 GnmSortData *data;
2098 int *perm;
2099 GnmCellRegion *old_contents;
2100 } CmdSort;
2102 MAKE_GNM_COMMAND (CmdSort, cmd_sort, NULL)
2104 static void
2105 cmd_sort_finalize (GObject *cmd)
2107 CmdSort *me = CMD_SORT (cmd);
2109 if (me->data != NULL)
2110 gnm_sort_data_destroy (me->data);
2111 g_free (me->perm);
2112 if (me->old_contents != NULL)
2113 cellregion_unref (me->old_contents);
2115 gnm_command_finalize (cmd);
2118 static gboolean
2119 cmd_sort_undo (GnmCommand *cmd, WorkbookControl *wbc)
2121 CmdSort *me = CMD_SORT (cmd);
2122 GnmSortData *data = me->data;
2123 GnmPasteTarget pt;
2125 paste_target_init (&pt, data->sheet, data->range,
2126 PASTE_CONTENTS | PASTE_FORMATS |
2127 (data->retain_formats ? PASTE_FORMATS : 0));
2128 clipboard_paste_region (me->old_contents,
2129 &pt,
2130 GO_CMD_CONTEXT (wbc));
2132 return FALSE;
2135 static gboolean
2136 cmd_sort_redo (GnmCommand *cmd, WorkbookControl *wbc)
2138 CmdSort *me = CMD_SORT (cmd);
2139 GnmSortData *data = me->data;
2141 /* Check for locks */
2142 if (cmd_cell_range_is_locked_effective
2143 (data->sheet, data->range, wbc, _("Sorting")))
2144 return TRUE;
2146 if (me->perm)
2147 gnm_sort_position (data, me->perm, GO_CMD_CONTEXT (wbc));
2148 else {
2149 me->old_contents =
2150 clipboard_copy_range (data->sheet, data->range);
2151 me->cmd.size = cellregion_cmd_size (me->old_contents);
2152 me->perm = gnm_sort_contents (data, GO_CMD_CONTEXT (wbc));
2155 return FALSE;
2158 gboolean
2159 cmd_sort (WorkbookControl *wbc, GnmSortData *data)
2161 CmdSort *me;
2162 char *desc;
2164 g_return_val_if_fail (data != NULL, TRUE);
2166 desc = g_strdup_printf (_("Sorting %s"), range_as_string (data->range));
2167 if (sheet_range_contains_merges_or_arrays (data->sheet, data->range, GO_CMD_CONTEXT (wbc), desc, TRUE, TRUE)) {
2168 gnm_sort_data_destroy (data);
2169 g_free (desc);
2170 return TRUE;
2173 me = g_object_new (CMD_SORT_TYPE, NULL);
2175 me->data = data;
2176 me->perm = NULL;
2177 me->cmd.sheet = data->sheet;
2178 me->cmd.size = 1; /* Changed in initial redo. */
2179 me->cmd.cmd_descriptor = desc;
2181 return gnm_command_push_undo (wbc, G_OBJECT (me));
2184 /******************************************************************/
2186 #define CMD_COLROW_HIDE_TYPE (cmd_colrow_hide_get_type ())
2187 #define CMD_COLROW_HIDE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_HIDE_TYPE, CmdColRowHide))
2189 typedef struct {
2190 GnmCommand cmd;
2192 gboolean is_cols;
2193 ColRowVisList *hide, *show;
2194 } CmdColRowHide;
2196 static void
2197 cmd_colrow_hide_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2199 CmdColRowHide const *orig = (CmdColRowHide const *) cmd;
2200 cmd_selection_colrow_hide (wbc, orig->is_cols, orig->show != NULL);
2202 MAKE_GNM_COMMAND (CmdColRowHide, cmd_colrow_hide, cmd_colrow_hide_repeat)
2205 * cmd_colrow_hide_correct_selection:
2207 * Try to ensure that the selection/cursor is set to a visible row/col
2209 * Added to fix bug 38179
2210 * Removed because the result is irritating and the bug is actually XL
2211 * compatibile
2213 static void
2214 cmd_colrow_hide_correct_selection (G_GNUC_UNUSED CmdColRowHide *me, G_GNUC_UNUSED WorkbookControl *wbc)
2216 #if 0
2217 int x, y, index;
2218 SheetView *sv = sheet_get_view (me->cmd.sheet,
2219 wb_control_view (wbc));
2221 index = colrow_find_adjacent_visible (me->cmd.sheet, me->is_cols,
2222 me->is_cols ? sv->edit_pos.col : sv->edit_pos.row,
2223 TRUE);
2225 x = me->is_cols ? sv->edit_pos.row : index;
2226 y = me->is_cols ? index : sv->edit_pos.col;
2228 if (index >= 0) {
2229 sv_selection_reset (sv);
2230 if (me->is_cols)
2231 sv_selection_add_full (sv, y, x, y, 0,
2232 y, gnm_sheet_get_last_row (sheet),
2233 GNM_SELECTION_MODE_ADD);
2234 else
2235 sv_selection_add_full (sv, y, x, 0, x,
2236 gnm_sheet_get_last_col (sheet), x,
2237 GNM_SELECTION_MODE_ADD);
2239 #endif
2242 static gboolean
2243 cmd_colrow_hide_undo (GnmCommand *cmd, WorkbookControl *wbc)
2245 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2247 g_return_val_if_fail (me != NULL, TRUE);
2249 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2250 TRUE, me->hide);
2251 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2252 FALSE, me->show);
2254 if (me->show != NULL)
2255 cmd_colrow_hide_correct_selection (me, wbc);
2257 return FALSE;
2260 static gboolean
2261 cmd_colrow_hide_redo (GnmCommand *cmd, WorkbookControl *wbc)
2263 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2265 g_return_val_if_fail (me != NULL, TRUE);
2267 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2268 FALSE, me->hide);
2269 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2270 TRUE, me->show);
2272 if (me->hide != NULL)
2273 cmd_colrow_hide_correct_selection (me, wbc);
2275 return FALSE;
2278 static void
2279 cmd_colrow_hide_finalize (GObject *cmd)
2281 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2282 colrow_vis_list_destroy (me->hide);
2283 me->hide = NULL;
2284 colrow_vis_list_destroy (me->show);
2285 me->show = NULL;
2286 gnm_command_finalize (cmd);
2289 gboolean
2290 cmd_selection_colrow_hide (WorkbookControl *wbc,
2291 gboolean is_cols, gboolean visible)
2293 CmdColRowHide *me;
2294 SheetView *sv = wb_control_cur_sheet_view (wbc);
2295 int n;
2296 Sheet *sheet;
2297 GSList *show = NULL, *hide = NULL;
2299 if (visible)
2300 show = colrow_get_visibility_toggle (sv, is_cols, TRUE);
2301 else
2302 hide = colrow_get_visibility_toggle (sv, is_cols, FALSE);
2303 n = colrow_vis_list_length (hide) + colrow_vis_list_length (show);
2304 sheet = sv_sheet (sv);
2306 if (!visible) {
2307 /* If these are the last colrows to hide, check with the user */
2308 int count = 0;
2309 if (is_cols) {
2310 int i, max = gnm_sheet_get_max_cols (sheet);
2311 ColRowInfo *ci;
2312 for (i = 0 ; i < max ; i++)
2313 if (NULL ==
2314 (ci = sheet_col_get (sheet, i)) ||
2315 (ci->visible))
2316 count++;
2317 } else {
2318 int i, max = gnm_sheet_get_max_rows (sheet);
2319 ColRowInfo *ci;
2320 for (i = 0 ; i < max ; i++)
2321 if (NULL ==
2322 (ci = sheet_row_get (sheet, i)) ||
2323 (ci->visible))
2324 count++;
2326 if (count <= n) {
2327 gchar const *text = is_cols ?
2328 _("Are you sure that you want to hide all columns? "
2329 "If you do so you can unhide them with the "
2330 "'Format\342\206\222Column\342\206\222Unhide' "
2331 "menu item.") :
2332 _("Are you sure that you want to hide all rows? "
2333 "If you do so you can unhide them with the "
2334 "'Format\342\206\222Row\342\206\222Unhide' "
2335 "menu item.");
2336 if (!go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)),
2337 FALSE, "%s", text)) {
2338 colrow_vis_list_destroy (show);
2339 colrow_vis_list_destroy (hide);
2340 return TRUE;
2345 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2346 me->show = show;
2347 me->hide = hide;
2348 me->is_cols = is_cols;
2349 me->cmd.sheet = sheet;
2350 me->cmd.size = 1 + g_slist_length (hide) + g_slist_length (show);
2351 me->cmd.cmd_descriptor = g_strdup (is_cols
2352 ? (visible ? _("Unhide columns") : _("Hide columns"))
2353 : (visible ? _("Unhide rows") : _("Hide rows")));
2355 return gnm_command_push_undo (wbc, G_OBJECT (me));
2358 gboolean
2359 cmd_selection_outline_change (WorkbookControl *wbc,
2360 gboolean is_cols, int index, int depth)
2362 CmdColRowHide *me;
2363 ColRowInfo const *cri;
2364 int first = -1, last = -1;
2365 gboolean visible = FALSE;
2366 int d;
2367 Sheet *sheet = wb_control_cur_sheet (wbc);
2368 SheetView *sv = wb_control_cur_sheet_view (wbc);
2370 cri = sheet_colrow_get_info (sheet, index, is_cols);
2372 d = cri->outline_level;
2373 if (depth > d)
2374 depth = d;
2376 /* Nodes only collapse when selected directly, selecting at a lower
2377 * level is a standard toggle. */
2378 if (depth == d) {
2379 if ((is_cols ? sheet->outline_symbols_right : sheet->outline_symbols_below)) {
2380 if (index > 0) {
2381 ColRowInfo const *prev =
2382 sheet_colrow_get (sheet, index-1, is_cols);
2384 if (prev != NULL && prev->outline_level > d) {
2385 visible = (depth == d && cri->is_collapsed);
2386 last = index - 1;
2387 first = colrow_find_outline_bound (sheet, is_cols,
2388 last, d+1, FALSE);
2391 } else if (index+1 < colrow_max (is_cols, sheet)) {
2392 ColRowInfo const *next =
2393 sheet_colrow_get (sheet, index+1, is_cols);
2395 if (next != NULL && next->outline_level > d) {
2396 visible = (depth == d && cri->is_collapsed);
2397 first = index + 1;
2398 last = colrow_find_outline_bound (sheet, is_cols,
2399 first, d+1, TRUE);
2404 /* If nothing done yet do a simple collapse */
2405 if (first < 0 && cri->outline_level > 0) {
2406 if (depth < d)
2407 ++depth;
2408 first = colrow_find_outline_bound (sheet, is_cols, index, depth, FALSE);
2409 last = colrow_find_outline_bound (sheet, is_cols, index, depth, TRUE);
2410 visible = FALSE;
2412 if (first == last && depth > cri->outline_level)
2413 return TRUE;
2416 if (first < 0 || last < 0)
2417 return TRUE;
2419 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2421 me->is_cols = is_cols;
2422 me->hide = me->show = NULL;
2423 if (visible)
2424 me->show = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2425 TRUE, first, last);
2426 else
2427 me->hide = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2428 FALSE, first, last);
2430 me->cmd.sheet = sv_sheet (sv);
2431 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2432 me->cmd.cmd_descriptor = g_strdup (is_cols
2433 ? (visible ? _("Expand columns") : _("Collapse columns"))
2434 : (visible ? _("Expand rows") : _("Collapse rows")));
2436 return gnm_command_push_undo (wbc, G_OBJECT (me));
2439 gboolean
2440 cmd_global_outline_change (WorkbookControl *wbc, gboolean is_cols, int depth)
2442 CmdColRowHide *me;
2443 ColRowVisList *hide, *show;
2444 SheetView *sv = wb_control_cur_sheet_view (wbc);
2446 colrow_get_global_outline (sv_sheet (sv), is_cols, depth, &show, &hide);
2448 if (show == NULL && hide == NULL)
2449 return TRUE;
2451 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2452 me->is_cols = is_cols;
2453 me->hide = hide;
2454 me->show = show;
2455 me->cmd.sheet = sv_sheet (sv);
2456 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2457 me->cmd.cmd_descriptor = g_strdup_printf (is_cols
2458 ? _("Show column outline %d") : _("Show row outline %d"), depth);
2460 return gnm_command_push_undo (wbc, G_OBJECT (me));
2463 /******************************************************************/
2465 #define CMD_GROUP_TYPE (cmd_group_get_type ())
2466 #define CMD_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GROUP_TYPE, CmdGroup))
2468 typedef struct {
2469 GnmCommand cmd;
2471 GnmRange range;
2472 gboolean is_cols;
2473 gboolean group;
2474 } CmdGroup;
2476 static void
2477 cmd_group_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2479 CmdGroup const *orig = (CmdGroup const *) cmd;
2480 cmd_selection_group (wbc, orig->is_cols, orig->group);
2482 MAKE_GNM_COMMAND (CmdGroup, cmd_group, cmd_group_repeat)
2484 static gboolean
2485 cmd_group_undo (GnmCommand *cmd,
2486 G_GNUC_UNUSED WorkbookControl *wbc)
2488 CmdGroup const *me = CMD_GROUP (cmd);
2489 sheet_colrow_group_ungroup (me->cmd.sheet,
2490 &me->range, me->is_cols, !me->group);
2491 return FALSE;
2494 static gboolean
2495 cmd_group_redo (GnmCommand *cmd,
2496 G_GNUC_UNUSED WorkbookControl *wbc)
2498 CmdGroup const *me = CMD_GROUP (cmd);
2499 sheet_colrow_group_ungroup (me->cmd.sheet,
2500 &me->range, me->is_cols, me->group);
2501 return FALSE;
2504 static void
2505 cmd_group_finalize (GObject *cmd)
2507 gnm_command_finalize (cmd);
2510 gboolean
2511 cmd_selection_group (WorkbookControl *wbc,
2512 gboolean is_cols, gboolean group)
2514 CmdGroup *me;
2515 SheetView *sv;
2516 GnmRange r;
2518 g_return_val_if_fail (wbc != NULL, TRUE);
2520 sv = wb_control_cur_sheet_view (wbc);
2521 r = *selection_first_range (sv, NULL, NULL);
2523 /* Check if this really is possible and display an error if it's not */
2524 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2525 if (group) {
2526 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2527 ? _("Those columns are already grouped")
2528 : _("Those rows are already grouped"));
2529 return TRUE;
2532 /* see if the user selected the col/row with the marker too */
2533 if (is_cols) {
2534 if (r.start.col != r.end.col) {
2535 if (sv->sheet->outline_symbols_right)
2536 r.end.col--;
2537 else
2538 r.start.col++;
2540 } else {
2541 if (r.start.row != r.end.row) {
2542 if (sv->sheet->outline_symbols_below)
2543 r.end.row--;
2544 else
2545 r.start.row++;
2549 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2550 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2551 ? _("Those columns are not grouped, you can't ungroup them")
2552 : _("Those rows are not grouped, you can't ungroup them"));
2553 return TRUE;
2557 me = g_object_new (CMD_GROUP_TYPE, NULL);
2558 me->is_cols = is_cols;
2559 me->group = group;
2560 me->range = r;
2562 me->cmd.sheet = sv->sheet;
2563 me->cmd.size = 1;
2564 me->cmd.cmd_descriptor = is_cols
2565 ? g_strdup_printf (group ? _("Group columns %s") : _("Ungroup columns %s"),
2566 cols_name (me->range.start.col, me->range.end.col))
2567 : g_strdup_printf (group ? _("Group rows %d:%d") : _("Ungroup rows %d:%d"),
2568 me->range.start.row + 1, me->range.end.row + 1);
2570 return gnm_command_push_undo (wbc, G_OBJECT (me));
2573 /******************************************************************/
2575 #define CMD_PASTE_CUT_TYPE (cmd_paste_cut_get_type ())
2576 #define CMD_PASTE_CUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_CUT_TYPE, CmdPasteCut))
2578 typedef struct {
2579 GnmCommand cmd;
2581 GnmExprRelocateInfo info;
2582 GSList *paste_contents;
2583 GOUndo *reloc_undo;
2584 gboolean move_selection;
2585 ColRowStateList *saved_sizes;
2587 /* handle redo-ing an undo with contents from a deleted sheet */
2588 GnmCellRegion *deleted_sheet_contents;
2589 } CmdPasteCut;
2591 MAKE_GNM_COMMAND (CmdPasteCut, cmd_paste_cut, NULL)
2593 typedef struct {
2594 GnmPasteTarget pt;
2595 GnmCellRegion *contents;
2596 } PasteContent;
2599 * cmd_paste_cut_update:
2601 * Utility routine to update things when we are transfering between sheets and
2602 * workbooks.
2604 static void
2605 cmd_paste_cut_update (GnmExprRelocateInfo const *info,
2606 G_GNUC_UNUSED WorkbookControl *wbc)
2608 Sheet *o = info->origin_sheet;
2609 Sheet *t = info->target_sheet;
2611 /* Dirty and update both sheets */
2612 sheet_mark_dirty (t);
2613 sheet_update (t);
2615 if (IS_SHEET (o) && o != t) {
2616 sheet_mark_dirty (o);
2617 sheet_update (o);
2621 static gboolean
2622 cmd_paste_cut_undo (GnmCommand *cmd, WorkbookControl *wbc)
2624 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2625 GnmExprRelocateInfo reverse;
2627 g_return_val_if_fail (me != NULL, TRUE);
2628 g_return_val_if_fail (me->paste_contents != NULL, TRUE);
2629 g_return_val_if_fail (me->deleted_sheet_contents == NULL, TRUE);
2631 reverse.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
2632 reverse.target_sheet = me->info.origin_sheet;
2633 reverse.origin_sheet = me->info.target_sheet;
2634 reverse.origin = me->info.origin;
2635 range_translate (&reverse.origin,
2636 me->info.origin_sheet, /* FIXME: What sheet? */
2637 me->info.col_offset,
2638 me->info.row_offset);
2639 reverse.col_offset = -me->info.col_offset;
2640 reverse.row_offset = -me->info.row_offset;
2642 /* Move things back being careful NOT to invalidate the src region */
2643 if (IS_SHEET (me->info.origin_sheet))
2644 sheet_move_range (&reverse, NULL, GO_CMD_CONTEXT (wbc));
2645 else
2646 me->deleted_sheet_contents = clipboard_copy_range (
2647 reverse.origin_sheet, &reverse.origin);
2649 /* Restore the original row heights */
2650 colrow_set_states (me->info.target_sheet, FALSE,
2651 reverse.origin.start.row, me->saved_sizes);
2652 colrow_state_list_destroy (me->saved_sizes);
2653 me->saved_sizes = NULL;
2655 if (me->reloc_undo) {
2656 go_undo_undo (me->reloc_undo);
2657 g_object_unref (me->reloc_undo);
2658 me->reloc_undo = NULL;
2661 while (me->paste_contents) {
2662 PasteContent *pc = me->paste_contents->data;
2663 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2665 clipboard_paste_region (pc->contents, &pc->pt, GO_CMD_CONTEXT (wbc));
2666 cellregion_unref (pc->contents);
2667 g_free (pc);
2670 /* Force update of the status area */
2671 sheet_flag_status_update_range (me->info.target_sheet, NULL);
2673 cmd_paste_cut_update (&me->info, wbc);
2675 /* Select the original region */
2676 if (me->move_selection && IS_SHEET (me->info.origin_sheet))
2677 select_range (me->info.origin_sheet,
2678 &me->info.origin,
2679 wbc);
2681 return FALSE;
2684 static gboolean
2685 cmd_paste_cut_redo (GnmCommand *cmd, WorkbookControl *wbc)
2687 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2688 GnmRange tmp;
2690 g_return_val_if_fail (me != NULL, TRUE);
2691 g_return_val_if_fail (me->paste_contents == NULL, TRUE);
2693 tmp = me->info.origin;
2694 range_translate (&tmp, me->info.origin_sheet, /* FIXME: What sheet? */
2695 me->info.col_offset, me->info.row_offset);
2696 range_normalize (&tmp);
2698 g_return_val_if_fail (range_is_sane (&tmp), TRUE);
2700 if (me->info.origin_sheet != me->info.target_sheet ||
2701 !range_overlap (&me->info.origin, &tmp)) {
2702 PasteContent *pc = g_new (PasteContent, 1);
2703 paste_target_init (&pc->pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2704 pc->contents = clipboard_copy_range (me->info.target_sheet, &tmp);
2705 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2706 } else {
2707 /* need to store any portions of the paste target
2708 * that do not overlap with the source.
2710 GSList *ptr, *frag = range_split_ranges (&me->info.origin, &tmp);
2711 for (ptr = frag ; ptr != NULL ; ptr = ptr->next) {
2712 GnmRange *r = ptr->data;
2714 if (!range_overlap (&me->info.origin, r)) {
2715 PasteContent *pc = g_new (PasteContent, 1);
2716 paste_target_init (&pc->pt, me->info.target_sheet, r, PASTE_ALL_SHEET);
2717 pc->contents = clipboard_copy_range (me->info.target_sheet, r);
2718 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2720 g_free (r);
2722 g_slist_free (frag);
2725 /* rare corner case. If the origin sheet has been deleted */
2726 if (!IS_SHEET (me->info.origin_sheet)) {
2727 GnmPasteTarget pt;
2728 paste_target_init (&pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2729 sheet_clear_region (pt.sheet,
2730 tmp.start.col, tmp.start.row, tmp.end.col, tmp.end.row,
2731 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
2732 GO_CMD_CONTEXT (wbc));
2733 clipboard_paste_region (me->deleted_sheet_contents,
2734 &pt, GO_CMD_CONTEXT (wbc));
2735 cellregion_unref (me->deleted_sheet_contents);
2736 me->deleted_sheet_contents = NULL;
2737 } else
2738 sheet_move_range (&me->info, &me->reloc_undo, GO_CMD_CONTEXT (wbc));
2740 cmd_paste_cut_update (&me->info, wbc);
2742 /* Backup row heights and adjust row heights to fit */
2743 me->saved_sizes = colrow_get_states (me->info.target_sheet, FALSE, tmp.start.row, tmp.end.row);
2744 rows_height_update (me->info.target_sheet, &tmp, FALSE);
2746 /* Make sure the destination is selected */
2747 if (me->move_selection)
2748 select_range (me->info.target_sheet, &tmp, wbc);
2750 return FALSE;
2753 static void
2754 cmd_paste_cut_finalize (GObject *cmd)
2756 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2758 if (me->saved_sizes)
2759 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
2760 while (me->paste_contents) {
2761 PasteContent *pc = me->paste_contents->data;
2762 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2763 cellregion_unref (pc->contents);
2764 g_free (pc);
2766 if (me->reloc_undo) {
2767 g_object_unref (me->reloc_undo);
2768 me->reloc_undo = NULL;
2770 if (me->deleted_sheet_contents) {
2771 cellregion_unref (me->deleted_sheet_contents);
2772 me->deleted_sheet_contents = NULL;
2775 gnm_command_finalize (cmd);
2778 gboolean
2779 cmd_paste_cut (WorkbookControl *wbc, GnmExprRelocateInfo const *info,
2780 gboolean move_selection, char *descriptor)
2782 CmdPasteCut *me;
2783 GnmRange r;
2784 char *where;
2786 g_return_val_if_fail (info != NULL, TRUE);
2788 /* This is vacuous */
2789 if (info->origin_sheet == info->target_sheet &&
2790 info->col_offset == 0 && info->row_offset == 0)
2791 return TRUE;
2793 /* FIXME: Do we want to show the destination range as well ? */
2794 where = undo_range_name (info->origin_sheet, &info->origin);
2795 if (descriptor == NULL)
2796 descriptor = g_strdup_printf (_("Moving %s"), where);
2797 g_free (where);
2799 g_return_val_if_fail (info != NULL, TRUE);
2801 r = info->origin;
2802 if (range_translate (&r, info->target_sheet,
2803 info->col_offset, info->row_offset)) {
2805 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), descriptor,
2806 _("is beyond sheet boundaries"));
2807 g_free (descriptor);
2808 return TRUE;
2811 /* Check array subdivision & merged regions */
2812 if (sheet_range_splits_region (info->target_sheet, &r,
2813 (info->origin_sheet == info->target_sheet)
2814 ? &info->origin : NULL, GO_CMD_CONTEXT (wbc), descriptor)) {
2815 g_free (descriptor);
2816 return TRUE;
2819 me = g_object_new (CMD_PASTE_CUT_TYPE, NULL);
2821 me->info = *info;
2822 me->paste_contents = NULL;
2823 me->deleted_sheet_contents = NULL;
2824 me->reloc_undo = NULL;
2825 me->move_selection = move_selection;
2826 me->saved_sizes = NULL;
2828 me->cmd.sheet = NULL; /* we have potentially two different. */
2829 me->cmd.size = 1; /* FIXME? */
2830 me->cmd.cmd_descriptor = descriptor;
2832 /* NOTE : if the destination workbook is different from the source
2833 * workbook should we have undo elements in both menus ?? It seems
2834 * poor form to hit undo in 1 window and effect another...
2836 * Maybe queue it as two different commands, as a clear in one book
2837 * and a paste in the other. This is not symmetric though. What
2838 * happens to the cells in the original sheet that now reference the
2839 * cells in the other? When do they reset to the original?
2841 * Probably when the clear in the original is undone.
2844 return gnm_command_push_undo (wbc, G_OBJECT (me));
2847 /******************************************************************/
2849 static void
2850 warn_if_date_trouble (WorkbookControl *wbc, GnmCellRegion *cr)
2852 Workbook *wb = wb_control_get_workbook (wbc);
2853 const GODateConventions *wb_date_conv = workbook_date_conv (wb);
2855 if (cr->date_conv == NULL)
2856 return;
2857 if (go_date_conv_equal (cr->date_conv, wb_date_conv))
2858 return;
2860 /* We would like to show a warning, but it seems we cannot via a context. */
2862 GError *err;
2863 err = g_error_new (go_error_invalid(), 0,
2864 _("Copying between files with different date conventions.\n"
2865 "It is possible that some dates could be copied\n"
2866 "incorrectly."));
2867 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
2868 g_error_free (err);
2873 #define CMD_PASTE_COPY_TYPE (cmd_paste_copy_get_type ())
2874 #define CMD_PASTE_COPY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_COPY_TYPE, CmdPasteCopy))
2876 typedef struct {
2877 GnmCommand cmd;
2879 GnmCellRegion *contents;
2880 GSList *pasted_objects,*orig_contents_objects;
2881 GnmPasteTarget dst;
2882 gboolean has_been_through_cycle;
2883 gboolean only_objects;
2884 gboolean single_merge_to_single_merge;
2885 } CmdPasteCopy;
2887 static void
2888 cmd_paste_copy_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2890 CmdPasteCopy const *orig = (CmdPasteCopy const *) cmd;
2891 GnmPasteTarget new_dst;
2892 SheetView *sv = wb_control_cur_sheet_view (wbc);
2893 GnmRange const *r = selection_first_range (sv,
2894 GO_CMD_CONTEXT (wbc), _("Paste Copy"));
2895 GnmCellRegion *newcr;
2897 if (r == NULL)
2898 return;
2900 paste_target_init (&new_dst, sv_sheet (sv), r, orig->dst.paste_flags);
2901 newcr = clipboard_copy_range (orig->dst.sheet, &orig->dst.range);
2902 cmd_paste_copy (wbc, &new_dst, newcr);
2903 cellregion_unref (newcr);
2905 MAKE_GNM_COMMAND (CmdPasteCopy, cmd_paste_copy, cmd_paste_copy_repeat)
2907 static int
2908 by_addr (gconstpointer a, gconstpointer b)
2910 if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b))
2911 return -1;
2912 if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b))
2913 return +1;
2914 return 0;
2918 * get_new_objects:
2919 * @sheet: #Sheet to query
2920 * @old: (element-type SheetObject): list of objects to disregard
2922 * Returns: (transfer full) (element-type SheetObject): A list of new objects
2923 * in sheet since @old was collected.
2925 static GSList *
2926 get_new_objects (Sheet *sheet, GSList *old)
2928 GSList *objs =
2929 g_slist_sort (g_slist_copy_deep (sheet->sheet_objects,
2930 (GCopyFunc)g_object_ref,
2931 NULL),
2932 by_addr);
2933 GSList *p = objs, *last = NULL;
2935 while (old) {
2936 int c = -1;
2937 while (p && (c = by_addr (p->data, old->data)) < 0) {
2938 last = p;
2939 p = p->next;
2942 old = old->next;
2944 if (c == 0) {
2945 GSList *next = p->next;
2946 if (last)
2947 last->next = next;
2948 else
2949 objs = next;
2950 g_slist_free_1 (p);
2951 p = next;
2955 return objs;
2958 static void
2959 cmd_paste_copy_select_obj (SheetObject *so, SheetControlGUI *scg)
2961 scg_object_select (scg, so);
2964 static gboolean
2965 cmd_paste_copy_impl (GnmCommand *cmd, WorkbookControl *wbc,
2966 gboolean is_undo)
2968 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
2969 GnmCellRegion *contents;
2970 GSList *old_objects;
2972 g_return_val_if_fail (me != NULL, TRUE);
2973 g_return_val_if_fail (me->contents != NULL, TRUE);
2975 g_slist_foreach (me->pasted_objects,
2976 (GFunc)sheet_object_clear_sheet,
2977 NULL);
2978 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
2979 me->pasted_objects = NULL;
2980 old_objects = get_new_objects (me->dst.sheet, NULL);
2982 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
2983 if (me->has_been_through_cycle)
2984 me->dst.paste_flags =
2985 PASTE_CONTENTS |
2986 PASTE_COLUMN_WIDTHS | PASTE_ROW_HEIGHTS |
2987 (me->dst.paste_flags & PASTE_ALL_SHEET);
2989 if (clipboard_paste_region (me->contents, &me->dst,
2990 GO_CMD_CONTEXT (wbc))) {
2991 /* There was a problem, avoid leaking */
2992 cellregion_unref (contents);
2993 g_slist_free_full (old_objects, g_object_unref);
2994 return TRUE;
2997 me->pasted_objects = get_new_objects (me->dst.sheet, old_objects);
2998 g_slist_free_full (old_objects, g_object_unref);
3000 if (!is_undo && !me->has_been_through_cycle) {
3001 colrow_autofit (me->dst.sheet, &me->dst.range, FALSE, FALSE,
3002 TRUE, FALSE,
3003 NULL, NULL);
3004 colrow_autofit (me->dst.sheet, &me->dst.range, TRUE, TRUE,
3005 TRUE, FALSE,
3006 NULL, NULL);
3009 if (is_undo) {
3010 // We cannot use the random set of objects at the target
3011 // location. http://bugzilla.gnome.org/show_bug.cgi?id=308300
3012 g_slist_free_full (contents->objects, g_object_unref);
3013 contents->objects = g_slist_copy_deep
3014 (me->orig_contents_objects,
3015 (GCopyFunc)sheet_object_dup, NULL);
3016 } else {
3017 GSList *l;
3018 for (l = contents->objects; l; l = l->next) {
3019 SheetObject *so = l->data;
3020 if (sheet_object_get_sheet (so)) {
3021 g_object_unref (so);
3022 l->data = NULL;
3023 } else {
3024 // Object got deleted by paste, so keep it for
3025 // undo. See bugzilla 732653
3028 contents->objects =
3029 g_slist_remove_all (contents->objects, NULL);
3032 cellregion_unref (me->contents);
3033 me->contents = contents;
3034 me->has_been_through_cycle = TRUE;
3036 /* Select the newly pasted contents (this queues a redraw) */
3037 if (me->only_objects && GNM_IS_WBC_GTK (wbc)) {
3038 SheetControlGUI *scg =
3039 wbcg_get_nth_scg (WBC_GTK (wbc),
3040 cmd->sheet->index_in_wb);
3041 scg_object_unselect (scg, NULL);
3042 g_slist_foreach (me->pasted_objects,
3043 (GFunc) cmd_paste_copy_select_obj, scg);
3045 select_range (me->dst.sheet, &me->dst.range, wbc);
3047 return FALSE;
3050 static gboolean
3051 cmd_paste_copy_undo (GnmCommand *cmd, WorkbookControl *wbc)
3053 return cmd_paste_copy_impl (cmd, wbc, TRUE);
3056 static gboolean
3057 cmd_paste_copy_redo (GnmCommand *cmd, WorkbookControl *wbc)
3059 return cmd_paste_copy_impl (cmd, wbc, FALSE);
3062 static void
3063 cmd_paste_copy_finalize (GObject *cmd)
3065 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
3067 if (me->contents) {
3068 cellregion_unref (me->contents);
3069 me->contents = NULL;
3071 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
3072 g_slist_free_full (me->orig_contents_objects, (GDestroyNotify)g_object_unref);
3073 gnm_command_finalize (cmd);
3077 * cmd_paste_copy will ref cr as needed.
3079 gboolean
3080 cmd_paste_copy (WorkbookControl *wbc,
3081 GnmPasteTarget const *pt, GnmCellRegion *cr)
3083 CmdPasteCopy *me;
3084 int n_r = 1, n_c = 1;
3085 char *range_name;
3086 GnmRange const *merge_src;
3088 g_return_val_if_fail (pt != NULL, TRUE);
3089 g_return_val_if_fail (IS_SHEET (pt->sheet), TRUE);
3090 g_return_val_if_fail (cr != NULL, TRUE);
3092 cellregion_ref (cr);
3094 me = g_object_new (CMD_PASTE_COPY_TYPE, NULL);
3096 me->cmd.sheet = pt->sheet;
3097 me->cmd.size = 1; /* FIXME? */
3099 range_name = undo_range_name (pt->sheet, &pt->range);
3100 me->cmd.cmd_descriptor = g_strdup_printf (_("Pasting into %s"),
3101 range_name);
3102 g_free (range_name);
3104 me->dst = *pt;
3105 me->contents = cr;
3106 me->has_been_through_cycle = FALSE;
3107 me->only_objects = (cr->cols < 1 || cr->rows < 1);
3108 me->pasted_objects = NULL;
3109 me->orig_contents_objects =
3110 g_slist_copy_deep (cr->objects,
3111 (GCopyFunc)sheet_object_dup, NULL);
3112 me->single_merge_to_single_merge = FALSE;
3114 /* If the input is only objects ignore all this range stuff */
3115 if (!me->only_objects) {
3116 /* see if we need to do any tiling */
3117 GnmRange *r = &me->dst.range;
3118 if (g_slist_length (cr->merged) == 1 &&
3119 (NULL != (merge_src = cr->merged->data)) &&
3120 range_height (merge_src) == cr->rows &&
3121 range_width (merge_src) == cr->cols) {
3122 /* We are copying from a single merge */
3123 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3124 if (merge != NULL && range_equal (r, merge)) {
3125 /* To a single merge */
3126 me->single_merge_to_single_merge = TRUE;
3127 n_c = n_r = 1;
3128 me->dst.paste_flags |= PASTE_DONT_MERGE;
3129 goto copy_ready;
3133 if (pt->paste_flags & PASTE_TRANSPOSE) {
3134 n_c = range_width (r) / cr->rows;
3135 if (n_c < 1) n_c = 1;
3136 r->end.col = r->start.col + n_c * cr->rows - 1;
3138 n_r = range_height (r) / cr->cols;
3139 if (n_r < 1) n_r = 1;
3140 r->end.row = r->start.row + n_r * cr->cols - 1;
3141 } else {
3142 /* Before looking for tiling if we are not transposing,
3143 * allow pasting a full col or row from a single cell */
3144 n_c = range_width (r);
3145 if (n_c == 1 && cr->cols == gnm_sheet_get_max_cols (me->cmd.sheet)) {
3146 r->start.col = 0;
3147 r->end.col = gnm_sheet_get_last_col (me->cmd.sheet);
3148 } else {
3149 n_c /= cr->cols;
3150 if (n_c < 1) n_c = 1;
3151 r->end.col = r->start.col + n_c * cr->cols - 1;
3154 n_r = range_height (r);
3155 if (n_r == 1 && cr->rows == gnm_sheet_get_max_rows (me->cmd.sheet)) {
3156 r->start.row = 0;
3157 r->end.row = gnm_sheet_get_last_row (me->cmd.sheet);
3158 } else {
3159 n_r /= cr->rows;
3160 if (n_r < 1) n_r = 1;
3161 r->end.row = r->start.row + n_r * cr->rows - 1;
3165 if (cr->cols != 1 || cr->rows != 1) {
3166 /* Note: when the source is a single cell, a single target merge is special */
3167 /* see clipboard.c (clipboard_paste_region) */
3168 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3169 if (merge != NULL && range_equal (r, merge)) {
3170 /* destination is a single merge */
3171 /* enlarge it such that the source fits */
3172 if (pt->paste_flags & PASTE_TRANSPOSE) {
3173 if ((r->end.col - r->start.col + 1) < cr->rows)
3174 r->end.col = r->start.col + cr->rows - 1;
3175 if ((r->end.row - r->start.row + 1) < cr->cols)
3176 r->end.row = r->start.row + cr->cols - 1;
3177 } else {
3178 if ((r->end.col - r->start.col + 1) < cr->cols)
3179 r->end.col = r->start.col + cr->cols - 1;
3180 if ((r->end.row - r->start.row + 1) < cr->rows)
3181 r->end.row = r->start.row + cr->rows - 1;
3187 if (n_c * (gnm_float)n_r > 10000.) {
3188 char *number = g_strdup_printf ("%0.0" GNM_FORMAT_f,
3189 (gnm_float)n_c * (gnm_float)n_r);
3190 gboolean result = go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)), FALSE,
3191 _("Do you really want to paste "
3192 "%s copies?"), number);
3193 g_free (number);
3194 if (!result) {
3195 g_object_unref (me);
3196 return TRUE;
3200 copy_ready:
3201 /* Use translate to do a quiet sanity check */
3202 if (range_translate (&me->dst.range, pt->sheet, 0, 0)) {
3203 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
3204 me->cmd.cmd_descriptor,
3205 _("is beyond sheet boundaries"));
3206 g_object_unref (me);
3207 return TRUE;
3210 /* no need to test if all we have are objects or are copying from */
3211 /*a single merge to a single merge*/
3212 if ((!me->only_objects) && (!me->single_merge_to_single_merge)&&
3213 sheet_range_splits_region (pt->sheet, &me->dst.range,
3214 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
3215 g_object_unref (me);
3216 return TRUE;
3219 warn_if_date_trouble (wbc, cr);
3221 return gnm_command_push_undo (wbc, G_OBJECT (me));
3224 /******************************************************************/
3226 #define CMD_AUTOFILL_TYPE (cmd_autofill_get_type ())
3227 #define CMD_AUTOFILL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFILL_TYPE, CmdAutofill))
3229 typedef struct {
3230 GnmCommand cmd;
3232 GnmCellRegion *contents;
3233 GnmPasteTarget dst;
3234 GnmRange src;
3235 int base_col, base_row, w, h, end_col, end_row;
3236 gboolean default_increment;
3237 gboolean inverse_autofill;
3238 ColRowIndexList *columns;
3239 ColRowStateGroup *old_widths;
3240 } CmdAutofill;
3242 static void
3243 cmd_autofill_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3245 CmdAutofill const *orig = (CmdAutofill const *) cmd;
3246 SheetView *sv = wb_control_cur_sheet_view (wbc);
3247 GnmRange const *r = selection_first_range (sv,
3248 GO_CMD_CONTEXT (wbc), _("Autofill"));
3250 if (r == NULL)
3251 return;
3253 cmd_autofill (wbc, sv_sheet (sv), orig->default_increment,
3254 r->start.col, r->start.row, range_width (r), range_height (r),
3255 r->start.col + (orig->end_col - orig->base_col),
3256 r->start.row + (orig->end_row - orig->base_row),
3257 orig->inverse_autofill);
3259 MAKE_GNM_COMMAND (CmdAutofill, cmd_autofill, cmd_autofill_repeat)
3261 static gboolean
3262 cmd_autofill_undo (GnmCommand *cmd, WorkbookControl *wbc)
3264 CmdAutofill *me = CMD_AUTOFILL (cmd);
3265 gboolean res;
3267 g_return_val_if_fail (wbc != NULL, TRUE);
3268 g_return_val_if_fail (me != NULL, TRUE);
3269 g_return_val_if_fail (me->contents != NULL, TRUE);
3271 res = clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc));
3272 cellregion_unref (me->contents);
3273 me->contents = NULL;
3275 if (me->old_widths) {
3276 colrow_restore_state_group (me->cmd.sheet, TRUE,
3277 me->columns,
3278 me->old_widths);
3279 colrow_state_group_destroy (me->old_widths);
3280 me->old_widths = NULL;
3281 colrow_index_list_destroy (me->columns);
3282 me->columns = NULL;
3285 if (res)
3286 return TRUE;
3288 select_range (me->dst.sheet, &me->src, wbc);
3290 return FALSE;
3293 static gboolean
3294 cmd_autofill_redo (GnmCommand *cmd, WorkbookControl *wbc)
3296 CmdAutofill *me = CMD_AUTOFILL (cmd);
3297 GnmRange r;
3299 g_return_val_if_fail (me != NULL, TRUE);
3300 g_return_val_if_fail (me->contents == NULL, TRUE);
3302 me->contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
3304 g_return_val_if_fail (me->contents != NULL, TRUE);
3306 /* FIXME : when we split autofill to support hints and better validation
3307 * move this in there.
3309 /* MW: May 2006: we support hints now. What's this about? */
3310 sheet_clear_region (me->dst.sheet,
3311 me->dst.range.start.col, me->dst.range.start.row,
3312 me->dst.range.end.col, me->dst.range.end.row,
3313 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3314 GO_CMD_CONTEXT (wbc));
3316 if (me->cmd.size == 1)
3317 me->cmd.size += cellregion_cmd_size (me->contents);
3318 if (me->inverse_autofill)
3319 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3320 me->end_col, me->end_row, me->w, me->h,
3321 me->base_col, me->base_row);
3322 else
3323 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3324 me->base_col, me->base_row, me->w, me->h,
3325 me->end_col, me->end_row);
3327 colrow_autofit (me->cmd.sheet, &me->dst.range, TRUE, TRUE,
3328 TRUE, FALSE,
3329 &me->columns, &me->old_widths);
3331 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3332 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3333 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3335 r = range_union (&me->dst.range, &me->src);
3336 select_range (me->dst.sheet, &r, wbc);
3338 return FALSE;
3341 static void
3342 cmd_autofill_finalize (GObject *cmd)
3344 CmdAutofill *me = CMD_AUTOFILL (cmd);
3346 if (me->contents) {
3347 cellregion_unref (me->contents);
3348 me->contents = NULL;
3350 colrow_index_list_destroy (me->columns);
3351 colrow_state_group_destroy (me->old_widths);
3352 gnm_command_finalize (cmd);
3355 gboolean
3356 cmd_autofill (WorkbookControl *wbc, Sheet *sheet,
3357 gboolean default_increment,
3358 int base_col, int base_row,
3359 int w, int h, int end_col, int end_row,
3360 gboolean inverse_autofill)
3362 CmdAutofill *me;
3363 GnmRange target, src;
3365 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3367 /* This would be meaningless */
3368 if (base_col+w-1 == end_col && base_row+h-1 == end_row)
3369 return FALSE;
3371 if (inverse_autofill) {
3372 if (end_col != base_col + w - 1) {
3373 range_init (&target, base_col, base_row,
3374 end_col - w, end_row);
3375 range_init (&src, end_col - w + 1, base_row,
3376 end_col, end_row);
3377 } else {
3378 range_init (&target, base_col, base_row,
3379 end_col, end_row - h);
3380 range_init (&src, base_col, end_row - h + 1,
3381 end_col, end_row);
3383 } else {
3384 if (end_col != base_col + w - 1) {
3385 range_init (&target, base_col + w, base_row,
3386 end_col, end_row);
3387 range_init (&src, base_col, base_row,
3388 base_col + w - 1, end_row);
3389 } else {
3390 range_init (&target, base_col, base_row + h,
3391 end_col, end_row);
3392 range_init (&src, base_col, base_row,
3393 end_col, base_row + h - 1);
3397 /* We don't support clearing regions, when a user uses the autofill
3398 * cursor to 'shrink' a selection
3400 if (target.start.col > target.end.col || target.start.row > target.end.row)
3401 return TRUE;
3403 /* Check arrays or merged regions in src or target regions */
3404 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")) ||
3405 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")))
3406 return TRUE;
3408 me = g_object_new (CMD_AUTOFILL_TYPE, NULL);
3410 me->contents = NULL;
3411 me->dst.sheet = sheet;
3412 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3413 me->dst.range = target;
3414 me->src = src;
3416 me->base_col = base_col;
3417 me->base_row = base_row,
3418 me->w = w;
3419 me->h = h;
3420 me->end_col = end_col;
3421 me->end_row = end_row;
3422 me->default_increment = default_increment;
3423 me->inverse_autofill = inverse_autofill;
3425 me->cmd.sheet = sheet;
3426 me->cmd.size = 1; /* Changed in initial redo. */
3427 me->cmd.cmd_descriptor = g_strdup_printf (_("Autofilling %s"),
3428 range_as_string (&me->dst.range));
3430 return gnm_command_push_undo (wbc, G_OBJECT (me));
3433 /******************************************************************/
3435 #define CMD_COPYREL_TYPE (cmd_copyrel_get_type ())
3436 #define CMD_COPYREL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COPYREL_TYPE, CmdCopyRel))
3438 typedef struct {
3439 GnmCommand cmd;
3441 GOUndo *undo;
3442 GnmPasteTarget dst, src;
3443 int dx, dy;
3444 char const *name;
3445 } CmdCopyRel;
3447 static void
3448 cmd_copyrel_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3450 CmdCopyRel const *orig = (CmdCopyRel const *) cmd;
3451 cmd_copyrel (wbc, orig->dx, orig->dy, orig->name);
3453 MAKE_GNM_COMMAND (CmdCopyRel, cmd_copyrel, cmd_copyrel_repeat)
3455 static gboolean
3456 cmd_copyrel_undo (GnmCommand *cmd, WorkbookControl *wbc)
3458 CmdCopyRel *me = CMD_COPYREL (cmd);
3460 g_return_val_if_fail (wbc != NULL, TRUE);
3461 g_return_val_if_fail (me != NULL, TRUE);
3462 g_return_val_if_fail (me->undo != NULL, TRUE);
3464 go_undo_undo (me->undo);
3466 /* Select the newly pasted contents (this queues a redraw) */
3467 select_range (me->dst.sheet, &me->dst.range, wbc);
3469 return FALSE;
3472 static gboolean
3473 cmd_copyrel_redo (GnmCommand *cmd, WorkbookControl *wbc)
3475 CmdCopyRel *me = CMD_COPYREL (cmd);
3476 GnmCellRegion *contents;
3477 gboolean res;
3479 g_return_val_if_fail (me != NULL, TRUE);
3481 sheet_clear_region (me->dst.sheet,
3482 me->dst.range.start.col, me->dst.range.start.row,
3483 me->dst.range.end.col, me->dst.range.end.row,
3484 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3485 GO_CMD_CONTEXT (wbc));
3487 contents = clipboard_copy_range (me->src.sheet, &me->src.range);
3488 res = clipboard_paste_region (contents, &me->dst, GO_CMD_CONTEXT (wbc));
3489 cellregion_unref (contents);
3490 if (res)
3491 return TRUE;
3493 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3494 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3495 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3497 /* Select the newly pasted contents (this queues a redraw) */
3498 select_range (me->dst.sheet, &me->dst.range, wbc);
3500 return FALSE;
3503 static void
3504 cmd_copyrel_finalize (GObject *cmd)
3506 CmdCopyRel *me = CMD_COPYREL (cmd);
3508 if (me->undo)
3509 g_object_unref (me->undo);
3511 gnm_command_finalize (cmd);
3514 gboolean
3515 cmd_copyrel (WorkbookControl *wbc,
3516 int dx, int dy,
3517 char const *name)
3519 CmdCopyRel *me;
3520 GnmRange target, src;
3521 SheetView *sv = wb_control_cur_sheet_view (wbc);
3522 Sheet *sheet = sv->sheet;
3523 GnmRange const *selr =
3524 selection_first_range (sv, GO_CMD_CONTEXT (wbc), name);
3526 g_return_val_if_fail (dx == 0 || dy == 0, TRUE);
3528 if (!selr)
3529 return FALSE;
3531 target = *selr;
3532 range_normalize (&target);
3533 src.start = src.end = target.start;
3535 if (dy) {
3536 src.end.col = target.end.col;
3537 if (target.start.row != target.end.row)
3538 target.start.row++;
3539 else
3540 src.start.row = src.end.row = (target.start.row + dy);
3543 if (dx) {
3544 src.end.row = target.end.row;
3545 if (target.start.col != target.end.col)
3546 target.start.col++;
3547 else
3548 src.start.col = src.end.col = (target.start.col + dx);
3551 if (src.start.col < 0 || src.start.col >= gnm_sheet_get_max_cols (sheet) ||
3552 src.start.row < 0 || src.start.row >= gnm_sheet_get_max_rows (sheet))
3553 return FALSE;
3555 /* Check arrays or merged regions in src or target regions */
3556 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), name) ||
3557 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), name))
3558 return TRUE;
3560 me = g_object_new (CMD_COPYREL_TYPE, NULL);
3562 me->dst.sheet = sheet;
3563 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3564 me->dst.range = target;
3565 me->src.sheet = sheet;
3566 me->src.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3567 me->src.range = src;
3568 me->dx = dx;
3569 me->dy = dy;
3570 me->name = name;
3571 me->undo = clipboard_copy_range_undo (me->dst.sheet, &me->dst.range);
3573 me->cmd.sheet = sheet;
3574 me->cmd.size = 1;
3575 me->cmd.cmd_descriptor = g_strdup (name);
3577 return gnm_command_push_undo (wbc, G_OBJECT (me));
3580 /******************************************************************/
3583 #define CMD_AUTOFORMAT_TYPE (cmd_autoformat_get_type ())
3584 #define CMD_AUTOFORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFORMAT_TYPE, CmdAutoFormat))
3586 typedef struct {
3587 GnmCellPos pos;
3588 GnmStyleList *styles;
3589 } CmdAutoFormatOldStyle;
3591 typedef struct {
3592 GnmCommand cmd;
3594 GSList *selection; /* Selections on the sheet */
3595 GSList *old_styles; /* Older styles, one style_list per selection range*/
3597 GnmFT *ft; /* Template that has been applied */
3598 } CmdAutoFormat;
3600 static void
3601 cmd_autoformat_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3603 CmdAutoFormat const *orig = (CmdAutoFormat const *) cmd;
3604 cmd_selection_autoformat (wbc, gnm_ft_clone (orig->ft));
3606 MAKE_GNM_COMMAND (CmdAutoFormat, cmd_autoformat, cmd_autoformat_repeat)
3608 static gboolean
3609 cmd_autoformat_undo (GnmCommand *cmd,
3610 G_GNUC_UNUSED WorkbookControl *wbc)
3612 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3614 g_return_val_if_fail (me != NULL, TRUE);
3616 if (me->old_styles) {
3617 GSList *l1 = me->old_styles;
3618 GSList *l2 = me->selection;
3620 for (; l1; l1 = l1->next, l2 = l2->next) {
3621 GnmRange *r;
3622 CmdAutoFormatOldStyle *os = l1->data;
3623 GnmSpanCalcFlags flags = sheet_style_set_list (me->cmd.sheet,
3624 &os->pos, os->styles, NULL, NULL);
3626 g_return_val_if_fail (l2 && l2->data, TRUE);
3628 r = l2->data;
3629 sheet_range_calc_spans (me->cmd.sheet, r, flags);
3630 if (flags != GNM_SPANCALC_SIMPLE)
3631 rows_height_update (me->cmd.sheet, r, TRUE);
3635 return FALSE;
3638 static gboolean
3639 cmd_autoformat_redo (GnmCommand *cmd,
3640 G_GNUC_UNUSED WorkbookControl *wbc)
3642 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3644 g_return_val_if_fail (me != NULL, TRUE);
3646 gnm_ft_apply_to_sheet_regions (me->ft,
3647 me->cmd.sheet, me->selection);
3649 return FALSE;
3652 static void
3653 cmd_autoformat_finalize (GObject *cmd)
3655 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3657 if (me->old_styles != NULL) {
3658 GSList *l;
3660 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
3661 CmdAutoFormatOldStyle *os = l->data;
3663 if (os->styles)
3664 style_list_free (os->styles);
3666 g_free (os);
3669 me->old_styles = NULL;
3672 range_fragment_free (me->selection);
3673 me->selection = NULL;
3675 gnm_ft_free (me->ft);
3677 gnm_command_finalize (cmd);
3681 * cmd_selection_autoformat:
3682 * @wbc: the context.
3683 * @ft: The format template that was applied
3685 * Return value: TRUE if there was a problem
3687 gboolean
3688 cmd_selection_autoformat (WorkbookControl *wbc, GnmFT *ft)
3690 CmdAutoFormat *me;
3691 char *names;
3692 GSList *l;
3693 SheetView *sv = wb_control_cur_sheet_view (wbc);
3695 me = g_object_new (CMD_AUTOFORMAT_TYPE, NULL);
3697 me->selection = selection_get_ranges (sv, FALSE); /* Regions may overlap */
3698 me->ft = ft;
3699 me->cmd.sheet = sv_sheet (sv);
3700 me->cmd.size = 1; /* FIXME? */
3702 if (!gnm_ft_check_valid (ft, me->selection, GO_CMD_CONTEXT (wbc))) {
3703 g_object_unref (me);
3704 return TRUE;
3707 me->old_styles = NULL;
3708 for (l = me->selection; l; l = l->next) {
3709 CmdFormatOldStyle *os;
3710 GnmRange range = *((GnmRange const *) l->data);
3712 /* Store the containing range to handle borders */
3713 if (range.start.col > 0) range.start.col--;
3714 if (range.start.row > 0) range.start.row--;
3715 if (range.end.col < gnm_sheet_get_last_col (sv->sheet)) range.end.col++;
3716 if (range.end.row < gnm_sheet_get_last_row (sv->sheet)) range.end.row++;
3718 os = g_new (CmdFormatOldStyle, 1);
3720 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
3721 os->pos = range.start;
3723 me->old_styles = g_slist_append (me->old_styles, os);
3726 names = undo_range_list_name (me->cmd.sheet, me->selection);
3727 me->cmd.cmd_descriptor = g_strdup_printf (_("Autoformatting %s"),
3728 names);
3729 g_free (names);
3731 return gnm_command_push_undo (wbc, G_OBJECT (me));
3734 /******************************************************************/
3736 #define CMD_UNMERGE_CELLS_TYPE (cmd_unmerge_cells_get_type ())
3737 #define CMD_UNMERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_UNMERGE_CELLS_TYPE, CmdUnmergeCells))
3739 typedef struct {
3740 GnmCommand cmd;
3742 Sheet *sheet;
3743 GArray *unmerged_regions;
3744 GArray *ranges;
3745 } CmdUnmergeCells;
3747 static void
3748 cmd_unmerge_cells_repeat (G_GNUC_UNUSED GnmCommand const *cmd, WorkbookControl *wbc)
3750 SheetView *sv = wb_control_cur_sheet_view (wbc);
3751 GSList *range_list = selection_get_ranges (sv, FALSE);
3752 cmd_unmerge_cells (wbc, sv_sheet (sv), range_list);
3753 range_fragment_free (range_list);
3755 MAKE_GNM_COMMAND (CmdUnmergeCells, cmd_unmerge_cells, cmd_unmerge_cells_repeat)
3757 static gboolean
3758 cmd_unmerge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3760 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3761 unsigned i;
3763 g_return_val_if_fail (me != NULL, TRUE);
3764 g_return_val_if_fail (me->unmerged_regions != NULL, TRUE);
3766 for (i = 0 ; i < me->unmerged_regions->len ; ++i) {
3767 GnmRange const *tmp = &(g_array_index (me->unmerged_regions, GnmRange, i));
3768 sheet_redraw_range (me->cmd.sheet, tmp);
3769 gnm_sheet_merge_add (me->cmd.sheet, tmp, TRUE, GO_CMD_CONTEXT (wbc));
3770 sheet_range_calc_spans (me->cmd.sheet, tmp, GNM_SPANCALC_RE_RENDER);
3773 g_array_free (me->unmerged_regions, TRUE);
3774 me->unmerged_regions = NULL;
3776 return FALSE;
3779 static gboolean
3780 cmd_unmerge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3782 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3783 unsigned i;
3785 g_return_val_if_fail (me != NULL, TRUE);
3786 g_return_val_if_fail (me->unmerged_regions == NULL, TRUE);
3788 me->unmerged_regions = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3789 for (i = 0 ; i < me->ranges->len ; ++i) {
3790 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (me->cmd.sheet,
3791 &(g_array_index (me->ranges, GnmRange, i)));
3792 for (ptr = merged ; ptr != NULL ; ptr = ptr->next) {
3793 GnmRange const *pr = ptr->data;
3794 GnmRange const tmp = *pr;
3795 g_array_append_val (me->unmerged_regions, tmp);
3796 gnm_sheet_merge_remove (me->cmd.sheet, &tmp);
3797 sheet_range_calc_spans (me->cmd.sheet, &tmp,
3798 GNM_SPANCALC_RE_RENDER);
3800 g_slist_free (merged);
3803 return FALSE;
3806 static void
3807 cmd_unmerge_cells_finalize (GObject *cmd)
3809 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3811 if (me->unmerged_regions != NULL) {
3812 g_array_free (me->unmerged_regions, TRUE);
3813 me->unmerged_regions = NULL;
3815 if (me->ranges != NULL) {
3816 g_array_free (me->ranges, TRUE);
3817 me->ranges = NULL;
3820 gnm_command_finalize (cmd);
3824 * cmd_unmerge_cells:
3825 * @wbc: the context.
3826 * @sheet: #Sheet
3827 * @selection: (element-type GnmRange): selection.
3829 * Return value: TRUE if there was a problem
3831 gboolean
3832 cmd_unmerge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection)
3834 CmdUnmergeCells *me;
3835 char *names;
3837 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3839 me = g_object_new (CMD_UNMERGE_CELLS_TYPE, NULL);
3841 me->cmd.sheet = sheet;
3842 me->cmd.size = 1;
3844 names = undo_range_list_name (sheet, selection);
3845 me->cmd.cmd_descriptor = g_strdup_printf (_("Unmerging %s"), names);
3846 g_free (names);
3848 me->unmerged_regions = NULL;
3849 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3850 for ( ; selection != NULL ; selection = selection->next) {
3851 GSList *merged = gnm_sheet_merge_get_overlap (sheet, selection->data);
3852 if (merged != NULL) {
3853 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
3854 g_slist_free (merged);
3858 if (me->ranges->len <= 0) {
3859 g_object_unref (me);
3860 return TRUE;
3863 return gnm_command_push_undo (wbc, G_OBJECT (me));
3866 /******************************************************************/
3868 #define CMD_MERGE_CELLS_TYPE (cmd_merge_cells_get_type ())
3869 #define CMD_MERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_CELLS_TYPE, CmdMergeCells))
3871 typedef struct {
3872 GnmCommand cmd;
3873 GArray *ranges;
3874 GSList *old_contents;
3875 gboolean center;
3876 } CmdMergeCells;
3878 static void
3879 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc);
3881 MAKE_GNM_COMMAND (CmdMergeCells, cmd_merge_cells, cmd_merge_cells_repeat)
3883 static void
3884 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3886 SheetView *sv = wb_control_cur_sheet_view (wbc);
3887 GSList *range_list = selection_get_ranges (sv, FALSE);
3888 cmd_merge_cells (wbc, sv_sheet (sv), range_list,
3889 CMD_MERGE_CELLS (cmd)->center);
3890 range_fragment_free (range_list);
3893 static gboolean
3894 cmd_merge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3896 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3897 unsigned i, flags;
3899 g_return_val_if_fail (me != NULL, TRUE);
3901 for (i = 0 ; i < me->ranges->len ; ++i) {
3902 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3903 gnm_sheet_merge_remove (me->cmd.sheet, r);
3906 /* Avoid pasting comments that are at 0,0. Redo copies the target
3907 * region (including all comments) . If there was a comment in the top
3908 * left we would end up duplicating it. */
3909 flags = PASTE_CONTENTS | PASTE_FORMATS | PASTE_COMMENTS |
3910 PASTE_IGNORE_COMMENTS_AT_ORIGIN;
3911 if (me->center)
3912 flags |= PASTE_FORMATS;
3913 for (i = 0 ; i < me->ranges->len ; ++i) {
3914 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3915 GnmPasteTarget pt;
3916 GnmCellRegion * c;
3918 g_return_val_if_fail (me->old_contents != NULL, TRUE);
3920 c = me->old_contents->data;
3921 clipboard_paste_region (c,
3922 paste_target_init (&pt, me->cmd.sheet, r, flags),
3923 GO_CMD_CONTEXT (wbc));
3924 cellregion_unref (c);
3925 me->old_contents = g_slist_remove (me->old_contents, c);
3927 g_return_val_if_fail (me->old_contents == NULL, TRUE);
3929 return FALSE;
3932 static gboolean
3933 cmd_merge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3935 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3936 GnmStyle *align_center = NULL;
3937 Sheet *sheet;
3938 unsigned i;
3940 g_return_val_if_fail (me != NULL, TRUE);
3942 if (me->center) {
3943 align_center = gnm_style_new ();
3944 gnm_style_set_align_h (align_center, GNM_HALIGN_CENTER);
3946 sheet = me->cmd.sheet;
3947 for (i = 0 ; i < me->ranges->len ; ++i) {
3948 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3949 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (sheet, r);
3951 /* save contents before removing contained merged regions */
3952 me->old_contents = g_slist_prepend (me->old_contents,
3953 clipboard_copy_range (sheet, r));
3954 for (ptr = merged ; ptr != NULL ; ptr = ptr->next)
3955 gnm_sheet_merge_remove (sheet, ptr->data);
3956 g_slist_free (merged);
3958 gnm_sheet_merge_add (sheet, r, TRUE, GO_CMD_CONTEXT (wbc));
3959 if (me->center)
3960 sheet_apply_style (me->cmd.sheet, r, align_center);
3963 if (me->center)
3964 gnm_style_unref (align_center);
3965 me->old_contents = g_slist_reverse (me->old_contents);
3966 return FALSE;
3969 static void
3970 cmd_merge_cells_finalize (GObject *cmd)
3972 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3974 if (me->old_contents != NULL) {
3975 GSList *l;
3976 for (l = me->old_contents ; l != NULL ; l = g_slist_remove (l, l->data))
3977 cellregion_unref (l->data);
3978 me->old_contents = NULL;
3981 if (me->ranges != NULL) {
3982 g_array_free (me->ranges, TRUE);
3983 me->ranges = NULL;
3986 gnm_command_finalize (cmd);
3990 * cmd_merge_cells:
3991 * @wbc: the context.
3992 * @sheet: #Sheet
3993 * @selection: (element-type GnmRange): selection.
3994 * @center:
3996 * Return value: %TRUE if there was a problem
3998 gboolean
3999 cmd_merge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection,
4000 gboolean center)
4002 CmdMergeCells *me;
4003 char *names;
4005 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4007 me = g_object_new (CMD_MERGE_CELLS_TYPE, NULL);
4009 me->cmd.sheet = sheet;
4010 me->cmd.size = 1;
4012 names = undo_range_list_name (sheet, selection);
4013 me->cmd.cmd_descriptor =
4014 g_strdup_printf ((center ? _("Merge and Center %s") :_("Merging %s")), names);
4015 g_free (names);
4017 me->center = center;
4018 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
4019 for ( ; selection != NULL ; selection = selection->next) {
4020 GnmRange const *exist;
4021 GnmRange const *r = selection->data;
4022 if (range_is_singleton (selection->data))
4023 continue;
4024 if (NULL != (exist = gnm_sheet_merge_is_corner (sheet, &r->start)) &&
4025 range_equal (r, exist))
4026 continue;
4027 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
4030 if (me->ranges->len <= 0) {
4031 g_object_unref (me);
4032 return TRUE;
4035 return gnm_command_push_undo (wbc, G_OBJECT (me));
4038 /******************************************************************/
4040 #define CMD_SEARCH_REPLACE_TYPE (cmd_search_replace_get_type())
4041 #define CMD_SEARCH_REPLACE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SEARCH_REPLACE_TYPE, CmdSearchReplace))
4043 typedef struct {
4044 GnmCommand cmd;
4045 GnmSearchReplace *sr;
4048 * Undo/redo use this list of SearchReplaceItems to do their
4049 * work. Note, that it is possible for a cell to occur
4050 * multiple times in the list.
4052 GList *cells;
4053 } CmdSearchReplace;
4055 MAKE_GNM_COMMAND (CmdSearchReplace, cmd_search_replace, NULL)
4057 typedef enum { SRI_text, SRI_comment } SearchReplaceItemType;
4059 typedef struct {
4060 GnmEvalPos pos;
4061 SearchReplaceItemType old_type, new_type;
4062 union {
4063 char *text;
4064 char *comment;
4065 } old, new;
4066 } SearchReplaceItem;
4069 static void
4070 cmd_search_replace_update_after_action (CmdSearchReplace *me,
4071 WorkbookControl *wbc)
4073 GList *tmp;
4074 Sheet *last_sheet = NULL;
4076 for (tmp = me->cells; tmp; tmp = tmp->next) {
4077 SearchReplaceItem *sri = tmp->data;
4078 if (sri->pos.sheet != last_sheet) {
4079 last_sheet = sri->pos.sheet;
4080 update_after_action (last_sheet, wbc);
4086 static gboolean
4087 cmd_search_replace_undo (GnmCommand *cmd,
4088 WorkbookControl *wbc)
4090 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4091 GList *tmp;
4093 /* Undo does replacements backwards. */
4094 for (tmp = g_list_last (me->cells); tmp; tmp = tmp->prev) {
4095 SearchReplaceItem *sri = tmp->data;
4096 switch (sri->old_type) {
4097 case SRI_text:
4099 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4100 sri->pos.eval.col,
4101 sri->pos.eval.row);
4102 sheet_cell_set_text (cell, sri->old.text, NULL);
4103 break;
4105 case SRI_comment:
4107 GnmComment *comment =
4108 sheet_get_comment (sri->pos.sheet,
4109 &sri->pos.eval);
4110 if (comment) {
4111 cell_comment_text_set (comment, sri->old.comment);
4112 } else {
4113 g_warning ("Undo/redo broken.");
4116 break;
4119 cmd_search_replace_update_after_action (me, wbc);
4121 return FALSE;
4124 static gboolean
4125 cmd_search_replace_redo (GnmCommand *cmd,
4126 WorkbookControl *wbc)
4128 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4129 GList *tmp;
4131 /* Redo does replacements forward. */
4132 for (tmp = me->cells; tmp; tmp = tmp->next) {
4133 SearchReplaceItem *sri = tmp->data;
4134 switch (sri->new_type) {
4135 case SRI_text:
4137 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4138 sri->pos.eval.col,
4139 sri->pos.eval.row);
4140 sheet_cell_set_text (cell, sri->new.text, NULL);
4141 break;
4143 case SRI_comment:
4145 GnmComment *comment =
4146 sheet_get_comment (sri->pos.sheet,
4147 &sri->pos.eval);
4148 if (comment) {
4149 cell_comment_text_set (comment, sri->new.comment);
4150 } else {
4151 g_warning ("Undo/redo broken.");
4154 break;
4157 cmd_search_replace_update_after_action (me, wbc);
4159 return FALSE;
4162 static gboolean
4163 cmd_search_replace_do_cell (CmdSearchReplace *me, GnmEvalPos *ep,
4164 gboolean test_run)
4166 GnmSearchReplace *sr = me->sr;
4168 GnmSearchReplaceCellResult cell_res;
4169 GnmSearchReplaceCommentResult comment_res;
4171 if (gnm_search_replace_cell (sr, ep, TRUE, &cell_res)) {
4172 GnmExprTop const *texpr;
4173 GnmValue *val;
4174 gboolean err;
4175 GnmParsePos pp;
4177 parse_pos_init_evalpos (&pp, ep);
4178 parse_text_value_or_expr (&pp, cell_res.new_text, &val, &texpr);
4181 * FIXME: this is a hack, but parse_text_value_or_expr
4182 * does not have a better way of signaling an error.
4184 err = (val &&
4185 gnm_expr_char_start_p (cell_res.new_text) &&
4186 !go_format_is_text (gnm_cell_get_format (cell_res.cell)));
4187 value_release (val);
4188 if (texpr) gnm_expr_top_unref (texpr);
4190 if (err) {
4191 if (test_run) {
4192 gnm_search_replace_query_fail (sr, &cell_res);
4193 g_free (cell_res.old_text);
4194 g_free (cell_res.new_text);
4195 return TRUE;
4196 } else {
4197 switch (sr->error_behaviour) {
4198 case GNM_SRE_ERROR: {
4199 GnmExprTop const *ee =
4200 gnm_expr_top_new
4201 (gnm_expr_new_funcall1
4202 (gnm_func_lookup ("ERROR", NULL),
4203 gnm_expr_new_constant
4204 (value_new_string_nocopy (cell_res.new_text))));
4205 GnmConventionsOut out;
4207 out.accum = g_string_new ("=");
4208 out.pp = &pp;
4209 out.convs = pp.sheet->convs;
4210 gnm_expr_top_as_gstring (ee, &out);
4211 gnm_expr_top_unref (ee);
4212 cell_res.new_text = g_string_free (out.accum, FALSE);
4213 err = FALSE;
4214 break;
4216 case GNM_SRE_STRING: {
4217 GString *s = g_string_new ("'");
4218 g_string_append (s, cell_res.new_text);
4219 g_free (cell_res.new_text);
4220 cell_res.new_text = g_string_free (s, FALSE);
4221 err = FALSE;
4222 break;
4224 case GNM_SRE_FAIL:
4225 g_assert_not_reached ();
4226 case GNM_SRE_SKIP:
4227 default:
4228 ; /* Nothing */
4233 if (!err && !test_run) {
4234 int res = gnm_search_replace_query_cell
4235 (sr, &cell_res);
4236 gboolean doit = (res == GTK_RESPONSE_YES);
4238 if (res == GTK_RESPONSE_CANCEL) {
4239 g_free (cell_res.old_text);
4240 g_free (cell_res.new_text);
4241 return TRUE;
4244 if (doit) {
4245 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4247 sheet_cell_set_text (cell_res.cell, cell_res.new_text, NULL);
4249 sri->pos = *ep;
4250 sri->old_type = sri->new_type = SRI_text;
4251 sri->old.text = cell_res.old_text;
4252 sri->new.text = cell_res.new_text;
4253 me->cells = g_list_prepend (me->cells, sri);
4255 cell_res.old_text = cell_res.new_text = NULL;
4259 g_free (cell_res.new_text);
4260 g_free (cell_res.old_text);
4263 if (!test_run &&
4264 gnm_search_replace_comment (sr, ep, TRUE, &comment_res)) {
4265 int res = gnm_search_replace_query_comment
4266 (sr, ep, &comment_res);
4267 gboolean doit = (res == GTK_RESPONSE_YES);
4269 if (doit) {
4270 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4271 sri->pos = *ep;
4272 sri->old_type = sri->new_type = SRI_comment;
4273 sri->old.comment = g_strdup (comment_res.old_text);
4274 sri->new.comment = comment_res.new_text;
4275 me->cells = g_list_prepend (me->cells, sri);
4277 cell_comment_text_set (comment_res.comment, comment_res.new_text);
4278 } else {
4279 g_free (comment_res.new_text);
4280 if (res == GTK_RESPONSE_CANCEL)
4281 return TRUE;
4285 return FALSE;
4289 static gboolean
4290 cmd_search_replace_do (CmdSearchReplace *me, gboolean test_run,
4291 WorkbookControl *wbc)
4293 GnmSearchReplace *sr = me->sr;
4294 GPtrArray *cells;
4295 gboolean result = FALSE;
4296 unsigned i;
4298 if (test_run) {
4299 switch (sr->error_behaviour) {
4300 case GNM_SRE_SKIP:
4301 case GNM_SRE_QUERY:
4302 case GNM_SRE_ERROR:
4303 case GNM_SRE_STRING:
4304 /* An error is not a problem. */
4305 return FALSE;
4307 case GNM_SRE_FAIL:
4308 ; /* Nothing. */
4312 cells = gnm_search_collect_cells (sr);
4314 for (i = 0; i < cells->len; i++) {
4315 GnmEvalPos *ep = g_ptr_array_index (cells, i);
4317 if (cmd_search_replace_do_cell (me, ep, test_run)) {
4318 result = TRUE;
4319 break;
4323 gnm_search_collect_cells_free (cells);
4325 if (!test_run) {
4326 /* Cells were added in the wrong order. Correct. */
4327 me->cells = g_list_reverse (me->cells);
4329 cmd_search_replace_update_after_action (me, wbc);
4332 return result;
4336 static void
4337 cmd_search_replace_finalize (GObject *cmd)
4339 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4340 GList *tmp;
4342 for (tmp = me->cells; tmp; tmp = tmp->next) {
4343 SearchReplaceItem *sri = tmp->data;
4344 switch (sri->old_type) {
4345 case SRI_text:
4346 g_free (sri->old.text);
4347 break;
4348 case SRI_comment:
4349 g_free (sri->old.comment);
4350 break;
4352 switch (sri->new_type) {
4353 case SRI_text:
4354 g_free (sri->new.text);
4355 break;
4356 case SRI_comment:
4357 g_free (sri->new.comment);
4358 break;
4360 g_free (sri);
4362 g_list_free (me->cells);
4363 g_object_unref (me->sr);
4365 gnm_command_finalize (cmd);
4368 gboolean
4369 cmd_search_replace (WorkbookControl *wbc, GnmSearchReplace *sr)
4371 CmdSearchReplace *me;
4373 g_return_val_if_fail (sr != NULL, TRUE);
4375 me = g_object_new (CMD_SEARCH_REPLACE_TYPE, NULL);
4377 me->cells = NULL;
4378 me->sr = g_object_ref (sr);
4380 me->cmd.sheet = NULL;
4381 me->cmd.size = 1; /* Corrected below. */
4382 me->cmd.cmd_descriptor = g_strdup (_("Search and Replace"));
4384 if (cmd_search_replace_do (me, TRUE, wbc)) {
4385 /* There was an error and nothing was done. */
4386 g_object_unref (me);
4387 return TRUE;
4390 cmd_search_replace_do (me, FALSE, wbc);
4391 me->cmd.size += g_list_length (me->cells);
4393 command_register_undo (wbc, G_OBJECT (me));
4394 return FALSE;
4397 /******************************************************************/
4399 #define CMD_COLROW_STD_SIZE_TYPE (cmd_colrow_std_size_get_type ())
4400 #define CMD_COLROW_STD_SIZE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_STD_SIZE_TYPE, CmdColRowStdSize))
4402 typedef struct {
4403 GnmCommand cmd;
4405 Sheet *sheet;
4406 gboolean is_cols;
4407 double new_default;
4408 double old_default;
4409 } CmdColRowStdSize;
4411 MAKE_GNM_COMMAND (CmdColRowStdSize, cmd_colrow_std_size, NULL)
4413 static gboolean
4414 cmd_colrow_std_size_undo (GnmCommand *cmd,
4415 G_GNUC_UNUSED WorkbookControl *wbc)
4417 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4419 g_return_val_if_fail (me != NULL, TRUE);
4420 g_return_val_if_fail (me->old_default != 0, TRUE);
4422 if (me->is_cols)
4423 sheet_col_set_default_size_pts (me->sheet, me->old_default);
4424 else
4425 sheet_row_set_default_size_pts (me->sheet, me->old_default);
4427 me->old_default = 0;
4429 return FALSE;
4432 static gboolean
4433 cmd_colrow_std_size_redo (GnmCommand *cmd,
4434 G_GNUC_UNUSED WorkbookControl *wbc)
4436 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4438 g_return_val_if_fail (me != NULL, TRUE);
4439 g_return_val_if_fail (me->old_default == 0, TRUE);
4441 if (me->is_cols) {
4442 me->old_default = sheet_col_get_default_size_pts (me->sheet);
4443 sheet_col_set_default_size_pts (me->sheet, me->new_default);
4444 } else {
4445 me->old_default = sheet_row_get_default_size_pts (me->sheet);
4446 sheet_row_set_default_size_pts (me->sheet, me->new_default);
4449 return FALSE;
4451 static void
4452 cmd_colrow_std_size_finalize (GObject *cmd)
4454 gnm_command_finalize (cmd);
4457 gboolean
4458 cmd_colrow_std_size (WorkbookControl *wbc, Sheet *sheet,
4459 gboolean is_cols, double new_default)
4461 CmdColRowStdSize *me;
4463 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4465 me = g_object_new (CMD_COLROW_STD_SIZE_TYPE, NULL);
4467 me->sheet = sheet;
4468 me->is_cols = is_cols;
4469 me->new_default = new_default;
4470 me->old_default = 0;
4472 me->cmd.sheet = sheet;
4473 me->cmd.size = 1; /* Changed in initial redo. */
4474 me->cmd.cmd_descriptor = is_cols
4475 ? g_strdup_printf (_("Setting default width of columns to %.2fpts"), new_default)
4476 : g_strdup_printf (_("Setting default height of rows to %.2fpts"), new_default);
4478 return gnm_command_push_undo (wbc, G_OBJECT (me));
4481 /******************************************************************/
4483 #define CMD_ZOOM_TYPE (cmd_zoom_get_type ())
4484 #define CMD_ZOOM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ZOOM_TYPE, CmdZoom))
4486 typedef struct {
4487 GnmCommand cmd;
4489 GSList *sheets;
4490 double new_factor;
4491 double *old_factors;
4492 } CmdZoom;
4494 MAKE_GNM_COMMAND (CmdZoom, cmd_zoom, NULL)
4496 static gboolean
4497 cmd_zoom_undo (GnmCommand *cmd,
4498 G_GNUC_UNUSED WorkbookControl *wbc)
4500 CmdZoom *me = CMD_ZOOM (cmd);
4501 GSList *l;
4502 int i;
4504 g_return_val_if_fail (me != NULL, TRUE);
4505 g_return_val_if_fail (me->sheets != NULL, TRUE);
4506 g_return_val_if_fail (me->old_factors != NULL, TRUE);
4508 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4509 Sheet *sheet = l->data;
4510 g_object_set (sheet, "zoom-factor", me->old_factors[i], NULL);
4513 return FALSE;
4516 static gboolean
4517 cmd_zoom_redo (GnmCommand *cmd,
4518 G_GNUC_UNUSED WorkbookControl *wbc)
4520 CmdZoom *me = CMD_ZOOM (cmd);
4521 GSList *l;
4523 g_return_val_if_fail (me != NULL, TRUE);
4524 g_return_val_if_fail (me->sheets != NULL, TRUE);
4526 for (l = me->sheets; l != NULL; l = l->next) {
4527 Sheet *sheet = l->data;
4528 g_object_set (sheet, "zoom-factor", me->new_factor, NULL);
4531 return FALSE;
4534 static void
4535 cmd_zoom_finalize (GObject *cmd)
4537 CmdZoom *me = CMD_ZOOM (cmd);
4539 g_slist_free (me->sheets);
4540 g_free (me->old_factors);
4542 gnm_command_finalize (cmd);
4546 * cmd_zoom:
4547 * @wbc: #WorkbookControl
4548 * @sheets: (element-type Sheet) (transfer container):
4549 * @factor:
4552 gboolean
4553 cmd_zoom (WorkbookControl *wbc, GSList *sheets, double factor)
4555 CmdZoom *me;
4556 GString *namelist;
4557 GSList *l;
4558 int i;
4560 g_return_val_if_fail (wbc != NULL, TRUE);
4561 g_return_val_if_fail (sheets != NULL, TRUE);
4563 me = g_object_new (CMD_ZOOM_TYPE, NULL);
4565 me->sheets = sheets;
4566 me->old_factors = g_new0 (double, g_slist_length (sheets));
4567 me->new_factor = factor;
4569 /* Make a list of all sheets to zoom and save zoom factor for each */
4570 namelist = g_string_new (NULL);
4571 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4572 Sheet *sheet = l->data;
4574 g_string_append (namelist, sheet->name_unquoted);
4575 me->old_factors[i] = sheet->last_zoom_factor_used;
4577 if (l->next)
4578 g_string_append (namelist, ", ");
4581 /* Make sure the string doesn't get overly wide */
4582 gnm_cmd_trunc_descriptor (namelist, NULL);
4584 me->cmd.sheet = NULL;
4585 me->cmd.size = 1;
4586 me->cmd.cmd_descriptor =
4587 g_strdup_printf (_("Zoom %s to %.0f%%"), namelist->str, factor * 100);
4589 g_string_free (namelist, TRUE);
4591 return gnm_command_push_undo (wbc, G_OBJECT (me));
4594 /******************************************************************/
4596 #define CMD_OBJECTS_DELETE_TYPE (cmd_objects_delete_get_type ())
4597 #define CMD_OBJECTS_DELETE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECTS_DELETE_TYPE, CmdObjectsDelete))
4599 typedef struct {
4600 GnmCommand cmd;
4601 GSList *objects;
4602 GArray *location;
4603 } CmdObjectsDelete;
4605 MAKE_GNM_COMMAND (CmdObjectsDelete, cmd_objects_delete, NULL)
4607 static gboolean
4608 cmd_objects_delete_redo (GnmCommand *cmd,
4609 G_GNUC_UNUSED WorkbookControl *wbc)
4611 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4612 g_slist_foreach (me->objects, (GFunc) sheet_object_clear_sheet, NULL);
4613 return FALSE;
4616 static void
4617 cmd_objects_restore_location (SheetObject *so, gint location)
4619 gint loc = sheet_object_get_stacking (so);
4620 if (loc != location)
4621 sheet_object_adjust_stacking(so, location - loc);
4624 static gboolean
4625 cmd_objects_delete_undo (GnmCommand *cmd,
4626 G_GNUC_UNUSED WorkbookControl *wbc)
4628 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4629 GSList *l;
4630 gint i;
4632 g_slist_foreach (me->objects,
4633 (GFunc) sheet_object_set_sheet, me->cmd.sheet);
4635 for (l = me->objects, i = 0; l; l = l->next, i++)
4636 cmd_objects_restore_location (GNM_SO (l->data),
4637 g_array_index(me->location,
4638 gint, i));
4639 return FALSE;
4642 static void
4643 cmd_objects_delete_finalize (GObject *cmd)
4645 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4646 g_slist_free_full (me->objects, g_object_unref);
4647 if (me->location) {
4648 g_array_free (me->location, TRUE);
4649 me->location = NULL;
4651 gnm_command_finalize (cmd);
4654 static void
4655 cmd_objects_store_location (SheetObject *so, GArray *location)
4657 gint loc = sheet_object_get_stacking (so);
4658 g_array_append_val (location, loc);
4662 * cmd_objects_delete:
4663 * @wbc: #WorkbookControl
4664 * @objects: (element-type SheetObject) (transfer container): the objects to
4665 * delete.
4666 * @name:
4668 * Absorbs the list, adding references to the contents.
4670 gboolean
4671 cmd_objects_delete (WorkbookControl *wbc, GSList *objects,
4672 char const *name)
4674 CmdObjectsDelete *me;
4676 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4677 g_return_val_if_fail (objects != NULL, TRUE);
4679 me = g_object_new (CMD_OBJECTS_DELETE_TYPE, NULL);
4681 me->objects = objects;
4682 g_slist_foreach (me->objects, (GFunc) g_object_ref, NULL);
4684 me->location = g_array_new (FALSE, FALSE, sizeof (gint));
4685 g_slist_foreach (me->objects, (GFunc) cmd_objects_store_location,
4686 me->location);
4688 me->cmd.sheet = sheet_object_get_sheet (objects->data);
4689 me->cmd.size = 1;
4690 me->cmd.cmd_descriptor = g_strdup (name ? name : _("Delete Object"));
4692 return gnm_command_push_undo (wbc, G_OBJECT (me));
4695 /******************************************************************/
4698 * cmd_objects_move:
4699 * @wbc: #WorkbookControl
4700 * @objects: (element-type SheetObject) (transfer container): the objects to move.
4701 * @anchors: (element-type SheetObjectAnchor) (transfer full): the anchors for the objects.
4702 * @objects_created:
4703 * @name:
4706 gboolean
4707 cmd_objects_move (WorkbookControl *wbc, GSList *objects, GSList *anchors,
4708 gboolean objects_created, char const *name)
4710 GOUndo *undo = NULL;
4711 GOUndo *redo = NULL;
4712 gboolean result = TRUE;
4714 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4716 undo = sheet_object_move_undo (objects, objects_created);
4717 redo = sheet_object_move_do (objects, anchors, objects_created);
4719 result = cmd_generic (wbc, name, undo, redo);
4721 g_slist_free (objects);
4722 g_slist_free_full (anchors, g_free);
4724 return result;
4727 /******************************************************************/
4729 #define CMD_REORGANIZE_SHEETS_TYPE (cmd_reorganize_sheets_get_type ())
4730 #define CMD_REORGANIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REORGANIZE_SHEETS_TYPE, CmdReorganizeSheets))
4732 typedef struct {
4733 GnmCommand cmd;
4734 Workbook *wb;
4735 WorkbookSheetState *old;
4736 WorkbookSheetState *new;
4737 gboolean first;
4738 Sheet *undo_sheet;
4739 Sheet *redo_sheet;
4740 } CmdReorganizeSheets;
4742 MAKE_GNM_COMMAND (CmdReorganizeSheets, cmd_reorganize_sheets, NULL)
4744 static gboolean
4745 cmd_reorganize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4747 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4748 workbook_sheet_state_restore (me->wb, me->old);
4749 if (me->undo_sheet) {
4750 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4751 wb_control_sheet_focus (control, me->undo_sheet););
4753 return FALSE;
4756 static gboolean
4757 cmd_reorganize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4759 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4761 if (me->first)
4762 me->first = FALSE;
4763 else {
4764 workbook_sheet_state_restore (me->wb, me->new);
4765 if (me->redo_sheet) {
4766 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4767 wb_control_sheet_focus (control, me->redo_sheet););
4771 return FALSE;
4774 static void
4775 cmd_reorganize_sheets_finalize (GObject *cmd)
4777 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4779 if (me->old)
4780 workbook_sheet_state_free (me->old);
4781 if (me->new)
4782 workbook_sheet_state_free (me->new);
4784 gnm_command_finalize (cmd);
4787 gboolean
4788 cmd_reorganize_sheets (WorkbookControl *wbc,
4789 WorkbookSheetState *old_state,
4790 Sheet *undo_sheet)
4792 CmdReorganizeSheets *me;
4793 Workbook *wb = wb_control_get_workbook (wbc);
4795 me = g_object_new (CMD_REORGANIZE_SHEETS_TYPE, NULL);
4796 me->wb = wb;
4797 me->old = old_state;
4798 me->new = workbook_sheet_state_new (me->wb);
4799 me->first = TRUE;
4800 me->undo_sheet = undo_sheet;
4801 me->redo_sheet = wb_control_cur_sheet (wbc);
4803 me->cmd.sheet = NULL;
4804 me->cmd.size = workbook_sheet_state_size (me->old) +
4805 workbook_sheet_state_size (me->new);
4806 me->cmd.cmd_descriptor =
4807 workbook_sheet_state_diff (me->old, me->new);
4809 if (me->cmd.cmd_descriptor)
4810 return gnm_command_push_undo (wbc, G_OBJECT (me));
4812 /* No change. */
4813 g_object_unref (me);
4814 return FALSE;
4817 /******************************************************************/
4819 gboolean
4820 cmd_rename_sheet (WorkbookControl *wbc,
4821 Sheet *sheet,
4822 char const *new_name)
4824 WorkbookSheetState *old_state;
4825 Sheet *collision;
4827 g_return_val_if_fail (new_name != NULL, TRUE);
4828 g_return_val_if_fail (sheet != NULL, TRUE);
4830 if (*new_name == 0) {
4831 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), _("Sheet names must be non-empty."));
4832 return TRUE;
4835 collision = workbook_sheet_by_name (sheet->workbook, new_name);
4836 if (collision && collision != sheet) {
4837 GError *err = g_error_new (go_error_invalid(), 0,
4838 _("A workbook cannot have two sheets with the same name."));
4839 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
4840 g_error_free (err);
4841 return TRUE;
4844 old_state = workbook_sheet_state_new (sheet->workbook);
4845 g_object_set (sheet, "name", new_name, NULL);
4846 return cmd_reorganize_sheets (wbc, old_state, sheet);
4849 /******************************************************************/
4851 #define CMD_RESIZE_SHEETS_TYPE (cmd_resize_sheets_get_type ())
4852 #define CMD_RESIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESIZE_SHEETS_TYPE, CmdResizeSheets))
4854 typedef struct {
4855 GnmCommand cmd;
4856 GSList *sheets;
4857 int cols, rows;
4858 GOUndo *undo;
4859 } CmdResizeSheets;
4861 MAKE_GNM_COMMAND (CmdResizeSheets, cmd_resize_sheets, NULL)
4863 static gboolean
4864 cmd_resize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4866 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4867 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4869 go_undo_undo_with_data (me->undo, cc);
4870 g_object_unref (me->undo);
4871 me->undo = NULL;
4873 return FALSE;
4876 static gboolean
4877 cmd_resize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4879 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4880 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4881 GSList *l;
4883 for (l = me->sheets; l; l = l->next) {
4884 Sheet *sheet = l->data;
4885 gboolean err;
4886 GOUndo *u = gnm_sheet_resize (sheet, me->cols, me->rows,
4887 cc, &err);
4888 me->undo = go_undo_combine (me->undo, u);
4890 if (err) {
4891 if (me->undo)
4892 go_undo_undo_with_data (me->undo, cc);
4893 return TRUE;
4897 return FALSE;
4900 static void
4901 cmd_resize_sheets_finalize (GObject *cmd)
4903 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4905 g_slist_free (me->sheets);
4906 if (me->undo) {
4907 g_object_unref (me->undo);
4908 me->undo = NULL;
4911 gnm_command_finalize (cmd);
4915 * cmd_resize_sheets:
4916 * @wbc: #WorkbookControl
4917 * @sheets: (element-type Sheet) (transfer container): the sheets to resize.
4918 * @cols: new columns number.
4919 * @rows: new rows number.
4922 gboolean
4923 cmd_resize_sheets (WorkbookControl *wbc,
4924 GSList *sheets,
4925 int cols, int rows)
4927 CmdResizeSheets *me;
4929 me = g_object_new (CMD_RESIZE_SHEETS_TYPE, NULL);
4930 me->sheets = sheets;
4931 me->cols = cols;
4932 me->rows = rows;
4933 me->cmd.sheet = sheets ? sheets->data : NULL;
4934 me->cmd.size = 1;
4935 me->cmd.cmd_descriptor = g_strdup (_("Resizing sheet"));
4937 if (sheets &&
4938 gnm_sheet_valid_size (cols, rows))
4939 return gnm_command_push_undo (wbc, G_OBJECT (me));
4941 /* No change. */
4942 g_object_unref (me);
4943 return FALSE;
4946 /******************************************************************/
4948 #define CMD_SET_COMMENT_TYPE (cmd_set_comment_get_type ())
4949 #define CMD_SET_COMMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SET_COMMENT_TYPE, CmdSetComment))
4951 typedef struct {
4952 GnmCommand cmd;
4954 Sheet *sheet;
4955 GnmCellPos pos;
4956 gchar *new_text;
4957 gchar *old_text;
4958 gchar *new_author;
4959 gchar *old_author;
4960 PangoAttrList *old_attributes;
4961 PangoAttrList *new_attributes;
4962 } CmdSetComment;
4964 MAKE_GNM_COMMAND (CmdSetComment, cmd_set_comment, NULL)
4966 static gboolean
4967 cmd_set_comment_apply (Sheet *sheet, GnmCellPos *pos,
4968 char const *text, PangoAttrList *attributes,
4969 char const *author)
4971 GnmComment *comment;
4972 Workbook *wb = sheet->workbook;
4974 comment = sheet_get_comment (sheet, pos);
4975 if (comment) {
4976 if (text)
4977 g_object_set (G_OBJECT (comment), "text", text,
4978 "author", author,
4979 "markup", attributes, NULL);
4980 else {
4981 GnmRange const *mr;
4983 mr = gnm_sheet_merge_contains_pos (sheet, pos);
4985 if (mr)
4986 sheet_objects_clear (sheet, mr, GNM_CELL_COMMENT_TYPE, NULL);
4987 else {
4988 GnmRange r;
4989 r.start = r.end = *pos;
4990 sheet_objects_clear (sheet, &r, GNM_CELL_COMMENT_TYPE, NULL);
4993 } else if (text && (strlen (text) > 0)) {
4994 cell_set_comment (sheet, pos, author, text, attributes);
4996 sheet_mark_dirty (sheet);
4998 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
4999 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
5001 return FALSE;
5004 static gboolean
5005 cmd_set_comment_undo (GnmCommand *cmd,
5006 G_GNUC_UNUSED WorkbookControl *wbc)
5008 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5010 return cmd_set_comment_apply (me->sheet, &me->pos,
5011 me->old_text, me->old_attributes,
5012 me->old_author);
5015 static gboolean
5016 cmd_set_comment_redo (GnmCommand *cmd,
5017 G_GNUC_UNUSED WorkbookControl *wbc)
5019 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5021 return cmd_set_comment_apply (me->sheet, &me->pos,
5022 me->new_text, me->new_attributes,
5023 me->new_author);
5026 static void
5027 cmd_set_comment_finalize (GObject *cmd)
5029 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5031 g_free (me->new_text);
5032 me->new_text = NULL;
5034 g_free (me->old_text);
5035 me->old_text = NULL;
5037 g_free (me->new_author);
5038 me->new_author = NULL;
5040 g_free (me->old_author);
5041 me->old_author = NULL;
5043 if (me->old_attributes != NULL) {
5044 pango_attr_list_unref (me->old_attributes);
5045 me->old_attributes = NULL;
5048 if (me->new_attributes != NULL) {
5049 pango_attr_list_unref (me->new_attributes);
5050 me->new_attributes = NULL;
5053 gnm_command_finalize (cmd);
5056 gboolean
5057 cmd_set_comment (WorkbookControl *wbc,
5058 Sheet *sheet, GnmCellPos const *pos,
5059 char const *new_text,
5060 PangoAttrList *attr,
5061 char const *new_author)
5063 CmdSetComment *me;
5064 GnmComment *comment;
5065 char *where;
5067 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5068 g_return_val_if_fail (new_text != NULL, TRUE);
5070 me = g_object_new (CMD_SET_COMMENT_TYPE, NULL);
5072 me->cmd.sheet = sheet;
5073 me->cmd.size = 1;
5074 if (strlen (new_text) < 1)
5075 me->new_text = NULL;
5076 else
5077 me->new_text = g_strdup (new_text);
5078 if (strlen (new_author) < 1)
5079 me->new_author = NULL;
5080 else
5081 me->new_author = g_strdup (new_author);
5082 if (attr != NULL)
5083 pango_attr_list_ref (attr);
5084 me->new_attributes = attr;
5085 where = undo_cell_pos_name (sheet, pos);
5086 me->cmd.cmd_descriptor =
5087 g_strdup_printf (me->new_text == NULL ?
5088 _("Clearing comment of %s") :
5089 _("Setting comment of %s"),
5090 where);
5091 g_free (where);
5092 me->old_text = NULL;
5093 me->old_author = NULL;
5094 me->old_attributes = NULL;
5095 me->pos = *pos;
5096 me->sheet = sheet;
5097 comment = sheet_get_comment (sheet, pos);
5098 if (comment) {
5099 g_object_get (G_OBJECT (comment),
5100 "text", &(me->old_text),
5101 "author", &(me->old_author),
5102 "markup", &(me->old_attributes), NULL);
5103 if (me->old_attributes != NULL)
5104 pango_attr_list_ref (me->old_attributes);
5105 me->old_text = g_strdup (me->old_text);
5106 me->old_author = g_strdup (me->old_author);
5109 /* Register the command object */
5110 return gnm_command_push_undo (wbc, G_OBJECT (me));
5113 /******************************************************************/
5115 #define CMD_ANALYSIS_TOOL_TYPE (cmd_analysis_tool_get_type ())
5116 #define CMD_ANALYSIS_TOOL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ANALYSIS_TOOL_TYPE, CmdAnalysis_Tool))
5118 typedef struct {
5119 GnmCommand cmd;
5121 data_analysis_output_t *dao;
5122 gpointer specs;
5123 gboolean specs_owned;
5124 analysis_tool_engine engine;
5125 data_analysis_output_type_t type;
5127 ColRowStateList *col_info;
5128 ColRowStateList *row_info;
5129 GnmRange old_range;
5130 GnmCellRegion *old_contents;
5131 GSList *newSheetObjects;
5132 } CmdAnalysis_Tool;
5134 MAKE_GNM_COMMAND (CmdAnalysis_Tool, cmd_analysis_tool, NULL)
5136 static gboolean
5137 cmd_analysis_tool_undo (GnmCommand *cmd, WorkbookControl *wbc)
5139 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5140 GnmPasteTarget pt;
5142 g_return_val_if_fail (me != NULL, TRUE);
5144 /* The old view might not exist anymore */
5145 me->dao->wbc = wbc;
5147 switch (me->type) {
5148 case NewSheetOutput:
5149 if (!command_undo_sheet_delete (me->dao->sheet))
5150 return TRUE;
5151 me->dao->sheet = NULL;
5152 break;
5153 case NewWorkbookOutput:
5154 g_warning ("How did we get here?");
5155 return TRUE;
5156 break;
5157 case RangeOutput:
5158 default:
5159 sheet_clear_region (me->dao->sheet,
5160 me->old_range.start.col, me->old_range.start.row,
5161 me->old_range.end.col, me->old_range.end.row,
5162 CLEAR_COMMENTS | CLEAR_FORMATS | CLEAR_NOCHECKARRAY |
5163 CLEAR_RECALC_DEPS | CLEAR_VALUES | CLEAR_MERGES,
5164 GO_CMD_CONTEXT (wbc));
5165 clipboard_paste_region (me->old_contents,
5166 paste_target_init (&pt, me->dao->sheet, &me->old_range, PASTE_ALL_SHEET),
5167 GO_CMD_CONTEXT (wbc));
5168 cellregion_unref (me->old_contents);
5169 me->old_contents = NULL;
5170 if (me->col_info) {
5171 dao_set_colrow_state_list (me->dao, TRUE, me->col_info);
5172 me->col_info = colrow_state_list_destroy (me->col_info);
5174 if (me->row_info) {
5175 dao_set_colrow_state_list (me->dao, FALSE, me->row_info);
5176 me->row_info = colrow_state_list_destroy (me->row_info);
5178 if (me->newSheetObjects == NULL)
5179 me->newSheetObjects = dao_surrender_so (me->dao);
5180 g_slist_foreach (me->newSheetObjects, (GFunc)sheet_object_clear_sheet, NULL);
5181 sheet_update (me->dao->sheet);
5184 return FALSE;
5187 static void
5188 cmd_analysis_tool_draw_old_so (SheetObject *so, data_analysis_output_t *dao)
5190 g_object_ref (so);
5191 dao_set_sheet_object (dao, 0, 1, so);
5194 static gboolean
5195 cmd_analysis_tool_redo (GnmCommand *cmd, WorkbookControl *wbc)
5197 gpointer continuity = NULL;
5198 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5199 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5201 g_return_val_if_fail (me != NULL, TRUE);
5203 /* The old view might not exist anymore */
5204 me->dao->wbc = wbc;
5206 if (me->col_info)
5207 me->col_info = colrow_state_list_destroy (me->col_info);
5208 me->col_info = dao_get_colrow_state_list (me->dao, TRUE);
5209 if (me->row_info)
5210 me->row_info = colrow_state_list_destroy (me->row_info);
5211 me->row_info = dao_get_colrow_state_list (me->dao, FALSE);
5213 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PREPARE_OUTPUT_RANGE, NULL)
5214 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5215 &me->cmd.cmd_descriptor)
5216 || cmd_dao_is_locked_effective (me->dao, wbc, me->cmd.cmd_descriptor)
5217 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_LAST_VALIDITY_CHECK, &continuity))
5218 return TRUE;
5220 switch (me->type) {
5221 case NewSheetOutput:
5222 me->old_contents = NULL;
5223 break;
5224 case NewWorkbookOutput:
5225 /* No undo in this case (see below) */
5226 me->old_contents = NULL;
5227 break;
5228 case RangeOutput:
5229 default:
5230 range_init (&me->old_range, me->dao->start_col, me->dao->start_row,
5231 me->dao->start_col + me->dao->cols - 1,
5232 me->dao->start_row + me->dao->rows - 1);
5233 me->old_contents = clipboard_copy_range (me->dao->sheet, &me->old_range);
5234 break;
5237 if (me->newSheetObjects != NULL)
5238 dao_set_omit_so (me->dao, TRUE);
5240 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_FORMAT_OUTPUT_RANGE, NULL))
5241 return TRUE;
5243 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PERFORM_CALC, &continuity)) {
5244 if (me->type == RangeOutput) {
5245 g_warning ("This is too late for failure! The target region has "
5246 "already been formatted!");
5247 } else
5248 return TRUE;
5250 if (me->newSheetObjects != NULL)
5252 GSList *l = g_slist_reverse
5253 (g_slist_copy (me->newSheetObjects));
5255 dao_set_omit_so (me->dao, FALSE);
5256 g_slist_foreach (l,
5257 (GFunc) cmd_analysis_tool_draw_old_so,
5258 me->dao);
5259 g_slist_free (l);
5262 if (continuity) {
5263 g_warning ("There shouldn't be any data left in here!");
5266 dao_autofit_columns (me->dao);
5267 sheet_mark_dirty (me->dao->sheet);
5268 sheet_update (me->dao->sheet);
5270 /* The concept of an undo if we create a new worksheet is extremely strange,
5271 * since we have separate undo/redo queues per worksheet.
5272 * Users can simply delete the worksheet if they so desire.
5275 return (me->type == NewWorkbookOutput);
5278 static void
5279 cmd_analysis_tool_finalize (GObject *cmd)
5281 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5283 if (me->col_info)
5284 me->col_info = colrow_state_list_destroy (me->col_info);
5285 if (me->row_info)
5286 me->row_info = colrow_state_list_destroy (me->row_info);
5288 me->engine (NULL, me->dao, me->specs, TOOL_ENGINE_CLEAN_UP, NULL);
5290 if (me->specs_owned) {
5291 g_free (me->specs);
5292 dao_free (me->dao);
5294 if (me->old_contents)
5295 cellregion_unref (me->old_contents);
5297 g_slist_free_full (me->newSheetObjects, g_object_unref);
5299 gnm_command_finalize (cmd);
5303 * cmd_analysis_tool: (skip)
5304 * Note: this takes ownership of specs and dao if and if only the command
5305 * succeeds.
5307 gboolean
5308 cmd_analysis_tool (WorkbookControl *wbc, G_GNUC_UNUSED Sheet *sheet,
5309 data_analysis_output_t *dao, gpointer specs,
5310 analysis_tool_engine engine, gboolean always_take_ownership)
5312 CmdAnalysis_Tool *me;
5313 gboolean trouble;
5314 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5316 g_return_val_if_fail (dao != NULL, TRUE);
5317 g_return_val_if_fail (specs != NULL, TRUE);
5318 g_return_val_if_fail (engine != NULL, TRUE);
5320 me = g_object_new (CMD_ANALYSIS_TOOL_TYPE, NULL);
5322 dao->wbc = wbc;
5324 /* Store the specs for the object */
5325 me->specs = specs;
5326 me->specs_owned = always_take_ownership;
5327 me->dao = dao;
5328 me->engine = engine;
5329 me->cmd.cmd_descriptor = NULL;
5330 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DAO, NULL)) {
5331 g_object_unref (me);
5332 return TRUE;
5334 me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5335 &me->cmd.cmd_descriptor);
5336 me->cmd.sheet = NULL;
5337 me->type = dao->type;
5338 me->row_info = NULL;
5339 me->col_info = NULL;
5341 /* We divide by 2 since many cells will be empty*/
5342 me->cmd.size = 1 + dao->rows * dao->cols / 2;
5344 /* Register the command object */
5345 trouble = gnm_command_push_undo (wbc, G_OBJECT (me));
5347 if (!trouble)
5348 me->specs_owned = TRUE;
5350 return trouble;
5353 /******************************************************************/
5355 #define CMD_MERGE_DATA_TYPE (cmd_merge_data_get_type ())
5356 #define CMD_MERGE_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_DATA_TYPE, CmdMergeData))
5358 typedef struct {
5359 GnmCommand cmd;
5360 GnmValue *merge_zone;
5361 GSList *merge_fields;
5362 GSList *merge_data;
5363 GSList *sheet_list;
5364 Sheet *sheet;
5365 gint n;
5366 } CmdMergeData;
5368 MAKE_GNM_COMMAND (CmdMergeData, cmd_merge_data, NULL)
5370 static void
5371 cmd_merge_data_delete_sheets (gpointer data, gpointer success)
5373 Sheet *sheet = data;
5375 if (!command_undo_sheet_delete (sheet))
5376 *(gboolean *)success = FALSE;
5379 static gboolean
5380 cmd_merge_data_undo (GnmCommand *cmd,
5381 G_GNUC_UNUSED WorkbookControl *wbc)
5383 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5384 gboolean success = TRUE;
5386 g_slist_foreach (me->sheet_list, cmd_merge_data_delete_sheets, &success);
5387 g_slist_free (me->sheet_list);
5388 me->sheet_list = NULL;
5390 return FALSE;
5393 static gboolean
5394 cmd_merge_data_redo (GnmCommand *cmd, WorkbookControl *wbc)
5396 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5397 int i;
5398 GnmCellRegion *merge_contents;
5399 GnmRangeRef *cell = &me->merge_zone->v_range.cell;
5400 GnmPasteTarget pt;
5401 GSList *this_field = me->merge_fields;
5402 GSList *this_data = me->merge_data;
5403 Sheet *source_sheet = cell->a.sheet;
5404 GSList *target_sheet;
5405 GnmRange target_range;
5406 ColRowStateList *state_col;
5407 ColRowStateList *state_row;
5409 range_init (&target_range, cell->a.col, cell->a.row,
5410 cell->b.col, cell->b.row);
5411 merge_contents = clipboard_copy_range (source_sheet, &target_range);
5412 state_col = colrow_get_states (source_sheet, TRUE, target_range.start.col,
5413 target_range.end.col);
5414 state_row = colrow_get_states (source_sheet, FALSE, target_range.start.row,
5415 target_range.end.row);
5417 for (i = 0; i < me->n; i++) {
5418 Sheet *new_sheet;
5420 new_sheet = workbook_sheet_add (me->sheet->workbook, -1,
5421 gnm_sheet_get_max_cols (me->sheet),
5422 gnm_sheet_get_max_rows (me->sheet));
5423 me->sheet_list = g_slist_prepend (me->sheet_list, new_sheet);
5425 colrow_set_states (new_sheet, TRUE, target_range.start.col, state_col);
5426 colrow_set_states (new_sheet, FALSE, target_range.start.row, state_row);
5427 sheet_objects_dup (source_sheet, new_sheet, &target_range);
5428 clipboard_paste_region (merge_contents,
5429 paste_target_init (&pt, new_sheet, &target_range, PASTE_ALL_SHEET),
5430 GO_CMD_CONTEXT (wbc));
5432 cellregion_unref (merge_contents);
5433 me->sheet_list = g_slist_reverse (me->sheet_list);
5434 colrow_state_list_destroy (state_col);
5435 colrow_state_list_destroy (state_row);
5437 while (this_field) {
5438 int col_source, row_source;
5439 int col_target, row_target;
5441 g_return_val_if_fail (this_data != NULL, TRUE);
5442 cell = &((GnmValue *)this_field->data)->v_range.cell;
5443 col_target = cell->a.col;
5444 row_target = cell->a.row;
5446 cell = &((GnmValue *)this_data->data)->v_range.cell;
5447 col_source = cell->a.col;
5448 row_source = cell->a.row;
5449 source_sheet = cell->a.sheet;
5451 target_sheet = me->sheet_list;
5452 while (target_sheet) {
5453 GnmCell *source_cell = sheet_cell_get (source_sheet,
5454 col_source, row_source);
5455 if (source_cell == NULL) {
5456 GnmCell *target_cell = sheet_cell_get ((Sheet *)target_sheet->data,
5457 col_target, row_target);
5458 if (target_cell != NULL)
5459 gnm_cell_set_value (target_cell,
5460 value_new_empty ());
5461 } else {
5462 GnmCell *target_cell = sheet_cell_fetch ((Sheet *)target_sheet->data,
5463 col_target, row_target);
5464 gnm_cell_set_value (target_cell,
5465 value_dup (source_cell->value));
5467 target_sheet = target_sheet->next;
5468 row_source++;
5471 this_field = this_field->next;
5472 this_data = this_data->next;
5475 return FALSE;
5478 static void
5479 cmd_merge_data_finalize (GObject *cmd)
5481 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5483 value_release (me->merge_zone);
5484 me->merge_zone = NULL;
5485 range_list_destroy (me->merge_data);
5486 me->merge_data = NULL;
5487 range_list_destroy (me->merge_fields);
5488 me->merge_fields = NULL;
5489 g_slist_free (me->sheet_list);
5490 me->sheet_list = NULL;
5491 me->n = 0;
5493 gnm_command_finalize (cmd);
5497 * cmd_merge_data:
5498 * @wbc: #WorkbookControl
5499 * @sheet: #Sheet
5500 * @merge_zone: (transfer full): #GnmValue
5501 * @merge_fields: (element-type GnmRange) (transfer full):
5502 * @merge_data: (element-type GnmRange) (transfer full):
5505 gboolean
5506 cmd_merge_data (WorkbookControl *wbc, Sheet *sheet,
5507 GnmValue *merge_zone, GSList *merge_fields, GSList *merge_data)
5509 CmdMergeData *me;
5510 GnmRangeRef *cell;
5512 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5513 g_return_val_if_fail (merge_zone != NULL, TRUE);
5514 g_return_val_if_fail (merge_fields != NULL, TRUE);
5515 g_return_val_if_fail (merge_data != NULL, TRUE);
5517 me = g_object_new (CMD_MERGE_DATA_TYPE, NULL);
5519 me->cmd.sheet = sheet;
5520 me->sheet = sheet;
5521 me->cmd.size = 1 + g_slist_length (merge_fields);
5522 me->cmd.cmd_descriptor =
5523 g_strdup_printf (_("Merging data into %s"), value_peek_string (merge_zone));
5525 me->merge_zone = merge_zone;
5526 me->merge_fields = merge_fields;
5527 me->merge_data = merge_data;
5528 me->sheet_list = NULL;
5530 cell = &((GnmValue *)merge_data->data)->v_range.cell;
5531 me->n = cell->b.row - cell->a.row + 1;
5533 /* Register the command object */
5534 return gnm_command_push_undo (wbc, G_OBJECT (me));
5537 /******************************************************************/
5539 #define CMD_CHANGE_META_DATA_TYPE (cmd_change_summary_get_type ())
5540 #define CMD_CHANGE_META_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_CHANGE_META_DATA_TYPE, CmdChangeMetaData))
5542 typedef struct {
5543 GnmCommand cmd;
5544 GSList *changed_props;
5545 GSList *removed_names;
5546 } CmdChangeMetaData;
5548 MAKE_GNM_COMMAND (CmdChangeMetaData, cmd_change_summary, NULL)
5550 static gboolean
5551 cmd_change_summary_undo (GnmCommand *cmd, WorkbookControl *wbc)
5553 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5554 GsfDocMetaData *meta = go_doc_get_meta_data (wb_control_get_doc (wbc));
5555 GSList *ptr, *old_vals = NULL, *dropped = NULL;
5556 GsfDocProp *prop;
5557 char const *name;
5559 for (ptr = me->removed_names; ptr != NULL ; ptr = ptr->next) {
5560 if (NULL != (prop = gsf_doc_meta_data_steal (meta, ptr->data)))
5561 old_vals = g_slist_prepend (old_vals, prop);
5562 g_free (ptr->data);
5564 g_slist_free (me->removed_names);
5566 for (ptr = me->changed_props; ptr != NULL ; ptr = ptr->next) {
5567 name = gsf_doc_prop_get_name (ptr->data);
5568 if (NULL != (prop = gsf_doc_meta_data_steal (meta, name)))
5569 old_vals = g_slist_prepend (old_vals, prop);
5570 else
5571 dropped = g_slist_prepend (old_vals, g_strdup (name));
5572 gsf_doc_meta_data_store (meta, ptr->data);
5574 g_slist_free (me->changed_props);
5576 me->removed_names = dropped;
5577 me->changed_props = old_vals;
5578 go_doc_update_meta_data (wb_control_get_doc (wbc));
5580 return FALSE;
5583 static gboolean
5584 cmd_change_summary_redo (GnmCommand *cmd, WorkbookControl *wbc)
5586 return cmd_change_summary_undo (cmd, wbc);
5589 static void
5590 cmd_change_summary_finalize (GObject *cmd)
5592 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5594 g_slist_free_full (me->changed_props, (GDestroyNotify)gsf_doc_prop_free);
5595 me->changed_props = NULL;
5596 g_slist_free_full (me->removed_names, g_free);
5597 me->removed_names = NULL;
5599 gnm_command_finalize (cmd);
5603 * cmd_change_meta_data:
5604 * @wbc: #WorkbookControl
5605 * @changes: (element-type GsfDocMetaData) (transfer full): the changed metadata.
5606 * @removed: (element-type GsfDocMetaData) (transfer full): the removed metadata.
5609 gboolean
5610 cmd_change_meta_data (WorkbookControl *wbc, GSList *changes, GSList *removed)
5612 CmdChangeMetaData *me = g_object_new (CMD_CHANGE_META_DATA_TYPE, NULL);
5614 me->changed_props = changes;
5615 me->removed_names = removed;
5616 me->cmd.sheet = NULL;
5618 me->cmd.size = g_slist_length (changes) + g_slist_length (removed);
5619 me->cmd.cmd_descriptor = g_strdup_printf (
5620 _("Changing workbook properties"));
5621 return gnm_command_push_undo (wbc, G_OBJECT (me));
5624 /******************************************************************/
5626 #define CMD_OBJECT_RAISE_TYPE (cmd_object_raise_get_type ())
5627 #define CMD_OBJECT_RAISE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECT_RAISE_TYPE, CmdObjectRaise))
5629 typedef struct {
5630 GnmCommand cmd;
5631 SheetObject *so;
5632 CmdObjectRaiseSelector dir;
5633 gint changed_positions;
5634 } CmdObjectRaise;
5636 MAKE_GNM_COMMAND (CmdObjectRaise, cmd_object_raise, NULL)
5638 static gboolean
5639 cmd_object_raise_redo (GnmCommand *cmd,
5640 G_GNUC_UNUSED WorkbookControl *wbc)
5642 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5643 switch (me->dir) {
5644 case cmd_object_pull_to_front:
5645 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MAXINT/2);
5646 break;
5647 case cmd_object_pull_forward:
5648 me->changed_positions = sheet_object_adjust_stacking (me->so, 1);
5649 break;
5650 case cmd_object_push_backward:
5651 me->changed_positions = sheet_object_adjust_stacking (me->so, -1);
5652 break;
5653 case cmd_object_push_to_back:
5654 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MININT/2);
5655 break;
5657 return FALSE;
5660 static gboolean
5661 cmd_object_raise_undo (GnmCommand *cmd,
5662 G_GNUC_UNUSED WorkbookControl *wbc)
5664 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5665 if (me->changed_positions != 0)
5666 sheet_object_adjust_stacking (me->so, - me->changed_positions);
5667 return FALSE;
5670 static void
5671 cmd_object_raise_finalize (GObject *cmd)
5673 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5674 g_object_unref (me->so);
5675 gnm_command_finalize (cmd);
5678 gboolean
5679 cmd_object_raise (WorkbookControl *wbc, SheetObject *so, CmdObjectRaiseSelector dir)
5681 CmdObjectRaise *me;
5683 g_return_val_if_fail (GNM_IS_SO (so), TRUE);
5685 me = g_object_new (CMD_OBJECT_RAISE_TYPE, NULL);
5687 me->so = so;
5688 g_object_ref (so);
5690 me->cmd.sheet = sheet_object_get_sheet (so);
5691 me->cmd.size = 1;
5692 switch (dir) {
5693 case cmd_object_pull_to_front:
5694 me->cmd.cmd_descriptor = g_strdup (_("Pull Object to the Front"));
5695 break;
5696 case cmd_object_pull_forward:
5697 me->cmd.cmd_descriptor = g_strdup (_("Pull Object Forward"));
5698 break;
5699 case cmd_object_push_backward:
5700 me->cmd.cmd_descriptor = g_strdup (_("Push Object Backward"));
5701 break;
5702 case cmd_object_push_to_back:
5703 me->cmd.cmd_descriptor = g_strdup (_("Push Object to the Back"));
5704 break;
5706 me->dir = dir;
5707 me->changed_positions = 0;
5709 return gnm_command_push_undo (wbc, G_OBJECT (me));
5712 /******************************************************************/
5714 #define CMD_PRINT_SETUP_TYPE (cmd_print_setup_get_type ())
5715 #define CMD_PRINT_SETUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PRINT_SETUP_TYPE, CmdPrintSetup))
5717 typedef struct {
5718 GnmCommand cmd;
5720 GSList *old_pi;
5721 GnmPrintInformation *new_pi;
5722 } CmdPrintSetup;
5724 MAKE_GNM_COMMAND (CmdPrintSetup, cmd_print_setup, NULL)
5726 static void
5727 update_sheet_graph_cb (Sheet *sheet)
5729 g_return_if_fail (IS_SHEET (sheet) && sheet->sheet_type == GNM_SHEET_OBJECT);
5731 sheet_object_graph_ensure_size (GNM_SO (sheet->sheet_objects->data));
5734 static gboolean
5735 cmd_print_setup_undo (GnmCommand *cmd, WorkbookControl *wbc)
5737 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5738 guint n, i;
5739 Workbook *book;
5740 GSList *infos;
5742 g_return_val_if_fail (me->old_pi != NULL, TRUE);
5744 if (me->cmd.sheet) {
5745 GnmPrintInformation *pi = me->old_pi->data;
5746 gnm_print_info_free (me->cmd.sheet->print_info);
5747 me->cmd.sheet->print_info = gnm_print_info_dup (pi);
5748 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5749 update_sheet_graph_cb (me->cmd.sheet);
5750 } else {
5751 book = wb_control_get_workbook(wbc);
5752 n = workbook_sheet_count (book);
5753 infos = me->old_pi;
5754 g_return_val_if_fail (g_slist_length (infos) == n, TRUE);
5756 for (i = 0 ; i < n ; i++) {
5757 GnmPrintInformation *pi = infos->data;
5758 Sheet *sheet = workbook_sheet_by_index (book, i);
5760 g_return_val_if_fail (infos != NULL, TRUE);
5762 gnm_print_info_free (sheet->print_info);
5763 sheet->print_info = gnm_print_info_dup (pi);
5764 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5765 update_sheet_graph_cb (sheet);
5766 infos = infos->next;
5769 return FALSE;
5772 static gboolean
5773 cmd_print_setup_redo (GnmCommand *cmd, WorkbookControl *wbc)
5775 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5776 int n, i;
5777 Workbook *book;
5778 gboolean save_pis = (me->old_pi == NULL);
5780 if (me->cmd.sheet) {
5781 if (save_pis)
5782 me->old_pi = g_slist_append (me->old_pi, me->cmd.sheet->print_info);
5783 else
5784 gnm_print_info_free (me->cmd.sheet->print_info);
5785 me->cmd.sheet->print_info = gnm_print_info_dup (me->new_pi);
5786 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5787 update_sheet_graph_cb (me->cmd.sheet);
5788 } else {
5789 book = wb_control_get_workbook(wbc);
5790 n = workbook_sheet_count (book);
5791 for (i = 0 ; i < n ; i++) {
5792 Sheet *sheet = workbook_sheet_by_index (book, i);
5793 sheet_mark_dirty (sheet);
5794 if (save_pis)
5795 me->old_pi = g_slist_prepend (me->old_pi, sheet->print_info);
5796 else
5797 gnm_print_info_free (sheet->print_info);
5798 sheet->print_info = gnm_print_info_dup (me->new_pi);
5799 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5800 update_sheet_graph_cb (sheet);
5802 if (save_pis)
5803 me->old_pi = g_slist_reverse (me->old_pi);
5805 return FALSE;
5808 static void
5809 cmd_print_setup_finalize (GObject *cmd)
5811 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5812 GSList *list = me->old_pi;
5814 if (me->new_pi)
5815 gnm_print_info_free (me->new_pi);
5816 for (; list; list = list->next)
5817 gnm_print_info_free ((GnmPrintInformation *) list->data);
5818 g_slist_free (me->old_pi);
5819 gnm_command_finalize (cmd);
5822 gboolean
5823 cmd_print_setup (WorkbookControl *wbc, Sheet *sheet, GnmPrintInformation const *pi)
5825 CmdPrintSetup *me;
5827 me = g_object_new (CMD_PRINT_SETUP_TYPE, NULL);
5829 me->cmd.sheet = sheet;
5830 me->cmd.size = 10;
5831 if (sheet)
5832 me->cmd.cmd_descriptor =
5833 g_strdup_printf (_("Page Setup For %s"), sheet->name_unquoted);
5834 else
5835 me->cmd.cmd_descriptor = g_strdup (_("Page Setup For All Sheets"));
5836 me->old_pi = NULL;
5837 me->new_pi = gnm_print_info_dup (pi);
5839 return gnm_command_push_undo (wbc, G_OBJECT (me));
5842 /******************************************************************/
5844 #define CMD_DEFINE_NAME_TYPE (cmd_define_name_get_type ())
5845 #define CMD_DEFINE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DEFINE_NAME_TYPE, CmdDefineName))
5847 typedef struct {
5848 GnmCommand cmd;
5850 GnmParsePos pp;
5851 char *name;
5852 GnmExprTop const *texpr;
5853 gboolean new_name;
5854 gboolean placeholder;
5855 } CmdDefineName;
5857 MAKE_GNM_COMMAND (CmdDefineName, cmd_define_name, NULL)
5859 static gboolean
5860 cmd_define_name_undo (GnmCommand *cmd,
5861 WorkbookControl *wbc)
5863 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5864 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5865 GnmExprTop const *texpr = nexpr->texpr;
5867 gnm_expr_top_ref (texpr);
5868 if (me->new_name)
5869 expr_name_remove (nexpr);
5870 else if (me->placeholder)
5871 expr_name_downgrade_to_placeholder (nexpr);
5872 else
5873 expr_name_set_expr (nexpr, me->texpr); /* restore old def */
5875 me->texpr = texpr;
5877 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5878 wb_view_menus_update (each_wbv);
5880 return FALSE;
5883 static gboolean
5884 cmd_define_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
5886 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5887 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5889 me->new_name = (nexpr == NULL);
5890 me->placeholder = (nexpr != NULL)
5891 && expr_name_is_placeholder (nexpr);
5893 if (me->new_name || me->placeholder) {
5894 char *err = NULL;
5895 nexpr = expr_name_add (&me->pp, me->name, me->texpr, &err, TRUE, NULL);
5896 if (nexpr == NULL) {
5897 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), err);
5898 g_free (err);
5899 return TRUE;
5901 me->texpr = NULL;
5902 } else { /* changing the definition */
5903 GnmExprTop const *tmp = nexpr->texpr;
5904 gnm_expr_top_ref (tmp);
5905 expr_name_set_expr (nexpr, me->texpr);
5906 me->texpr = tmp; /* store the old definition */
5908 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5909 wb_view_menus_update (each_wbv);
5912 return FALSE;
5915 static void
5916 cmd_define_name_finalize (GObject *cmd)
5918 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5920 g_free (me->name); me->name = NULL;
5922 if (me->texpr) {
5923 gnm_expr_top_unref (me->texpr);
5924 me->texpr = NULL;
5927 gnm_command_finalize (cmd);
5931 * cmd_define_name:
5932 * @wbc:
5933 * @name:
5934 * @pp:
5935 * @texpr: absorbs a ref to the texpr.
5936 * @descriptor: optional descriptor.
5938 * If the @name has never been defined in context @pp create a new name
5939 * If its a placeholder assign @texpr to it and make it real
5940 * If it already exists as a real name just assign @expr.
5942 * Returns TRUE on error
5944 gboolean
5945 cmd_define_name (WorkbookControl *wbc, char const *name,
5946 GnmParsePos const *pp, GnmExprTop const *texpr,
5947 char const *descriptor)
5949 CmdDefineName *me;
5950 GnmNamedExpr *nexpr;
5951 Sheet *sheet;
5953 g_return_val_if_fail (name != NULL, TRUE);
5954 g_return_val_if_fail (pp != NULL, TRUE);
5955 g_return_val_if_fail (texpr != NULL, TRUE);
5957 if (name[0] == '\0') {
5958 go_cmd_context_error_invalid
5959 (GO_CMD_CONTEXT (wbc), _("Defined Name"),
5960 _("An empty string is not allowed as defined name."));
5961 gnm_expr_top_unref (texpr);
5962 return TRUE;
5965 sheet = wb_control_cur_sheet (wbc);
5966 if (!expr_name_validate (name)) {
5967 gchar *err = g_strdup_printf
5968 (_("'%s' is not allowed as defined name."), name);
5969 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
5970 _("Defined Name"), err);
5971 g_free (err);
5972 gnm_expr_top_unref (texpr);
5973 return TRUE;
5976 if (expr_name_check_for_loop (name, texpr)) {
5977 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), name,
5978 _("has a circular reference"));
5979 gnm_expr_top_unref (texpr);
5980 return TRUE;
5982 nexpr = expr_name_lookup (pp, name);
5983 if (nexpr != NULL && !expr_name_is_placeholder (nexpr) &&
5984 gnm_expr_top_equal (texpr, nexpr->texpr)) {
5985 gnm_expr_top_unref (texpr);
5986 return FALSE; /* expr is not changing, do nothing */
5989 me = g_object_new (CMD_DEFINE_NAME_TYPE, NULL);
5990 me->name = g_strdup (name);
5991 me->pp = *pp;
5992 me->texpr = texpr;
5994 me->cmd.sheet = sheet;
5995 me->cmd.size = 1;
5997 if (descriptor == NULL) {
5998 char const *tmp;
5999 GString *res;
6001 /* Underscores need to be doubled. */
6002 res = g_string_new (NULL);
6003 for (tmp = name; *tmp; tmp++) {
6004 if (*tmp == '_')
6005 g_string_append_c (res, '_');
6006 g_string_append_c (res, *tmp);
6009 nexpr = expr_name_lookup (pp, name);
6010 if (nexpr == NULL || expr_name_is_placeholder (nexpr))
6011 me->cmd.cmd_descriptor =
6012 g_strdup_printf (_("Define Name %s"), res->str);
6013 else
6014 me->cmd.cmd_descriptor =
6015 g_strdup_printf (_("Update Name %s"), res->str);
6016 g_string_free (res, TRUE);
6017 } else
6018 me->cmd.cmd_descriptor = g_strdup (descriptor);
6020 return gnm_command_push_undo (wbc, G_OBJECT (me));
6023 /******************************************************************/
6025 #define CMD_REMOVE_NAME_TYPE (cmd_remove_name_get_type ())
6026 #define CMD_REMOVE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REMOVE_NAME_TYPE, CmdRemoveName))
6028 typedef struct {
6029 GnmCommand cmd;
6031 GnmParsePos pp;
6032 GnmNamedExpr *nexpr;
6033 const GnmExprTop *texpr;
6034 } CmdRemoveName;
6036 MAKE_GNM_COMMAND (CmdRemoveName, cmd_remove_name, NULL)
6038 static gboolean
6039 cmd_remove_name_undo (GnmCommand *cmd,
6040 G_GNUC_UNUSED WorkbookControl *wbc)
6042 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6043 GnmNamedExpr *nexpr =
6044 expr_name_add (&me->nexpr->pos, expr_name_name (me->nexpr),
6045 me->texpr, NULL, TRUE, NULL);
6046 if (nexpr) {
6047 me->texpr = NULL;
6048 expr_name_ref (nexpr);
6049 expr_name_unref (me->nexpr);
6050 me->nexpr = nexpr;
6051 return FALSE;
6052 } else {
6053 g_warning ("Redefining name failed.");
6054 return TRUE;
6058 static gboolean
6059 cmd_remove_name_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6061 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6063 me->texpr = me->nexpr->texpr;
6064 gnm_expr_top_ref (me->texpr);
6065 expr_name_downgrade_to_placeholder (me->nexpr);
6067 return FALSE;
6070 static void
6071 cmd_remove_name_finalize (GObject *cmd)
6073 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6075 expr_name_unref (me->nexpr);
6077 if (me->texpr) {
6078 gnm_expr_top_unref (me->texpr);
6079 me->texpr = NULL;
6082 gnm_command_finalize (cmd);
6086 * cmd_remove_name:
6087 * @wbc:
6088 * @nexpr: name to remove.
6090 * Returns TRUE on error
6092 gboolean
6093 cmd_remove_name (WorkbookControl *wbc, GnmNamedExpr *nexpr)
6095 CmdRemoveName *me;
6097 g_return_val_if_fail (wbc != NULL, TRUE);
6098 g_return_val_if_fail (nexpr != NULL, TRUE);
6099 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6101 expr_name_ref (nexpr);
6103 me = g_object_new (CMD_REMOVE_NAME_TYPE, NULL);
6104 me->nexpr = nexpr;
6105 me->texpr = NULL;
6106 me->cmd.sheet = wb_control_cur_sheet (wbc);
6107 me->cmd.size = 1;
6108 me->cmd.cmd_descriptor = g_strdup_printf (_("Remove Name %s"),
6109 expr_name_name (nexpr));
6111 return gnm_command_push_undo (wbc, G_OBJECT (me));
6114 /******************************************************************/
6116 #define CMD_RESCOPE_NAME_TYPE (cmd_rescope_name_get_type ())
6117 #define CMD_RESCOPE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESCOPE_NAME_TYPE, CmdRescopeName))
6119 typedef struct {
6120 GnmCommand cmd;
6121 GnmNamedExpr *nexpr;
6122 Sheet *scope;
6123 } CmdRescopeName;
6125 MAKE_GNM_COMMAND (CmdRescopeName, cmd_rescope_name, NULL)
6127 static gboolean
6128 cmd_rescope_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
6130 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6131 Sheet *old_scope = me->nexpr->pos.sheet;
6132 char *err;
6133 GnmParsePos pp = me->nexpr->pos;
6135 pp.sheet = me->scope;
6136 err = expr_name_set_pos (me->nexpr, &pp);
6138 if (err != NULL) {
6139 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Change Scope of Name"), err);
6140 g_free (err);
6141 return TRUE;
6144 me->scope = old_scope;
6145 return FALSE;
6148 static gboolean
6149 cmd_rescope_name_undo (GnmCommand *cmd, WorkbookControl *wbc)
6151 return cmd_rescope_name_redo (cmd, wbc);
6155 static void
6156 cmd_rescope_name_finalize (GObject *cmd)
6158 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6160 expr_name_unref (me->nexpr);
6161 gnm_command_finalize (cmd);
6165 * cmd_rescope_name:
6166 * @wbc:
6167 * @nexpr: name to rescope.
6169 * Returns TRUE on error
6171 gboolean
6172 cmd_rescope_name (WorkbookControl *wbc, GnmNamedExpr *nexpr, Sheet *scope)
6174 CmdRescopeName *me;
6176 g_return_val_if_fail (wbc != NULL, TRUE);
6177 g_return_val_if_fail (nexpr != NULL, TRUE);
6178 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6180 expr_name_ref (nexpr);
6182 me = g_object_new (CMD_RESCOPE_NAME_TYPE, NULL);
6183 me->nexpr = nexpr;
6184 me->scope = scope;
6185 me->cmd.sheet = wb_control_cur_sheet (wbc);
6186 me->cmd.size = 1;
6187 me->cmd.cmd_descriptor = g_strdup_printf (_("Change Scope of Name %s"),
6188 expr_name_name (nexpr));
6190 return gnm_command_push_undo (wbc, G_OBJECT (me));
6192 /******************************************************************/
6194 #define CMD_SCENARIO_ADD_TYPE (cmd_scenario_add_get_type ())
6195 #define CMD_SCENARIO_ADD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_ADD_TYPE, CmdScenarioAdd))
6197 typedef struct {
6198 GnmCommand cmd;
6199 GnmScenario *scenario;
6200 } CmdScenarioAdd;
6202 MAKE_GNM_COMMAND (CmdScenarioAdd, cmd_scenario_add, NULL)
6204 static gboolean
6205 cmd_scenario_add_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6207 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6208 GnmScenario *sc = g_object_ref (me->scenario);
6209 gnm_sheet_scenario_add (sc->sheet, sc);
6210 return FALSE;
6213 static gboolean
6214 cmd_scenario_add_undo (GnmCommand *cmd,
6215 G_GNUC_UNUSED WorkbookControl *wbc)
6217 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6218 GnmScenario *sc = me->scenario;
6219 gnm_sheet_scenario_remove (sc->sheet, sc);
6220 return FALSE;
6223 static void
6224 cmd_scenario_add_finalize (GObject *cmd)
6226 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6228 g_object_unref (me->scenario);
6229 gnm_command_finalize (cmd);
6232 gboolean
6233 cmd_scenario_add (WorkbookControl *wbc, GnmScenario *s, Sheet *sheet)
6235 CmdScenarioAdd *me;
6237 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6238 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6240 me = g_object_new (CMD_SCENARIO_ADD_TYPE, NULL);
6242 me->scenario = s; /* Take ownership */
6243 me->cmd.sheet = sheet;
6244 me->cmd.size = 1;
6245 me->cmd.cmd_descriptor = g_strdup (_("Add scenario"));
6247 return gnm_command_push_undo (wbc, G_OBJECT (me));
6250 /******************************************************************/
6252 #define CMD_SCENARIO_MNGR_TYPE (cmd_scenario_mngr_get_type ())
6253 #define CMD_SCENARIO_MNGR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_MNGR_TYPE, CmdScenarioMngr))
6255 typedef struct {
6256 GnmCommand cmd;
6257 GnmScenario *sc;
6258 GOUndo *undo;
6259 } CmdScenarioMngr;
6261 MAKE_GNM_COMMAND (CmdScenarioMngr, cmd_scenario_mngr, NULL)
6263 static gboolean
6264 cmd_scenario_mngr_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6266 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6267 if (!me->undo)
6268 me->undo = gnm_scenario_apply (me->sc);
6269 return FALSE;
6272 static gboolean
6273 cmd_scenario_mngr_undo (GnmCommand *cmd,
6274 G_GNUC_UNUSED WorkbookControl *wbc)
6276 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6277 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6278 g_object_unref (me->undo);
6279 me->undo = NULL;
6280 return FALSE;
6283 static void
6284 cmd_scenario_mngr_finalize (GObject *cmd)
6286 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6288 g_object_unref (me->sc);
6289 if (me->undo)
6290 g_object_unref (me->undo);
6292 gnm_command_finalize (cmd);
6295 gboolean
6296 cmd_scenario_mngr (WorkbookControl *wbc, GnmScenario *sc, GOUndo *undo)
6298 CmdScenarioMngr *me;
6300 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6301 g_return_val_if_fail (GNM_IS_SCENARIO (sc), TRUE);
6303 me = g_object_new (CMD_SCENARIO_MNGR_TYPE, NULL);
6305 me->sc = g_object_ref (sc);
6306 me->undo = g_object_ref (undo);
6307 me->cmd.sheet = sc->sheet;
6308 me->cmd.size = 1;
6309 me->cmd.cmd_descriptor = g_strdup (_("Scenario Show"));
6311 return gnm_command_push_undo (wbc, G_OBJECT (me));
6314 /******************************************************************/
6316 #define CMD_DATA_SHUFFLE_TYPE (cmd_data_shuffle_get_type ())
6317 #define CMD_DATA_SHUFFLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DATA_SHUFFLE_TYPE, CmdDataShuffle))
6319 typedef struct {
6320 GnmCommand cmd;
6321 data_shuffling_t *ds;
6322 } CmdDataShuffle;
6324 MAKE_GNM_COMMAND (CmdDataShuffle, cmd_data_shuffle, NULL)
6326 static gboolean
6327 cmd_data_shuffle_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6329 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6331 data_shuffling_redo (me->ds);
6332 return FALSE;
6335 static gboolean
6336 cmd_data_shuffle_undo (GnmCommand *cmd,
6337 G_GNUC_UNUSED WorkbookControl *wbc)
6339 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6341 data_shuffling_redo (me->ds);
6342 return FALSE;
6345 static void
6346 cmd_data_shuffle_finalize (GObject *cmd)
6348 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6350 data_shuffling_free (me->ds);
6351 gnm_command_finalize (cmd);
6354 gboolean
6355 cmd_data_shuffle (WorkbookControl *wbc, data_shuffling_t *sc, Sheet *sheet)
6357 CmdDataShuffle *me;
6359 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6360 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6362 me = g_object_new (CMD_DATA_SHUFFLE_TYPE, NULL);
6364 me->ds = sc;
6365 me->cmd.sheet = sheet;
6366 me->cmd.size = 1;
6367 me->cmd.cmd_descriptor = g_strdup (_("Shuffle Data"));
6369 return gnm_command_push_undo (wbc, G_OBJECT (me));
6372 /******************************************************************/
6374 #define CMD_TEXT_TO_COLUMNS_TYPE (cmd_text_to_columns_get_type ())
6375 #define CMD_TEXT_TO_COLUMNS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TEXT_TO_COLUMNS_TYPE, CmdTextToColumns))
6377 typedef struct {
6378 GnmCommand cmd;
6380 GnmCellRegion *contents;
6381 GnmPasteTarget dst;
6382 GnmRange src;
6383 Sheet *src_sheet;
6384 ColRowStateList *saved_sizes;
6385 } CmdTextToColumns;
6387 MAKE_GNM_COMMAND (CmdTextToColumns, cmd_text_to_columns, NULL)
6389 static gboolean
6390 cmd_text_to_columns_impl (GnmCommand *cmd, WorkbookControl *wbc,
6391 gboolean is_undo)
6393 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6394 GnmCellRegion *contents;
6396 g_return_val_if_fail (me != NULL, TRUE);
6397 g_return_val_if_fail (me->contents != NULL, TRUE);
6399 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
6400 if (clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc))) {
6401 /* There was a problem, avoid leaking */
6402 cellregion_unref (contents);
6403 return TRUE;
6406 cellregion_unref (me->contents);
6408 if (is_undo) {
6409 colrow_set_states (me->dst.sheet, FALSE,
6410 me->dst.range.start.row, me->saved_sizes);
6411 colrow_state_list_destroy (me->saved_sizes);
6412 me->saved_sizes = NULL;
6413 } else {
6414 me->saved_sizes = colrow_get_states (me->dst.sheet,
6415 FALSE, me->dst.range.start.row, me->dst.range.end.row);
6416 rows_height_update (me->dst.sheet, &me->dst.range, FALSE);
6419 me->contents = contents;
6421 /* Select the newly pasted contents (this queues a redraw) */
6422 select_range (me->dst.sheet, &me->dst.range, wbc);
6424 return FALSE;
6427 static gboolean
6428 cmd_text_to_columns_undo (GnmCommand *cmd, WorkbookControl *wbc)
6430 return cmd_text_to_columns_impl (cmd, wbc, TRUE);
6433 static gboolean
6434 cmd_text_to_columns_redo (GnmCommand *cmd, WorkbookControl *wbc)
6436 return cmd_text_to_columns_impl (cmd, wbc, FALSE);
6439 static void
6440 cmd_text_to_columns_finalize (GObject *cmd)
6442 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6444 if (me->saved_sizes)
6445 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
6446 if (me->contents) {
6447 cellregion_unref (me->contents);
6448 me->contents = NULL;
6450 gnm_command_finalize (cmd);
6453 gboolean
6454 cmd_text_to_columns (WorkbookControl *wbc,
6455 GnmRange const *src, Sheet *src_sheet,
6456 GnmRange const *target, Sheet *target_sheet,
6457 GnmCellRegion *contents)
6459 CmdTextToColumns *me;
6460 char *src_range_name, *target_range_name;
6462 g_return_val_if_fail (contents != NULL, TRUE);
6464 src_range_name = undo_range_name (src_sheet, src);
6465 target_range_name = undo_range_name (target_sheet, target);
6467 me = g_object_new (CMD_TEXT_TO_COLUMNS_TYPE, NULL);
6469 me->cmd.sheet = (src_sheet == target_sheet ? src_sheet : NULL);
6470 me->cmd.size = 1; /* FIXME? */
6471 me->cmd.cmd_descriptor = g_strdup_printf (_("Text (%s) to Columns (%s)"),
6472 src_range_name,
6473 target_range_name);
6474 me->dst.range = *target;
6475 me->dst.sheet = target_sheet;
6476 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
6477 me->src = *src;
6478 me->src_sheet = src_sheet;
6479 me->contents = contents;
6480 me->saved_sizes = NULL;
6482 g_free (src_range_name);
6483 g_free (target_range_name);
6485 /* Check array subdivision & merged regions */
6486 if (sheet_range_splits_region (target_sheet, &me->dst.range,
6487 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
6488 g_object_unref (me);
6489 return TRUE;
6492 return gnm_command_push_undo (wbc, G_OBJECT (me));
6495 /******************************************************************/
6497 #define CMD_GENERIC_TYPE (cmd_generic_get_type ())
6498 #define CMD_GENERIC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GENERIC_TYPE, CmdGeneric))
6500 typedef struct {
6501 GnmCommand cmd;
6502 GOUndo *undo, *redo;
6503 } CmdGeneric;
6505 MAKE_GNM_COMMAND (CmdGeneric, cmd_generic, NULL)
6507 static gboolean
6508 cmd_generic_undo (GnmCommand *cmd, WorkbookControl *wbc)
6510 CmdGeneric *me = CMD_GENERIC (cmd);
6511 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6512 return FALSE;
6515 static gboolean
6516 cmd_generic_redo (GnmCommand *cmd, WorkbookControl *wbc)
6518 CmdGeneric *me = CMD_GENERIC (cmd);
6519 go_undo_undo_with_data (me->redo, GO_CMD_CONTEXT (wbc));
6520 return FALSE;
6523 static void
6524 cmd_generic_finalize (GObject *cmd)
6526 CmdGeneric *me = CMD_GENERIC (cmd);
6528 g_object_unref (me->undo);
6529 g_object_unref (me->redo);
6531 gnm_command_finalize (cmd);
6534 gboolean
6535 cmd_generic_with_size (WorkbookControl *wbc, const char *txt,
6536 int size,
6537 GOUndo *undo, GOUndo *redo)
6539 CmdGeneric *me;
6541 g_return_val_if_fail (GO_IS_UNDO (undo), TRUE);
6542 g_return_val_if_fail (GO_IS_UNDO (redo), TRUE);
6544 me = g_object_new (CMD_GENERIC_TYPE, NULL);
6546 me->cmd.sheet = wb_control_cur_sheet (wbc);
6547 me->cmd.size = size;
6548 me->cmd.cmd_descriptor = g_strdup (txt);
6550 me->undo = undo;
6551 me->redo = redo;
6553 return gnm_command_push_undo (wbc, G_OBJECT (me));
6556 gboolean
6557 cmd_generic (WorkbookControl *wbc, const char *txt, GOUndo *undo, GOUndo *redo)
6559 return cmd_generic_with_size (wbc, txt, 1, undo, redo);
6562 /******************************************************************/
6564 #define CMD_GOAL_SEEK_TYPE (cmd_goal_seek_get_type ())
6565 #define CMD_GOAL_SEEK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GOAL_SEEK_TYPE, CmdGoalSeek))
6567 typedef struct {
6568 GnmCommand cmd;
6570 GnmCell *cell;
6571 GnmValue *ov;
6572 GnmValue *nv;
6573 } CmdGoalSeek;
6575 MAKE_GNM_COMMAND (CmdGoalSeek, cmd_goal_seek, NULL)
6577 static gboolean
6578 cmd_goal_seek_impl (GnmCell *cell, GnmValue *value)
6580 sheet_cell_set_value (cell, value_dup(value));
6581 return FALSE;
6585 static gboolean
6586 cmd_goal_seek_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6588 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6590 return cmd_goal_seek_impl (me->cell, me->ov);
6593 static gboolean
6594 cmd_goal_seek_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6596 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6598 return cmd_goal_seek_impl (me->cell, me->nv);
6601 static void
6602 cmd_goal_seek_finalize (GObject *cmd)
6604 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6606 value_release (me->ov);
6607 me->ov = NULL;
6608 value_release (me->nv);
6609 me->nv = NULL;
6611 gnm_command_finalize (cmd);
6614 gboolean
6615 cmd_goal_seek (WorkbookControl *wbc, GnmCell *cell, GnmValue *ov, GnmValue *nv)
6617 CmdGoalSeek *me;
6618 GnmRange range;
6620 g_return_val_if_fail (cell != NULL, TRUE);
6621 g_return_val_if_fail (ov != NULL || nv != NULL, TRUE);
6623 me = g_object_new (CMD_GOAL_SEEK_TYPE, NULL);
6625 me->cmd.sheet = cell->base.sheet;
6626 me->cmd.size = 1;
6627 range_init_cellpos (&range, &cell->pos);
6628 me->cmd.cmd_descriptor = g_strdup_printf
6629 (_("Goal Seek (%s)"), undo_range_name (cell->base.sheet, &range));
6631 me->cell = cell;
6632 me->ov = ov;
6633 me->nv = nv;
6635 if (me->ov == NULL)
6636 me->ov = value_dup (cell->value);
6637 if (me->nv == NULL)
6638 me->nv = value_dup (cell->value);
6640 return gnm_command_push_undo (wbc, G_OBJECT (me));
6643 /******************************************************************/
6645 #if 0
6646 #define CMD_FREEZE_PANES_TYPE (cmd_freeze_panes_get_type ())
6647 #define CMD_FREEZE_PANES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FREEZE_PANES_TYPE, CmdFreezePanes))
6649 typedef struct {
6650 GnmCommand cmd;
6652 SheetView *sv;
6653 GnmCellPos pos;
6654 } CmdFreezePanes;
6656 MAKE_GNM_COMMAND (CmdFreezePanes, cmd_freeze_panes, NULL)
6658 static gboolean
6659 cmd_freeze_panes_undo (GnmCommand *cmd, WorkbookControl *wbc)
6661 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6663 return FALSE;
6666 static gboolean
6667 cmd_freeze_panes_redo (GnmCommand *cmd, WorkbookControl *wbc)
6669 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6671 return FALSE;
6674 static void
6675 cmd_freeze_panes_finalize (GObject *cmd)
6677 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6679 gnm_command_finalize (cmd);
6683 * cmd_freeze_panes:
6684 * @wbc: where to report errors
6685 * @sv: the view to freeze
6686 * @frozen:
6687 * @unfrozen:
6689 * Returns TRUE on error
6691 gboolean
6692 cmd_freeze_panes (WorkbookControl *wbc, SheetView *sv,
6693 GnmCellPos const *frozen, GnmCellPos const *unfrozen)
6695 CmdFreezePanes *me;
6697 g_return_val_if_fail (name != NULL, TRUE);
6698 g_return_val_if_fail (pp != NULL, TRUE);
6699 g_return_val_if_fail (expr != NULL, TRUE);
6701 me = g_object_new (CMD_FREEZE_PANES_TYPE, NULL);
6702 me->sv = sv;
6703 me->frozen = f;
6704 me->unfrozen = expr;
6705 return gnm_command_push_undo (wbc, G_OBJECT (me));
6708 #endif
6711 /******************************************************************/
6714 #define CMD_TABULATE_TYPE (cmd_tabulate_get_type ())
6715 #define CMD_TABULATE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TABULATE_TYPE, CmdTabulate))
6717 typedef struct {
6718 GnmCommand cmd;
6719 GSList *sheet_idx;
6720 GnmTabulateInfo *data;
6721 } CmdTabulate;
6723 MAKE_GNM_COMMAND (CmdTabulate, cmd_tabulate, NULL)
6725 static gint
6726 cmd_tabulate_cmp_f (gconstpointer a,
6727 gconstpointer b)
6729 guint const a_val = GPOINTER_TO_INT (a);
6730 guint const b_val = GPOINTER_TO_INT (b);
6732 if (a_val > b_val)
6733 return -1;
6734 if (a_val < b_val)
6735 return 1;
6736 return 0;
6739 static gboolean
6740 cmd_tabulate_undo (GnmCommand *cmd, WorkbookControl *wbc)
6742 CmdTabulate *me = CMD_TABULATE (cmd);
6743 GSList *l;
6744 gboolean res = TRUE;
6746 me->sheet_idx = g_slist_sort (me->sheet_idx,
6747 cmd_tabulate_cmp_f);
6749 for (l = me->sheet_idx; l != NULL; l = l->next) {
6750 int i = GPOINTER_TO_INT (l->data);
6751 Sheet *new_sheet =
6752 workbook_sheet_by_index (wb_control_get_workbook (wbc),
6754 res = res && command_undo_sheet_delete (new_sheet);
6756 return !res;
6759 static gboolean
6760 cmd_tabulate_redo (GnmCommand *cmd, WorkbookControl *wbc)
6762 CmdTabulate *me = CMD_TABULATE (cmd);
6764 g_slist_free (me->sheet_idx);
6765 me->sheet_idx = do_tabulation (wbc, me->data);
6767 return (me->sheet_idx == NULL);
6770 static void
6771 cmd_tabulate_finalize (GObject *cmd)
6773 CmdTabulate *me = CMD_TABULATE (cmd);
6775 g_free (me->data->cells);
6776 g_free (me->data->minima);
6777 g_free (me->data->maxima);
6778 g_free (me->data->steps);
6779 g_free (me->data);
6780 gnm_command_finalize (cmd);
6783 gboolean
6784 cmd_tabulate (WorkbookControl *wbc, gpointer data)
6786 CmdTabulate *me;
6788 g_return_val_if_fail (data != NULL, TRUE);
6790 me = g_object_new (CMD_TABULATE_TYPE, NULL);
6792 me->cmd.sheet = NULL;
6793 me->cmd.size = 1;
6794 me->cmd.cmd_descriptor =
6795 g_strdup_printf (_("Tabulating Dependencies"));
6796 me->data = data;
6797 me->sheet_idx = NULL;
6799 return gnm_command_push_undo (wbc, G_OBJECT (me));
6802 /******************************************************************/
6804 #define CMD_SO_GRAPH_CONFIG_TYPE (cmd_so_graph_config_get_type ())
6805 #define CMD_SO_GRAPH_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_GRAPH_CONFIG_TYPE, CmdSOGraphConfig))
6807 typedef struct {
6808 GnmCommand cmd;
6809 SheetObject *so;
6810 GogGraph *new_graph;
6811 GogGraph *old_graph;
6812 } CmdSOGraphConfig;
6814 MAKE_GNM_COMMAND (CmdSOGraphConfig, cmd_so_graph_config, NULL)
6816 static gboolean
6817 cmd_so_graph_config_redo (GnmCommand *cmd,
6818 G_GNUC_UNUSED WorkbookControl *wbc)
6820 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6821 sheet_object_graph_set_gog (me->so, me->new_graph);
6822 return FALSE;
6825 static gboolean
6826 cmd_so_graph_config_undo (GnmCommand *cmd,
6827 G_GNUC_UNUSED WorkbookControl *wbc)
6829 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6830 sheet_object_graph_set_gog (me->so, me->old_graph);
6831 return FALSE;
6834 static void
6835 cmd_so_graph_config_finalize (GObject *cmd)
6837 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6839 g_object_unref (me->so);
6840 g_object_unref (me->new_graph);
6841 g_object_unref (me->old_graph);
6843 gnm_command_finalize (cmd);
6846 gboolean
6847 cmd_so_graph_config (WorkbookControl *wbc, SheetObject *so,
6848 GObject *n_graph, GObject *o_graph)
6850 CmdSOGraphConfig *me;
6852 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6853 g_return_val_if_fail (GNM_IS_SO_GRAPH (so), TRUE);
6854 g_return_val_if_fail (GOG_IS_GRAPH (n_graph), TRUE);
6855 g_return_val_if_fail (GOG_IS_GRAPH (o_graph), TRUE);
6857 me = g_object_new (CMD_SO_GRAPH_CONFIG_TYPE, NULL);
6859 me->so = so;
6860 g_object_ref (so);
6862 me->new_graph = GOG_GRAPH (n_graph);
6863 g_object_ref (me->new_graph);
6864 me->old_graph = GOG_GRAPH (o_graph);
6865 g_object_ref (me->old_graph);
6867 me->cmd.sheet = sheet_object_get_sheet (so);
6868 me->cmd.size = 10;
6869 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Graph"));
6871 return gnm_command_push_undo (wbc, G_OBJECT (me));
6874 /******************************************************************/
6876 #define CMD_SO_COMPONENT_CONFIG_TYPE (cmd_so_component_config_get_type ())
6877 #define CMD_SO_COMPONENT_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_COMPONENT_CONFIG_TYPE, CmdSOComponentConfig))
6879 typedef struct {
6880 GnmCommand cmd;
6881 SheetObject *so;
6882 GOComponent *new_obj;
6883 GOComponent *old_obj;
6884 } CmdSOComponentConfig;
6886 MAKE_GNM_COMMAND (CmdSOComponentConfig, cmd_so_component_config, NULL)
6888 static gboolean
6889 cmd_so_component_config_redo (GnmCommand *cmd,
6890 G_GNUC_UNUSED WorkbookControl *wbc)
6892 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6893 sheet_object_component_set_component (me->so, me->new_obj);
6894 return FALSE;
6897 static gboolean
6898 cmd_so_component_config_undo (GnmCommand *cmd,
6899 G_GNUC_UNUSED WorkbookControl *wbc)
6901 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6902 sheet_object_component_set_component (me->so, me->old_obj);
6903 return FALSE;
6906 static void
6907 cmd_so_component_config_finalize (GObject *cmd)
6909 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6911 g_object_unref (me->so);
6912 g_object_unref (me->new_obj);
6913 g_object_unref (me->old_obj);
6915 gnm_command_finalize (cmd);
6918 gboolean
6919 cmd_so_component_config (WorkbookControl *wbc, SheetObject *so,
6920 GObject *n_obj, GObject *o_obj)
6922 CmdSOComponentConfig *me;
6924 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6925 g_return_val_if_fail (GNM_IS_SO_COMPONENT (so), TRUE);
6926 g_return_val_if_fail (GO_IS_COMPONENT (n_obj), TRUE);
6927 g_return_val_if_fail (GO_IS_COMPONENT (o_obj), TRUE);
6929 me = g_object_new (CMD_SO_COMPONENT_CONFIG_TYPE, NULL);
6931 me->so = so;
6932 g_object_ref (so);
6934 me->new_obj = GO_COMPONENT (g_object_ref (n_obj));
6935 me->old_obj = GO_COMPONENT (g_object_ref (o_obj));
6937 me->cmd.sheet = sheet_object_get_sheet (so);
6938 me->cmd.size = 10;
6939 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Object"));
6941 return gnm_command_push_undo (wbc, G_OBJECT (me));
6944 /******************************************************************/
6946 #define CMD_TOGGLE_RTL_TYPE (cmd_toggle_rtl_get_type ())
6947 #define CMD_TOGGLE_RTL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TOGGLE_RTL_TYPE, CmdToggleRTL))
6949 typedef GnmCommand CmdToggleRTL;
6951 MAKE_GNM_COMMAND (CmdToggleRTL, cmd_toggle_rtl, NULL)
6953 static gboolean
6954 cmd_toggle_rtl_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6956 go_object_toggle (cmd->sheet, "text-is-rtl");
6957 return FALSE;
6960 static gboolean
6961 cmd_toggle_rtl_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6963 return cmd_toggle_rtl_redo (cmd, wbc);
6966 static void
6967 cmd_toggle_rtl_finalize (GObject *cmd)
6969 gnm_command_finalize (cmd);
6972 gboolean
6973 cmd_toggle_rtl (WorkbookControl *wbc, Sheet *sheet)
6975 CmdToggleRTL *me;
6977 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6978 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6980 me = g_object_new (CMD_TOGGLE_RTL_TYPE, NULL);
6981 me->sheet = sheet;
6982 me->size = 1;
6983 me->cmd_descriptor = g_strdup (sheet->text_is_rtl ? _("Left to Right") : _("Right to Left"));
6985 return gnm_command_push_undo (wbc, G_OBJECT (me));
6988 /******************************************************************/
6990 #define CMD_SO_SET_VALUE_TYPE (cmd_so_set_value_get_type ())
6991 #define CMD_SO_SET_VALUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_VALUE_TYPE, CmdSOSetValue))
6993 typedef struct {
6994 GnmCommand cmd;
6995 GnmCellRef ref;
6996 GnmValue *val;
6997 GOUndo *undo;
6998 } CmdSOSetValue;
7000 MAKE_GNM_COMMAND (CmdSOSetValue, cmd_so_set_value, NULL)
7002 static gboolean
7003 cmd_so_set_value_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7005 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7006 Sheet *sheet = me->ref.sheet;
7007 GnmCell *cell = sheet_cell_fetch (sheet, me->ref.col, me->ref.row);
7009 sheet_cell_set_value (cell, value_dup (me->val));
7010 sheet_update (sheet);
7012 return FALSE;
7015 static gboolean
7016 cmd_so_set_value_undo (GnmCommand *cmd, WorkbookControl *wbc)
7018 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7020 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
7022 return FALSE;
7025 static void
7026 cmd_so_set_value_finalize (GObject *cmd)
7028 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7030 value_release (me->val);
7031 g_object_unref (me->undo);
7033 gnm_command_finalize (cmd);
7036 gboolean
7037 cmd_so_set_value (WorkbookControl *wbc,
7038 const char *text,
7039 const GnmCellRef *pref,
7040 GnmValue *new_val,
7041 Sheet *sheet)
7043 CmdSOSetValue *me;
7044 GnmRange r;
7046 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7048 r.start.col = r.end.col = pref->col;
7049 r.start.row = r.end.row = pref->row;
7051 me = g_object_new (CMD_SO_SET_VALUE_TYPE, NULL);
7052 me->cmd.sheet = sheet;
7053 me->cmd.size = 1;
7054 me->cmd.cmd_descriptor = g_strdup (text);
7055 me->ref = *pref;
7056 me->val = new_val;
7057 me->undo = clipboard_copy_range_undo (pref->sheet, &r);
7059 return gnm_command_push_undo (wbc, G_OBJECT (me));
7062 /******************************************************************/
7064 #define CMD_HYPERLINK_TYPE (cmd_hyperlink_get_type ())
7065 #define CMD_HYPERLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_HYPERLINK_TYPE, CmdHyperlink))
7067 typedef struct {
7068 GnmCommand cmd;
7069 GSList *selection;
7070 GnmStyle *new_style;
7071 char *opt_content;
7072 GOUndo *undo;
7073 gboolean update_size;
7074 } CmdHyperlink;
7076 static void
7077 cmd_hyperlink_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
7079 CmdHyperlink const *orig = (CmdHyperlink const *) cmd;
7081 if (orig->new_style)
7082 gnm_style_ref (orig->new_style);
7084 cmd_selection_hyperlink (wbc, orig->new_style, NULL,
7085 g_strdup (orig->opt_content));
7087 MAKE_GNM_COMMAND (CmdHyperlink, cmd_hyperlink, cmd_hyperlink_repeat)
7089 static gboolean
7090 cmd_hyperlink_undo (GnmCommand *cmd, WorkbookControl *wbc)
7092 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7093 Workbook *wb = wb_control_get_workbook (wbc);
7095 if (me->undo) {
7096 go_undo_undo (me->undo);
7097 g_clear_object (&me->undo);
7100 select_selection (me->cmd.sheet, me->selection, wbc);
7102 WORKBOOK_FOREACH_CONTROL (wb, view, ctl, {
7103 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS);
7106 return FALSE;
7109 static GnmValue *
7110 cb_hyperlink_set_text (GnmCellIter const *iter, gpointer user)
7112 CmdHyperlink *me = user;
7113 GnmCell *cell = iter->cell;
7115 if (cell == NULL)
7116 cell = sheet_cell_fetch (iter->pp.sheet,
7117 iter->pp.eval.col,
7118 iter->pp.eval.row);
7120 /* We skip non-empty cells. */
7121 if (gnm_cell_is_empty (cell) &&
7122 !gnm_cell_is_nonsingleton_array (cell)) {
7123 sheet_cell_set_value (cell, value_new_string (me->opt_content));
7124 if (me->update_size)
7125 me->cmd.size++;
7128 return NULL;
7131 static gboolean
7132 cmd_hyperlink_redo (GnmCommand *cmd, WorkbookControl *wbc)
7134 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7135 GSList *l;
7136 Workbook *wb = wb_control_get_workbook (wbc);
7137 Sheet *sheet;
7139 g_return_val_if_fail (me != NULL, TRUE);
7141 sheet = me->cmd.sheet;
7143 /* Check for locked cells */
7144 if (cmd_selection_is_locked_effective (sheet, me->selection,
7145 wbc, _("Changing Hyperlink")))
7146 return TRUE;
7148 me->undo = clipboard_copy_ranges_undo (sheet, me->selection);
7150 for (l = me->selection; l; l = l->next) {
7151 GnmRange const *r = l->data;
7153 if (me->new_style) {
7154 gnm_style_ref (me->new_style);
7155 sheet_apply_style (sheet, r, me->new_style);
7156 sheet_flag_style_update_range (sheet, r);
7159 if (me->opt_content) {
7160 sheet_foreach_cell_in_range (sheet, CELL_ITER_ALL, r,
7161 cb_hyperlink_set_text,
7162 me);
7165 me->update_size = FALSE;
7167 sheet_redraw_all (sheet, FALSE);
7168 sheet_mark_dirty (sheet);
7170 select_selection (sheet, me->selection, wbc);
7172 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
7173 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
7175 return FALSE;
7178 static void
7179 cmd_hyperlink_finalize (GObject *cmd)
7181 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7183 g_clear_object (&me->undo);
7185 if (me->new_style)
7186 gnm_style_unref (me->new_style);
7187 me->new_style = NULL;
7189 range_fragment_free (me->selection);
7190 me->selection = NULL;
7192 g_free (me->opt_content);
7194 gnm_command_finalize (cmd);
7198 * cmd_selection_hyperlink:
7199 * @wbc: the workbook control.
7200 * @style: style to apply to the selection
7201 * @opt_translated_name: An optional name to use in place of 'Hyperlink Cells'
7202 * @opt_content: optional content for otherwise empty cells.
7204 * If borders is non NULL, then the GnmBorder references are passed,
7205 * the GnmStyle reference is also passed.
7207 * It absorbs the reference to the style.
7209 * Return value: TRUE if there was a problem
7211 gboolean
7212 cmd_selection_hyperlink (WorkbookControl *wbc,
7213 GnmStyle *style,
7214 char const *opt_translated_name,
7215 char *opt_content)
7217 CmdHyperlink *me;
7218 SheetView *sv = wb_control_cur_sheet_view (wbc);
7220 me = g_object_new (CMD_HYPERLINK_TYPE, NULL);
7222 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
7223 me->new_style = style;
7225 me->cmd.sheet = sv_sheet (sv);
7226 me->cmd.size = 1; /* Updated later. */
7227 me->update_size = TRUE;
7229 me->opt_content = opt_content;
7231 if (opt_translated_name == NULL) {
7232 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
7234 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing hyperlink of %s"), names);
7235 g_free (names);
7236 } else
7237 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
7240 return gnm_command_push_undo (wbc, G_OBJECT (me));
7243 /******************************************************************/
7246 #define CMD_SO_SET_LINKS_TYPE (cmd_so_set_links_get_type ())
7247 #define CMD_SO_SET_LINKS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_LINKS_TYPE, CmdSOSetLink))
7249 typedef struct {
7250 GnmCommand cmd;
7251 SheetObject *so;
7252 GnmExprTop const *output;
7253 GnmExprTop const *content;
7254 gboolean as_index;
7255 } CmdSOSetLink;
7257 MAKE_GNM_COMMAND (CmdSOSetLink, cmd_so_set_links, NULL)
7259 static gboolean
7260 cmd_so_set_links_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7262 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7263 GnmExprTop const *old_output;
7264 GnmExprTop const *old_content;
7265 gboolean old_as_index;
7267 old_content = sheet_widget_list_base_get_content_link (me->so);
7268 old_output = sheet_widget_list_base_get_result_link (me->so);
7269 old_as_index = sheet_widget_list_base_result_type_is_index (me->so);
7271 sheet_widget_list_base_set_links
7272 (me->so, me->output, me->content);
7273 if (old_as_index != me->as_index) {
7274 sheet_widget_list_base_set_result_type (me->so, me->as_index);
7275 me->as_index = old_as_index;
7277 if (me->output)
7278 gnm_expr_top_unref (me->output);
7279 if (me->content)
7280 gnm_expr_top_unref (me->content);
7281 me->output = old_output;
7282 me->content = old_content;
7284 return FALSE;
7287 static gboolean
7288 cmd_so_set_links_undo (GnmCommand *cmd, WorkbookControl *wbc)
7290 return cmd_so_set_links_redo (cmd, wbc);
7293 static void
7294 cmd_so_set_links_finalize (GObject *cmd)
7296 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7298 if (me->output)
7299 gnm_expr_top_unref (me->output);
7300 if (me->content)
7301 gnm_expr_top_unref (me->content);
7302 gnm_command_finalize (cmd);
7305 gboolean
7306 cmd_so_set_links (WorkbookControl *wbc,
7307 SheetObject *so,
7308 GnmExprTop const *output,
7309 GnmExprTop const *content,
7310 gboolean as_index)
7312 CmdSOSetLink *me;
7314 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7316 me = g_object_new (CMD_SO_SET_LINKS_TYPE, NULL);
7317 me->cmd.sheet = sheet_object_get_sheet (so);
7318 me->cmd.size = 1;
7319 me->cmd.cmd_descriptor = g_strdup (_("Configure List"));
7320 me->so = so;
7321 me->output = output;
7322 me->content = content;
7323 me->as_index = as_index;
7325 return gnm_command_push_undo (wbc, G_OBJECT (me));
7328 /******************************************************************/
7332 #define CMD_SO_SET_FRAME_LABEL_TYPE (cmd_so_set_frame_label_get_type ())
7333 #define CMD_SO_SET_FRAME_LABEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_FRAME_LABEL_TYPE, CmdSOSetFrameLabel))
7335 typedef struct {
7336 GnmCommand cmd;
7337 SheetObject *so;
7338 char *old_label;
7339 char *new_label;
7340 } CmdSOSetFrameLabel;
7342 MAKE_GNM_COMMAND (CmdSOSetFrameLabel, cmd_so_set_frame_label, NULL)
7344 static gboolean
7345 cmd_so_set_frame_label_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7347 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7349 sheet_widget_frame_set_label (me->so, me->new_label);
7351 return FALSE;
7354 static gboolean
7355 cmd_so_set_frame_label_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7357 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7359 sheet_widget_frame_set_label (me->so, me->old_label);
7361 return FALSE;
7364 static void
7365 cmd_so_set_frame_label_finalize (GObject *cmd)
7367 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7369 g_free (me->old_label);
7370 me->old_label = NULL;
7372 g_free (me->new_label);
7373 me->new_label = NULL;
7375 gnm_command_finalize (cmd);
7378 gboolean
7379 cmd_so_set_frame_label (WorkbookControl *wbc,
7380 SheetObject *so,
7381 char *old_label, char *new_label )
7383 CmdSOSetFrameLabel *me;
7385 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7387 me = g_object_new (CMD_SO_SET_FRAME_LABEL_TYPE, NULL);
7388 me->cmd.sheet = sheet_object_get_sheet (so);
7389 me->cmd.size = 1;
7390 me->cmd.cmd_descriptor = g_strdup (_("Set Frame Label"));
7391 me->so = so;
7392 me->old_label = old_label;
7393 me->new_label = new_label;
7395 return gnm_command_push_undo (wbc, G_OBJECT (me));
7398 /******************************************************************/
7399 #define CMD_SO_SET_BUTTON_TYPE (cmd_so_set_button_get_type ())
7400 #define CMD_SO_SET_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_BUTTON_TYPE, CmdSOSetButton))
7402 typedef struct {
7403 GnmCommand cmd;
7404 SheetObject *so;
7405 GnmExprTop const *new_link;
7406 GnmExprTop const *old_link;
7407 char *old_label;
7408 char *new_label;
7409 } CmdSOSetButton;
7411 MAKE_GNM_COMMAND (CmdSOSetButton, cmd_so_set_button, NULL)
7413 static gboolean
7414 cmd_so_set_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7416 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7418 sheet_widget_button_set_link (me->so, me->new_link);
7419 sheet_widget_button_set_label (me->so, me->new_label);
7421 return FALSE;
7424 static gboolean
7425 cmd_so_set_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7427 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7429 sheet_widget_button_set_link (me->so, me->old_link);
7430 sheet_widget_button_set_label (me->so, me->old_label);
7432 return FALSE;
7435 static void
7436 cmd_so_set_button_finalize (GObject *cmd)
7438 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7440 if (me->new_link)
7441 gnm_expr_top_unref (me->new_link);
7442 if (me->old_link)
7443 gnm_expr_top_unref (me->old_link);
7444 g_free (me->old_label);
7445 g_free (me->new_label);
7446 gnm_command_finalize (cmd);
7449 gboolean
7450 cmd_so_set_button (WorkbookControl *wbc,
7451 SheetObject *so, GnmExprTop const *lnk,
7452 char *old_label, char *new_label)
7454 CmdSOSetButton *me;
7456 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7458 me = g_object_new (CMD_SO_SET_BUTTON_TYPE, NULL);
7459 me->cmd.sheet = sheet_object_get_sheet (so);
7460 me->cmd.size = 1;
7461 me->cmd.cmd_descriptor = g_strdup (_("Configure Button"));
7462 me->so = so;
7463 me->new_link = lnk;
7464 me->old_label = old_label;
7465 me->new_label = new_label;
7467 me->old_link = sheet_widget_button_get_link (so);
7469 return gnm_command_push_undo (wbc, G_OBJECT (me));
7472 /******************************************************************/
7473 #define CMD_SO_SET_RADIO_BUTTON_TYPE (cmd_so_set_radio_button_get_type ())
7474 #define CMD_SO_SET_RADIO_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_RADIO_BUTTON_TYPE, CmdSOSetRadioButton))
7476 typedef struct {
7477 GnmCommand cmd;
7478 SheetObject *so;
7479 GnmExprTop const *new_link;
7480 GnmExprTop const *old_link;
7481 char *old_label;
7482 char *new_label;
7483 GnmValue *old_value;
7484 GnmValue *new_value;
7485 } CmdSOSetRadioButton;
7487 MAKE_GNM_COMMAND (CmdSOSetRadioButton, cmd_so_set_radio_button, NULL)
7489 static gboolean
7490 cmd_so_set_radio_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7492 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7494 sheet_widget_radio_button_set_link (me->so, me->new_link);
7495 sheet_widget_radio_button_set_label (me->so, me->new_label);
7496 sheet_widget_radio_button_set_value (me->so, me->new_value);
7498 return FALSE;
7501 static gboolean
7502 cmd_so_set_radio_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7504 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7506 sheet_widget_radio_button_set_link (me->so, me->old_link);
7507 sheet_widget_radio_button_set_label (me->so, me->old_label);
7508 sheet_widget_radio_button_set_value (me->so, me->old_value);
7510 return FALSE;
7513 static void
7514 cmd_so_set_radio_button_finalize (GObject *cmd)
7516 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7518 if (me->new_link)
7519 gnm_expr_top_unref (me->new_link);
7520 if (me->old_link)
7521 gnm_expr_top_unref (me->old_link);
7522 g_free (me->old_label);
7523 g_free (me->new_label);
7524 value_release (me->old_value);
7525 value_release (me->new_value);
7526 gnm_command_finalize (cmd);
7529 gboolean
7530 cmd_so_set_radio_button (WorkbookControl *wbc,
7531 SheetObject *so, GnmExprTop const *lnk,
7532 char *old_label, char *new_label,
7533 GnmValue *old_value, GnmValue *new_value)
7535 CmdSOSetRadioButton *me;
7537 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7539 me = g_object_new (CMD_SO_SET_RADIO_BUTTON_TYPE, NULL);
7540 me->cmd.sheet = sheet_object_get_sheet (so);
7541 me->cmd.size = 1;
7542 me->cmd.cmd_descriptor = g_strdup (_("Configure Radio Button"));
7543 me->so = so;
7544 me->new_link = lnk;
7545 me->old_label = old_label;
7546 me->new_label = new_label;
7547 me->old_value = old_value;
7548 me->new_value = new_value;
7550 me->old_link = sheet_widget_radio_button_get_link (so);
7552 return gnm_command_push_undo (wbc, G_OBJECT (me));
7555 /******************************************************************/
7556 #define CMD_SO_SET_CHECKBOX_TYPE (cmd_so_set_checkbox_get_type ())
7557 #define CMD_SO_SET_CHECKBOX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_CHECKBOX_TYPE, CmdSOSetCheckbox))
7559 typedef struct {
7560 GnmCommand cmd;
7561 SheetObject *so;
7562 GnmExprTop const *new_link;
7563 GnmExprTop const *old_link;
7564 char *old_label;
7565 char *new_label;
7566 } CmdSOSetCheckbox;
7568 MAKE_GNM_COMMAND (CmdSOSetCheckbox, cmd_so_set_checkbox, NULL)
7570 static gboolean
7571 cmd_so_set_checkbox_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7573 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7575 sheet_widget_checkbox_set_link (me->so, me->new_link);
7576 sheet_widget_checkbox_set_label (me->so, me->new_label);
7578 return FALSE;
7581 static gboolean
7582 cmd_so_set_checkbox_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7584 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7586 sheet_widget_checkbox_set_link (me->so, me->old_link);
7587 sheet_widget_checkbox_set_label (me->so, me->old_label);
7589 return FALSE;
7592 static void
7593 cmd_so_set_checkbox_finalize (GObject *cmd)
7595 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7597 if (me->new_link)
7598 gnm_expr_top_unref (me->new_link);
7599 if (me->old_link)
7600 gnm_expr_top_unref (me->old_link);
7601 g_free (me->old_label);
7602 g_free (me->new_label);
7603 gnm_command_finalize (cmd);
7606 gboolean
7607 cmd_so_set_checkbox (WorkbookControl *wbc,
7608 SheetObject *so, GnmExprTop const *lnk,
7609 char *old_label, char *new_label)
7611 CmdSOSetCheckbox *me;
7613 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7615 me = g_object_new (CMD_SO_SET_CHECKBOX_TYPE, NULL);
7616 me->cmd.sheet = sheet_object_get_sheet (so);
7617 me->cmd.size = 1;
7618 me->cmd.cmd_descriptor = g_strdup (_("Configure Checkbox"));
7619 me->so = so;
7620 me->new_link = lnk;
7621 me->old_label = old_label;
7622 me->new_label = new_label;
7624 me->old_link = sheet_widget_checkbox_get_link (so);
7626 return gnm_command_push_undo (wbc, G_OBJECT (me));
7629 /******************************************************************/
7631 #define CMD_SO_SET_ADJUSTMENT_TYPE (cmd_so_set_adjustment_get_type ())
7632 #define CMD_SO_SET_ADJUSTMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_ADJUSTMENT_TYPE, CmdSOSetAdjustment))
7634 typedef struct {
7635 GnmCommand cmd;
7636 SheetObject *so;
7637 GnmExprTop const *new_link;
7638 GnmExprTop const *old_link;
7639 double old_lower;
7640 double old_upper;
7641 double old_step;
7642 double old_page;
7643 gboolean old_horizontal;
7644 } CmdSOSetAdjustment;
7646 MAKE_GNM_COMMAND (CmdSOSetAdjustment, cmd_so_set_adjustment, NULL)
7648 static void
7649 cmd_so_set_adjustment_adj (CmdSOSetAdjustment *me)
7651 GtkAdjustment *adj = sheet_widget_adjustment_get_adjustment (me->so);
7653 double old_lower = gtk_adjustment_get_lower (adj);
7654 double old_upper = gtk_adjustment_get_upper (adj);
7655 double old_step = gtk_adjustment_get_step_increment (adj);
7656 double old_page = gtk_adjustment_get_page_increment (adj);
7657 gboolean old_horizontal;
7658 g_object_get (G_OBJECT (me->so), "horizontal", &old_horizontal, NULL);
7660 gtk_adjustment_configure (adj,
7661 gtk_adjustment_get_value (adj),
7662 me->old_lower,
7663 me->old_upper,
7664 me->old_step,
7665 me->old_page,
7666 gtk_adjustment_get_page_size (adj));
7667 g_object_set (G_OBJECT (me->so), "horizontal", me->old_horizontal, NULL);
7669 me->old_lower = old_lower;
7670 me->old_upper = old_upper;
7671 me->old_step = old_step;
7672 me->old_page = old_page;
7673 me->old_horizontal = old_horizontal;
7676 static gboolean
7677 cmd_so_set_adjustment_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7679 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7681 sheet_widget_adjustment_set_link (me->so, me->new_link);
7682 cmd_so_set_adjustment_adj (me);
7683 return FALSE;
7686 static gboolean
7687 cmd_so_set_adjustment_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7689 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7691 sheet_widget_adjustment_set_link (me->so, me->old_link);
7692 cmd_so_set_adjustment_adj (me);
7694 return FALSE;
7697 static void
7698 cmd_so_set_adjustment_finalize (GObject *cmd)
7700 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7702 if (me->new_link)
7703 gnm_expr_top_unref (me->new_link);
7704 if (me->old_link)
7705 gnm_expr_top_unref (me->old_link);
7706 gnm_command_finalize (cmd);
7709 gboolean
7710 cmd_so_set_adjustment (WorkbookControl *wbc,
7711 SheetObject *so, GnmExprTop const *lnk,
7712 gboolean horizontal,
7713 int lower, int upper,
7714 int step, int page,
7715 char const *undo_label)
7717 CmdSOSetAdjustment *me;
7719 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7721 me = g_object_new (CMD_SO_SET_ADJUSTMENT_TYPE, NULL);
7722 me->cmd.sheet = sheet_object_get_sheet (so);
7723 me->cmd.size = 1;
7724 me->cmd.cmd_descriptor = g_strdup ((undo_label == NULL) ?
7725 _("Configure Adjustment") : _(undo_label));
7726 me->so = so;
7727 me->new_link = lnk;
7728 me->old_lower = lower;
7729 me->old_upper = upper;
7730 me->old_step = step;
7731 me->old_page = page;
7732 me->old_horizontal = horizontal;
7734 me->old_link = sheet_widget_adjustment_get_link (so);
7736 return gnm_command_push_undo (wbc, G_OBJECT (me));
7739 /******************************************************************/
7741 gboolean
7742 cmd_autofilter_add_remove (WorkbookControl *wbc)
7744 SheetView *sv = wb_control_cur_sheet_view (wbc);
7745 GnmFilter *f = gnm_sheet_view_editpos_in_filter (sv);
7746 gboolean add = (f == NULL);
7747 char *descr = NULL, *name = NULL;
7748 GOUndo *undo = NULL;
7749 GOUndo *redo = NULL;
7750 gboolean result;
7753 if (add) {
7754 GnmRange region;
7755 GnmRange const *src = selection_first_range (sv,
7756 GO_CMD_CONTEXT (wbc), _("Add Filter"));
7757 GnmFilter *f_old = NULL;
7759 if (src == NULL)
7760 return TRUE;
7762 f_old = gnm_sheet_filter_intersect_rows
7763 (sv->sheet, src->start.row, src->end.row);
7765 if (f_old != NULL) {
7766 GnmRange *r = gnm_sheet_filter_can_be_extended
7767 (sv->sheet, f_old, src);
7768 if (r == NULL) {
7769 char *error;
7770 name = undo_range_name (sv->sheet, &(f_old->r));
7771 error = g_strdup_printf
7772 (_("Auto Filter blocked by %s"),
7773 name);
7774 g_free(name);
7775 go_cmd_context_error_invalid
7776 (GO_CMD_CONTEXT (wbc),
7777 _("AutoFilter"), error);
7778 g_free (error);
7779 return TRUE;
7781 /* extending existing filter. */
7782 undo = go_undo_binary_new
7783 (gnm_filter_ref (f_old), sv->sheet,
7784 (GOUndoBinaryFunc) gnm_filter_attach,
7785 (GFreeFunc) gnm_filter_unref,
7786 NULL);
7787 redo = go_undo_unary_new
7788 (gnm_filter_ref (f_old),
7789 (GOUndoUnaryFunc) gnm_filter_remove,
7790 (GFreeFunc) gnm_filter_unref);
7791 gnm_filter_remove (f_old);
7792 region = *r;
7793 g_free (r);
7794 } else {
7795 /* if only one row is selected
7796 * assume that the user wants to
7797 * filter the region below this row. */
7798 region = *src;
7799 if (src->start.row == src->end.row)
7800 gnm_sheet_guess_region (sv->sheet, &region);
7801 if (region.start.row == region.end.row) {
7802 go_cmd_context_error_invalid
7803 (GO_CMD_CONTEXT (wbc),
7804 _("AutoFilter"),
7805 _("Requires more than 1 row"));
7806 return TRUE;
7809 f = gnm_filter_new (sv->sheet, &region);
7810 if (f == NULL) {
7811 go_cmd_context_error_invalid
7812 (GO_CMD_CONTEXT (wbc),
7813 _("AutoFilter"),
7814 _("Unable to create Autofilter"));
7815 if (f_old)
7816 gnm_filter_attach (f_old, sv->sheet);
7817 return TRUE;
7820 gnm_filter_remove (f);
7821 if (f_old)
7822 gnm_filter_attach (f_old, sv->sheet);
7824 redo = go_undo_combine (go_undo_binary_new
7825 (gnm_filter_ref (f), sv->sheet,
7826 (GOUndoBinaryFunc) gnm_filter_attach,
7827 (GFreeFunc) gnm_filter_unref,
7828 NULL), redo);
7829 undo = go_undo_combine (undo,
7830 go_undo_unary_new
7832 (GOUndoUnaryFunc) gnm_filter_remove,
7833 (GFreeFunc) gnm_filter_unref));
7835 name = undo_range_name (sv->sheet, &(f->r));
7836 descr = g_strdup_printf
7837 ((f_old == NULL) ? _("Add Autofilter to %s")
7838 : _("Extend Autofilter to %s"),
7839 name);
7840 } else {
7841 undo = go_undo_binary_new
7842 (gnm_filter_ref (f), sv->sheet,
7843 (GOUndoBinaryFunc) gnm_filter_attach,
7844 (GFreeFunc) gnm_filter_unref,
7845 NULL);
7846 redo = go_undo_unary_new
7847 (gnm_filter_ref (f),
7848 (GOUndoUnaryFunc) gnm_filter_remove,
7849 (GFreeFunc) gnm_filter_unref);
7850 name = undo_range_name (sv->sheet, &(f->r));
7851 descr = g_strdup_printf (_("Remove Autofilter from %s"),
7852 name);
7854 result = cmd_generic (wbc, descr, undo, redo);
7855 g_free (name);
7856 g_free (descr);
7858 return result;
7862 /******************************************************************/
7864 gboolean cmd_autofilter_set_condition (WorkbookControl *wbc,
7865 GnmFilter *filter, unsigned i,
7866 GnmFilterCondition *cond)
7868 char *descr = NULL, *name = NULL;
7869 GOUndo *undo = NULL;
7870 GOUndo *redo = NULL;
7871 gboolean result;
7873 undo = gnm_undo_filter_set_condition_new (filter, i,
7874 NULL, TRUE);
7875 g_return_val_if_fail (undo != NULL, TRUE);
7876 redo = gnm_undo_filter_set_condition_new (filter, i,
7877 cond, FALSE);
7878 g_return_val_if_fail (redo != NULL, TRUE);
7880 name = undo_range_name (filter->sheet, &(filter->r));
7881 descr = g_strdup_printf (_("Change filter condition for %s"),
7882 name);
7884 result = cmd_generic (wbc, descr, undo, redo);
7885 g_free (name);
7886 g_free (descr);
7888 return result;
7892 /******************************************************************/
7894 static void
7895 cmd_page_breaks_set_breaks (Sheet *sheet,
7896 GnmPageBreaks const *breaks)
7898 print_info_set_breaks (sheet->print_info, gnm_page_breaks_dup (breaks));
7900 SHEET_FOREACH_CONTROL (sheet, sv, sc, wb_control_menu_state_update (sc_wbc (sc), MS_PAGE_BREAKS););
7903 gboolean
7904 cmd_page_breaks_clear (WorkbookControl *wbc, Sheet *sheet)
7906 GOUndo *undo = NULL;
7907 GOUndo *redo = NULL;
7909 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7910 g_return_val_if_fail (sheet != NULL, TRUE);
7912 if (sheet->print_info->page_breaks.v != NULL) {
7913 redo = go_undo_binary_new
7914 (sheet,
7915 gnm_page_breaks_new (TRUE),
7916 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7917 NULL,
7918 (GFreeFunc) gnm_page_breaks_free);
7919 undo = go_undo_binary_new
7920 (sheet,
7921 gnm_page_breaks_dup
7922 (sheet->print_info->page_breaks.v),
7923 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7924 NULL,
7925 (GFreeFunc) gnm_page_breaks_free);
7928 if (sheet->print_info->page_breaks.h != NULL) {
7929 redo = go_undo_combine
7930 (redo,
7931 go_undo_binary_new
7932 (sheet,
7933 gnm_page_breaks_new (FALSE),
7934 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7935 NULL,
7936 (GFreeFunc) gnm_page_breaks_free));
7938 undo = go_undo_combine
7939 (undo,
7940 go_undo_binary_new
7941 (sheet,
7942 gnm_page_breaks_dup
7943 (sheet->print_info->page_breaks.h),
7944 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7945 NULL,
7946 (GFreeFunc) gnm_page_breaks_free));
7949 if (undo != NULL)
7950 return cmd_generic (wbc, _("Clear All Page Breaks"), undo, redo);
7951 else
7952 return TRUE;
7955 gboolean
7956 cmd_page_break_toggle (WorkbookControl *wbc, Sheet *sheet, gboolean is_vert)
7958 SheetView const *sv = wb_control_cur_sheet_view (wbc);
7959 gint col = sv->edit_pos.col;
7960 gint row = sv->edit_pos.row;
7961 int rc = is_vert ? col : row;
7962 GnmPageBreaks *old, *new, *target;
7963 GnmPageBreakType type;
7964 char const *label;
7965 GOUndo *undo;
7966 GOUndo *redo;
7968 target = is_vert ? sheet->print_info->page_breaks.v
7969 : sheet->print_info->page_breaks.h;
7971 old = (target == NULL) ? gnm_page_breaks_new (is_vert)
7972 : gnm_page_breaks_dup (target);
7973 new = gnm_page_breaks_dup (old);
7975 if (gnm_page_breaks_get_break (new, rc) != GNM_PAGE_BREAK_MANUAL) {
7976 type = GNM_PAGE_BREAK_MANUAL;
7977 label = is_vert ? _("Remove Column Page Break") : _("Remove Row Page Break");
7978 } else {
7979 type = GNM_PAGE_BREAK_NONE;
7980 label = is_vert ? _("Add Column Page Break") : _("Add Row Page Break");
7983 gnm_page_breaks_set_break (new, rc, type);
7985 redo = go_undo_binary_new
7986 (sheet, new,
7987 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7988 NULL,
7989 (GFreeFunc) gnm_page_breaks_free);
7990 undo = go_undo_binary_new
7991 (sheet, old,
7992 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7993 NULL,
7994 (GFreeFunc) gnm_page_breaks_free);
7996 return cmd_generic (wbc, label, undo, redo);
7999 /******************************************************************/