Introspection: add col/row manipulations
[gnumeric.git] / src / commands.c
blob4ecddd717f4552c80c097a9e17b1379035c6c2fb
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 sv_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 sv_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,
867 r->start.col, r->start.row,
868 r->end.col, r->end.row,
869 (CellIterFunc) cmd_set_text_full_check_texpr,
870 (gpointer) texpr);
872 same_texpr = (val != VALUE_TERMINATE);
873 if (val != NULL && same_texpr)
874 value_release (val);
877 if (same_texpr) {
878 gnm_expr_top_unref (texpr);
879 g_free (name);
880 range_fragment_free (selection);
881 return TRUE;
884 text = g_strdup_printf (_("Inserting expression in %s"), name);
886 if (go_format_is_general (format)) {
887 sf = gnm_auto_style_format_suggest (texpr, ep);
888 if (sf != NULL) {
889 new_style = gnm_style_new ();
890 gnm_style_set_format (new_style, sf);
891 go_format_unref (sf);
895 for (l = selection; l != NULL; l = l->next) {
896 GnmSheetRange *sr;
897 undo = go_undo_combine
898 (undo, clipboard_copy_range_undo (sheet, l->data));
899 sr = gnm_sheet_range_new (sheet, l->data);
900 redo = go_undo_combine
901 (redo, sheet_range_set_expr_undo (sr, texpr));
902 if (new_style) {
903 sr = gnm_sheet_range_new (sheet, l->data);
904 redo = go_undo_combine
905 (redo, sheet_apply_style_undo (sr, new_style));
908 if (new_style)
909 gnm_style_unref (new_style);
910 gnm_expr_top_unref (texpr);
911 autofit_col = TRUE;
912 } else {
913 GString *text_str;
914 PangoAttrList *adj_markup = NULL;
915 char *corrected;
916 gboolean same_text = TRUE;
917 gboolean same_markup = TRUE;
919 if (new_text == NULL)
920 corrected = NULL;
921 else if (autocorrect)
922 corrected = autocorrect_tool (new_text);
923 else
924 corrected = g_strdup (new_text);
926 if (corrected && (corrected[0] == '\'') && corrected[1] == '\0') {
927 g_free (corrected);
928 corrected = g_strdup ("");
931 /* We should check whether we are in fact changing anything: */
932 /* We'll handle */
933 for (l = selection; l != NULL && same_text; l = l->next) {
934 GnmRange *r = l->data;
935 GnmValue *val =
936 sheet_foreach_cell_in_range
937 (sheet, CELL_ITER_ALL,
938 r->start.col, r->start.row,
939 r->end.col, r->end.row,
940 (CellIterFunc) cmd_set_text_full_check_text,
941 (gpointer) corrected);
943 same_text = (val != VALUE_TERMINATE);
944 if (val != NULL && same_text)
945 value_release (val);
948 if (go_pango_attr_list_is_empty (markup))
949 markup = NULL;
950 if (markup && corrected && corrected[0] == '\'') {
951 markup = adj_markup = pango_attr_list_copy (markup);
952 go_pango_attr_list_erase (adj_markup, 0, 1);
955 if (same_text) {
956 for (l = selection; l != NULL && same_text; l = l->next) {
957 GnmRange *r = l->data;
958 GnmValue *val =
959 sheet_foreach_cell_in_range
960 (sheet, CELL_ITER_IGNORE_BLANK,
961 r->start.col, r->start.row,
962 r->end.col, r->end.row,
963 (CellIterFunc) cmd_set_text_full_check_markup,
964 (gpointer) markup);
966 same_markup = (val != VALUE_TERMINATE);
967 if (val != NULL && same_markup)
968 value_release (val);
971 if (same_markup) {
972 g_free (corrected);
973 g_free (name);
974 range_fragment_free (selection);
975 if (adj_markup)
976 pango_attr_list_unref (adj_markup);
977 return TRUE;
980 text = g_strdup_printf (_("Editing style of %s"), name);
981 } else {
982 text_str = gnm_cmd_trunc_descriptor (g_string_new (corrected), NULL);
983 text = g_strdup_printf (_("Typing \"%s\" in %s"), text_str->str, name);
984 g_string_free (text_str, TRUE);
987 for (l = selection; l != NULL; l = l->next) {
988 GnmSheetRange *sr;
989 undo = go_undo_combine
990 (undo, clipboard_copy_range_undo (sheet, l->data));
991 if (corrected) {
992 sr = gnm_sheet_range_new (sheet, l->data);
993 redo = go_undo_combine
994 (redo, sheet_range_set_text_undo
995 (sr, corrected));
997 if (markup) {
998 sr = gnm_sheet_range_new (sheet, l->data);
999 /* Note: order of combination matters!! */
1000 redo = go_undo_combine
1001 (sheet_range_set_markup_undo (sr, markup), redo);
1005 if (adj_markup)
1006 pango_attr_list_unref (adj_markup);
1007 g_free (corrected);
1009 same_text_and_not_same_markup = (same_text && !same_markup);
1011 g_free (name);
1013 /* We are combining this since we don't want to apply and undo twice.*/
1014 if (same_text_and_not_same_markup || !autofit_col) {
1015 GnmCell *cell = sheet_cell_fetch
1016 (sheet, ep->eval.col, ep->eval.row);
1017 gboolean nvis;
1019 go_undo_undo (redo);
1020 nvis = !VALUE_IS_STRING (cell->value);
1021 if (!autofit_col)
1022 autofit_col = nvis;
1023 if (same_text_and_not_same_markup)
1024 /* We only have to do something if at least one cell */
1025 /* now contains a string, but they contain all the same thing. */
1026 same_text_and_not_same_markup = nvis;
1027 go_undo_undo (undo);
1029 if (same_text_and_not_same_markup) {
1030 /*We had the same text and different markup but we are not entering strings. */
1031 g_object_unref (undo);
1032 g_object_unref (redo);
1033 g_free (text);
1034 range_fragment_free (selection);
1035 return TRUE;
1037 for (l = selection; l != NULL; l = l->next) {
1038 GnmRange *r = l->data;
1039 GnmRange *new_r;
1041 new_r = g_new (GnmRange, 1);
1042 *new_r = *r;
1043 redo = go_undo_combine
1044 (go_undo_binary_new
1045 (sheet, new_r,
1046 (GOUndoBinaryFunc) colrow_autofit_row,
1047 NULL, g_free),
1048 redo);
1049 cri_row_list = colrow_get_index_list
1050 (r->start.row, r->end.row, cri_row_list);
1052 if (autofit_col) {
1053 new_r = g_new (GnmRange, 1);
1054 *new_r = *r;
1055 redo = go_undo_combine
1056 (go_undo_binary_new
1057 (sheet, new_r,
1058 (GOUndoBinaryFunc) colrow_autofit_col,
1059 NULL, g_free),
1060 redo);
1061 cri_col_list = colrow_get_index_list
1062 (r->start.col, r->end.col, cri_col_list);
1065 undo = go_undo_combine (undo,
1066 gnm_undo_colrow_restore_state_group_new
1067 (sheet, TRUE,
1068 cri_col_list,
1069 colrow_get_sizes (sheet, TRUE,
1070 cri_col_list, -1)));
1071 undo = go_undo_combine (undo,
1072 gnm_undo_colrow_restore_state_group_new
1073 (sheet, FALSE,
1074 cri_row_list,
1075 colrow_get_sizes (sheet, FALSE,
1076 cri_row_list, -1)));
1079 result = cmd_generic (wbc, text, undo, redo);
1080 g_free (text);
1081 range_fragment_free (selection);
1082 return result;
1086 * cmd_area_set_text
1088 * the caller is expected to have ensured:
1090 * 1) that no array is being split
1091 * 2) that the range is not locked.
1095 gboolean
1096 cmd_area_set_text (WorkbookControl *wbc, SheetView *sv,
1097 char const *new_text, PangoAttrList *markup)
1099 GnmEvalPos ep;
1100 gboolean result;
1101 GSList *selection = selection_get_ranges (sv, FALSE);
1103 eval_pos_init_editpos (&ep, sv);
1104 result = cmd_set_text_full (wbc, selection, &ep,
1105 new_text, markup, TRUE);
1106 return result;
1109 gboolean
1110 cmd_set_text (WorkbookControl *wbc,
1111 Sheet *sheet, GnmCellPos const *pos,
1112 char const *new_text,
1113 PangoAttrList *markup,
1114 gboolean autocorrect)
1116 GnmCell const *cell;
1117 GnmEvalPos ep;
1118 gboolean result;
1119 GSList *selection;
1120 GnmRange *r;
1122 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1123 g_return_val_if_fail (new_text != NULL, TRUE);
1125 /* Ensure that we are not splitting up an array */
1126 cell = sheet_cell_get (sheet, pos->col, pos->row);
1127 if (gnm_cell_is_nonsingleton_array (cell)) {
1128 gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc),
1129 _("Set Text"), NULL);
1130 return TRUE;
1133 eval_pos_init_pos (&ep, sheet, pos);
1134 r = g_new (GnmRange, 1);
1135 r->start = r->end = *pos;
1136 selection = g_slist_prepend (NULL, r);
1137 result = cmd_set_text_full (wbc, selection, &ep,
1138 new_text, markup, autocorrect);
1139 return result;
1144 * cmd_area_set_array_expr
1146 * the caller is expected to have ensured:
1148 * 1) that no array is being split
1149 * 2) that the selection consists of a single range
1150 * 3) that the range is not locked.
1154 gboolean
1155 cmd_area_set_array_expr (WorkbookControl *wbc, SheetView *sv,
1156 GnmExprTop const *texpr)
1158 GSList *selection = selection_get_ranges (sv, FALSE);
1159 GOUndo *undo = NULL;
1160 GOUndo *redo = NULL;
1161 gboolean result;
1162 Sheet *sheet = sv_sheet (sv);
1163 char *name;
1164 char *text;
1165 GnmSheetRange *sr;
1166 GnmRange *r_1, *r_2, *r;
1167 ColRowIndexList *cri_col_list;
1168 ColRowIndexList *cri_row_list;
1170 g_return_val_if_fail (selection != NULL , TRUE);
1171 g_return_val_if_fail (selection->next == NULL , TRUE);
1173 name = undo_range_list_name (sheet, selection);
1174 text = g_strdup_printf (_("Inserting array expression in %s"), name);
1175 g_free (name);
1177 r = selection->data;
1179 cri_row_list = colrow_get_index_list
1180 (r->start.row, r->end.row, NULL);
1181 cri_col_list = colrow_get_index_list
1182 (r->start.col, r->end.col, NULL);
1183 undo = clipboard_copy_range_undo (sheet, selection->data);
1184 undo = go_undo_combine (undo,
1185 gnm_undo_colrow_restore_state_group_new
1186 (sheet, TRUE,
1187 cri_col_list,
1188 colrow_get_sizes (sheet, TRUE,
1189 cri_col_list, -1)));
1190 undo = go_undo_combine (undo,
1191 gnm_undo_colrow_restore_state_group_new
1192 (sheet, FALSE,
1193 cri_row_list,
1194 colrow_get_sizes (sheet, FALSE,
1195 cri_row_list, -1)));
1197 sr = gnm_sheet_range_new (sheet, r);
1198 r_1 = g_new (GnmRange, 1);
1199 *r_1 = *r;
1200 r_2 = g_new (GnmRange, 1);
1201 *r_2 = *r;
1202 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1203 redo = go_undo_combine
1204 (go_undo_binary_new
1205 (sheet, r_1,
1206 (GOUndoBinaryFunc) colrow_autofit_col,
1207 NULL, g_free),
1208 redo);
1209 redo = go_undo_combine
1210 (go_undo_binary_new
1211 (sheet, r_2,
1212 (GOUndoBinaryFunc) colrow_autofit_row,
1213 NULL, g_free),
1214 redo);
1216 range_fragment_free (selection);
1217 result = cmd_generic (wbc, text, undo, redo);
1218 g_free (text);
1219 return result;
1223 * cmd_create_data_table
1225 * the caller is expected to have ensured:
1227 * 1) that no array is being split
1228 * 2) that the range is not locked.
1231 gboolean
1232 cmd_create_data_table (WorkbookControl *wbc, Sheet *sheet, GnmRange const *r,
1233 char const *col_input, char const *row_input)
1235 GOUndo *undo = NULL;
1236 GOUndo *redo = NULL;
1237 gboolean result;
1238 char *name;
1239 char *text;
1240 GnmSheetRange *sr;
1241 GnmParsePos pp;
1242 GnmExprTop const *texpr;
1244 name = undo_range_name (sheet, r);
1245 text = g_strdup_printf (_("Creating a Data Table in %s"), name);
1246 g_free (name);
1248 undo = clipboard_copy_range_undo (sheet, r);
1250 sr = gnm_sheet_range_new (sheet, r);
1251 parse_pos_init (&pp, NULL, sheet, r->start.col, r->start.row);
1252 name = g_strdup_printf ("TABLE(%s,%s)", row_input, col_input);
1253 texpr = gnm_expr_parse_str
1254 (name, &pp, GNM_EXPR_PARSE_DEFAULT,
1255 sheet_get_conventions (sheet), NULL);
1256 g_free (name);
1258 if (texpr == NULL) {
1259 g_object_unref (undo);
1260 g_free (text);
1261 return TRUE;
1264 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1265 gnm_expr_top_unref (texpr);
1267 result = cmd_generic (wbc, text, undo, redo);
1268 g_free (text);
1269 return result;
1272 /******************************************************************/
1274 #define CMD_INS_DEL_COLROW_TYPE (cmd_ins_del_colrow_get_type ())
1275 #define CMD_INS_DEL_COLROW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_INS_DEL_COLROW_TYPE, CmdInsDelColRow))
1277 typedef struct {
1278 GnmCommand cmd;
1280 Sheet *sheet;
1281 gboolean is_insert;
1282 gboolean is_cols;
1283 gboolean is_cut;
1284 int index;
1285 int count;
1286 GnmRange *cutcopied;
1287 SheetView *cut_copy_view;
1289 gboolean (*redo_action) (Sheet *sheet, int col, int count,
1290 GOUndo **pundo, GOCmdContext *cc);
1292 gboolean (*repeat_action) (WorkbookControl *wbc, Sheet *sheet,
1293 int start, int count);
1295 GOUndo *undo;
1296 } CmdInsDelColRow;
1298 static void
1299 cmd_ins_del_colrow_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1301 CmdInsDelColRow const *orig = (CmdInsDelColRow const *) cmd;
1302 SheetView *sv = wb_control_cur_sheet_view (wbc);
1303 Sheet *sheet = sv_sheet (sv);
1304 GnmRange const *r = selection_first_range (sv,
1305 GO_CMD_CONTEXT (wbc), _("Ins/Del Column/Row"));
1306 int start, count;
1308 if (r == NULL)
1309 return;
1311 if (orig->is_cols)
1312 start = r->start.col, count = range_width (r);
1313 else
1314 start = r->start.row, count = range_height (r);
1316 orig->repeat_action (wbc, sheet, start, count);
1319 MAKE_GNM_COMMAND (CmdInsDelColRow, cmd_ins_del_colrow, cmd_ins_del_colrow_repeat)
1321 static gboolean
1322 cmd_ins_del_colrow_undo (GnmCommand *cmd, WorkbookControl *wbc)
1324 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1326 if (me->undo) {
1327 go_undo_undo (me->undo);
1328 g_object_unref (me->undo);
1329 me->undo = NULL;
1332 /* Ins/Del Row/Col re-ants things completely to account
1333 * for the shift of col/rows.
1335 if (me->cutcopied != NULL && me->cut_copy_view != NULL)
1336 gnm_app_clipboard_cut_copy (wbc, me->is_cut, me->cut_copy_view,
1337 me->cutcopied, FALSE);
1339 return FALSE;
1342 static gboolean
1343 cmd_ins_del_colrow_redo (GnmCommand *cmd, WorkbookControl *wbc)
1345 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1346 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
1347 int idx = me->index;
1348 int count = me->count;
1350 if (me->redo_action (me->sheet, idx, count, &me->undo, cc)) {
1351 /* Trouble. */
1352 return TRUE;
1355 /* Ins/Del Row/Col re-ants things completely to account
1356 * for the shift of col/rows. */
1357 if (me->cutcopied != NULL && me->cut_copy_view != NULL) {
1358 if (me->is_cut) {
1359 GnmRange s = *me->cutcopied;
1360 int key = me->is_insert ? count : -count;
1361 int threshold = me->is_insert ? idx : idx + 1;
1364 * Really only applies if the regions that are
1365 * inserted/deleted are above the cut/copied region.
1367 if (me->is_cols) {
1368 if (threshold <= s.start.col) {
1369 s.start.col += key;
1370 s.end.col += key;
1372 } else if (threshold <= s.start.row) {
1373 s.start.row += key;
1374 s.end.row += key;
1377 gnm_app_clipboard_cut_copy (wbc, me->is_cut,
1378 me->cut_copy_view,
1379 &s, FALSE);
1380 } else
1381 gnm_app_clipboard_unant ();
1384 return FALSE;
1387 static void
1388 cmd_ins_del_colrow_finalize (GObject *cmd)
1390 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1392 if (me->undo)
1393 g_object_unref (me->undo);
1395 g_free (me->cutcopied);
1397 sv_weak_unref (&(me->cut_copy_view));
1399 gnm_command_finalize (cmd);
1402 static gboolean
1403 cmd_ins_del_colrow (WorkbookControl *wbc,
1404 Sheet *sheet,
1405 gboolean is_cols, gboolean is_insert,
1406 char const *descriptor, int index, int count)
1408 CmdInsDelColRow *me;
1409 int first, last;
1410 GnmRange r;
1412 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1413 g_return_val_if_fail (count > 0, TRUE);
1415 me = g_object_new (CMD_INS_DEL_COLROW_TYPE, NULL);
1417 me->sheet = sheet;
1418 me->is_cols = is_cols;
1419 me->is_insert = is_insert;
1420 me->index = index;
1421 me->count = count;
1422 me->redo_action = me->is_insert
1423 ? (me->is_cols ? sheet_insert_cols : sheet_insert_rows)
1424 : (me->is_cols ? sheet_delete_cols : sheet_delete_rows);
1425 me->repeat_action = me->is_insert
1426 ? (me->is_cols ? cmd_insert_cols : cmd_insert_rows)
1427 : (me->is_cols ? cmd_delete_cols : cmd_delete_rows);
1429 /* Range that will get deleted. */
1430 first = me->is_insert
1431 ? colrow_max (is_cols, sheet) - count
1432 : index;
1433 last = first + count - 1;
1434 (is_cols ? range_init_cols : range_init_rows) (&r, sheet, first, last);
1436 /* Note: redo_action checks for array subdivision. */
1438 /* Check for locks */
1439 if (cmd_cell_range_is_locked_effective (sheet, &r, wbc, descriptor)) {
1440 g_object_unref (me);
1441 return TRUE;
1444 /* We store the cut or/copied range if applicable */
1445 if (!gnm_app_clipboard_is_empty () &&
1446 gnm_app_clipboard_area_get () &&
1447 sheet == gnm_app_clipboard_sheet_get ()) {
1448 me->cutcopied = gnm_range_dup (gnm_app_clipboard_area_get ());
1449 me->is_cut = gnm_app_clipboard_is_cut ();
1450 sv_weak_ref (gnm_app_clipboard_sheet_view_get (),
1451 &(me->cut_copy_view));
1452 } else
1453 me->cutcopied = NULL;
1455 me->cmd.sheet = sheet;
1456 me->cmd.size = count * 10; /* FIXME? */
1457 me->cmd.cmd_descriptor = descriptor;
1459 return gnm_command_push_undo (wbc, G_OBJECT (me));
1462 gboolean
1463 cmd_insert_cols (WorkbookControl *wbc,
1464 Sheet *sheet, int start_col, int count)
1466 char *mesg;
1467 GnmRange r;
1469 range_init_full_sheet (&r, sheet);
1470 r.start.col = r.end.col - count + 1;
1472 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1473 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1474 ngettext ("Inserting %i column before column %s would push data off the sheet. "
1475 "Please enlarge the sheet first.",
1476 "Inserting %i columns before column %s would push data off the sheet. "
1477 "Please enlarge the sheet first.",
1478 count),
1479 count, col_name (start_col));
1480 return TRUE;
1483 mesg = g_strdup_printf
1484 (ngettext ("Inserting %d column before %s",
1485 "Inserting %d columns before %s",
1486 count),
1487 count, col_name (start_col));
1488 return cmd_ins_del_colrow (wbc, sheet, TRUE, TRUE, mesg, start_col, count);
1491 gboolean
1492 cmd_insert_rows (WorkbookControl *wbc,
1493 Sheet *sheet, int start_row, int count)
1495 char *mesg;
1496 GnmRange r;
1498 range_init_full_sheet (&r, sheet);
1499 r.start.row = r.end.row - count + 1;
1501 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1502 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1503 ngettext ("Inserting %i row before row %s would push data off the sheet. "
1504 "Please enlarge the sheet first.",
1505 "Inserting %i rows before row %s would push data off the sheet. "
1506 "Please enlarge the sheet first.",
1507 count),
1508 count, row_name (start_row));
1509 return TRUE;
1512 mesg = g_strdup_printf
1513 (ngettext ("Inserting %d row before %s",
1514 "Inserting %d rows before %s",
1515 count),
1516 count, row_name (start_row));
1517 return cmd_ins_del_colrow (wbc, sheet, FALSE, TRUE, mesg, start_row, count);
1520 gboolean
1521 cmd_delete_cols (WorkbookControl *wbc,
1522 Sheet *sheet, int start_col, int count)
1524 char *mesg = g_strdup_printf ((count > 1)
1525 ? _("Deleting columns %s")
1526 : _("Deleting column %s"),
1527 cols_name (start_col, start_col + count - 1));
1528 return cmd_ins_del_colrow (wbc, sheet, TRUE, FALSE, mesg, start_col, count);
1531 gboolean
1532 cmd_delete_rows (WorkbookControl *wbc,
1533 Sheet *sheet, int start_row, int count)
1535 char *mesg = g_strdup_printf ((count > 1)
1536 ? _("Deleting rows %s")
1537 : _("Deleting row %s"),
1538 rows_name (start_row, start_row + count - 1));
1539 return cmd_ins_del_colrow (wbc, sheet, FALSE, FALSE, mesg, start_row, count);
1542 /******************************************************************/
1544 typedef struct {
1545 GSList *selection;
1546 GnmRange const *r;
1547 } cmd_selection_clear_row_handler_t;
1549 static gboolean
1550 cmd_selection_clear_row_handler (GnmColRowIter const *iter,
1551 cmd_selection_clear_row_handler_t *data)
1553 if ((!iter->cri->in_filter) || iter->cri->visible) {
1554 GnmRange *r = gnm_range_dup (data->r);
1555 r->start.row = r->end.row = iter->pos;
1556 data->selection = g_slist_prepend (data->selection, r);
1558 return FALSE;
1561 gboolean
1562 cmd_selection_clear (WorkbookControl *wbc, int clear_flags)
1564 char *names, *descriptor;
1565 GString *types;
1566 SheetView *sv = wb_control_cur_sheet_view (wbc);
1567 GSList *selection = selection_get_ranges (sv, FALSE /* No intersection */);
1568 Sheet *sheet = sv_sheet (sv);
1569 gboolean result;
1570 int size;
1571 GOUndo *undo = NULL;
1572 GOUndo *redo = NULL;
1573 GSList *ranges;
1575 if ((clear_flags & CLEAR_FILTERED_ONLY) != 0 && sheet->filters != NULL) {
1576 /* We need to modify the selection to only include filtered rows. */
1577 cmd_selection_clear_row_handler_t data;
1578 data.selection = selection;
1579 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1580 GnmFilter *filter;
1581 data.r = ranges->data;
1582 filter = gnm_sheet_filter_intersect_rows
1583 (sheet, data.r->start.row, data.r->end.row);
1584 if (filter) {
1585 colrow_foreach (&sheet->rows, data.r->start.row, data.r->end.row,
1586 (ColRowHandler) cmd_selection_clear_row_handler, &data);
1587 g_free (ranges->data);
1588 ranges->data = NULL;
1591 selection = g_slist_remove_all (data.selection, NULL);
1594 /* We should first determine whether we break anything by clearing */
1595 /* Check for array subdivision *//* Check for locked cells */
1596 if (sheet_ranges_split_region (sheet, selection,
1597 GO_CMD_CONTEXT (wbc), _("Clear")) ||
1598 cmd_selection_is_locked_effective (sheet, selection, wbc, _("Clear"))) {
1599 range_fragment_free (selection);
1600 return TRUE;
1604 /* We now need to build the descriptor */
1605 /* Collect clear types for descriptor */
1606 if (clear_flags != (CLEAR_VALUES | CLEAR_FORMATS | CLEAR_COMMENTS)) {
1607 GSList *m, *l = NULL;
1608 types = g_string_new (NULL);
1609 if (clear_flags & CLEAR_VALUES)
1610 l = g_slist_append (l, g_string_new (_("contents")));
1611 if (clear_flags & CLEAR_FORMATS)
1612 l = g_slist_append (l, g_string_new (_("formats")));
1613 if (clear_flags & CLEAR_COMMENTS)
1614 l = g_slist_append (l, g_string_new (_("comments")));
1615 /* Using a list for this may seem overkill, but is really the only
1616 * right way to do this
1618 for (m = l; m != NULL; m = m->next) {
1619 GString *s = m->data;
1621 g_string_append_len (types, s->str, s->len);
1622 g_string_free (s, TRUE);
1624 if (m->next)
1625 g_string_append (types, ", ");
1627 g_slist_free (l);
1628 } else
1629 types = g_string_new (_("all"));
1630 /* The range name string will automatically be truncated, we don't
1631 * need to truncate the "types" list because it will not grow
1632 * indefinitely
1634 names = undo_range_list_name (sheet, selection);
1635 descriptor = g_strdup_printf (_("Clearing %s in %s"), types->str, names);
1636 g_free (names);
1637 g_string_free (types, TRUE);
1638 size = g_slist_length (selection);
1640 clear_flags |= CLEAR_NOCHECKARRAY;
1642 if (clear_flags & (CLEAR_VALUES | CLEAR_FORMATS))
1643 clear_flags |= CLEAR_RECALC_DEPS;
1645 /* We are now ready to build the redo and undo items */
1646 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1647 GnmRange const *r = ranges->data;
1648 GnmSheetRange *sr = gnm_sheet_range_new (sheet, r);
1650 undo = go_undo_combine (undo, clipboard_copy_range_undo (sheet, r));
1651 redo = go_undo_combine
1652 (redo, sheet_clear_region_undo
1653 (sr, clear_flags));
1656 range_fragment_free (selection);
1658 result = cmd_generic_with_size (wbc, descriptor, size, undo, redo);
1659 g_free (descriptor);
1661 return result;
1664 /******************************************************************/
1666 #define CMD_FORMAT_TYPE (cmd_format_get_type ())
1667 #define CMD_FORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FORMAT_TYPE, CmdFormat))
1669 typedef struct {
1670 GnmCellPos pos;
1671 GnmStyleList *styles;
1672 ColRowIndexList *rows;
1673 ColRowStateGroup *old_heights;
1674 } CmdFormatOldStyle;
1676 typedef struct {
1677 GnmCommand cmd;
1678 GSList *selection;
1679 GSList *old_styles;
1680 GnmStyle *new_style;
1681 GnmBorder **borders;
1682 } CmdFormat;
1684 static void
1685 cmd_format_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1687 CmdFormat const *orig = (CmdFormat const *) cmd;
1688 int i;
1690 if (orig->new_style)
1691 gnm_style_ref (orig->new_style);
1692 if (orig->borders)
1693 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1694 gnm_style_border_ref (orig->borders [i]);
1696 cmd_selection_format (wbc, orig->new_style, orig->borders, NULL);
1698 MAKE_GNM_COMMAND (CmdFormat, cmd_format, cmd_format_repeat)
1700 static gboolean
1701 cmd_format_undo (GnmCommand *cmd,
1702 G_GNUC_UNUSED WorkbookControl *wbc)
1704 CmdFormat *me = CMD_FORMAT (cmd);
1706 g_return_val_if_fail (me != NULL, TRUE);
1708 if (me->old_styles) {
1709 GSList *rstyles = g_slist_reverse (g_slist_copy (me->old_styles));
1710 GSList *rsel = g_slist_reverse (g_slist_copy (me->selection));
1711 GSList *l1, *l2;
1713 for (l1 = rstyles, l2 = rsel; l1; l1 = l1->next, l2 = l2->next) {
1714 CmdFormatOldStyle *os = l1->data;
1715 GnmRange const *r = l2->data;
1716 GnmSpanCalcFlags flags = sheet_style_set_list
1717 (me->cmd.sheet,
1718 &os->pos, os->styles, NULL, NULL);
1720 if (os->old_heights) {
1721 colrow_restore_state_group (me->cmd.sheet, FALSE,
1722 os->rows,
1723 os->old_heights);
1724 colrow_state_group_destroy (os->old_heights);
1725 os->old_heights = NULL;
1726 colrow_index_list_destroy (os->rows);
1727 os->rows = NULL;
1730 sheet_range_calc_spans (me->cmd.sheet, r, flags);
1731 sheet_flag_style_update_range (me->cmd.sheet, r);
1734 sheet_redraw_all (me->cmd.sheet, FALSE);
1735 g_slist_free (rstyles);
1736 g_slist_free (rsel);
1739 select_selection (me->cmd.sheet, me->selection, wbc);
1741 return FALSE;
1744 static gboolean
1745 cmd_format_redo (GnmCommand *cmd, WorkbookControl *wbc)
1747 CmdFormat *me = CMD_FORMAT (cmd);
1748 GSList *l1, *l2;
1749 gboolean re_fit_height;
1751 g_return_val_if_fail (me != NULL, TRUE);
1753 /* Check for locked cells */
1754 if (cmd_selection_is_locked_effective (me->cmd.sheet, me->selection,
1755 wbc, _("Changing Format")))
1756 return TRUE;
1758 re_fit_height = me->new_style &&
1759 (GNM_SPANCALC_ROW_HEIGHT & gnm_style_required_spanflags (me->new_style));
1761 for (l1 = me->old_styles, l2 = me->selection; l2; l1 = l1->next, l2 = l2->next) {
1762 CmdFormatOldStyle *os = l1->data;
1763 GnmRange const *r = l2->data;
1765 if (me->borders)
1766 sheet_apply_border (me->cmd.sheet, r, me->borders);
1767 if (me->new_style) {
1768 gnm_style_ref (me->new_style);
1769 sheet_apply_style (me->cmd.sheet, r, me->new_style);
1770 if (re_fit_height)
1771 colrow_autofit (me->cmd.sheet, r, FALSE, FALSE,
1772 TRUE, FALSE,
1773 &os->rows, &os->old_heights);
1776 sheet_flag_style_update_range (me->cmd.sheet, r);
1778 sheet_redraw_all (me->cmd.sheet, FALSE);
1779 sheet_mark_dirty (me->cmd.sheet);
1781 select_selection (me->cmd.sheet, me->selection, wbc);
1783 return FALSE;
1786 static void
1787 cmd_format_finalize (GObject *cmd)
1789 CmdFormat *me = CMD_FORMAT (cmd);
1790 int i;
1792 if (me->new_style)
1793 gnm_style_unref (me->new_style);
1794 me->new_style = NULL;
1796 if (me->borders) {
1797 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1798 gnm_style_border_unref (me->borders [i]);
1799 g_free (me->borders);
1800 me->borders = NULL;
1803 if (me->old_styles != NULL) {
1804 GSList *l;
1806 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
1807 CmdFormatOldStyle *os = l->data;
1809 style_list_free (os->styles);
1810 colrow_index_list_destroy (os->rows);
1811 colrow_state_group_destroy (os->old_heights);
1812 g_free (os);
1814 me->old_styles = NULL;
1817 range_fragment_free (me->selection);
1818 me->selection = NULL;
1820 gnm_command_finalize (cmd);
1824 * cmd_format:
1825 * @wbc: the workbook control.
1826 * @sheet: the sheet
1827 * @style: style to apply to the selection
1828 * @borders: borders to apply to the selection
1829 * @opt_translated_name: An optional name to use in place of 'Format Cells'
1831 * If borders is non NULL, then the GnmBorder references are passed,
1832 * the GnmStyle reference is also passed.
1834 * It absorbs the reference to the style.
1836 * Return value: TRUE if there was a problem
1838 gboolean
1839 cmd_selection_format (WorkbookControl *wbc,
1840 GnmStyle *style, GnmBorder **borders,
1841 char const *opt_translated_name)
1843 CmdFormat *me;
1844 GSList *l;
1845 SheetView *sv = wb_control_cur_sheet_view (wbc);
1847 me = g_object_new (CMD_FORMAT_TYPE, NULL);
1849 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
1850 me->new_style = style;
1852 me->cmd.sheet = sv_sheet (sv);
1853 me->cmd.size = 1; /* Updated below. */
1855 me->old_styles = NULL;
1856 for (l = me->selection; l; l = l->next) {
1857 GnmRange const *sel_r = l->data;
1858 GnmRange range = *sel_r;
1859 CmdFormatOldStyle *os;
1861 /* Store the containing range to handle borders */
1862 if (borders != NULL) {
1863 if (range.start.col > 0) range.start.col--;
1864 if (range.start.row > 0) range.start.row--;
1865 if (range.end.col < gnm_sheet_get_last_col (me->cmd.sheet)) range.end.col++;
1866 if (range.end.row < gnm_sheet_get_last_row (me->cmd.sheet)) range.end.row++;
1869 os = g_new (CmdFormatOldStyle, 1);
1871 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
1872 os->pos = range.start;
1873 os->rows = NULL;
1874 os->old_heights = NULL;
1876 me->cmd.size += g_slist_length (os->styles);
1877 me->old_styles = g_slist_append (me->old_styles, os);
1880 if (borders) {
1881 int i;
1883 me->borders = g_new (GnmBorder *, GNM_STYLE_BORDER_EDGE_MAX);
1884 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1885 me->borders [i] = borders [i];
1886 } else
1887 me->borders = NULL;
1889 if (opt_translated_name == NULL) {
1890 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
1892 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing format of %s"), names);
1893 g_free (names);
1894 } else
1895 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
1897 return gnm_command_push_undo (wbc, G_OBJECT (me));
1900 /******************************************************************/
1902 static gboolean
1903 cmd_selection_format_toggle_font_style_filter (PangoAttribute *attribute, PangoAttrType *pt)
1905 return ((attribute->klass->type == *pt) ||
1906 ((PANGO_ATTR_RISE == *pt) && (attribute->klass->type == PANGO_ATTR_SCALE)));
1909 typedef struct {
1910 GOUndo *undo;
1911 PangoAttrType pt;
1912 } csftfs;
1914 static GnmValue *
1915 cmd_selection_format_toggle_font_style_cb (GnmCellIter const *iter, csftfs *closure)
1917 if (iter->cell && iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
1918 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
1919 if (fmt && go_format_is_markup (fmt)) {
1920 const PangoAttrList *old_markup =
1921 go_format_get_markup (fmt);
1922 PangoAttrList *new_markup = pango_attr_list_copy ((PangoAttrList *)old_markup);
1923 PangoAttrList *other = pango_attr_list_filter
1924 (new_markup,
1925 (PangoAttrFilterFunc) cmd_selection_format_toggle_font_style_filter,
1926 &closure->pt);
1927 if (other != NULL) {
1928 GnmSheetRange *sr;
1929 GnmRange r;
1930 range_init_cellpos (&r, &iter->pp.eval);
1931 sr = gnm_sheet_range_new (iter->pp.sheet, &r);
1932 closure->undo = go_undo_combine (closure->undo,
1933 sheet_range_set_markup_undo (sr, new_markup));
1935 pango_attr_list_unref (new_markup);
1936 pango_attr_list_unref (other);
1939 return NULL;
1942 gboolean
1943 cmd_selection_format_toggle_font_style (WorkbookControl *wbc,
1944 GnmStyle *style, GnmStyleElement t)
1946 SheetView *sv = wb_control_cur_sheet_view (wbc);
1947 Sheet *sheet = sv->sheet;
1948 GSList *selection = selection_get_ranges (sv, FALSE), *l;
1949 gboolean result;
1950 char *text, *name;
1951 GOUndo *undo = NULL;
1952 GOUndo *redo = NULL;
1953 PangoAttrType pt;
1956 switch (t) {
1957 case MSTYLE_FONT_BOLD:
1958 pt = PANGO_ATTR_WEIGHT;
1959 break;
1960 case MSTYLE_FONT_ITALIC:
1961 pt = PANGO_ATTR_STYLE;
1962 break;
1963 case MSTYLE_FONT_UNDERLINE:
1964 pt = PANGO_ATTR_UNDERLINE;
1965 break;
1966 case MSTYLE_FONT_STRIKETHROUGH:
1967 pt = PANGO_ATTR_STRIKETHROUGH;
1968 break;
1969 case MSTYLE_FONT_SCRIPT:
1970 pt = PANGO_ATTR_RISE; /* and PANGO_ATTR_SCALE (see ) */
1971 break;
1972 default:
1973 pt = PANGO_ATTR_INVALID;
1974 break;
1978 name = undo_range_list_name (sheet, selection);
1979 text = g_strdup_printf (_("Setting Font Style of %s"), name);
1980 g_free (name);
1982 for (l = selection; l != NULL; l = l->next) {
1983 GnmSheetRange *sr;
1984 undo = go_undo_combine
1985 (undo, clipboard_copy_range_undo (sheet, l->data));
1986 sr = gnm_sheet_range_new (sheet, l->data);
1987 redo = go_undo_combine
1988 (redo, sheet_apply_style_undo (sr, style));
1989 if (pt != PANGO_ATTR_INVALID) {
1990 csftfs closure;
1991 closure.undo = NULL;
1992 closure.pt = pt;
1993 sheet_foreach_cell_in_range (sheet, CELL_ITER_IGNORE_BLANK,
1994 sr->range.start.col, sr->range.start.row,
1995 sr->range.end.col, sr->range.end.row,
1996 (CellIterFunc) cmd_selection_format_toggle_font_style_cb,
1997 &closure);
1998 redo = go_undo_combine (redo, closure.undo);
2001 gnm_style_unref (style);
2002 result = cmd_generic (wbc, text, undo, redo);
2003 g_free (text);
2004 range_fragment_free (selection);
2006 return result;
2010 /******************************************************************/
2013 gboolean
2014 cmd_resize_colrow (WorkbookControl *wbc, Sheet *sheet,
2015 gboolean is_cols, ColRowIndexList *selection,
2016 int new_size)
2018 int size = 1;
2019 char *text;
2020 GOUndo *undo = NULL;
2021 GOUndo *redo = NULL;
2022 gboolean is_single, result;
2023 GString *list;
2024 ColRowStateGroup *saved_state;
2026 list = colrow_index_list_to_string (selection, is_cols, &is_single);
2027 gnm_cmd_trunc_descriptor (list, NULL);
2029 if (is_single) {
2030 if (new_size < 0)
2031 text = is_cols
2032 ? g_strdup_printf (_("Autofitting column %s"), list->str)
2033 : g_strdup_printf (_("Autofitting row %s"), list->str);
2034 else if (new_size > 0)
2035 text = is_cols
2036 ? g_strdup_printf (ngettext ("Setting width of column %s to %d pixel",
2037 "Setting width of column %s to %d pixels",
2038 new_size),
2039 list->str, new_size)
2040 : g_strdup_printf (ngettext ("Setting height of row %s to %d pixel",
2041 "Setting height of row %s to %d pixels",
2042 new_size),
2043 list->str, new_size);
2044 else text = is_cols
2045 ? g_strdup_printf (_("Setting width of column %s to default"),
2046 list->str)
2047 : g_strdup_printf (
2048 _("Setting height of row %s to default"), list->str);
2049 } else {
2050 if (new_size < 0)
2051 text = is_cols
2052 ? g_strdup_printf (_("Autofitting columns %s"), list->str)
2053 : g_strdup_printf (_("Autofitting rows %s"), list->str);
2054 else if (new_size > 0)
2055 text = is_cols
2056 ? g_strdup_printf (ngettext("Setting width of columns %s to %d pixel",
2057 "Setting width of columns %s to %d pixels",
2058 new_size),
2059 list->str, new_size)
2060 : g_strdup_printf (ngettext("Setting height of rows %s to %d pixel",
2061 "Setting height of rows %s to %d pixels",
2062 new_size),
2063 list->str, new_size);
2064 else text = is_cols
2065 ? g_strdup_printf (
2066 _("Setting width of columns %s to default"), list->str)
2067 : g_strdup_printf (
2068 _("Setting height of rows %s to default"), list->str);
2070 g_string_free (list, TRUE);
2072 saved_state = colrow_get_sizes (sheet, is_cols, selection, new_size);
2073 undo = gnm_undo_colrow_restore_state_group_new
2074 (sheet, is_cols, colrow_index_list_copy (selection), saved_state);
2076 redo = gnm_undo_colrow_set_sizes_new (sheet, is_cols, selection, new_size, NULL);
2078 result = cmd_generic_with_size (wbc, text, size, undo, redo);
2079 g_free (text);
2081 return result;
2084 gboolean
2085 cmd_autofit_selection (WorkbookControl *wbc, SheetView *sv, Sheet *sheet, gboolean fit_width,
2086 ColRowIndexList *selectionlist)
2088 GOUndo *undo = NULL;
2089 GOUndo *redo = NULL;
2090 gboolean result;
2091 ColRowStateGroup *saved_state;
2092 GSList *l, *selection = selection_get_ranges (sv, TRUE);
2093 gchar *names = undo_range_list_name (sheet, selection);
2094 gchar const *format = fit_width ?
2095 N_("Autofitting width of %s") : N_("Autofitting height of %s");
2096 gchar *text = g_strdup_printf (_(format), names);
2098 g_free (names);
2100 saved_state = colrow_get_sizes (sheet, fit_width, selectionlist, -1);
2101 undo = gnm_undo_colrow_restore_state_group_new
2102 (sheet, fit_width, colrow_index_list_copy (selectionlist), saved_state);
2104 for (l = selection; l != NULL; l = l->next)
2105 redo = go_undo_combine
2106 (redo, gnm_undo_colrow_set_sizes_new
2107 (sheet, fit_width, NULL, -1, l->data));
2109 result = cmd_generic (wbc, text, undo, redo);
2110 g_free (text);
2111 return result;
2115 /******************************************************************/
2117 #define CMD_SORT_TYPE (cmd_sort_get_type ())
2118 #define CMD_SORT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SORT_TYPE, CmdSort))
2120 typedef struct {
2121 GnmCommand cmd;
2123 GnmSortData *data;
2124 int *perm;
2125 GnmCellRegion *old_contents;
2126 } CmdSort;
2128 MAKE_GNM_COMMAND (CmdSort, cmd_sort, NULL)
2130 static void
2131 cmd_sort_finalize (GObject *cmd)
2133 CmdSort *me = CMD_SORT (cmd);
2135 if (me->data != NULL)
2136 gnm_sort_data_destroy (me->data);
2137 g_free (me->perm);
2138 if (me->old_contents != NULL)
2139 cellregion_unref (me->old_contents);
2141 gnm_command_finalize (cmd);
2144 static gboolean
2145 cmd_sort_undo (GnmCommand *cmd, WorkbookControl *wbc)
2147 CmdSort *me = CMD_SORT (cmd);
2148 GnmSortData *data = me->data;
2149 GnmPasteTarget pt;
2151 paste_target_init (&pt, data->sheet, data->range,
2152 PASTE_CONTENTS | PASTE_FORMATS |
2153 (data->retain_formats ? PASTE_FORMATS : 0));
2154 clipboard_paste_region (me->old_contents,
2155 &pt,
2156 GO_CMD_CONTEXT (wbc));
2158 return FALSE;
2161 static gboolean
2162 cmd_sort_redo (GnmCommand *cmd, WorkbookControl *wbc)
2164 CmdSort *me = CMD_SORT (cmd);
2165 GnmSortData *data = me->data;
2167 /* Check for locks */
2168 if (cmd_cell_range_is_locked_effective
2169 (data->sheet, data->range, wbc, _("Sorting")))
2170 return TRUE;
2172 if (me->perm)
2173 gnm_sort_position (data, me->perm, GO_CMD_CONTEXT (wbc));
2174 else {
2175 me->old_contents =
2176 clipboard_copy_range (data->sheet, data->range);
2177 me->cmd.size = cellregion_cmd_size (me->old_contents);
2178 me->perm = gnm_sort_contents (data, GO_CMD_CONTEXT (wbc));
2181 return FALSE;
2184 gboolean
2185 cmd_sort (WorkbookControl *wbc, GnmSortData *data)
2187 CmdSort *me;
2188 char *desc;
2190 g_return_val_if_fail (data != NULL, TRUE);
2192 desc = g_strdup_printf (_("Sorting %s"), range_as_string (data->range));
2193 if (sheet_range_contains_merges_or_arrays (data->sheet, data->range, GO_CMD_CONTEXT (wbc), desc, TRUE, TRUE)) {
2194 gnm_sort_data_destroy (data);
2195 g_free (desc);
2196 return TRUE;
2199 me = g_object_new (CMD_SORT_TYPE, NULL);
2201 me->data = data;
2202 me->perm = NULL;
2203 me->cmd.sheet = data->sheet;
2204 me->cmd.size = 1; /* Changed in initial redo. */
2205 me->cmd.cmd_descriptor = desc;
2207 return gnm_command_push_undo (wbc, G_OBJECT (me));
2210 /******************************************************************/
2212 #define CMD_COLROW_HIDE_TYPE (cmd_colrow_hide_get_type ())
2213 #define CMD_COLROW_HIDE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_HIDE_TYPE, CmdColRowHide))
2215 typedef struct {
2216 GnmCommand cmd;
2218 gboolean is_cols;
2219 ColRowVisList *hide, *show;
2220 } CmdColRowHide;
2222 static void
2223 cmd_colrow_hide_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2225 CmdColRowHide const *orig = (CmdColRowHide const *) cmd;
2226 cmd_selection_colrow_hide (wbc, orig->is_cols, orig->show != NULL);
2228 MAKE_GNM_COMMAND (CmdColRowHide, cmd_colrow_hide, cmd_colrow_hide_repeat)
2231 * cmd_colrow_hide_correct_selection :
2233 * Try to ensure that the selection/cursor is set to a visible row/col
2235 * Added to fix bug 38179
2236 * Removed because the result is irritating and the bug is actually XL
2237 * compatibile
2239 static void
2240 cmd_colrow_hide_correct_selection (G_GNUC_UNUSED CmdColRowHide *me, G_GNUC_UNUSED WorkbookControl *wbc)
2242 #if 0
2243 int x, y, index;
2244 SheetView *sv = sheet_get_view (me->cmd.sheet,
2245 wb_control_view (wbc));
2247 index = colrow_find_adjacent_visible (me->cmd.sheet, me->is_cols,
2248 me->is_cols ? sv->edit_pos.col : sv->edit_pos.row,
2249 TRUE);
2251 x = me->is_cols ? sv->edit_pos.row : index;
2252 y = me->is_cols ? index : sv->edit_pos.col;
2254 if (index >= 0) {
2255 sv_selection_reset (sv);
2256 if (me->is_cols)
2257 sv_selection_add_full (sv, y, x, y, 0,
2258 y, gnm_sheet_get_last_row (sheet),
2259 GNM_SELECTION_MODE_ADD);
2260 else
2261 sv_selection_add_full (sv, y, x, 0, x,
2262 gnm_sheet_get_last_col (sheet), x,
2263 GNM_SELECTION_MODE_ADD);
2265 #endif
2268 static gboolean
2269 cmd_colrow_hide_undo (GnmCommand *cmd, WorkbookControl *wbc)
2271 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2273 g_return_val_if_fail (me != NULL, TRUE);
2275 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2276 TRUE, me->hide);
2277 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2278 FALSE, me->show);
2280 if (me->show != NULL)
2281 cmd_colrow_hide_correct_selection (me, wbc);
2283 return FALSE;
2286 static gboolean
2287 cmd_colrow_hide_redo (GnmCommand *cmd, WorkbookControl *wbc)
2289 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2291 g_return_val_if_fail (me != NULL, TRUE);
2293 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2294 FALSE, me->hide);
2295 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2296 TRUE, me->show);
2298 if (me->hide != NULL)
2299 cmd_colrow_hide_correct_selection (me, wbc);
2301 return FALSE;
2304 static void
2305 cmd_colrow_hide_finalize (GObject *cmd)
2307 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2308 colrow_vis_list_destroy (me->hide);
2309 me->hide = NULL;
2310 colrow_vis_list_destroy (me->show);
2311 me->show = NULL;
2312 gnm_command_finalize (cmd);
2315 gboolean
2316 cmd_selection_colrow_hide (WorkbookControl *wbc,
2317 gboolean is_cols, gboolean visible)
2319 CmdColRowHide *me;
2320 SheetView *sv = wb_control_cur_sheet_view (wbc);
2321 int n;
2322 Sheet *sheet;
2323 GSList *show = NULL, *hide = NULL;
2325 if (visible)
2326 show = colrow_get_visiblity_toggle (sv, is_cols, TRUE);
2327 else
2328 hide = colrow_get_visiblity_toggle (sv, is_cols, FALSE);
2329 n = colrow_vis_list_length (hide) + colrow_vis_list_length (show);
2330 sheet = sv_sheet (sv);
2332 if (!visible) {
2333 /* If these are the last colrows to hide, check with the user */
2334 int count = 0;
2335 if (is_cols) {
2336 int i, max = gnm_sheet_get_max_cols (sheet);
2337 ColRowInfo *ci;
2338 for (i = 0 ; i < max ; i++)
2339 if (NULL ==
2340 (ci = sheet_col_get (sheet, i)) ||
2341 (ci->visible))
2342 count++;
2343 } else {
2344 int i, max = gnm_sheet_get_max_rows (sheet);
2345 ColRowInfo *ci;
2346 for (i = 0 ; i < max ; i++)
2347 if (NULL ==
2348 (ci = sheet_row_get (sheet, i)) ||
2349 (ci->visible))
2350 count++;
2352 if (count <= n) {
2353 gchar const *text = is_cols ?
2354 _("Are you sure that you want to hide all columns? "
2355 "If you do so you can unhide them with the "
2356 "'Format\342\206\222Column\342\206\222Unhide' "
2357 "menu item.") :
2358 _("Are you sure that you want to hide all rows? "
2359 "If you do so you can unhide them with the "
2360 "'Format\342\206\222Row\342\206\222Unhide' "
2361 "menu item.");
2362 if (!go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)),
2363 FALSE, "%s", text)) {
2364 colrow_vis_list_destroy (show);
2365 colrow_vis_list_destroy (hide);
2366 return TRUE;
2371 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2372 me->show = show;
2373 me->hide = hide;
2374 me->is_cols = is_cols;
2375 me->cmd.sheet = sheet;
2376 me->cmd.size = 1 + g_slist_length (hide) + g_slist_length (show);
2377 me->cmd.cmd_descriptor = g_strdup (is_cols
2378 ? (visible ? _("Unhide columns") : _("Hide columns"))
2379 : (visible ? _("Unhide rows") : _("Hide rows")));
2381 return gnm_command_push_undo (wbc, G_OBJECT (me));
2384 gboolean
2385 cmd_selection_outline_change (WorkbookControl *wbc,
2386 gboolean is_cols, int index, int depth)
2388 CmdColRowHide *me;
2389 ColRowInfo const *cri;
2390 int first = -1, last = -1;
2391 gboolean visible = FALSE;
2392 int d;
2393 Sheet *sheet = wb_control_cur_sheet (wbc);
2394 SheetView *sv = wb_control_cur_sheet_view (wbc);
2396 cri = sheet_colrow_get_info (sheet, index, is_cols);
2398 d = cri->outline_level;
2399 if (depth > d)
2400 depth = d;
2402 /* Nodes only collapse when selected directly, selecting at a lower
2403 * level is a standard toggle. */
2404 if (depth == d) {
2405 if ((is_cols ? sheet->outline_symbols_right : sheet->outline_symbols_below)) {
2406 if (index > 0) {
2407 ColRowInfo const *prev =
2408 sheet_colrow_get (sheet, index-1, is_cols);
2410 if (prev != NULL && prev->outline_level > d) {
2411 visible = (depth == d && cri->is_collapsed);
2412 last = index - 1;
2413 first = colrow_find_outline_bound (sheet, is_cols,
2414 last, d+1, FALSE);
2417 } else if (index+1 < colrow_max (is_cols, sheet)) {
2418 ColRowInfo const *next =
2419 sheet_colrow_get (sheet, index+1, is_cols);
2421 if (next != NULL && next->outline_level > d) {
2422 visible = (depth == d && cri->is_collapsed);
2423 first = index + 1;
2424 last = colrow_find_outline_bound (sheet, is_cols,
2425 first, d+1, TRUE);
2430 /* If nothing done yet do a simple collapse */
2431 if (first < 0 && cri->outline_level > 0) {
2432 if (depth < d)
2433 ++depth;
2434 first = colrow_find_outline_bound (sheet, is_cols, index, depth, FALSE);
2435 last = colrow_find_outline_bound (sheet, is_cols, index, depth, TRUE);
2436 visible = FALSE;
2438 if (first == last && depth > cri->outline_level)
2439 return TRUE;
2442 if (first < 0 || last < 0)
2443 return TRUE;
2445 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2447 me->is_cols = is_cols;
2448 me->hide = me->show = NULL;
2449 if (visible)
2450 me->show = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2451 TRUE, first, last);
2452 else
2453 me->hide = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2454 FALSE, first, last);
2456 me->cmd.sheet = sv_sheet (sv);
2457 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2458 me->cmd.cmd_descriptor = g_strdup (is_cols
2459 ? (visible ? _("Expand columns") : _("Collapse columns"))
2460 : (visible ? _("Expand rows") : _("Collapse rows")));
2462 return gnm_command_push_undo (wbc, G_OBJECT (me));
2465 gboolean
2466 cmd_global_outline_change (WorkbookControl *wbc, gboolean is_cols, int depth)
2468 CmdColRowHide *me;
2469 ColRowVisList *hide, *show;
2470 SheetView *sv = wb_control_cur_sheet_view (wbc);
2472 colrow_get_global_outline (sv_sheet (sv), is_cols, depth, &show, &hide);
2474 if (show == NULL && hide == NULL)
2475 return TRUE;
2477 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2478 me->is_cols = is_cols;
2479 me->hide = hide;
2480 me->show = show;
2481 me->cmd.sheet = sv_sheet (sv);
2482 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2483 me->cmd.cmd_descriptor = g_strdup_printf (is_cols
2484 ? _("Show column outline %d") : _("Show row outline %d"), depth);
2486 return gnm_command_push_undo (wbc, G_OBJECT (me));
2489 /******************************************************************/
2491 #define CMD_GROUP_TYPE (cmd_group_get_type ())
2492 #define CMD_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GROUP_TYPE, CmdGroup))
2494 typedef struct {
2495 GnmCommand cmd;
2497 GnmRange range;
2498 gboolean is_cols;
2499 gboolean group;
2500 } CmdGroup;
2502 static void
2503 cmd_group_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2505 CmdGroup const *orig = (CmdGroup const *) cmd;
2506 cmd_selection_group (wbc, orig->is_cols, orig->group);
2508 MAKE_GNM_COMMAND (CmdGroup, cmd_group, cmd_group_repeat)
2510 static gboolean
2511 cmd_group_undo (GnmCommand *cmd,
2512 G_GNUC_UNUSED WorkbookControl *wbc)
2514 CmdGroup const *me = CMD_GROUP (cmd);
2515 sheet_colrow_group_ungroup (me->cmd.sheet,
2516 &me->range, me->is_cols, !me->group);
2517 return FALSE;
2520 static gboolean
2521 cmd_group_redo (GnmCommand *cmd,
2522 G_GNUC_UNUSED WorkbookControl *wbc)
2524 CmdGroup const *me = CMD_GROUP (cmd);
2525 sheet_colrow_group_ungroup (me->cmd.sheet,
2526 &me->range, me->is_cols, me->group);
2527 return FALSE;
2530 static void
2531 cmd_group_finalize (GObject *cmd)
2533 gnm_command_finalize (cmd);
2536 gboolean
2537 cmd_selection_group (WorkbookControl *wbc,
2538 gboolean is_cols, gboolean group)
2540 CmdGroup *me;
2541 SheetView *sv;
2542 GnmRange r;
2544 g_return_val_if_fail (wbc != NULL, TRUE);
2546 sv = wb_control_cur_sheet_view (wbc);
2547 r = *selection_first_range (sv, NULL, NULL);
2549 /* Check if this really is possible and display an error if it's not */
2550 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2551 if (group) {
2552 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2553 ? _("Those columns are already grouped")
2554 : _("Those rows are already grouped"));
2555 return TRUE;
2558 /* see if the user selected the col/row with the marker too */
2559 if (is_cols) {
2560 if (r.start.col != r.end.col) {
2561 if (sv->sheet->outline_symbols_right)
2562 r.end.col--;
2563 else
2564 r.start.col++;
2566 } else {
2567 if (r.start.row != r.end.row) {
2568 if (sv->sheet->outline_symbols_below)
2569 r.end.row--;
2570 else
2571 r.start.row++;
2575 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2576 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2577 ? _("Those columns are not grouped, you can't ungroup them")
2578 : _("Those rows are not grouped, you can't ungroup them"));
2579 return TRUE;
2583 me = g_object_new (CMD_GROUP_TYPE, NULL);
2584 me->is_cols = is_cols;
2585 me->group = group;
2586 me->range = r;
2588 me->cmd.sheet = sv->sheet;
2589 me->cmd.size = 1;
2590 me->cmd.cmd_descriptor = is_cols
2591 ? g_strdup_printf (group ? _("Group columns %s") : _("Ungroup columns %s"),
2592 cols_name (me->range.start.col, me->range.end.col))
2593 : g_strdup_printf (group ? _("Group rows %d:%d") : _("Ungroup rows %d:%d"),
2594 me->range.start.row + 1, me->range.end.row + 1);
2596 return gnm_command_push_undo (wbc, G_OBJECT (me));
2599 /******************************************************************/
2601 #define CMD_PASTE_CUT_TYPE (cmd_paste_cut_get_type ())
2602 #define CMD_PASTE_CUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_CUT_TYPE, CmdPasteCut))
2604 typedef struct {
2605 GnmCommand cmd;
2607 GnmExprRelocateInfo info;
2608 GSList *paste_contents;
2609 GOUndo *reloc_undo;
2610 gboolean move_selection;
2611 ColRowStateList *saved_sizes;
2613 /* handle redo-ing an undo with contents from a deleted sheet */
2614 GnmCellRegion *deleted_sheet_contents;
2615 } CmdPasteCut;
2617 MAKE_GNM_COMMAND (CmdPasteCut, cmd_paste_cut, NULL)
2619 typedef struct {
2620 GnmPasteTarget pt;
2621 GnmCellRegion *contents;
2622 } PasteContent;
2625 * cmd_paste_cut_update :
2627 * Utility routine to update things when we are transfering between sheets and
2628 * workbooks.
2630 static void
2631 cmd_paste_cut_update (GnmExprRelocateInfo const *info,
2632 G_GNUC_UNUSED WorkbookControl *wbc)
2634 Sheet *o = info->origin_sheet;
2635 Sheet *t = info->target_sheet;
2637 /* Dirty and update both sheets */
2638 sheet_mark_dirty (t);
2639 sheet_update (t);
2641 if (IS_SHEET (o) && o != t) {
2642 sheet_mark_dirty (o);
2643 sheet_update (o);
2647 static gboolean
2648 cmd_paste_cut_undo (GnmCommand *cmd, WorkbookControl *wbc)
2650 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2651 GnmExprRelocateInfo reverse;
2653 g_return_val_if_fail (me != NULL, TRUE);
2654 g_return_val_if_fail (me->paste_contents != NULL, TRUE);
2655 g_return_val_if_fail (me->deleted_sheet_contents == NULL, TRUE);
2657 reverse.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
2658 reverse.target_sheet = me->info.origin_sheet;
2659 reverse.origin_sheet = me->info.target_sheet;
2660 reverse.origin = me->info.origin;
2661 range_translate (&reverse.origin,
2662 me->info.origin_sheet, /* FIXME: What sheet? */
2663 me->info.col_offset,
2664 me->info.row_offset);
2665 reverse.col_offset = -me->info.col_offset;
2666 reverse.row_offset = -me->info.row_offset;
2668 /* Move things back being careful NOT to invalidate the src region */
2669 if (IS_SHEET (me->info.origin_sheet))
2670 sheet_move_range (&reverse, NULL, GO_CMD_CONTEXT (wbc));
2671 else
2672 me->deleted_sheet_contents = clipboard_copy_range (
2673 reverse.origin_sheet, &reverse.origin);
2675 /* Restore the original row heights */
2676 colrow_set_states (me->info.target_sheet, FALSE,
2677 reverse.origin.start.row, me->saved_sizes);
2678 colrow_state_list_destroy (me->saved_sizes);
2679 me->saved_sizes = NULL;
2681 if (me->reloc_undo) {
2682 go_undo_undo (me->reloc_undo);
2683 g_object_unref (me->reloc_undo);
2684 me->reloc_undo = NULL;
2687 while (me->paste_contents) {
2688 PasteContent *pc = me->paste_contents->data;
2689 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2691 clipboard_paste_region (pc->contents, &pc->pt, GO_CMD_CONTEXT (wbc));
2692 cellregion_unref (pc->contents);
2693 g_free (pc);
2696 /* Force update of the status area */
2697 sheet_flag_status_update_range (me->info.target_sheet, NULL);
2699 cmd_paste_cut_update (&me->info, wbc);
2701 /* Select the original region */
2702 if (me->move_selection && IS_SHEET (me->info.origin_sheet))
2703 select_range (me->info.origin_sheet,
2704 &me->info.origin,
2705 wbc);
2707 return FALSE;
2710 static gboolean
2711 cmd_paste_cut_redo (GnmCommand *cmd, WorkbookControl *wbc)
2713 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2714 GnmRange tmp;
2716 g_return_val_if_fail (me != NULL, TRUE);
2717 g_return_val_if_fail (me->paste_contents == NULL, TRUE);
2719 tmp = me->info.origin;
2720 range_translate (&tmp, me->info.origin_sheet, /* FIXME: What sheet? */
2721 me->info.col_offset, me->info.row_offset);
2722 range_normalize (&tmp);
2724 g_return_val_if_fail (range_is_sane (&tmp), TRUE);
2726 if (me->info.origin_sheet != me->info.target_sheet ||
2727 !range_overlap (&me->info.origin, &tmp)) {
2728 PasteContent *pc = g_new (PasteContent, 1);
2729 paste_target_init (&pc->pt, me->info.target_sheet, &tmp, PASTE_ALL_TYPES);
2730 pc->contents = clipboard_copy_range (me->info.target_sheet, &tmp);
2731 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2732 } else {
2733 /* need to store any portions of the paste target
2734 * that do not overlap with the source.
2736 GSList *ptr, *frag = range_split_ranges (&me->info.origin, &tmp);
2737 for (ptr = frag ; ptr != NULL ; ptr = ptr->next) {
2738 GnmRange *r = ptr->data;
2740 if (!range_overlap (&me->info.origin, r)) {
2741 PasteContent *pc = g_new (PasteContent, 1);
2742 paste_target_init (&pc->pt, me->info.target_sheet, r, PASTE_ALL_TYPES);
2743 pc->contents = clipboard_copy_range (me->info.target_sheet, r);
2744 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2746 g_free (r);
2748 g_slist_free (frag);
2751 /* rare corner case. If the origin sheet has been deleted */
2752 if (!IS_SHEET (me->info.origin_sheet)) {
2753 GnmPasteTarget pt;
2754 paste_target_init (&pt, me->info.target_sheet, &tmp, PASTE_ALL_TYPES);
2755 sheet_clear_region (pt.sheet,
2756 tmp.start.col, tmp.start.row, tmp.end.col, tmp.end.row,
2757 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
2758 GO_CMD_CONTEXT (wbc));
2759 clipboard_paste_region (me->deleted_sheet_contents,
2760 &pt, GO_CMD_CONTEXT (wbc));
2761 cellregion_unref (me->deleted_sheet_contents);
2762 me->deleted_sheet_contents = NULL;
2763 } else
2764 sheet_move_range (&me->info, &me->reloc_undo, GO_CMD_CONTEXT (wbc));
2766 cmd_paste_cut_update (&me->info, wbc);
2768 /* Backup row heights and adjust row heights to fit */
2769 me->saved_sizes = colrow_get_states (me->info.target_sheet, FALSE, tmp.start.row, tmp.end.row);
2770 rows_height_update (me->info.target_sheet, &tmp, FALSE);
2772 /* Make sure the destination is selected */
2773 if (me->move_selection)
2774 select_range (me->info.target_sheet, &tmp, wbc);
2776 return FALSE;
2779 static void
2780 cmd_paste_cut_finalize (GObject *cmd)
2782 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2784 if (me->saved_sizes)
2785 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
2786 while (me->paste_contents) {
2787 PasteContent *pc = me->paste_contents->data;
2788 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2789 cellregion_unref (pc->contents);
2790 g_free (pc);
2792 if (me->reloc_undo) {
2793 g_object_unref (me->reloc_undo);
2794 me->reloc_undo = NULL;
2796 if (me->deleted_sheet_contents) {
2797 cellregion_unref (me->deleted_sheet_contents);
2798 me->deleted_sheet_contents = NULL;
2801 gnm_command_finalize (cmd);
2804 gboolean
2805 cmd_paste_cut (WorkbookControl *wbc, GnmExprRelocateInfo const *info,
2806 gboolean move_selection, char *descriptor)
2808 CmdPasteCut *me;
2809 GnmRange r;
2810 char *where;
2812 g_return_val_if_fail (info != NULL, TRUE);
2814 /* This is vacuous */
2815 if (info->origin_sheet == info->target_sheet &&
2816 info->col_offset == 0 && info->row_offset == 0)
2817 return TRUE;
2819 /* FIXME: Do we want to show the destination range as well ? */
2820 where = undo_range_name (info->origin_sheet, &info->origin);
2821 if (descriptor == NULL)
2822 descriptor = g_strdup_printf (_("Moving %s"), where);
2823 g_free (where);
2825 g_return_val_if_fail (info != NULL, TRUE);
2827 r = info->origin;
2828 if (range_translate (&r, info->target_sheet,
2829 info->col_offset, info->row_offset)) {
2831 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), descriptor,
2832 _("is beyond sheet boundaries"));
2833 g_free (descriptor);
2834 return TRUE;
2837 /* Check array subdivision & merged regions */
2838 if (sheet_range_splits_region (info->target_sheet, &r,
2839 (info->origin_sheet == info->target_sheet)
2840 ? &info->origin : NULL, GO_CMD_CONTEXT (wbc), descriptor)) {
2841 g_free (descriptor);
2842 return TRUE;
2845 me = g_object_new (CMD_PASTE_CUT_TYPE, NULL);
2847 me->info = *info;
2848 me->paste_contents = NULL;
2849 me->deleted_sheet_contents = NULL;
2850 me->reloc_undo = NULL;
2851 me->move_selection = move_selection;
2852 me->saved_sizes = NULL;
2854 me->cmd.sheet = NULL; /* we have potentially two different. */
2855 me->cmd.size = 1; /* FIXME? */
2856 me->cmd.cmd_descriptor = descriptor;
2858 /* NOTE : if the destination workbook is different from the source
2859 * workbook should we have undo elements in both menus ?? It seems
2860 * poor form to hit undo in 1 window and effect another...
2862 * Maybe queue it as two different commands, as a clear in one book
2863 * and a paste in the other. This is not symmetric though. What
2864 * happens to the cells in the original sheet that now reference the
2865 * cells in the other? When do they reset to the original?
2867 * Probably when the clear in the original is undone.
2870 return gnm_command_push_undo (wbc, G_OBJECT (me));
2873 /******************************************************************/
2875 static void
2876 warn_if_date_trouble (WorkbookControl *wbc, GnmCellRegion *cr)
2878 Workbook *wb = wb_control_get_workbook (wbc);
2879 const GODateConventions *wb_date_conv = workbook_date_conv (wb);
2881 if (cr->date_conv == NULL)
2882 return;
2883 if (go_date_conv_equal (cr->date_conv, wb_date_conv))
2884 return;
2886 /* We would like to show a warning, but it seems we cannot via a context. */
2888 GError *err;
2889 err = g_error_new (go_error_invalid(), 0,
2890 _("Copying between files with different date conventions.\n"
2891 "It is possible that some dates could be copied\n"
2892 "incorrectly."));
2893 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
2894 g_error_free (err);
2899 #define CMD_PASTE_COPY_TYPE (cmd_paste_copy_get_type ())
2900 #define CMD_PASTE_COPY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_COPY_TYPE, CmdPasteCopy))
2902 typedef struct {
2903 GnmCommand cmd;
2905 GnmCellRegion *contents;
2906 GSList *pasted_objects,*orig_contents_objects;
2907 GnmPasteTarget dst;
2908 gboolean has_been_through_cycle;
2909 gboolean only_objects;
2910 ColRowStateGroup *saved_sizes_rows;
2911 ColRowStateGroup *saved_sizes_cols;
2912 ColRowIndexList *saved_list_rows;
2913 ColRowIndexList *saved_list_cols;
2914 gboolean single_merge_to_single_merge;
2915 } CmdPasteCopy;
2917 static void
2918 cmd_paste_copy_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2920 CmdPasteCopy const *orig = (CmdPasteCopy const *) cmd;
2921 GnmPasteTarget new_dst;
2922 SheetView *sv = wb_control_cur_sheet_view (wbc);
2923 GnmRange const *r = selection_first_range (sv,
2924 GO_CMD_CONTEXT (wbc), _("Paste Copy"));
2925 GnmCellRegion *newcr;
2927 if (r == NULL)
2928 return;
2930 paste_target_init (&new_dst, sv_sheet (sv), r, orig->dst.paste_flags);
2931 newcr = clipboard_copy_range (orig->dst.sheet, &orig->dst.range);
2932 cmd_paste_copy (wbc, &new_dst, newcr);
2933 cellregion_unref (newcr);
2935 MAKE_GNM_COMMAND (CmdPasteCopy, cmd_paste_copy, cmd_paste_copy_repeat)
2937 static int
2938 by_addr (gconstpointer a, gconstpointer b)
2940 if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b))
2941 return -1;
2942 if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b))
2943 return +1;
2944 return 0;
2947 static GSList *
2948 get_new_objects (Sheet *sheet, GSList *old)
2950 GSList *objs = g_slist_sort (g_slist_copy (sheet->sheet_objects),
2951 by_addr);
2952 GSList *p = objs, *last = NULL;
2954 while (old) {
2955 int c = -1;
2956 while (p && (c = by_addr (p->data, old->data)) < 0) {
2957 last = p;
2958 p = p->next;
2961 old = old->next;
2963 if (c == 0) {
2964 GSList *next = p->next;
2965 if (last)
2966 last->next = next;
2967 else
2968 objs = next;
2969 g_slist_free_1 (p);
2970 p = next;
2974 return objs;
2977 static void
2978 cmd_paste_copy_select_obj (SheetObject *so, SheetControlGUI *scg)
2980 scg_object_select (scg, so);
2983 static gboolean
2984 cmd_paste_copy_impl (GnmCommand *cmd, WorkbookControl *wbc,
2985 gboolean is_undo)
2987 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
2988 GnmCellRegion *contents;
2989 GSList *old_objects;
2990 GnmPasteTarget actual_dst;
2992 g_return_val_if_fail (me != NULL, TRUE);
2993 g_return_val_if_fail (me->contents != NULL, TRUE);
2995 g_slist_foreach (me->pasted_objects,
2996 (GFunc)sheet_object_clear_sheet,
2997 NULL);
2998 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
2999 me->pasted_objects = NULL;
3000 old_objects = get_new_objects (me->dst.sheet, NULL);
3002 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
3003 if (me->has_been_through_cycle)
3004 me->dst.paste_flags = PASTE_CONTENTS |
3005 (me->dst.paste_flags & PASTE_ALL_TYPES);
3006 actual_dst = me->dst;
3007 if (clipboard_paste_region (me->contents, &actual_dst, GO_CMD_CONTEXT (wbc))) {
3008 /* There was a problem, avoid leaking */
3009 cellregion_unref (contents);
3010 g_slist_free (old_objects);
3011 return TRUE;
3014 me->pasted_objects = get_new_objects (me->dst.sheet, old_objects);
3015 g_slist_foreach (me->pasted_objects, (GFunc)g_object_ref, NULL);
3016 g_slist_free (old_objects);
3018 if (is_undo) {
3019 colrow_restore_state_group (me->dst.sheet, FALSE,
3020 me->saved_list_rows, me->saved_sizes_rows);
3021 colrow_state_group_destroy (me->saved_sizes_rows);
3022 me->saved_sizes_rows = NULL;
3023 colrow_index_list_destroy (me->saved_list_rows);
3024 me->saved_list_rows = NULL;
3025 colrow_restore_state_group (me->dst.sheet, TRUE,
3026 me->saved_list_cols, me->saved_sizes_cols);
3027 colrow_state_group_destroy (me->saved_sizes_cols);
3028 me->saved_sizes_cols = NULL;
3029 colrow_index_list_destroy (me->saved_list_cols);
3030 me->saved_list_cols = NULL;
3031 } else {
3032 colrow_autofit (me->dst.sheet, &me->dst.range, FALSE, FALSE,
3033 TRUE, FALSE,
3034 &me->saved_list_rows, &me->saved_sizes_rows);
3035 colrow_autofit (me->dst.sheet, &me->dst.range, TRUE, TRUE,
3036 TRUE, FALSE,
3037 &me->saved_list_cols, &me->saved_sizes_cols);
3041 * We cannot use the random set of objects at the target location.
3042 * See http://bugzilla.gnome.org/show_bug.cgi?id=308300
3044 g_slist_free_full (contents->objects, (GDestroyNotify)g_object_unref);
3045 contents->objects = is_undo
3046 ? go_slist_map (me->orig_contents_objects,
3047 (GOMapFunc)sheet_object_dup)
3048 : NULL;
3050 cellregion_unref (me->contents);
3051 me->contents = contents;
3052 me->has_been_through_cycle = TRUE;
3054 /* Select the newly pasted contents (this queues a redraw) */
3055 if (me->only_objects && GNM_IS_WBC_GTK (wbc)) {
3056 SheetControlGUI *scg =
3057 wbcg_get_nth_scg (WBC_GTK (wbc),
3058 cmd->sheet->index_in_wb);
3059 scg_object_unselect (scg, NULL);
3060 g_slist_foreach (me->pasted_objects,
3061 (GFunc) cmd_paste_copy_select_obj, scg);
3063 select_range (me->dst.sheet, &me->dst.range, wbc);
3065 return FALSE;
3068 static gboolean
3069 cmd_paste_copy_undo (GnmCommand *cmd, WorkbookControl *wbc)
3071 return cmd_paste_copy_impl (cmd, wbc, TRUE);
3074 static gboolean
3075 cmd_paste_copy_redo (GnmCommand *cmd, WorkbookControl *wbc)
3077 return cmd_paste_copy_impl (cmd, wbc, FALSE);
3080 static void
3081 cmd_paste_copy_finalize (GObject *cmd)
3083 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
3085 me->saved_sizes_rows = colrow_state_group_destroy (me->saved_sizes_rows);
3086 colrow_index_list_destroy (me->saved_list_rows);
3087 me->saved_list_rows = NULL;
3088 me->saved_sizes_cols = colrow_state_group_destroy (me->saved_sizes_cols);
3089 colrow_index_list_destroy (me->saved_list_cols);
3090 me->saved_list_cols = NULL;
3091 if (me->contents) {
3092 cellregion_unref (me->contents);
3093 me->contents = NULL;
3095 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
3096 g_slist_free_full (me->orig_contents_objects, (GDestroyNotify)g_object_unref);
3097 gnm_command_finalize (cmd);
3101 * cmd_paste_copy will ref cr as needed.
3103 gboolean
3104 cmd_paste_copy (WorkbookControl *wbc,
3105 GnmPasteTarget const *pt, GnmCellRegion *cr)
3107 CmdPasteCopy *me;
3108 int n_r = 1, n_c = 1;
3109 char *range_name;
3110 GnmRange const *merge_src;
3112 g_return_val_if_fail (pt != NULL, TRUE);
3113 g_return_val_if_fail (IS_SHEET (pt->sheet), TRUE);
3114 g_return_val_if_fail (cr != NULL, TRUE);
3116 cellregion_ref (cr);
3118 me = g_object_new (CMD_PASTE_COPY_TYPE, NULL);
3120 me->cmd.sheet = pt->sheet;
3121 me->cmd.size = 1; /* FIXME? */
3123 range_name = undo_range_name (pt->sheet, &pt->range);
3124 me->cmd.cmd_descriptor = g_strdup_printf (_("Pasting into %s"),
3125 range_name);
3126 g_free (range_name);
3128 me->dst = *pt;
3129 me->contents = cr;
3130 me->has_been_through_cycle = FALSE;
3131 me->only_objects = (cr->cols < 1 || cr->rows < 1);
3132 me->saved_sizes_rows = NULL;
3133 me->saved_sizes_cols = NULL;
3134 me->saved_list_rows = NULL;
3135 me->saved_list_cols = NULL;
3136 me->pasted_objects = NULL;
3137 me->orig_contents_objects =
3138 go_slist_map (cr->objects, (GOMapFunc)sheet_object_dup);
3139 me->single_merge_to_single_merge = FALSE;
3141 /* If the input is only objects ignore all this range stuff */
3142 if (!me->only_objects) {
3143 /* see if we need to do any tiling */
3144 GnmRange *r = &me->dst.range;
3145 if (g_slist_length (cr->merged) == 1 &&
3146 (NULL != (merge_src = cr->merged->data)) &&
3147 range_height (merge_src) == cr->rows &&
3148 range_width (merge_src) == cr->cols) {
3149 /* We are copying from a single merge */
3150 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3151 if (merge != NULL && range_equal (r, merge)) {
3152 /* To a single merge */
3153 me->single_merge_to_single_merge = TRUE;
3154 n_c = n_r = 1;
3155 me->dst.paste_flags |= PASTE_DONT_MERGE;
3156 goto copy_ready;
3160 if (pt->paste_flags & PASTE_TRANSPOSE) {
3161 n_c = range_width (r) / cr->rows;
3162 if (n_c < 1) n_c = 1;
3163 r->end.col = r->start.col + n_c * cr->rows - 1;
3165 n_r = range_height (r) / cr->cols;
3166 if (n_r < 1) n_r = 1;
3167 r->end.row = r->start.row + n_r * cr->cols - 1;
3168 } else {
3169 /* Before looking for tiling if we are not transposing,
3170 * allow pasting a full col or row from a single cell */
3171 n_c = range_width (r);
3172 if (n_c == 1 && cr->cols == gnm_sheet_get_max_cols (me->cmd.sheet)) {
3173 r->start.col = 0;
3174 r->end.col = gnm_sheet_get_last_col (me->cmd.sheet);
3175 } else {
3176 n_c /= cr->cols;
3177 if (n_c < 1) n_c = 1;
3178 r->end.col = r->start.col + n_c * cr->cols - 1;
3181 n_r = range_height (r);
3182 if (n_r == 1 && cr->rows == gnm_sheet_get_max_rows (me->cmd.sheet)) {
3183 r->start.row = 0;
3184 r->end.row = gnm_sheet_get_last_row (me->cmd.sheet);
3185 } else {
3186 n_r /= cr->rows;
3187 if (n_r < 1) n_r = 1;
3188 r->end.row = r->start.row + n_r * cr->rows - 1;
3192 if (cr->cols != 1 || cr->rows != 1) {
3193 /* Note: when the source is a single cell, a single target merge is special */
3194 /* see clipboard.c (clipboard_paste_region) */
3195 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3196 if (merge != NULL && range_equal (r, merge)) {
3197 /* destination is a single merge */
3198 /* enlarge it such that the source fits */
3199 if (pt->paste_flags & PASTE_TRANSPOSE) {
3200 if ((r->end.col - r->start.col + 1) < cr->rows)
3201 r->end.col = r->start.col + cr->rows - 1;
3202 if ((r->end.row - r->start.row + 1) < cr->cols)
3203 r->end.row = r->start.row + cr->cols - 1;
3204 } else {
3205 if ((r->end.col - r->start.col + 1) < cr->cols)
3206 r->end.col = r->start.col + cr->cols - 1;
3207 if ((r->end.row - r->start.row + 1) < cr->rows)
3208 r->end.row = r->start.row + cr->rows - 1;
3214 if (n_c * (gnm_float)n_r > 10000.) {
3215 char *number = g_strdup_printf ("%0.0" GNM_FORMAT_f,
3216 (gnm_float)n_c * (gnm_float)n_r);
3217 gboolean result = go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)), FALSE,
3218 _("Do you really want to paste "
3219 "%s copies?"), number);
3220 g_free (number);
3221 if (!result) {
3222 g_object_unref (me);
3223 return TRUE;
3227 copy_ready:
3228 /* Use translate to do a quiet sanity check */
3229 if (range_translate (&me->dst.range, pt->sheet, 0, 0)) {
3230 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
3231 me->cmd.cmd_descriptor,
3232 _("is beyond sheet boundaries"));
3233 g_object_unref (me);
3234 return TRUE;
3237 /* no need to test if all we have are objects or are copying from */
3238 /*a single merge to a single merge*/
3239 if ((!me->only_objects) && (!me->single_merge_to_single_merge)&&
3240 sheet_range_splits_region (pt->sheet, &me->dst.range,
3241 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
3242 g_object_unref (me);
3243 return TRUE;
3246 warn_if_date_trouble (wbc, cr);
3248 return gnm_command_push_undo (wbc, G_OBJECT (me));
3251 /******************************************************************/
3253 #define CMD_AUTOFILL_TYPE (cmd_autofill_get_type ())
3254 #define CMD_AUTOFILL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFILL_TYPE, CmdAutofill))
3256 typedef struct {
3257 GnmCommand cmd;
3259 GnmCellRegion *contents;
3260 GnmPasteTarget dst;
3261 GnmRange src;
3262 int base_col, base_row, w, h, end_col, end_row;
3263 gboolean default_increment;
3264 gboolean inverse_autofill;
3265 ColRowIndexList *columns;
3266 ColRowStateGroup *old_widths;
3267 } CmdAutofill;
3269 static void
3270 cmd_autofill_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3272 CmdAutofill const *orig = (CmdAutofill const *) cmd;
3273 SheetView *sv = wb_control_cur_sheet_view (wbc);
3274 GnmRange const *r = selection_first_range (sv,
3275 GO_CMD_CONTEXT (wbc), _("Autofill"));
3277 if (r == NULL)
3278 return;
3280 cmd_autofill (wbc, sv_sheet (sv), orig->default_increment,
3281 r->start.col, r->start.row, range_width (r), range_height (r),
3282 r->start.col + (orig->end_col - orig->base_col),
3283 r->start.row + (orig->end_row - orig->base_row),
3284 orig->inverse_autofill);
3286 MAKE_GNM_COMMAND (CmdAutofill, cmd_autofill, cmd_autofill_repeat)
3288 static gboolean
3289 cmd_autofill_undo (GnmCommand *cmd, WorkbookControl *wbc)
3291 CmdAutofill *me = CMD_AUTOFILL (cmd);
3292 gboolean res;
3294 g_return_val_if_fail (wbc != NULL, TRUE);
3295 g_return_val_if_fail (me != NULL, TRUE);
3296 g_return_val_if_fail (me->contents != NULL, TRUE);
3298 res = clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc));
3299 cellregion_unref (me->contents);
3300 me->contents = NULL;
3302 if (me->old_widths) {
3303 colrow_restore_state_group (me->cmd.sheet, TRUE,
3304 me->columns,
3305 me->old_widths);
3306 colrow_state_group_destroy (me->old_widths);
3307 me->old_widths = NULL;
3308 colrow_index_list_destroy (me->columns);
3309 me->columns = NULL;
3312 if (res)
3313 return TRUE;
3315 select_range (me->dst.sheet, &me->src, wbc);
3317 return FALSE;
3320 static gboolean
3321 cmd_autofill_redo (GnmCommand *cmd, WorkbookControl *wbc)
3323 CmdAutofill *me = CMD_AUTOFILL (cmd);
3324 GnmRange r;
3326 g_return_val_if_fail (me != NULL, TRUE);
3327 g_return_val_if_fail (me->contents == NULL, TRUE);
3329 me->contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
3331 g_return_val_if_fail (me->contents != NULL, TRUE);
3333 /* FIXME : when we split autofill to support hints and better validation
3334 * move this in there.
3336 /* MW: May 2006: we support hints now. What's this about? */
3337 sheet_clear_region (me->dst.sheet,
3338 me->dst.range.start.col, me->dst.range.start.row,
3339 me->dst.range.end.col, me->dst.range.end.row,
3340 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3341 GO_CMD_CONTEXT (wbc));
3343 if (me->cmd.size == 1)
3344 me->cmd.size += cellregion_cmd_size (me->contents);
3345 if (me->inverse_autofill)
3346 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3347 me->end_col, me->end_row, me->w, me->h,
3348 me->base_col, me->base_row);
3349 else
3350 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3351 me->base_col, me->base_row, me->w, me->h,
3352 me->end_col, me->end_row);
3354 colrow_autofit (me->cmd.sheet, &me->dst.range, TRUE, TRUE,
3355 TRUE, FALSE,
3356 &me->columns, &me->old_widths);
3358 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3359 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3360 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3362 r = range_union (&me->dst.range, &me->src);
3363 select_range (me->dst.sheet, &r, wbc);
3365 return FALSE;
3368 static void
3369 cmd_autofill_finalize (GObject *cmd)
3371 CmdAutofill *me = CMD_AUTOFILL (cmd);
3373 if (me->contents) {
3374 cellregion_unref (me->contents);
3375 me->contents = NULL;
3377 colrow_index_list_destroy (me->columns);
3378 colrow_state_group_destroy (me->old_widths);
3379 gnm_command_finalize (cmd);
3382 gboolean
3383 cmd_autofill (WorkbookControl *wbc, Sheet *sheet,
3384 gboolean default_increment,
3385 int base_col, int base_row,
3386 int w, int h, int end_col, int end_row,
3387 gboolean inverse_autofill)
3389 CmdAutofill *me;
3390 GnmRange target, src;
3392 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3394 /* This would be meaningless */
3395 if (base_col+w-1 == end_col && base_row+h-1 == end_row)
3396 return FALSE;
3398 if (inverse_autofill) {
3399 if (end_col != base_col + w - 1) {
3400 range_init (&target, base_col, base_row,
3401 end_col - w, end_row);
3402 range_init (&src, end_col - w + 1, base_row,
3403 end_col, end_row);
3404 } else {
3405 range_init (&target, base_col, base_row,
3406 end_col, end_row - h);
3407 range_init (&src, base_col, end_row - h + 1,
3408 end_col, end_row);
3410 } else {
3411 if (end_col != base_col + w - 1) {
3412 range_init (&target, base_col + w, base_row,
3413 end_col, end_row);
3414 range_init (&src, base_col, base_row,
3415 base_col + w - 1, end_row);
3416 } else {
3417 range_init (&target, base_col, base_row + h,
3418 end_col, end_row);
3419 range_init (&src, base_col, base_row,
3420 end_col, base_row + h - 1);
3424 /* We don't support clearing regions, when a user uses the autofill
3425 * cursor to 'shrink' a selection
3427 if (target.start.col > target.end.col || target.start.row > target.end.row)
3428 return TRUE;
3430 /* Check arrays or merged regions in src or target regions */
3431 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")) ||
3432 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")))
3433 return TRUE;
3435 me = g_object_new (CMD_AUTOFILL_TYPE, NULL);
3437 me->contents = NULL;
3438 me->dst.sheet = sheet;
3439 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3440 me->dst.range = target;
3441 me->src = src;
3443 me->base_col = base_col;
3444 me->base_row = base_row,
3445 me->w = w;
3446 me->h = h;
3447 me->end_col = end_col;
3448 me->end_row = end_row;
3449 me->default_increment = default_increment;
3450 me->inverse_autofill = inverse_autofill;
3452 me->cmd.sheet = sheet;
3453 me->cmd.size = 1; /* Changed in initial redo. */
3454 me->cmd.cmd_descriptor = g_strdup_printf (_("Autofilling %s"),
3455 range_as_string (&me->dst.range));
3457 return gnm_command_push_undo (wbc, G_OBJECT (me));
3460 /******************************************************************/
3462 #define CMD_COPYREL_TYPE (cmd_copyrel_get_type ())
3463 #define CMD_COPYREL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COPYREL_TYPE, CmdCopyRel))
3465 typedef struct {
3466 GnmCommand cmd;
3468 GOUndo *undo;
3469 GnmPasteTarget dst, src;
3470 int dx, dy;
3471 char const *name;
3472 } CmdCopyRel;
3474 static void
3475 cmd_copyrel_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3477 CmdCopyRel const *orig = (CmdCopyRel const *) cmd;
3478 cmd_copyrel (wbc, orig->dx, orig->dy, orig->name);
3480 MAKE_GNM_COMMAND (CmdCopyRel, cmd_copyrel, cmd_copyrel_repeat)
3482 static gboolean
3483 cmd_copyrel_undo (GnmCommand *cmd, WorkbookControl *wbc)
3485 CmdCopyRel *me = CMD_COPYREL (cmd);
3487 g_return_val_if_fail (wbc != NULL, TRUE);
3488 g_return_val_if_fail (me != NULL, TRUE);
3489 g_return_val_if_fail (me->undo != NULL, TRUE);
3491 go_undo_undo (me->undo);
3493 /* Select the newly pasted contents (this queues a redraw) */
3494 select_range (me->dst.sheet, &me->dst.range, wbc);
3496 return FALSE;
3499 static gboolean
3500 cmd_copyrel_redo (GnmCommand *cmd, WorkbookControl *wbc)
3502 CmdCopyRel *me = CMD_COPYREL (cmd);
3503 GnmCellRegion *contents;
3504 gboolean res;
3506 g_return_val_if_fail (me != NULL, TRUE);
3508 sheet_clear_region (me->dst.sheet,
3509 me->dst.range.start.col, me->dst.range.start.row,
3510 me->dst.range.end.col, me->dst.range.end.row,
3511 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3512 GO_CMD_CONTEXT (wbc));
3514 contents = clipboard_copy_range (me->src.sheet, &me->src.range);
3515 res = clipboard_paste_region (contents, &me->dst, GO_CMD_CONTEXT (wbc));
3516 cellregion_unref (contents);
3517 if (res)
3518 return TRUE;
3520 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3521 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3522 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3524 /* Select the newly pasted contents (this queues a redraw) */
3525 select_range (me->dst.sheet, &me->dst.range, wbc);
3527 return FALSE;
3530 static void
3531 cmd_copyrel_finalize (GObject *cmd)
3533 CmdCopyRel *me = CMD_COPYREL (cmd);
3535 if (me->undo)
3536 g_object_unref (me->undo);
3538 gnm_command_finalize (cmd);
3541 gboolean
3542 cmd_copyrel (WorkbookControl *wbc,
3543 int dx, int dy,
3544 char const *name)
3546 CmdCopyRel *me;
3547 GnmRange target, src;
3548 SheetView *sv = wb_control_cur_sheet_view (wbc);
3549 Sheet *sheet = sv->sheet;
3550 GnmRange const *selr =
3551 selection_first_range (sv, GO_CMD_CONTEXT (wbc), name);
3553 g_return_val_if_fail (dx == 0 || dy == 0, TRUE);
3555 if (!selr)
3556 return FALSE;
3558 target = *selr;
3559 range_normalize (&target);
3560 src.start = src.end = target.start;
3562 if (dy) {
3563 src.end.col = target.end.col;
3564 if (target.start.row != target.end.row)
3565 target.start.row++;
3566 else
3567 src.start.row = src.end.row = (target.start.row + dy);
3570 if (dx) {
3571 src.end.row = target.end.row;
3572 if (target.start.col != target.end.col)
3573 target.start.col++;
3574 else
3575 src.start.col = src.end.col = (target.start.col + dx);
3578 if (src.start.col < 0 || src.start.col >= gnm_sheet_get_max_cols (sheet) ||
3579 src.start.row < 0 || src.start.row >= gnm_sheet_get_max_rows (sheet))
3580 return FALSE;
3582 /* Check arrays or merged regions in src or target regions */
3583 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), name) ||
3584 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), name))
3585 return TRUE;
3587 me = g_object_new (CMD_COPYREL_TYPE, NULL);
3589 me->dst.sheet = sheet;
3590 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3591 me->dst.range = target;
3592 me->src.sheet = sheet;
3593 me->src.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3594 me->src.range = src;
3595 me->dx = dx;
3596 me->dy = dy;
3597 me->name = name;
3598 me->undo = clipboard_copy_range_undo (me->dst.sheet, &me->dst.range);
3600 me->cmd.sheet = sheet;
3601 me->cmd.size = 1;
3602 me->cmd.cmd_descriptor = g_strdup (name);
3604 return gnm_command_push_undo (wbc, G_OBJECT (me));
3607 /******************************************************************/
3610 #define CMD_AUTOFORMAT_TYPE (cmd_autoformat_get_type ())
3611 #define CMD_AUTOFORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFORMAT_TYPE, CmdAutoFormat))
3613 typedef struct {
3614 GnmCellPos pos;
3615 GnmStyleList *styles;
3616 } CmdAutoFormatOldStyle;
3618 typedef struct {
3619 GnmCommand cmd;
3621 GSList *selection; /* Selections on the sheet */
3622 GSList *old_styles; /* Older styles, one style_list per selection range*/
3624 GnmFT *ft; /* Template that has been applied */
3625 } CmdAutoFormat;
3627 static void
3628 cmd_autoformat_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3630 CmdAutoFormat const *orig = (CmdAutoFormat const *) cmd;
3631 cmd_selection_autoformat (wbc, gnm_ft_clone (orig->ft));
3633 MAKE_GNM_COMMAND (CmdAutoFormat, cmd_autoformat, cmd_autoformat_repeat)
3635 static gboolean
3636 cmd_autoformat_undo (GnmCommand *cmd,
3637 G_GNUC_UNUSED WorkbookControl *wbc)
3639 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3641 g_return_val_if_fail (me != NULL, TRUE);
3643 if (me->old_styles) {
3644 GSList *l1 = me->old_styles;
3645 GSList *l2 = me->selection;
3647 for (; l1; l1 = l1->next, l2 = l2->next) {
3648 GnmRange *r;
3649 CmdAutoFormatOldStyle *os = l1->data;
3650 GnmSpanCalcFlags flags = sheet_style_set_list (me->cmd.sheet,
3651 &os->pos, os->styles, NULL, NULL);
3653 g_return_val_if_fail (l2 && l2->data, TRUE);
3655 r = l2->data;
3656 sheet_range_calc_spans (me->cmd.sheet, r, flags);
3657 if (flags != GNM_SPANCALC_SIMPLE)
3658 rows_height_update (me->cmd.sheet, r, TRUE);
3662 return FALSE;
3665 static gboolean
3666 cmd_autoformat_redo (GnmCommand *cmd,
3667 G_GNUC_UNUSED WorkbookControl *wbc)
3669 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3671 g_return_val_if_fail (me != NULL, TRUE);
3673 gnm_ft_apply_to_sheet_regions (me->ft,
3674 me->cmd.sheet, me->selection);
3676 return FALSE;
3679 static void
3680 cmd_autoformat_finalize (GObject *cmd)
3682 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3684 if (me->old_styles != NULL) {
3685 GSList *l;
3687 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
3688 CmdAutoFormatOldStyle *os = l->data;
3690 if (os->styles)
3691 style_list_free (os->styles);
3693 g_free (os);
3696 me->old_styles = NULL;
3699 range_fragment_free (me->selection);
3700 me->selection = NULL;
3702 gnm_ft_free (me->ft);
3704 gnm_command_finalize (cmd);
3708 * cmd_selection_autoformat:
3709 * @wbc: the context.
3710 * @ft: The format template that was applied
3712 * Return value: TRUE if there was a problem
3714 gboolean
3715 cmd_selection_autoformat (WorkbookControl *wbc, GnmFT *ft)
3717 CmdAutoFormat *me;
3718 char *names;
3719 GSList *l;
3720 SheetView *sv = wb_control_cur_sheet_view (wbc);
3722 me = g_object_new (CMD_AUTOFORMAT_TYPE, NULL);
3724 me->selection = selection_get_ranges (sv, FALSE); /* Regions may overlap */
3725 me->ft = ft;
3726 me->cmd.sheet = sv_sheet (sv);
3727 me->cmd.size = 1; /* FIXME? */
3729 if (!gnm_ft_check_valid (ft, me->selection, GO_CMD_CONTEXT (wbc))) {
3730 g_object_unref (me);
3731 return TRUE;
3734 me->old_styles = NULL;
3735 for (l = me->selection; l; l = l->next) {
3736 CmdFormatOldStyle *os;
3737 GnmRange range = *((GnmRange const *) l->data);
3739 /* Store the containing range to handle borders */
3740 if (range.start.col > 0) range.start.col--;
3741 if (range.start.row > 0) range.start.row--;
3742 if (range.end.col < gnm_sheet_get_last_col (sv->sheet)) range.end.col++;
3743 if (range.end.row < gnm_sheet_get_last_row (sv->sheet)) range.end.row++;
3745 os = g_new (CmdFormatOldStyle, 1);
3747 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
3748 os->pos = range.start;
3750 me->old_styles = g_slist_append (me->old_styles, os);
3753 names = undo_range_list_name (me->cmd.sheet, me->selection);
3754 me->cmd.cmd_descriptor = g_strdup_printf (_("Autoformatting %s"),
3755 names);
3756 g_free (names);
3758 return gnm_command_push_undo (wbc, G_OBJECT (me));
3761 /******************************************************************/
3763 #define CMD_UNMERGE_CELLS_TYPE (cmd_unmerge_cells_get_type ())
3764 #define CMD_UNMERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_UNMERGE_CELLS_TYPE, CmdUnmergeCells))
3766 typedef struct {
3767 GnmCommand cmd;
3769 Sheet *sheet;
3770 GArray *unmerged_regions;
3771 GArray *ranges;
3772 } CmdUnmergeCells;
3774 static void
3775 cmd_unmerge_cells_repeat (G_GNUC_UNUSED GnmCommand const *cmd, WorkbookControl *wbc)
3777 SheetView *sv = wb_control_cur_sheet_view (wbc);
3778 GSList *range_list = selection_get_ranges (sv, FALSE);
3779 cmd_unmerge_cells (wbc, sv_sheet (sv), range_list);
3780 range_fragment_free (range_list);
3782 MAKE_GNM_COMMAND (CmdUnmergeCells, cmd_unmerge_cells, cmd_unmerge_cells_repeat)
3784 static gboolean
3785 cmd_unmerge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3787 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3788 unsigned i;
3790 g_return_val_if_fail (me != NULL, TRUE);
3791 g_return_val_if_fail (me->unmerged_regions != NULL, TRUE);
3793 for (i = 0 ; i < me->unmerged_regions->len ; ++i) {
3794 GnmRange const *tmp = &(g_array_index (me->unmerged_regions, GnmRange, i));
3795 sheet_redraw_range (me->cmd.sheet, tmp);
3796 gnm_sheet_merge_add (me->cmd.sheet, tmp, TRUE, GO_CMD_CONTEXT (wbc));
3797 sheet_range_calc_spans (me->cmd.sheet, tmp, GNM_SPANCALC_RE_RENDER);
3800 g_array_free (me->unmerged_regions, TRUE);
3801 me->unmerged_regions = NULL;
3803 return FALSE;
3806 static gboolean
3807 cmd_unmerge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3809 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3810 unsigned i;
3812 g_return_val_if_fail (me != NULL, TRUE);
3813 g_return_val_if_fail (me->unmerged_regions == NULL, TRUE);
3815 me->unmerged_regions = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3816 for (i = 0 ; i < me->ranges->len ; ++i) {
3817 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (me->cmd.sheet,
3818 &(g_array_index (me->ranges, GnmRange, i)));
3819 for (ptr = merged ; ptr != NULL ; ptr = ptr->next) {
3820 GnmRange const *pr = ptr->data;
3821 GnmRange const tmp = *pr;
3822 g_array_append_val (me->unmerged_regions, tmp);
3823 gnm_sheet_merge_remove (me->cmd.sheet, &tmp);
3824 sheet_range_calc_spans (me->cmd.sheet, &tmp,
3825 GNM_SPANCALC_RE_RENDER);
3827 g_slist_free (merged);
3830 return FALSE;
3833 static void
3834 cmd_unmerge_cells_finalize (GObject *cmd)
3836 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3838 if (me->unmerged_regions != NULL) {
3839 g_array_free (me->unmerged_regions, TRUE);
3840 me->unmerged_regions = NULL;
3842 if (me->ranges != NULL) {
3843 g_array_free (me->ranges, TRUE);
3844 me->ranges = NULL;
3847 gnm_command_finalize (cmd);
3851 * cmd_unmerge_cells:
3852 * @wbc: the context.
3853 * @sheet: #Sheet
3854 * @selection: (element-type GnmRange): selection.
3856 * Return value: TRUE if there was a problem
3858 gboolean
3859 cmd_unmerge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection)
3861 CmdUnmergeCells *me;
3862 char *names;
3864 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3866 me = g_object_new (CMD_UNMERGE_CELLS_TYPE, NULL);
3868 me->cmd.sheet = sheet;
3869 me->cmd.size = 1;
3871 names = undo_range_list_name (sheet, selection);
3872 me->cmd.cmd_descriptor = g_strdup_printf (_("Unmerging %s"), names);
3873 g_free (names);
3875 me->unmerged_regions = NULL;
3876 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3877 for ( ; selection != NULL ; selection = selection->next) {
3878 GSList *merged = gnm_sheet_merge_get_overlap (sheet, selection->data);
3879 if (merged != NULL) {
3880 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
3881 g_slist_free (merged);
3885 if (me->ranges->len <= 0) {
3886 g_object_unref (me);
3887 return TRUE;
3890 return gnm_command_push_undo (wbc, G_OBJECT (me));
3893 /******************************************************************/
3895 #define CMD_MERGE_CELLS_TYPE (cmd_merge_cells_get_type ())
3896 #define CMD_MERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_CELLS_TYPE, CmdMergeCells))
3898 typedef struct {
3899 GnmCommand cmd;
3900 GArray *ranges;
3901 GSList *old_contents;
3902 gboolean center;
3903 } CmdMergeCells;
3905 static void
3906 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc);
3908 MAKE_GNM_COMMAND (CmdMergeCells, cmd_merge_cells, cmd_merge_cells_repeat)
3910 static void
3911 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3913 SheetView *sv = wb_control_cur_sheet_view (wbc);
3914 GSList *range_list = selection_get_ranges (sv, FALSE);
3915 cmd_merge_cells (wbc, sv_sheet (sv), range_list,
3916 CMD_MERGE_CELLS (cmd)->center);
3917 range_fragment_free (range_list);
3920 static gboolean
3921 cmd_merge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3923 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3924 unsigned i, flags;
3926 g_return_val_if_fail (me != NULL, TRUE);
3928 for (i = 0 ; i < me->ranges->len ; ++i) {
3929 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3930 gnm_sheet_merge_remove (me->cmd.sheet, r);
3933 /* Avoid pasting comments that are at 0,0. Redo copies the target
3934 * region (including all comments) . If there was a comment in the top
3935 * left we would end up duplicating it. */
3936 flags = PASTE_CONTENTS | PASTE_FORMATS | PASTE_COMMENTS |
3937 PASTE_IGNORE_COMMENTS_AT_ORIGIN;
3938 if (me->center)
3939 flags |= PASTE_FORMATS;
3940 for (i = 0 ; i < me->ranges->len ; ++i) {
3941 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3942 GnmPasteTarget pt;
3943 GnmCellRegion * c;
3945 g_return_val_if_fail (me->old_contents != NULL, TRUE);
3947 c = me->old_contents->data;
3948 clipboard_paste_region (c,
3949 paste_target_init (&pt, me->cmd.sheet, r, flags),
3950 GO_CMD_CONTEXT (wbc));
3951 cellregion_unref (c);
3952 me->old_contents = g_slist_remove (me->old_contents, c);
3954 g_return_val_if_fail (me->old_contents == NULL, TRUE);
3956 return FALSE;
3959 static gboolean
3960 cmd_merge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3962 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3963 GnmStyle *align_center = NULL;
3964 Sheet *sheet;
3965 unsigned i;
3967 g_return_val_if_fail (me != NULL, TRUE);
3969 if (me->center) {
3970 align_center = gnm_style_new ();
3971 gnm_style_set_align_h (align_center, GNM_HALIGN_CENTER);
3973 sheet = me->cmd.sheet;
3974 for (i = 0 ; i < me->ranges->len ; ++i) {
3975 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3976 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (sheet, r);
3978 /* save contents before removing contained merged regions */
3979 me->old_contents = g_slist_prepend (me->old_contents,
3980 clipboard_copy_range (sheet, r));
3981 for (ptr = merged ; ptr != NULL ; ptr = ptr->next)
3982 gnm_sheet_merge_remove (sheet, ptr->data);
3983 g_slist_free (merged);
3985 gnm_sheet_merge_add (sheet, r, TRUE, GO_CMD_CONTEXT (wbc));
3986 if (me->center)
3987 sheet_apply_style (me->cmd.sheet, r, align_center);
3990 if (me->center)
3991 gnm_style_unref (align_center);
3992 me->old_contents = g_slist_reverse (me->old_contents);
3993 return FALSE;
3996 static void
3997 cmd_merge_cells_finalize (GObject *cmd)
3999 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
4001 if (me->old_contents != NULL) {
4002 GSList *l;
4003 for (l = me->old_contents ; l != NULL ; l = g_slist_remove (l, l->data))
4004 cellregion_unref (l->data);
4005 me->old_contents = NULL;
4008 if (me->ranges != NULL) {
4009 g_array_free (me->ranges, TRUE);
4010 me->ranges = NULL;
4013 gnm_command_finalize (cmd);
4017 * cmd_merge_cells:
4018 * @wbc: the context.
4019 * @sheet: #Sheet
4020 * @selection: (element-type GnmRange): selection.
4021 * @center:
4023 * Return value: %TRUE if there was a problem
4025 gboolean
4026 cmd_merge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection,
4027 gboolean center)
4029 CmdMergeCells *me;
4030 char *names;
4032 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4034 me = g_object_new (CMD_MERGE_CELLS_TYPE, NULL);
4036 me->cmd.sheet = sheet;
4037 me->cmd.size = 1;
4039 names = undo_range_list_name (sheet, selection);
4040 me->cmd.cmd_descriptor =
4041 g_strdup_printf ((center ? _("Merge and Center %s") :_("Merging %s")), names);
4042 g_free (names);
4044 me->center = center;
4045 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
4046 for ( ; selection != NULL ; selection = selection->next) {
4047 GnmRange const *exist;
4048 GnmRange const *r = selection->data;
4049 if (range_is_singleton (selection->data))
4050 continue;
4051 if (NULL != (exist = gnm_sheet_merge_is_corner (sheet, &r->start)) &&
4052 range_equal (r, exist))
4053 continue;
4054 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
4057 if (me->ranges->len <= 0) {
4058 g_object_unref (me);
4059 return TRUE;
4062 return gnm_command_push_undo (wbc, G_OBJECT (me));
4065 /******************************************************************/
4067 #define CMD_SEARCH_REPLACE_TYPE (cmd_search_replace_get_type())
4068 #define CMD_SEARCH_REPLACE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SEARCH_REPLACE_TYPE, CmdSearchReplace))
4070 typedef struct {
4071 GnmCommand cmd;
4072 GnmSearchReplace *sr;
4075 * Undo/redo use this list of SearchReplaceItems to do their
4076 * work. Note, that it is possible for a cell to occur
4077 * multiple times in the list.
4079 GList *cells;
4080 } CmdSearchReplace;
4082 MAKE_GNM_COMMAND (CmdSearchReplace, cmd_search_replace, NULL)
4084 typedef enum { SRI_text, SRI_comment } SearchReplaceItemType;
4086 typedef struct {
4087 GnmEvalPos pos;
4088 SearchReplaceItemType old_type, new_type;
4089 union {
4090 char *text;
4091 char *comment;
4092 } old, new;
4093 } SearchReplaceItem;
4096 static void
4097 cmd_search_replace_update_after_action (CmdSearchReplace *me,
4098 WorkbookControl *wbc)
4100 GList *tmp;
4101 Sheet *last_sheet = NULL;
4103 for (tmp = me->cells; tmp; tmp = tmp->next) {
4104 SearchReplaceItem *sri = tmp->data;
4105 if (sri->pos.sheet != last_sheet) {
4106 last_sheet = sri->pos.sheet;
4107 update_after_action (last_sheet, wbc);
4113 static gboolean
4114 cmd_search_replace_undo (GnmCommand *cmd,
4115 WorkbookControl *wbc)
4117 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4118 GList *tmp;
4120 /* Undo does replacements backwards. */
4121 for (tmp = g_list_last (me->cells); tmp; tmp = tmp->prev) {
4122 SearchReplaceItem *sri = tmp->data;
4123 switch (sri->old_type) {
4124 case SRI_text:
4126 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4127 sri->pos.eval.col,
4128 sri->pos.eval.row);
4129 sheet_cell_set_text (cell, sri->old.text, NULL);
4130 break;
4132 case SRI_comment:
4134 GnmComment *comment =
4135 sheet_get_comment (sri->pos.sheet,
4136 &sri->pos.eval);
4137 if (comment) {
4138 cell_comment_text_set (comment, sri->old.comment);
4139 } else {
4140 g_warning ("Undo/redo broken.");
4143 break;
4146 cmd_search_replace_update_after_action (me, wbc);
4148 return FALSE;
4151 static gboolean
4152 cmd_search_replace_redo (GnmCommand *cmd,
4153 WorkbookControl *wbc)
4155 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4156 GList *tmp;
4158 /* Redo does replacements forward. */
4159 for (tmp = me->cells; tmp; tmp = tmp->next) {
4160 SearchReplaceItem *sri = tmp->data;
4161 switch (sri->new_type) {
4162 case SRI_text:
4164 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4165 sri->pos.eval.col,
4166 sri->pos.eval.row);
4167 sheet_cell_set_text (cell, sri->new.text, NULL);
4168 break;
4170 case SRI_comment:
4172 GnmComment *comment =
4173 sheet_get_comment (sri->pos.sheet,
4174 &sri->pos.eval);
4175 if (comment) {
4176 cell_comment_text_set (comment, sri->new.comment);
4177 } else {
4178 g_warning ("Undo/redo broken.");
4181 break;
4184 cmd_search_replace_update_after_action (me, wbc);
4186 return FALSE;
4189 static gboolean
4190 cmd_search_replace_do_cell (CmdSearchReplace *me, GnmEvalPos *ep,
4191 gboolean test_run)
4193 GnmSearchReplace *sr = me->sr;
4195 GnmSearchReplaceCellResult cell_res;
4196 GnmSearchReplaceCommentResult comment_res;
4198 if (gnm_search_replace_cell (sr, ep, TRUE, &cell_res)) {
4199 GnmExprTop const *texpr;
4200 GnmValue *val;
4201 gboolean err;
4202 GnmParsePos pp;
4204 parse_pos_init_evalpos (&pp, ep);
4205 parse_text_value_or_expr (&pp, cell_res.new_text, &val, &texpr);
4208 * FIXME: this is a hack, but parse_text_value_or_expr
4209 * does not have a better way of signaling an error.
4211 err = (val &&
4212 gnm_expr_char_start_p (cell_res.new_text) &&
4213 !go_format_is_text (gnm_cell_get_format (cell_res.cell)));
4214 value_release (val);
4215 if (texpr) gnm_expr_top_unref (texpr);
4217 if (err) {
4218 if (test_run) {
4219 gnm_search_replace_query_fail (sr, &cell_res);
4220 g_free (cell_res.old_text);
4221 g_free (cell_res.new_text);
4222 return TRUE;
4223 } else {
4224 switch (sr->error_behaviour) {
4225 case GNM_SRE_ERROR: {
4226 GnmExprTop const *ee =
4227 gnm_expr_top_new
4228 (gnm_expr_new_funcall1
4229 (gnm_func_lookup ("ERROR", NULL),
4230 gnm_expr_new_constant
4231 (value_new_string_nocopy (cell_res.new_text))));
4232 GnmConventionsOut out;
4234 out.accum = g_string_new ("=");
4235 out.pp = &pp;
4236 out.convs = pp.sheet->convs;
4237 gnm_expr_top_as_gstring (ee, &out);
4238 gnm_expr_top_unref (ee);
4239 cell_res.new_text = g_string_free (out.accum, FALSE);
4240 err = FALSE;
4241 break;
4243 case GNM_SRE_STRING: {
4244 GString *s = g_string_new ("'");
4245 g_string_append (s, cell_res.new_text);
4246 g_free (cell_res.new_text);
4247 cell_res.new_text = g_string_free (s, FALSE);
4248 err = FALSE;
4249 break;
4251 case GNM_SRE_FAIL:
4252 g_assert_not_reached ();
4253 case GNM_SRE_SKIP:
4254 default:
4255 ; /* Nothing */
4260 if (!err && !test_run) {
4261 int res = gnm_search_replace_query_cell
4262 (sr, &cell_res);
4263 gboolean doit = (res == GTK_RESPONSE_YES);
4265 if (res == GTK_RESPONSE_CANCEL) {
4266 g_free (cell_res.old_text);
4267 g_free (cell_res.new_text);
4268 return TRUE;
4271 if (doit) {
4272 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4274 sheet_cell_set_text (cell_res.cell, cell_res.new_text, NULL);
4276 sri->pos = *ep;
4277 sri->old_type = sri->new_type = SRI_text;
4278 sri->old.text = cell_res.old_text;
4279 sri->new.text = cell_res.new_text;
4280 me->cells = g_list_prepend (me->cells, sri);
4282 cell_res.old_text = cell_res.new_text = NULL;
4286 g_free (cell_res.new_text);
4287 g_free (cell_res.old_text);
4290 if (!test_run &&
4291 gnm_search_replace_comment (sr, ep, TRUE, &comment_res)) {
4292 int res = gnm_search_replace_query_comment
4293 (sr, ep, &comment_res);
4294 gboolean doit = (res == GTK_RESPONSE_YES);
4296 if (doit) {
4297 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4298 sri->pos = *ep;
4299 sri->old_type = sri->new_type = SRI_comment;
4300 sri->old.comment = g_strdup (comment_res.old_text);
4301 sri->new.comment = comment_res.new_text;
4302 me->cells = g_list_prepend (me->cells, sri);
4304 cell_comment_text_set (comment_res.comment, comment_res.new_text);
4305 } else {
4306 g_free (comment_res.new_text);
4307 if (res == GTK_RESPONSE_CANCEL)
4308 return TRUE;
4312 return FALSE;
4316 static gboolean
4317 cmd_search_replace_do (CmdSearchReplace *me, gboolean test_run,
4318 WorkbookControl *wbc)
4320 GnmSearchReplace *sr = me->sr;
4321 GPtrArray *cells;
4322 gboolean result = FALSE;
4323 unsigned i;
4325 if (test_run) {
4326 switch (sr->error_behaviour) {
4327 case GNM_SRE_SKIP:
4328 case GNM_SRE_QUERY:
4329 case GNM_SRE_ERROR:
4330 case GNM_SRE_STRING:
4331 /* An error is not a problem. */
4332 return FALSE;
4334 case GNM_SRE_FAIL:
4335 ; /* Nothing. */
4339 cells = gnm_search_collect_cells (sr);
4341 for (i = 0; i < cells->len; i++) {
4342 GnmEvalPos *ep = g_ptr_array_index (cells, i);
4344 if (cmd_search_replace_do_cell (me, ep, test_run)) {
4345 result = TRUE;
4346 break;
4350 gnm_search_collect_cells_free (cells);
4352 if (!test_run) {
4353 /* Cells were added in the wrong order. Correct. */
4354 me->cells = g_list_reverse (me->cells);
4356 cmd_search_replace_update_after_action (me, wbc);
4359 return result;
4363 static void
4364 cmd_search_replace_finalize (GObject *cmd)
4366 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4367 GList *tmp;
4369 for (tmp = me->cells; tmp; tmp = tmp->next) {
4370 SearchReplaceItem *sri = tmp->data;
4371 switch (sri->old_type) {
4372 case SRI_text:
4373 g_free (sri->old.text);
4374 break;
4375 case SRI_comment:
4376 g_free (sri->old.comment);
4377 break;
4379 switch (sri->new_type) {
4380 case SRI_text:
4381 g_free (sri->new.text);
4382 break;
4383 case SRI_comment:
4384 g_free (sri->new.comment);
4385 break;
4387 g_free (sri);
4389 g_list_free (me->cells);
4390 g_object_unref (me->sr);
4392 gnm_command_finalize (cmd);
4395 gboolean
4396 cmd_search_replace (WorkbookControl *wbc, GnmSearchReplace *sr)
4398 CmdSearchReplace *me;
4400 g_return_val_if_fail (sr != NULL, TRUE);
4402 me = g_object_new (CMD_SEARCH_REPLACE_TYPE, NULL);
4404 me->cells = NULL;
4405 me->sr = g_object_ref (sr);
4407 me->cmd.sheet = NULL;
4408 me->cmd.size = 1; /* Corrected below. */
4409 me->cmd.cmd_descriptor = g_strdup (_("Search and Replace"));
4411 if (cmd_search_replace_do (me, TRUE, wbc)) {
4412 /* There was an error and nothing was done. */
4413 g_object_unref (me);
4414 return TRUE;
4417 cmd_search_replace_do (me, FALSE, wbc);
4418 me->cmd.size += g_list_length (me->cells);
4420 command_register_undo (wbc, G_OBJECT (me));
4421 return FALSE;
4424 /******************************************************************/
4426 #define CMD_COLROW_STD_SIZE_TYPE (cmd_colrow_std_size_get_type ())
4427 #define CMD_COLROW_STD_SIZE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_STD_SIZE_TYPE, CmdColRowStdSize))
4429 typedef struct {
4430 GnmCommand cmd;
4432 Sheet *sheet;
4433 gboolean is_cols;
4434 double new_default;
4435 double old_default;
4436 } CmdColRowStdSize;
4438 MAKE_GNM_COMMAND (CmdColRowStdSize, cmd_colrow_std_size, NULL)
4440 static gboolean
4441 cmd_colrow_std_size_undo (GnmCommand *cmd,
4442 G_GNUC_UNUSED WorkbookControl *wbc)
4444 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4446 g_return_val_if_fail (me != NULL, TRUE);
4447 g_return_val_if_fail (me->old_default != 0, TRUE);
4449 if (me->is_cols)
4450 sheet_col_set_default_size_pts (me->sheet, me->old_default);
4451 else
4452 sheet_row_set_default_size_pts (me->sheet, me->old_default);
4454 me->old_default = 0;
4456 return FALSE;
4459 static gboolean
4460 cmd_colrow_std_size_redo (GnmCommand *cmd,
4461 G_GNUC_UNUSED WorkbookControl *wbc)
4463 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4465 g_return_val_if_fail (me != NULL, TRUE);
4466 g_return_val_if_fail (me->old_default == 0, TRUE);
4468 if (me->is_cols) {
4469 me->old_default = sheet_col_get_default_size_pts (me->sheet);
4470 sheet_col_set_default_size_pts (me->sheet, me->new_default);
4471 } else {
4472 me->old_default = sheet_row_get_default_size_pts (me->sheet);
4473 sheet_row_set_default_size_pts (me->sheet, me->new_default);
4476 return FALSE;
4478 static void
4479 cmd_colrow_std_size_finalize (GObject *cmd)
4481 gnm_command_finalize (cmd);
4484 gboolean
4485 cmd_colrow_std_size (WorkbookControl *wbc, Sheet *sheet,
4486 gboolean is_cols, double new_default)
4488 CmdColRowStdSize *me;
4490 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4492 me = g_object_new (CMD_COLROW_STD_SIZE_TYPE, NULL);
4494 me->sheet = sheet;
4495 me->is_cols = is_cols;
4496 me->new_default = new_default;
4497 me->old_default = 0;
4499 me->cmd.sheet = sheet;
4500 me->cmd.size = 1; /* Changed in initial redo. */
4501 me->cmd.cmd_descriptor = is_cols
4502 ? g_strdup_printf (_("Setting default width of columns to %.2fpts"), new_default)
4503 : g_strdup_printf (_("Setting default height of rows to %.2fpts"), new_default);
4505 return gnm_command_push_undo (wbc, G_OBJECT (me));
4508 /******************************************************************/
4510 #define CMD_ZOOM_TYPE (cmd_zoom_get_type ())
4511 #define CMD_ZOOM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ZOOM_TYPE, CmdZoom))
4513 typedef struct {
4514 GnmCommand cmd;
4516 GSList *sheets;
4517 double new_factor;
4518 double *old_factors;
4519 } CmdZoom;
4521 MAKE_GNM_COMMAND (CmdZoom, cmd_zoom, NULL)
4523 static gboolean
4524 cmd_zoom_undo (GnmCommand *cmd,
4525 G_GNUC_UNUSED WorkbookControl *wbc)
4527 CmdZoom *me = CMD_ZOOM (cmd);
4528 GSList *l;
4529 int i;
4531 g_return_val_if_fail (me != NULL, TRUE);
4532 g_return_val_if_fail (me->sheets != NULL, TRUE);
4533 g_return_val_if_fail (me->old_factors != NULL, TRUE);
4535 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4536 Sheet *sheet = l->data;
4537 g_object_set (sheet, "zoom-factor", me->old_factors[i], NULL);
4540 return FALSE;
4543 static gboolean
4544 cmd_zoom_redo (GnmCommand *cmd,
4545 G_GNUC_UNUSED WorkbookControl *wbc)
4547 CmdZoom *me = CMD_ZOOM (cmd);
4548 GSList *l;
4550 g_return_val_if_fail (me != NULL, TRUE);
4551 g_return_val_if_fail (me->sheets != NULL, TRUE);
4553 for (l = me->sheets; l != NULL; l = l->next) {
4554 Sheet *sheet = l->data;
4555 g_object_set (sheet, "zoom-factor", me->new_factor, NULL);
4558 return FALSE;
4561 static void
4562 cmd_zoom_finalize (GObject *cmd)
4564 CmdZoom *me = CMD_ZOOM (cmd);
4566 g_slist_free (me->sheets);
4567 g_free (me->old_factors);
4569 gnm_command_finalize (cmd);
4573 * cmd_zoom:
4574 * @wbc: #WorkbookControl
4575 * @sheets: (element-type Sheet) (transfer container):
4576 * @factor:
4579 gboolean
4580 cmd_zoom (WorkbookControl *wbc, GSList *sheets, double factor)
4582 CmdZoom *me;
4583 GString *namelist;
4584 GSList *l;
4585 int i;
4587 g_return_val_if_fail (wbc != NULL, TRUE);
4588 g_return_val_if_fail (sheets != NULL, TRUE);
4590 me = g_object_new (CMD_ZOOM_TYPE, NULL);
4592 me->sheets = sheets;
4593 me->old_factors = g_new0 (double, g_slist_length (sheets));
4594 me->new_factor = factor;
4596 /* Make a list of all sheets to zoom and save zoom factor for each */
4597 namelist = g_string_new (NULL);
4598 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4599 Sheet *sheet = l->data;
4601 g_string_append (namelist, sheet->name_unquoted);
4602 me->old_factors[i] = sheet->last_zoom_factor_used;
4604 if (l->next)
4605 g_string_append (namelist, ", ");
4608 /* Make sure the string doesn't get overly wide */
4609 gnm_cmd_trunc_descriptor (namelist, NULL);
4611 me->cmd.sheet = NULL;
4612 me->cmd.size = 1;
4613 me->cmd.cmd_descriptor =
4614 g_strdup_printf (_("Zoom %s to %.0f%%"), namelist->str, factor * 100);
4616 g_string_free (namelist, TRUE);
4618 return gnm_command_push_undo (wbc, G_OBJECT (me));
4621 /******************************************************************/
4623 #define CMD_OBJECTS_DELETE_TYPE (cmd_objects_delete_get_type ())
4624 #define CMD_OBJECTS_DELETE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECTS_DELETE_TYPE, CmdObjectsDelete))
4626 typedef struct {
4627 GnmCommand cmd;
4628 GSList *objects;
4629 GArray *location;
4630 } CmdObjectsDelete;
4632 MAKE_GNM_COMMAND (CmdObjectsDelete, cmd_objects_delete, NULL)
4634 static gboolean
4635 cmd_objects_delete_redo (GnmCommand *cmd,
4636 G_GNUC_UNUSED WorkbookControl *wbc)
4638 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4639 g_slist_foreach (me->objects, (GFunc) sheet_object_clear_sheet, NULL);
4640 return FALSE;
4643 static void
4644 cmd_objects_restore_location (SheetObject *so, gint location)
4646 gint loc = sheet_object_get_stacking (so);
4647 if (loc != location)
4648 sheet_object_adjust_stacking(so, location - loc);
4651 static gboolean
4652 cmd_objects_delete_undo (GnmCommand *cmd,
4653 G_GNUC_UNUSED WorkbookControl *wbc)
4655 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4656 GSList *l;
4657 gint i;
4659 g_slist_foreach (me->objects,
4660 (GFunc) sheet_object_set_sheet, me->cmd.sheet);
4662 for (l = me->objects, i = 0; l; l = l->next, i++)
4663 cmd_objects_restore_location (GNM_SO (l->data),
4664 g_array_index(me->location,
4665 gint, i));
4666 return FALSE;
4669 static void
4670 cmd_objects_delete_finalize (GObject *cmd)
4672 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4673 g_slist_free_full (me->objects, g_object_unref);
4674 if (me->location) {
4675 g_array_free (me->location, TRUE);
4676 me->location = NULL;
4678 gnm_command_finalize (cmd);
4681 static void
4682 cmd_objects_store_location (SheetObject *so, GArray *location)
4684 gint loc = sheet_object_get_stacking (so);
4685 g_array_append_val (location, loc);
4689 * cmd_objects_delete:
4690 * @wbc: #WorkbookControl
4691 * @objects: (element-type SheetObject) (transfer container): the objects to
4692 * delete.
4693 * @name:
4695 * Absorbs the list, adding references to the contents.
4697 gboolean
4698 cmd_objects_delete (WorkbookControl *wbc, GSList *objects,
4699 char const *name)
4701 CmdObjectsDelete *me;
4703 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4704 g_return_val_if_fail (objects != NULL, TRUE);
4706 me = g_object_new (CMD_OBJECTS_DELETE_TYPE, NULL);
4708 me->objects = objects;
4709 g_slist_foreach (me->objects, (GFunc) g_object_ref, NULL);
4711 me->location = g_array_new (FALSE, FALSE, sizeof (gint));
4712 g_slist_foreach (me->objects, (GFunc) cmd_objects_store_location,
4713 me->location);
4715 me->cmd.sheet = sheet_object_get_sheet (objects->data);
4716 me->cmd.size = 1;
4717 me->cmd.cmd_descriptor = g_strdup (name ? name : _("Delete Object"));
4719 return gnm_command_push_undo (wbc, G_OBJECT (me));
4722 /******************************************************************/
4725 * cmd_objects_move:
4726 * @wbc: #WorkbookControl
4727 * @objects: (element-type SheetObject) (transfer container): the objects to move.
4728 * @anchors: (element-type SheetObjectAnchor) (transfer full): the anchors for the objects.
4729 * @objects_created:
4730 * @name:
4733 gboolean
4734 cmd_objects_move (WorkbookControl *wbc, GSList *objects, GSList *anchors,
4735 gboolean objects_created, char const *name)
4737 GOUndo *undo = NULL;
4738 GOUndo *redo = NULL;
4739 gboolean result = TRUE;
4741 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4743 undo = sheet_object_move_undo (objects, objects_created);
4744 redo = sheet_object_move_do (objects, anchors, objects_created);
4746 result = cmd_generic (wbc, name, undo, redo);
4748 g_slist_free (objects);
4749 g_slist_free_full (anchors, g_free);
4751 return result;
4754 /******************************************************************/
4756 #define CMD_REORGANIZE_SHEETS_TYPE (cmd_reorganize_sheets_get_type ())
4757 #define CMD_REORGANIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REORGANIZE_SHEETS_TYPE, CmdReorganizeSheets))
4759 typedef struct {
4760 GnmCommand cmd;
4761 Workbook *wb;
4762 WorkbookSheetState *old;
4763 WorkbookSheetState *new;
4764 gboolean first;
4765 Sheet *undo_sheet;
4766 Sheet *redo_sheet;
4767 } CmdReorganizeSheets;
4769 MAKE_GNM_COMMAND (CmdReorganizeSheets, cmd_reorganize_sheets, NULL)
4771 static gboolean
4772 cmd_reorganize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4774 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4775 workbook_sheet_state_restore (me->wb, me->old);
4776 if (me->undo_sheet) {
4777 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4778 wb_control_sheet_focus (control, me->undo_sheet););
4780 return FALSE;
4783 static gboolean
4784 cmd_reorganize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4786 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4788 if (me->first)
4789 me->first = FALSE;
4790 else {
4791 workbook_sheet_state_restore (me->wb, me->new);
4792 if (me->redo_sheet) {
4793 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4794 wb_control_sheet_focus (control, me->redo_sheet););
4798 return FALSE;
4801 static void
4802 cmd_reorganize_sheets_finalize (GObject *cmd)
4804 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4806 if (me->old)
4807 workbook_sheet_state_free (me->old);
4808 if (me->new)
4809 workbook_sheet_state_free (me->new);
4811 gnm_command_finalize (cmd);
4814 gboolean
4815 cmd_reorganize_sheets (WorkbookControl *wbc,
4816 WorkbookSheetState *old_state,
4817 Sheet *undo_sheet)
4819 CmdReorganizeSheets *me;
4820 Workbook *wb = wb_control_get_workbook (wbc);
4822 me = g_object_new (CMD_REORGANIZE_SHEETS_TYPE, NULL);
4823 me->wb = wb;
4824 me->old = old_state;
4825 me->new = workbook_sheet_state_new (me->wb);
4826 me->first = TRUE;
4827 me->undo_sheet = undo_sheet;
4828 me->redo_sheet = wb_control_cur_sheet (wbc);
4830 me->cmd.sheet = NULL;
4831 me->cmd.size = workbook_sheet_state_size (me->old) +
4832 workbook_sheet_state_size (me->new);
4833 me->cmd.cmd_descriptor =
4834 workbook_sheet_state_diff (me->old, me->new);
4836 if (me->cmd.cmd_descriptor)
4837 return gnm_command_push_undo (wbc, G_OBJECT (me));
4839 /* No change. */
4840 g_object_unref (me);
4841 return FALSE;
4844 /******************************************************************/
4846 gboolean
4847 cmd_rename_sheet (WorkbookControl *wbc,
4848 Sheet *sheet,
4849 char const *new_name)
4851 WorkbookSheetState *old_state;
4852 Sheet *collision;
4854 g_return_val_if_fail (new_name != NULL, TRUE);
4855 g_return_val_if_fail (sheet != NULL, TRUE);
4857 if (*new_name == 0) {
4858 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), _("Sheet names must be non-empty."));
4859 return TRUE;
4862 collision = workbook_sheet_by_name (sheet->workbook, new_name);
4863 if (collision && collision != sheet) {
4864 GError *err = g_error_new (go_error_invalid(), 0,
4865 _("A workbook cannot have two sheets with the same name."));
4866 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
4867 g_error_free (err);
4868 return TRUE;
4871 old_state = workbook_sheet_state_new (sheet->workbook);
4872 g_object_set (sheet, "name", new_name, NULL);
4873 return cmd_reorganize_sheets (wbc, old_state, sheet);
4876 /******************************************************************/
4878 #define CMD_RESIZE_SHEETS_TYPE (cmd_resize_sheets_get_type ())
4879 #define CMD_RESIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESIZE_SHEETS_TYPE, CmdResizeSheets))
4881 typedef struct {
4882 GnmCommand cmd;
4883 GSList *sheets;
4884 int cols, rows;
4885 GOUndo *undo;
4886 } CmdResizeSheets;
4888 MAKE_GNM_COMMAND (CmdResizeSheets, cmd_resize_sheets, NULL)
4890 static gboolean
4891 cmd_resize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4893 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4894 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4896 go_undo_undo_with_data (me->undo, cc);
4897 g_object_unref (me->undo);
4898 me->undo = NULL;
4900 return FALSE;
4903 static gboolean
4904 cmd_resize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4906 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4907 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4908 GSList *l;
4910 for (l = me->sheets; l; l = l->next) {
4911 Sheet *sheet = l->data;
4912 gboolean err;
4913 GOUndo *u = gnm_sheet_resize (sheet, me->cols, me->rows,
4914 cc, &err);
4915 me->undo = go_undo_combine (me->undo, u);
4917 if (err) {
4918 if (me->undo)
4919 go_undo_undo_with_data (me->undo, cc);
4920 return TRUE;
4924 return FALSE;
4927 static void
4928 cmd_resize_sheets_finalize (GObject *cmd)
4930 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4932 g_slist_free (me->sheets);
4933 if (me->undo) {
4934 g_object_unref (me->undo);
4935 me->undo = NULL;
4938 gnm_command_finalize (cmd);
4942 * cmd_resize_sheets:
4943 * @wbc: #WorkbookControl
4944 * @sheets: (element-type Sheet) (transfer container): the sheets to resize.
4945 * @cols: new columns number.
4946 * @rows: new rows number.
4949 gboolean
4950 cmd_resize_sheets (WorkbookControl *wbc,
4951 GSList *sheets,
4952 int cols, int rows)
4954 CmdResizeSheets *me;
4956 me = g_object_new (CMD_RESIZE_SHEETS_TYPE, NULL);
4957 me->sheets = sheets;
4958 me->cols = cols;
4959 me->rows = rows;
4960 me->cmd.sheet = sheets ? sheets->data : NULL;
4961 me->cmd.size = 1;
4962 me->cmd.cmd_descriptor = g_strdup (_("Resizing sheet"));
4964 if (sheets &&
4965 gnm_sheet_valid_size (cols, rows))
4966 return gnm_command_push_undo (wbc, G_OBJECT (me));
4968 /* No change. */
4969 g_object_unref (me);
4970 return FALSE;
4973 /******************************************************************/
4975 #define CMD_SET_COMMENT_TYPE (cmd_set_comment_get_type ())
4976 #define CMD_SET_COMMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SET_COMMENT_TYPE, CmdSetComment))
4978 typedef struct {
4979 GnmCommand cmd;
4981 Sheet *sheet;
4982 GnmCellPos pos;
4983 gchar *new_text;
4984 gchar *old_text;
4985 gchar *new_author;
4986 gchar *old_author;
4987 PangoAttrList *old_attributes;
4988 PangoAttrList *new_attributes;
4989 } CmdSetComment;
4991 MAKE_GNM_COMMAND (CmdSetComment, cmd_set_comment, NULL)
4993 static gboolean
4994 cmd_set_comment_apply (Sheet *sheet, GnmCellPos *pos,
4995 char const *text, PangoAttrList *attributes,
4996 char const *author)
4998 GnmComment *comment;
4999 Workbook *wb = sheet->workbook;
5001 comment = sheet_get_comment (sheet, pos);
5002 if (comment) {
5003 if (text)
5004 g_object_set (G_OBJECT (comment), "text", text,
5005 "author", author,
5006 "markup", attributes, NULL);
5007 else {
5008 GnmRange const *mr;
5010 mr = gnm_sheet_merge_contains_pos (sheet, pos);
5012 if (mr)
5013 sheet_objects_clear (sheet, mr, GNM_CELL_COMMENT_TYPE, NULL);
5014 else {
5015 GnmRange r;
5016 r.start = r.end = *pos;
5017 sheet_objects_clear (sheet, &r, GNM_CELL_COMMENT_TYPE, NULL);
5020 } else if (text && (strlen (text) > 0)) {
5021 cell_set_comment (sheet, pos, author, text, attributes);
5023 sheet_mark_dirty (sheet);
5025 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
5026 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
5028 return FALSE;
5031 static gboolean
5032 cmd_set_comment_undo (GnmCommand *cmd,
5033 G_GNUC_UNUSED WorkbookControl *wbc)
5035 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5037 return cmd_set_comment_apply (me->sheet, &me->pos,
5038 me->old_text, me->old_attributes,
5039 me->old_author);
5042 static gboolean
5043 cmd_set_comment_redo (GnmCommand *cmd,
5044 G_GNUC_UNUSED WorkbookControl *wbc)
5046 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5048 return cmd_set_comment_apply (me->sheet, &me->pos,
5049 me->new_text, me->new_attributes,
5050 me->new_author);
5053 static void
5054 cmd_set_comment_finalize (GObject *cmd)
5056 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5058 g_free (me->new_text);
5059 me->new_text = NULL;
5061 g_free (me->old_text);
5062 me->old_text = NULL;
5064 g_free (me->new_author);
5065 me->new_author = NULL;
5067 g_free (me->old_author);
5068 me->old_author = NULL;
5070 if (me->old_attributes != NULL) {
5071 pango_attr_list_unref (me->old_attributes);
5072 me->old_attributes = NULL;
5075 if (me->new_attributes != NULL) {
5076 pango_attr_list_unref (me->new_attributes);
5077 me->new_attributes = NULL;
5080 gnm_command_finalize (cmd);
5083 gboolean
5084 cmd_set_comment (WorkbookControl *wbc,
5085 Sheet *sheet, GnmCellPos const *pos,
5086 char const *new_text,
5087 PangoAttrList *attr,
5088 char const *new_author)
5090 CmdSetComment *me;
5091 GnmComment *comment;
5092 char *where;
5094 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5095 g_return_val_if_fail (new_text != NULL, TRUE);
5097 me = g_object_new (CMD_SET_COMMENT_TYPE, NULL);
5099 me->cmd.sheet = sheet;
5100 me->cmd.size = 1;
5101 if (strlen (new_text) < 1)
5102 me->new_text = NULL;
5103 else
5104 me->new_text = g_strdup (new_text);
5105 if (strlen (new_author) < 1)
5106 me->new_author = NULL;
5107 else
5108 me->new_author = g_strdup (new_author);
5109 if (attr != NULL)
5110 pango_attr_list_ref (attr);
5111 me->new_attributes = attr;
5112 where = undo_cell_pos_name (sheet, pos);
5113 me->cmd.cmd_descriptor =
5114 g_strdup_printf (me->new_text == NULL ?
5115 _("Clearing comment of %s") :
5116 _("Setting comment of %s"),
5117 where);
5118 g_free (where);
5119 me->old_text = NULL;
5120 me->old_author = NULL;
5121 me->old_attributes = NULL;
5122 me->pos = *pos;
5123 me->sheet = sheet;
5124 comment = sheet_get_comment (sheet, pos);
5125 if (comment) {
5126 g_object_get (G_OBJECT (comment),
5127 "text", &(me->old_text),
5128 "author", &(me->old_author),
5129 "markup", &(me->old_attributes), NULL);
5130 if (me->old_attributes != NULL)
5131 pango_attr_list_ref (me->old_attributes);
5132 me->old_text = g_strdup (me->old_text);
5133 me->old_author = g_strdup (me->old_author);
5136 /* Register the command object */
5137 return gnm_command_push_undo (wbc, G_OBJECT (me));
5140 /******************************************************************/
5142 #define CMD_ANALYSIS_TOOL_TYPE (cmd_analysis_tool_get_type ())
5143 #define CMD_ANALYSIS_TOOL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ANALYSIS_TOOL_TYPE, CmdAnalysis_Tool))
5145 typedef struct {
5146 GnmCommand cmd;
5148 data_analysis_output_t *dao;
5149 gpointer specs;
5150 gboolean specs_owned;
5151 analysis_tool_engine engine;
5152 data_analysis_output_type_t type;
5154 ColRowStateList *col_info;
5155 ColRowStateList *row_info;
5156 GnmRange old_range;
5157 GnmCellRegion *old_contents;
5158 GSList *newSheetObjects;
5159 } CmdAnalysis_Tool;
5161 MAKE_GNM_COMMAND (CmdAnalysis_Tool, cmd_analysis_tool, NULL)
5163 static gboolean
5164 cmd_analysis_tool_undo (GnmCommand *cmd, WorkbookControl *wbc)
5166 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5167 GnmPasteTarget pt;
5169 g_return_val_if_fail (me != NULL, TRUE);
5171 /* The old view might not exist anymore */
5172 me->dao->wbc = wbc;
5174 switch (me->type) {
5175 case NewSheetOutput:
5176 if (!command_undo_sheet_delete (me->dao->sheet))
5177 return TRUE;
5178 me->dao->sheet = NULL;
5179 break;
5180 case NewWorkbookOutput:
5181 g_warning ("How did we get here?");
5182 return TRUE;
5183 break;
5184 case RangeOutput:
5185 default:
5186 sheet_clear_region (me->dao->sheet,
5187 me->old_range.start.col, me->old_range.start.row,
5188 me->old_range.end.col, me->old_range.end.row,
5189 CLEAR_COMMENTS | CLEAR_FORMATS | CLEAR_NOCHECKARRAY |
5190 CLEAR_RECALC_DEPS | CLEAR_VALUES | CLEAR_MERGES,
5191 GO_CMD_CONTEXT (wbc));
5192 clipboard_paste_region (me->old_contents,
5193 paste_target_init (&pt, me->dao->sheet, &me->old_range, PASTE_ALL_TYPES),
5194 GO_CMD_CONTEXT (wbc));
5195 cellregion_unref (me->old_contents);
5196 me->old_contents = NULL;
5197 if (me->col_info) {
5198 dao_set_colrow_state_list (me->dao, TRUE, me->col_info);
5199 me->col_info = colrow_state_list_destroy (me->col_info);
5201 if (me->row_info) {
5202 dao_set_colrow_state_list (me->dao, FALSE, me->row_info);
5203 me->row_info = colrow_state_list_destroy (me->row_info);
5205 if (me->newSheetObjects == NULL)
5206 me->newSheetObjects = dao_surrender_so (me->dao);
5207 g_slist_foreach (me->newSheetObjects, (GFunc)sheet_object_clear_sheet, NULL);
5208 sheet_update (me->dao->sheet);
5211 return FALSE;
5214 static void
5215 cmd_analysis_tool_draw_old_so (SheetObject *so, data_analysis_output_t *dao)
5217 g_object_ref (so);
5218 dao_set_sheet_object (dao, 0, 1, so);
5221 static gboolean
5222 cmd_analysis_tool_redo (GnmCommand *cmd, WorkbookControl *wbc)
5224 gpointer continuity = NULL;
5225 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5226 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5228 g_return_val_if_fail (me != NULL, TRUE);
5230 /* The old view might not exist anymore */
5231 me->dao->wbc = wbc;
5233 if (me->col_info)
5234 me->col_info = colrow_state_list_destroy (me->col_info);
5235 me->col_info = dao_get_colrow_state_list (me->dao, TRUE);
5236 if (me->row_info)
5237 me->row_info = colrow_state_list_destroy (me->row_info);
5238 me->row_info = dao_get_colrow_state_list (me->dao, FALSE);
5240 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PREPARE_OUTPUT_RANGE, NULL)
5241 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5242 &me->cmd.cmd_descriptor)
5243 || cmd_dao_is_locked_effective (me->dao, wbc, me->cmd.cmd_descriptor)
5244 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_LAST_VALIDITY_CHECK, &continuity))
5245 return TRUE;
5247 switch (me->type) {
5248 case NewSheetOutput:
5249 me->old_contents = NULL;
5250 break;
5251 case NewWorkbookOutput:
5252 /* No undo in this case (see below) */
5253 me->old_contents = NULL;
5254 break;
5255 case RangeOutput:
5256 default:
5257 range_init (&me->old_range, me->dao->start_col, me->dao->start_row,
5258 me->dao->start_col + me->dao->cols - 1,
5259 me->dao->start_row + me->dao->rows - 1);
5260 me->old_contents = clipboard_copy_range (me->dao->sheet, &me->old_range);
5261 break;
5264 if (me->newSheetObjects != NULL)
5265 dao_set_omit_so (me->dao, TRUE);
5267 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_FORMAT_OUTPUT_RANGE, NULL))
5268 return TRUE;
5270 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PERFORM_CALC, &continuity)) {
5271 if (me->type == RangeOutput) {
5272 g_warning ("This is too late for failure! The target region has "
5273 "already been formatted!");
5274 } else
5275 return TRUE;
5277 if (me->newSheetObjects != NULL)
5279 GSList *l = g_slist_reverse
5280 (g_slist_copy (me->newSheetObjects));
5282 dao_set_omit_so (me->dao, FALSE);
5283 g_slist_foreach (l,
5284 (GFunc) cmd_analysis_tool_draw_old_so,
5285 me->dao);
5286 g_slist_free (l);
5289 if (continuity) {
5290 g_warning ("There shouldn't be any data left in here!");
5293 dao_autofit_columns (me->dao);
5294 sheet_mark_dirty (me->dao->sheet);
5295 sheet_update (me->dao->sheet);
5297 /* The concept of an undo if we create a new worksheet is extremely strange,
5298 * since we have separate undo/redo queues per worksheet.
5299 * Users can simply delete the worksheet if they so desire.
5302 return (me->type == NewWorkbookOutput);
5305 static void
5306 cmd_analysis_tool_finalize (GObject *cmd)
5308 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5310 if (me->col_info)
5311 me->col_info = colrow_state_list_destroy (me->col_info);
5312 if (me->row_info)
5313 me->row_info = colrow_state_list_destroy (me->row_info);
5315 me->engine (NULL, me->dao, me->specs, TOOL_ENGINE_CLEAN_UP, NULL);
5317 if (me->specs_owned) {
5318 g_free (me->specs);
5319 dao_free (me->dao);
5321 if (me->old_contents)
5322 cellregion_unref (me->old_contents);
5324 g_slist_free_full (me->newSheetObjects, g_object_unref);
5326 gnm_command_finalize (cmd);
5330 * cmd_analysis_tool: (skip)
5331 * Note: this takes ownership of specs and dao if and if only the command
5332 * succeeds.
5334 gboolean
5335 cmd_analysis_tool (WorkbookControl *wbc, G_GNUC_UNUSED Sheet *sheet,
5336 data_analysis_output_t *dao, gpointer specs,
5337 analysis_tool_engine engine, gboolean always_take_ownership)
5339 CmdAnalysis_Tool *me;
5340 gboolean trouble;
5341 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5343 g_return_val_if_fail (dao != NULL, TRUE);
5344 g_return_val_if_fail (specs != NULL, TRUE);
5345 g_return_val_if_fail (engine != NULL, TRUE);
5347 me = g_object_new (CMD_ANALYSIS_TOOL_TYPE, NULL);
5349 dao->wbc = wbc;
5351 /* Store the specs for the object */
5352 me->specs = specs;
5353 me->specs_owned = always_take_ownership;
5354 me->dao = dao;
5355 me->engine = engine;
5356 me->cmd.cmd_descriptor = NULL;
5357 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DAO, NULL)) {
5358 g_object_unref (me);
5359 return TRUE;
5361 me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5362 &me->cmd.cmd_descriptor);
5363 me->cmd.sheet = NULL;
5364 me->type = dao->type;
5365 me->row_info = NULL;
5366 me->col_info = NULL;
5368 /* We divide by 2 since many cells will be empty*/
5369 me->cmd.size = 1 + dao->rows * dao->cols / 2;
5371 /* Register the command object */
5372 trouble = gnm_command_push_undo (wbc, G_OBJECT (me));
5374 if (!trouble)
5375 me->specs_owned = TRUE;
5377 return trouble;
5380 /******************************************************************/
5382 #define CMD_MERGE_DATA_TYPE (cmd_merge_data_get_type ())
5383 #define CMD_MERGE_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_DATA_TYPE, CmdMergeData))
5385 typedef struct {
5386 GnmCommand cmd;
5387 GnmValue *merge_zone;
5388 GSList *merge_fields;
5389 GSList *merge_data;
5390 GSList *sheet_list;
5391 Sheet *sheet;
5392 gint n;
5393 } CmdMergeData;
5395 MAKE_GNM_COMMAND (CmdMergeData, cmd_merge_data, NULL)
5397 static void
5398 cmd_merge_data_delete_sheets (gpointer data, gpointer success)
5400 Sheet *sheet = data;
5402 if (!command_undo_sheet_delete (sheet))
5403 *(gboolean *)success = FALSE;
5406 static gboolean
5407 cmd_merge_data_undo (GnmCommand *cmd,
5408 G_GNUC_UNUSED WorkbookControl *wbc)
5410 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5411 gboolean success = TRUE;
5413 g_slist_foreach (me->sheet_list, cmd_merge_data_delete_sheets, &success);
5414 g_slist_free (me->sheet_list);
5415 me->sheet_list = NULL;
5417 return FALSE;
5420 static gboolean
5421 cmd_merge_data_redo (GnmCommand *cmd, WorkbookControl *wbc)
5423 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5424 int i;
5425 GnmCellRegion *merge_contents;
5426 GnmRangeRef *cell = &me->merge_zone->v_range.cell;
5427 GnmPasteTarget pt;
5428 GSList *this_field = me->merge_fields;
5429 GSList *this_data = me->merge_data;
5430 Sheet *source_sheet = cell->a.sheet;
5431 GSList *target_sheet;
5432 GnmRange target_range;
5433 ColRowStateList *state_col;
5434 ColRowStateList *state_row;
5436 range_init (&target_range, cell->a.col, cell->a.row,
5437 cell->b.col, cell->b.row);
5438 merge_contents = clipboard_copy_range (source_sheet, &target_range);
5439 state_col = colrow_get_states (source_sheet, TRUE, target_range.start.col,
5440 target_range.end.col);
5441 state_row = colrow_get_states (source_sheet, FALSE, target_range.start.row,
5442 target_range.end.row);
5444 for (i = 0; i < me->n; i++) {
5445 Sheet *new_sheet;
5447 new_sheet = workbook_sheet_add (me->sheet->workbook, -1,
5448 gnm_sheet_get_max_cols (me->sheet),
5449 gnm_sheet_get_max_rows (me->sheet));
5450 me->sheet_list = g_slist_prepend (me->sheet_list, new_sheet);
5452 colrow_set_states (new_sheet, TRUE, target_range.start.col, state_col);
5453 colrow_set_states (new_sheet, FALSE, target_range.start.row, state_row);
5454 sheet_objects_dup (source_sheet, new_sheet, &target_range);
5455 clipboard_paste_region (merge_contents,
5456 paste_target_init (&pt, new_sheet, &target_range, PASTE_ALL_TYPES),
5457 GO_CMD_CONTEXT (wbc));
5459 cellregion_unref (merge_contents);
5460 me->sheet_list = g_slist_reverse (me->sheet_list);
5461 colrow_state_list_destroy (state_col);
5462 colrow_state_list_destroy (state_row);
5464 while (this_field) {
5465 int col_source, row_source;
5466 int col_target, row_target;
5468 g_return_val_if_fail (this_data != NULL, TRUE);
5469 cell = &((GnmValue *)this_field->data)->v_range.cell;
5470 col_target = cell->a.col;
5471 row_target = cell->a.row;
5473 cell = &((GnmValue *)this_data->data)->v_range.cell;
5474 col_source = cell->a.col;
5475 row_source = cell->a.row;
5476 source_sheet = cell->a.sheet;
5478 target_sheet = me->sheet_list;
5479 while (target_sheet) {
5480 GnmCell *source_cell = sheet_cell_get (source_sheet,
5481 col_source, row_source);
5482 if (source_cell == NULL) {
5483 GnmCell *target_cell = sheet_cell_get ((Sheet *)target_sheet->data,
5484 col_target, row_target);
5485 if (target_cell != NULL)
5486 gnm_cell_set_value (target_cell,
5487 value_new_empty ());
5488 } else {
5489 GnmCell *target_cell = sheet_cell_fetch ((Sheet *)target_sheet->data,
5490 col_target, row_target);
5491 gnm_cell_set_value (target_cell,
5492 value_dup (source_cell->value));
5494 target_sheet = target_sheet->next;
5495 row_source++;
5498 this_field = this_field->next;
5499 this_data = this_data->next;
5502 return FALSE;
5505 static void
5506 cmd_merge_data_finalize (GObject *cmd)
5508 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5510 value_release (me->merge_zone);
5511 me->merge_zone = NULL;
5512 range_list_destroy (me->merge_data);
5513 me->merge_data = NULL;
5514 range_list_destroy (me->merge_fields);
5515 me->merge_fields = NULL;
5516 g_slist_free (me->sheet_list);
5517 me->sheet_list = NULL;
5518 me->n = 0;
5520 gnm_command_finalize (cmd);
5524 * cmd_merge_data:
5525 * @wbc: #WorkbookControl
5526 * @sheet: #Sheet
5527 * @merge_zone: (transfer full): #GnmValue
5528 * @merge_fields: (element-type GnmRange) (transfer full):
5529 * @merge_data: (element-type GnmRange) (transfer full):
5532 gboolean
5533 cmd_merge_data (WorkbookControl *wbc, Sheet *sheet,
5534 GnmValue *merge_zone, GSList *merge_fields, GSList *merge_data)
5536 CmdMergeData *me;
5537 GnmRangeRef *cell;
5539 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5540 g_return_val_if_fail (merge_zone != NULL, TRUE);
5541 g_return_val_if_fail (merge_fields != NULL, TRUE);
5542 g_return_val_if_fail (merge_data != NULL, TRUE);
5544 me = g_object_new (CMD_MERGE_DATA_TYPE, NULL);
5546 me->cmd.sheet = sheet;
5547 me->sheet = sheet;
5548 me->cmd.size = 1 + g_slist_length (merge_fields);
5549 me->cmd.cmd_descriptor =
5550 g_strdup_printf (_("Merging data into %s"), value_peek_string (merge_zone));
5552 me->merge_zone = merge_zone;
5553 me->merge_fields = merge_fields;
5554 me->merge_data = merge_data;
5555 me->sheet_list = NULL;
5557 cell = &((GnmValue *)merge_data->data)->v_range.cell;
5558 me->n = cell->b.row - cell->a.row + 1;
5560 /* Register the command object */
5561 return gnm_command_push_undo (wbc, G_OBJECT (me));
5564 /******************************************************************/
5566 #define CMD_CHANGE_META_DATA_TYPE (cmd_change_summary_get_type ())
5567 #define CMD_CHANGE_META_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_CHANGE_META_DATA_TYPE, CmdChangeMetaData))
5569 typedef struct {
5570 GnmCommand cmd;
5571 GSList *changed_props;
5572 GSList *removed_names;
5573 } CmdChangeMetaData;
5575 MAKE_GNM_COMMAND (CmdChangeMetaData, cmd_change_summary, NULL)
5577 static gboolean
5578 cmd_change_summary_undo (GnmCommand *cmd, WorkbookControl *wbc)
5580 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5581 GsfDocMetaData *meta = go_doc_get_meta_data (wb_control_get_doc (wbc));
5582 GSList *ptr, *old_vals = NULL, *dropped = NULL;
5583 GsfDocProp *prop;
5584 char const *name;
5586 for (ptr = me->removed_names; ptr != NULL ; ptr = ptr->next) {
5587 if (NULL != (prop = gsf_doc_meta_data_steal (meta, ptr->data)))
5588 old_vals = g_slist_prepend (old_vals, prop);
5589 g_free (ptr->data);
5591 g_slist_free (me->removed_names);
5593 for (ptr = me->changed_props; ptr != NULL ; ptr = ptr->next) {
5594 name = gsf_doc_prop_get_name (ptr->data);
5595 if (NULL != (prop = gsf_doc_meta_data_steal (meta, name)))
5596 old_vals = g_slist_prepend (old_vals, prop);
5597 else
5598 dropped = g_slist_prepend (old_vals, g_strdup (name));
5599 gsf_doc_meta_data_store (meta, ptr->data);
5601 g_slist_free (me->changed_props);
5603 me->removed_names = dropped;
5604 me->changed_props = old_vals;
5605 go_doc_update_meta_data (wb_control_get_doc (wbc));
5607 return FALSE;
5610 static gboolean
5611 cmd_change_summary_redo (GnmCommand *cmd, WorkbookControl *wbc)
5613 return cmd_change_summary_undo (cmd, wbc);
5616 static void
5617 cmd_change_summary_finalize (GObject *cmd)
5619 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5621 g_slist_free_full (me->changed_props, (GDestroyNotify)gsf_doc_prop_free);
5622 me->changed_props = NULL;
5623 g_slist_free_full (me->removed_names, g_free);
5624 me->removed_names = NULL;
5626 gnm_command_finalize (cmd);
5630 * cmd_change_meta_data:
5631 * @wbc: #WorkbookControl
5632 * @changes: (element-type GsfDocMetaData) (transfer full): the changed metadata.
5633 * @removed: (element-type GsfDocMetaData) (transfer full): the removed metadata.
5636 gboolean
5637 cmd_change_meta_data (WorkbookControl *wbc, GSList *changes, GSList *removed)
5639 CmdChangeMetaData *me = g_object_new (CMD_CHANGE_META_DATA_TYPE, NULL);
5641 me->changed_props = changes;
5642 me->removed_names = removed;
5643 me->cmd.sheet = NULL;
5645 me->cmd.size = g_slist_length (changes) + g_slist_length (removed);
5646 me->cmd.cmd_descriptor = g_strdup_printf (
5647 _("Changing workbook properties"));
5648 return gnm_command_push_undo (wbc, G_OBJECT (me));
5651 /******************************************************************/
5653 #define CMD_OBJECT_RAISE_TYPE (cmd_object_raise_get_type ())
5654 #define CMD_OBJECT_RAISE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECT_RAISE_TYPE, CmdObjectRaise))
5656 typedef struct {
5657 GnmCommand cmd;
5658 SheetObject *so;
5659 CmdObjectRaiseSelector dir;
5660 gint changed_positions;
5661 } CmdObjectRaise;
5663 MAKE_GNM_COMMAND (CmdObjectRaise, cmd_object_raise, NULL)
5665 static gboolean
5666 cmd_object_raise_redo (GnmCommand *cmd,
5667 G_GNUC_UNUSED WorkbookControl *wbc)
5669 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5670 switch (me->dir) {
5671 case cmd_object_pull_to_front:
5672 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MAXINT/2);
5673 break;
5674 case cmd_object_pull_forward:
5675 me->changed_positions = sheet_object_adjust_stacking (me->so, 1);
5676 break;
5677 case cmd_object_push_backward:
5678 me->changed_positions = sheet_object_adjust_stacking (me->so, -1);
5679 break;
5680 case cmd_object_push_to_back:
5681 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MININT/2);
5682 break;
5684 return FALSE;
5687 static gboolean
5688 cmd_object_raise_undo (GnmCommand *cmd,
5689 G_GNUC_UNUSED WorkbookControl *wbc)
5691 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5692 if (me->changed_positions != 0)
5693 sheet_object_adjust_stacking (me->so, - me->changed_positions);
5694 return FALSE;
5697 static void
5698 cmd_object_raise_finalize (GObject *cmd)
5700 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5701 g_object_unref (me->so);
5702 gnm_command_finalize (cmd);
5705 gboolean
5706 cmd_object_raise (WorkbookControl *wbc, SheetObject *so, CmdObjectRaiseSelector dir)
5708 CmdObjectRaise *me;
5710 g_return_val_if_fail (GNM_IS_SO (so), TRUE);
5712 me = g_object_new (CMD_OBJECT_RAISE_TYPE, NULL);
5714 me->so = so;
5715 g_object_ref (so);
5717 me->cmd.sheet = sheet_object_get_sheet (so);
5718 me->cmd.size = 1;
5719 switch (dir) {
5720 case cmd_object_pull_to_front:
5721 me->cmd.cmd_descriptor = g_strdup (_("Pull Object to the Front"));
5722 break;
5723 case cmd_object_pull_forward:
5724 me->cmd.cmd_descriptor = g_strdup (_("Pull Object Forward"));
5725 break;
5726 case cmd_object_push_backward:
5727 me->cmd.cmd_descriptor = g_strdup (_("Push Object Backward"));
5728 break;
5729 case cmd_object_push_to_back:
5730 me->cmd.cmd_descriptor = g_strdup (_("Push Object to the Back"));
5731 break;
5733 me->dir = dir;
5734 me->changed_positions = 0;
5736 return gnm_command_push_undo (wbc, G_OBJECT (me));
5739 /******************************************************************/
5741 #define CMD_PRINT_SETUP_TYPE (cmd_print_setup_get_type ())
5742 #define CMD_PRINT_SETUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PRINT_SETUP_TYPE, CmdPrintSetup))
5744 typedef struct {
5745 GnmCommand cmd;
5747 GSList *old_pi;
5748 GnmPrintInformation *new_pi;
5749 } CmdPrintSetup;
5751 MAKE_GNM_COMMAND (CmdPrintSetup, cmd_print_setup, NULL)
5753 static void
5754 update_sheet_graph_cb (Sheet *sheet)
5756 g_return_if_fail (IS_SHEET (sheet) && sheet->sheet_type == GNM_SHEET_OBJECT);
5758 sheet_object_graph_ensure_size (GNM_SO (sheet->sheet_objects->data));
5761 static gboolean
5762 cmd_print_setup_undo (GnmCommand *cmd, WorkbookControl *wbc)
5764 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5765 guint n, i;
5766 Workbook *book;
5767 GSList *infos;
5769 g_return_val_if_fail (me->old_pi != NULL, TRUE);
5771 if (me->cmd.sheet) {
5772 GnmPrintInformation *pi = me->old_pi->data;
5773 gnm_print_info_free (me->cmd.sheet->print_info);
5774 me->cmd.sheet->print_info = gnm_print_info_dup (pi);
5775 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5776 update_sheet_graph_cb (me->cmd.sheet);
5777 } else {
5778 book = wb_control_get_workbook(wbc);
5779 n = workbook_sheet_count (book);
5780 infos = me->old_pi;
5781 g_return_val_if_fail (g_slist_length (infos) == n, TRUE);
5783 for (i = 0 ; i < n ; i++) {
5784 GnmPrintInformation *pi = infos->data;
5785 Sheet *sheet = workbook_sheet_by_index (book, i);
5787 g_return_val_if_fail (infos != NULL, TRUE);
5789 gnm_print_info_free (sheet->print_info);
5790 sheet->print_info = gnm_print_info_dup (pi);
5791 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5792 update_sheet_graph_cb (sheet);
5793 infos = infos->next;
5796 return FALSE;
5799 static gboolean
5800 cmd_print_setup_redo (GnmCommand *cmd, WorkbookControl *wbc)
5802 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5803 int n, i;
5804 Workbook *book;
5805 gboolean save_pis = (me->old_pi == NULL);
5807 if (me->cmd.sheet) {
5808 if (save_pis)
5809 me->old_pi = g_slist_append (me->old_pi, me->cmd.sheet->print_info);
5810 else
5811 gnm_print_info_free (me->cmd.sheet->print_info);
5812 me->cmd.sheet->print_info = gnm_print_info_dup (me->new_pi);
5813 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5814 update_sheet_graph_cb (me->cmd.sheet);
5815 } else {
5816 book = wb_control_get_workbook(wbc);
5817 n = workbook_sheet_count (book);
5818 for (i = 0 ; i < n ; i++) {
5819 Sheet *sheet = workbook_sheet_by_index (book, i);
5820 sheet_mark_dirty (sheet);
5821 if (save_pis)
5822 me->old_pi = g_slist_prepend (me->old_pi, sheet->print_info);
5823 else
5824 gnm_print_info_free (sheet->print_info);
5825 sheet->print_info = gnm_print_info_dup (me->new_pi);
5826 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5827 update_sheet_graph_cb (sheet);
5829 if (save_pis)
5830 me->old_pi = g_slist_reverse (me->old_pi);
5832 return FALSE;
5835 static void
5836 cmd_print_setup_finalize (GObject *cmd)
5838 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5839 GSList *list = me->old_pi;
5841 if (me->new_pi)
5842 gnm_print_info_free (me->new_pi);
5843 for (; list; list = list->next)
5844 gnm_print_info_free ((GnmPrintInformation *) list->data);
5845 g_slist_free (me->old_pi);
5846 gnm_command_finalize (cmd);
5849 gboolean
5850 cmd_print_setup (WorkbookControl *wbc, Sheet *sheet, GnmPrintInformation const *pi)
5852 CmdPrintSetup *me;
5854 me = g_object_new (CMD_PRINT_SETUP_TYPE, NULL);
5856 me->cmd.sheet = sheet;
5857 me->cmd.size = 10;
5858 if (sheet)
5859 me->cmd.cmd_descriptor =
5860 g_strdup_printf (_("Page Setup For %s"), sheet->name_unquoted);
5861 else
5862 me->cmd.cmd_descriptor = g_strdup (_("Page Setup For All Sheets"));
5863 me->old_pi = NULL;
5864 me->new_pi = gnm_print_info_dup (pi);
5866 return gnm_command_push_undo (wbc, G_OBJECT (me));
5869 /******************************************************************/
5871 #define CMD_DEFINE_NAME_TYPE (cmd_define_name_get_type ())
5872 #define CMD_DEFINE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DEFINE_NAME_TYPE, CmdDefineName))
5874 typedef struct {
5875 GnmCommand cmd;
5877 GnmParsePos pp;
5878 char *name;
5879 GnmExprTop const *texpr;
5880 gboolean new_name;
5881 gboolean placeholder;
5882 } CmdDefineName;
5884 MAKE_GNM_COMMAND (CmdDefineName, cmd_define_name, NULL)
5886 static gboolean
5887 cmd_define_name_undo (GnmCommand *cmd,
5888 WorkbookControl *wbc)
5890 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5891 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5892 GnmExprTop const *texpr = nexpr->texpr;
5894 gnm_expr_top_ref (texpr);
5895 if (me->new_name)
5896 expr_name_remove (nexpr);
5897 else if (me->placeholder)
5898 expr_name_downgrade_to_placeholder (nexpr);
5899 else
5900 expr_name_set_expr (nexpr, me->texpr); /* restore old def */
5902 me->texpr = texpr;
5904 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5905 wb_view_menus_update (each_wbv);
5907 return FALSE;
5910 static gboolean
5911 cmd_define_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
5913 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5914 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5916 me->new_name = (nexpr == NULL);
5917 me->placeholder = (nexpr != NULL)
5918 && expr_name_is_placeholder (nexpr);
5920 if (me->new_name || me->placeholder) {
5921 char *err = NULL;
5922 nexpr = expr_name_add (&me->pp, me->name, me->texpr, &err, TRUE, NULL);
5923 if (nexpr == NULL) {
5924 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), err);
5925 g_free (err);
5926 return TRUE;
5928 me->texpr = NULL;
5929 } else { /* changing the definition */
5930 GnmExprTop const *tmp = nexpr->texpr;
5931 gnm_expr_top_ref (tmp);
5932 expr_name_set_expr (nexpr, me->texpr);
5933 me->texpr = tmp; /* store the old definition */
5935 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5936 wb_view_menus_update (each_wbv);
5939 return FALSE;
5942 static void
5943 cmd_define_name_finalize (GObject *cmd)
5945 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5947 g_free (me->name); me->name = NULL;
5949 if (me->texpr) {
5950 gnm_expr_top_unref (me->texpr);
5951 me->texpr = NULL;
5954 gnm_command_finalize (cmd);
5958 * cmd_define_name :
5959 * @wbc:
5960 * @name:
5961 * @pp:
5962 * @texpr: absorbs a ref to the texpr.
5963 * @descriptor: optional descriptor.
5965 * If the @name has never been defined in context @pp create a new name
5966 * If its a placeholder assign @texpr to it and make it real
5967 * If it already exists as a real name just assign @expr.
5969 * Returns TRUE on error
5971 gboolean
5972 cmd_define_name (WorkbookControl *wbc, char const *name,
5973 GnmParsePos const *pp, GnmExprTop const *texpr,
5974 char const *descriptor)
5976 CmdDefineName *me;
5977 GnmNamedExpr *nexpr;
5978 Sheet *sheet;
5980 g_return_val_if_fail (name != NULL, TRUE);
5981 g_return_val_if_fail (pp != NULL, TRUE);
5982 g_return_val_if_fail (texpr != NULL, TRUE);
5984 if (name[0] == '\0') {
5985 go_cmd_context_error_invalid
5986 (GO_CMD_CONTEXT (wbc), _("Defined Name"),
5987 _("An empty string is not allowed as defined name."));
5988 gnm_expr_top_unref (texpr);
5989 return TRUE;
5992 sheet = wb_control_cur_sheet (wbc);
5993 if (!expr_name_validate (name)) {
5994 gchar *err = g_strdup_printf
5995 (_("'%s' is not allowed as defined name."), name);
5996 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
5997 _("Defined Name"), err);
5998 g_free (err);
5999 gnm_expr_top_unref (texpr);
6000 return TRUE;
6003 if (expr_name_check_for_loop (name, texpr)) {
6004 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), name,
6005 _("has a circular reference"));
6006 gnm_expr_top_unref (texpr);
6007 return TRUE;
6009 nexpr = expr_name_lookup (pp, name);
6010 if (nexpr != NULL && !expr_name_is_placeholder (nexpr) &&
6011 gnm_expr_top_equal (texpr, nexpr->texpr)) {
6012 gnm_expr_top_unref (texpr);
6013 return FALSE; /* expr is not changing, do nothing */
6016 me = g_object_new (CMD_DEFINE_NAME_TYPE, NULL);
6017 me->name = g_strdup (name);
6018 me->pp = *pp;
6019 me->texpr = texpr;
6021 me->cmd.sheet = sheet;
6022 me->cmd.size = 1;
6024 if (descriptor == NULL) {
6025 char const *tmp;
6026 GString *res;
6028 /* Underscores need to be doubled. */
6029 res = g_string_new (NULL);
6030 for (tmp = name; *tmp; tmp++) {
6031 if (*tmp == '_')
6032 g_string_append_c (res, '_');
6033 g_string_append_c (res, *tmp);
6036 nexpr = expr_name_lookup (pp, name);
6037 if (nexpr == NULL || expr_name_is_placeholder (nexpr))
6038 me->cmd.cmd_descriptor =
6039 g_strdup_printf (_("Define Name %s"), res->str);
6040 else
6041 me->cmd.cmd_descriptor =
6042 g_strdup_printf (_("Update Name %s"), res->str);
6043 g_string_free (res, TRUE);
6044 } else
6045 me->cmd.cmd_descriptor = g_strdup (descriptor);
6047 return gnm_command_push_undo (wbc, G_OBJECT (me));
6050 /******************************************************************/
6052 #define CMD_REMOVE_NAME_TYPE (cmd_remove_name_get_type ())
6053 #define CMD_REMOVE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REMOVE_NAME_TYPE, CmdRemoveName))
6055 typedef struct {
6056 GnmCommand cmd;
6058 GnmParsePos pp;
6059 GnmNamedExpr *nexpr;
6060 const GnmExprTop *texpr;
6061 } CmdRemoveName;
6063 MAKE_GNM_COMMAND (CmdRemoveName, cmd_remove_name, NULL)
6065 static gboolean
6066 cmd_remove_name_undo (GnmCommand *cmd,
6067 G_GNUC_UNUSED WorkbookControl *wbc)
6069 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6070 GnmNamedExpr *nexpr =
6071 expr_name_add (&me->nexpr->pos, expr_name_name (me->nexpr),
6072 me->texpr, NULL, TRUE, NULL);
6073 if (nexpr) {
6074 me->texpr = NULL;
6075 expr_name_ref (nexpr);
6076 expr_name_unref (me->nexpr);
6077 me->nexpr = nexpr;
6078 return FALSE;
6079 } else {
6080 g_warning ("Redefining name failed.");
6081 return TRUE;
6085 static gboolean
6086 cmd_remove_name_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6088 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6090 me->texpr = me->nexpr->texpr;
6091 gnm_expr_top_ref (me->texpr);
6092 expr_name_downgrade_to_placeholder (me->nexpr);
6094 return FALSE;
6097 static void
6098 cmd_remove_name_finalize (GObject *cmd)
6100 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6102 expr_name_unref (me->nexpr);
6104 if (me->texpr) {
6105 gnm_expr_top_unref (me->texpr);
6106 me->texpr = NULL;
6109 gnm_command_finalize (cmd);
6113 * cmd_remove_name :
6114 * @wbc:
6115 * @nexpr: name to remove.
6117 * Returns TRUE on error
6119 gboolean
6120 cmd_remove_name (WorkbookControl *wbc, GnmNamedExpr *nexpr)
6122 CmdRemoveName *me;
6124 g_return_val_if_fail (wbc != NULL, TRUE);
6125 g_return_val_if_fail (nexpr != NULL, TRUE);
6126 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6128 expr_name_ref (nexpr);
6130 me = g_object_new (CMD_REMOVE_NAME_TYPE, NULL);
6131 me->nexpr = nexpr;
6132 me->texpr = NULL;
6133 me->cmd.sheet = wb_control_cur_sheet (wbc);
6134 me->cmd.size = 1;
6135 me->cmd.cmd_descriptor = g_strdup_printf (_("Remove Name %s"),
6136 expr_name_name (nexpr));
6138 return gnm_command_push_undo (wbc, G_OBJECT (me));
6141 /******************************************************************/
6143 #define CMD_RESCOPE_NAME_TYPE (cmd_rescope_name_get_type ())
6144 #define CMD_RESCOPE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESCOPE_NAME_TYPE, CmdRescopeName))
6146 typedef struct {
6147 GnmCommand cmd;
6148 GnmNamedExpr *nexpr;
6149 Sheet *scope;
6150 } CmdRescopeName;
6152 MAKE_GNM_COMMAND (CmdRescopeName, cmd_rescope_name, NULL)
6154 static gboolean
6155 cmd_rescope_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
6157 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6158 Sheet *old_scope = me->nexpr->pos.sheet;
6159 char *err;
6160 GnmParsePos pp = me->nexpr->pos;
6162 pp.sheet = me->scope;
6163 err = expr_name_set_pos (me->nexpr, &pp);
6165 if (err != NULL) {
6166 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Change Scope of Name"), err);
6167 g_free (err);
6168 return TRUE;
6171 me->scope = old_scope;
6172 return FALSE;
6175 static gboolean
6176 cmd_rescope_name_undo (GnmCommand *cmd, WorkbookControl *wbc)
6178 return cmd_rescope_name_redo (cmd, wbc);
6182 static void
6183 cmd_rescope_name_finalize (GObject *cmd)
6185 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6187 expr_name_unref (me->nexpr);
6188 gnm_command_finalize (cmd);
6192 * cmd_rescope_name :
6193 * @wbc:
6194 * @nexpr: name to rescope.
6196 * Returns TRUE on error
6198 gboolean
6199 cmd_rescope_name (WorkbookControl *wbc, GnmNamedExpr *nexpr, Sheet *scope)
6201 CmdRescopeName *me;
6203 g_return_val_if_fail (wbc != NULL, TRUE);
6204 g_return_val_if_fail (nexpr != NULL, TRUE);
6205 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6207 expr_name_ref (nexpr);
6209 me = g_object_new (CMD_RESCOPE_NAME_TYPE, NULL);
6210 me->nexpr = nexpr;
6211 me->scope = scope;
6212 me->cmd.sheet = wb_control_cur_sheet (wbc);
6213 me->cmd.size = 1;
6214 me->cmd.cmd_descriptor = g_strdup_printf (_("Change Scope of Name %s"),
6215 expr_name_name (nexpr));
6217 return gnm_command_push_undo (wbc, G_OBJECT (me));
6219 /******************************************************************/
6221 #define CMD_SCENARIO_ADD_TYPE (cmd_scenario_add_get_type ())
6222 #define CMD_SCENARIO_ADD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_ADD_TYPE, CmdScenarioAdd))
6224 typedef struct {
6225 GnmCommand cmd;
6226 GnmScenario *scenario;
6227 } CmdScenarioAdd;
6229 MAKE_GNM_COMMAND (CmdScenarioAdd, cmd_scenario_add, NULL)
6231 static gboolean
6232 cmd_scenario_add_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6234 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6235 GnmScenario *sc = g_object_ref (me->scenario);
6236 gnm_sheet_scenario_add (sc->sheet, sc);
6237 return FALSE;
6240 static gboolean
6241 cmd_scenario_add_undo (GnmCommand *cmd,
6242 G_GNUC_UNUSED WorkbookControl *wbc)
6244 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6245 GnmScenario *sc = me->scenario;
6246 gnm_sheet_scenario_remove (sc->sheet, sc);
6247 return FALSE;
6250 static void
6251 cmd_scenario_add_finalize (GObject *cmd)
6253 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6255 g_object_unref (me->scenario);
6256 gnm_command_finalize (cmd);
6259 gboolean
6260 cmd_scenario_add (WorkbookControl *wbc, GnmScenario *s, Sheet *sheet)
6262 CmdScenarioAdd *me;
6264 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6265 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6267 me = g_object_new (CMD_SCENARIO_ADD_TYPE, NULL);
6269 me->scenario = s; /* Take ownership */
6270 me->cmd.sheet = sheet;
6271 me->cmd.size = 1;
6272 me->cmd.cmd_descriptor = g_strdup (_("Add scenario"));
6274 return gnm_command_push_undo (wbc, G_OBJECT (me));
6277 /******************************************************************/
6279 #define CMD_SCENARIO_MNGR_TYPE (cmd_scenario_mngr_get_type ())
6280 #define CMD_SCENARIO_MNGR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_MNGR_TYPE, CmdScenarioMngr))
6282 typedef struct {
6283 GnmCommand cmd;
6284 GnmScenario *sc;
6285 GOUndo *undo;
6286 } CmdScenarioMngr;
6288 MAKE_GNM_COMMAND (CmdScenarioMngr, cmd_scenario_mngr, NULL)
6290 static gboolean
6291 cmd_scenario_mngr_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6293 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6294 if (!me->undo)
6295 me->undo = gnm_scenario_apply (me->sc);
6296 return FALSE;
6299 static gboolean
6300 cmd_scenario_mngr_undo (GnmCommand *cmd,
6301 G_GNUC_UNUSED WorkbookControl *wbc)
6303 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6304 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6305 g_object_unref (me->undo);
6306 me->undo = NULL;
6307 return FALSE;
6310 static void
6311 cmd_scenario_mngr_finalize (GObject *cmd)
6313 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6315 g_object_unref (me->sc);
6316 if (me->undo)
6317 g_object_unref (me->undo);
6319 gnm_command_finalize (cmd);
6322 gboolean
6323 cmd_scenario_mngr (WorkbookControl *wbc, GnmScenario *sc, GOUndo *undo)
6325 CmdScenarioMngr *me;
6327 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6328 g_return_val_if_fail (GNM_IS_SCENARIO (sc), TRUE);
6330 me = g_object_new (CMD_SCENARIO_MNGR_TYPE, NULL);
6332 me->sc = g_object_ref (sc);
6333 me->undo = g_object_ref (undo);
6334 me->cmd.sheet = sc->sheet;
6335 me->cmd.size = 1;
6336 me->cmd.cmd_descriptor = g_strdup (_("Scenario Show"));
6338 return gnm_command_push_undo (wbc, G_OBJECT (me));
6341 /******************************************************************/
6343 #define CMD_DATA_SHUFFLE_TYPE (cmd_data_shuffle_get_type ())
6344 #define CMD_DATA_SHUFFLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DATA_SHUFFLE_TYPE, CmdDataShuffle))
6346 typedef struct {
6347 GnmCommand cmd;
6348 data_shuffling_t *ds;
6349 } CmdDataShuffle;
6351 MAKE_GNM_COMMAND (CmdDataShuffle, cmd_data_shuffle, NULL)
6353 static gboolean
6354 cmd_data_shuffle_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6356 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6358 data_shuffling_redo (me->ds);
6359 return FALSE;
6362 static gboolean
6363 cmd_data_shuffle_undo (GnmCommand *cmd,
6364 G_GNUC_UNUSED WorkbookControl *wbc)
6366 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6368 data_shuffling_redo (me->ds);
6369 return FALSE;
6372 static void
6373 cmd_data_shuffle_finalize (GObject *cmd)
6375 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6377 data_shuffling_free (me->ds);
6378 gnm_command_finalize (cmd);
6381 gboolean
6382 cmd_data_shuffle (WorkbookControl *wbc, data_shuffling_t *sc, Sheet *sheet)
6384 CmdDataShuffle *me;
6386 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6387 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6389 me = g_object_new (CMD_DATA_SHUFFLE_TYPE, NULL);
6391 me->ds = sc;
6392 me->cmd.sheet = sheet;
6393 me->cmd.size = 1;
6394 me->cmd.cmd_descriptor = g_strdup (_("Shuffle Data"));
6396 return gnm_command_push_undo (wbc, G_OBJECT (me));
6399 /******************************************************************/
6401 #define CMD_TEXT_TO_COLUMNS_TYPE (cmd_text_to_columns_get_type ())
6402 #define CMD_TEXT_TO_COLUMNS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TEXT_TO_COLUMNS_TYPE, CmdTextToColumns))
6404 typedef struct {
6405 GnmCommand cmd;
6407 GnmCellRegion *contents;
6408 GnmPasteTarget dst;
6409 GnmRange src;
6410 Sheet *src_sheet;
6411 ColRowStateList *saved_sizes;
6412 } CmdTextToColumns;
6414 MAKE_GNM_COMMAND (CmdTextToColumns, cmd_text_to_columns, NULL)
6416 static gboolean
6417 cmd_text_to_columns_impl (GnmCommand *cmd, WorkbookControl *wbc,
6418 gboolean is_undo)
6420 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6421 GnmCellRegion *contents;
6423 g_return_val_if_fail (me != NULL, TRUE);
6424 g_return_val_if_fail (me->contents != NULL, TRUE);
6426 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
6427 if (clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc))) {
6428 /* There was a problem, avoid leaking */
6429 cellregion_unref (contents);
6430 return TRUE;
6433 cellregion_unref (me->contents);
6435 if (is_undo) {
6436 colrow_set_states (me->dst.sheet, FALSE,
6437 me->dst.range.start.row, me->saved_sizes);
6438 colrow_state_list_destroy (me->saved_sizes);
6439 me->saved_sizes = NULL;
6440 } else {
6441 me->saved_sizes = colrow_get_states (me->dst.sheet,
6442 FALSE, me->dst.range.start.row, me->dst.range.end.row);
6443 rows_height_update (me->dst.sheet, &me->dst.range, FALSE);
6446 me->contents = contents;
6448 /* Select the newly pasted contents (this queues a redraw) */
6449 select_range (me->dst.sheet, &me->dst.range, wbc);
6451 return FALSE;
6454 static gboolean
6455 cmd_text_to_columns_undo (GnmCommand *cmd, WorkbookControl *wbc)
6457 return cmd_text_to_columns_impl (cmd, wbc, TRUE);
6460 static gboolean
6461 cmd_text_to_columns_redo (GnmCommand *cmd, WorkbookControl *wbc)
6463 return cmd_text_to_columns_impl (cmd, wbc, FALSE);
6466 static void
6467 cmd_text_to_columns_finalize (GObject *cmd)
6469 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6471 if (me->saved_sizes)
6472 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
6473 if (me->contents) {
6474 cellregion_unref (me->contents);
6475 me->contents = NULL;
6477 gnm_command_finalize (cmd);
6480 gboolean
6481 cmd_text_to_columns (WorkbookControl *wbc,
6482 GnmRange const *src, Sheet *src_sheet,
6483 GnmRange const *target, Sheet *target_sheet,
6484 GnmCellRegion *contents)
6486 CmdTextToColumns *me;
6487 char *src_range_name, *target_range_name;
6489 g_return_val_if_fail (contents != NULL, TRUE);
6491 src_range_name = undo_range_name (src_sheet, src);
6492 target_range_name = undo_range_name (target_sheet, target);
6494 me = g_object_new (CMD_TEXT_TO_COLUMNS_TYPE, NULL);
6496 me->cmd.sheet = (src_sheet == target_sheet ? src_sheet : NULL);
6497 me->cmd.size = 1; /* FIXME? */
6498 me->cmd.cmd_descriptor = g_strdup_printf (_("Text (%s) to Columns (%s)"),
6499 src_range_name,
6500 target_range_name);
6501 me->dst.range = *target;
6502 me->dst.sheet = target_sheet;
6503 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
6504 me->src = *src;
6505 me->src_sheet = src_sheet;
6506 me->contents = contents;
6507 me->saved_sizes = NULL;
6509 g_free (src_range_name);
6510 g_free (target_range_name);
6512 /* Check array subdivision & merged regions */
6513 if (sheet_range_splits_region (target_sheet, &me->dst.range,
6514 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
6515 g_object_unref (me);
6516 return TRUE;
6519 return gnm_command_push_undo (wbc, G_OBJECT (me));
6522 /******************************************************************/
6524 #define CMD_GENERIC_TYPE (cmd_generic_get_type ())
6525 #define CMD_GENERIC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GENERIC_TYPE, CmdGeneric))
6527 typedef struct {
6528 GnmCommand cmd;
6529 GOUndo *undo, *redo;
6530 } CmdGeneric;
6532 MAKE_GNM_COMMAND (CmdGeneric, cmd_generic, NULL)
6534 static gboolean
6535 cmd_generic_undo (GnmCommand *cmd, WorkbookControl *wbc)
6537 CmdGeneric *me = CMD_GENERIC (cmd);
6538 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6539 return FALSE;
6542 static gboolean
6543 cmd_generic_redo (GnmCommand *cmd, WorkbookControl *wbc)
6545 CmdGeneric *me = CMD_GENERIC (cmd);
6546 go_undo_undo_with_data (me->redo, GO_CMD_CONTEXT (wbc));
6547 return FALSE;
6550 static void
6551 cmd_generic_finalize (GObject *cmd)
6553 CmdGeneric *me = CMD_GENERIC (cmd);
6555 g_object_unref (me->undo);
6556 g_object_unref (me->redo);
6558 gnm_command_finalize (cmd);
6561 gboolean
6562 cmd_generic_with_size (WorkbookControl *wbc, const char *txt,
6563 int size,
6564 GOUndo *undo, GOUndo *redo)
6566 CmdGeneric *me;
6568 g_return_val_if_fail (GO_IS_UNDO (undo), TRUE);
6569 g_return_val_if_fail (GO_IS_UNDO (redo), TRUE);
6571 me = g_object_new (CMD_GENERIC_TYPE, NULL);
6573 me->cmd.sheet = wb_control_cur_sheet (wbc);
6574 me->cmd.size = size;
6575 me->cmd.cmd_descriptor = g_strdup (txt);
6577 me->undo = undo;
6578 me->redo = redo;
6580 return gnm_command_push_undo (wbc, G_OBJECT (me));
6583 gboolean
6584 cmd_generic (WorkbookControl *wbc, const char *txt, GOUndo *undo, GOUndo *redo)
6586 return cmd_generic_with_size (wbc, txt, 1, undo, redo);
6589 /******************************************************************/
6591 #define CMD_GOAL_SEEK_TYPE (cmd_goal_seek_get_type ())
6592 #define CMD_GOAL_SEEK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GOAL_SEEK_TYPE, CmdGoalSeek))
6594 typedef struct {
6595 GnmCommand cmd;
6597 GnmCell *cell;
6598 GnmValue *ov;
6599 GnmValue *nv;
6600 } CmdGoalSeek;
6602 MAKE_GNM_COMMAND (CmdGoalSeek, cmd_goal_seek, NULL)
6604 static gboolean
6605 cmd_goal_seek_impl (GnmCell *cell, GnmValue *value)
6607 sheet_cell_set_value (cell, value_dup(value));
6608 return FALSE;
6612 static gboolean
6613 cmd_goal_seek_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6615 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6617 return cmd_goal_seek_impl (me->cell, me->ov);
6620 static gboolean
6621 cmd_goal_seek_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6623 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6625 return cmd_goal_seek_impl (me->cell, me->nv);
6628 static void
6629 cmd_goal_seek_finalize (GObject *cmd)
6631 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6633 value_release (me->ov);
6634 me->ov = NULL;
6635 value_release (me->nv);
6636 me->nv = NULL;
6638 gnm_command_finalize (cmd);
6641 gboolean
6642 cmd_goal_seek (WorkbookControl *wbc, GnmCell *cell, GnmValue *ov, GnmValue *nv)
6644 CmdGoalSeek *me;
6645 GnmRange range;
6647 g_return_val_if_fail (cell != NULL, TRUE);
6648 g_return_val_if_fail (ov != NULL || nv != NULL, TRUE);
6650 me = g_object_new (CMD_GOAL_SEEK_TYPE, NULL);
6652 me->cmd.sheet = cell->base.sheet;
6653 me->cmd.size = 1;
6654 range_init_cellpos (&range, &cell->pos);
6655 me->cmd.cmd_descriptor = g_strdup_printf
6656 (_("Goal Seek (%s)"), undo_range_name (cell->base.sheet, &range));
6658 me->cell = cell;
6659 me->ov = ov;
6660 me->nv = nv;
6662 if (me->ov == NULL)
6663 me->ov = value_dup (cell->value);
6664 if (me->nv == NULL)
6665 me->nv = value_dup (cell->value);
6667 return gnm_command_push_undo (wbc, G_OBJECT (me));
6670 /******************************************************************/
6672 #if 0
6673 #define CMD_FREEZE_PANES_TYPE (cmd_freeze_panes_get_type ())
6674 #define CMD_FREEZE_PANES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FREEZE_PANES_TYPE, CmdFreezePanes))
6676 typedef struct {
6677 GnmCommand cmd;
6679 SheetView *sv;
6680 GnmCellPos pos;
6681 } CmdFreezePanes;
6683 MAKE_GNM_COMMAND (CmdFreezePanes, cmd_freeze_panes, NULL)
6685 static gboolean
6686 cmd_freeze_panes_undo (GnmCommand *cmd, WorkbookControl *wbc)
6688 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6690 return FALSE;
6693 static gboolean
6694 cmd_freeze_panes_redo (GnmCommand *cmd, WorkbookControl *wbc)
6696 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6698 return FALSE;
6701 static void
6702 cmd_freeze_panes_finalize (GObject *cmd)
6704 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6706 gnm_command_finalize (cmd);
6710 * cmd_freeze_panes :
6711 * @wbc: where to report errors
6712 * @sv: the view to freeze
6713 * @frozen:
6714 * @unfrozen:
6716 * Returns TRUE on error
6718 gboolean
6719 cmd_freeze_panes (WorkbookControl *wbc, SheetView *sv,
6720 GnmCellPos const *frozen, GnmCellPos const *unfrozen)
6722 CmdFreezePanes *me;
6724 g_return_val_if_fail (name != NULL, TRUE);
6725 g_return_val_if_fail (pp != NULL, TRUE);
6726 g_return_val_if_fail (expr != NULL, TRUE);
6728 me = g_object_new (CMD_FREEZE_PANES_TYPE, NULL);
6729 me->sv = sv;
6730 me->frozen = f;
6731 me->unfrozen = expr;
6732 return gnm_command_push_undo (wbc, G_OBJECT (me));
6735 #endif
6738 /******************************************************************/
6741 #define CMD_TABULATE_TYPE (cmd_tabulate_get_type ())
6742 #define CMD_TABULATE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TABULATE_TYPE, CmdTabulate))
6744 typedef struct {
6745 GnmCommand cmd;
6746 GSList *sheet_idx;
6747 GnmTabulateInfo *data;
6748 } CmdTabulate;
6750 MAKE_GNM_COMMAND (CmdTabulate, cmd_tabulate, NULL)
6752 static gint
6753 cmd_tabulate_cmp_f (gconstpointer a,
6754 gconstpointer b)
6756 guint const a_val = GPOINTER_TO_INT (a);
6757 guint const b_val = GPOINTER_TO_INT (b);
6759 if (a_val > b_val)
6760 return -1;
6761 if (a_val < b_val)
6762 return 1;
6763 return 0;
6766 static gboolean
6767 cmd_tabulate_undo (GnmCommand *cmd, WorkbookControl *wbc)
6769 CmdTabulate *me = CMD_TABULATE (cmd);
6770 GSList *l;
6771 gboolean res = TRUE;
6773 me->sheet_idx = g_slist_sort (me->sheet_idx,
6774 cmd_tabulate_cmp_f);
6776 for (l = me->sheet_idx; l != NULL; l = l->next) {
6777 int i = GPOINTER_TO_INT (l->data);
6778 Sheet *new_sheet =
6779 workbook_sheet_by_index (wb_control_get_workbook (wbc),
6781 res = res && command_undo_sheet_delete (new_sheet);
6783 return !res;
6786 static gboolean
6787 cmd_tabulate_redo (GnmCommand *cmd, WorkbookControl *wbc)
6789 CmdTabulate *me = CMD_TABULATE (cmd);
6791 g_slist_free (me->sheet_idx);
6792 me->sheet_idx = do_tabulation (wbc, me->data);
6794 return (me->sheet_idx == NULL);
6797 static void
6798 cmd_tabulate_finalize (GObject *cmd)
6800 CmdTabulate *me = CMD_TABULATE (cmd);
6802 g_free (me->data->cells);
6803 g_free (me->data->minima);
6804 g_free (me->data->maxima);
6805 g_free (me->data->steps);
6806 g_free (me->data);
6807 gnm_command_finalize (cmd);
6810 gboolean
6811 cmd_tabulate (WorkbookControl *wbc, gpointer data)
6813 CmdTabulate *me;
6815 g_return_val_if_fail (data != NULL, TRUE);
6817 me = g_object_new (CMD_TABULATE_TYPE, NULL);
6819 me->cmd.sheet = NULL;
6820 me->cmd.size = 1;
6821 me->cmd.cmd_descriptor =
6822 g_strdup_printf (_("Tabulating Dependencies"));
6823 me->data = data;
6824 me->sheet_idx = NULL;
6826 return gnm_command_push_undo (wbc, G_OBJECT (me));
6829 /******************************************************************/
6831 #define CMD_SO_GRAPH_CONFIG_TYPE (cmd_so_graph_config_get_type ())
6832 #define CMD_SO_GRAPH_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_GRAPH_CONFIG_TYPE, CmdSOGraphConfig))
6834 typedef struct {
6835 GnmCommand cmd;
6836 SheetObject *so;
6837 GogGraph *new_graph;
6838 GogGraph *old_graph;
6839 } CmdSOGraphConfig;
6841 MAKE_GNM_COMMAND (CmdSOGraphConfig, cmd_so_graph_config, NULL)
6843 static gboolean
6844 cmd_so_graph_config_redo (GnmCommand *cmd,
6845 G_GNUC_UNUSED WorkbookControl *wbc)
6847 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6848 sheet_object_graph_set_gog (me->so, me->new_graph);
6849 return FALSE;
6852 static gboolean
6853 cmd_so_graph_config_undo (GnmCommand *cmd,
6854 G_GNUC_UNUSED WorkbookControl *wbc)
6856 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6857 sheet_object_graph_set_gog (me->so, me->old_graph);
6858 return FALSE;
6861 static void
6862 cmd_so_graph_config_finalize (GObject *cmd)
6864 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6866 g_object_unref (me->so);
6867 g_object_unref (me->new_graph);
6868 g_object_unref (me->old_graph);
6870 gnm_command_finalize (cmd);
6873 gboolean
6874 cmd_so_graph_config (WorkbookControl *wbc, SheetObject *so,
6875 GObject *n_graph, GObject *o_graph)
6877 CmdSOGraphConfig *me;
6879 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6880 g_return_val_if_fail (GNM_IS_SO_GRAPH (so), TRUE);
6881 g_return_val_if_fail (GOG_IS_GRAPH (n_graph), TRUE);
6882 g_return_val_if_fail (GOG_IS_GRAPH (o_graph), TRUE);
6884 me = g_object_new (CMD_SO_GRAPH_CONFIG_TYPE, NULL);
6886 me->so = so;
6887 g_object_ref (so);
6889 me->new_graph = GOG_GRAPH (n_graph);
6890 g_object_ref (me->new_graph);
6891 me->old_graph = GOG_GRAPH (o_graph);
6892 g_object_ref (me->old_graph);
6894 me->cmd.sheet = sheet_object_get_sheet (so);
6895 me->cmd.size = 10;
6896 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Graph"));
6898 return gnm_command_push_undo (wbc, G_OBJECT (me));
6901 /******************************************************************/
6903 #define CMD_SO_COMPONENT_CONFIG_TYPE (cmd_so_component_config_get_type ())
6904 #define CMD_SO_COMPONENT_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_COMPONENT_CONFIG_TYPE, CmdSOComponentConfig))
6906 typedef struct {
6907 GnmCommand cmd;
6908 SheetObject *so;
6909 GOComponent *new_obj;
6910 GOComponent *old_obj;
6911 } CmdSOComponentConfig;
6913 MAKE_GNM_COMMAND (CmdSOComponentConfig, cmd_so_component_config, NULL)
6915 static gboolean
6916 cmd_so_component_config_redo (GnmCommand *cmd,
6917 G_GNUC_UNUSED WorkbookControl *wbc)
6919 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6920 sheet_object_component_set_component (me->so, me->new_obj);
6921 return FALSE;
6924 static gboolean
6925 cmd_so_component_config_undo (GnmCommand *cmd,
6926 G_GNUC_UNUSED WorkbookControl *wbc)
6928 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6929 sheet_object_component_set_component (me->so, me->old_obj);
6930 return FALSE;
6933 static void
6934 cmd_so_component_config_finalize (GObject *cmd)
6936 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6938 g_object_unref (me->so);
6939 g_object_unref (me->new_obj);
6940 g_object_unref (me->old_obj);
6942 gnm_command_finalize (cmd);
6945 gboolean
6946 cmd_so_component_config (WorkbookControl *wbc, SheetObject *so,
6947 GObject *n_obj, GObject *o_obj)
6949 CmdSOComponentConfig *me;
6951 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6952 g_return_val_if_fail (GNM_IS_SO_COMPONENT (so), TRUE);
6953 g_return_val_if_fail (GO_IS_COMPONENT (n_obj), TRUE);
6954 g_return_val_if_fail (GO_IS_COMPONENT (o_obj), TRUE);
6956 me = g_object_new (CMD_SO_COMPONENT_CONFIG_TYPE, NULL);
6958 me->so = so;
6959 g_object_ref (so);
6961 me->new_obj = GO_COMPONENT (g_object_ref (n_obj));
6962 me->old_obj = GO_COMPONENT (g_object_ref (o_obj));
6964 me->cmd.sheet = sheet_object_get_sheet (so);
6965 me->cmd.size = 10;
6966 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Object"));
6968 return gnm_command_push_undo (wbc, G_OBJECT (me));
6971 /******************************************************************/
6973 #define CMD_TOGGLE_RTL_TYPE (cmd_toggle_rtl_get_type ())
6974 #define CMD_TOGGLE_RTL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TOGGLE_RTL_TYPE, CmdToggleRTL))
6976 typedef GnmCommand CmdToggleRTL;
6978 MAKE_GNM_COMMAND (CmdToggleRTL, cmd_toggle_rtl, NULL)
6980 static gboolean
6981 cmd_toggle_rtl_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6983 go_object_toggle (cmd->sheet, "text-is-rtl");
6984 return FALSE;
6987 static gboolean
6988 cmd_toggle_rtl_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6990 return cmd_toggle_rtl_redo (cmd, wbc);
6993 static void
6994 cmd_toggle_rtl_finalize (GObject *cmd)
6996 gnm_command_finalize (cmd);
6999 gboolean
7000 cmd_toggle_rtl (WorkbookControl *wbc, Sheet *sheet)
7002 CmdToggleRTL *me;
7004 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7005 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
7007 me = g_object_new (CMD_TOGGLE_RTL_TYPE, NULL);
7008 me->sheet = sheet;
7009 me->size = 1;
7010 me->cmd_descriptor = g_strdup (sheet->text_is_rtl ? _("Left to Right") : _("Right to Left"));
7012 return gnm_command_push_undo (wbc, G_OBJECT (me));
7015 /******************************************************************/
7017 #define CMD_SO_SET_VALUE_TYPE (cmd_so_set_value_get_type ())
7018 #define CMD_SO_SET_VALUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_VALUE_TYPE, CmdSOSetValue))
7020 typedef struct {
7021 GnmCommand cmd;
7022 GnmCellRef ref;
7023 GnmValue *val;
7024 GOUndo *undo;
7025 } CmdSOSetValue;
7027 MAKE_GNM_COMMAND (CmdSOSetValue, cmd_so_set_value, NULL)
7029 static gboolean
7030 cmd_so_set_value_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7032 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7033 Sheet *sheet = me->ref.sheet;
7034 GnmCell *cell = sheet_cell_fetch (sheet, me->ref.col, me->ref.row);
7036 sheet_cell_set_value (cell, value_dup (me->val));
7037 sheet_update (sheet);
7039 return FALSE;
7042 static gboolean
7043 cmd_so_set_value_undo (GnmCommand *cmd, WorkbookControl *wbc)
7045 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7047 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
7049 return FALSE;
7052 static void
7053 cmd_so_set_value_finalize (GObject *cmd)
7055 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7057 value_release (me->val);
7058 g_object_unref (me->undo);
7060 gnm_command_finalize (cmd);
7063 gboolean
7064 cmd_so_set_value (WorkbookControl *wbc,
7065 const char *text,
7066 const GnmCellRef *pref,
7067 GnmValue *new_val,
7068 Sheet *sheet)
7070 CmdSOSetValue *me;
7071 GnmRange r;
7073 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7075 r.start.col = r.end.col = pref->col;
7076 r.start.row = r.end.row = pref->row;
7078 me = g_object_new (CMD_SO_SET_VALUE_TYPE, NULL);
7079 me->cmd.sheet = sheet;
7080 me->cmd.size = 1;
7081 me->cmd.cmd_descriptor = g_strdup (text);
7082 me->ref = *pref;
7083 me->val = new_val;
7084 me->undo = clipboard_copy_range_undo (pref->sheet, &r);
7086 return gnm_command_push_undo (wbc, G_OBJECT (me));
7089 /******************************************************************/
7091 #define CMD_HYPERLINK_TYPE (cmd_hyperlink_get_type ())
7092 #define CMD_HYPERLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_HYPERLINK_TYPE, CmdHyperlink))
7094 typedef struct {
7095 GnmCommand cmd;
7096 GSList *selection;
7097 GnmStyle *new_style;
7098 char *opt_content;
7099 GOUndo *undo;
7100 gboolean update_size;
7101 } CmdHyperlink;
7103 static void
7104 cmd_hyperlink_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
7106 CmdHyperlink const *orig = (CmdHyperlink const *) cmd;
7108 if (orig->new_style)
7109 gnm_style_ref (orig->new_style);
7111 cmd_selection_hyperlink (wbc, orig->new_style, NULL,
7112 g_strdup (orig->opt_content));
7114 MAKE_GNM_COMMAND (CmdHyperlink, cmd_hyperlink, cmd_hyperlink_repeat)
7116 static gboolean
7117 cmd_hyperlink_undo (GnmCommand *cmd, WorkbookControl *wbc)
7119 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7120 Workbook *wb = wb_control_get_workbook (wbc);
7122 if (me->undo) {
7123 go_undo_undo (me->undo);
7124 g_clear_object (&me->undo);
7127 select_selection (me->cmd.sheet, me->selection, wbc);
7129 WORKBOOK_FOREACH_CONTROL (wb, view, ctl, {
7130 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS);
7133 return FALSE;
7136 static GnmValue *
7137 cb_hyperlink_set_text (GnmCellIter const *iter, gpointer user)
7139 CmdHyperlink *me = user;
7140 GnmCell *cell = iter->cell;
7142 if (cell == NULL)
7143 cell = sheet_cell_fetch (iter->pp.sheet,
7144 iter->pp.eval.col,
7145 iter->pp.eval.row);
7147 /* We skip non-empty cells. */
7148 if (gnm_cell_is_empty (cell) &&
7149 !gnm_cell_is_nonsingleton_array (cell)) {
7150 sheet_cell_set_value (cell, value_new_string (me->opt_content));
7151 if (me->update_size)
7152 me->cmd.size++;
7155 return NULL;
7158 static gboolean
7159 cmd_hyperlink_redo (GnmCommand *cmd, WorkbookControl *wbc)
7161 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7162 GSList *l;
7163 Workbook *wb = wb_control_get_workbook (wbc);
7164 Sheet *sheet;
7166 g_return_val_if_fail (me != NULL, TRUE);
7168 sheet = me->cmd.sheet;
7170 /* Check for locked cells */
7171 if (cmd_selection_is_locked_effective (sheet, me->selection,
7172 wbc, _("Changing Hyperlink")))
7173 return TRUE;
7175 me->undo = clipboard_copy_ranges_undo (sheet, me->selection);
7177 for (l = me->selection; l; l = l->next) {
7178 GnmRange const *r = l->data;
7180 if (me->new_style) {
7181 gnm_style_ref (me->new_style);
7182 sheet_apply_style (sheet, r, me->new_style);
7183 sheet_flag_style_update_range (sheet, r);
7186 if (me->opt_content) {
7187 sheet_foreach_cell_in_range (sheet, CELL_ITER_ALL,
7188 r->start.col, r->start.row,
7189 r->end.col, r->end.row,
7190 cb_hyperlink_set_text,
7191 me);
7194 me->update_size = FALSE;
7196 sheet_redraw_all (sheet, FALSE);
7197 sheet_mark_dirty (sheet);
7199 select_selection (sheet, me->selection, wbc);
7201 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
7202 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
7204 return FALSE;
7207 static void
7208 cmd_hyperlink_finalize (GObject *cmd)
7210 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7212 g_clear_object (&me->undo);
7214 if (me->new_style)
7215 gnm_style_unref (me->new_style);
7216 me->new_style = NULL;
7218 range_fragment_free (me->selection);
7219 me->selection = NULL;
7221 g_free (me->opt_content);
7223 gnm_command_finalize (cmd);
7227 * cmd_selection_hyperlink:
7228 * @wbc: the workbook control.
7229 * @style: style to apply to the selection
7230 * @opt_translated_name: An optional name to use in place of 'Hyperlink Cells'
7231 * @opt_content: optional content for otherwise empty cells.
7233 * If borders is non NULL, then the GnmBorder references are passed,
7234 * the GnmStyle reference is also passed.
7236 * It absorbs the reference to the style.
7238 * Return value: TRUE if there was a problem
7240 gboolean
7241 cmd_selection_hyperlink (WorkbookControl *wbc,
7242 GnmStyle *style,
7243 char const *opt_translated_name,
7244 char *opt_content)
7246 CmdHyperlink *me;
7247 SheetView *sv = wb_control_cur_sheet_view (wbc);
7249 me = g_object_new (CMD_HYPERLINK_TYPE, NULL);
7251 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
7252 me->new_style = style;
7254 me->cmd.sheet = sv_sheet (sv);
7255 me->cmd.size = 1; /* Updated later. */
7256 me->update_size = TRUE;
7258 me->opt_content = opt_content;
7260 if (opt_translated_name == NULL) {
7261 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
7263 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing hyperlink of %s"), names);
7264 g_free (names);
7265 } else
7266 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
7269 return gnm_command_push_undo (wbc, G_OBJECT (me));
7272 /******************************************************************/
7275 #define CMD_SO_SET_LINKS_TYPE (cmd_so_set_links_get_type ())
7276 #define CMD_SO_SET_LINKS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_LINKS_TYPE, CmdSOSetLink))
7278 typedef struct {
7279 GnmCommand cmd;
7280 SheetObject *so;
7281 GnmExprTop const *output;
7282 GnmExprTop const *content;
7283 gboolean as_index;
7284 } CmdSOSetLink;
7286 MAKE_GNM_COMMAND (CmdSOSetLink, cmd_so_set_links, NULL)
7288 static gboolean
7289 cmd_so_set_links_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7291 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7292 GnmExprTop const *old_output;
7293 GnmExprTop const *old_content;
7294 gboolean old_as_index;
7296 old_content = sheet_widget_list_base_get_content_link (me->so);
7297 old_output = sheet_widget_list_base_get_result_link (me->so);
7298 old_as_index = sheet_widget_list_base_result_type_is_index (me->so);
7300 sheet_widget_list_base_set_links
7301 (me->so, me->output, me->content);
7302 if (old_as_index != me->as_index) {
7303 sheet_widget_list_base_set_result_type (me->so, me->as_index);
7304 me->as_index = old_as_index;
7306 if (me->output)
7307 gnm_expr_top_unref (me->output);
7308 if (me->content)
7309 gnm_expr_top_unref (me->content);
7310 me->output = old_output;
7311 me->content = old_content;
7313 return FALSE;
7316 static gboolean
7317 cmd_so_set_links_undo (GnmCommand *cmd, WorkbookControl *wbc)
7319 return cmd_so_set_links_redo (cmd, wbc);
7322 static void
7323 cmd_so_set_links_finalize (GObject *cmd)
7325 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7327 if (me->output)
7328 gnm_expr_top_unref (me->output);
7329 if (me->content)
7330 gnm_expr_top_unref (me->content);
7331 gnm_command_finalize (cmd);
7334 gboolean
7335 cmd_so_set_links (WorkbookControl *wbc,
7336 SheetObject *so,
7337 GnmExprTop const *output,
7338 GnmExprTop const *content,
7339 gboolean as_index)
7341 CmdSOSetLink *me;
7343 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7345 me = g_object_new (CMD_SO_SET_LINKS_TYPE, NULL);
7346 me->cmd.sheet = sheet_object_get_sheet (so);
7347 me->cmd.size = 1;
7348 me->cmd.cmd_descriptor = g_strdup (_("Configure List"));
7349 me->so = so;
7350 me->output = output;
7351 me->content = content;
7352 me->as_index = as_index;
7354 return gnm_command_push_undo (wbc, G_OBJECT (me));
7357 /******************************************************************/
7361 #define CMD_SO_SET_FRAME_LABEL_TYPE (cmd_so_set_frame_label_get_type ())
7362 #define CMD_SO_SET_FRAME_LABEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_FRAME_LABEL_TYPE, CmdSOSetFrameLabel))
7364 typedef struct {
7365 GnmCommand cmd;
7366 SheetObject *so;
7367 char *old_label;
7368 char *new_label;
7369 } CmdSOSetFrameLabel;
7371 MAKE_GNM_COMMAND (CmdSOSetFrameLabel, cmd_so_set_frame_label, NULL)
7373 static gboolean
7374 cmd_so_set_frame_label_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7376 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7378 sheet_widget_frame_set_label (me->so, me->new_label);
7380 return FALSE;
7383 static gboolean
7384 cmd_so_set_frame_label_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7386 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7388 sheet_widget_frame_set_label (me->so, me->old_label);
7390 return FALSE;
7393 static void
7394 cmd_so_set_frame_label_finalize (GObject *cmd)
7396 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7398 g_free (me->old_label);
7399 me->old_label = NULL;
7401 g_free (me->new_label);
7402 me->new_label = NULL;
7404 gnm_command_finalize (cmd);
7407 gboolean
7408 cmd_so_set_frame_label (WorkbookControl *wbc,
7409 SheetObject *so,
7410 char *old_label, char *new_label )
7412 CmdSOSetFrameLabel *me;
7414 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7416 me = g_object_new (CMD_SO_SET_FRAME_LABEL_TYPE, NULL);
7417 me->cmd.sheet = sheet_object_get_sheet (so);
7418 me->cmd.size = 1;
7419 me->cmd.cmd_descriptor = g_strdup (_("Set Frame Label"));
7420 me->so = so;
7421 me->old_label = old_label;
7422 me->new_label = new_label;
7424 return gnm_command_push_undo (wbc, G_OBJECT (me));
7427 /******************************************************************/
7428 #define CMD_SO_SET_BUTTON_TYPE (cmd_so_set_button_get_type ())
7429 #define CMD_SO_SET_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_BUTTON_TYPE, CmdSOSetButton))
7431 typedef struct {
7432 GnmCommand cmd;
7433 SheetObject *so;
7434 GnmExprTop const *new_link;
7435 GnmExprTop const *old_link;
7436 char *old_label;
7437 char *new_label;
7438 } CmdSOSetButton;
7440 MAKE_GNM_COMMAND (CmdSOSetButton, cmd_so_set_button, NULL)
7442 static gboolean
7443 cmd_so_set_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7445 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7447 sheet_widget_button_set_link (me->so, me->new_link);
7448 sheet_widget_button_set_label (me->so, me->new_label);
7450 return FALSE;
7453 static gboolean
7454 cmd_so_set_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7456 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7458 sheet_widget_button_set_link (me->so, me->old_link);
7459 sheet_widget_button_set_label (me->so, me->old_label);
7461 return FALSE;
7464 static void
7465 cmd_so_set_button_finalize (GObject *cmd)
7467 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7469 if (me->new_link)
7470 gnm_expr_top_unref (me->new_link);
7471 if (me->old_link)
7472 gnm_expr_top_unref (me->old_link);
7473 g_free (me->old_label);
7474 g_free (me->new_label);
7475 gnm_command_finalize (cmd);
7478 gboolean
7479 cmd_so_set_button (WorkbookControl *wbc,
7480 SheetObject *so, GnmExprTop const *lnk,
7481 char *old_label, char *new_label)
7483 CmdSOSetButton *me;
7485 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7487 me = g_object_new (CMD_SO_SET_BUTTON_TYPE, NULL);
7488 me->cmd.sheet = sheet_object_get_sheet (so);
7489 me->cmd.size = 1;
7490 me->cmd.cmd_descriptor = g_strdup (_("Configure Button"));
7491 me->so = so;
7492 me->new_link = lnk;
7493 me->old_label = old_label;
7494 me->new_label = new_label;
7496 me->old_link = sheet_widget_button_get_link (so);
7498 return gnm_command_push_undo (wbc, G_OBJECT (me));
7501 /******************************************************************/
7502 #define CMD_SO_SET_RADIO_BUTTON_TYPE (cmd_so_set_radio_button_get_type ())
7503 #define CMD_SO_SET_RADIO_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_RADIO_BUTTON_TYPE, CmdSOSetRadioButton))
7505 typedef struct {
7506 GnmCommand cmd;
7507 SheetObject *so;
7508 GnmExprTop const *new_link;
7509 GnmExprTop const *old_link;
7510 char *old_label;
7511 char *new_label;
7512 GnmValue *old_value;
7513 GnmValue *new_value;
7514 } CmdSOSetRadioButton;
7516 MAKE_GNM_COMMAND (CmdSOSetRadioButton, cmd_so_set_radio_button, NULL)
7518 static gboolean
7519 cmd_so_set_radio_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7521 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7523 sheet_widget_radio_button_set_link (me->so, me->new_link);
7524 sheet_widget_radio_button_set_label (me->so, me->new_label);
7525 sheet_widget_radio_button_set_value (me->so, me->new_value);
7527 return FALSE;
7530 static gboolean
7531 cmd_so_set_radio_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7533 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7535 sheet_widget_radio_button_set_link (me->so, me->old_link);
7536 sheet_widget_radio_button_set_label (me->so, me->old_label);
7537 sheet_widget_radio_button_set_value (me->so, me->old_value);
7539 return FALSE;
7542 static void
7543 cmd_so_set_radio_button_finalize (GObject *cmd)
7545 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7547 if (me->new_link)
7548 gnm_expr_top_unref (me->new_link);
7549 if (me->old_link)
7550 gnm_expr_top_unref (me->old_link);
7551 g_free (me->old_label);
7552 g_free (me->new_label);
7553 value_release (me->old_value);
7554 value_release (me->new_value);
7555 gnm_command_finalize (cmd);
7558 gboolean
7559 cmd_so_set_radio_button (WorkbookControl *wbc,
7560 SheetObject *so, GnmExprTop const *lnk,
7561 char *old_label, char *new_label,
7562 GnmValue *old_value, GnmValue *new_value)
7564 CmdSOSetRadioButton *me;
7566 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7568 me = g_object_new (CMD_SO_SET_RADIO_BUTTON_TYPE, NULL);
7569 me->cmd.sheet = sheet_object_get_sheet (so);
7570 me->cmd.size = 1;
7571 me->cmd.cmd_descriptor = g_strdup (_("Configure Radio Button"));
7572 me->so = so;
7573 me->new_link = lnk;
7574 me->old_label = old_label;
7575 me->new_label = new_label;
7576 me->old_value = old_value;
7577 me->new_value = new_value;
7579 me->old_link = sheet_widget_radio_button_get_link (so);
7581 return gnm_command_push_undo (wbc, G_OBJECT (me));
7584 /******************************************************************/
7585 #define CMD_SO_SET_CHECKBOX_TYPE (cmd_so_set_checkbox_get_type ())
7586 #define CMD_SO_SET_CHECKBOX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_CHECKBOX_TYPE, CmdSOSetCheckbox))
7588 typedef struct {
7589 GnmCommand cmd;
7590 SheetObject *so;
7591 GnmExprTop const *new_link;
7592 GnmExprTop const *old_link;
7593 char *old_label;
7594 char *new_label;
7595 } CmdSOSetCheckbox;
7597 MAKE_GNM_COMMAND (CmdSOSetCheckbox, cmd_so_set_checkbox, NULL)
7599 static gboolean
7600 cmd_so_set_checkbox_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7602 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7604 sheet_widget_checkbox_set_link (me->so, me->new_link);
7605 sheet_widget_checkbox_set_label (me->so, me->new_label);
7607 return FALSE;
7610 static gboolean
7611 cmd_so_set_checkbox_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7613 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7615 sheet_widget_checkbox_set_link (me->so, me->old_link);
7616 sheet_widget_checkbox_set_label (me->so, me->old_label);
7618 return FALSE;
7621 static void
7622 cmd_so_set_checkbox_finalize (GObject *cmd)
7624 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7626 if (me->new_link)
7627 gnm_expr_top_unref (me->new_link);
7628 if (me->old_link)
7629 gnm_expr_top_unref (me->old_link);
7630 g_free (me->old_label);
7631 g_free (me->new_label);
7632 gnm_command_finalize (cmd);
7635 gboolean
7636 cmd_so_set_checkbox (WorkbookControl *wbc,
7637 SheetObject *so, GnmExprTop const *lnk,
7638 char *old_label, char *new_label)
7640 CmdSOSetCheckbox *me;
7642 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7644 me = g_object_new (CMD_SO_SET_CHECKBOX_TYPE, NULL);
7645 me->cmd.sheet = sheet_object_get_sheet (so);
7646 me->cmd.size = 1;
7647 me->cmd.cmd_descriptor = g_strdup (_("Configure Checkbox"));
7648 me->so = so;
7649 me->new_link = lnk;
7650 me->old_label = old_label;
7651 me->new_label = new_label;
7653 me->old_link = sheet_widget_checkbox_get_link (so);
7655 return gnm_command_push_undo (wbc, G_OBJECT (me));
7658 /******************************************************************/
7660 #define CMD_SO_SET_ADJUSTMENT_TYPE (cmd_so_set_adjustment_get_type ())
7661 #define CMD_SO_SET_ADJUSTMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_ADJUSTMENT_TYPE, CmdSOSetAdjustment))
7663 typedef struct {
7664 GnmCommand cmd;
7665 SheetObject *so;
7666 GnmExprTop const *new_link;
7667 GnmExprTop const *old_link;
7668 double old_lower;
7669 double old_upper;
7670 double old_step;
7671 double old_page;
7672 gboolean old_horizontal;
7673 } CmdSOSetAdjustment;
7675 MAKE_GNM_COMMAND (CmdSOSetAdjustment, cmd_so_set_adjustment, NULL)
7677 static void
7678 cmd_so_set_adjustment_adj (CmdSOSetAdjustment *me)
7680 GtkAdjustment *adj = sheet_widget_adjustment_get_adjustment (me->so);
7682 double old_lower = gtk_adjustment_get_lower (adj);
7683 double old_upper = gtk_adjustment_get_upper (adj);
7684 double old_step = gtk_adjustment_get_step_increment (adj);
7685 double old_page = gtk_adjustment_get_page_increment (adj);
7686 gboolean old_horizontal;
7687 g_object_get (G_OBJECT (me->so), "horizontal", &old_horizontal, NULL);
7689 gtk_adjustment_configure (adj,
7690 gtk_adjustment_get_value (adj),
7691 me->old_lower,
7692 me->old_upper,
7693 me->old_step,
7694 me->old_page,
7695 gtk_adjustment_get_page_size (adj));
7696 g_object_set (G_OBJECT (me->so), "horizontal", me->old_horizontal, NULL);
7698 me->old_lower = old_lower;
7699 me->old_upper = old_upper;
7700 me->old_step = old_step;
7701 me->old_page = old_page;
7702 me->old_horizontal = old_horizontal;
7705 static gboolean
7706 cmd_so_set_adjustment_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7708 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7710 sheet_widget_adjustment_set_link (me->so, me->new_link);
7711 cmd_so_set_adjustment_adj (me);
7712 return FALSE;
7715 static gboolean
7716 cmd_so_set_adjustment_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7718 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7720 sheet_widget_adjustment_set_link (me->so, me->old_link);
7721 cmd_so_set_adjustment_adj (me);
7723 return FALSE;
7726 static void
7727 cmd_so_set_adjustment_finalize (GObject *cmd)
7729 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7731 if (me->new_link)
7732 gnm_expr_top_unref (me->new_link);
7733 if (me->old_link)
7734 gnm_expr_top_unref (me->old_link);
7735 gnm_command_finalize (cmd);
7738 gboolean
7739 cmd_so_set_adjustment (WorkbookControl *wbc,
7740 SheetObject *so, GnmExprTop const *lnk,
7741 gboolean horizontal,
7742 int lower, int upper,
7743 int step, int page,
7744 char const *undo_label)
7746 CmdSOSetAdjustment *me;
7748 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7750 me = g_object_new (CMD_SO_SET_ADJUSTMENT_TYPE, NULL);
7751 me->cmd.sheet = sheet_object_get_sheet (so);
7752 me->cmd.size = 1;
7753 me->cmd.cmd_descriptor = g_strdup ((undo_label == NULL) ?
7754 _("Configure Adjustment") : _(undo_label));
7755 me->so = so;
7756 me->new_link = lnk;
7757 me->old_lower = lower;
7758 me->old_upper = upper;
7759 me->old_step = step;
7760 me->old_page = page;
7761 me->old_horizontal = horizontal;
7763 me->old_link = sheet_widget_adjustment_get_link (so);
7765 return gnm_command_push_undo (wbc, G_OBJECT (me));
7768 /******************************************************************/
7770 gboolean
7771 cmd_autofilter_add_remove (WorkbookControl *wbc)
7773 SheetView *sv = wb_control_cur_sheet_view (wbc);
7774 GnmFilter *f = sv_editpos_in_filter (sv);
7775 gboolean add = (f == NULL);
7776 char *descr = NULL, *name = NULL;
7777 GOUndo *undo = NULL;
7778 GOUndo *redo = NULL;
7779 gboolean result;
7782 if (add) {
7783 GnmRange region;
7784 GnmRange const *src = selection_first_range (sv,
7785 GO_CMD_CONTEXT (wbc), _("Add Filter"));
7786 GnmFilter *f_old = NULL;
7788 if (src == NULL)
7789 return TRUE;
7791 f_old = gnm_sheet_filter_intersect_rows
7792 (sv->sheet, src->start.row, src->end.row);
7794 if (f_old != NULL) {
7795 GnmRange *r = gnm_sheet_filter_can_be_extended
7796 (sv->sheet, f_old, src);
7797 if (r == NULL) {
7798 char *error;
7799 name = undo_range_name (sv->sheet, &(f_old->r));
7800 error = g_strdup_printf
7801 (_("Auto Filter blocked by %s"),
7802 name);
7803 g_free(name);
7804 go_cmd_context_error_invalid
7805 (GO_CMD_CONTEXT (wbc),
7806 _("AutoFilter"), error);
7807 g_free (error);
7808 return TRUE;
7810 /* extending existing filter. */
7811 undo = go_undo_binary_new
7812 (gnm_filter_ref (f_old), sv->sheet,
7813 (GOUndoBinaryFunc) gnm_filter_attach,
7814 (GFreeFunc) gnm_filter_unref,
7815 NULL);
7816 redo = go_undo_unary_new
7817 (gnm_filter_ref (f_old),
7818 (GOUndoUnaryFunc) gnm_filter_remove,
7819 (GFreeFunc) gnm_filter_unref);
7820 gnm_filter_remove (f_old);
7821 region = *r;
7822 g_free (r);
7823 } else {
7824 /* if only one row is selected
7825 * assume that the user wants to
7826 * filter the region below this row. */
7827 region = *src;
7828 if (src->start.row == src->end.row)
7829 gnm_sheet_guess_region (sv->sheet, &region);
7830 if (region.start.row == region.end.row) {
7831 go_cmd_context_error_invalid
7832 (GO_CMD_CONTEXT (wbc),
7833 _("AutoFilter"),
7834 _("Requires more than 1 row"));
7835 return TRUE;
7838 f = gnm_filter_new (sv->sheet, &region);
7839 if (f == NULL) {
7840 go_cmd_context_error_invalid
7841 (GO_CMD_CONTEXT (wbc),
7842 _("AutoFilter"),
7843 _("Unable to create Autofilter"));
7844 if (f_old)
7845 gnm_filter_attach (f_old, sv->sheet);
7846 return TRUE;
7849 gnm_filter_remove (f);
7850 if (f_old)
7851 gnm_filter_attach (f_old, sv->sheet);
7853 redo = go_undo_combine (go_undo_binary_new
7854 (gnm_filter_ref (f), sv->sheet,
7855 (GOUndoBinaryFunc) gnm_filter_attach,
7856 (GFreeFunc) gnm_filter_unref,
7857 NULL), redo);
7858 undo = go_undo_combine (undo,
7859 go_undo_unary_new
7861 (GOUndoUnaryFunc) gnm_filter_remove,
7862 (GFreeFunc) gnm_filter_unref));
7864 name = undo_range_name (sv->sheet, &(f->r));
7865 descr = g_strdup_printf
7866 ((f_old == NULL) ? _("Add Autofilter to %s")
7867 : _("Extend Autofilter to %s"),
7868 name);
7869 } else {
7870 undo = go_undo_binary_new
7871 (gnm_filter_ref (f), sv->sheet,
7872 (GOUndoBinaryFunc) gnm_filter_attach,
7873 (GFreeFunc) gnm_filter_unref,
7874 NULL);
7875 redo = go_undo_unary_new
7876 (gnm_filter_ref (f),
7877 (GOUndoUnaryFunc) gnm_filter_remove,
7878 (GFreeFunc) gnm_filter_unref);
7879 name = undo_range_name (sv->sheet, &(f->r));
7880 descr = g_strdup_printf (_("Remove Autofilter from %s"),
7881 name);
7883 result = cmd_generic (wbc, descr, undo, redo);
7884 g_free (name);
7885 g_free (descr);
7887 return result;
7891 /******************************************************************/
7893 gboolean cmd_autofilter_set_condition (WorkbookControl *wbc,
7894 GnmFilter *filter, unsigned i,
7895 GnmFilterCondition *cond)
7897 char *descr = NULL, *name = NULL;
7898 GOUndo *undo = NULL;
7899 GOUndo *redo = NULL;
7900 gboolean result;
7902 undo = gnm_undo_filter_set_condition_new (filter, i,
7903 NULL, TRUE);
7904 g_return_val_if_fail (undo != NULL, TRUE);
7905 redo = gnm_undo_filter_set_condition_new (filter, i,
7906 cond, FALSE);
7907 g_return_val_if_fail (redo != NULL, TRUE);
7909 name = undo_range_name (filter->sheet, &(filter->r));
7910 descr = g_strdup_printf (_("Change filter condition for %s"),
7911 name);
7913 result = cmd_generic (wbc, descr, undo, redo);
7914 g_free (name);
7915 g_free (descr);
7917 return result;
7921 /******************************************************************/
7923 static void
7924 cmd_page_breaks_set_breaks (Sheet *sheet,
7925 GnmPageBreaks const *breaks)
7927 print_info_set_breaks (sheet->print_info, gnm_page_breaks_dup (breaks));
7929 SHEET_FOREACH_CONTROL (sheet, sv, sc, wb_control_menu_state_update (sc_wbc (sc), MS_PAGE_BREAKS););
7932 gboolean
7933 cmd_page_breaks_clear (WorkbookControl *wbc, Sheet *sheet)
7935 GOUndo *undo = NULL;
7936 GOUndo *redo = NULL;
7938 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7939 g_return_val_if_fail (sheet != NULL, TRUE);
7941 if (sheet->print_info->page_breaks.v != NULL) {
7942 redo = go_undo_binary_new
7943 (sheet,
7944 gnm_page_breaks_new (TRUE),
7945 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7946 NULL,
7947 (GFreeFunc) gnm_page_breaks_free);
7948 undo = go_undo_binary_new
7949 (sheet,
7950 gnm_page_breaks_dup
7951 (sheet->print_info->page_breaks.v),
7952 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7953 NULL,
7954 (GFreeFunc) gnm_page_breaks_free);
7957 if (sheet->print_info->page_breaks.h != NULL) {
7958 redo = go_undo_combine
7959 (redo,
7960 go_undo_binary_new
7961 (sheet,
7962 gnm_page_breaks_new (FALSE),
7963 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7964 NULL,
7965 (GFreeFunc) gnm_page_breaks_free));
7967 undo = go_undo_combine
7968 (undo,
7969 go_undo_binary_new
7970 (sheet,
7971 gnm_page_breaks_dup
7972 (sheet->print_info->page_breaks.h),
7973 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7974 NULL,
7975 (GFreeFunc) gnm_page_breaks_free));
7978 if (undo != NULL)
7979 return cmd_generic (wbc, _("Clear All Page Breaks"), undo, redo);
7980 else
7981 return TRUE;
7984 gboolean
7985 cmd_page_break_toggle (WorkbookControl *wbc, Sheet *sheet, gboolean is_vert)
7987 SheetView const *sv = wb_control_cur_sheet_view (wbc);
7988 gint col = sv->edit_pos.col;
7989 gint row = sv->edit_pos.row;
7990 int rc = is_vert ? col : row;
7991 GnmPageBreaks *old, *new, *target;
7992 GnmPageBreakType type;
7993 char const *label;
7994 GOUndo *undo;
7995 GOUndo *redo;
7997 target = is_vert ? sheet->print_info->page_breaks.v
7998 : sheet->print_info->page_breaks.h;
8000 old = (target == NULL) ? gnm_page_breaks_new (is_vert)
8001 : gnm_page_breaks_dup (target);
8002 new = gnm_page_breaks_dup (old);
8004 if (gnm_page_breaks_get_break (new, rc) != GNM_PAGE_BREAK_MANUAL) {
8005 type = GNM_PAGE_BREAK_MANUAL;
8006 label = is_vert ? _("Remove Column Page Break") : _("Remove Row Page Break");
8007 } else {
8008 type = GNM_PAGE_BREAK_NONE;
8009 label = is_vert ? _("Add Column Page Break") : _("Add Row Page Break");
8012 gnm_page_breaks_set_break (new, rc, type);
8014 redo = go_undo_binary_new
8015 (sheet, new,
8016 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8017 NULL,
8018 (GFreeFunc) gnm_page_breaks_free);
8019 undo = go_undo_binary_new
8020 (sheet, old,
8021 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8022 NULL,
8023 (GFreeFunc) gnm_page_breaks_free);
8025 return cmd_generic (wbc, label, undo, redo);
8028 /******************************************************************/