Update Spanish translation
[gnumeric.git] / src / commands.c
blobbd7efde85e8604c51996e08cfef4914cc8ebd2f0
2 /*
3 * commands.c: Handlers to undo & redo commands
5 * Copyright (C) 1999-2008 Jody Goldberg (jody@gnome.org)
6 * Copyright (C) 2002-2008 Morten Welinder (terra@gnome.org)
8 * Contributors : Almer S. Tigelaar (almer@gnome.org)
9 * Andreas J. Guelzow (aguelzow@taliesin.ca)
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation; either version 2 of the
14 * License, or (at your option) version 3.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 * USA
26 #include <gnumeric-config.h>
27 #include <glib/gi18n-lib.h>
28 #include <gnumeric.h>
29 #include <commands.h>
30 #include <gnm-command-impl.h>
32 #include <application.h>
33 #include <sheet.h>
34 #include <sheet-view.h>
35 #include <sheet-style.h>
36 #include <gnm-format.h>
37 #include <format-template.h>
38 #include <command-context.h>
39 #include <workbook-control.h>
40 #include <workbook-view.h>
41 #include <workbook-priv.h> /* For the undo/redo queues and the FOREACH */
42 #include <ranges.h>
43 #include <sort.h>
44 #include <dependent.h>
45 #include <value.h>
46 #include <expr.h>
47 #include <func.h>
48 #include <expr-name.h>
49 #include <cell.h>
50 #include <sheet-merge.h>
51 #include <parse-util.h>
52 #include <print-info.h>
53 #include <clipboard.h>
54 #include <selection.h>
55 #include <colrow.h>
56 #include <style-border.h>
57 #include <tools/auto-correct.h>
58 #include <sheet-autofill.h>
59 #include <mstyle.h>
60 #include <search.h>
61 #include <gutils.h>
62 #include <gui-util.h>
63 #include <sheet-object-cell-comment.h>
64 #include <sheet-object-widget.h>
65 #include <sheet-object.h>
66 #include <sheet-object-component.h>
67 #include <sheet-object-graph.h>
68 #include <sheet-control.h>
69 #include <sheet-control-gui.h>
70 #include <sheet-utils.h>
71 #include <style-color.h>
72 #include <sheet-filter.h>
73 #include <auto-format.h>
74 #include <tools/dao.h>
75 #include <gnumeric-conf.h>
76 #include <tools/scenarios.h>
77 #include <tools/data-shuffling.h>
78 #include <tools/tabulate.h>
79 #include <wbc-gtk.h>
80 #include <undo.h>
82 #include <goffice/goffice.h>
83 #include <gsf/gsf-doc-meta-data.h>
84 #include <string.h>
86 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
90 * There are several distinct stages to wrapping each command.
92 * 1) Find the appropriate place(s) in the catch all calls to activations
93 * of this logical function. Be careful. This should only be called by
94 * user initiated actions, not internal commands.
96 * 2) Copy the boiler plate code into place and implement the descriptor.
98 * 3) Implement the guts of the support functions.
100 * That way undo redo just become applications of the old or the new styles.
102 * Design thoughts:
103 * 1) redo : this should be renamed 'exec' and should be the place that the
104 * the actual command executes. This avoid duplicating the code for
105 * application and re-application.
107 * 2) The command objects are responsible for generating recalc and redraw
108 * events. None of the internal utility routines should do so. Those are
109 * expensive events and should only be done once per command to avoid
110 * duplicating work. The lower levels can queue redraws if they must, and
111 * flag state changes but the call to gnm_app_recalc and sheet_update is
112 * by GnmCommand.
114 * FIXME: Filter the list of commands when a sheet is deleted.
116 * TODO : Possibly clear lists on save.
118 * TODO : Reqs for selective undo
120 * Future thoughts
121 * - undoable preference setting ? XL does not have this. Do we want it ?
123 /******************************************************************/
125 #define GNM_COMMAND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_COMMAND_TYPE, GnmCommand))
126 #define GNM_COMMAND_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_COMMAND_TYPE, GnmCommandClass))
127 #define GNM_IS_COMMAND(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_COMMAND_TYPE))
128 #define CMD_CLASS(o) GNM_COMMAND_CLASS (G_OBJECT_GET_CLASS(cmd))
130 GSF_CLASS (GnmCommand, gnm_command, NULL, NULL, G_TYPE_OBJECT)
132 void
133 gnm_command_finalize (GObject *obj)
135 GnmCommand *cmd = GNM_COMMAND (obj);
136 GObjectClass *parent;
138 /* The const was to avoid accidental changes elsewhere */
139 g_free ((gchar *)cmd->cmd_descriptor);
140 cmd->cmd_descriptor = NULL;
142 parent = g_type_class_peek (g_type_parent(G_TYPE_FROM_INSTANCE (obj)));
143 (*parent->finalize) (obj);
146 /******************************************************************/
148 GString *
149 gnm_cmd_trunc_descriptor (GString *src, gboolean *truncated)
151 int max_len = gnm_conf_get_undo_max_descriptor_width ();
152 glong len;
153 char *pos;
155 if (max_len < 5)
156 max_len = 5;
158 while ((pos = strchr(src->str, '\n')) != NULL ||
159 (pos = strchr(src->str, '\r')) != NULL)
160 *pos = ' ';
162 len = g_utf8_strlen (src->str, -1);
164 if (truncated)
165 *truncated = (len > max_len);
167 if (len > max_len) {
168 gchar* last = g_utf8_offset_to_pointer (src->str,
169 max_len - 1);
170 g_string_truncate (src, last - src->str);
171 g_string_append (src, UNICODE_ELLIPSIS);
173 return src;
178 * cmd_cell_range_is_locked_effective:
179 * @sheet: #Sheet
180 * @range: #GnmRange
181 * @wbc: #WorkbookControl
182 * @cmd_name: the command name.
184 * checks whether the cells are effectively locked
186 * static gboolean cmd_cell_range_is_locked_effective
189 * Do not use this function unless the sheet is part of the
190 * workbook with the given wbc (otherwise the results may be strange)
192 * Returns: %TRUE if there was a problem, %FALSE otherwise.
194 gboolean
195 cmd_cell_range_is_locked_effective (Sheet *sheet, GnmRange *range,
196 WorkbookControl *wbc, char const *cmd_name)
198 int i, j;
199 WorkbookView *wbv = wb_control_view (wbc);
201 if (wbv->is_protected || sheet->is_protected)
202 for (i = range->start.row; i <= range->end.row; i++)
203 for (j = range->start.col; j <= range->end.col; j++)
204 if (gnm_style_get_contents_locked (sheet_style_get (sheet, j, i))) {
205 char *r = global_range_name (sheet, range);
206 char *text = g_strdup_printf (wbv->is_protected ?
207 _("%s is locked. Unprotect the workbook to enable editing.") :
208 _("%s is locked. Unprotect the sheet to enable editing."),
210 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
211 cmd_name, text);
212 g_free (text);
213 g_free (r);
214 return TRUE;
216 return FALSE;
220 * checks whether the cells are effectively locked
222 * static gboolean cmd_dao_is_locked_effective
225 * Do not use this function unless the sheet is part of the
226 * workbook with the given wbcg (otherwise the results may be strange)
228 * Returns: %TRUE if there was a problem, %FALSE otherwise.
230 static gboolean
231 cmd_dao_is_locked_effective (data_analysis_output_t *dao,
232 WorkbookControl *wbc, char const *cmd_name)
234 GnmRange range;
235 range_init (&range, dao->start_col, dao->start_row,
236 dao->start_col + dao->cols - 1, dao->start_row + dao->rows - 1);
237 return (dao->type != NewWorkbookOutput &&
238 cmd_cell_range_is_locked_effective (dao->sheet, &range, wbc, cmd_name));
242 * cmd_selection_is_locked_effective: (skip)
243 * checks whether the selection is effectively locked
245 * static gboolean cmd_selection_is_locked_effective
247 * Do not use this function unless the sheet is part of the
248 * workbook with the given wbcg (otherwise the results may be strange)
250 * Returns: %TRUE if there was a problem, %FALSE otherwise.
252 gboolean
253 cmd_selection_is_locked_effective (Sheet *sheet, GSList *selection,
254 WorkbookControl *wbc, char const *cmd_name)
256 for (; selection; selection = selection->next) {
257 GnmRange *range = selection->data;
258 if (cmd_cell_range_is_locked_effective (sheet, range, wbc, cmd_name))
259 return TRUE;
261 return FALSE;
265 * A helper routine to select a range and make sure the top-left
266 * is visible.
268 static void
269 select_range (Sheet *sheet, const GnmRange *r, WorkbookControl *wbc)
271 SheetView *sv;
273 if (sheet->workbook != wb_control_get_workbook (wbc)) {
275 * We could try to pick a random wbc for the sheet's
276 * workbook. But not right now.
278 return;
281 wb_control_sheet_focus (wbc, sheet);
282 sv = sheet_get_view (sheet, wb_control_view (wbc));
283 sv_selection_reset (sv);
284 sv_selection_add_range (sv, r);
285 gnm_sheet_view_make_cell_visible (sv, r->start.col, r->start.row, FALSE);
289 * A helper routine to select a list of ranges and make sure the top-left
290 * corner of the last is visible.
292 static void
293 select_selection (Sheet *sheet, GSList *selection, WorkbookControl *wbc)
295 SheetView *sv = sheet_get_view (sheet, wb_control_view (wbc));
296 const GnmRange *r0 = NULL;
297 GSList *l;
299 g_return_if_fail (selection != NULL);
301 wb_control_sheet_focus (wbc, sheet);
302 sv_selection_reset (sv);
303 for (l = selection; l; l = l->next) {
304 GnmRange const *r = l->data;
305 sv_selection_add_range (sv, r);
306 r0 = r;
308 gnm_sheet_view_make_cell_visible (sv, r0->start.col, r0->start.row, FALSE);
312 * get_menu_label:
313 * with a list of commands.
314 * @cmd_list: The command list to check.
316 * Utility routine to get the descriptor associated
317 * Returns : A static reference to a descriptor. DO NOT free this.
319 static char const *
320 get_menu_label (GSList *cmd_list)
322 if (cmd_list != NULL) {
323 GnmCommand *cmd = GNM_COMMAND (cmd_list->data);
324 return cmd->cmd_descriptor;
327 return NULL;
331 * undo_redo_menu_labels:
332 * @wb: The book whose undo/redo queues we are modifying
334 * Another utility to set the menus correctly.
336 static void
337 undo_redo_menu_labels (Workbook *wb)
339 char const *undo_label = get_menu_label (wb->undo_commands);
340 char const *redo_label = get_menu_label (wb->redo_commands);
342 WORKBOOK_FOREACH_CONTROL (wb, view, control,
343 wb_control_undo_redo_labels (control, undo_label, redo_label);
347 static void
348 update_after_action (Sheet *sheet, WorkbookControl *wbc)
350 gnm_app_recalc ();
352 if (sheet != NULL) {
353 g_return_if_fail (IS_SHEET (sheet));
355 sheet_mark_dirty (sheet);
356 sheet_update (sheet);
358 if (sheet->workbook == wb_control_get_workbook (wbc))
359 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
360 wb_control_sheet_focus (control, sheet););
361 } else if (wbc != NULL) {
362 Sheet *sheet = wb_control_cur_sheet (wbc);
363 if (sheet)
364 sheet_update (sheet);
370 * command_undo:
371 * @wbc: The workbook control which issued the request.
372 * Any user level errors generated by undoing will be reported
373 * here.
375 * Undo the last command executed.
377 void
378 command_undo (WorkbookControl *wbc)
380 GnmCommand *cmd;
381 GnmCommandClass *klass;
382 Workbook *wb = wb_control_get_workbook (wbc);
384 g_return_if_fail (wb != NULL);
385 g_return_if_fail (wb->undo_commands != NULL);
387 cmd = GNM_COMMAND (wb->undo_commands->data);
388 g_return_if_fail (cmd != NULL);
390 klass = CMD_CLASS (cmd);
391 g_return_if_fail (klass != NULL);
393 g_object_ref (cmd);
395 /* TRUE indicates a failure to undo. Leave the command where it is */
396 if (!klass->undo_cmd (cmd, wbc)) {
397 gboolean undo_cleared;
399 update_after_action (cmd->sheet, wbc);
401 if (!cmd->workbook_modified_before_do)
402 go_doc_set_dirty (GO_DOC (wb), FALSE);
405 * A few commands clear the undo queue. For those, we do not
406 * want to stuff the cmd object on the redo queue.
408 undo_cleared = (wb->undo_commands == NULL);
410 if (!undo_cleared) {
411 wb->undo_commands = g_slist_remove (wb->undo_commands, cmd);
412 wb->redo_commands = g_slist_prepend (wb->redo_commands, cmd);
414 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
415 wb_control_undo_redo_pop (control, TRUE);
416 wb_control_undo_redo_push (control, FALSE, cmd->cmd_descriptor, cmd);
418 undo_redo_menu_labels (wb);
419 /* TODO : Should we mark the workbook as clean or pristine too */
423 g_object_unref (cmd);
427 * command_redo:
428 * @wbc: The workbook control which issued the request.
430 * Redo the last command that was undone.
431 * Any user level errors generated by redoing will be reported
432 * here.
434 void
435 command_redo (WorkbookControl *wbc)
437 GnmCommand *cmd;
438 GnmCommandClass *klass;
439 Workbook *wb = wb_control_get_workbook (wbc);
441 g_return_if_fail (wb);
442 g_return_if_fail (wb->redo_commands);
444 cmd = GNM_COMMAND (wb->redo_commands->data);
445 g_return_if_fail (cmd != NULL);
447 klass = CMD_CLASS (cmd);
448 g_return_if_fail (klass != NULL);
450 g_object_ref (cmd);
452 cmd->workbook_modified_before_do =
453 go_doc_is_dirty (wb_control_get_doc (wbc));
455 /* TRUE indicates a failure to redo. Leave the command where it is */
456 if (!klass->redo_cmd (cmd, wbc)) {
457 gboolean redo_cleared;
459 update_after_action (cmd->sheet, wbc);
462 * A few commands clear the undo queue. For those, we do not
463 * want to stuff the cmd object on the redo queue.
465 redo_cleared = (wb->redo_commands == NULL);
467 if (!redo_cleared) {
468 wb->redo_commands = g_slist_remove (wb->redo_commands, cmd);
469 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
471 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
472 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
473 wb_control_undo_redo_pop (control, FALSE);
475 undo_redo_menu_labels (wb);
479 g_object_unref (cmd);
483 * command_repeat:
484 * @wbc: The workbook control which issued the request.
486 * Repeat the last command (if possible)
488 * Any user level errors generated by redoing will be reported
489 * here.
491 void
492 command_repeat (WorkbookControl *wbc)
494 GnmCommand *cmd;
495 GnmCommandClass *klass;
496 Workbook *wb = wb_control_get_workbook (wbc);
498 g_return_if_fail (wb);
499 g_return_if_fail (wb->undo_commands);
501 cmd = GNM_COMMAND (wb->undo_commands->data);
502 g_return_if_fail (cmd != NULL);
504 klass = CMD_CLASS (cmd);
505 g_return_if_fail (klass != NULL);
507 if (klass->repeat_cmd != NULL)
508 (*klass->repeat_cmd) (cmd, wbc);
512 * command_setup_combos:
513 * @wbc:
515 * Initialize the combos to correspond to the current undo/redo state.
517 void
518 command_setup_combos (WorkbookControl *wbc)
520 char const *undo_label = NULL, *redo_label = NULL;
521 GSList *ptr, *tmp;
522 Workbook *wb = wb_control_get_workbook (wbc);
524 g_return_if_fail (wb);
526 wb_control_undo_redo_truncate (wbc, 0, TRUE);
527 tmp = g_slist_reverse (wb->undo_commands);
528 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
529 undo_label = get_menu_label (ptr);
530 wb_control_undo_redo_push (wbc, TRUE, undo_label, ptr->data);
532 if (g_slist_reverse (tmp)) {} /* ignore, list is in undo_commands */
534 wb_control_undo_redo_truncate (wbc, 0, FALSE);
535 tmp = g_slist_reverse (wb->redo_commands);
536 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
537 redo_label = get_menu_label (ptr);
538 wb_control_undo_redo_push (wbc, FALSE, redo_label, ptr->data);
540 if (g_slist_reverse (tmp)) {} /* ignore, list is in redo_commands */
542 /* update the menus too */
543 wb_control_undo_redo_labels (wbc, undo_label, redo_label);
547 * command_list_release:
548 * @cmds: (element-type GObject): the set of commands to free.
550 * command_list_release : utility routine to free the resources associated
551 * with a list of commands.
553 * NOTE : remember to NULL the list when you are done.
555 void
556 command_list_release (GSList *cmd_list)
558 while (cmd_list != NULL) {
559 GObject *cmd = G_OBJECT (cmd_list->data);
561 g_return_if_fail (cmd != NULL);
563 g_object_unref (cmd);
564 cmd_list = g_slist_remove (cmd_list, cmd_list->data);
569 * Each undo item has a certain size. The size of typing a value into
570 * a cell is the unit size. A large autoformat could have a size of
571 * hundreds or even thousands.
573 * We wish to have the same undo behaviour across platforms, so please
574 * don't use sizeof in computing the undo size.
577 #undef DEBUG_TRUNCATE_UNDO
580 * Truncate the undo list if it is too big.
582 * Returns -1 if no truncation was done, or else the number of elements
583 * left.
585 static int
586 truncate_undo_info (Workbook *wb)
588 int size_left;
589 int max_num;
590 int ok_count;
591 GSList *l, *prev;
593 size_left = gnm_conf_get_undo_size ();
594 max_num = gnm_conf_get_undo_maxnum ();
596 #ifdef DEBUG_TRUNCATE_UNDO
597 g_printerr ("Undo sizes:");
598 #endif
600 for (l = wb->undo_commands, prev = NULL, ok_count = 0;
602 prev = l, l = l->next, ok_count++) {
603 int min_leave;
604 GnmCommand *cmd = GNM_COMMAND (l->data);
605 int size = cmd->size;
607 if (size < 1) {
609 * We could g_assert, but that would cause data loss.
610 * Instead, just continue.
612 g_warning ("Faulty undo_size_func, please report.");
613 size = 1;
616 #ifdef DEBUG_TRUNCATE_UNDO
617 g_printerr (" %d", size);
618 #endif
620 /* Keep at least one undo item. */
621 if (ok_count >= max_num || (size > size_left && ok_count >= 1)) {
622 /* Current item is too big; truncate list here. */
623 command_list_release (l);
624 if (prev)
625 prev->next = NULL;
626 else
627 wb->undo_commands = NULL;
628 #ifdef DEBUG_TRUNCATE_UNDO
629 g_printerr ("[trunc]\n");
630 #endif
631 return ok_count;
635 * In order to allow a series of useful small items behind
636 * a big item, leave at least 10% of current item's size.
638 min_leave = size / 10;
639 size_left = MAX (size_left - size, min_leave);
642 #ifdef DEBUG_TRUNCATE_UNDO
643 g_printerr ("\n");
644 #endif
645 return -1;
650 * command_register_undo:
651 * @wbc: The workbook control that issued the command.
652 * @cmd: The new command to add.
654 * An internal utility to tack a new command
655 * onto the undo list.
657 static void
658 command_register_undo (WorkbookControl *wbc, GObject *obj)
660 Workbook *wb;
661 GnmCommand *cmd;
662 int undo_trunc;
664 g_return_if_fail (wbc != NULL);
665 wb = wb_control_get_workbook (wbc);
667 cmd = GNM_COMMAND (obj);
668 g_return_if_fail (cmd != NULL);
670 command_list_release (wb->redo_commands);
671 wb->redo_commands = NULL;
673 g_object_ref (obj); /* keep a ref in case it gets truncated away */
674 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
675 undo_trunc = truncate_undo_info (wb);
677 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
678 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
679 if (undo_trunc >= 0)
680 wb_control_undo_redo_truncate (control, undo_trunc, TRUE);
681 wb_control_undo_redo_truncate (control, 0, FALSE);
683 undo_redo_menu_labels (wb);
684 g_object_unref (obj);
689 * gnm_command_push_undo:
690 * @wbc: The workbook control that issued the command.
691 * @obj: The new command to add.
693 * An internal utility to tack a new command
694 * onto the undo list.
696 * Returns: %TRUE if there was a problem, %FALSE otherwise.
701 * cmd_set_text_full
703 * the caller is expected to have ensured:
705 * 1) that no array is being split
706 * 2) that the range is not locked.
708 * Note:
709 * We will free the selection but nothing else.
711 * Returns: %TRUE if there was a problem, %FALSE otherwise.
713 gboolean
714 gnm_command_push_undo (WorkbookControl *wbc, GObject *obj)
716 gboolean trouble;
717 GnmCommand *cmd;
718 GnmCommandClass *klass;
720 g_return_val_if_fail (wbc != NULL, TRUE);
722 cmd = GNM_COMMAND (obj);
723 cmd->workbook_modified_before_do =
724 go_doc_is_dirty (wb_control_get_doc (wbc));
726 g_return_val_if_fail (cmd != NULL, TRUE);
728 klass = CMD_CLASS (cmd);
729 g_return_val_if_fail (klass != NULL, TRUE);
731 /* TRUE indicates a failure to do the command */
732 trouble = klass->redo_cmd (cmd, wbc);
733 update_after_action (cmd->sheet, wbc);
735 if (!trouble)
736 command_register_undo (wbc, obj);
737 else
738 g_object_unref (obj);
740 return trouble;
744 * command_undo_sheet_delete deletes the sheet without deleting the current cmd.
745 * returns true if is indeed deleted the sheet.
746 * Note: only call this for a sheet of your current workbook from the undo procedure
749 static gboolean
750 command_undo_sheet_delete (Sheet* sheet)
752 Workbook *wb = sheet->workbook;
754 g_return_val_if_fail (IS_SHEET (sheet), FALSE);
756 if (wb->redo_commands != NULL) {
757 command_list_release (wb->redo_commands);
758 wb->redo_commands = NULL;
759 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
760 wb_control_undo_redo_truncate (ctl, 0, FALSE););
761 undo_redo_menu_labels (wb);
764 workbook_sheet_delete (sheet);
766 return (TRUE);
769 /******************************************************************/
771 static GnmValue *
772 cmd_set_text_full_check_texpr (GnmCellIter const *iter, GnmExprTop const *texpr)
774 if (iter->cell == NULL ||
775 !gnm_expr_top_equal (iter->cell->base.texpr, texpr))
776 return VALUE_TERMINATE;
777 return NULL;
780 static GnmValue *
781 cmd_set_text_full_check_text (GnmCellIter const *iter, char *text)
783 char *old_text;
784 gboolean same;
785 gboolean quoted = FALSE;
787 if (gnm_cell_is_blank (iter->cell))
788 return ((text == NULL || text[0] == '\0') ? NULL : VALUE_TERMINATE);
790 if (text == NULL || text[0] == '\0')
791 return VALUE_TERMINATE;
793 old_text = gnm_cell_get_text_for_editing (iter->cell, NULL, &quoted);
794 same = g_strcmp0 (old_text, text) == 0;
796 if (!same && !quoted && iter->cell->value && VALUE_IS_STRING (iter->cell->value)
797 && text[0] == '\'')
798 same = g_strcmp0 (old_text, text + 1) == 0;
800 g_free (old_text);
802 return (same ? NULL : VALUE_TERMINATE);
805 static GnmValue *
806 cmd_set_text_full_check_markup (GnmCellIter const *iter, PangoAttrList *markup)
808 PangoAttrList const *old_markup = NULL;
809 gboolean same_markup;
811 g_return_val_if_fail (iter->cell != NULL, NULL);
813 if (iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
814 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
815 if (fmt && go_format_is_markup (fmt)) {
816 old_markup = go_format_get_markup (fmt);
817 if (go_pango_attr_list_is_empty (old_markup))
818 old_markup = NULL;
822 same_markup = gnm_pango_attr_list_equal (old_markup, markup);
824 return same_markup ? NULL : VALUE_TERMINATE;
828 * cmd_set_text_full
830 * the caller is expected to have ensured:
832 * 1) that no array is being split
833 * 2) that the range is not locked.
835 * Note:
836 * We will free the selection but nothing else.
838 * Returns: %TRUE if there was a problem, %FALSE otherwise.
840 static gboolean
841 cmd_set_text_full (WorkbookControl *wbc, GSList *selection, GnmEvalPos *ep,
842 char const *new_text, PangoAttrList *markup,
843 gboolean autocorrect)
845 GSList *l;
846 char const *expr_txt;
847 GnmExprTop const *texpr = NULL;
848 GOUndo *undo = NULL;
849 GOUndo *redo = NULL;
850 gboolean result, autofit_col = FALSE, same_text_and_not_same_markup = FALSE;
851 char *text = NULL;
852 char *name;
853 Sheet *sheet = ep->sheet;
854 GnmParsePos pp;
855 ColRowIndexList *cri_col_list = NULL, *cri_row_list = NULL;
856 GOFormat const *format = gnm_style_get_format
857 (sheet_style_get (sheet, ep->eval.col, ep->eval.row));
859 g_return_val_if_fail (selection != NULL , TRUE);
861 parse_pos_init_evalpos (&pp, ep);
862 name = undo_range_list_name (sheet, selection);
864 if ((format == NULL) || !go_format_is_text (format)) {
865 expr_txt = gnm_expr_char_start_p (new_text);
866 if (expr_txt != NULL)
867 texpr = gnm_expr_parse_str
868 (expr_txt, &pp, GNM_EXPR_PARSE_DEFAULT,
869 sheet_get_conventions (sheet), NULL);
872 if (texpr != NULL) {
873 GOFormat const *sf;
874 GnmStyle *new_style = NULL;
875 gboolean same_texpr = TRUE;
877 /* We should check whether we are in fact changing anything: */
878 for (l = selection; l != NULL && same_texpr; l = l->next) {
879 GnmRange *r = l->data;
880 GnmValue *val =
881 sheet_foreach_cell_in_range
882 (sheet, CELL_ITER_ALL, r,
883 (CellIterFunc) cmd_set_text_full_check_texpr,
884 (gpointer) texpr);
886 same_texpr = (val != VALUE_TERMINATE);
887 if (val != NULL && same_texpr)
888 value_release (val);
891 if (same_texpr) {
892 gnm_expr_top_unref (texpr);
893 g_free (name);
894 range_fragment_free (selection);
895 return TRUE;
898 text = g_strdup_printf (_("Inserting expression in %s"), name);
900 if (go_format_is_general (format)) {
901 sf = gnm_auto_style_format_suggest (texpr, ep);
902 if (sf != NULL) {
903 new_style = gnm_style_new ();
904 gnm_style_set_format (new_style, sf);
905 go_format_unref (sf);
909 for (l = selection; l != NULL; l = l->next) {
910 GnmSheetRange *sr;
911 undo = go_undo_combine
912 (undo, clipboard_copy_range_undo (sheet, l->data));
913 sr = gnm_sheet_range_new (sheet, l->data);
914 redo = go_undo_combine
915 (redo, sheet_range_set_expr_undo (sr, texpr));
916 if (new_style) {
917 sr = gnm_sheet_range_new (sheet, l->data);
918 redo = go_undo_combine
919 (redo, sheet_apply_style_undo (sr, new_style));
922 if (new_style)
923 gnm_style_unref (new_style);
924 gnm_expr_top_unref (texpr);
925 autofit_col = TRUE;
926 } else {
927 GString *text_str;
928 PangoAttrList *adj_markup = NULL;
929 char *corrected;
930 gboolean same_text = TRUE;
931 gboolean same_markup = TRUE;
933 if (new_text == NULL)
934 corrected = NULL;
935 else if (autocorrect)
936 corrected = autocorrect_tool (new_text);
937 else
938 corrected = g_strdup (new_text);
940 if (corrected && (corrected[0] == '\'') && corrected[1] == '\0') {
941 g_free (corrected);
942 corrected = g_strdup ("");
945 /* We should check whether we are in fact changing anything: */
946 /* We'll handle */
947 for (l = selection; l != NULL && same_text; l = l->next) {
948 GnmRange *r = l->data;
949 GnmValue *val =
950 sheet_foreach_cell_in_range
951 (sheet, CELL_ITER_ALL, r,
952 (CellIterFunc) cmd_set_text_full_check_text,
953 (gpointer) corrected);
955 same_text = (val != VALUE_TERMINATE);
956 if (val != NULL && same_text)
957 value_release (val);
960 if (go_pango_attr_list_is_empty (markup))
961 markup = NULL;
962 if (markup && corrected && corrected[0] == '\'') {
963 markup = adj_markup = pango_attr_list_copy (markup);
964 go_pango_attr_list_erase (adj_markup, 0, 1);
967 if (same_text) {
968 for (l = selection; l != NULL && same_text; l = l->next) {
969 GnmRange *r = l->data;
970 GnmValue *val =
971 sheet_foreach_cell_in_range
972 (sheet, CELL_ITER_IGNORE_BLANK, r,
973 (CellIterFunc) cmd_set_text_full_check_markup,
974 (gpointer) markup);
976 same_markup = (val != VALUE_TERMINATE);
977 if (val != NULL && same_markup)
978 value_release (val);
981 if (same_markup) {
982 g_free (corrected);
983 g_free (name);
984 range_fragment_free (selection);
985 if (adj_markup)
986 pango_attr_list_unref (adj_markup);
987 return TRUE;
990 text = g_strdup_printf (_("Editing style of %s"), name);
991 } else {
992 text_str = gnm_cmd_trunc_descriptor (g_string_new (corrected), NULL);
993 text = g_strdup_printf (_("Typing \"%s\" in %s"), text_str->str, name);
994 g_string_free (text_str, TRUE);
997 for (l = selection; l != NULL; l = l->next) {
998 GnmSheetRange *sr;
999 undo = go_undo_combine
1000 (undo, clipboard_copy_range_undo (sheet, l->data));
1001 if (corrected) {
1002 sr = gnm_sheet_range_new (sheet, l->data);
1003 redo = go_undo_combine
1004 (redo, sheet_range_set_text_undo
1005 (sr, corrected));
1007 if (markup) {
1008 sr = gnm_sheet_range_new (sheet, l->data);
1009 /* Note: order of combination matters!! */
1010 redo = go_undo_combine
1011 (sheet_range_set_markup_undo (sr, markup), redo);
1015 if (adj_markup)
1016 pango_attr_list_unref (adj_markup);
1017 g_free (corrected);
1019 same_text_and_not_same_markup = (same_text && !same_markup);
1021 g_free (name);
1023 /* We are combining this since we don't want to apply and undo twice.*/
1024 if (same_text_and_not_same_markup || !autofit_col) {
1025 GnmCell *cell = sheet_cell_fetch
1026 (sheet, ep->eval.col, ep->eval.row);
1027 gboolean nvis;
1029 go_undo_undo (redo);
1030 nvis = !VALUE_IS_STRING (cell->value);
1031 if (!autofit_col)
1032 autofit_col = nvis;
1033 if (same_text_and_not_same_markup)
1034 /* We only have to do something if at least one cell */
1035 /* now contains a string, but they contain all the same thing. */
1036 same_text_and_not_same_markup = nvis;
1037 go_undo_undo (undo);
1039 if (same_text_and_not_same_markup) {
1040 /*We had the same text and different markup but we are not entering strings. */
1041 g_object_unref (undo);
1042 g_object_unref (redo);
1043 g_free (text);
1044 range_fragment_free (selection);
1045 return TRUE;
1047 for (l = selection; l != NULL; l = l->next) {
1048 GnmRange *r = l->data;
1049 GnmRange *new_r;
1051 new_r = g_new (GnmRange, 1);
1052 *new_r = *r;
1053 redo = go_undo_combine
1054 (go_undo_binary_new
1055 (sheet, new_r,
1056 (GOUndoBinaryFunc) colrow_autofit_row,
1057 NULL, g_free),
1058 redo);
1059 cri_row_list = colrow_get_index_list
1060 (r->start.row, r->end.row, cri_row_list);
1062 if (autofit_col) {
1063 new_r = g_new (GnmRange, 1);
1064 *new_r = *r;
1065 redo = go_undo_combine
1066 (go_undo_binary_new
1067 (sheet, new_r,
1068 (GOUndoBinaryFunc) colrow_autofit_col,
1069 NULL, g_free),
1070 redo);
1071 cri_col_list = colrow_get_index_list
1072 (r->start.col, r->end.col, cri_col_list);
1076 undo = go_undo_combine (undo,
1077 gnm_undo_colrow_restore_state_group_new
1078 (sheet, TRUE,
1079 cri_col_list,
1080 colrow_get_sizes (sheet, TRUE,
1081 cri_col_list, -1)));
1082 undo = go_undo_combine (undo,
1083 gnm_undo_colrow_restore_state_group_new
1084 (sheet, FALSE,
1085 cri_row_list,
1086 colrow_get_sizes (sheet, FALSE,
1087 cri_row_list, -1)));
1089 result = cmd_generic (wbc, text, undo, redo);
1090 g_free (text);
1091 range_fragment_free (selection);
1092 return result;
1096 * cmd_area_set_text
1098 * the caller is expected to have ensured:
1100 * 1) that no array is being split
1101 * 2) that the range is not locked.
1103 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1105 gboolean
1106 cmd_area_set_text (WorkbookControl *wbc, SheetView *sv,
1107 char const *new_text, PangoAttrList *markup)
1109 GnmEvalPos ep;
1110 gboolean result;
1111 GSList *selection = selection_get_ranges (sv, FALSE);
1113 eval_pos_init_editpos (&ep, sv);
1114 result = cmd_set_text_full (wbc, selection, &ep,
1115 new_text, markup, TRUE);
1116 return result;
1119 gboolean
1120 cmd_set_text (WorkbookControl *wbc,
1121 Sheet *sheet, GnmCellPos const *pos,
1122 char const *new_text,
1123 PangoAttrList *markup,
1124 gboolean autocorrect)
1126 GnmCell const *cell;
1127 GnmEvalPos ep;
1128 gboolean result;
1129 GSList *selection;
1130 GnmRange *r;
1132 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1133 g_return_val_if_fail (new_text != NULL, TRUE);
1135 /* Ensure that we are not splitting up an array */
1136 cell = sheet_cell_get (sheet, pos->col, pos->row);
1137 if (gnm_cell_is_nonsingleton_array (cell)) {
1138 gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc),
1139 _("Set Text"), NULL);
1140 return TRUE;
1143 eval_pos_init_pos (&ep, sheet, pos);
1144 r = g_new (GnmRange, 1);
1145 r->start = r->end = *pos;
1146 selection = g_slist_prepend (NULL, r);
1147 result = cmd_set_text_full (wbc, selection, &ep,
1148 new_text, markup, autocorrect);
1149 return result;
1154 * cmd_area_set_array_expr
1156 * the caller is expected to have ensured:
1158 * 1) that no array is being split
1159 * 2) that the selection consists of a single range
1160 * 3) that the range is not locked.
1162 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1164 gboolean
1165 cmd_area_set_array_expr (WorkbookControl *wbc, SheetView *sv,
1166 GnmExprTop const *texpr)
1168 GSList *selection = selection_get_ranges (sv, FALSE);
1169 GOUndo *undo = NULL;
1170 GOUndo *redo = NULL;
1171 gboolean result;
1172 Sheet *sheet = sv_sheet (sv);
1173 char *name;
1174 char *text;
1175 GnmSheetRange *sr;
1176 GnmRange *r;
1178 g_return_val_if_fail (selection != NULL , TRUE);
1179 g_return_val_if_fail (selection->next == NULL , TRUE);
1181 name = undo_range_list_name (sheet, selection);
1182 text = g_strdup_printf (_("Inserting array expression in %s"), name);
1183 g_free (name);
1185 r = selection->data;
1187 undo = clipboard_copy_range_undo (sheet, selection->data);
1189 sr = gnm_sheet_range_new (sheet, r);
1190 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1191 redo = go_undo_combine
1192 (go_undo_binary_new
1193 (sheet, g_memdup (r, sizeof (*r)),
1194 (GOUndoBinaryFunc) colrow_autofit_col,
1195 NULL, g_free),
1196 redo);
1197 redo = go_undo_combine
1198 (go_undo_binary_new
1199 (sheet, g_memdup (r, sizeof (*r)),
1200 (GOUndoBinaryFunc) colrow_autofit_row,
1201 NULL, g_free),
1202 redo);
1204 range_fragment_free (selection);
1205 result = cmd_generic (wbc, text, undo, redo);
1206 g_free (text);
1207 return result;
1211 * cmd_create_data_table
1213 * the caller is expected to have ensured:
1215 * 1) that no array is being split
1216 * 2) that the range is not locked.
1218 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1220 gboolean
1221 cmd_create_data_table (WorkbookControl *wbc, Sheet *sheet, GnmRange const *r,
1222 char const *col_input, char const *row_input)
1224 GOUndo *undo = NULL;
1225 GOUndo *redo = NULL;
1226 gboolean result;
1227 char *name;
1228 char *text;
1229 GnmSheetRange *sr;
1230 GnmParsePos pp;
1231 GnmExprTop const *texpr;
1233 name = undo_range_name (sheet, r);
1234 text = g_strdup_printf (_("Creating a Data Table in %s"), name);
1235 g_free (name);
1237 undo = clipboard_copy_range_undo (sheet, r);
1239 sr = gnm_sheet_range_new (sheet, r);
1240 parse_pos_init (&pp, NULL, sheet, r->start.col, r->start.row);
1241 name = g_strdup_printf ("TABLE(%s,%s)", row_input, col_input);
1242 texpr = gnm_expr_parse_str
1243 (name, &pp, GNM_EXPR_PARSE_DEFAULT,
1244 sheet_get_conventions (sheet), NULL);
1245 g_free (name);
1247 if (texpr == NULL) {
1248 g_object_unref (undo);
1249 g_free (text);
1250 return TRUE;
1253 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1254 gnm_expr_top_unref (texpr);
1256 result = cmd_generic (wbc, text, undo, redo);
1257 g_free (text);
1258 return result;
1261 /******************************************************************/
1263 #define CMD_INS_DEL_COLROW_TYPE (cmd_ins_del_colrow_get_type ())
1264 #define CMD_INS_DEL_COLROW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_INS_DEL_COLROW_TYPE, CmdInsDelColRow))
1266 typedef struct {
1267 GnmCommand cmd;
1269 Sheet *sheet;
1270 gboolean is_insert;
1271 gboolean is_cols;
1272 gboolean is_cut;
1273 int index;
1274 int count;
1275 GnmRange *cutcopied;
1276 SheetView *cut_copy_view;
1278 gboolean (*redo_action) (Sheet *sheet, int col, int count,
1279 GOUndo **pundo, GOCmdContext *cc);
1281 gboolean (*repeat_action) (WorkbookControl *wbc, Sheet *sheet,
1282 int start, int count);
1284 GOUndo *undo;
1285 } CmdInsDelColRow;
1287 static void
1288 cmd_ins_del_colrow_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1290 CmdInsDelColRow const *orig = (CmdInsDelColRow const *) cmd;
1291 SheetView *sv = wb_control_cur_sheet_view (wbc);
1292 Sheet *sheet = sv_sheet (sv);
1293 GnmRange const *r = selection_first_range (sv,
1294 GO_CMD_CONTEXT (wbc), _("Ins/Del Column/Row"));
1295 int start, count;
1297 if (r == NULL)
1298 return;
1300 if (orig->is_cols)
1301 start = r->start.col, count = range_width (r);
1302 else
1303 start = r->start.row, count = range_height (r);
1305 orig->repeat_action (wbc, sheet, start, count);
1308 MAKE_GNM_COMMAND (CmdInsDelColRow, cmd_ins_del_colrow, cmd_ins_del_colrow_repeat)
1310 static gboolean
1311 cmd_ins_del_colrow_undo (GnmCommand *cmd, WorkbookControl *wbc)
1313 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1315 if (me->undo) {
1316 go_undo_undo (me->undo);
1317 g_object_unref (me->undo);
1318 me->undo = NULL;
1321 /* Ins/Del Row/Col re-ants things completely to account
1322 * for the shift of col/rows.
1324 if (me->cutcopied != NULL && me->cut_copy_view != NULL)
1325 gnm_app_clipboard_cut_copy (wbc, me->is_cut, me->cut_copy_view,
1326 me->cutcopied, FALSE);
1328 return FALSE;
1331 static gboolean
1332 cmd_ins_del_colrow_redo (GnmCommand *cmd, WorkbookControl *wbc)
1334 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1335 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
1336 int idx = me->index;
1337 int count = me->count;
1339 if (me->redo_action (me->sheet, idx, count, &me->undo, cc)) {
1340 /* Trouble. */
1341 return TRUE;
1344 /* Ins/Del Row/Col re-ants things completely to account
1345 * for the shift of col/rows. */
1346 if (me->cutcopied != NULL && me->cut_copy_view != NULL) {
1347 if (me->is_cut) {
1348 GnmRange s = *me->cutcopied;
1349 int key = me->is_insert ? count : -count;
1350 int threshold = me->is_insert ? idx : idx + 1;
1353 * Really only applies if the regions that are
1354 * inserted/deleted are above the cut/copied region.
1356 if (me->is_cols) {
1357 if (threshold <= s.start.col) {
1358 s.start.col += key;
1359 s.end.col += key;
1361 } else if (threshold <= s.start.row) {
1362 s.start.row += key;
1363 s.end.row += key;
1366 gnm_app_clipboard_cut_copy (wbc, me->is_cut,
1367 me->cut_copy_view,
1368 &s, FALSE);
1369 } else
1370 gnm_app_clipboard_unant ();
1373 return FALSE;
1376 static void
1377 cmd_ins_del_colrow_finalize (GObject *cmd)
1379 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1381 if (me->undo)
1382 g_object_unref (me->undo);
1384 g_free (me->cutcopied);
1386 gnm_sheet_view_weak_unref (&(me->cut_copy_view));
1388 gnm_command_finalize (cmd);
1391 static gboolean
1392 cmd_ins_del_colrow (WorkbookControl *wbc,
1393 Sheet *sheet,
1394 gboolean is_cols, gboolean is_insert,
1395 char const *descriptor, int index, int count)
1397 CmdInsDelColRow *me;
1398 int first, last;
1399 GnmRange r;
1401 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1402 g_return_val_if_fail (count > 0, TRUE);
1404 me = g_object_new (CMD_INS_DEL_COLROW_TYPE, NULL);
1406 me->sheet = sheet;
1407 me->is_cols = is_cols;
1408 me->is_insert = is_insert;
1409 me->index = index;
1410 me->count = count;
1411 me->redo_action = me->is_insert
1412 ? (me->is_cols ? sheet_insert_cols : sheet_insert_rows)
1413 : (me->is_cols ? sheet_delete_cols : sheet_delete_rows);
1414 me->repeat_action = me->is_insert
1415 ? (me->is_cols ? cmd_insert_cols : cmd_insert_rows)
1416 : (me->is_cols ? cmd_delete_cols : cmd_delete_rows);
1418 /* Range that will get deleted. */
1419 first = me->is_insert
1420 ? colrow_max (is_cols, sheet) - count
1421 : index;
1422 last = first + count - 1;
1423 (is_cols ? range_init_cols : range_init_rows) (&r, sheet, first, last);
1425 /* Note: redo_action checks for array subdivision. */
1427 /* Check for locks */
1428 if (cmd_cell_range_is_locked_effective (sheet, &r, wbc, descriptor)) {
1429 g_object_unref (me);
1430 return TRUE;
1433 /* We store the cut or/copied range if applicable */
1434 if (!gnm_app_clipboard_is_empty () &&
1435 gnm_app_clipboard_area_get () &&
1436 sheet == gnm_app_clipboard_sheet_get ()) {
1437 me->cutcopied = gnm_range_dup (gnm_app_clipboard_area_get ());
1438 me->is_cut = gnm_app_clipboard_is_cut ();
1439 gnm_sheet_view_weak_ref (gnm_app_clipboard_sheet_view_get (),
1440 &(me->cut_copy_view));
1441 } else
1442 me->cutcopied = NULL;
1444 me->cmd.sheet = sheet;
1445 me->cmd.size = count * 10; /* FIXME? */
1446 me->cmd.cmd_descriptor = descriptor;
1448 return gnm_command_push_undo (wbc, G_OBJECT (me));
1451 gboolean
1452 cmd_insert_cols (WorkbookControl *wbc,
1453 Sheet *sheet, int start_col, int count)
1455 char *mesg;
1456 GnmRange r;
1458 range_init_full_sheet (&r, sheet);
1459 r.start.col = r.end.col - count + 1;
1461 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1462 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1463 ngettext ("Inserting %i column before column %s would push data off the sheet. "
1464 "Please enlarge the sheet first.",
1465 "Inserting %i columns before column %s would push data off the sheet. "
1466 "Please enlarge the sheet first.",
1467 count),
1468 count, col_name (start_col));
1469 return TRUE;
1472 mesg = g_strdup_printf
1473 (ngettext ("Inserting %d column before %s",
1474 "Inserting %d columns before %s",
1475 count),
1476 count, col_name (start_col));
1477 return cmd_ins_del_colrow (wbc, sheet, TRUE, TRUE, mesg, start_col, count);
1480 gboolean
1481 cmd_insert_rows (WorkbookControl *wbc,
1482 Sheet *sheet, int start_row, int count)
1484 char *mesg;
1485 GnmRange r;
1487 range_init_full_sheet (&r, sheet);
1488 r.start.row = r.end.row - count + 1;
1490 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1491 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1492 ngettext ("Inserting %i row before row %s would push data off the sheet. "
1493 "Please enlarge the sheet first.",
1494 "Inserting %i rows before row %s would push data off the sheet. "
1495 "Please enlarge the sheet first.",
1496 count),
1497 count, row_name (start_row));
1498 return TRUE;
1501 mesg = g_strdup_printf
1502 (ngettext ("Inserting %d row before %s",
1503 "Inserting %d rows before %s",
1504 count),
1505 count, row_name (start_row));
1506 return cmd_ins_del_colrow (wbc, sheet, FALSE, TRUE, mesg, start_row, count);
1509 gboolean
1510 cmd_delete_cols (WorkbookControl *wbc,
1511 Sheet *sheet, int start_col, int count)
1513 char *mesg = g_strdup_printf ((count > 1)
1514 ? _("Deleting columns %s")
1515 : _("Deleting column %s"),
1516 cols_name (start_col, start_col + count - 1));
1517 return cmd_ins_del_colrow (wbc, sheet, TRUE, FALSE, mesg, start_col, count);
1520 gboolean
1521 cmd_delete_rows (WorkbookControl *wbc,
1522 Sheet *sheet, int start_row, int count)
1524 char *mesg = g_strdup_printf ((count > 1)
1525 ? _("Deleting rows %s")
1526 : _("Deleting row %s"),
1527 rows_name (start_row, start_row + count - 1));
1528 return cmd_ins_del_colrow (wbc, sheet, FALSE, FALSE, mesg, start_row, count);
1531 /******************************************************************/
1533 typedef struct {
1534 GSList *selection;
1535 GnmRange const *r;
1536 } cmd_selection_clear_row_handler_t;
1538 static gboolean
1539 cmd_selection_clear_row_handler (GnmColRowIter const *iter,
1540 cmd_selection_clear_row_handler_t *data)
1542 if ((!iter->cri->in_filter) || iter->cri->visible) {
1543 GnmRange *r = gnm_range_dup (data->r);
1544 r->start.row = r->end.row = iter->pos;
1545 data->selection = g_slist_prepend (data->selection, r);
1547 return FALSE;
1550 gboolean
1551 cmd_selection_clear (WorkbookControl *wbc, int clear_flags)
1553 char *names, *descriptor;
1554 GString *types;
1555 SheetView *sv = wb_control_cur_sheet_view (wbc);
1556 GSList *selection = selection_get_ranges (sv, FALSE /* No intersection */);
1557 Sheet *sheet = sv_sheet (sv);
1558 gboolean result;
1559 int size;
1560 GOUndo *undo = NULL;
1561 GOUndo *redo = NULL;
1562 GSList *ranges;
1564 if ((clear_flags & CLEAR_FILTERED_ONLY) != 0 && sheet->filters != NULL) {
1565 /* We need to modify the selection to only include filtered rows. */
1566 cmd_selection_clear_row_handler_t data;
1567 data.selection = selection;
1568 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1569 GnmFilter *filter;
1570 data.r = ranges->data;
1571 filter = gnm_sheet_filter_intersect_rows
1572 (sheet, data.r->start.row, data.r->end.row);
1573 if (filter) {
1574 sheet_colrow_foreach (sheet, FALSE,
1575 data.r->start.row,
1576 data.r->end.row,
1577 (ColRowHandler) cmd_selection_clear_row_handler,
1578 &data);
1579 g_free (ranges->data);
1580 ranges->data = NULL;
1583 selection = g_slist_remove_all (data.selection, NULL);
1586 /* We should first determine whether we break anything by clearing */
1587 /* Check for array subdivision *//* Check for locked cells */
1588 if (sheet_ranges_split_region (sheet, selection,
1589 GO_CMD_CONTEXT (wbc), _("Clear")) ||
1590 cmd_selection_is_locked_effective (sheet, selection, wbc, _("Clear"))) {
1591 range_fragment_free (selection);
1592 return TRUE;
1596 /* We now need to build the descriptor */
1597 /* Collect clear types for descriptor */
1598 if (clear_flags != (CLEAR_VALUES | CLEAR_FORMATS | CLEAR_COMMENTS)) {
1599 GSList *m, *l = NULL;
1600 types = g_string_new (NULL);
1601 if (clear_flags & CLEAR_VALUES)
1602 l = g_slist_append (l, g_string_new (_("contents")));
1603 if (clear_flags & CLEAR_FORMATS)
1604 l = g_slist_append (l, g_string_new (_("formats")));
1605 if (clear_flags & CLEAR_COMMENTS)
1606 l = g_slist_append (l, g_string_new (_("comments")));
1607 /* Using a list for this may seem overkill, but is really the only
1608 * right way to do this
1610 for (m = l; m != NULL; m = m->next) {
1611 GString *s = m->data;
1613 g_string_append_len (types, s->str, s->len);
1614 g_string_free (s, TRUE);
1616 if (m->next)
1617 g_string_append (types, ", ");
1619 g_slist_free (l);
1620 } else
1621 types = g_string_new (_("all"));
1622 /* The range name string will automatically be truncated, we don't
1623 * need to truncate the "types" list because it will not grow
1624 * indefinitely
1626 names = undo_range_list_name (sheet, selection);
1627 descriptor = g_strdup_printf (_("Clearing %s in %s"), types->str, names);
1628 g_free (names);
1629 g_string_free (types, TRUE);
1630 size = g_slist_length (selection);
1632 clear_flags |= CLEAR_NOCHECKARRAY;
1634 if (clear_flags & (CLEAR_VALUES | CLEAR_FORMATS))
1635 clear_flags |= CLEAR_RECALC_DEPS;
1637 /* We are now ready to build the redo and undo items */
1638 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1639 GnmRange const *r = ranges->data;
1640 GnmSheetRange *sr = gnm_sheet_range_new (sheet, r);
1642 undo = go_undo_combine (undo, clipboard_copy_range_undo (sheet, r));
1643 redo = go_undo_combine
1644 (redo, sheet_clear_region_undo
1645 (sr, clear_flags));
1648 range_fragment_free (selection);
1650 result = cmd_generic_with_size (wbc, descriptor, size, undo, redo);
1651 g_free (descriptor);
1653 return result;
1656 /******************************************************************/
1658 #define CMD_FORMAT_TYPE (cmd_format_get_type ())
1659 #define CMD_FORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FORMAT_TYPE, CmdFormat))
1661 typedef struct {
1662 GnmCellPos pos;
1663 GnmStyleList *styles;
1664 ColRowIndexList *rows;
1665 ColRowStateGroup *old_heights;
1666 } CmdFormatOldStyle;
1668 typedef struct {
1669 GnmCommand cmd;
1670 GSList *selection;
1671 GSList *old_styles;
1672 GnmStyle *new_style;
1673 GnmBorder **borders;
1674 } CmdFormat;
1676 static void
1677 cmd_format_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1679 CmdFormat const *orig = (CmdFormat const *) cmd;
1680 int i;
1682 if (orig->new_style)
1683 gnm_style_ref (orig->new_style);
1684 if (orig->borders)
1685 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1686 gnm_style_border_ref (orig->borders [i]);
1688 cmd_selection_format (wbc, orig->new_style, orig->borders, NULL);
1690 MAKE_GNM_COMMAND (CmdFormat, cmd_format, cmd_format_repeat)
1692 static gboolean
1693 cmd_format_undo (GnmCommand *cmd,
1694 G_GNUC_UNUSED WorkbookControl *wbc)
1696 CmdFormat *me = CMD_FORMAT (cmd);
1698 g_return_val_if_fail (me != NULL, TRUE);
1700 if (me->old_styles) {
1701 GSList *rstyles = g_slist_reverse (g_slist_copy (me->old_styles));
1702 GSList *rsel = g_slist_reverse (g_slist_copy (me->selection));
1703 GSList *l1, *l2;
1705 for (l1 = rstyles, l2 = rsel; l1; l1 = l1->next, l2 = l2->next) {
1706 CmdFormatOldStyle *os = l1->data;
1707 GnmRange const *r = l2->data;
1708 GnmSpanCalcFlags flags = sheet_style_set_list
1709 (me->cmd.sheet,
1710 &os->pos, os->styles, NULL, NULL);
1712 if (os->old_heights) {
1713 colrow_restore_state_group (me->cmd.sheet, FALSE,
1714 os->rows,
1715 os->old_heights);
1716 colrow_state_group_destroy (os->old_heights);
1717 os->old_heights = NULL;
1718 colrow_index_list_destroy (os->rows);
1719 os->rows = NULL;
1722 sheet_range_calc_spans (me->cmd.sheet, r, flags);
1723 sheet_flag_style_update_range (me->cmd.sheet, r);
1726 sheet_redraw_all (me->cmd.sheet, FALSE);
1727 g_slist_free (rstyles);
1728 g_slist_free (rsel);
1731 select_selection (me->cmd.sheet, me->selection, wbc);
1733 return FALSE;
1736 static gboolean
1737 cmd_format_redo (GnmCommand *cmd, WorkbookControl *wbc)
1739 CmdFormat *me = CMD_FORMAT (cmd);
1740 GSList *l1, *l2;
1741 gboolean re_fit_height;
1743 g_return_val_if_fail (me != NULL, TRUE);
1745 /* Check for locked cells */
1746 if (cmd_selection_is_locked_effective (me->cmd.sheet, me->selection,
1747 wbc, _("Changing Format")))
1748 return TRUE;
1750 re_fit_height = me->new_style &&
1751 (GNM_SPANCALC_ROW_HEIGHT & gnm_style_required_spanflags (me->new_style));
1753 for (l1 = me->old_styles, l2 = me->selection; l2; l1 = l1->next, l2 = l2->next) {
1754 CmdFormatOldStyle *os = l1->data;
1755 GnmRange const *r = l2->data;
1757 if (me->borders)
1758 sheet_apply_border (me->cmd.sheet, r, me->borders);
1759 if (me->new_style) {
1760 gnm_style_ref (me->new_style);
1761 sheet_apply_style (me->cmd.sheet, r, me->new_style);
1762 if (re_fit_height)
1763 colrow_autofit (me->cmd.sheet, r, FALSE, FALSE,
1764 TRUE, FALSE,
1765 &os->rows, &os->old_heights);
1768 sheet_flag_style_update_range (me->cmd.sheet, r);
1770 sheet_redraw_all (me->cmd.sheet, FALSE);
1771 sheet_mark_dirty (me->cmd.sheet);
1773 select_selection (me->cmd.sheet, me->selection, wbc);
1775 return FALSE;
1778 static void
1779 cmd_format_finalize (GObject *cmd)
1781 CmdFormat *me = CMD_FORMAT (cmd);
1782 int i;
1784 if (me->new_style)
1785 gnm_style_unref (me->new_style);
1786 me->new_style = NULL;
1788 if (me->borders) {
1789 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1790 gnm_style_border_unref (me->borders [i]);
1791 g_free (me->borders);
1792 me->borders = NULL;
1795 if (me->old_styles != NULL) {
1796 GSList *l;
1798 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
1799 CmdFormatOldStyle *os = l->data;
1801 style_list_free (os->styles);
1802 colrow_index_list_destroy (os->rows);
1803 colrow_state_group_destroy (os->old_heights);
1804 g_free (os);
1806 me->old_styles = NULL;
1809 range_fragment_free (me->selection);
1810 me->selection = NULL;
1812 gnm_command_finalize (cmd);
1816 * cmd_format: (skip)
1817 * @wbc: the workbook control.
1818 * @sheet: the sheet
1819 * @style: (transfer full): style to apply to the selection
1820 * @borders: (nullable) (transfer full): borders to apply to the selection
1821 * @opt_translated_name: An optional name to use in place of 'Format Cells'
1823 * If borders is non-%NULL, then the GnmBorder references are passed,
1824 * the GnmStyle reference is also passed.
1826 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1828 gboolean
1829 cmd_selection_format (WorkbookControl *wbc,
1830 GnmStyle *style, GnmBorder **borders,
1831 char const *opt_translated_name)
1833 CmdFormat *me;
1834 GSList *l;
1835 SheetView *sv = wb_control_cur_sheet_view (wbc);
1837 me = g_object_new (CMD_FORMAT_TYPE, NULL);
1839 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
1840 me->new_style = style;
1842 me->cmd.sheet = sv_sheet (sv);
1843 me->cmd.size = 1; /* Updated below. */
1845 me->old_styles = NULL;
1846 for (l = me->selection; l; l = l->next) {
1847 GnmRange const *sel_r = l->data;
1848 GnmRange range = *sel_r;
1849 CmdFormatOldStyle *os;
1851 /* Store the containing range to handle borders */
1852 if (borders != NULL) {
1853 if (range.start.col > 0) range.start.col--;
1854 if (range.start.row > 0) range.start.row--;
1855 if (range.end.col < gnm_sheet_get_last_col (me->cmd.sheet)) range.end.col++;
1856 if (range.end.row < gnm_sheet_get_last_row (me->cmd.sheet)) range.end.row++;
1859 os = g_new (CmdFormatOldStyle, 1);
1861 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
1862 os->pos = range.start;
1863 os->rows = NULL;
1864 os->old_heights = NULL;
1866 me->cmd.size += g_slist_length (os->styles);
1867 me->old_styles = g_slist_append (me->old_styles, os);
1870 if (borders) {
1871 int i;
1873 me->borders = g_new (GnmBorder *, GNM_STYLE_BORDER_EDGE_MAX);
1874 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1875 me->borders [i] = borders [i];
1876 } else
1877 me->borders = NULL;
1879 if (opt_translated_name == NULL) {
1880 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
1882 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing format of %s"), names);
1883 g_free (names);
1884 } else
1885 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
1887 return gnm_command_push_undo (wbc, G_OBJECT (me));
1890 /******************************************************************/
1892 static gboolean
1893 cmd_selection_format_toggle_font_style_filter (PangoAttribute *attribute, PangoAttrType *pt)
1895 return ((attribute->klass->type == *pt) ||
1896 ((PANGO_ATTR_RISE == *pt) && (attribute->klass->type == PANGO_ATTR_SCALE)));
1899 typedef struct {
1900 GOUndo *undo;
1901 PangoAttrType pt;
1902 } csftfs;
1904 static GnmValue *
1905 cmd_selection_format_toggle_font_style_cb (GnmCellIter const *iter, csftfs *closure)
1907 if (iter->cell && iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
1908 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
1909 if (fmt && go_format_is_markup (fmt)) {
1910 const PangoAttrList *old_markup =
1911 go_format_get_markup (fmt);
1912 PangoAttrList *new_markup = pango_attr_list_copy ((PangoAttrList *)old_markup);
1913 PangoAttrList *other = pango_attr_list_filter
1914 (new_markup,
1915 (PangoAttrFilterFunc) cmd_selection_format_toggle_font_style_filter,
1916 &closure->pt);
1917 if (other != NULL) {
1918 GnmSheetRange *sr;
1919 GnmRange r;
1920 range_init_cellpos (&r, &iter->pp.eval);
1921 sr = gnm_sheet_range_new (iter->pp.sheet, &r);
1922 closure->undo = go_undo_combine (closure->undo,
1923 sheet_range_set_markup_undo (sr, new_markup));
1925 pango_attr_list_unref (new_markup);
1926 pango_attr_list_unref (other);
1929 return NULL;
1932 gboolean
1933 cmd_selection_format_toggle_font_style (WorkbookControl *wbc,
1934 GnmStyle *style, GnmStyleElement t)
1936 SheetView *sv = wb_control_cur_sheet_view (wbc);
1937 Sheet *sheet = sv->sheet;
1938 GSList *selection = selection_get_ranges (sv, FALSE), *l;
1939 gboolean result;
1940 char *text, *name;
1941 GOUndo *undo = NULL;
1942 GOUndo *redo = NULL;
1943 PangoAttrType pt;
1946 switch (t) {
1947 case MSTYLE_FONT_BOLD:
1948 pt = PANGO_ATTR_WEIGHT;
1949 break;
1950 case MSTYLE_FONT_ITALIC:
1951 pt = PANGO_ATTR_STYLE;
1952 break;
1953 case MSTYLE_FONT_UNDERLINE:
1954 pt = PANGO_ATTR_UNDERLINE;
1955 break;
1956 case MSTYLE_FONT_STRIKETHROUGH:
1957 pt = PANGO_ATTR_STRIKETHROUGH;
1958 break;
1959 case MSTYLE_FONT_SCRIPT:
1960 pt = PANGO_ATTR_RISE; /* and PANGO_ATTR_SCALE (see ) */
1961 break;
1962 default:
1963 pt = PANGO_ATTR_INVALID;
1964 break;
1968 name = undo_range_list_name (sheet, selection);
1969 text = g_strdup_printf (_("Setting Font Style of %s"), name);
1970 g_free (name);
1972 for (l = selection; l != NULL; l = l->next) {
1973 GnmSheetRange *sr;
1974 undo = go_undo_combine
1975 (undo, clipboard_copy_range_undo (sheet, l->data));
1976 sr = gnm_sheet_range_new (sheet, l->data);
1977 redo = go_undo_combine
1978 (redo, sheet_apply_style_undo (sr, style));
1979 if (pt != PANGO_ATTR_INVALID) {
1980 csftfs closure;
1981 closure.undo = NULL;
1982 closure.pt = pt;
1983 sheet_foreach_cell_in_range
1984 (sheet, CELL_ITER_IGNORE_BLANK, &sr->range,
1985 (CellIterFunc) cmd_selection_format_toggle_font_style_cb,
1986 &closure);
1987 redo = go_undo_combine (redo, closure.undo);
1990 gnm_style_unref (style);
1991 result = cmd_generic (wbc, text, undo, redo);
1992 g_free (text);
1993 range_fragment_free (selection);
1995 return result;
1999 /******************************************************************/
2002 gboolean
2003 cmd_resize_colrow (WorkbookControl *wbc, Sheet *sheet,
2004 gboolean is_cols, ColRowIndexList *selection,
2005 int new_size)
2007 int size = 1;
2008 char *text;
2009 GOUndo *undo = NULL;
2010 GOUndo *redo = NULL;
2011 gboolean is_single, result;
2012 GString *list;
2013 ColRowStateGroup *saved_state;
2015 list = colrow_index_list_to_string (selection, is_cols, &is_single);
2016 gnm_cmd_trunc_descriptor (list, NULL);
2018 if (is_single) {
2019 if (new_size < 0)
2020 text = is_cols
2021 ? g_strdup_printf (_("Autofitting column %s"), list->str)
2022 : g_strdup_printf (_("Autofitting row %s"), list->str);
2023 else if (new_size > 0)
2024 text = is_cols
2025 ? g_strdup_printf (ngettext ("Setting width of column %s to %d pixel",
2026 "Setting width of column %s to %d pixels",
2027 new_size),
2028 list->str, new_size)
2029 : g_strdup_printf (ngettext ("Setting height of row %s to %d pixel",
2030 "Setting height of row %s to %d pixels",
2031 new_size),
2032 list->str, new_size);
2033 else text = is_cols
2034 ? g_strdup_printf (_("Setting width of column %s to default"),
2035 list->str)
2036 : g_strdup_printf (
2037 _("Setting height of row %s to default"), list->str);
2038 } else {
2039 if (new_size < 0)
2040 text = is_cols
2041 ? g_strdup_printf (_("Autofitting columns %s"), list->str)
2042 : g_strdup_printf (_("Autofitting rows %s"), list->str);
2043 else if (new_size > 0)
2044 text = is_cols
2045 ? g_strdup_printf (ngettext("Setting width of columns %s to %d pixel",
2046 "Setting width of columns %s to %d pixels",
2047 new_size),
2048 list->str, new_size)
2049 : g_strdup_printf (ngettext("Setting height of rows %s to %d pixel",
2050 "Setting height of rows %s to %d pixels",
2051 new_size),
2052 list->str, new_size);
2053 else text = is_cols
2054 ? g_strdup_printf (
2055 _("Setting width of columns %s to default"), list->str)
2056 : g_strdup_printf (
2057 _("Setting height of rows %s to default"), list->str);
2059 g_string_free (list, TRUE);
2061 saved_state = colrow_get_sizes (sheet, is_cols, selection, new_size);
2062 undo = gnm_undo_colrow_restore_state_group_new
2063 (sheet, is_cols, colrow_index_list_copy (selection), saved_state);
2065 redo = gnm_undo_colrow_set_sizes_new (sheet, is_cols, selection, new_size, NULL);
2067 result = cmd_generic_with_size (wbc, text, size, undo, redo);
2068 g_free (text);
2070 return result;
2073 gboolean
2074 cmd_autofit_selection (WorkbookControl *wbc, SheetView *sv, Sheet *sheet, gboolean fit_width,
2075 ColRowIndexList *selectionlist)
2077 GOUndo *undo = NULL;
2078 GOUndo *redo = NULL;
2079 gboolean result;
2080 ColRowStateGroup *saved_state;
2081 GSList *l, *selection = selection_get_ranges (sv, TRUE);
2082 gchar *names = undo_range_list_name (sheet, selection);
2083 gchar const *format = fit_width ?
2084 N_("Autofitting width of %s") : N_("Autofitting height of %s");
2085 gchar *text = g_strdup_printf (_(format), names);
2087 g_free (names);
2089 saved_state = colrow_get_sizes (sheet, fit_width, selectionlist, -1);
2090 undo = gnm_undo_colrow_restore_state_group_new
2091 (sheet, fit_width, colrow_index_list_copy (selectionlist), saved_state);
2093 for (l = selection; l != NULL; l = l->next)
2094 redo = go_undo_combine
2095 (redo, gnm_undo_colrow_set_sizes_new
2096 (sheet, fit_width, NULL, -1, l->data));
2098 result = cmd_generic (wbc, text, undo, redo);
2099 g_free (text);
2100 return result;
2104 /******************************************************************/
2106 #define CMD_SORT_TYPE (cmd_sort_get_type ())
2107 #define CMD_SORT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SORT_TYPE, CmdSort))
2109 typedef struct {
2110 GnmCommand cmd;
2112 GnmSortData *data;
2113 int *perm;
2114 GnmCellRegion *old_contents;
2115 } CmdSort;
2117 MAKE_GNM_COMMAND (CmdSort, cmd_sort, NULL)
2119 static void
2120 cmd_sort_finalize (GObject *cmd)
2122 CmdSort *me = CMD_SORT (cmd);
2124 if (me->data != NULL)
2125 gnm_sort_data_destroy (me->data);
2126 g_free (me->perm);
2127 if (me->old_contents != NULL)
2128 cellregion_unref (me->old_contents);
2130 gnm_command_finalize (cmd);
2133 static gboolean
2134 cmd_sort_undo (GnmCommand *cmd, WorkbookControl *wbc)
2136 CmdSort *me = CMD_SORT (cmd);
2137 GnmSortData *data = me->data;
2138 GnmPasteTarget pt;
2140 paste_target_init (&pt, data->sheet, data->range,
2141 PASTE_CONTENTS | PASTE_FORMATS |
2142 (data->retain_formats ? PASTE_FORMATS : 0));
2143 clipboard_paste_region (me->old_contents,
2144 &pt,
2145 GO_CMD_CONTEXT (wbc));
2147 return FALSE;
2150 static gboolean
2151 cmd_sort_redo (GnmCommand *cmd, WorkbookControl *wbc)
2153 CmdSort *me = CMD_SORT (cmd);
2154 GnmSortData *data = me->data;
2156 /* Check for locks */
2157 if (cmd_cell_range_is_locked_effective
2158 (data->sheet, data->range, wbc, _("Sorting")))
2159 return TRUE;
2161 if (me->perm)
2162 gnm_sort_position (data, me->perm, GO_CMD_CONTEXT (wbc));
2163 else {
2164 me->old_contents =
2165 clipboard_copy_range (data->sheet, data->range);
2166 me->cmd.size = cellregion_cmd_size (me->old_contents);
2167 me->perm = gnm_sort_contents (data, GO_CMD_CONTEXT (wbc));
2170 return FALSE;
2173 gboolean
2174 cmd_sort (WorkbookControl *wbc, GnmSortData *data)
2176 CmdSort *me;
2177 char *desc;
2179 g_return_val_if_fail (data != NULL, TRUE);
2181 desc = g_strdup_printf (_("Sorting %s"), range_as_string (data->range));
2182 if (sheet_range_contains_merges_or_arrays (data->sheet, data->range, GO_CMD_CONTEXT (wbc), desc, TRUE, TRUE)) {
2183 gnm_sort_data_destroy (data);
2184 g_free (desc);
2185 return TRUE;
2188 me = g_object_new (CMD_SORT_TYPE, NULL);
2190 me->data = data;
2191 me->perm = NULL;
2192 me->cmd.sheet = data->sheet;
2193 me->cmd.size = 1; /* Changed in initial redo. */
2194 me->cmd.cmd_descriptor = desc;
2196 return gnm_command_push_undo (wbc, G_OBJECT (me));
2199 /******************************************************************/
2201 #define CMD_COLROW_HIDE_TYPE (cmd_colrow_hide_get_type ())
2202 #define CMD_COLROW_HIDE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_HIDE_TYPE, CmdColRowHide))
2204 typedef struct {
2205 GnmCommand cmd;
2207 gboolean is_cols;
2208 ColRowVisList *hide, *show;
2209 } CmdColRowHide;
2211 static void
2212 cmd_colrow_hide_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2214 CmdColRowHide const *orig = (CmdColRowHide const *) cmd;
2215 cmd_selection_colrow_hide (wbc, orig->is_cols, orig->show != NULL);
2217 MAKE_GNM_COMMAND (CmdColRowHide, cmd_colrow_hide, cmd_colrow_hide_repeat)
2220 * cmd_colrow_hide_correct_selection:
2222 * Try to ensure that the selection/cursor is set to a visible row/col
2224 * Added to fix bug 38179
2225 * Removed because the result is irritating and the bug is actually XL
2226 * compatibile
2228 static void
2229 cmd_colrow_hide_correct_selection (G_GNUC_UNUSED CmdColRowHide *me, G_GNUC_UNUSED WorkbookControl *wbc)
2231 #if 0
2232 int x, y, index;
2233 SheetView *sv = sheet_get_view (me->cmd.sheet,
2234 wb_control_view (wbc));
2236 index = colrow_find_adjacent_visible (me->cmd.sheet, me->is_cols,
2237 me->is_cols ? sv->edit_pos.col : sv->edit_pos.row,
2238 TRUE);
2240 x = me->is_cols ? sv->edit_pos.row : index;
2241 y = me->is_cols ? index : sv->edit_pos.col;
2243 if (index >= 0) {
2244 sv_selection_reset (sv);
2245 if (me->is_cols)
2246 sv_selection_add_full (sv, y, x, y, 0,
2247 y, gnm_sheet_get_last_row (sheet),
2248 GNM_SELECTION_MODE_ADD);
2249 else
2250 sv_selection_add_full (sv, y, x, 0, x,
2251 gnm_sheet_get_last_col (sheet), x,
2252 GNM_SELECTION_MODE_ADD);
2254 #endif
2257 static gboolean
2258 cmd_colrow_hide_undo (GnmCommand *cmd, WorkbookControl *wbc)
2260 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2262 g_return_val_if_fail (me != NULL, TRUE);
2264 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2265 TRUE, me->hide);
2266 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2267 FALSE, me->show);
2269 if (me->show != NULL)
2270 cmd_colrow_hide_correct_selection (me, wbc);
2272 return FALSE;
2275 static gboolean
2276 cmd_colrow_hide_redo (GnmCommand *cmd, WorkbookControl *wbc)
2278 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2280 g_return_val_if_fail (me != NULL, TRUE);
2282 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2283 FALSE, me->hide);
2284 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2285 TRUE, me->show);
2287 if (me->hide != NULL)
2288 cmd_colrow_hide_correct_selection (me, wbc);
2290 return FALSE;
2293 static void
2294 cmd_colrow_hide_finalize (GObject *cmd)
2296 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2297 colrow_vis_list_destroy (me->hide);
2298 me->hide = NULL;
2299 colrow_vis_list_destroy (me->show);
2300 me->show = NULL;
2301 gnm_command_finalize (cmd);
2304 gboolean
2305 cmd_selection_colrow_hide (WorkbookControl *wbc,
2306 gboolean is_cols, gboolean visible)
2308 CmdColRowHide *me;
2309 SheetView *sv = wb_control_cur_sheet_view (wbc);
2310 int n;
2311 Sheet *sheet;
2312 GSList *show = NULL, *hide = NULL;
2314 if (visible)
2315 show = colrow_get_visibility_toggle (sv, is_cols, TRUE);
2316 else
2317 hide = colrow_get_visibility_toggle (sv, is_cols, FALSE);
2318 n = colrow_vis_list_length (hide) + colrow_vis_list_length (show);
2319 sheet = sv_sheet (sv);
2321 if (!visible) {
2322 /* If these are the last colrows to hide, check with the user */
2323 int count = 0;
2324 if (is_cols) {
2325 int i, max = gnm_sheet_get_max_cols (sheet);
2326 ColRowInfo *ci;
2327 for (i = 0 ; i < max ; i++)
2328 if (NULL ==
2329 (ci = sheet_col_get (sheet, i)) ||
2330 (ci->visible))
2331 count++;
2332 } else {
2333 int i, max = gnm_sheet_get_max_rows (sheet);
2334 ColRowInfo *ci;
2335 for (i = 0 ; i < max ; i++)
2336 if (NULL ==
2337 (ci = sheet_row_get (sheet, i)) ||
2338 (ci->visible))
2339 count++;
2341 if (count <= n) {
2342 gchar const *text = is_cols ?
2343 _("Are you sure that you want to hide all columns? "
2344 "If you do so you can unhide them with the "
2345 "'Format\342\206\222Column\342\206\222Unhide' "
2346 "menu item.") :
2347 _("Are you sure that you want to hide all rows? "
2348 "If you do so you can unhide them with the "
2349 "'Format\342\206\222Row\342\206\222Unhide' "
2350 "menu item.");
2351 if (!go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)),
2352 FALSE, "%s", text)) {
2353 colrow_vis_list_destroy (show);
2354 colrow_vis_list_destroy (hide);
2355 return TRUE;
2360 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2361 me->show = show;
2362 me->hide = hide;
2363 me->is_cols = is_cols;
2364 me->cmd.sheet = sheet;
2365 me->cmd.size = 1 + g_slist_length (hide) + g_slist_length (show);
2366 me->cmd.cmd_descriptor = g_strdup (is_cols
2367 ? (visible ? _("Unhide columns") : _("Hide columns"))
2368 : (visible ? _("Unhide rows") : _("Hide rows")));
2370 return gnm_command_push_undo (wbc, G_OBJECT (me));
2373 gboolean
2374 cmd_selection_outline_change (WorkbookControl *wbc,
2375 gboolean is_cols, int index, int depth)
2377 CmdColRowHide *me;
2378 ColRowInfo const *cri;
2379 int first = -1, last = -1;
2380 gboolean visible = FALSE;
2381 int d;
2382 Sheet *sheet = wb_control_cur_sheet (wbc);
2383 SheetView *sv = wb_control_cur_sheet_view (wbc);
2385 cri = sheet_colrow_get_info (sheet, index, is_cols);
2387 d = cri->outline_level;
2388 if (depth > d)
2389 depth = d;
2391 /* Nodes only collapse when selected directly, selecting at a lower
2392 * level is a standard toggle. */
2393 if (depth == d) {
2394 if ((is_cols ? sheet->outline_symbols_right : sheet->outline_symbols_below)) {
2395 if (index > 0) {
2396 ColRowInfo const *prev =
2397 sheet_colrow_get (sheet, index-1, is_cols);
2399 if (prev != NULL && prev->outline_level > d) {
2400 visible = (depth == d && cri->is_collapsed);
2401 last = index - 1;
2402 first = colrow_find_outline_bound (sheet, is_cols,
2403 last, d+1, FALSE);
2406 } else if (index+1 < colrow_max (is_cols, sheet)) {
2407 ColRowInfo const *next =
2408 sheet_colrow_get (sheet, index+1, is_cols);
2410 if (next != NULL && next->outline_level > d) {
2411 visible = (depth == d && cri->is_collapsed);
2412 first = index + 1;
2413 last = colrow_find_outline_bound (sheet, is_cols,
2414 first, d+1, TRUE);
2419 /* If nothing done yet do a simple collapse */
2420 if (first < 0 && cri->outline_level > 0) {
2421 if (depth < d)
2422 ++depth;
2423 first = colrow_find_outline_bound (sheet, is_cols, index, depth, FALSE);
2424 last = colrow_find_outline_bound (sheet, is_cols, index, depth, TRUE);
2425 visible = FALSE;
2427 if (first == last && depth > cri->outline_level)
2428 return TRUE;
2431 if (first < 0 || last < 0)
2432 return TRUE;
2434 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2436 me->is_cols = is_cols;
2437 me->hide = me->show = NULL;
2438 if (visible)
2439 me->show = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2440 TRUE, first, last);
2441 else
2442 me->hide = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2443 FALSE, first, last);
2445 me->cmd.sheet = sv_sheet (sv);
2446 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2447 me->cmd.cmd_descriptor = g_strdup (is_cols
2448 ? (visible ? _("Expand columns") : _("Collapse columns"))
2449 : (visible ? _("Expand rows") : _("Collapse rows")));
2451 return gnm_command_push_undo (wbc, G_OBJECT (me));
2454 gboolean
2455 cmd_global_outline_change (WorkbookControl *wbc, gboolean is_cols, int depth)
2457 CmdColRowHide *me;
2458 ColRowVisList *hide, *show;
2459 SheetView *sv = wb_control_cur_sheet_view (wbc);
2461 colrow_get_global_outline (sv_sheet (sv), is_cols, depth, &show, &hide);
2463 if (show == NULL && hide == NULL)
2464 return TRUE;
2466 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2467 me->is_cols = is_cols;
2468 me->hide = hide;
2469 me->show = show;
2470 me->cmd.sheet = sv_sheet (sv);
2471 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2472 me->cmd.cmd_descriptor = g_strdup_printf (is_cols
2473 ? _("Show column outline %d") : _("Show row outline %d"), depth);
2475 return gnm_command_push_undo (wbc, G_OBJECT (me));
2478 /******************************************************************/
2480 #define CMD_GROUP_TYPE (cmd_group_get_type ())
2481 #define CMD_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GROUP_TYPE, CmdGroup))
2483 typedef struct {
2484 GnmCommand cmd;
2486 GnmRange range;
2487 gboolean is_cols;
2488 gboolean group;
2489 } CmdGroup;
2491 static void
2492 cmd_group_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2494 CmdGroup const *orig = (CmdGroup const *) cmd;
2495 cmd_selection_group (wbc, orig->is_cols, orig->group);
2497 MAKE_GNM_COMMAND (CmdGroup, cmd_group, cmd_group_repeat)
2499 static gboolean
2500 cmd_group_undo (GnmCommand *cmd,
2501 G_GNUC_UNUSED WorkbookControl *wbc)
2503 CmdGroup const *me = CMD_GROUP (cmd);
2504 sheet_colrow_group_ungroup (me->cmd.sheet,
2505 &me->range, me->is_cols, !me->group);
2506 return FALSE;
2509 static gboolean
2510 cmd_group_redo (GnmCommand *cmd,
2511 G_GNUC_UNUSED WorkbookControl *wbc)
2513 CmdGroup const *me = CMD_GROUP (cmd);
2514 sheet_colrow_group_ungroup (me->cmd.sheet,
2515 &me->range, me->is_cols, me->group);
2516 return FALSE;
2519 static void
2520 cmd_group_finalize (GObject *cmd)
2522 gnm_command_finalize (cmd);
2525 gboolean
2526 cmd_selection_group (WorkbookControl *wbc,
2527 gboolean is_cols, gboolean group)
2529 CmdGroup *me;
2530 SheetView *sv;
2531 GnmRange r;
2533 g_return_val_if_fail (wbc != NULL, TRUE);
2535 sv = wb_control_cur_sheet_view (wbc);
2536 r = *selection_first_range (sv, NULL, NULL);
2538 /* Check if this really is possible and display an error if it's not */
2539 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2540 if (group) {
2541 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2542 ? _("Those columns are already grouped")
2543 : _("Those rows are already grouped"));
2544 return TRUE;
2547 /* see if the user selected the col/row with the marker too */
2548 if (is_cols) {
2549 if (r.start.col != r.end.col) {
2550 if (sv->sheet->outline_symbols_right)
2551 r.end.col--;
2552 else
2553 r.start.col++;
2555 } else {
2556 if (r.start.row != r.end.row) {
2557 if (sv->sheet->outline_symbols_below)
2558 r.end.row--;
2559 else
2560 r.start.row++;
2564 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2565 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2566 ? _("Those columns are not grouped, you can't ungroup them")
2567 : _("Those rows are not grouped, you can't ungroup them"));
2568 return TRUE;
2572 me = g_object_new (CMD_GROUP_TYPE, NULL);
2573 me->is_cols = is_cols;
2574 me->group = group;
2575 me->range = r;
2577 me->cmd.sheet = sv->sheet;
2578 me->cmd.size = 1;
2579 me->cmd.cmd_descriptor = is_cols
2580 ? g_strdup_printf (group ? _("Group columns %s") : _("Ungroup columns %s"),
2581 cols_name (me->range.start.col, me->range.end.col))
2582 : g_strdup_printf (group ? _("Group rows %d:%d") : _("Ungroup rows %d:%d"),
2583 me->range.start.row + 1, me->range.end.row + 1);
2585 return gnm_command_push_undo (wbc, G_OBJECT (me));
2588 /******************************************************************/
2590 #define CMD_PASTE_CUT_TYPE (cmd_paste_cut_get_type ())
2591 #define CMD_PASTE_CUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_CUT_TYPE, CmdPasteCut))
2593 typedef struct {
2594 GnmCommand cmd;
2596 GnmExprRelocateInfo info;
2597 GSList *paste_contents;
2598 GOUndo *reloc_undo;
2599 gboolean move_selection;
2600 ColRowStateList *saved_sizes;
2602 /* handle redo-ing an undo with contents from a deleted sheet */
2603 GnmCellRegion *deleted_sheet_contents;
2604 } CmdPasteCut;
2606 MAKE_GNM_COMMAND (CmdPasteCut, cmd_paste_cut, NULL)
2608 typedef struct {
2609 GnmPasteTarget pt;
2610 GnmCellRegion *contents;
2611 } PasteContent;
2614 * cmd_paste_cut_update:
2616 * Utility routine to update things when we are transfering between sheets and
2617 * workbooks.
2619 static void
2620 cmd_paste_cut_update (GnmExprRelocateInfo const *info,
2621 G_GNUC_UNUSED WorkbookControl *wbc)
2623 Sheet *o = info->origin_sheet;
2624 Sheet *t = info->target_sheet;
2626 /* Dirty and update both sheets */
2627 sheet_mark_dirty (t);
2628 sheet_update (t);
2630 if (IS_SHEET (o) && o != t) {
2631 sheet_mark_dirty (o);
2632 sheet_update (o);
2636 static gboolean
2637 cmd_paste_cut_undo (GnmCommand *cmd, WorkbookControl *wbc)
2639 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2640 GnmExprRelocateInfo reverse;
2642 g_return_val_if_fail (me != NULL, TRUE);
2643 g_return_val_if_fail (me->paste_contents != NULL, TRUE);
2644 g_return_val_if_fail (me->deleted_sheet_contents == NULL, TRUE);
2646 reverse.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
2647 reverse.target_sheet = me->info.origin_sheet;
2648 reverse.origin_sheet = me->info.target_sheet;
2649 reverse.origin = me->info.origin;
2650 range_translate (&reverse.origin,
2651 me->info.origin_sheet, /* FIXME: What sheet? */
2652 me->info.col_offset,
2653 me->info.row_offset);
2654 reverse.col_offset = -me->info.col_offset;
2655 reverse.row_offset = -me->info.row_offset;
2657 /* Move things back being careful NOT to invalidate the src region */
2658 if (IS_SHEET (me->info.origin_sheet))
2659 sheet_move_range (&reverse, NULL, GO_CMD_CONTEXT (wbc));
2660 else
2661 me->deleted_sheet_contents = clipboard_copy_range (
2662 reverse.origin_sheet, &reverse.origin);
2664 /* Restore the original row heights */
2665 colrow_set_states (me->info.target_sheet, FALSE,
2666 reverse.origin.start.row, me->saved_sizes);
2667 colrow_state_list_destroy (me->saved_sizes);
2668 me->saved_sizes = NULL;
2670 if (me->reloc_undo) {
2671 go_undo_undo (me->reloc_undo);
2672 g_object_unref (me->reloc_undo);
2673 me->reloc_undo = NULL;
2676 while (me->paste_contents) {
2677 PasteContent *pc = me->paste_contents->data;
2678 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2680 clipboard_paste_region (pc->contents, &pc->pt, GO_CMD_CONTEXT (wbc));
2681 cellregion_unref (pc->contents);
2682 g_free (pc);
2685 /* Force update of the status area */
2686 sheet_flag_status_update_range (me->info.target_sheet, NULL);
2688 cmd_paste_cut_update (&me->info, wbc);
2690 /* Select the original region */
2691 if (me->move_selection && IS_SHEET (me->info.origin_sheet))
2692 select_range (me->info.origin_sheet,
2693 &me->info.origin,
2694 wbc);
2696 return FALSE;
2699 static gboolean
2700 cmd_paste_cut_redo (GnmCommand *cmd, WorkbookControl *wbc)
2702 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2703 GnmRange tmp;
2705 g_return_val_if_fail (me != NULL, TRUE);
2706 g_return_val_if_fail (me->paste_contents == NULL, TRUE);
2708 tmp = me->info.origin;
2709 range_translate (&tmp, me->info.origin_sheet, /* FIXME: What sheet? */
2710 me->info.col_offset, me->info.row_offset);
2711 range_normalize (&tmp);
2713 g_return_val_if_fail (range_is_sane (&tmp), TRUE);
2715 if (me->info.origin_sheet != me->info.target_sheet ||
2716 !range_overlap (&me->info.origin, &tmp)) {
2717 PasteContent *pc = g_new (PasteContent, 1);
2718 paste_target_init (&pc->pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2719 pc->contents = clipboard_copy_range (me->info.target_sheet, &tmp);
2720 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2721 } else {
2722 /* need to store any portions of the paste target
2723 * that do not overlap with the source.
2725 GSList *ptr, *frag = range_split_ranges (&me->info.origin, &tmp);
2726 for (ptr = frag ; ptr != NULL ; ptr = ptr->next) {
2727 GnmRange *r = ptr->data;
2729 if (!range_overlap (&me->info.origin, r)) {
2730 PasteContent *pc = g_new (PasteContent, 1);
2731 paste_target_init (&pc->pt, me->info.target_sheet, r, PASTE_ALL_SHEET);
2732 pc->contents = clipboard_copy_range (me->info.target_sheet, r);
2733 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2735 g_free (r);
2737 g_slist_free (frag);
2740 /* rare corner case. If the origin sheet has been deleted */
2741 if (!IS_SHEET (me->info.origin_sheet)) {
2742 GnmPasteTarget pt;
2743 paste_target_init (&pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2744 sheet_clear_region (pt.sheet,
2745 tmp.start.col, tmp.start.row, tmp.end.col, tmp.end.row,
2746 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
2747 GO_CMD_CONTEXT (wbc));
2748 clipboard_paste_region (me->deleted_sheet_contents,
2749 &pt, GO_CMD_CONTEXT (wbc));
2750 cellregion_unref (me->deleted_sheet_contents);
2751 me->deleted_sheet_contents = NULL;
2752 } else
2753 sheet_move_range (&me->info, &me->reloc_undo, GO_CMD_CONTEXT (wbc));
2755 cmd_paste_cut_update (&me->info, wbc);
2757 /* Backup row heights and adjust row heights to fit */
2758 me->saved_sizes = colrow_get_states (me->info.target_sheet, FALSE, tmp.start.row, tmp.end.row);
2759 rows_height_update (me->info.target_sheet, &tmp, FALSE);
2761 /* Make sure the destination is selected */
2762 if (me->move_selection)
2763 select_range (me->info.target_sheet, &tmp, wbc);
2765 return FALSE;
2768 static void
2769 cmd_paste_cut_finalize (GObject *cmd)
2771 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2773 if (me->saved_sizes)
2774 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
2775 while (me->paste_contents) {
2776 PasteContent *pc = me->paste_contents->data;
2777 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2778 cellregion_unref (pc->contents);
2779 g_free (pc);
2781 if (me->reloc_undo) {
2782 g_object_unref (me->reloc_undo);
2783 me->reloc_undo = NULL;
2785 if (me->deleted_sheet_contents) {
2786 cellregion_unref (me->deleted_sheet_contents);
2787 me->deleted_sheet_contents = NULL;
2790 gnm_command_finalize (cmd);
2793 gboolean
2794 cmd_paste_cut (WorkbookControl *wbc, GnmExprRelocateInfo const *info,
2795 gboolean move_selection, char *descriptor)
2797 CmdPasteCut *me;
2798 GnmRange r;
2799 char *where;
2801 g_return_val_if_fail (info != NULL, TRUE);
2803 /* This is vacuous */
2804 if (info->origin_sheet == info->target_sheet &&
2805 info->col_offset == 0 && info->row_offset == 0)
2806 return TRUE;
2808 /* FIXME: Do we want to show the destination range as well ? */
2809 where = undo_range_name (info->origin_sheet, &info->origin);
2810 if (descriptor == NULL)
2811 descriptor = g_strdup_printf (_("Moving %s"), where);
2812 g_free (where);
2814 g_return_val_if_fail (info != NULL, TRUE);
2816 r = info->origin;
2817 if (range_translate (&r, info->target_sheet,
2818 info->col_offset, info->row_offset)) {
2820 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), descriptor,
2821 _("is beyond sheet boundaries"));
2822 g_free (descriptor);
2823 return TRUE;
2826 /* Check array subdivision & merged regions */
2827 if (sheet_range_splits_region (info->target_sheet, &r,
2828 (info->origin_sheet == info->target_sheet)
2829 ? &info->origin : NULL, GO_CMD_CONTEXT (wbc), descriptor)) {
2830 g_free (descriptor);
2831 return TRUE;
2834 me = g_object_new (CMD_PASTE_CUT_TYPE, NULL);
2836 me->info = *info;
2837 me->paste_contents = NULL;
2838 me->deleted_sheet_contents = NULL;
2839 me->reloc_undo = NULL;
2840 me->move_selection = move_selection;
2841 me->saved_sizes = NULL;
2843 me->cmd.sheet = NULL; /* we have potentially two different. */
2844 me->cmd.size = 1; /* FIXME? */
2845 me->cmd.cmd_descriptor = descriptor;
2847 /* NOTE : if the destination workbook is different from the source
2848 * workbook should we have undo elements in both menus ?? It seems
2849 * poor form to hit undo in 1 window and effect another...
2851 * Maybe queue it as two different commands, as a clear in one book
2852 * and a paste in the other. This is not symmetric though. What
2853 * happens to the cells in the original sheet that now reference the
2854 * cells in the other? When do they reset to the original?
2856 * Probably when the clear in the original is undone.
2859 return gnm_command_push_undo (wbc, G_OBJECT (me));
2862 /******************************************************************/
2864 static void
2865 warn_if_date_trouble (WorkbookControl *wbc, GnmCellRegion *cr)
2867 Workbook *wb = wb_control_get_workbook (wbc);
2868 const GODateConventions *wb_date_conv = workbook_date_conv (wb);
2870 if (cr->date_conv == NULL)
2871 return;
2872 if (go_date_conv_equal (cr->date_conv, wb_date_conv))
2873 return;
2875 /* We would like to show a warning, but it seems we cannot via a context. */
2877 GError *err;
2878 err = g_error_new (go_error_invalid(), 0,
2879 _("Copying between files with different date conventions.\n"
2880 "It is possible that some dates could be copied\n"
2881 "incorrectly."));
2882 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
2883 g_error_free (err);
2888 #define CMD_PASTE_COPY_TYPE (cmd_paste_copy_get_type ())
2889 #define CMD_PASTE_COPY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_COPY_TYPE, CmdPasteCopy))
2891 typedef struct {
2892 GnmCommand cmd;
2894 GnmCellRegion *contents;
2895 GSList *pasted_objects,*orig_contents_objects;
2896 GnmPasteTarget dst;
2897 gboolean has_been_through_cycle;
2898 gboolean only_objects;
2899 gboolean single_merge_to_single_merge;
2900 } CmdPasteCopy;
2902 static void
2903 cmd_paste_copy_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2905 CmdPasteCopy const *orig = (CmdPasteCopy const *) cmd;
2906 GnmPasteTarget new_dst;
2907 SheetView *sv = wb_control_cur_sheet_view (wbc);
2908 GnmRange const *r = selection_first_range (sv,
2909 GO_CMD_CONTEXT (wbc), _("Paste Copy"));
2910 GnmCellRegion *newcr;
2912 if (r == NULL)
2913 return;
2915 paste_target_init (&new_dst, sv_sheet (sv), r, orig->dst.paste_flags);
2916 newcr = clipboard_copy_range (orig->dst.sheet, &orig->dst.range);
2917 cmd_paste_copy (wbc, &new_dst, newcr);
2918 cellregion_unref (newcr);
2920 MAKE_GNM_COMMAND (CmdPasteCopy, cmd_paste_copy, cmd_paste_copy_repeat)
2922 static int
2923 by_addr (gconstpointer a, gconstpointer b)
2925 if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b))
2926 return -1;
2927 if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b))
2928 return +1;
2929 return 0;
2933 * get_new_objects:
2934 * @sheet: #Sheet to query
2935 * @old: (element-type SheetObject): list of objects to disregard
2937 * Returns: (transfer full) (element-type SheetObject): A list of new objects
2938 * in sheet since @old was collected.
2940 static GSList *
2941 get_new_objects (Sheet *sheet, GSList *old)
2943 GSList *objs =
2944 g_slist_sort (g_slist_copy_deep (sheet->sheet_objects,
2945 (GCopyFunc)g_object_ref,
2946 NULL),
2947 by_addr);
2948 GSList *p = objs, *last = NULL;
2950 while (old) {
2951 int c = -1;
2952 while (p && (c = by_addr (p->data, old->data)) < 0) {
2953 last = p;
2954 p = p->next;
2957 old = old->next;
2959 if (c == 0) {
2960 GSList *next = p->next;
2961 if (last)
2962 last->next = next;
2963 else
2964 objs = next;
2965 g_slist_free_1 (p);
2966 p = next;
2970 return objs;
2973 static void
2974 cmd_paste_copy_select_obj (SheetObject *so, SheetControlGUI *scg)
2976 scg_object_select (scg, so);
2979 static gboolean
2980 cmd_paste_copy_impl (GnmCommand *cmd, WorkbookControl *wbc,
2981 gboolean is_undo)
2983 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
2984 GnmCellRegion *contents;
2985 GSList *old_objects;
2987 g_return_val_if_fail (me != NULL, TRUE);
2988 g_return_val_if_fail (me->contents != NULL, TRUE);
2990 g_slist_foreach (me->pasted_objects,
2991 (GFunc)sheet_object_clear_sheet,
2992 NULL);
2993 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
2994 me->pasted_objects = NULL;
2995 old_objects = get_new_objects (me->dst.sheet, NULL);
2997 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
2998 if (me->has_been_through_cycle)
2999 me->dst.paste_flags =
3000 PASTE_CONTENTS |
3001 PASTE_COLUMN_WIDTHS | PASTE_ROW_HEIGHTS |
3002 (me->dst.paste_flags & PASTE_ALL_SHEET);
3004 if (clipboard_paste_region (me->contents, &me->dst,
3005 GO_CMD_CONTEXT (wbc))) {
3006 /* There was a problem, avoid leaking */
3007 cellregion_unref (contents);
3008 g_slist_free_full (old_objects, g_object_unref);
3009 return TRUE;
3012 me->pasted_objects = get_new_objects (me->dst.sheet, old_objects);
3013 g_slist_free_full (old_objects, g_object_unref);
3015 if (!is_undo && !me->has_been_through_cycle) {
3016 colrow_autofit (me->dst.sheet, &me->dst.range, FALSE, FALSE,
3017 TRUE, FALSE,
3018 NULL, NULL);
3019 colrow_autofit (me->dst.sheet, &me->dst.range, TRUE, TRUE,
3020 TRUE, FALSE,
3021 NULL, NULL);
3024 if (is_undo) {
3025 // We cannot use the random set of objects at the target
3026 // location. http://bugzilla.gnome.org/show_bug.cgi?id=308300
3027 g_slist_free_full (contents->objects, g_object_unref);
3028 contents->objects = g_slist_copy_deep
3029 (me->orig_contents_objects,
3030 (GCopyFunc)sheet_object_dup, NULL);
3031 } else {
3032 GSList *l;
3033 for (l = contents->objects; l; l = l->next) {
3034 SheetObject *so = l->data;
3035 if (sheet_object_get_sheet (so)) {
3036 g_object_unref (so);
3037 l->data = NULL;
3038 } else {
3039 // Object got deleted by paste, so keep it for
3040 // undo. See bugzilla 732653
3043 contents->objects =
3044 g_slist_remove_all (contents->objects, NULL);
3047 cellregion_unref (me->contents);
3048 me->contents = contents;
3049 me->has_been_through_cycle = TRUE;
3051 /* Select the newly pasted contents (this queues a redraw) */
3052 if (me->only_objects && GNM_IS_WBC_GTK (wbc)) {
3053 SheetControlGUI *scg =
3054 wbcg_get_nth_scg (WBC_GTK (wbc),
3055 cmd->sheet->index_in_wb);
3056 scg_object_unselect (scg, NULL);
3057 g_slist_foreach (me->pasted_objects,
3058 (GFunc) cmd_paste_copy_select_obj, scg);
3060 select_range (me->dst.sheet, &me->dst.range, wbc);
3062 return FALSE;
3065 static gboolean
3066 cmd_paste_copy_undo (GnmCommand *cmd, WorkbookControl *wbc)
3068 return cmd_paste_copy_impl (cmd, wbc, TRUE);
3071 static gboolean
3072 cmd_paste_copy_redo (GnmCommand *cmd, WorkbookControl *wbc)
3074 return cmd_paste_copy_impl (cmd, wbc, FALSE);
3077 static void
3078 cmd_paste_copy_finalize (GObject *cmd)
3080 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
3082 if (me->contents) {
3083 cellregion_unref (me->contents);
3084 me->contents = NULL;
3086 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
3087 g_slist_free_full (me->orig_contents_objects, (GDestroyNotify)g_object_unref);
3088 gnm_command_finalize (cmd);
3092 * cmd_paste_copy:
3093 * @wbc:
3094 * @pt:
3095 * @cr: (transfer none):
3097 * Returns: %TRUE if there was a problem, %FALSE otherwise.
3099 gboolean
3100 cmd_paste_copy (WorkbookControl *wbc,
3101 GnmPasteTarget const *pt, GnmCellRegion *cr)
3103 CmdPasteCopy *me;
3104 int n_r = 1, n_c = 1;
3105 char *range_name;
3106 GnmRange const *merge_src;
3108 g_return_val_if_fail (pt != NULL, TRUE);
3109 g_return_val_if_fail (IS_SHEET (pt->sheet), TRUE);
3110 g_return_val_if_fail (cr != NULL, TRUE);
3112 cellregion_ref (cr);
3114 me = g_object_new (CMD_PASTE_COPY_TYPE, NULL);
3116 me->cmd.sheet = pt->sheet;
3117 me->cmd.size = 1; /* FIXME? */
3119 range_name = undo_range_name (pt->sheet, &pt->range);
3120 me->cmd.cmd_descriptor = g_strdup_printf (_("Pasting into %s"),
3121 range_name);
3122 g_free (range_name);
3124 me->dst = *pt;
3125 me->contents = cr;
3126 me->has_been_through_cycle = FALSE;
3127 me->only_objects = (cr->cols < 1 || cr->rows < 1);
3128 me->pasted_objects = NULL;
3129 me->orig_contents_objects =
3130 g_slist_copy_deep (cr->objects,
3131 (GCopyFunc)sheet_object_dup, NULL);
3132 me->single_merge_to_single_merge = FALSE;
3134 /* If the input is only objects ignore all this range stuff */
3135 if (!me->only_objects) {
3136 /* see if we need to do any tiling */
3137 GnmRange *r = &me->dst.range;
3138 if (g_slist_length (cr->merged) == 1 &&
3139 (NULL != (merge_src = cr->merged->data)) &&
3140 range_height (merge_src) == cr->rows &&
3141 range_width (merge_src) == cr->cols) {
3142 /* We are copying from a single merge */
3143 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3144 if (merge != NULL && range_equal (r, merge)) {
3145 /* To a single merge */
3146 me->single_merge_to_single_merge = TRUE;
3147 n_c = n_r = 1;
3148 me->dst.paste_flags |= PASTE_DONT_MERGE;
3149 goto copy_ready;
3153 if (pt->paste_flags & PASTE_TRANSPOSE) {
3154 n_c = range_width (r) / cr->rows;
3155 if (n_c < 1) n_c = 1;
3156 r->end.col = r->start.col + n_c * cr->rows - 1;
3158 n_r = range_height (r) / cr->cols;
3159 if (n_r < 1) n_r = 1;
3160 r->end.row = r->start.row + n_r * cr->cols - 1;
3161 } else {
3162 /* Before looking for tiling if we are not transposing,
3163 * allow pasting a full col or row from a single cell */
3164 n_c = range_width (r);
3165 if (n_c == 1 && cr->cols == gnm_sheet_get_max_cols (me->cmd.sheet)) {
3166 r->start.col = 0;
3167 r->end.col = gnm_sheet_get_last_col (me->cmd.sheet);
3168 } else {
3169 n_c /= cr->cols;
3170 if (n_c < 1) n_c = 1;
3171 r->end.col = r->start.col + n_c * cr->cols - 1;
3174 n_r = range_height (r);
3175 if (n_r == 1 && cr->rows == gnm_sheet_get_max_rows (me->cmd.sheet)) {
3176 r->start.row = 0;
3177 r->end.row = gnm_sheet_get_last_row (me->cmd.sheet);
3178 } else {
3179 n_r /= cr->rows;
3180 if (n_r < 1) n_r = 1;
3181 r->end.row = r->start.row + n_r * cr->rows - 1;
3185 if (cr->cols != 1 || cr->rows != 1) {
3186 /* Note: when the source is a single cell, a single target merge is special */
3187 /* see clipboard.c (clipboard_paste_region) */
3188 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3189 if (merge != NULL && range_equal (r, merge)) {
3190 /* destination is a single merge */
3191 /* enlarge it such that the source fits */
3192 if (pt->paste_flags & PASTE_TRANSPOSE) {
3193 if ((r->end.col - r->start.col + 1) < cr->rows)
3194 r->end.col = r->start.col + cr->rows - 1;
3195 if ((r->end.row - r->start.row + 1) < cr->cols)
3196 r->end.row = r->start.row + cr->cols - 1;
3197 } else {
3198 if ((r->end.col - r->start.col + 1) < cr->cols)
3199 r->end.col = r->start.col + cr->cols - 1;
3200 if ((r->end.row - r->start.row + 1) < cr->rows)
3201 r->end.row = r->start.row + cr->rows - 1;
3207 if (n_c * (gnm_float)n_r > 10000.) {
3208 char *number = g_strdup_printf ("%0.0" GNM_FORMAT_f,
3209 (gnm_float)n_c * (gnm_float)n_r);
3210 gboolean result = go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)), FALSE,
3211 _("Do you really want to paste "
3212 "%s copies?"), number);
3213 g_free (number);
3214 if (!result) {
3215 g_object_unref (me);
3216 return TRUE;
3220 copy_ready:
3221 /* Use translate to do a quiet sanity check */
3222 if (range_translate (&me->dst.range, pt->sheet, 0, 0)) {
3223 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
3224 me->cmd.cmd_descriptor,
3225 _("is beyond sheet boundaries"));
3226 g_object_unref (me);
3227 return TRUE;
3230 /* no need to test if all we have are objects or are copying from */
3231 /*a single merge to a single merge*/
3232 if ((!me->only_objects) && (!me->single_merge_to_single_merge)&&
3233 sheet_range_splits_region (pt->sheet, &me->dst.range,
3234 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
3235 g_object_unref (me);
3236 return TRUE;
3239 warn_if_date_trouble (wbc, cr);
3241 return gnm_command_push_undo (wbc, G_OBJECT (me));
3244 /******************************************************************/
3246 #define CMD_AUTOFILL_TYPE (cmd_autofill_get_type ())
3247 #define CMD_AUTOFILL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFILL_TYPE, CmdAutofill))
3249 typedef struct {
3250 GnmCommand cmd;
3252 GnmCellRegion *contents;
3253 GnmPasteTarget dst;
3254 GnmRange src;
3255 int base_col, base_row, w, h, end_col, end_row;
3256 gboolean default_increment;
3257 gboolean inverse_autofill;
3258 ColRowIndexList *columns;
3259 ColRowStateGroup *old_widths;
3260 } CmdAutofill;
3262 static void
3263 cmd_autofill_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3265 CmdAutofill const *orig = (CmdAutofill const *) cmd;
3266 SheetView *sv = wb_control_cur_sheet_view (wbc);
3267 GnmRange const *r = selection_first_range (sv,
3268 GO_CMD_CONTEXT (wbc), _("Autofill"));
3270 if (r == NULL)
3271 return;
3273 cmd_autofill (wbc, sv_sheet (sv), orig->default_increment,
3274 r->start.col, r->start.row, range_width (r), range_height (r),
3275 r->start.col + (orig->end_col - orig->base_col),
3276 r->start.row + (orig->end_row - orig->base_row),
3277 orig->inverse_autofill);
3279 MAKE_GNM_COMMAND (CmdAutofill, cmd_autofill, cmd_autofill_repeat)
3281 static gboolean
3282 cmd_autofill_undo (GnmCommand *cmd, WorkbookControl *wbc)
3284 CmdAutofill *me = CMD_AUTOFILL (cmd);
3285 gboolean res;
3287 g_return_val_if_fail (wbc != NULL, TRUE);
3288 g_return_val_if_fail (me != NULL, TRUE);
3289 g_return_val_if_fail (me->contents != NULL, TRUE);
3291 res = clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc));
3292 cellregion_unref (me->contents);
3293 me->contents = NULL;
3295 if (me->old_widths) {
3296 colrow_restore_state_group (me->cmd.sheet, TRUE,
3297 me->columns,
3298 me->old_widths);
3299 colrow_state_group_destroy (me->old_widths);
3300 me->old_widths = NULL;
3301 colrow_index_list_destroy (me->columns);
3302 me->columns = NULL;
3305 if (res)
3306 return TRUE;
3308 select_range (me->dst.sheet, &me->src, wbc);
3310 return FALSE;
3313 static gboolean
3314 cmd_autofill_redo (GnmCommand *cmd, WorkbookControl *wbc)
3316 CmdAutofill *me = CMD_AUTOFILL (cmd);
3317 GnmRange r;
3319 g_return_val_if_fail (me != NULL, TRUE);
3320 g_return_val_if_fail (me->contents == NULL, TRUE);
3322 me->contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
3324 g_return_val_if_fail (me->contents != NULL, TRUE);
3326 /* FIXME : when we split autofill to support hints and better validation
3327 * move this in there.
3329 /* MW: May 2006: we support hints now. What's this about? */
3330 sheet_clear_region (me->dst.sheet,
3331 me->dst.range.start.col, me->dst.range.start.row,
3332 me->dst.range.end.col, me->dst.range.end.row,
3333 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3334 GO_CMD_CONTEXT (wbc));
3336 if (me->cmd.size == 1)
3337 me->cmd.size += cellregion_cmd_size (me->contents);
3338 if (me->inverse_autofill)
3339 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3340 me->end_col, me->end_row, me->w, me->h,
3341 me->base_col, me->base_row);
3342 else
3343 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3344 me->base_col, me->base_row, me->w, me->h,
3345 me->end_col, me->end_row);
3347 colrow_autofit (me->cmd.sheet, &me->dst.range, TRUE, TRUE,
3348 TRUE, FALSE,
3349 &me->columns, &me->old_widths);
3351 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3352 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3353 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3355 r = range_union (&me->dst.range, &me->src);
3356 select_range (me->dst.sheet, &r, wbc);
3358 return FALSE;
3361 static void
3362 cmd_autofill_finalize (GObject *cmd)
3364 CmdAutofill *me = CMD_AUTOFILL (cmd);
3366 if (me->contents) {
3367 cellregion_unref (me->contents);
3368 me->contents = NULL;
3370 colrow_index_list_destroy (me->columns);
3371 colrow_state_group_destroy (me->old_widths);
3372 gnm_command_finalize (cmd);
3375 gboolean
3376 cmd_autofill (WorkbookControl *wbc, Sheet *sheet,
3377 gboolean default_increment,
3378 int base_col, int base_row,
3379 int w, int h, int end_col, int end_row,
3380 gboolean inverse_autofill)
3382 CmdAutofill *me;
3383 GnmRange target, src;
3385 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3387 /* This would be meaningless */
3388 if (base_col+w-1 == end_col && base_row+h-1 == end_row)
3389 return FALSE;
3391 if (inverse_autofill) {
3392 if (end_col != base_col + w - 1) {
3393 range_init (&target, base_col, base_row,
3394 end_col - w, end_row);
3395 range_init (&src, end_col - w + 1, base_row,
3396 end_col, end_row);
3397 } else {
3398 range_init (&target, base_col, base_row,
3399 end_col, end_row - h);
3400 range_init (&src, base_col, end_row - h + 1,
3401 end_col, end_row);
3403 } else {
3404 if (end_col != base_col + w - 1) {
3405 range_init (&target, base_col + w, base_row,
3406 end_col, end_row);
3407 range_init (&src, base_col, base_row,
3408 base_col + w - 1, end_row);
3409 } else {
3410 range_init (&target, base_col, base_row + h,
3411 end_col, end_row);
3412 range_init (&src, base_col, base_row,
3413 end_col, base_row + h - 1);
3417 /* We don't support clearing regions, when a user uses the autofill
3418 * cursor to 'shrink' a selection
3420 if (target.start.col > target.end.col || target.start.row > target.end.row)
3421 return TRUE;
3423 /* Check arrays or merged regions in src or target regions */
3424 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")) ||
3425 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")))
3426 return TRUE;
3428 me = g_object_new (CMD_AUTOFILL_TYPE, NULL);
3430 me->contents = NULL;
3431 me->dst.sheet = sheet;
3432 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3433 me->dst.range = target;
3434 me->src = src;
3436 me->base_col = base_col;
3437 me->base_row = base_row,
3438 me->w = w;
3439 me->h = h;
3440 me->end_col = end_col;
3441 me->end_row = end_row;
3442 me->default_increment = default_increment;
3443 me->inverse_autofill = inverse_autofill;
3445 me->cmd.sheet = sheet;
3446 me->cmd.size = 1; /* Changed in initial redo. */
3447 me->cmd.cmd_descriptor = g_strdup_printf (_("Autofilling %s"),
3448 range_as_string (&me->dst.range));
3450 return gnm_command_push_undo (wbc, G_OBJECT (me));
3453 /******************************************************************/
3455 #define CMD_COPYREL_TYPE (cmd_copyrel_get_type ())
3456 #define CMD_COPYREL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COPYREL_TYPE, CmdCopyRel))
3458 typedef struct {
3459 GnmCommand cmd;
3461 GOUndo *undo;
3462 GnmPasteTarget dst, src;
3463 int dx, dy;
3464 char const *name;
3465 } CmdCopyRel;
3467 static void
3468 cmd_copyrel_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3470 CmdCopyRel const *orig = (CmdCopyRel const *) cmd;
3471 cmd_copyrel (wbc, orig->dx, orig->dy, orig->name);
3473 MAKE_GNM_COMMAND (CmdCopyRel, cmd_copyrel, cmd_copyrel_repeat)
3475 static gboolean
3476 cmd_copyrel_undo (GnmCommand *cmd, WorkbookControl *wbc)
3478 CmdCopyRel *me = CMD_COPYREL (cmd);
3480 g_return_val_if_fail (wbc != NULL, TRUE);
3481 g_return_val_if_fail (me != NULL, TRUE);
3482 g_return_val_if_fail (me->undo != NULL, TRUE);
3484 go_undo_undo (me->undo);
3486 /* Select the newly pasted contents (this queues a redraw) */
3487 select_range (me->dst.sheet, &me->dst.range, wbc);
3489 return FALSE;
3492 static gboolean
3493 cmd_copyrel_redo (GnmCommand *cmd, WorkbookControl *wbc)
3495 CmdCopyRel *me = CMD_COPYREL (cmd);
3496 GnmCellRegion *contents;
3497 gboolean res;
3499 g_return_val_if_fail (me != NULL, TRUE);
3501 sheet_clear_region (me->dst.sheet,
3502 me->dst.range.start.col, me->dst.range.start.row,
3503 me->dst.range.end.col, me->dst.range.end.row,
3504 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3505 GO_CMD_CONTEXT (wbc));
3507 contents = clipboard_copy_range (me->src.sheet, &me->src.range);
3508 res = clipboard_paste_region (contents, &me->dst, GO_CMD_CONTEXT (wbc));
3509 cellregion_unref (contents);
3510 if (res)
3511 return TRUE;
3513 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3514 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3515 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3517 /* Select the newly pasted contents (this queues a redraw) */
3518 select_range (me->dst.sheet, &me->dst.range, wbc);
3520 return FALSE;
3523 static void
3524 cmd_copyrel_finalize (GObject *cmd)
3526 CmdCopyRel *me = CMD_COPYREL (cmd);
3528 if (me->undo)
3529 g_object_unref (me->undo);
3531 gnm_command_finalize (cmd);
3534 gboolean
3535 cmd_copyrel (WorkbookControl *wbc,
3536 int dx, int dy,
3537 char const *name)
3539 CmdCopyRel *me;
3540 GnmRange target, src;
3541 SheetView *sv = wb_control_cur_sheet_view (wbc);
3542 Sheet *sheet = sv->sheet;
3543 GnmRange const *selr =
3544 selection_first_range (sv, GO_CMD_CONTEXT (wbc), name);
3546 g_return_val_if_fail (dx == 0 || dy == 0, TRUE);
3548 if (!selr)
3549 return FALSE;
3551 target = *selr;
3552 range_normalize (&target);
3553 src.start = src.end = target.start;
3555 if (dy) {
3556 src.end.col = target.end.col;
3557 if (target.start.row != target.end.row)
3558 target.start.row++;
3559 else
3560 src.start.row = src.end.row = (target.start.row + dy);
3563 if (dx) {
3564 src.end.row = target.end.row;
3565 if (target.start.col != target.end.col)
3566 target.start.col++;
3567 else
3568 src.start.col = src.end.col = (target.start.col + dx);
3571 if (src.start.col < 0 || src.start.col >= gnm_sheet_get_max_cols (sheet) ||
3572 src.start.row < 0 || src.start.row >= gnm_sheet_get_max_rows (sheet))
3573 return FALSE;
3575 /* Check arrays or merged regions in src or target regions */
3576 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), name) ||
3577 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), name))
3578 return TRUE;
3580 me = g_object_new (CMD_COPYREL_TYPE, NULL);
3582 me->dst.sheet = sheet;
3583 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3584 me->dst.range = target;
3585 me->src.sheet = sheet;
3586 me->src.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3587 me->src.range = src;
3588 me->dx = dx;
3589 me->dy = dy;
3590 me->name = name;
3591 me->undo = clipboard_copy_range_undo (me->dst.sheet, &me->dst.range);
3593 me->cmd.sheet = sheet;
3594 me->cmd.size = 1;
3595 me->cmd.cmd_descriptor = g_strdup (name);
3597 return gnm_command_push_undo (wbc, G_OBJECT (me));
3600 /******************************************************************/
3603 #define CMD_AUTOFORMAT_TYPE (cmd_autoformat_get_type ())
3604 #define CMD_AUTOFORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFORMAT_TYPE, CmdAutoFormat))
3606 typedef struct {
3607 GnmCellPos pos;
3608 GnmStyleList *styles;
3609 } CmdAutoFormatOldStyle;
3611 typedef struct {
3612 GnmCommand cmd;
3614 GSList *selection; /* Selections on the sheet */
3615 GSList *old_styles; /* Older styles, one style_list per selection range*/
3617 GnmFT *ft; /* Template that has been applied */
3618 } CmdAutoFormat;
3620 static void
3621 cmd_autoformat_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3623 CmdAutoFormat const *orig = (CmdAutoFormat const *) cmd;
3624 cmd_selection_autoformat (wbc, gnm_ft_clone (orig->ft));
3626 MAKE_GNM_COMMAND (CmdAutoFormat, cmd_autoformat, cmd_autoformat_repeat)
3628 static gboolean
3629 cmd_autoformat_undo (GnmCommand *cmd,
3630 G_GNUC_UNUSED WorkbookControl *wbc)
3632 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3634 g_return_val_if_fail (me != NULL, TRUE);
3636 if (me->old_styles) {
3637 GSList *l1 = me->old_styles;
3638 GSList *l2 = me->selection;
3640 for (; l1; l1 = l1->next, l2 = l2->next) {
3641 GnmRange *r;
3642 CmdAutoFormatOldStyle *os = l1->data;
3643 GnmSpanCalcFlags flags = sheet_style_set_list (me->cmd.sheet,
3644 &os->pos, os->styles, NULL, NULL);
3646 g_return_val_if_fail (l2 && l2->data, TRUE);
3648 r = l2->data;
3649 sheet_range_calc_spans (me->cmd.sheet, r, flags);
3650 if (flags != GNM_SPANCALC_SIMPLE)
3651 rows_height_update (me->cmd.sheet, r, TRUE);
3655 return FALSE;
3658 static gboolean
3659 cmd_autoformat_redo (GnmCommand *cmd,
3660 G_GNUC_UNUSED WorkbookControl *wbc)
3662 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3664 g_return_val_if_fail (me != NULL, TRUE);
3666 gnm_ft_apply_to_sheet_regions (me->ft,
3667 me->cmd.sheet, me->selection);
3669 return FALSE;
3672 static void
3673 cmd_autoformat_finalize (GObject *cmd)
3675 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3677 if (me->old_styles != NULL) {
3678 GSList *l;
3680 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
3681 CmdAutoFormatOldStyle *os = l->data;
3683 if (os->styles)
3684 style_list_free (os->styles);
3686 g_free (os);
3689 me->old_styles = NULL;
3692 range_fragment_free (me->selection);
3693 me->selection = NULL;
3695 gnm_ft_free (me->ft);
3697 gnm_command_finalize (cmd);
3701 * cmd_selection_autoformat:
3702 * @wbc: the context.
3703 * @ft: The format template that was applied
3705 * Returns: %TRUE if there was a problem, %FALSE otherwise.
3707 gboolean
3708 cmd_selection_autoformat (WorkbookControl *wbc, GnmFT *ft)
3710 CmdAutoFormat *me;
3711 char *names;
3712 GSList *l;
3713 SheetView *sv = wb_control_cur_sheet_view (wbc);
3715 me = g_object_new (CMD_AUTOFORMAT_TYPE, NULL);
3717 me->selection = selection_get_ranges (sv, FALSE); /* Regions may overlap */
3718 me->ft = ft;
3719 me->cmd.sheet = sv_sheet (sv);
3720 me->cmd.size = 1; /* FIXME? */
3722 if (!gnm_ft_check_valid (ft, me->selection, GO_CMD_CONTEXT (wbc))) {
3723 g_object_unref (me);
3724 return TRUE;
3727 me->old_styles = NULL;
3728 for (l = me->selection; l; l = l->next) {
3729 CmdFormatOldStyle *os;
3730 GnmRange range = *((GnmRange const *) l->data);
3732 /* Store the containing range to handle borders */
3733 if (range.start.col > 0) range.start.col--;
3734 if (range.start.row > 0) range.start.row--;
3735 if (range.end.col < gnm_sheet_get_last_col (sv->sheet)) range.end.col++;
3736 if (range.end.row < gnm_sheet_get_last_row (sv->sheet)) range.end.row++;
3738 os = g_new (CmdFormatOldStyle, 1);
3740 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
3741 os->pos = range.start;
3743 me->old_styles = g_slist_append (me->old_styles, os);
3746 names = undo_range_list_name (me->cmd.sheet, me->selection);
3747 me->cmd.cmd_descriptor = g_strdup_printf (_("Autoformatting %s"),
3748 names);
3749 g_free (names);
3751 return gnm_command_push_undo (wbc, G_OBJECT (me));
3754 /******************************************************************/
3756 #define CMD_UNMERGE_CELLS_TYPE (cmd_unmerge_cells_get_type ())
3757 #define CMD_UNMERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_UNMERGE_CELLS_TYPE, CmdUnmergeCells))
3759 typedef struct {
3760 GnmCommand cmd;
3762 Sheet *sheet;
3763 GArray *unmerged_regions;
3764 GArray *ranges;
3765 } CmdUnmergeCells;
3767 static void
3768 cmd_unmerge_cells_repeat (G_GNUC_UNUSED GnmCommand const *cmd, WorkbookControl *wbc)
3770 SheetView *sv = wb_control_cur_sheet_view (wbc);
3771 GSList *range_list = selection_get_ranges (sv, FALSE);
3772 cmd_unmerge_cells (wbc, sv_sheet (sv), range_list);
3773 range_fragment_free (range_list);
3775 MAKE_GNM_COMMAND (CmdUnmergeCells, cmd_unmerge_cells, cmd_unmerge_cells_repeat)
3777 static gboolean
3778 cmd_unmerge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3780 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3781 unsigned i;
3783 g_return_val_if_fail (me != NULL, TRUE);
3784 g_return_val_if_fail (me->unmerged_regions != NULL, TRUE);
3786 for (i = 0 ; i < me->unmerged_regions->len ; ++i) {
3787 GnmRange const *tmp = &(g_array_index (me->unmerged_regions, GnmRange, i));
3788 sheet_redraw_range (me->cmd.sheet, tmp);
3789 gnm_sheet_merge_add (me->cmd.sheet, tmp, TRUE, GO_CMD_CONTEXT (wbc));
3790 sheet_range_calc_spans (me->cmd.sheet, tmp, GNM_SPANCALC_RE_RENDER);
3793 g_array_free (me->unmerged_regions, TRUE);
3794 me->unmerged_regions = NULL;
3796 return FALSE;
3799 static gboolean
3800 cmd_unmerge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3802 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3803 unsigned i;
3805 g_return_val_if_fail (me != NULL, TRUE);
3806 g_return_val_if_fail (me->unmerged_regions == NULL, TRUE);
3808 me->unmerged_regions = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3809 for (i = 0 ; i < me->ranges->len ; ++i) {
3810 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (me->cmd.sheet,
3811 &(g_array_index (me->ranges, GnmRange, i)));
3812 for (ptr = merged ; ptr != NULL ; ptr = ptr->next) {
3813 GnmRange const *pr = ptr->data;
3814 GnmRange const tmp = *pr;
3815 g_array_append_val (me->unmerged_regions, tmp);
3816 gnm_sheet_merge_remove (me->cmd.sheet, &tmp);
3817 sheet_range_calc_spans (me->cmd.sheet, &tmp,
3818 GNM_SPANCALC_RE_RENDER);
3820 g_slist_free (merged);
3823 return FALSE;
3826 static void
3827 cmd_unmerge_cells_finalize (GObject *cmd)
3829 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3831 if (me->unmerged_regions != NULL) {
3832 g_array_free (me->unmerged_regions, TRUE);
3833 me->unmerged_regions = NULL;
3835 if (me->ranges != NULL) {
3836 g_array_free (me->ranges, TRUE);
3837 me->ranges = NULL;
3840 gnm_command_finalize (cmd);
3844 * cmd_unmerge_cells:
3845 * @wbc: the context.
3846 * @sheet: #Sheet
3847 * @selection: (element-type GnmRange): selection.
3849 * Returns: %TRUE if there was a problem, %FALSE otherwise.
3851 gboolean
3852 cmd_unmerge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection)
3854 CmdUnmergeCells *me;
3855 char *names;
3857 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3859 me = g_object_new (CMD_UNMERGE_CELLS_TYPE, NULL);
3861 me->cmd.sheet = sheet;
3862 me->cmd.size = 1;
3864 names = undo_range_list_name (sheet, selection);
3865 me->cmd.cmd_descriptor = g_strdup_printf (_("Unmerging %s"), names);
3866 g_free (names);
3868 me->unmerged_regions = NULL;
3869 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3870 for ( ; selection != NULL ; selection = selection->next) {
3871 GSList *merged = gnm_sheet_merge_get_overlap (sheet, selection->data);
3872 if (merged != NULL) {
3873 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
3874 g_slist_free (merged);
3878 if (me->ranges->len <= 0) {
3879 g_object_unref (me);
3880 return TRUE;
3883 return gnm_command_push_undo (wbc, G_OBJECT (me));
3886 /******************************************************************/
3888 #define CMD_MERGE_CELLS_TYPE (cmd_merge_cells_get_type ())
3889 #define CMD_MERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_CELLS_TYPE, CmdMergeCells))
3891 typedef struct {
3892 GnmCommand cmd;
3893 GArray *ranges;
3894 GSList *old_contents;
3895 gboolean center;
3896 } CmdMergeCells;
3898 static void
3899 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc);
3901 MAKE_GNM_COMMAND (CmdMergeCells, cmd_merge_cells, cmd_merge_cells_repeat)
3903 static void
3904 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3906 SheetView *sv = wb_control_cur_sheet_view (wbc);
3907 GSList *range_list = selection_get_ranges (sv, FALSE);
3908 cmd_merge_cells (wbc, sv_sheet (sv), range_list,
3909 CMD_MERGE_CELLS (cmd)->center);
3910 range_fragment_free (range_list);
3913 static gboolean
3914 cmd_merge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3916 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3917 unsigned i, flags;
3919 g_return_val_if_fail (me != NULL, TRUE);
3921 for (i = 0 ; i < me->ranges->len ; ++i) {
3922 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3923 gnm_sheet_merge_remove (me->cmd.sheet, r);
3926 /* Avoid pasting comments that are at 0,0. Redo copies the target
3927 * region (including all comments) . If there was a comment in the top
3928 * left we would end up duplicating it. */
3929 flags = PASTE_CONTENTS | PASTE_FORMATS | PASTE_COMMENTS |
3930 PASTE_IGNORE_COMMENTS_AT_ORIGIN;
3931 if (me->center)
3932 flags |= PASTE_FORMATS;
3933 for (i = 0 ; i < me->ranges->len ; ++i) {
3934 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3935 GnmPasteTarget pt;
3936 GnmCellRegion * c;
3938 g_return_val_if_fail (me->old_contents != NULL, TRUE);
3940 c = me->old_contents->data;
3941 clipboard_paste_region (c,
3942 paste_target_init (&pt, me->cmd.sheet, r, flags),
3943 GO_CMD_CONTEXT (wbc));
3944 cellregion_unref (c);
3945 me->old_contents = g_slist_remove (me->old_contents, c);
3947 g_return_val_if_fail (me->old_contents == NULL, TRUE);
3949 return FALSE;
3952 static gboolean
3953 cmd_merge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3955 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3956 GnmStyle *align_center = NULL;
3957 Sheet *sheet;
3958 unsigned i;
3960 g_return_val_if_fail (me != NULL, TRUE);
3962 if (me->center) {
3963 align_center = gnm_style_new ();
3964 gnm_style_set_align_h (align_center, GNM_HALIGN_CENTER);
3966 sheet = me->cmd.sheet;
3967 for (i = 0 ; i < me->ranges->len ; ++i) {
3968 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3969 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (sheet, r);
3971 /* save contents before removing contained merged regions */
3972 me->old_contents = g_slist_prepend (me->old_contents,
3973 clipboard_copy_range (sheet, r));
3974 for (ptr = merged ; ptr != NULL ; ptr = ptr->next)
3975 gnm_sheet_merge_remove (sheet, ptr->data);
3976 g_slist_free (merged);
3978 gnm_sheet_merge_add (sheet, r, TRUE, GO_CMD_CONTEXT (wbc));
3979 if (me->center)
3980 sheet_apply_style (me->cmd.sheet, r, align_center);
3983 if (me->center)
3984 gnm_style_unref (align_center);
3985 me->old_contents = g_slist_reverse (me->old_contents);
3986 return FALSE;
3989 static void
3990 cmd_merge_cells_finalize (GObject *cmd)
3992 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3994 if (me->old_contents != NULL) {
3995 GSList *l;
3996 for (l = me->old_contents ; l != NULL ; l = g_slist_remove (l, l->data))
3997 cellregion_unref (l->data);
3998 me->old_contents = NULL;
4001 if (me->ranges != NULL) {
4002 g_array_free (me->ranges, TRUE);
4003 me->ranges = NULL;
4006 gnm_command_finalize (cmd);
4010 * cmd_merge_cells:
4011 * @wbc: the context.
4012 * @sheet: #Sheet
4013 * @selection: (element-type GnmRange): selection.
4014 * @center:
4016 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4018 gboolean
4019 cmd_merge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection,
4020 gboolean center)
4022 CmdMergeCells *me;
4023 char *names;
4025 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4027 me = g_object_new (CMD_MERGE_CELLS_TYPE, NULL);
4029 me->cmd.sheet = sheet;
4030 me->cmd.size = 1;
4032 names = undo_range_list_name (sheet, selection);
4033 me->cmd.cmd_descriptor =
4034 g_strdup_printf ((center ? _("Merge and Center %s") :_("Merging %s")), names);
4035 g_free (names);
4037 me->center = center;
4038 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
4039 for ( ; selection != NULL ; selection = selection->next) {
4040 GnmRange const *exist;
4041 GnmRange const *r = selection->data;
4042 if (range_is_singleton (selection->data))
4043 continue;
4044 if (NULL != (exist = gnm_sheet_merge_is_corner (sheet, &r->start)) &&
4045 range_equal (r, exist))
4046 continue;
4047 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
4050 if (me->ranges->len <= 0) {
4051 g_object_unref (me);
4052 return TRUE;
4055 return gnm_command_push_undo (wbc, G_OBJECT (me));
4058 /******************************************************************/
4060 #define CMD_SEARCH_REPLACE_TYPE (cmd_search_replace_get_type())
4061 #define CMD_SEARCH_REPLACE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SEARCH_REPLACE_TYPE, CmdSearchReplace))
4063 typedef struct {
4064 GnmCommand cmd;
4065 GnmSearchReplace *sr;
4068 * Undo/redo use this list of SearchReplaceItems to do their
4069 * work. Note, that it is possible for a cell to occur
4070 * multiple times in the list.
4072 GList *cells;
4073 } CmdSearchReplace;
4075 MAKE_GNM_COMMAND (CmdSearchReplace, cmd_search_replace, NULL)
4077 typedef enum { SRI_text, SRI_comment } SearchReplaceItemType;
4079 typedef struct {
4080 GnmEvalPos pos;
4081 SearchReplaceItemType old_type, new_type;
4082 union {
4083 char *text;
4084 char *comment;
4085 } old, new;
4086 } SearchReplaceItem;
4089 static void
4090 cmd_search_replace_update_after_action (CmdSearchReplace *me,
4091 WorkbookControl *wbc)
4093 GList *tmp;
4094 Sheet *last_sheet = NULL;
4096 for (tmp = me->cells; tmp; tmp = tmp->next) {
4097 SearchReplaceItem *sri = tmp->data;
4098 if (sri->pos.sheet != last_sheet) {
4099 last_sheet = sri->pos.sheet;
4100 update_after_action (last_sheet, wbc);
4106 static gboolean
4107 cmd_search_replace_undo (GnmCommand *cmd,
4108 WorkbookControl *wbc)
4110 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4111 GList *tmp;
4113 /* Undo does replacements backwards. */
4114 for (tmp = g_list_last (me->cells); tmp; tmp = tmp->prev) {
4115 SearchReplaceItem *sri = tmp->data;
4116 switch (sri->old_type) {
4117 case SRI_text:
4119 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4120 sri->pos.eval.col,
4121 sri->pos.eval.row);
4122 sheet_cell_set_text (cell, sri->old.text, NULL);
4123 break;
4125 case SRI_comment:
4127 GnmComment *comment =
4128 sheet_get_comment (sri->pos.sheet,
4129 &sri->pos.eval);
4130 if (comment) {
4131 cell_comment_text_set (comment, sri->old.comment);
4132 } else {
4133 g_warning ("Undo/redo broken.");
4136 break;
4139 cmd_search_replace_update_after_action (me, wbc);
4141 return FALSE;
4144 static gboolean
4145 cmd_search_replace_redo (GnmCommand *cmd,
4146 WorkbookControl *wbc)
4148 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4149 GList *tmp;
4151 /* Redo does replacements forward. */
4152 for (tmp = me->cells; tmp; tmp = tmp->next) {
4153 SearchReplaceItem *sri = tmp->data;
4154 switch (sri->new_type) {
4155 case SRI_text:
4157 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4158 sri->pos.eval.col,
4159 sri->pos.eval.row);
4160 sheet_cell_set_text (cell, sri->new.text, NULL);
4161 break;
4163 case SRI_comment:
4165 GnmComment *comment =
4166 sheet_get_comment (sri->pos.sheet,
4167 &sri->pos.eval);
4168 if (comment) {
4169 cell_comment_text_set (comment, sri->new.comment);
4170 } else {
4171 g_warning ("Undo/redo broken.");
4174 break;
4177 cmd_search_replace_update_after_action (me, wbc);
4179 return FALSE;
4182 static gboolean
4183 cmd_search_replace_do_cell (CmdSearchReplace *me, GnmEvalPos *ep,
4184 gboolean test_run)
4186 GnmSearchReplace *sr = me->sr;
4188 GnmSearchReplaceCellResult cell_res;
4189 GnmSearchReplaceCommentResult comment_res;
4191 if (gnm_search_replace_cell (sr, ep, TRUE, &cell_res)) {
4192 GnmExprTop const *texpr;
4193 GnmValue *val;
4194 gboolean err;
4195 GnmParsePos pp;
4197 parse_pos_init_evalpos (&pp, ep);
4198 parse_text_value_or_expr (&pp, cell_res.new_text, &val, &texpr);
4201 * FIXME: this is a hack, but parse_text_value_or_expr
4202 * does not have a better way of signaling an error.
4204 err = (val &&
4205 gnm_expr_char_start_p (cell_res.new_text) &&
4206 !go_format_is_text (gnm_cell_get_format (cell_res.cell)));
4207 value_release (val);
4208 if (texpr) gnm_expr_top_unref (texpr);
4210 if (err) {
4211 if (test_run) {
4212 gnm_search_replace_query_fail (sr, &cell_res);
4213 g_free (cell_res.old_text);
4214 g_free (cell_res.new_text);
4215 return TRUE;
4216 } else {
4217 switch (sr->error_behaviour) {
4218 case GNM_SRE_ERROR: {
4219 GnmExprTop const *ee =
4220 gnm_expr_top_new
4221 (gnm_expr_new_funcall1
4222 (gnm_func_lookup ("ERROR", NULL),
4223 gnm_expr_new_constant
4224 (value_new_string_nocopy (cell_res.new_text))));
4225 GnmConventionsOut out;
4227 out.accum = g_string_new ("=");
4228 out.pp = &pp;
4229 out.convs = pp.sheet->convs;
4230 gnm_expr_top_as_gstring (ee, &out);
4231 gnm_expr_top_unref (ee);
4232 cell_res.new_text = g_string_free (out.accum, FALSE);
4233 err = FALSE;
4234 break;
4236 case GNM_SRE_STRING: {
4237 GString *s = g_string_new ("'");
4238 g_string_append (s, cell_res.new_text);
4239 g_free (cell_res.new_text);
4240 cell_res.new_text = g_string_free (s, FALSE);
4241 err = FALSE;
4242 break;
4244 case GNM_SRE_FAIL:
4245 g_assert_not_reached ();
4246 case GNM_SRE_SKIP:
4247 default:
4248 ; /* Nothing */
4253 if (!err && !test_run) {
4254 int res = gnm_search_replace_query_cell
4255 (sr, &cell_res);
4256 gboolean doit = (res == GTK_RESPONSE_YES);
4258 if (res == GTK_RESPONSE_CANCEL) {
4259 g_free (cell_res.old_text);
4260 g_free (cell_res.new_text);
4261 return TRUE;
4264 if (doit) {
4265 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4267 sheet_cell_set_text (cell_res.cell, cell_res.new_text, NULL);
4269 sri->pos = *ep;
4270 sri->old_type = sri->new_type = SRI_text;
4271 sri->old.text = cell_res.old_text;
4272 sri->new.text = cell_res.new_text;
4273 me->cells = g_list_prepend (me->cells, sri);
4275 cell_res.old_text = cell_res.new_text = NULL;
4279 g_free (cell_res.new_text);
4280 g_free (cell_res.old_text);
4283 if (!test_run &&
4284 gnm_search_replace_comment (sr, ep, TRUE, &comment_res)) {
4285 int res = gnm_search_replace_query_comment
4286 (sr, ep, &comment_res);
4287 gboolean doit = (res == GTK_RESPONSE_YES);
4289 if (doit) {
4290 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4291 sri->pos = *ep;
4292 sri->old_type = sri->new_type = SRI_comment;
4293 sri->old.comment = g_strdup (comment_res.old_text);
4294 sri->new.comment = comment_res.new_text;
4295 me->cells = g_list_prepend (me->cells, sri);
4297 cell_comment_text_set (comment_res.comment, comment_res.new_text);
4298 } else {
4299 g_free (comment_res.new_text);
4300 if (res == GTK_RESPONSE_CANCEL)
4301 return TRUE;
4305 return FALSE;
4309 static gboolean
4310 cmd_search_replace_do (CmdSearchReplace *me, gboolean test_run,
4311 WorkbookControl *wbc)
4313 GnmSearchReplace *sr = me->sr;
4314 GPtrArray *cells;
4315 gboolean result = FALSE;
4316 unsigned i;
4318 if (test_run) {
4319 switch (sr->error_behaviour) {
4320 case GNM_SRE_SKIP:
4321 case GNM_SRE_QUERY:
4322 case GNM_SRE_ERROR:
4323 case GNM_SRE_STRING:
4324 /* An error is not a problem. */
4325 return FALSE;
4327 case GNM_SRE_FAIL:
4328 ; /* Nothing. */
4332 cells = gnm_search_collect_cells (sr);
4334 for (i = 0; i < cells->len; i++) {
4335 GnmEvalPos *ep = g_ptr_array_index (cells, i);
4337 if (cmd_search_replace_do_cell (me, ep, test_run)) {
4338 result = TRUE;
4339 break;
4343 gnm_search_collect_cells_free (cells);
4345 if (!test_run) {
4346 /* Cells were added in the wrong order. Correct. */
4347 me->cells = g_list_reverse (me->cells);
4349 cmd_search_replace_update_after_action (me, wbc);
4352 return result;
4356 static void
4357 cmd_search_replace_finalize (GObject *cmd)
4359 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4360 GList *tmp;
4362 for (tmp = me->cells; tmp; tmp = tmp->next) {
4363 SearchReplaceItem *sri = tmp->data;
4364 switch (sri->old_type) {
4365 case SRI_text:
4366 g_free (sri->old.text);
4367 break;
4368 case SRI_comment:
4369 g_free (sri->old.comment);
4370 break;
4372 switch (sri->new_type) {
4373 case SRI_text:
4374 g_free (sri->new.text);
4375 break;
4376 case SRI_comment:
4377 g_free (sri->new.comment);
4378 break;
4380 g_free (sri);
4382 g_list_free (me->cells);
4383 g_object_unref (me->sr);
4385 gnm_command_finalize (cmd);
4388 gboolean
4389 cmd_search_replace (WorkbookControl *wbc, GnmSearchReplace *sr)
4391 CmdSearchReplace *me;
4393 g_return_val_if_fail (sr != NULL, TRUE);
4395 me = g_object_new (CMD_SEARCH_REPLACE_TYPE, NULL);
4397 me->cells = NULL;
4398 me->sr = g_object_ref (sr);
4400 me->cmd.sheet = NULL;
4401 me->cmd.size = 1; /* Corrected below. */
4402 me->cmd.cmd_descriptor = g_strdup (_("Search and Replace"));
4404 if (cmd_search_replace_do (me, TRUE, wbc)) {
4405 /* There was an error and nothing was done. */
4406 g_object_unref (me);
4407 return TRUE;
4410 cmd_search_replace_do (me, FALSE, wbc);
4411 me->cmd.size += g_list_length (me->cells);
4413 command_register_undo (wbc, G_OBJECT (me));
4414 return FALSE;
4417 /******************************************************************/
4419 #define CMD_COLROW_STD_SIZE_TYPE (cmd_colrow_std_size_get_type ())
4420 #define CMD_COLROW_STD_SIZE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_STD_SIZE_TYPE, CmdColRowStdSize))
4422 typedef struct {
4423 GnmCommand cmd;
4425 Sheet *sheet;
4426 gboolean is_cols;
4427 double new_default;
4428 double old_default;
4429 } CmdColRowStdSize;
4431 MAKE_GNM_COMMAND (CmdColRowStdSize, cmd_colrow_std_size, NULL)
4433 static gboolean
4434 cmd_colrow_std_size_undo (GnmCommand *cmd,
4435 G_GNUC_UNUSED WorkbookControl *wbc)
4437 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4439 g_return_val_if_fail (me != NULL, TRUE);
4440 g_return_val_if_fail (me->old_default != 0, TRUE);
4442 if (me->is_cols)
4443 sheet_col_set_default_size_pts (me->sheet, me->old_default);
4444 else
4445 sheet_row_set_default_size_pts (me->sheet, me->old_default);
4447 me->old_default = 0;
4449 return FALSE;
4452 static gboolean
4453 cmd_colrow_std_size_redo (GnmCommand *cmd,
4454 G_GNUC_UNUSED WorkbookControl *wbc)
4456 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4458 g_return_val_if_fail (me != NULL, TRUE);
4459 g_return_val_if_fail (me->old_default == 0, TRUE);
4461 if (me->is_cols) {
4462 me->old_default = sheet_col_get_default_size_pts (me->sheet);
4463 sheet_col_set_default_size_pts (me->sheet, me->new_default);
4464 } else {
4465 me->old_default = sheet_row_get_default_size_pts (me->sheet);
4466 sheet_row_set_default_size_pts (me->sheet, me->new_default);
4469 return FALSE;
4471 static void
4472 cmd_colrow_std_size_finalize (GObject *cmd)
4474 gnm_command_finalize (cmd);
4477 gboolean
4478 cmd_colrow_std_size (WorkbookControl *wbc, Sheet *sheet,
4479 gboolean is_cols, double new_default)
4481 CmdColRowStdSize *me;
4483 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4485 me = g_object_new (CMD_COLROW_STD_SIZE_TYPE, NULL);
4487 me->sheet = sheet;
4488 me->is_cols = is_cols;
4489 me->new_default = new_default;
4490 me->old_default = 0;
4492 me->cmd.sheet = sheet;
4493 me->cmd.size = 1; /* Changed in initial redo. */
4494 me->cmd.cmd_descriptor = is_cols
4495 ? g_strdup_printf (_("Setting default width of columns to %.2fpts"), new_default)
4496 : g_strdup_printf (_("Setting default height of rows to %.2fpts"), new_default);
4498 return gnm_command_push_undo (wbc, G_OBJECT (me));
4501 /******************************************************************/
4503 #define CMD_ZOOM_TYPE (cmd_zoom_get_type ())
4504 #define CMD_ZOOM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ZOOM_TYPE, CmdZoom))
4506 typedef struct {
4507 GnmCommand cmd;
4509 GSList *sheets;
4510 double new_factor;
4511 double *old_factors;
4512 } CmdZoom;
4514 MAKE_GNM_COMMAND (CmdZoom, cmd_zoom, NULL)
4516 static gboolean
4517 cmd_zoom_undo (GnmCommand *cmd,
4518 G_GNUC_UNUSED WorkbookControl *wbc)
4520 CmdZoom *me = CMD_ZOOM (cmd);
4521 GSList *l;
4522 int i;
4524 g_return_val_if_fail (me != NULL, TRUE);
4525 g_return_val_if_fail (me->sheets != NULL, TRUE);
4526 g_return_val_if_fail (me->old_factors != NULL, TRUE);
4528 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4529 Sheet *sheet = l->data;
4530 g_object_set (sheet, "zoom-factor", me->old_factors[i], NULL);
4533 return FALSE;
4536 static gboolean
4537 cmd_zoom_redo (GnmCommand *cmd,
4538 G_GNUC_UNUSED WorkbookControl *wbc)
4540 CmdZoom *me = CMD_ZOOM (cmd);
4541 GSList *l;
4543 g_return_val_if_fail (me != NULL, TRUE);
4544 g_return_val_if_fail (me->sheets != NULL, TRUE);
4546 for (l = me->sheets; l != NULL; l = l->next) {
4547 Sheet *sheet = l->data;
4548 g_object_set (sheet, "zoom-factor", me->new_factor, NULL);
4551 return FALSE;
4554 static void
4555 cmd_zoom_finalize (GObject *cmd)
4557 CmdZoom *me = CMD_ZOOM (cmd);
4559 g_slist_free (me->sheets);
4560 g_free (me->old_factors);
4562 gnm_command_finalize (cmd);
4566 * cmd_zoom:
4567 * @wbc: #WorkbookControl
4568 * @sheets: (element-type Sheet) (transfer container):
4569 * @factor:
4571 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4573 gboolean
4574 cmd_zoom (WorkbookControl *wbc, GSList *sheets, double factor)
4576 CmdZoom *me;
4577 GString *namelist;
4578 GSList *l;
4579 int i;
4581 g_return_val_if_fail (wbc != NULL, TRUE);
4582 g_return_val_if_fail (sheets != NULL, TRUE);
4584 me = g_object_new (CMD_ZOOM_TYPE, NULL);
4586 me->sheets = sheets;
4587 me->old_factors = g_new0 (double, g_slist_length (sheets));
4588 me->new_factor = factor;
4590 /* Make a list of all sheets to zoom and save zoom factor for each */
4591 namelist = g_string_new (NULL);
4592 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4593 Sheet *sheet = l->data;
4595 g_string_append (namelist, sheet->name_unquoted);
4596 me->old_factors[i] = sheet->last_zoom_factor_used;
4598 if (l->next)
4599 g_string_append (namelist, ", ");
4602 /* Make sure the string doesn't get overly wide */
4603 gnm_cmd_trunc_descriptor (namelist, NULL);
4605 me->cmd.sheet = NULL;
4606 me->cmd.size = 1;
4607 me->cmd.cmd_descriptor =
4608 g_strdup_printf (_("Zoom %s to %.0f%%"), namelist->str, factor * 100);
4610 g_string_free (namelist, TRUE);
4612 return gnm_command_push_undo (wbc, G_OBJECT (me));
4615 /******************************************************************/
4617 #define CMD_OBJECTS_DELETE_TYPE (cmd_objects_delete_get_type ())
4618 #define CMD_OBJECTS_DELETE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECTS_DELETE_TYPE, CmdObjectsDelete))
4620 typedef struct {
4621 GnmCommand cmd;
4622 GSList *objects;
4623 GArray *location;
4624 } CmdObjectsDelete;
4626 MAKE_GNM_COMMAND (CmdObjectsDelete, cmd_objects_delete, NULL)
4628 static gboolean
4629 cmd_objects_delete_redo (GnmCommand *cmd,
4630 G_GNUC_UNUSED WorkbookControl *wbc)
4632 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4633 g_slist_foreach (me->objects, (GFunc) sheet_object_clear_sheet, NULL);
4634 return FALSE;
4637 static void
4638 cmd_objects_restore_location (SheetObject *so, gint location)
4640 gint loc = sheet_object_get_stacking (so);
4641 if (loc != location)
4642 sheet_object_adjust_stacking(so, location - loc);
4645 static gboolean
4646 cmd_objects_delete_undo (GnmCommand *cmd,
4647 G_GNUC_UNUSED WorkbookControl *wbc)
4649 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4650 GSList *l;
4651 gint i;
4653 g_slist_foreach (me->objects,
4654 (GFunc) sheet_object_set_sheet, me->cmd.sheet);
4656 for (l = me->objects, i = 0; l; l = l->next, i++)
4657 cmd_objects_restore_location (GNM_SO (l->data),
4658 g_array_index(me->location,
4659 gint, i));
4660 return FALSE;
4663 static void
4664 cmd_objects_delete_finalize (GObject *cmd)
4666 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4667 g_slist_free_full (me->objects, g_object_unref);
4668 if (me->location) {
4669 g_array_free (me->location, TRUE);
4670 me->location = NULL;
4672 gnm_command_finalize (cmd);
4675 static void
4676 cmd_objects_store_location (SheetObject *so, GArray *location)
4678 gint loc = sheet_object_get_stacking (so);
4679 g_array_append_val (location, loc);
4683 * cmd_objects_delete:
4684 * @wbc: #WorkbookControl
4685 * @objects: (element-type SheetObject) (transfer container): the objects to
4686 * delete.
4687 * @name:
4689 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4691 gboolean
4692 cmd_objects_delete (WorkbookControl *wbc, GSList *objects,
4693 char const *name)
4695 CmdObjectsDelete *me;
4697 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4698 g_return_val_if_fail (objects != NULL, TRUE);
4700 me = g_object_new (CMD_OBJECTS_DELETE_TYPE, NULL);
4702 me->objects = objects;
4703 g_slist_foreach (me->objects, (GFunc) g_object_ref, NULL);
4705 me->location = g_array_new (FALSE, FALSE, sizeof (gint));
4706 g_slist_foreach (me->objects, (GFunc) cmd_objects_store_location,
4707 me->location);
4709 me->cmd.sheet = sheet_object_get_sheet (objects->data);
4710 me->cmd.size = 1;
4711 me->cmd.cmd_descriptor = g_strdup (name ? name : _("Delete Object"));
4713 return gnm_command_push_undo (wbc, G_OBJECT (me));
4716 /******************************************************************/
4719 * cmd_objects_move:
4720 * @wbc: #WorkbookControl
4721 * @objects: (element-type SheetObject) (transfer container): the objects to move.
4722 * @anchors: (element-type SheetObjectAnchor) (transfer full): the anchors for the objects.
4723 * @objects_created:
4724 * @name:
4726 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4728 gboolean
4729 cmd_objects_move (WorkbookControl *wbc, GSList *objects, GSList *anchors,
4730 gboolean objects_created, char const *name)
4732 GOUndo *undo = NULL;
4733 GOUndo *redo = NULL;
4734 gboolean result = TRUE;
4736 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4738 undo = sheet_object_move_undo (objects, objects_created);
4739 redo = sheet_object_move_do (objects, anchors, objects_created);
4741 result = cmd_generic (wbc, name, undo, redo);
4743 g_slist_free (objects);
4744 g_slist_free_full (anchors, g_free);
4746 return result;
4749 /******************************************************************/
4751 #define CMD_REORGANIZE_SHEETS_TYPE (cmd_reorganize_sheets_get_type ())
4752 #define CMD_REORGANIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REORGANIZE_SHEETS_TYPE, CmdReorganizeSheets))
4754 typedef struct {
4755 GnmCommand cmd;
4756 Workbook *wb;
4757 WorkbookSheetState *old;
4758 WorkbookSheetState *new;
4759 gboolean first;
4760 Sheet *undo_sheet;
4761 Sheet *redo_sheet;
4762 } CmdReorganizeSheets;
4764 MAKE_GNM_COMMAND (CmdReorganizeSheets, cmd_reorganize_sheets, NULL)
4766 static gboolean
4767 cmd_reorganize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4769 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4770 workbook_sheet_state_restore (me->wb, me->old);
4771 if (me->undo_sheet) {
4772 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4773 wb_control_sheet_focus (control, me->undo_sheet););
4775 return FALSE;
4778 static gboolean
4779 cmd_reorganize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4781 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4783 if (me->first)
4784 me->first = FALSE;
4785 else {
4786 workbook_sheet_state_restore (me->wb, me->new);
4787 if (me->redo_sheet) {
4788 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4789 wb_control_sheet_focus (control, me->redo_sheet););
4793 return FALSE;
4796 static void
4797 cmd_reorganize_sheets_finalize (GObject *cmd)
4799 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4801 if (me->old)
4802 workbook_sheet_state_free (me->old);
4803 if (me->new)
4804 workbook_sheet_state_free (me->new);
4806 gnm_command_finalize (cmd);
4809 gboolean
4810 cmd_reorganize_sheets (WorkbookControl *wbc,
4811 WorkbookSheetState *old_state,
4812 Sheet *undo_sheet)
4814 CmdReorganizeSheets *me;
4815 Workbook *wb = wb_control_get_workbook (wbc);
4817 me = g_object_new (CMD_REORGANIZE_SHEETS_TYPE, NULL);
4818 me->wb = wb;
4819 me->old = old_state;
4820 me->new = workbook_sheet_state_new (me->wb);
4821 me->first = TRUE;
4822 me->undo_sheet = undo_sheet;
4823 me->redo_sheet = wb_control_cur_sheet (wbc);
4825 me->cmd.sheet = NULL;
4826 me->cmd.size = workbook_sheet_state_size (me->old) +
4827 workbook_sheet_state_size (me->new);
4828 me->cmd.cmd_descriptor =
4829 workbook_sheet_state_diff (me->old, me->new);
4831 if (me->cmd.cmd_descriptor)
4832 return gnm_command_push_undo (wbc, G_OBJECT (me));
4834 /* No change. */
4835 g_object_unref (me);
4836 return FALSE;
4839 /******************************************************************/
4841 gboolean
4842 cmd_rename_sheet (WorkbookControl *wbc,
4843 Sheet *sheet,
4844 char const *new_name)
4846 WorkbookSheetState *old_state;
4847 Sheet *collision;
4849 g_return_val_if_fail (new_name != NULL, TRUE);
4850 g_return_val_if_fail (sheet != NULL, TRUE);
4852 if (*new_name == 0) {
4853 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), _("Sheet names must be non-empty."));
4854 return TRUE;
4857 collision = workbook_sheet_by_name (sheet->workbook, new_name);
4858 if (collision && collision != sheet) {
4859 GError *err = g_error_new (go_error_invalid(), 0,
4860 _("A workbook cannot have two sheets with the same name."));
4861 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
4862 g_error_free (err);
4863 return TRUE;
4866 old_state = workbook_sheet_state_new (sheet->workbook);
4867 g_object_set (sheet, "name", new_name, NULL);
4868 return cmd_reorganize_sheets (wbc, old_state, sheet);
4871 /******************************************************************/
4873 #define CMD_RESIZE_SHEETS_TYPE (cmd_resize_sheets_get_type ())
4874 #define CMD_RESIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESIZE_SHEETS_TYPE, CmdResizeSheets))
4876 typedef struct {
4877 GnmCommand cmd;
4878 GSList *sheets;
4879 int cols, rows;
4880 GOUndo *undo;
4881 } CmdResizeSheets;
4883 MAKE_GNM_COMMAND (CmdResizeSheets, cmd_resize_sheets, NULL)
4885 static gboolean
4886 cmd_resize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4888 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4889 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4891 go_undo_undo_with_data (me->undo, cc);
4892 g_object_unref (me->undo);
4893 me->undo = NULL;
4895 return FALSE;
4898 static gboolean
4899 cmd_resize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4901 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4902 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4903 GSList *l;
4905 for (l = me->sheets; l; l = l->next) {
4906 Sheet *sheet = l->data;
4907 gboolean err;
4908 GOUndo *u = gnm_sheet_resize (sheet, me->cols, me->rows,
4909 cc, &err);
4910 me->undo = go_undo_combine (me->undo, u);
4912 if (err) {
4913 if (me->undo)
4914 go_undo_undo_with_data (me->undo, cc);
4915 return TRUE;
4919 return FALSE;
4922 static void
4923 cmd_resize_sheets_finalize (GObject *cmd)
4925 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4927 g_slist_free (me->sheets);
4928 if (me->undo) {
4929 g_object_unref (me->undo);
4930 me->undo = NULL;
4933 gnm_command_finalize (cmd);
4937 * cmd_resize_sheets:
4938 * @wbc: #WorkbookControl
4939 * @sheets: (element-type Sheet) (transfer container): the sheets to resize.
4940 * @cols: new columns number.
4941 * @rows: new rows number.
4943 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4945 gboolean
4946 cmd_resize_sheets (WorkbookControl *wbc,
4947 GSList *sheets,
4948 int cols, int rows)
4950 CmdResizeSheets *me;
4952 me = g_object_new (CMD_RESIZE_SHEETS_TYPE, NULL);
4953 me->sheets = sheets;
4954 me->cols = cols;
4955 me->rows = rows;
4956 me->cmd.sheet = sheets ? sheets->data : NULL;
4957 me->cmd.size = 1;
4958 me->cmd.cmd_descriptor = g_strdup (_("Resizing sheet"));
4960 if (sheets &&
4961 gnm_sheet_valid_size (cols, rows))
4962 return gnm_command_push_undo (wbc, G_OBJECT (me));
4964 /* No change. */
4965 g_object_unref (me);
4966 return FALSE;
4969 /******************************************************************/
4971 #define CMD_SET_COMMENT_TYPE (cmd_set_comment_get_type ())
4972 #define CMD_SET_COMMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SET_COMMENT_TYPE, CmdSetComment))
4974 typedef struct {
4975 GnmCommand cmd;
4977 Sheet *sheet;
4978 GnmCellPos pos;
4979 gchar *new_text;
4980 gchar *old_text;
4981 gchar *new_author;
4982 gchar *old_author;
4983 PangoAttrList *old_attributes;
4984 PangoAttrList *new_attributes;
4985 } CmdSetComment;
4987 MAKE_GNM_COMMAND (CmdSetComment, cmd_set_comment, NULL)
4989 static gboolean
4990 cmd_set_comment_apply (Sheet *sheet, GnmCellPos *pos,
4991 char const *text, PangoAttrList *attributes,
4992 char const *author)
4994 GnmComment *comment;
4995 Workbook *wb = sheet->workbook;
4997 comment = sheet_get_comment (sheet, pos);
4998 if (comment) {
4999 if (text)
5000 g_object_set (G_OBJECT (comment), "text", text,
5001 "author", author,
5002 "markup", attributes, NULL);
5003 else {
5004 GnmRange const *mr;
5006 mr = gnm_sheet_merge_contains_pos (sheet, pos);
5008 if (mr)
5009 sheet_objects_clear (sheet, mr, GNM_CELL_COMMENT_TYPE, NULL);
5010 else {
5011 GnmRange r;
5012 r.start = r.end = *pos;
5013 sheet_objects_clear (sheet, &r, GNM_CELL_COMMENT_TYPE, NULL);
5016 } else if (text && (strlen (text) > 0)) {
5017 cell_set_comment (sheet, pos, author, text, attributes);
5019 sheet_mark_dirty (sheet);
5021 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
5022 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
5024 return FALSE;
5027 static gboolean
5028 cmd_set_comment_undo (GnmCommand *cmd,
5029 G_GNUC_UNUSED WorkbookControl *wbc)
5031 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5033 return cmd_set_comment_apply (me->sheet, &me->pos,
5034 me->old_text, me->old_attributes,
5035 me->old_author);
5038 static gboolean
5039 cmd_set_comment_redo (GnmCommand *cmd,
5040 G_GNUC_UNUSED WorkbookControl *wbc)
5042 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5044 return cmd_set_comment_apply (me->sheet, &me->pos,
5045 me->new_text, me->new_attributes,
5046 me->new_author);
5049 static void
5050 cmd_set_comment_finalize (GObject *cmd)
5052 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5054 g_free (me->new_text);
5055 me->new_text = NULL;
5057 g_free (me->old_text);
5058 me->old_text = NULL;
5060 g_free (me->new_author);
5061 me->new_author = NULL;
5063 g_free (me->old_author);
5064 me->old_author = NULL;
5066 if (me->old_attributes != NULL) {
5067 pango_attr_list_unref (me->old_attributes);
5068 me->old_attributes = NULL;
5071 if (me->new_attributes != NULL) {
5072 pango_attr_list_unref (me->new_attributes);
5073 me->new_attributes = NULL;
5076 gnm_command_finalize (cmd);
5079 gboolean
5080 cmd_set_comment (WorkbookControl *wbc,
5081 Sheet *sheet, GnmCellPos const *pos,
5082 char const *new_text,
5083 PangoAttrList *attr,
5084 char const *new_author)
5086 CmdSetComment *me;
5087 GnmComment *comment;
5088 char *where;
5090 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5091 g_return_val_if_fail (new_text != NULL, TRUE);
5093 me = g_object_new (CMD_SET_COMMENT_TYPE, NULL);
5095 me->cmd.sheet = sheet;
5096 me->cmd.size = 1;
5097 if (strlen (new_text) < 1)
5098 me->new_text = NULL;
5099 else
5100 me->new_text = g_strdup (new_text);
5101 if (strlen (new_author) < 1)
5102 me->new_author = NULL;
5103 else
5104 me->new_author = g_strdup (new_author);
5105 if (attr != NULL)
5106 pango_attr_list_ref (attr);
5107 me->new_attributes = attr;
5108 where = undo_cell_pos_name (sheet, pos);
5109 me->cmd.cmd_descriptor =
5110 g_strdup_printf (me->new_text == NULL ?
5111 _("Clearing comment of %s") :
5112 _("Setting comment of %s"),
5113 where);
5114 g_free (where);
5115 me->old_text = NULL;
5116 me->old_author = NULL;
5117 me->old_attributes = NULL;
5118 me->pos = *pos;
5119 me->sheet = sheet;
5120 comment = sheet_get_comment (sheet, pos);
5121 if (comment) {
5122 g_object_get (G_OBJECT (comment),
5123 "text", &(me->old_text),
5124 "author", &(me->old_author),
5125 "markup", &(me->old_attributes), NULL);
5126 if (me->old_attributes != NULL)
5127 pango_attr_list_ref (me->old_attributes);
5128 me->old_text = g_strdup (me->old_text);
5129 me->old_author = g_strdup (me->old_author);
5132 /* Register the command object */
5133 return gnm_command_push_undo (wbc, G_OBJECT (me));
5136 /******************************************************************/
5138 #define CMD_ANALYSIS_TOOL_TYPE (cmd_analysis_tool_get_type ())
5139 #define CMD_ANALYSIS_TOOL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ANALYSIS_TOOL_TYPE, CmdAnalysis_Tool))
5141 typedef struct {
5142 GnmCommand cmd;
5144 data_analysis_output_t *dao;
5145 gpointer specs;
5146 gboolean specs_owned;
5147 analysis_tool_engine engine;
5148 data_analysis_output_type_t type;
5150 ColRowStateList *col_info;
5151 ColRowStateList *row_info;
5152 GnmRange old_range;
5153 GnmCellRegion *old_contents;
5154 GSList *newSheetObjects;
5155 } CmdAnalysis_Tool;
5157 MAKE_GNM_COMMAND (CmdAnalysis_Tool, cmd_analysis_tool, NULL)
5159 static gboolean
5160 cmd_analysis_tool_undo (GnmCommand *cmd, WorkbookControl *wbc)
5162 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5163 GnmPasteTarget pt;
5165 g_return_val_if_fail (me != NULL, TRUE);
5167 /* The old view might not exist anymore */
5168 me->dao->wbc = wbc;
5170 switch (me->type) {
5171 case NewSheetOutput:
5172 if (!command_undo_sheet_delete (me->dao->sheet))
5173 return TRUE;
5174 me->dao->sheet = NULL;
5175 break;
5176 case NewWorkbookOutput:
5177 g_warning ("How did we get here?");
5178 return TRUE;
5179 break;
5180 case RangeOutput:
5181 default:
5182 sheet_clear_region (me->dao->sheet,
5183 me->old_range.start.col, me->old_range.start.row,
5184 me->old_range.end.col, me->old_range.end.row,
5185 CLEAR_COMMENTS | CLEAR_FORMATS | CLEAR_NOCHECKARRAY |
5186 CLEAR_RECALC_DEPS | CLEAR_VALUES | CLEAR_MERGES,
5187 GO_CMD_CONTEXT (wbc));
5188 clipboard_paste_region (me->old_contents,
5189 paste_target_init (&pt, me->dao->sheet, &me->old_range, PASTE_ALL_SHEET),
5190 GO_CMD_CONTEXT (wbc));
5191 cellregion_unref (me->old_contents);
5192 me->old_contents = NULL;
5193 if (me->col_info) {
5194 dao_set_colrow_state_list (me->dao, TRUE, me->col_info);
5195 me->col_info = colrow_state_list_destroy (me->col_info);
5197 if (me->row_info) {
5198 dao_set_colrow_state_list (me->dao, FALSE, me->row_info);
5199 me->row_info = colrow_state_list_destroy (me->row_info);
5201 if (me->newSheetObjects == NULL)
5202 me->newSheetObjects = dao_surrender_so (me->dao);
5203 g_slist_foreach (me->newSheetObjects, (GFunc)sheet_object_clear_sheet, NULL);
5204 sheet_update (me->dao->sheet);
5207 return FALSE;
5210 static void
5211 cmd_analysis_tool_draw_old_so (SheetObject *so, data_analysis_output_t *dao)
5213 g_object_ref (so);
5214 dao_set_sheet_object (dao, 0, 1, so);
5217 static gboolean
5218 cmd_analysis_tool_redo (GnmCommand *cmd, WorkbookControl *wbc)
5220 gpointer continuity = NULL;
5221 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5222 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5224 g_return_val_if_fail (me != NULL, TRUE);
5226 /* The old view might not exist anymore */
5227 me->dao->wbc = wbc;
5229 if (me->col_info)
5230 me->col_info = colrow_state_list_destroy (me->col_info);
5231 me->col_info = dao_get_colrow_state_list (me->dao, TRUE);
5232 if (me->row_info)
5233 me->row_info = colrow_state_list_destroy (me->row_info);
5234 me->row_info = dao_get_colrow_state_list (me->dao, FALSE);
5236 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PREPARE_OUTPUT_RANGE, NULL)
5237 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5238 &me->cmd.cmd_descriptor)
5239 || cmd_dao_is_locked_effective (me->dao, wbc, me->cmd.cmd_descriptor)
5240 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_LAST_VALIDITY_CHECK, &continuity))
5241 return TRUE;
5243 switch (me->type) {
5244 case NewSheetOutput:
5245 me->old_contents = NULL;
5246 break;
5247 case NewWorkbookOutput:
5248 /* No undo in this case (see below) */
5249 me->old_contents = NULL;
5250 break;
5251 case RangeOutput:
5252 default:
5253 range_init (&me->old_range, me->dao->start_col, me->dao->start_row,
5254 me->dao->start_col + me->dao->cols - 1,
5255 me->dao->start_row + me->dao->rows - 1);
5256 me->old_contents = clipboard_copy_range (me->dao->sheet, &me->old_range);
5257 break;
5260 if (me->newSheetObjects != NULL)
5261 dao_set_omit_so (me->dao, TRUE);
5263 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_FORMAT_OUTPUT_RANGE, NULL))
5264 return TRUE;
5266 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PERFORM_CALC, &continuity)) {
5267 if (me->type == RangeOutput) {
5268 g_warning ("This is too late for failure! The target region has "
5269 "already been formatted!");
5270 } else
5271 return TRUE;
5273 if (me->newSheetObjects != NULL)
5275 GSList *l = g_slist_reverse
5276 (g_slist_copy (me->newSheetObjects));
5278 dao_set_omit_so (me->dao, FALSE);
5279 g_slist_foreach (l,
5280 (GFunc) cmd_analysis_tool_draw_old_so,
5281 me->dao);
5282 g_slist_free (l);
5285 if (continuity) {
5286 g_warning ("There shouldn't be any data left in here!");
5289 dao_autofit_columns (me->dao);
5290 sheet_mark_dirty (me->dao->sheet);
5291 sheet_update (me->dao->sheet);
5293 /* The concept of an undo if we create a new worksheet is extremely strange,
5294 * since we have separate undo/redo queues per worksheet.
5295 * Users can simply delete the worksheet if they so desire.
5298 return (me->type == NewWorkbookOutput);
5301 static void
5302 cmd_analysis_tool_finalize (GObject *cmd)
5304 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5306 if (me->col_info)
5307 me->col_info = colrow_state_list_destroy (me->col_info);
5308 if (me->row_info)
5309 me->row_info = colrow_state_list_destroy (me->row_info);
5311 me->engine (NULL, me->dao, me->specs, TOOL_ENGINE_CLEAN_UP, NULL);
5313 if (me->specs_owned) {
5314 g_free (me->specs);
5315 dao_free (me->dao);
5317 if (me->old_contents)
5318 cellregion_unref (me->old_contents);
5320 g_slist_free_full (me->newSheetObjects, g_object_unref);
5322 gnm_command_finalize (cmd);
5326 * cmd_analysis_tool: (skip)
5327 * Note: this takes ownership of specs and dao if the command
5328 * succeeds.
5330 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5332 gboolean
5333 cmd_analysis_tool (WorkbookControl *wbc, G_GNUC_UNUSED Sheet *sheet,
5334 data_analysis_output_t *dao, gpointer specs,
5335 analysis_tool_engine engine, gboolean always_take_ownership)
5337 CmdAnalysis_Tool *me;
5338 gboolean trouble;
5339 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5341 g_return_val_if_fail (dao != NULL, TRUE);
5342 g_return_val_if_fail (specs != NULL, TRUE);
5343 g_return_val_if_fail (engine != NULL, TRUE);
5345 me = g_object_new (CMD_ANALYSIS_TOOL_TYPE, NULL);
5347 dao->wbc = wbc;
5349 /* Store the specs for the object */
5350 me->specs = specs;
5351 me->specs_owned = always_take_ownership;
5352 me->dao = dao;
5353 me->engine = engine;
5354 me->cmd.cmd_descriptor = NULL;
5355 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DAO, NULL)) {
5356 g_object_unref (me);
5357 return TRUE;
5359 me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5360 &me->cmd.cmd_descriptor);
5361 me->cmd.sheet = NULL;
5362 me->type = dao->type;
5363 me->row_info = NULL;
5364 me->col_info = NULL;
5366 /* We divide by 2 since many cells will be empty*/
5367 me->cmd.size = 1 + dao->rows * dao->cols / 2;
5369 /* Register the command object */
5370 trouble = gnm_command_push_undo (wbc, G_OBJECT (me));
5372 if (!trouble)
5373 me->specs_owned = TRUE;
5375 return trouble;
5378 /******************************************************************/
5380 #define CMD_MERGE_DATA_TYPE (cmd_merge_data_get_type ())
5381 #define CMD_MERGE_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_DATA_TYPE, CmdMergeData))
5383 typedef struct {
5384 GnmCommand cmd;
5385 GnmValue *merge_zone;
5386 GSList *merge_fields;
5387 GSList *merge_data;
5388 GSList *sheet_list;
5389 Sheet *sheet;
5390 gint n;
5391 } CmdMergeData;
5393 MAKE_GNM_COMMAND (CmdMergeData, cmd_merge_data, NULL)
5395 static void
5396 cmd_merge_data_delete_sheets (gpointer data, gpointer success)
5398 Sheet *sheet = data;
5400 if (!command_undo_sheet_delete (sheet))
5401 *(gboolean *)success = FALSE;
5404 static gboolean
5405 cmd_merge_data_undo (GnmCommand *cmd,
5406 G_GNUC_UNUSED WorkbookControl *wbc)
5408 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5409 gboolean success = TRUE;
5411 g_slist_foreach (me->sheet_list, cmd_merge_data_delete_sheets, &success);
5412 g_slist_free (me->sheet_list);
5413 me->sheet_list = NULL;
5415 return FALSE;
5418 static gboolean
5419 cmd_merge_data_redo (GnmCommand *cmd, WorkbookControl *wbc)
5421 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5422 int i;
5423 GnmCellRegion *merge_contents;
5424 GnmRangeRef *cell = &me->merge_zone->v_range.cell;
5425 GnmPasteTarget pt;
5426 GSList *this_field = me->merge_fields;
5427 GSList *this_data = me->merge_data;
5428 Sheet *source_sheet = cell->a.sheet;
5429 GSList *target_sheet;
5430 GnmRange target_range;
5431 ColRowStateList *state_col;
5432 ColRowStateList *state_row;
5434 range_init (&target_range, cell->a.col, cell->a.row,
5435 cell->b.col, cell->b.row);
5436 merge_contents = clipboard_copy_range (source_sheet, &target_range);
5437 state_col = colrow_get_states (source_sheet, TRUE, target_range.start.col,
5438 target_range.end.col);
5439 state_row = colrow_get_states (source_sheet, FALSE, target_range.start.row,
5440 target_range.end.row);
5442 for (i = 0; i < me->n; i++) {
5443 Sheet *new_sheet;
5445 new_sheet = workbook_sheet_add (me->sheet->workbook, -1,
5446 gnm_sheet_get_max_cols (me->sheet),
5447 gnm_sheet_get_max_rows (me->sheet));
5448 me->sheet_list = g_slist_prepend (me->sheet_list, new_sheet);
5450 colrow_set_states (new_sheet, TRUE, target_range.start.col, state_col);
5451 colrow_set_states (new_sheet, FALSE, target_range.start.row, state_row);
5452 sheet_objects_dup (source_sheet, new_sheet, &target_range);
5453 clipboard_paste_region (merge_contents,
5454 paste_target_init (&pt, new_sheet, &target_range, PASTE_ALL_SHEET),
5455 GO_CMD_CONTEXT (wbc));
5457 cellregion_unref (merge_contents);
5458 me->sheet_list = g_slist_reverse (me->sheet_list);
5459 colrow_state_list_destroy (state_col);
5460 colrow_state_list_destroy (state_row);
5462 while (this_field) {
5463 int col_source, row_source;
5464 int col_target, row_target;
5466 g_return_val_if_fail (this_data != NULL, TRUE);
5467 cell = &((GnmValue *)this_field->data)->v_range.cell;
5468 col_target = cell->a.col;
5469 row_target = cell->a.row;
5471 cell = &((GnmValue *)this_data->data)->v_range.cell;
5472 col_source = cell->a.col;
5473 row_source = cell->a.row;
5474 source_sheet = cell->a.sheet;
5476 target_sheet = me->sheet_list;
5477 while (target_sheet) {
5478 GnmCell *source_cell = sheet_cell_get (source_sheet,
5479 col_source, row_source);
5480 if (source_cell == NULL) {
5481 GnmCell *target_cell = sheet_cell_get ((Sheet *)target_sheet->data,
5482 col_target, row_target);
5483 if (target_cell != NULL)
5484 gnm_cell_set_value (target_cell,
5485 value_new_empty ());
5486 } else {
5487 GnmCell *target_cell = sheet_cell_fetch ((Sheet *)target_sheet->data,
5488 col_target, row_target);
5489 gnm_cell_set_value (target_cell,
5490 value_dup (source_cell->value));
5492 target_sheet = target_sheet->next;
5493 row_source++;
5496 this_field = this_field->next;
5497 this_data = this_data->next;
5500 return FALSE;
5503 static void
5504 cmd_merge_data_finalize (GObject *cmd)
5506 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5508 value_release (me->merge_zone);
5509 me->merge_zone = NULL;
5510 range_list_destroy (me->merge_data);
5511 me->merge_data = NULL;
5512 range_list_destroy (me->merge_fields);
5513 me->merge_fields = NULL;
5514 g_slist_free (me->sheet_list);
5515 me->sheet_list = NULL;
5516 me->n = 0;
5518 gnm_command_finalize (cmd);
5522 * cmd_merge_data:
5523 * @wbc: #WorkbookControl
5524 * @sheet: #Sheet
5525 * @merge_zone: (transfer full): #GnmValue
5526 * @merge_fields: (element-type GnmRange) (transfer full):
5527 * @merge_data: (element-type GnmRange) (transfer full):
5529 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5531 gboolean
5532 cmd_merge_data (WorkbookControl *wbc, Sheet *sheet,
5533 GnmValue *merge_zone, GSList *merge_fields, GSList *merge_data)
5535 CmdMergeData *me;
5536 GnmRangeRef *cell;
5538 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5539 g_return_val_if_fail (merge_zone != NULL, TRUE);
5540 g_return_val_if_fail (merge_fields != NULL, TRUE);
5541 g_return_val_if_fail (merge_data != NULL, TRUE);
5543 me = g_object_new (CMD_MERGE_DATA_TYPE, NULL);
5545 me->cmd.sheet = sheet;
5546 me->sheet = sheet;
5547 me->cmd.size = 1 + g_slist_length (merge_fields);
5548 me->cmd.cmd_descriptor =
5549 g_strdup_printf (_("Merging data into %s"), value_peek_string (merge_zone));
5551 me->merge_zone = merge_zone;
5552 me->merge_fields = merge_fields;
5553 me->merge_data = merge_data;
5554 me->sheet_list = NULL;
5556 cell = &((GnmValue *)merge_data->data)->v_range.cell;
5557 me->n = cell->b.row - cell->a.row + 1;
5559 /* Register the command object */
5560 return gnm_command_push_undo (wbc, G_OBJECT (me));
5563 /******************************************************************/
5565 #define CMD_CHANGE_META_DATA_TYPE (cmd_change_summary_get_type ())
5566 #define CMD_CHANGE_META_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_CHANGE_META_DATA_TYPE, CmdChangeMetaData))
5568 typedef struct {
5569 GnmCommand cmd;
5570 GSList *changed_props;
5571 GSList *removed_names;
5572 } CmdChangeMetaData;
5574 MAKE_GNM_COMMAND (CmdChangeMetaData, cmd_change_summary, NULL)
5576 static gboolean
5577 cmd_change_summary_undo (GnmCommand *cmd, WorkbookControl *wbc)
5579 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5580 GsfDocMetaData *meta = go_doc_get_meta_data (wb_control_get_doc (wbc));
5581 GSList *ptr, *old_vals = NULL, *dropped = NULL;
5582 GsfDocProp *prop;
5583 char const *name;
5585 for (ptr = me->removed_names; ptr != NULL ; ptr = ptr->next) {
5586 if (NULL != (prop = gsf_doc_meta_data_steal (meta, ptr->data)))
5587 old_vals = g_slist_prepend (old_vals, prop);
5588 g_free (ptr->data);
5590 g_slist_free (me->removed_names);
5592 for (ptr = me->changed_props; ptr != NULL ; ptr = ptr->next) {
5593 name = gsf_doc_prop_get_name (ptr->data);
5594 if (NULL != (prop = gsf_doc_meta_data_steal (meta, name)))
5595 old_vals = g_slist_prepend (old_vals, prop);
5596 else
5597 dropped = g_slist_prepend (old_vals, g_strdup (name));
5598 gsf_doc_meta_data_store (meta, ptr->data);
5600 g_slist_free (me->changed_props);
5602 me->removed_names = dropped;
5603 me->changed_props = old_vals;
5604 go_doc_update_meta_data (wb_control_get_doc (wbc));
5606 return FALSE;
5609 static gboolean
5610 cmd_change_summary_redo (GnmCommand *cmd, WorkbookControl *wbc)
5612 return cmd_change_summary_undo (cmd, wbc);
5615 static void
5616 cmd_change_summary_finalize (GObject *cmd)
5618 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5620 g_slist_free_full (me->changed_props, (GDestroyNotify)gsf_doc_prop_free);
5621 me->changed_props = NULL;
5622 g_slist_free_full (me->removed_names, g_free);
5623 me->removed_names = NULL;
5625 gnm_command_finalize (cmd);
5629 * cmd_change_meta_data:
5630 * @wbc: #WorkbookControl
5631 * @changes: (element-type GsfDocMetaData) (transfer full): the changed metadata.
5632 * @removed: (element-type GsfDocMetaData) (transfer full): the removed metadata.
5634 * Returns: %TRUE if there was a problem, %FALSE otherwise.
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: (transfer full): #GnmExprTop
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 if there was a problem, %FALSE otherwise.
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.
6195 * @scope:
6197 * Returns: %TRUE if there was a problem, %FALSE otherwise.
6199 gboolean
6200 cmd_rescope_name (WorkbookControl *wbc, GnmNamedExpr *nexpr, Sheet *scope)
6202 CmdRescopeName *me;
6204 g_return_val_if_fail (wbc != NULL, TRUE);
6205 g_return_val_if_fail (nexpr != NULL, TRUE);
6206 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6208 expr_name_ref (nexpr);
6210 me = g_object_new (CMD_RESCOPE_NAME_TYPE, NULL);
6211 me->nexpr = nexpr;
6212 me->scope = scope;
6213 me->cmd.sheet = wb_control_cur_sheet (wbc);
6214 me->cmd.size = 1;
6215 me->cmd.cmd_descriptor = g_strdup_printf (_("Change Scope of Name %s"),
6216 expr_name_name (nexpr));
6218 return gnm_command_push_undo (wbc, G_OBJECT (me));
6220 /******************************************************************/
6222 #define CMD_SCENARIO_ADD_TYPE (cmd_scenario_add_get_type ())
6223 #define CMD_SCENARIO_ADD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_ADD_TYPE, CmdScenarioAdd))
6225 typedef struct {
6226 GnmCommand cmd;
6227 GnmScenario *scenario;
6228 } CmdScenarioAdd;
6230 MAKE_GNM_COMMAND (CmdScenarioAdd, cmd_scenario_add, NULL)
6232 static gboolean
6233 cmd_scenario_add_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6235 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6236 GnmScenario *sc = g_object_ref (me->scenario);
6237 gnm_sheet_scenario_add (sc->sheet, sc);
6238 return FALSE;
6241 static gboolean
6242 cmd_scenario_add_undo (GnmCommand *cmd,
6243 G_GNUC_UNUSED WorkbookControl *wbc)
6245 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6246 GnmScenario *sc = me->scenario;
6247 gnm_sheet_scenario_remove (sc->sheet, sc);
6248 return FALSE;
6251 static void
6252 cmd_scenario_add_finalize (GObject *cmd)
6254 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6256 g_object_unref (me->scenario);
6257 gnm_command_finalize (cmd);
6261 * cmd_scenario_add: (skip)
6262 * @wbc:
6263 * @s: (transfer full):
6264 * @sheet:
6266 * Returns: %TRUE if there was a problem, %FALSE otherwise.
6268 gboolean
6269 cmd_scenario_add (WorkbookControl *wbc, GnmScenario *s, Sheet *sheet)
6271 CmdScenarioAdd *me;
6273 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6274 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6276 me = g_object_new (CMD_SCENARIO_ADD_TYPE, NULL);
6278 me->scenario = s; /* Take ownership */
6279 me->cmd.sheet = sheet;
6280 me->cmd.size = 1;
6281 me->cmd.cmd_descriptor = g_strdup (_("Add scenario"));
6283 return gnm_command_push_undo (wbc, G_OBJECT (me));
6286 /******************************************************************/
6288 #define CMD_SCENARIO_MNGR_TYPE (cmd_scenario_mngr_get_type ())
6289 #define CMD_SCENARIO_MNGR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_MNGR_TYPE, CmdScenarioMngr))
6291 typedef struct {
6292 GnmCommand cmd;
6293 GnmScenario *sc;
6294 GOUndo *undo;
6295 } CmdScenarioMngr;
6297 MAKE_GNM_COMMAND (CmdScenarioMngr, cmd_scenario_mngr, NULL)
6299 static gboolean
6300 cmd_scenario_mngr_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6302 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6303 if (!me->undo)
6304 me->undo = gnm_scenario_apply (me->sc);
6305 return FALSE;
6308 static gboolean
6309 cmd_scenario_mngr_undo (GnmCommand *cmd,
6310 G_GNUC_UNUSED WorkbookControl *wbc)
6312 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6313 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6314 g_object_unref (me->undo);
6315 me->undo = NULL;
6316 return FALSE;
6319 static void
6320 cmd_scenario_mngr_finalize (GObject *cmd)
6322 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6324 g_object_unref (me->sc);
6325 if (me->undo)
6326 g_object_unref (me->undo);
6328 gnm_command_finalize (cmd);
6331 gboolean
6332 cmd_scenario_mngr (WorkbookControl *wbc, GnmScenario *sc, GOUndo *undo)
6334 CmdScenarioMngr *me;
6336 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6337 g_return_val_if_fail (GNM_IS_SCENARIO (sc), TRUE);
6339 me = g_object_new (CMD_SCENARIO_MNGR_TYPE, NULL);
6341 me->sc = g_object_ref (sc);
6342 me->undo = g_object_ref (undo);
6343 me->cmd.sheet = sc->sheet;
6344 me->cmd.size = 1;
6345 me->cmd.cmd_descriptor = g_strdup (_("Scenario Show"));
6347 return gnm_command_push_undo (wbc, G_OBJECT (me));
6350 /******************************************************************/
6352 #define CMD_DATA_SHUFFLE_TYPE (cmd_data_shuffle_get_type ())
6353 #define CMD_DATA_SHUFFLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DATA_SHUFFLE_TYPE, CmdDataShuffle))
6355 typedef struct {
6356 GnmCommand cmd;
6357 data_shuffling_t *ds;
6358 } CmdDataShuffle;
6360 MAKE_GNM_COMMAND (CmdDataShuffle, cmd_data_shuffle, NULL)
6362 static gboolean
6363 cmd_data_shuffle_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6365 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6367 data_shuffling_redo (me->ds);
6368 return FALSE;
6371 static gboolean
6372 cmd_data_shuffle_undo (GnmCommand *cmd,
6373 G_GNUC_UNUSED WorkbookControl *wbc)
6375 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6377 data_shuffling_redo (me->ds);
6378 return FALSE;
6381 static void
6382 cmd_data_shuffle_finalize (GObject *cmd)
6384 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6386 data_shuffling_free (me->ds);
6387 gnm_command_finalize (cmd);
6390 gboolean
6391 cmd_data_shuffle (WorkbookControl *wbc, data_shuffling_t *sc, Sheet *sheet)
6393 CmdDataShuffle *me;
6395 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6396 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6398 me = g_object_new (CMD_DATA_SHUFFLE_TYPE, NULL);
6400 me->ds = sc;
6401 me->cmd.sheet = sheet;
6402 me->cmd.size = 1;
6403 me->cmd.cmd_descriptor = g_strdup (_("Shuffle Data"));
6405 return gnm_command_push_undo (wbc, G_OBJECT (me));
6408 /******************************************************************/
6410 #define CMD_TEXT_TO_COLUMNS_TYPE (cmd_text_to_columns_get_type ())
6411 #define CMD_TEXT_TO_COLUMNS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TEXT_TO_COLUMNS_TYPE, CmdTextToColumns))
6413 typedef struct {
6414 GnmCommand cmd;
6416 GnmCellRegion *contents;
6417 GnmPasteTarget dst;
6418 GnmRange src;
6419 Sheet *src_sheet;
6420 ColRowStateList *saved_sizes;
6421 } CmdTextToColumns;
6423 MAKE_GNM_COMMAND (CmdTextToColumns, cmd_text_to_columns, NULL)
6425 static gboolean
6426 cmd_text_to_columns_impl (GnmCommand *cmd, WorkbookControl *wbc,
6427 gboolean is_undo)
6429 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6430 GnmCellRegion *contents;
6432 g_return_val_if_fail (me != NULL, TRUE);
6433 g_return_val_if_fail (me->contents != NULL, TRUE);
6435 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
6436 if (clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc))) {
6437 /* There was a problem, avoid leaking */
6438 cellregion_unref (contents);
6439 return TRUE;
6442 cellregion_unref (me->contents);
6444 if (is_undo) {
6445 colrow_set_states (me->dst.sheet, FALSE,
6446 me->dst.range.start.row, me->saved_sizes);
6447 colrow_state_list_destroy (me->saved_sizes);
6448 me->saved_sizes = NULL;
6449 } else {
6450 me->saved_sizes = colrow_get_states (me->dst.sheet,
6451 FALSE, me->dst.range.start.row, me->dst.range.end.row);
6452 rows_height_update (me->dst.sheet, &me->dst.range, FALSE);
6455 me->contents = contents;
6457 /* Select the newly pasted contents (this queues a redraw) */
6458 select_range (me->dst.sheet, &me->dst.range, wbc);
6460 return FALSE;
6463 static gboolean
6464 cmd_text_to_columns_undo (GnmCommand *cmd, WorkbookControl *wbc)
6466 return cmd_text_to_columns_impl (cmd, wbc, TRUE);
6469 static gboolean
6470 cmd_text_to_columns_redo (GnmCommand *cmd, WorkbookControl *wbc)
6472 return cmd_text_to_columns_impl (cmd, wbc, FALSE);
6475 static void
6476 cmd_text_to_columns_finalize (GObject *cmd)
6478 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6480 if (me->saved_sizes)
6481 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
6482 if (me->contents) {
6483 cellregion_unref (me->contents);
6484 me->contents = NULL;
6486 gnm_command_finalize (cmd);
6489 gboolean
6490 cmd_text_to_columns (WorkbookControl *wbc,
6491 GnmRange const *src, Sheet *src_sheet,
6492 GnmRange const *target, Sheet *target_sheet,
6493 GnmCellRegion *contents)
6495 CmdTextToColumns *me;
6496 char *src_range_name, *target_range_name;
6498 g_return_val_if_fail (contents != NULL, TRUE);
6500 src_range_name = undo_range_name (src_sheet, src);
6501 target_range_name = undo_range_name (target_sheet, target);
6503 me = g_object_new (CMD_TEXT_TO_COLUMNS_TYPE, NULL);
6505 me->cmd.sheet = (src_sheet == target_sheet ? src_sheet : NULL);
6506 me->cmd.size = 1; /* FIXME? */
6507 me->cmd.cmd_descriptor = g_strdup_printf (_("Text (%s) to Columns (%s)"),
6508 src_range_name,
6509 target_range_name);
6510 me->dst.range = *target;
6511 me->dst.sheet = target_sheet;
6512 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
6513 me->src = *src;
6514 me->src_sheet = src_sheet;
6515 me->contents = contents;
6516 me->saved_sizes = NULL;
6518 g_free (src_range_name);
6519 g_free (target_range_name);
6521 /* Check array subdivision & merged regions */
6522 if (sheet_range_splits_region (target_sheet, &me->dst.range,
6523 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
6524 g_object_unref (me);
6525 return TRUE;
6528 return gnm_command_push_undo (wbc, G_OBJECT (me));
6531 /******************************************************************/
6533 #define CMD_GENERIC_TYPE (cmd_generic_get_type ())
6534 #define CMD_GENERIC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GENERIC_TYPE, CmdGeneric))
6536 typedef struct {
6537 GnmCommand cmd;
6538 GOUndo *undo, *redo;
6539 } CmdGeneric;
6541 MAKE_GNM_COMMAND (CmdGeneric, cmd_generic, NULL)
6543 static gboolean
6544 cmd_generic_undo (GnmCommand *cmd, WorkbookControl *wbc)
6546 CmdGeneric *me = CMD_GENERIC (cmd);
6547 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6548 return FALSE;
6551 static gboolean
6552 cmd_generic_redo (GnmCommand *cmd, WorkbookControl *wbc)
6554 CmdGeneric *me = CMD_GENERIC (cmd);
6555 go_undo_undo_with_data (me->redo, GO_CMD_CONTEXT (wbc));
6556 return FALSE;
6559 static void
6560 cmd_generic_finalize (GObject *cmd)
6562 CmdGeneric *me = CMD_GENERIC (cmd);
6564 g_object_unref (me->undo);
6565 g_object_unref (me->redo);
6567 gnm_command_finalize (cmd);
6570 gboolean
6571 cmd_generic_with_size (WorkbookControl *wbc, const char *txt,
6572 int size,
6573 GOUndo *undo, GOUndo *redo)
6575 CmdGeneric *me;
6577 g_return_val_if_fail (GO_IS_UNDO (undo), TRUE);
6578 g_return_val_if_fail (GO_IS_UNDO (redo), TRUE);
6580 me = g_object_new (CMD_GENERIC_TYPE, NULL);
6582 me->cmd.sheet = wb_control_cur_sheet (wbc);
6583 me->cmd.size = size;
6584 me->cmd.cmd_descriptor = g_strdup (txt);
6586 me->undo = undo;
6587 me->redo = redo;
6589 return gnm_command_push_undo (wbc, G_OBJECT (me));
6592 gboolean
6593 cmd_generic (WorkbookControl *wbc, const char *txt, GOUndo *undo, GOUndo *redo)
6595 return cmd_generic_with_size (wbc, txt, 1, undo, redo);
6598 /******************************************************************/
6600 #define CMD_GOAL_SEEK_TYPE (cmd_goal_seek_get_type ())
6601 #define CMD_GOAL_SEEK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GOAL_SEEK_TYPE, CmdGoalSeek))
6603 typedef struct {
6604 GnmCommand cmd;
6606 GnmCell *cell;
6607 GnmValue *ov;
6608 GnmValue *nv;
6609 } CmdGoalSeek;
6611 MAKE_GNM_COMMAND (CmdGoalSeek, cmd_goal_seek, NULL)
6613 static gboolean
6614 cmd_goal_seek_impl (GnmCell *cell, GnmValue *value)
6616 sheet_cell_set_value (cell, value_dup(value));
6617 return FALSE;
6621 static gboolean
6622 cmd_goal_seek_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6624 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6626 return cmd_goal_seek_impl (me->cell, me->ov);
6629 static gboolean
6630 cmd_goal_seek_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6632 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6634 return cmd_goal_seek_impl (me->cell, me->nv);
6637 static void
6638 cmd_goal_seek_finalize (GObject *cmd)
6640 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6642 value_release (me->ov);
6643 me->ov = NULL;
6644 value_release (me->nv);
6645 me->nv = NULL;
6647 gnm_command_finalize (cmd);
6650 gboolean
6651 cmd_goal_seek (WorkbookControl *wbc, GnmCell *cell, GnmValue *ov, GnmValue *nv)
6653 CmdGoalSeek *me;
6654 GnmRange range;
6656 g_return_val_if_fail (cell != NULL, TRUE);
6657 g_return_val_if_fail (ov != NULL || nv != NULL, TRUE);
6659 me = g_object_new (CMD_GOAL_SEEK_TYPE, NULL);
6661 me->cmd.sheet = cell->base.sheet;
6662 me->cmd.size = 1;
6663 range_init_cellpos (&range, &cell->pos);
6664 me->cmd.cmd_descriptor = g_strdup_printf
6665 (_("Goal Seek (%s)"), undo_range_name (cell->base.sheet, &range));
6667 me->cell = cell;
6668 me->ov = ov;
6669 me->nv = nv;
6671 if (me->ov == NULL)
6672 me->ov = value_dup (cell->value);
6673 if (me->nv == NULL)
6674 me->nv = value_dup (cell->value);
6676 return gnm_command_push_undo (wbc, G_OBJECT (me));
6679 /******************************************************************/
6681 #if 0
6682 #define CMD_FREEZE_PANES_TYPE (cmd_freeze_panes_get_type ())
6683 #define CMD_FREEZE_PANES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FREEZE_PANES_TYPE, CmdFreezePanes))
6685 typedef struct {
6686 GnmCommand cmd;
6688 SheetView *sv;
6689 GnmCellPos pos;
6690 } CmdFreezePanes;
6692 MAKE_GNM_COMMAND (CmdFreezePanes, cmd_freeze_panes, NULL)
6694 static gboolean
6695 cmd_freeze_panes_undo (GnmCommand *cmd, WorkbookControl *wbc)
6697 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6699 return FALSE;
6702 static gboolean
6703 cmd_freeze_panes_redo (GnmCommand *cmd, WorkbookControl *wbc)
6705 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6707 return FALSE;
6710 static void
6711 cmd_freeze_panes_finalize (GObject *cmd)
6713 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6715 gnm_command_finalize (cmd);
6719 * cmd_freeze_panes:
6720 * @wbc: where to report errors
6721 * @sv: the view to freeze
6722 * @frozen:
6723 * @unfrozen:
6725 * Returns: %TRUE if there was a problem, %FALSE otherwise.
6727 gboolean
6728 cmd_freeze_panes (WorkbookControl *wbc, SheetView *sv,
6729 GnmCellPos const *frozen, GnmCellPos const *unfrozen)
6731 CmdFreezePanes *me;
6733 g_return_val_if_fail (name != NULL, TRUE);
6734 g_return_val_if_fail (pp != NULL, TRUE);
6735 g_return_val_if_fail (expr != NULL, TRUE);
6737 me = g_object_new (CMD_FREEZE_PANES_TYPE, NULL);
6738 me->sv = sv;
6739 me->frozen = f;
6740 me->unfrozen = expr;
6741 return gnm_command_push_undo (wbc, G_OBJECT (me));
6744 #endif
6747 /******************************************************************/
6750 #define CMD_TABULATE_TYPE (cmd_tabulate_get_type ())
6751 #define CMD_TABULATE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TABULATE_TYPE, CmdTabulate))
6753 typedef struct {
6754 GnmCommand cmd;
6755 GSList *sheet_idx;
6756 GnmTabulateInfo *data;
6757 } CmdTabulate;
6759 MAKE_GNM_COMMAND (CmdTabulate, cmd_tabulate, NULL)
6761 static gint
6762 cmd_tabulate_cmp_f (gconstpointer a,
6763 gconstpointer b)
6765 guint const a_val = GPOINTER_TO_INT (a);
6766 guint const b_val = GPOINTER_TO_INT (b);
6768 if (a_val > b_val)
6769 return -1;
6770 if (a_val < b_val)
6771 return 1;
6772 return 0;
6775 static gboolean
6776 cmd_tabulate_undo (GnmCommand *cmd, WorkbookControl *wbc)
6778 CmdTabulate *me = CMD_TABULATE (cmd);
6779 GSList *l;
6780 gboolean res = TRUE;
6782 me->sheet_idx = g_slist_sort (me->sheet_idx,
6783 cmd_tabulate_cmp_f);
6785 for (l = me->sheet_idx; l != NULL; l = l->next) {
6786 int i = GPOINTER_TO_INT (l->data);
6787 Sheet *new_sheet =
6788 workbook_sheet_by_index (wb_control_get_workbook (wbc),
6790 res = res && command_undo_sheet_delete (new_sheet);
6792 return !res;
6795 static gboolean
6796 cmd_tabulate_redo (GnmCommand *cmd, WorkbookControl *wbc)
6798 CmdTabulate *me = CMD_TABULATE (cmd);
6800 g_slist_free (me->sheet_idx);
6801 me->sheet_idx = do_tabulation (wbc, me->data);
6803 return (me->sheet_idx == NULL);
6806 static void
6807 cmd_tabulate_finalize (GObject *cmd)
6809 CmdTabulate *me = CMD_TABULATE (cmd);
6811 g_free (me->data->cells);
6812 g_free (me->data->minima);
6813 g_free (me->data->maxima);
6814 g_free (me->data->steps);
6815 g_free (me->data);
6816 gnm_command_finalize (cmd);
6819 gboolean
6820 cmd_tabulate (WorkbookControl *wbc, gpointer data)
6822 CmdTabulate *me;
6824 g_return_val_if_fail (data != NULL, TRUE);
6826 me = g_object_new (CMD_TABULATE_TYPE, NULL);
6828 me->cmd.sheet = NULL;
6829 me->cmd.size = 1;
6830 me->cmd.cmd_descriptor =
6831 g_strdup_printf (_("Tabulating Dependencies"));
6832 me->data = data;
6833 me->sheet_idx = NULL;
6835 return gnm_command_push_undo (wbc, G_OBJECT (me));
6838 /******************************************************************/
6840 #define CMD_SO_GRAPH_CONFIG_TYPE (cmd_so_graph_config_get_type ())
6841 #define CMD_SO_GRAPH_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_GRAPH_CONFIG_TYPE, CmdSOGraphConfig))
6843 typedef struct {
6844 GnmCommand cmd;
6845 SheetObject *so;
6846 GogGraph *new_graph;
6847 GogGraph *old_graph;
6848 } CmdSOGraphConfig;
6850 MAKE_GNM_COMMAND (CmdSOGraphConfig, cmd_so_graph_config, NULL)
6852 static gboolean
6853 cmd_so_graph_config_redo (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->new_graph);
6858 return FALSE;
6861 static gboolean
6862 cmd_so_graph_config_undo (GnmCommand *cmd,
6863 G_GNUC_UNUSED WorkbookControl *wbc)
6865 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6866 sheet_object_graph_set_gog (me->so, me->old_graph);
6867 return FALSE;
6870 static void
6871 cmd_so_graph_config_finalize (GObject *cmd)
6873 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6875 g_object_unref (me->so);
6876 g_object_unref (me->new_graph);
6877 g_object_unref (me->old_graph);
6879 gnm_command_finalize (cmd);
6882 gboolean
6883 cmd_so_graph_config (WorkbookControl *wbc, SheetObject *so,
6884 GObject *n_graph, GObject *o_graph)
6886 CmdSOGraphConfig *me;
6888 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6889 g_return_val_if_fail (GNM_IS_SO_GRAPH (so), TRUE);
6890 g_return_val_if_fail (GOG_IS_GRAPH (n_graph), TRUE);
6891 g_return_val_if_fail (GOG_IS_GRAPH (o_graph), TRUE);
6893 me = g_object_new (CMD_SO_GRAPH_CONFIG_TYPE, NULL);
6895 me->so = so;
6896 g_object_ref (so);
6898 me->new_graph = GOG_GRAPH (n_graph);
6899 g_object_ref (me->new_graph);
6900 me->old_graph = GOG_GRAPH (o_graph);
6901 g_object_ref (me->old_graph);
6903 me->cmd.sheet = sheet_object_get_sheet (so);
6904 me->cmd.size = 10;
6905 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Graph"));
6907 return gnm_command_push_undo (wbc, G_OBJECT (me));
6910 /******************************************************************/
6912 #define CMD_SO_COMPONENT_CONFIG_TYPE (cmd_so_component_config_get_type ())
6913 #define CMD_SO_COMPONENT_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_COMPONENT_CONFIG_TYPE, CmdSOComponentConfig))
6915 typedef struct {
6916 GnmCommand cmd;
6917 SheetObject *so;
6918 GOComponent *new_obj;
6919 GOComponent *old_obj;
6920 } CmdSOComponentConfig;
6922 MAKE_GNM_COMMAND (CmdSOComponentConfig, cmd_so_component_config, NULL)
6924 static gboolean
6925 cmd_so_component_config_redo (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->new_obj);
6930 return FALSE;
6933 static gboolean
6934 cmd_so_component_config_undo (GnmCommand *cmd,
6935 G_GNUC_UNUSED WorkbookControl *wbc)
6937 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6938 sheet_object_component_set_component (me->so, me->old_obj);
6939 return FALSE;
6942 static void
6943 cmd_so_component_config_finalize (GObject *cmd)
6945 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6947 g_object_unref (me->so);
6948 g_object_unref (me->new_obj);
6949 g_object_unref (me->old_obj);
6951 gnm_command_finalize (cmd);
6954 gboolean
6955 cmd_so_component_config (WorkbookControl *wbc, SheetObject *so,
6956 GObject *n_obj, GObject *o_obj)
6958 CmdSOComponentConfig *me;
6960 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6961 g_return_val_if_fail (GNM_IS_SO_COMPONENT (so), TRUE);
6962 g_return_val_if_fail (GO_IS_COMPONENT (n_obj), TRUE);
6963 g_return_val_if_fail (GO_IS_COMPONENT (o_obj), TRUE);
6965 me = g_object_new (CMD_SO_COMPONENT_CONFIG_TYPE, NULL);
6967 me->so = so;
6968 g_object_ref (so);
6970 me->new_obj = GO_COMPONENT (g_object_ref (n_obj));
6971 me->old_obj = GO_COMPONENT (g_object_ref (o_obj));
6973 me->cmd.sheet = sheet_object_get_sheet (so);
6974 me->cmd.size = 10;
6975 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Object"));
6977 return gnm_command_push_undo (wbc, G_OBJECT (me));
6980 /******************************************************************/
6982 #define CMD_TOGGLE_RTL_TYPE (cmd_toggle_rtl_get_type ())
6983 #define CMD_TOGGLE_RTL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TOGGLE_RTL_TYPE, CmdToggleRTL))
6985 typedef GnmCommand CmdToggleRTL;
6987 MAKE_GNM_COMMAND (CmdToggleRTL, cmd_toggle_rtl, NULL)
6989 static gboolean
6990 cmd_toggle_rtl_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6992 go_object_toggle (cmd->sheet, "text-is-rtl");
6993 return FALSE;
6996 static gboolean
6997 cmd_toggle_rtl_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6999 return cmd_toggle_rtl_redo (cmd, wbc);
7002 static void
7003 cmd_toggle_rtl_finalize (GObject *cmd)
7005 gnm_command_finalize (cmd);
7008 gboolean
7009 cmd_toggle_rtl (WorkbookControl *wbc, Sheet *sheet)
7011 CmdToggleRTL *me;
7013 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7014 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
7016 me = g_object_new (CMD_TOGGLE_RTL_TYPE, NULL);
7017 me->sheet = sheet;
7018 me->size = 1;
7019 me->cmd_descriptor = g_strdup (sheet->text_is_rtl ? _("Left to Right") : _("Right to Left"));
7021 return gnm_command_push_undo (wbc, G_OBJECT (me));
7024 /******************************************************************/
7026 #define CMD_SO_SET_VALUE_TYPE (cmd_so_set_value_get_type ())
7027 #define CMD_SO_SET_VALUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_VALUE_TYPE, CmdSOSetValue))
7029 typedef struct {
7030 GnmCommand cmd;
7031 GnmCellRef ref;
7032 GnmValue *val;
7033 GOUndo *undo;
7034 } CmdSOSetValue;
7036 MAKE_GNM_COMMAND (CmdSOSetValue, cmd_so_set_value, NULL)
7038 static gboolean
7039 cmd_so_set_value_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7041 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7042 Sheet *sheet = me->ref.sheet;
7043 GnmCell *cell = sheet_cell_fetch (sheet, me->ref.col, me->ref.row);
7045 sheet_cell_set_value (cell, value_dup (me->val));
7046 sheet_update (sheet);
7048 return FALSE;
7051 static gboolean
7052 cmd_so_set_value_undo (GnmCommand *cmd, WorkbookControl *wbc)
7054 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7056 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
7058 return FALSE;
7061 static void
7062 cmd_so_set_value_finalize (GObject *cmd)
7064 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7066 value_release (me->val);
7067 g_object_unref (me->undo);
7069 gnm_command_finalize (cmd);
7072 gboolean
7073 cmd_so_set_value (WorkbookControl *wbc,
7074 const char *text,
7075 const GnmCellRef *pref,
7076 GnmValue *new_val,
7077 Sheet *sheet)
7079 CmdSOSetValue *me;
7080 GnmRange r;
7082 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7084 r.start.col = r.end.col = pref->col;
7085 r.start.row = r.end.row = pref->row;
7087 me = g_object_new (CMD_SO_SET_VALUE_TYPE, NULL);
7088 me->cmd.sheet = sheet;
7089 me->cmd.size = 1;
7090 me->cmd.cmd_descriptor = g_strdup (text);
7091 me->ref = *pref;
7092 me->val = new_val;
7093 me->undo = clipboard_copy_range_undo (pref->sheet, &r);
7095 return gnm_command_push_undo (wbc, G_OBJECT (me));
7098 /******************************************************************/
7100 #define CMD_HYPERLINK_TYPE (cmd_hyperlink_get_type ())
7101 #define CMD_HYPERLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_HYPERLINK_TYPE, CmdHyperlink))
7103 typedef struct {
7104 GnmCommand cmd;
7105 GSList *selection;
7106 GnmStyle *new_style;
7107 char *opt_content;
7108 GOUndo *undo;
7109 gboolean update_size;
7110 } CmdHyperlink;
7112 static void
7113 cmd_hyperlink_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
7115 CmdHyperlink const *orig = (CmdHyperlink const *) cmd;
7117 if (orig->new_style)
7118 gnm_style_ref (orig->new_style);
7120 cmd_selection_hyperlink (wbc, orig->new_style, NULL,
7121 g_strdup (orig->opt_content));
7123 MAKE_GNM_COMMAND (CmdHyperlink, cmd_hyperlink, cmd_hyperlink_repeat)
7125 static gboolean
7126 cmd_hyperlink_undo (GnmCommand *cmd, WorkbookControl *wbc)
7128 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7129 Workbook *wb = wb_control_get_workbook (wbc);
7131 if (me->undo) {
7132 go_undo_undo (me->undo);
7133 g_clear_object (&me->undo);
7136 select_selection (me->cmd.sheet, me->selection, wbc);
7138 WORKBOOK_FOREACH_CONTROL (wb, view, ctl, {
7139 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS);
7142 return FALSE;
7145 static GnmValue *
7146 cb_hyperlink_set_text (GnmCellIter const *iter, gpointer user)
7148 CmdHyperlink *me = user;
7149 GnmCell *cell = iter->cell;
7151 if (cell == NULL)
7152 cell = sheet_cell_fetch (iter->pp.sheet,
7153 iter->pp.eval.col,
7154 iter->pp.eval.row);
7156 /* We skip non-empty cells. */
7157 if (gnm_cell_is_empty (cell) &&
7158 !gnm_cell_is_nonsingleton_array (cell)) {
7159 sheet_cell_set_value (cell, value_new_string (me->opt_content));
7160 if (me->update_size)
7161 me->cmd.size++;
7164 return NULL;
7167 static gboolean
7168 cmd_hyperlink_redo (GnmCommand *cmd, WorkbookControl *wbc)
7170 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7171 GSList *l;
7172 Workbook *wb = wb_control_get_workbook (wbc);
7173 Sheet *sheet;
7175 g_return_val_if_fail (me != NULL, TRUE);
7177 sheet = me->cmd.sheet;
7179 /* Check for locked cells */
7180 if (cmd_selection_is_locked_effective (sheet, me->selection,
7181 wbc, _("Changing Hyperlink")))
7182 return TRUE;
7184 me->undo = clipboard_copy_ranges_undo (sheet, me->selection);
7186 for (l = me->selection; l; l = l->next) {
7187 GnmRange const *r = l->data;
7189 if (me->new_style) {
7190 gnm_style_ref (me->new_style);
7191 sheet_apply_style (sheet, r, me->new_style);
7192 sheet_flag_style_update_range (sheet, r);
7195 if (me->opt_content) {
7196 sheet_foreach_cell_in_range (sheet, CELL_ITER_ALL, r,
7197 cb_hyperlink_set_text,
7198 me);
7201 me->update_size = FALSE;
7203 sheet_redraw_all (sheet, FALSE);
7204 sheet_mark_dirty (sheet);
7206 select_selection (sheet, me->selection, wbc);
7208 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
7209 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
7211 return FALSE;
7214 static void
7215 cmd_hyperlink_finalize (GObject *cmd)
7217 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7219 g_clear_object (&me->undo);
7221 if (me->new_style)
7222 gnm_style_unref (me->new_style);
7223 me->new_style = NULL;
7225 range_fragment_free (me->selection);
7226 me->selection = NULL;
7228 g_free (me->opt_content);
7230 gnm_command_finalize (cmd);
7234 * cmd_selection_hyperlink:
7235 * @wbc: the workbook control.
7236 * @style: (transfer full): style to apply to the selection
7237 * @opt_translated_name: An optional name to use in place of 'Hyperlink Cells'
7238 * @opt_content: optional content for otherwise empty cells.
7240 * Returns: %TRUE if there was a problem, %FALSE otherwise.
7242 gboolean
7243 cmd_selection_hyperlink (WorkbookControl *wbc,
7244 GnmStyle *style,
7245 char const *opt_translated_name,
7246 char *opt_content)
7248 CmdHyperlink *me;
7249 SheetView *sv = wb_control_cur_sheet_view (wbc);
7251 me = g_object_new (CMD_HYPERLINK_TYPE, NULL);
7253 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
7254 me->new_style = style;
7256 me->cmd.sheet = sv_sheet (sv);
7257 me->cmd.size = 1; /* Updated later. */
7258 me->update_size = TRUE;
7260 me->opt_content = opt_content;
7262 if (opt_translated_name == NULL) {
7263 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
7265 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing hyperlink of %s"), names);
7266 g_free (names);
7267 } else
7268 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
7271 return gnm_command_push_undo (wbc, G_OBJECT (me));
7274 /******************************************************************/
7277 #define CMD_SO_SET_LINKS_TYPE (cmd_so_set_links_get_type ())
7278 #define CMD_SO_SET_LINKS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_LINKS_TYPE, CmdSOSetLink))
7280 typedef struct {
7281 GnmCommand cmd;
7282 SheetObject *so;
7283 GnmExprTop const *output;
7284 GnmExprTop const *content;
7285 gboolean as_index;
7286 } CmdSOSetLink;
7288 MAKE_GNM_COMMAND (CmdSOSetLink, cmd_so_set_links, NULL)
7290 static gboolean
7291 cmd_so_set_links_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7293 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7294 GnmExprTop const *old_output;
7295 GnmExprTop const *old_content;
7296 gboolean old_as_index;
7298 old_content = sheet_widget_list_base_get_content_link (me->so);
7299 old_output = sheet_widget_list_base_get_result_link (me->so);
7300 old_as_index = sheet_widget_list_base_result_type_is_index (me->so);
7302 sheet_widget_list_base_set_links
7303 (me->so, me->output, me->content);
7304 if (old_as_index != me->as_index) {
7305 sheet_widget_list_base_set_result_type (me->so, me->as_index);
7306 me->as_index = old_as_index;
7308 if (me->output)
7309 gnm_expr_top_unref (me->output);
7310 if (me->content)
7311 gnm_expr_top_unref (me->content);
7312 me->output = old_output;
7313 me->content = old_content;
7315 return FALSE;
7318 static gboolean
7319 cmd_so_set_links_undo (GnmCommand *cmd, WorkbookControl *wbc)
7321 return cmd_so_set_links_redo (cmd, wbc);
7324 static void
7325 cmd_so_set_links_finalize (GObject *cmd)
7327 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7329 if (me->output)
7330 gnm_expr_top_unref (me->output);
7331 if (me->content)
7332 gnm_expr_top_unref (me->content);
7333 gnm_command_finalize (cmd);
7336 gboolean
7337 cmd_so_set_links (WorkbookControl *wbc,
7338 SheetObject *so,
7339 GnmExprTop const *output,
7340 GnmExprTop const *content,
7341 gboolean as_index)
7343 CmdSOSetLink *me;
7345 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7347 me = g_object_new (CMD_SO_SET_LINKS_TYPE, NULL);
7348 me->cmd.sheet = sheet_object_get_sheet (so);
7349 me->cmd.size = 1;
7350 me->cmd.cmd_descriptor = g_strdup (_("Configure List"));
7351 me->so = so;
7352 me->output = output;
7353 me->content = content;
7354 me->as_index = as_index;
7356 return gnm_command_push_undo (wbc, G_OBJECT (me));
7359 /******************************************************************/
7363 #define CMD_SO_SET_FRAME_LABEL_TYPE (cmd_so_set_frame_label_get_type ())
7364 #define CMD_SO_SET_FRAME_LABEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_FRAME_LABEL_TYPE, CmdSOSetFrameLabel))
7366 typedef struct {
7367 GnmCommand cmd;
7368 SheetObject *so;
7369 char *old_label;
7370 char *new_label;
7371 } CmdSOSetFrameLabel;
7373 MAKE_GNM_COMMAND (CmdSOSetFrameLabel, cmd_so_set_frame_label, NULL)
7375 static gboolean
7376 cmd_so_set_frame_label_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7378 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7380 sheet_widget_frame_set_label (me->so, me->new_label);
7382 return FALSE;
7385 static gboolean
7386 cmd_so_set_frame_label_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7388 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7390 sheet_widget_frame_set_label (me->so, me->old_label);
7392 return FALSE;
7395 static void
7396 cmd_so_set_frame_label_finalize (GObject *cmd)
7398 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7400 g_free (me->old_label);
7401 me->old_label = NULL;
7403 g_free (me->new_label);
7404 me->new_label = NULL;
7406 gnm_command_finalize (cmd);
7409 gboolean
7410 cmd_so_set_frame_label (WorkbookControl *wbc,
7411 SheetObject *so,
7412 char *old_label, char *new_label )
7414 CmdSOSetFrameLabel *me;
7416 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7418 me = g_object_new (CMD_SO_SET_FRAME_LABEL_TYPE, NULL);
7419 me->cmd.sheet = sheet_object_get_sheet (so);
7420 me->cmd.size = 1;
7421 me->cmd.cmd_descriptor = g_strdup (_("Set Frame Label"));
7422 me->so = so;
7423 me->old_label = old_label;
7424 me->new_label = new_label;
7426 return gnm_command_push_undo (wbc, G_OBJECT (me));
7429 /******************************************************************/
7430 #define CMD_SO_SET_BUTTON_TYPE (cmd_so_set_button_get_type ())
7431 #define CMD_SO_SET_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_BUTTON_TYPE, CmdSOSetButton))
7433 typedef struct {
7434 GnmCommand cmd;
7435 SheetObject *so;
7436 GnmExprTop const *new_link;
7437 GnmExprTop const *old_link;
7438 char *old_label;
7439 char *new_label;
7440 } CmdSOSetButton;
7442 MAKE_GNM_COMMAND (CmdSOSetButton, cmd_so_set_button, NULL)
7444 static gboolean
7445 cmd_so_set_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7447 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7449 sheet_widget_button_set_link (me->so, me->new_link);
7450 sheet_widget_button_set_label (me->so, me->new_label);
7452 return FALSE;
7455 static gboolean
7456 cmd_so_set_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7458 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7460 sheet_widget_button_set_link (me->so, me->old_link);
7461 sheet_widget_button_set_label (me->so, me->old_label);
7463 return FALSE;
7466 static void
7467 cmd_so_set_button_finalize (GObject *cmd)
7469 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7471 if (me->new_link)
7472 gnm_expr_top_unref (me->new_link);
7473 if (me->old_link)
7474 gnm_expr_top_unref (me->old_link);
7475 g_free (me->old_label);
7476 g_free (me->new_label);
7477 gnm_command_finalize (cmd);
7480 gboolean
7481 cmd_so_set_button (WorkbookControl *wbc,
7482 SheetObject *so, GnmExprTop const *lnk,
7483 char *old_label, char *new_label)
7485 CmdSOSetButton *me;
7487 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7489 me = g_object_new (CMD_SO_SET_BUTTON_TYPE, NULL);
7490 me->cmd.sheet = sheet_object_get_sheet (so);
7491 me->cmd.size = 1;
7492 me->cmd.cmd_descriptor = g_strdup (_("Configure Button"));
7493 me->so = so;
7494 me->new_link = lnk;
7495 me->old_label = old_label;
7496 me->new_label = new_label;
7498 me->old_link = sheet_widget_button_get_link (so);
7500 return gnm_command_push_undo (wbc, G_OBJECT (me));
7503 /******************************************************************/
7504 #define CMD_SO_SET_RADIO_BUTTON_TYPE (cmd_so_set_radio_button_get_type ())
7505 #define CMD_SO_SET_RADIO_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_RADIO_BUTTON_TYPE, CmdSOSetRadioButton))
7507 typedef struct {
7508 GnmCommand cmd;
7509 SheetObject *so;
7510 GnmExprTop const *new_link;
7511 GnmExprTop const *old_link;
7512 char *old_label;
7513 char *new_label;
7514 GnmValue *old_value;
7515 GnmValue *new_value;
7516 } CmdSOSetRadioButton;
7518 MAKE_GNM_COMMAND (CmdSOSetRadioButton, cmd_so_set_radio_button, NULL)
7520 static gboolean
7521 cmd_so_set_radio_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7523 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7525 sheet_widget_radio_button_set_link (me->so, me->new_link);
7526 sheet_widget_radio_button_set_label (me->so, me->new_label);
7527 sheet_widget_radio_button_set_value (me->so, me->new_value);
7529 return FALSE;
7532 static gboolean
7533 cmd_so_set_radio_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7535 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7537 sheet_widget_radio_button_set_link (me->so, me->old_link);
7538 sheet_widget_radio_button_set_label (me->so, me->old_label);
7539 sheet_widget_radio_button_set_value (me->so, me->old_value);
7541 return FALSE;
7544 static void
7545 cmd_so_set_radio_button_finalize (GObject *cmd)
7547 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7549 if (me->new_link)
7550 gnm_expr_top_unref (me->new_link);
7551 if (me->old_link)
7552 gnm_expr_top_unref (me->old_link);
7553 g_free (me->old_label);
7554 g_free (me->new_label);
7555 value_release (me->old_value);
7556 value_release (me->new_value);
7557 gnm_command_finalize (cmd);
7560 gboolean
7561 cmd_so_set_radio_button (WorkbookControl *wbc,
7562 SheetObject *so, GnmExprTop const *lnk,
7563 char *old_label, char *new_label,
7564 GnmValue *old_value, GnmValue *new_value)
7566 CmdSOSetRadioButton *me;
7568 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7570 me = g_object_new (CMD_SO_SET_RADIO_BUTTON_TYPE, NULL);
7571 me->cmd.sheet = sheet_object_get_sheet (so);
7572 me->cmd.size = 1;
7573 me->cmd.cmd_descriptor = g_strdup (_("Configure Radio Button"));
7574 me->so = so;
7575 me->new_link = lnk;
7576 me->old_label = old_label;
7577 me->new_label = new_label;
7578 me->old_value = old_value;
7579 me->new_value = new_value;
7581 me->old_link = sheet_widget_radio_button_get_link (so);
7583 return gnm_command_push_undo (wbc, G_OBJECT (me));
7586 /******************************************************************/
7587 #define CMD_SO_SET_CHECKBOX_TYPE (cmd_so_set_checkbox_get_type ())
7588 #define CMD_SO_SET_CHECKBOX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_CHECKBOX_TYPE, CmdSOSetCheckbox))
7590 typedef struct {
7591 GnmCommand cmd;
7592 SheetObject *so;
7593 GnmExprTop const *new_link;
7594 GnmExprTop const *old_link;
7595 char *old_label;
7596 char *new_label;
7597 } CmdSOSetCheckbox;
7599 MAKE_GNM_COMMAND (CmdSOSetCheckbox, cmd_so_set_checkbox, NULL)
7601 static gboolean
7602 cmd_so_set_checkbox_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7604 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7606 sheet_widget_checkbox_set_link (me->so, me->new_link);
7607 sheet_widget_checkbox_set_label (me->so, me->new_label);
7609 return FALSE;
7612 static gboolean
7613 cmd_so_set_checkbox_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7615 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7617 sheet_widget_checkbox_set_link (me->so, me->old_link);
7618 sheet_widget_checkbox_set_label (me->so, me->old_label);
7620 return FALSE;
7623 static void
7624 cmd_so_set_checkbox_finalize (GObject *cmd)
7626 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7628 if (me->new_link)
7629 gnm_expr_top_unref (me->new_link);
7630 if (me->old_link)
7631 gnm_expr_top_unref (me->old_link);
7632 g_free (me->old_label);
7633 g_free (me->new_label);
7634 gnm_command_finalize (cmd);
7637 gboolean
7638 cmd_so_set_checkbox (WorkbookControl *wbc,
7639 SheetObject *so, GnmExprTop const *lnk,
7640 char *old_label, char *new_label)
7642 CmdSOSetCheckbox *me;
7644 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7646 me = g_object_new (CMD_SO_SET_CHECKBOX_TYPE, NULL);
7647 me->cmd.sheet = sheet_object_get_sheet (so);
7648 me->cmd.size = 1;
7649 me->cmd.cmd_descriptor = g_strdup (_("Configure Checkbox"));
7650 me->so = so;
7651 me->new_link = lnk;
7652 me->old_label = old_label;
7653 me->new_label = new_label;
7655 me->old_link = sheet_widget_checkbox_get_link (so);
7657 return gnm_command_push_undo (wbc, G_OBJECT (me));
7660 /******************************************************************/
7662 #define CMD_SO_SET_ADJUSTMENT_TYPE (cmd_so_set_adjustment_get_type ())
7663 #define CMD_SO_SET_ADJUSTMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_ADJUSTMENT_TYPE, CmdSOSetAdjustment))
7665 typedef struct {
7666 GnmCommand cmd;
7667 SheetObject *so;
7668 GnmExprTop const *new_link;
7669 GnmExprTop const *old_link;
7670 double old_lower;
7671 double old_upper;
7672 double old_step;
7673 double old_page;
7674 gboolean old_horizontal;
7675 } CmdSOSetAdjustment;
7677 MAKE_GNM_COMMAND (CmdSOSetAdjustment, cmd_so_set_adjustment, NULL)
7679 static void
7680 cmd_so_set_adjustment_adj (CmdSOSetAdjustment *me)
7682 GtkAdjustment *adj = sheet_widget_adjustment_get_adjustment (me->so);
7684 double old_lower = gtk_adjustment_get_lower (adj);
7685 double old_upper = gtk_adjustment_get_upper (adj);
7686 double old_step = gtk_adjustment_get_step_increment (adj);
7687 double old_page = gtk_adjustment_get_page_increment (adj);
7688 gboolean old_horizontal;
7689 g_object_get (G_OBJECT (me->so), "horizontal", &old_horizontal, NULL);
7691 gtk_adjustment_configure (adj,
7692 gtk_adjustment_get_value (adj),
7693 me->old_lower,
7694 me->old_upper,
7695 me->old_step,
7696 me->old_page,
7697 gtk_adjustment_get_page_size (adj));
7698 g_object_set (G_OBJECT (me->so), "horizontal", me->old_horizontal, NULL);
7700 me->old_lower = old_lower;
7701 me->old_upper = old_upper;
7702 me->old_step = old_step;
7703 me->old_page = old_page;
7704 me->old_horizontal = old_horizontal;
7707 static gboolean
7708 cmd_so_set_adjustment_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7710 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7712 sheet_widget_adjustment_set_link (me->so, me->new_link);
7713 cmd_so_set_adjustment_adj (me);
7714 return FALSE;
7717 static gboolean
7718 cmd_so_set_adjustment_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7720 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7722 sheet_widget_adjustment_set_link (me->so, me->old_link);
7723 cmd_so_set_adjustment_adj (me);
7725 return FALSE;
7728 static void
7729 cmd_so_set_adjustment_finalize (GObject *cmd)
7731 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7733 if (me->new_link)
7734 gnm_expr_top_unref (me->new_link);
7735 if (me->old_link)
7736 gnm_expr_top_unref (me->old_link);
7737 gnm_command_finalize (cmd);
7740 gboolean
7741 cmd_so_set_adjustment (WorkbookControl *wbc,
7742 SheetObject *so, GnmExprTop const *lnk,
7743 gboolean horizontal,
7744 int lower, int upper,
7745 int step, int page,
7746 char const *undo_label)
7748 CmdSOSetAdjustment *me;
7750 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7752 me = g_object_new (CMD_SO_SET_ADJUSTMENT_TYPE, NULL);
7753 me->cmd.sheet = sheet_object_get_sheet (so);
7754 me->cmd.size = 1;
7755 me->cmd.cmd_descriptor = g_strdup ((undo_label == NULL) ?
7756 _("Configure Adjustment") : _(undo_label));
7757 me->so = so;
7758 me->new_link = lnk;
7759 me->old_lower = lower;
7760 me->old_upper = upper;
7761 me->old_step = step;
7762 me->old_page = page;
7763 me->old_horizontal = horizontal;
7765 me->old_link = sheet_widget_adjustment_get_link (so);
7767 return gnm_command_push_undo (wbc, G_OBJECT (me));
7770 /******************************************************************/
7772 gboolean
7773 cmd_autofilter_add_remove (WorkbookControl *wbc)
7775 SheetView *sv = wb_control_cur_sheet_view (wbc);
7776 GnmFilter *f = gnm_sheet_view_editpos_in_filter (sv);
7777 gboolean add = (f == NULL);
7778 char *descr = NULL, *name = NULL;
7779 GOUndo *undo = NULL;
7780 GOUndo *redo = NULL;
7781 gboolean result;
7784 if (add) {
7785 GnmRange region;
7786 GnmRange const *src = selection_first_range (sv,
7787 GO_CMD_CONTEXT (wbc), _("Add Filter"));
7788 GnmFilter *f_old = NULL;
7790 if (src == NULL)
7791 return TRUE;
7793 f_old = gnm_sheet_filter_intersect_rows
7794 (sv->sheet, src->start.row, src->end.row);
7796 if (f_old != NULL) {
7797 GnmRange *r = gnm_sheet_filter_can_be_extended
7798 (sv->sheet, f_old, src);
7799 if (r == NULL) {
7800 char *error;
7801 name = undo_range_name (sv->sheet, &(f_old->r));
7802 error = g_strdup_printf
7803 (_("Auto Filter blocked by %s"),
7804 name);
7805 g_free(name);
7806 go_cmd_context_error_invalid
7807 (GO_CMD_CONTEXT (wbc),
7808 _("AutoFilter"), error);
7809 g_free (error);
7810 return TRUE;
7812 /* extending existing filter. */
7813 undo = go_undo_binary_new
7814 (gnm_filter_ref (f_old), sv->sheet,
7815 (GOUndoBinaryFunc) gnm_filter_attach,
7816 (GFreeFunc) gnm_filter_unref,
7817 NULL);
7818 redo = go_undo_unary_new
7819 (gnm_filter_ref (f_old),
7820 (GOUndoUnaryFunc) gnm_filter_remove,
7821 (GFreeFunc) gnm_filter_unref);
7822 gnm_filter_remove (f_old);
7823 region = *r;
7824 g_free (r);
7825 } else {
7826 /* if only one row is selected
7827 * assume that the user wants to
7828 * filter the region below this row. */
7829 region = *src;
7830 if (src->start.row == src->end.row)
7831 gnm_sheet_guess_region (sv->sheet, &region);
7832 if (region.start.row == region.end.row) {
7833 go_cmd_context_error_invalid
7834 (GO_CMD_CONTEXT (wbc),
7835 _("AutoFilter"),
7836 _("Requires more than 1 row"));
7837 return TRUE;
7840 f = gnm_filter_new (sv->sheet, &region);
7841 if (f == NULL) {
7842 go_cmd_context_error_invalid
7843 (GO_CMD_CONTEXT (wbc),
7844 _("AutoFilter"),
7845 _("Unable to create Autofilter"));
7846 if (f_old)
7847 gnm_filter_attach (f_old, sv->sheet);
7848 return TRUE;
7851 gnm_filter_remove (f);
7852 if (f_old)
7853 gnm_filter_attach (f_old, sv->sheet);
7855 redo = go_undo_combine (go_undo_binary_new
7856 (gnm_filter_ref (f), sv->sheet,
7857 (GOUndoBinaryFunc) gnm_filter_attach,
7858 (GFreeFunc) gnm_filter_unref,
7859 NULL), redo);
7860 undo = go_undo_combine (undo,
7861 go_undo_unary_new
7863 (GOUndoUnaryFunc) gnm_filter_remove,
7864 (GFreeFunc) gnm_filter_unref));
7866 name = undo_range_name (sv->sheet, &(f->r));
7867 descr = g_strdup_printf
7868 ((f_old == NULL) ? _("Add Autofilter to %s")
7869 : _("Extend Autofilter to %s"),
7870 name);
7871 } else {
7872 undo = go_undo_binary_new
7873 (gnm_filter_ref (f), sv->sheet,
7874 (GOUndoBinaryFunc) gnm_filter_attach,
7875 (GFreeFunc) gnm_filter_unref,
7876 NULL);
7877 redo = go_undo_unary_new
7878 (gnm_filter_ref (f),
7879 (GOUndoUnaryFunc) gnm_filter_remove,
7880 (GFreeFunc) gnm_filter_unref);
7881 name = undo_range_name (sv->sheet, &(f->r));
7882 descr = g_strdup_printf (_("Remove Autofilter from %s"),
7883 name);
7885 result = cmd_generic (wbc, descr, undo, redo);
7886 g_free (name);
7887 g_free (descr);
7889 return result;
7893 /******************************************************************/
7895 gboolean cmd_autofilter_set_condition (WorkbookControl *wbc,
7896 GnmFilter *filter, unsigned i,
7897 GnmFilterCondition *cond)
7899 char *descr = NULL, *name = NULL;
7900 GOUndo *undo = NULL;
7901 GOUndo *redo = NULL;
7902 gboolean result;
7904 undo = gnm_undo_filter_set_condition_new (filter, i,
7905 NULL, TRUE);
7906 g_return_val_if_fail (undo != NULL, TRUE);
7907 redo = gnm_undo_filter_set_condition_new (filter, i,
7908 cond, FALSE);
7909 g_return_val_if_fail (redo != NULL, TRUE);
7911 name = undo_range_name (filter->sheet, &(filter->r));
7912 descr = g_strdup_printf (_("Change filter condition for %s"),
7913 name);
7915 result = cmd_generic (wbc, descr, undo, redo);
7916 g_free (name);
7917 g_free (descr);
7919 return result;
7923 /******************************************************************/
7925 static void
7926 cmd_page_breaks_set_breaks (Sheet *sheet,
7927 GnmPageBreaks const *breaks)
7929 print_info_set_breaks (sheet->print_info, gnm_page_breaks_dup (breaks));
7931 SHEET_FOREACH_CONTROL (sheet, sv, sc, wb_control_menu_state_update (sc_wbc (sc), MS_PAGE_BREAKS););
7934 gboolean
7935 cmd_page_breaks_clear (WorkbookControl *wbc, Sheet *sheet)
7937 GOUndo *undo = NULL;
7938 GOUndo *redo = NULL;
7940 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7941 g_return_val_if_fail (sheet != NULL, TRUE);
7943 if (sheet->print_info->page_breaks.v != NULL) {
7944 redo = go_undo_binary_new
7945 (sheet,
7946 gnm_page_breaks_new (TRUE),
7947 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7948 NULL,
7949 (GFreeFunc) gnm_page_breaks_free);
7950 undo = go_undo_binary_new
7951 (sheet,
7952 gnm_page_breaks_dup
7953 (sheet->print_info->page_breaks.v),
7954 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7955 NULL,
7956 (GFreeFunc) gnm_page_breaks_free);
7959 if (sheet->print_info->page_breaks.h != NULL) {
7960 redo = go_undo_combine
7961 (redo,
7962 go_undo_binary_new
7963 (sheet,
7964 gnm_page_breaks_new (FALSE),
7965 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7966 NULL,
7967 (GFreeFunc) gnm_page_breaks_free));
7969 undo = go_undo_combine
7970 (undo,
7971 go_undo_binary_new
7972 (sheet,
7973 gnm_page_breaks_dup
7974 (sheet->print_info->page_breaks.h),
7975 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7976 NULL,
7977 (GFreeFunc) gnm_page_breaks_free));
7980 if (undo != NULL)
7981 return cmd_generic (wbc, _("Clear All Page Breaks"), undo, redo);
7982 else
7983 return TRUE;
7986 gboolean
7987 cmd_page_break_toggle (WorkbookControl *wbc, Sheet *sheet, gboolean is_vert)
7989 SheetView const *sv = wb_control_cur_sheet_view (wbc);
7990 gint col = sv->edit_pos.col;
7991 gint row = sv->edit_pos.row;
7992 int rc = is_vert ? col : row;
7993 GnmPageBreaks *old, *new, *target;
7994 GnmPageBreakType type;
7995 char const *label;
7996 GOUndo *undo;
7997 GOUndo *redo;
7999 target = is_vert ? sheet->print_info->page_breaks.v
8000 : sheet->print_info->page_breaks.h;
8002 old = (target == NULL) ? gnm_page_breaks_new (is_vert)
8003 : gnm_page_breaks_dup (target);
8004 new = gnm_page_breaks_dup (old);
8006 if (gnm_page_breaks_get_break (new, rc) != GNM_PAGE_BREAK_MANUAL) {
8007 type = GNM_PAGE_BREAK_MANUAL;
8008 label = is_vert ? _("Remove Column Page Break") : _("Remove Row Page Break");
8009 } else {
8010 type = GNM_PAGE_BREAK_NONE;
8011 label = is_vert ? _("Add Column Page Break") : _("Add Row Page Break");
8014 gnm_page_breaks_set_break (new, rc, type);
8016 redo = go_undo_binary_new
8017 (sheet, new,
8018 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8019 NULL,
8020 (GFreeFunc) gnm_page_breaks_free);
8021 undo = go_undo_binary_new
8022 (sheet, old,
8023 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8024 NULL,
8025 (GFreeFunc) gnm_page_breaks_free);
8027 return cmd_generic (wbc, label, undo, redo);
8030 /******************************************************************/