1
2 /*
3 * commands.c: Handlers to undo & redo commands
4 *
5 * Copyright (C) 1999-2008 Jody Goldberg (jody@gnome.org)
6 * Copyright (C) 2002-2008 Morten Welinder (terra@gnome.org)
7 *
8 * Contributors : Almer S. Tigelaar (almer@gnome.org)
9 * Andreas J. Guelzow (aguelzow@taliesin.ca)
10 *
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.
15 *
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.
20 *
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
25 */
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>
31
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>
81
82 #include <goffice/goffice.h>
83 #include <gsf/gsf-doc-meta-data.h>
84 #include <string.h>
85
86 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
87
88
89 /*
90 * There are several distinct stages to wrapping each command.
91 *
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.
95 *
96 * 2) Copy the boiler plate code into place and implement the descriptor.
97 *
98 * 3) Implement the guts of the support functions.
99 *
100 * That way undo redo just become applications of the old or the new styles.
101 *
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.
106 *
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.
113 *
114 * FIXME: Filter the list of commands when a sheet is deleted.
115 *
116 * TODO : Possibly clear lists on save.
117 *
118 * TODO : Reqs for selective undo
119 *
120 * Future thoughts
121 * - undoable preference setting ? XL does not have this. Do we want it ?
122 */
123 /******************************************************************/
124
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))
129
GSF_CLASS(GnmCommand,gnm_command,NULL,NULL,G_TYPE_OBJECT)130 GSF_CLASS (GnmCommand, gnm_command, NULL, NULL, G_TYPE_OBJECT)
131
132 void
133 gnm_command_finalize (GObject *obj)
134 {
135 GnmCommand *cmd = GNM_COMMAND (obj);
136 GObjectClass *parent;
137
138 /* The const was to avoid accidental changes elsewhere */
139 g_free ((gchar *)cmd->cmd_descriptor);
140 cmd->cmd_descriptor = NULL;
141
142 parent = g_type_class_peek (g_type_parent(G_TYPE_FROM_INSTANCE (obj)));
143 (*parent->finalize) (obj);
144 }
145
146 /******************************************************************/
147
148 GString *
gnm_cmd_trunc_descriptor(GString * src,gboolean * truncated)149 gnm_cmd_trunc_descriptor (GString *src, gboolean *truncated)
150 {
151 int max_len = gnm_conf_get_undo_max_descriptor_width ();
152 glong len;
153 char *pos;
154
155 if (max_len < 5)
156 max_len = 5;
157
158 while ((pos = strchr(src->str, '\n')) != NULL ||
159 (pos = strchr(src->str, '\r')) != NULL)
160 *pos = ' ';
161
162 len = g_utf8_strlen (src->str, -1);
163
164 if (truncated)
165 *truncated = (len > max_len);
166
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);
172 }
173 return src;
174 }
175
176
177 /**
178 * cmd_cell_range_is_locked_effective:
179 * @sheet: #Sheet
180 * @range: #GnmRange
181 * @wbc: #WorkbookControl
182 * @cmd_name: the command name.
183 *
184 * checks whether the cells are effectively locked
185 *
186 * static gboolean cmd_cell_range_is_locked_effective
187 *
188 *
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)
191 *
192 * Returns: %TRUE if there was a problem, %FALSE otherwise.
193 */
194 gboolean
cmd_cell_range_is_locked_effective(Sheet * sheet,GnmRange * range,WorkbookControl * wbc,char const * cmd_name)195 cmd_cell_range_is_locked_effective (Sheet *sheet, GnmRange *range,
196 WorkbookControl *wbc, char const *cmd_name)
197 {
198 int i, j;
199 WorkbookView *wbv = wb_control_view (wbc);
200
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."),
209 r);
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;
215 }
216 return FALSE;
217 }
218
219 /*
220 * checks whether the cells are effectively locked
221 *
222 * static gboolean cmd_dao_is_locked_effective
223 *
224 *
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)
227 *
228 * Returns: %TRUE if there was a problem, %FALSE otherwise.
229 */
230 static gboolean
cmd_dao_is_locked_effective(data_analysis_output_t * dao,WorkbookControl * wbc,char const * cmd_name)231 cmd_dao_is_locked_effective (data_analysis_output_t *dao,
232 WorkbookControl *wbc, char const *cmd_name)
233 {
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));
239 }
240
241 /**
242 * cmd_selection_is_locked_effective: (skip)
243 * checks whether the selection is effectively locked
244 *
245 * static gboolean cmd_selection_is_locked_effective
246 *
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)
249 *
250 * Returns: %TRUE if there was a problem, %FALSE otherwise.
251 */
252 gboolean
cmd_selection_is_locked_effective(Sheet * sheet,GSList * selection,WorkbookControl * wbc,char const * cmd_name)253 cmd_selection_is_locked_effective (Sheet *sheet, GSList *selection,
254 WorkbookControl *wbc, char const *cmd_name)
255 {
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;
260 }
261 return FALSE;
262 }
263
264 /*
265 * A helper routine to select a range and make sure the top-left
266 * is visible.
267 */
268 static void
select_range(Sheet * sheet,const GnmRange * r,WorkbookControl * wbc)269 select_range (Sheet *sheet, const GnmRange *r, WorkbookControl *wbc)
270 {
271 SheetView *sv;
272
273 if (sheet->workbook != wb_control_get_workbook (wbc)) {
274 /*
275 * We could try to pick a random wbc for the sheet's
276 * workbook. But not right now.
277 */
278 return;
279 }
280
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);
286 }
287
288 /*
289 * A helper routine to select a list of ranges and make sure the top-left
290 * corner of the last is visible.
291 */
292 static void
select_selection(Sheet * sheet,GSList * selection,WorkbookControl * wbc)293 select_selection (Sheet *sheet, GSList *selection, WorkbookControl *wbc)
294 {
295 SheetView *sv = sheet_get_view (sheet, wb_control_view (wbc));
296 const GnmRange *r0 = NULL;
297 GSList *l;
298
299 g_return_if_fail (selection != NULL);
300
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;
307 }
308 gnm_sheet_view_make_cell_visible (sv, r0->start.col, r0->start.row, FALSE);
309 }
310
311 /**
312 * get_menu_label:
313 * with a list of commands.
314 * @cmd_list: The command list to check.
315 *
316 * Utility routine to get the descriptor associated
317 * Returns : A static reference to a descriptor. DO NOT free this.
318 */
319 static char const *
get_menu_label(GSList * cmd_list)320 get_menu_label (GSList *cmd_list)
321 {
322 if (cmd_list != NULL) {
323 GnmCommand *cmd = GNM_COMMAND (cmd_list->data);
324 return cmd->cmd_descriptor;
325 }
326
327 return NULL;
328 }
329
330 /**
331 * undo_redo_menu_labels:
332 * @wb: The book whose undo/redo queues we are modifying
333 *
334 * Another utility to set the menus correctly.
335 */
336 static void
undo_redo_menu_labels(Workbook * wb)337 undo_redo_menu_labels (Workbook *wb)
338 {
339 char const *undo_label = get_menu_label (wb->undo_commands);
340 char const *redo_label = get_menu_label (wb->redo_commands);
341
342 WORKBOOK_FOREACH_CONTROL (wb, view, control,
343 wb_control_undo_redo_labels (control, undo_label, redo_label);
344 );
345 }
346
347 static void
update_after_action(Sheet * sheet,WorkbookControl * wbc)348 update_after_action (Sheet *sheet, WorkbookControl *wbc)
349 {
350 gnm_app_recalc ();
351
352 if (sheet != NULL) {
353 g_return_if_fail (IS_SHEET (sheet));
354
355 sheet_mark_dirty (sheet);
356 sheet_update (sheet);
357
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);
365 }
366 }
367
368
369 /**
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.
374 *
375 * Undo the last command executed.
376 **/
377 void
command_undo(WorkbookControl * wbc)378 command_undo (WorkbookControl *wbc)
379 {
380 GnmCommand *cmd;
381 GnmCommandClass *klass;
382 Workbook *wb = wb_control_get_workbook (wbc);
383
384 g_return_if_fail (wb != NULL);
385 g_return_if_fail (wb->undo_commands != NULL);
386
387 cmd = GNM_COMMAND (wb->undo_commands->data);
388 g_return_if_fail (cmd != NULL);
389
390 klass = CMD_CLASS (cmd);
391 g_return_if_fail (klass != NULL);
392
393 g_object_ref (cmd);
394
395 /* TRUE indicates a failure to undo. Leave the command where it is */
396 if (!klass->undo_cmd (cmd, wbc)) {
397 gboolean undo_cleared;
398
399 update_after_action (cmd->sheet, wbc);
400
401 go_doc_set_state (GO_DOC (wb), cmd->state_before_do);
402
403 /*
404 * A few commands clear the undo queue. For those, we do not
405 * want to stuff the cmd object on the redo queue.
406 */
407 undo_cleared = (wb->undo_commands == NULL);
408
409 if (!undo_cleared) {
410 wb->undo_commands = g_slist_remove (wb->undo_commands, cmd);
411 wb->redo_commands = g_slist_prepend (wb->redo_commands, cmd);
412
413 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
414 wb_control_undo_redo_pop (control, TRUE);
415 wb_control_undo_redo_push (control, FALSE, cmd->cmd_descriptor, cmd);
416 });
417 undo_redo_menu_labels (wb);
418 /* TODO : Should we mark the workbook as clean or pristine too */
419 }
420 }
421
422 g_object_unref (cmd);
423 }
424
425 /**
426 * command_redo:
427 * @wbc: The workbook control which issued the request.
428 *
429 * Redo the last command that was undone.
430 * Any user level errors generated by redoing will be reported
431 * here.
432 **/
433 void
command_redo(WorkbookControl * wbc)434 command_redo (WorkbookControl *wbc)
435 {
436 GnmCommand *cmd;
437 GnmCommandClass *klass;
438 Workbook *wb = wb_control_get_workbook (wbc);
439
440 g_return_if_fail (wb);
441 g_return_if_fail (wb->redo_commands);
442
443 cmd = GNM_COMMAND (wb->redo_commands->data);
444 g_return_if_fail (cmd != NULL);
445
446 klass = CMD_CLASS (cmd);
447 g_return_if_fail (klass != NULL);
448
449 g_object_ref (cmd);
450
451 cmd->state_before_do = go_doc_get_state (wb_control_get_doc (wbc));
452
453 /* TRUE indicates a failure to redo. Leave the command where it is */
454 if (!klass->redo_cmd (cmd, wbc)) {
455 gboolean redo_cleared;
456
457 update_after_action (cmd->sheet, wbc);
458
459 /*
460 * A few commands clear the undo queue. For those, we do not
461 * want to stuff the cmd object on the redo queue.
462 */
463 redo_cleared = (wb->redo_commands == NULL);
464
465 if (!redo_cleared) {
466 wb->redo_commands = g_slist_remove (wb->redo_commands, cmd);
467 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
468
469 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
470 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
471 wb_control_undo_redo_pop (control, FALSE);
472 });
473 undo_redo_menu_labels (wb);
474 }
475 }
476
477 g_object_unref (cmd);
478 }
479
480 /**
481 * command_repeat:
482 * @wbc: The workbook control which issued the request.
483 *
484 * Repeat the last command (if possible)
485 *
486 * Any user level errors generated by redoing will be reported
487 * here.
488 **/
489 void
command_repeat(WorkbookControl * wbc)490 command_repeat (WorkbookControl *wbc)
491 {
492 GnmCommand *cmd;
493 GnmCommandClass *klass;
494 Workbook *wb = wb_control_get_workbook (wbc);
495
496 g_return_if_fail (wb);
497 g_return_if_fail (wb->undo_commands);
498
499 cmd = GNM_COMMAND (wb->undo_commands->data);
500 g_return_if_fail (cmd != NULL);
501
502 klass = CMD_CLASS (cmd);
503 g_return_if_fail (klass != NULL);
504
505 if (klass->repeat_cmd != NULL)
506 (*klass->repeat_cmd) (cmd, wbc);
507 }
508
509 /**
510 * command_setup_combos:
511 * @wbc:
512 *
513 * Initialize the combos to correspond to the current undo/redo state.
514 */
515 void
command_setup_combos(WorkbookControl * wbc)516 command_setup_combos (WorkbookControl *wbc)
517 {
518 char const *undo_label = NULL, *redo_label = NULL;
519 GSList *ptr, *tmp;
520 Workbook *wb = wb_control_get_workbook (wbc);
521
522 g_return_if_fail (wb);
523
524 wb_control_undo_redo_truncate (wbc, 0, TRUE);
525 tmp = g_slist_reverse (wb->undo_commands);
526 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
527 undo_label = get_menu_label (ptr);
528 wb_control_undo_redo_push (wbc, TRUE, undo_label, ptr->data);
529 }
530 if (g_slist_reverse (tmp)) {} /* ignore, list is in undo_commands */
531
532 wb_control_undo_redo_truncate (wbc, 0, FALSE);
533 tmp = g_slist_reverse (wb->redo_commands);
534 for (ptr = tmp ; ptr != NULL ; ptr = ptr->next) {
535 redo_label = get_menu_label (ptr);
536 wb_control_undo_redo_push (wbc, FALSE, redo_label, ptr->data);
537 }
538 if (g_slist_reverse (tmp)) {} /* ignore, list is in redo_commands */
539
540 /* update the menus too */
541 wb_control_undo_redo_labels (wbc, undo_label, redo_label);
542 }
543
544 /**
545 * command_list_release:
546 * @cmds: (element-type GObject): the set of commands to free.
547 *
548 * command_list_release : utility routine to free the resources associated
549 * with a list of commands.
550 *
551 * NOTE : remember to NULL the list when you are done.
552 */
553 void
command_list_release(GSList * cmd_list)554 command_list_release (GSList *cmd_list)
555 {
556 while (cmd_list != NULL) {
557 GObject *cmd = G_OBJECT (cmd_list->data);
558
559 g_return_if_fail (cmd != NULL);
560
561 g_object_unref (cmd);
562 cmd_list = g_slist_remove (cmd_list, cmd_list->data);
563 }
564 }
565
566 /*
567 * Each undo item has a certain size. The size of typing a value into
568 * a cell is the unit size. A large autoformat could have a size of
569 * hundreds or even thousands.
570 *
571 * We wish to have the same undo behaviour across platforms, so please
572 * don't use sizeof in computing the undo size.
573 */
574
575 #undef DEBUG_TRUNCATE_UNDO
576
577 /*
578 * Truncate the undo list if it is too big.
579 *
580 * Returns -1 if no truncation was done, or else the number of elements
581 * left.
582 */
583 static int
truncate_undo_info(Workbook * wb)584 truncate_undo_info (Workbook *wb)
585 {
586 int size_left;
587 int max_num;
588 int ok_count;
589 GSList *l, *prev;
590
591 size_left = gnm_conf_get_undo_size ();
592 max_num = gnm_conf_get_undo_maxnum ();
593
594 #ifdef DEBUG_TRUNCATE_UNDO
595 g_printerr ("Undo sizes:");
596 #endif
597
598 for (l = wb->undo_commands, prev = NULL, ok_count = 0;
599 l;
600 prev = l, l = l->next, ok_count++) {
601 int min_leave;
602 GnmCommand *cmd = GNM_COMMAND (l->data);
603 int size = cmd->size;
604
605 if (size < 1) {
606 /*
607 * We could g_assert, but that would cause data loss.
608 * Instead, just continue.
609 */
610 g_warning ("Faulty undo_size_func, please report.");
611 size = 1;
612 }
613
614 #ifdef DEBUG_TRUNCATE_UNDO
615 g_printerr (" %d", size);
616 #endif
617
618 /* Keep at least one undo item. */
619 if (ok_count >= max_num || (size > size_left && ok_count >= 1)) {
620 /* Current item is too big; truncate list here. */
621 command_list_release (l);
622 if (prev)
623 prev->next = NULL;
624 else
625 wb->undo_commands = NULL;
626 #ifdef DEBUG_TRUNCATE_UNDO
627 g_printerr ("[trunc]\n");
628 #endif
629 return ok_count;
630 }
631
632 /*
633 * In order to allow a series of useful small items behind
634 * a big item, leave at least 10% of current item's size.
635 */
636 min_leave = size / 10;
637 size_left = MAX (size_left - size, min_leave);
638 }
639
640 #ifdef DEBUG_TRUNCATE_UNDO
641 g_printerr ("\n");
642 #endif
643 return -1;
644 }
645
646
647 /**
648 * command_register_undo:
649 * @wbc: The workbook control that issued the command.
650 * @cmd: The new command to add.
651 *
652 * An internal utility to tack a new command
653 * onto the undo list.
654 */
655 static void
command_register_undo(WorkbookControl * wbc,GObject * obj)656 command_register_undo (WorkbookControl *wbc, GObject *obj)
657 {
658 Workbook *wb;
659 GnmCommand *cmd;
660 int undo_trunc;
661
662 g_return_if_fail (wbc != NULL);
663 wb = wb_control_get_workbook (wbc);
664
665 cmd = GNM_COMMAND (obj);
666 g_return_if_fail (cmd != NULL);
667
668 command_list_release (wb->redo_commands);
669 wb->redo_commands = NULL;
670
671 g_object_ref (obj); /* keep a ref in case it gets truncated away */
672 wb->undo_commands = g_slist_prepend (wb->undo_commands, cmd);
673 undo_trunc = truncate_undo_info (wb);
674
675 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
676 wb_control_undo_redo_push (control, TRUE, cmd->cmd_descriptor, cmd);
677 if (undo_trunc >= 0)
678 wb_control_undo_redo_truncate (control, undo_trunc, TRUE);
679 wb_control_undo_redo_truncate (control, 0, FALSE);
680 });
681 undo_redo_menu_labels (wb);
682 g_object_unref (obj);
683 }
684
685
686 /**
687 * gnm_command_push_undo:
688 * @wbc: The workbook control that issued the command.
689 * @obj: The new command to add.
690 *
691 * An internal utility to tack a new command
692 * onto the undo list.
693 *
694 * Returns: %TRUE if there was a problem, %FALSE otherwise.
695 */
696
697
698 /*
699 * cmd_set_text_full
700 *
701 * the caller is expected to have ensured:
702 *
703 * 1) that no array is being split
704 * 2) that the range is not locked.
705 *
706 * Note:
707 * We will free the selection but nothing else.
708 *
709 * Returns: %TRUE if there was a problem, %FALSE otherwise.
710 */
711 gboolean
gnm_command_push_undo(WorkbookControl * wbc,GObject * obj)712 gnm_command_push_undo (WorkbookControl *wbc, GObject *obj)
713 {
714 gboolean trouble;
715 GnmCommand *cmd;
716 GnmCommandClass *klass;
717
718 g_return_val_if_fail (wbc != NULL, TRUE);
719
720 cmd = GNM_COMMAND (obj);
721 cmd->state_before_do = go_doc_get_state (wb_control_get_doc (wbc));
722
723 g_return_val_if_fail (cmd != NULL, TRUE);
724
725 klass = CMD_CLASS (cmd);
726 g_return_val_if_fail (klass != NULL, TRUE);
727
728 /* TRUE indicates a failure to do the command */
729 trouble = klass->redo_cmd (cmd, wbc);
730 update_after_action (cmd->sheet, wbc);
731
732 if (!trouble)
733 command_register_undo (wbc, obj);
734 else
735 g_object_unref (obj);
736
737 return trouble;
738 }
739
740 /*
741 * command_undo_sheet_delete deletes the sheet without deleting the current cmd.
742 * returns true if it indeed deleted the sheet.
743 * Note: only call this for a sheet of your current workbook from the undo procedure
744 */
745
746 static gboolean
command_undo_sheet_delete(Sheet * sheet)747 command_undo_sheet_delete (Sheet* sheet)
748 {
749 Workbook *wb = sheet->workbook;
750
751 g_return_val_if_fail (IS_SHEET (sheet), FALSE);
752
753 if (wb->redo_commands != NULL) {
754 command_list_release (wb->redo_commands);
755 wb->redo_commands = NULL;
756 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
757 wb_control_undo_redo_truncate (ctl, 0, FALSE););
758 undo_redo_menu_labels (wb);
759 }
760
761 workbook_sheet_delete (sheet);
762
763 return (TRUE);
764 }
765
766 /******************************************************************/
767
768 static GnmValue *
cmd_set_text_full_check_texpr(GnmCellIter const * iter,GnmExprTop const * texpr)769 cmd_set_text_full_check_texpr (GnmCellIter const *iter, GnmExprTop const *texpr)
770 {
771 if (iter->cell == NULL ||
772 !gnm_expr_top_equal (iter->cell->base.texpr, texpr))
773 return VALUE_TERMINATE;
774 return NULL;
775 }
776
777 static GnmValue *
cmd_set_text_full_check_text(GnmCellIter const * iter,char * text)778 cmd_set_text_full_check_text (GnmCellIter const *iter, char *text)
779 {
780 char *old_text;
781 gboolean same;
782 gboolean quoted = FALSE;
783
784 if (gnm_cell_is_blank (iter->cell))
785 return ((text == NULL || text[0] == '\0') ? NULL : VALUE_TERMINATE);
786
787 if (text == NULL || text[0] == '\0')
788 return VALUE_TERMINATE;
789
790 old_text = gnm_cell_get_text_for_editing (iter->cell, NULL, "ed);
791 same = g_strcmp0 (old_text, text) == 0;
792
793 if (!same && !quoted && iter->cell->value && VALUE_IS_STRING (iter->cell->value)
794 && text[0] == '\'')
795 same = g_strcmp0 (old_text, text + 1) == 0;
796
797 g_free (old_text);
798
799 return (same ? NULL : VALUE_TERMINATE);
800 }
801
802 static GnmValue *
cmd_set_text_full_check_markup(GnmCellIter const * iter,PangoAttrList * markup)803 cmd_set_text_full_check_markup (GnmCellIter const *iter, PangoAttrList *markup)
804 {
805 PangoAttrList const *old_markup = NULL;
806 gboolean same_markup;
807
808 g_return_val_if_fail (iter->cell != NULL, NULL);
809
810 if (iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
811 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
812 if (fmt && go_format_is_markup (fmt)) {
813 old_markup = go_format_get_markup (fmt);
814 if (go_pango_attr_list_is_empty (old_markup))
815 old_markup = NULL;
816 }
817 }
818
819 same_markup = gnm_pango_attr_list_equal (old_markup, markup);
820
821 return same_markup ? NULL : VALUE_TERMINATE;
822 }
823
824 /*
825 * cmd_set_text_full
826 *
827 * the caller is expected to have ensured:
828 *
829 * 1) that no array is being split
830 * 2) that the range is not locked.
831 *
832 * Note:
833 * We will free the selection but nothing else.
834 *
835 * Returns: %TRUE if there was a problem, %FALSE otherwise.
836 */
837 static gboolean
cmd_set_text_full(WorkbookControl * wbc,GSList * selection,GnmEvalPos * ep,char const * new_text,PangoAttrList * markup,gboolean autocorrect)838 cmd_set_text_full (WorkbookControl *wbc, GSList *selection, GnmEvalPos *ep,
839 char const *new_text, PangoAttrList *markup,
840 gboolean autocorrect)
841 {
842 GSList *l;
843 char const *expr_txt;
844 GnmExprTop const *texpr = NULL;
845 GOUndo *undo = NULL;
846 GOUndo *redo = NULL;
847 gboolean result, autofit_col = FALSE, same_text_and_not_same_markup = FALSE;
848 char *text = NULL;
849 char *name;
850 Sheet *sheet = ep->sheet;
851 GnmParsePos pp;
852 ColRowIndexList *cri_col_list = NULL, *cri_row_list = NULL;
853 GOFormat const *format = gnm_style_get_format
854 (sheet_style_get (sheet, ep->eval.col, ep->eval.row));
855
856 g_return_val_if_fail (selection != NULL , TRUE);
857
858 parse_pos_init_evalpos (&pp, ep);
859 name = undo_range_list_name (sheet, selection);
860
861 if ((format == NULL) || !go_format_is_text (format)) {
862 expr_txt = gnm_expr_char_start_p (new_text);
863 if (expr_txt != NULL)
864 texpr = gnm_expr_parse_str
865 (expr_txt, &pp, GNM_EXPR_PARSE_DEFAULT,
866 sheet_get_conventions (sheet), NULL);
867 }
868
869 if (texpr != NULL) {
870 GOFormat const *sf;
871 GnmStyle *new_style = NULL;
872 gboolean same_texpr = TRUE;
873
874 /* We should check whether we are in fact changing anything: */
875 for (l = selection; l != NULL && same_texpr; l = l->next) {
876 GnmRange *r = l->data;
877 GnmValue *val =
878 sheet_foreach_cell_in_range
879 (sheet, CELL_ITER_ALL, r,
880 (CellIterFunc) cmd_set_text_full_check_texpr,
881 (gpointer) texpr);
882
883 same_texpr = (val != VALUE_TERMINATE);
884 if (val != NULL && same_texpr)
885 value_release (val);
886 }
887
888 if (same_texpr) {
889 gnm_expr_top_unref (texpr);
890 g_free (name);
891 range_fragment_free (selection);
892 return TRUE;
893 }
894
895 text = g_strdup_printf (_("Inserting expression in %s"), name);
896
897 if (go_format_is_general (format)) {
898 sf = gnm_auto_style_format_suggest (texpr, ep);
899 if (sf != NULL) {
900 new_style = gnm_style_new ();
901 gnm_style_set_format (new_style, sf);
902 go_format_unref (sf);
903 }
904 }
905
906 for (l = selection; l != NULL; l = l->next) {
907 GnmSheetRange *sr;
908 undo = go_undo_combine
909 (undo, clipboard_copy_range_undo (sheet, l->data));
910 sr = gnm_sheet_range_new (sheet, l->data);
911 redo = go_undo_combine
912 (redo, sheet_range_set_expr_undo (sr, texpr));
913 if (new_style) {
914 sr = gnm_sheet_range_new (sheet, l->data);
915 redo = go_undo_combine
916 (redo, sheet_apply_style_undo (sr, new_style));
917 }
918 }
919 if (new_style)
920 gnm_style_unref (new_style);
921 gnm_expr_top_unref (texpr);
922 autofit_col = TRUE;
923 } else {
924 GString *text_str;
925 PangoAttrList *adj_markup = NULL;
926 char *corrected;
927 gboolean same_text = TRUE;
928 gboolean same_markup = TRUE;
929
930 if (new_text == NULL)
931 corrected = NULL;
932 else if (autocorrect)
933 corrected = autocorrect_tool (new_text);
934 else
935 corrected = g_strdup (new_text);
936
937 if (corrected && (corrected[0] == '\'') && corrected[1] == '\0') {
938 g_free (corrected);
939 corrected = g_strdup ("");
940 }
941
942 /* We should check whether we are in fact changing anything: */
943 /* We'll handle */
944 for (l = selection; l != NULL && same_text; l = l->next) {
945 GnmRange *r = l->data;
946 GnmValue *val =
947 sheet_foreach_cell_in_range
948 (sheet, CELL_ITER_ALL, r,
949 (CellIterFunc) cmd_set_text_full_check_text,
950 (gpointer) corrected);
951
952 same_text = (val != VALUE_TERMINATE);
953 if (val != NULL && same_text)
954 value_release (val);
955 }
956
957 if (go_pango_attr_list_is_empty (markup))
958 markup = NULL;
959 if (markup && corrected && corrected[0] == '\'') {
960 markup = adj_markup = pango_attr_list_copy (markup);
961 go_pango_attr_list_erase (adj_markup, 0, 1);
962 }
963
964 if (same_text) {
965 for (l = selection; l != NULL && same_text; l = l->next) {
966 GnmRange *r = l->data;
967 GnmValue *val =
968 sheet_foreach_cell_in_range
969 (sheet, CELL_ITER_IGNORE_BLANK, r,
970 (CellIterFunc) cmd_set_text_full_check_markup,
971 (gpointer) markup);
972
973 same_markup = (val != VALUE_TERMINATE);
974 if (val != NULL && same_markup)
975 value_release (val);
976 }
977
978 if (same_markup) {
979 g_free (corrected);
980 g_free (name);
981 range_fragment_free (selection);
982 if (adj_markup)
983 pango_attr_list_unref (adj_markup);
984 return TRUE;
985 }
986
987 text = g_strdup_printf (_("Editing style of %s"), name);
988 } else {
989 text_str = gnm_cmd_trunc_descriptor (g_string_new (corrected), NULL);
990 text = g_strdup_printf (_("Typing \"%s\" in %s"), text_str->str, name);
991 g_string_free (text_str, TRUE);
992 }
993
994 for (l = selection; l != NULL; l = l->next) {
995 GnmSheetRange *sr;
996 undo = go_undo_combine
997 (undo, clipboard_copy_range_undo (sheet, l->data));
998 if (corrected) {
999 sr = gnm_sheet_range_new (sheet, l->data);
1000 redo = go_undo_combine
1001 (redo, sheet_range_set_text_undo
1002 (sr, corrected));
1003 }
1004 if (markup) {
1005 sr = gnm_sheet_range_new (sheet, l->data);
1006 /* Note: order of combination matters!! */
1007 redo = go_undo_combine
1008 (sheet_range_set_markup_undo (sr, markup), redo);
1009 }
1010 }
1011
1012 if (adj_markup)
1013 pango_attr_list_unref (adj_markup);
1014 g_free (corrected);
1015
1016 same_text_and_not_same_markup = (same_text && !same_markup);
1017 }
1018 g_free (name);
1019
1020 /* We are combining this since we don't want to apply and undo twice.*/
1021 if (same_text_and_not_same_markup || !autofit_col) {
1022 GnmCell *cell = sheet_cell_fetch
1023 (sheet, ep->eval.col, ep->eval.row);
1024 gboolean nvis;
1025
1026 go_undo_undo (redo);
1027 nvis = !VALUE_IS_STRING (cell->value);
1028 if (!autofit_col)
1029 autofit_col = nvis;
1030 if (same_text_and_not_same_markup)
1031 /* We only have to do something if at least one cell */
1032 /* now contains a string, but they contain all the same thing. */
1033 same_text_and_not_same_markup = nvis;
1034 go_undo_undo (undo);
1035 }
1036 if (same_text_and_not_same_markup) {
1037 /*We had the same text and different markup but we are not entering strings. */
1038 g_object_unref (undo);
1039 g_object_unref (redo);
1040 g_free (text);
1041 range_fragment_free (selection);
1042 return TRUE;
1043 }
1044 for (l = selection; l != NULL; l = l->next) {
1045 GnmRange *r = l->data;
1046 GnmRange *new_r;
1047
1048 new_r = g_new (GnmRange, 1);
1049 *new_r = *r;
1050 redo = go_undo_combine
1051 (go_undo_binary_new
1052 (sheet, new_r,
1053 (GOUndoBinaryFunc) colrow_autofit_row,
1054 NULL, g_free),
1055 redo);
1056 cri_row_list = colrow_get_index_list
1057 (r->start.row, r->end.row, cri_row_list);
1058
1059 if (autofit_col) {
1060 new_r = g_new (GnmRange, 1);
1061 *new_r = *r;
1062 redo = go_undo_combine
1063 (go_undo_binary_new
1064 (sheet, new_r,
1065 (GOUndoBinaryFunc) colrow_autofit_col,
1066 NULL, g_free),
1067 redo);
1068 cri_col_list = colrow_get_index_list
1069 (r->start.col, r->end.col, cri_col_list);
1070 }
1071 }
1072
1073 undo = go_undo_combine (undo,
1074 gnm_undo_colrow_restore_state_group_new
1075 (sheet, TRUE,
1076 cri_col_list,
1077 colrow_get_sizes (sheet, TRUE,
1078 cri_col_list, -1)));
1079 undo = go_undo_combine (undo,
1080 gnm_undo_colrow_restore_state_group_new
1081 (sheet, FALSE,
1082 cri_row_list,
1083 colrow_get_sizes (sheet, FALSE,
1084 cri_row_list, -1)));
1085
1086 result = cmd_generic (wbc, text, undo, redo);
1087 g_free (text);
1088 range_fragment_free (selection);
1089 return result;
1090 }
1091
1092 /*
1093 * cmd_area_set_text
1094 *
1095 * the caller is expected to have ensured:
1096 *
1097 * 1) that no array is being split
1098 * 2) that the range is not locked.
1099 *
1100 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1101 */
1102 gboolean
cmd_area_set_text(WorkbookControl * wbc,SheetView * sv,char const * new_text,PangoAttrList * markup)1103 cmd_area_set_text (WorkbookControl *wbc, SheetView *sv,
1104 char const *new_text, PangoAttrList *markup)
1105 {
1106 GnmEvalPos ep;
1107 gboolean result;
1108 GSList *selection = selection_get_ranges (sv, FALSE);
1109
1110 eval_pos_init_editpos (&ep, sv);
1111 result = cmd_set_text_full (wbc, selection, &ep,
1112 new_text, markup, TRUE);
1113 return result;
1114 }
1115
1116 gboolean
cmd_set_text(WorkbookControl * wbc,Sheet * sheet,GnmCellPos const * pos,char const * new_text,PangoAttrList * markup,gboolean autocorrect)1117 cmd_set_text (WorkbookControl *wbc,
1118 Sheet *sheet, GnmCellPos const *pos,
1119 char const *new_text,
1120 PangoAttrList *markup,
1121 gboolean autocorrect)
1122 {
1123 GnmCell const *cell;
1124 GnmEvalPos ep;
1125 gboolean result;
1126 GSList *selection;
1127 GnmRange *r;
1128
1129 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1130 g_return_val_if_fail (new_text != NULL, TRUE);
1131
1132 /* Ensure that we are not splitting up an array */
1133 cell = sheet_cell_get (sheet, pos->col, pos->row);
1134 if (gnm_cell_is_nonsingleton_array (cell)) {
1135 gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc),
1136 _("Set Text"), NULL);
1137 return TRUE;
1138 }
1139
1140 eval_pos_init_pos (&ep, sheet, pos);
1141 r = g_new (GnmRange, 1);
1142 r->start = r->end = *pos;
1143 selection = g_slist_prepend (NULL, r);
1144 result = cmd_set_text_full (wbc, selection, &ep,
1145 new_text, markup, autocorrect);
1146 return result;
1147 }
1148
1149
1150 /*
1151 * cmd_area_set_array_expr
1152 *
1153 * the caller is expected to have ensured:
1154 *
1155 * 1) that no array is being split
1156 * 2) that the selection consists of a single range
1157 * 3) that the range is not locked.
1158 *
1159 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1160 */
1161 gboolean
cmd_area_set_array_expr(WorkbookControl * wbc,SheetView * sv,GnmExprTop const * texpr)1162 cmd_area_set_array_expr (WorkbookControl *wbc, SheetView *sv,
1163 GnmExprTop const *texpr)
1164 {
1165 GSList *selection = selection_get_ranges (sv, FALSE);
1166 GOUndo *undo = NULL;
1167 GOUndo *redo = NULL;
1168 gboolean result;
1169 Sheet *sheet = sv_sheet (sv);
1170 char *name;
1171 char *text;
1172 GnmSheetRange *sr;
1173 GnmRange *r;
1174
1175 g_return_val_if_fail (selection != NULL , TRUE);
1176 g_return_val_if_fail (selection->next == NULL , TRUE);
1177
1178 name = undo_range_list_name (sheet, selection);
1179 text = g_strdup_printf (_("Inserting array expression in %s"), name);
1180 g_free (name);
1181
1182 r = selection->data;
1183
1184 undo = clipboard_copy_range_undo (sheet, selection->data);
1185
1186 sr = gnm_sheet_range_new (sheet, r);
1187 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1188 redo = go_undo_combine
1189 (go_undo_binary_new
1190 (sheet, g_memdup (r, sizeof (*r)),
1191 (GOUndoBinaryFunc) colrow_autofit_col,
1192 NULL, g_free),
1193 redo);
1194 redo = go_undo_combine
1195 (go_undo_binary_new
1196 (sheet, g_memdup (r, sizeof (*r)),
1197 (GOUndoBinaryFunc) colrow_autofit_row,
1198 NULL, g_free),
1199 redo);
1200
1201 range_fragment_free (selection);
1202 result = cmd_generic (wbc, text, undo, redo);
1203 g_free (text);
1204 return result;
1205 }
1206
1207 /*
1208 * cmd_create_data_table
1209 *
1210 * the caller is expected to have ensured:
1211 *
1212 * 1) that no array is being split
1213 * 2) that the range is not locked.
1214 *
1215 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1216 */
1217 gboolean
cmd_create_data_table(WorkbookControl * wbc,Sheet * sheet,GnmRange const * r,char const * col_input,char const * row_input)1218 cmd_create_data_table (WorkbookControl *wbc, Sheet *sheet, GnmRange const *r,
1219 char const *col_input, char const *row_input)
1220 {
1221 GOUndo *undo = NULL;
1222 GOUndo *redo = NULL;
1223 gboolean result;
1224 char *name;
1225 char *text;
1226 GnmSheetRange *sr;
1227 GnmParsePos pp;
1228 GnmExprTop const *texpr;
1229
1230 name = undo_range_name (sheet, r);
1231 text = g_strdup_printf (_("Creating a Data Table in %s"), name);
1232 g_free (name);
1233
1234 undo = clipboard_copy_range_undo (sheet, r);
1235
1236 sr = gnm_sheet_range_new (sheet, r);
1237 parse_pos_init (&pp, NULL, sheet, r->start.col, r->start.row);
1238 name = g_strdup_printf ("TABLE(%s,%s)", row_input, col_input);
1239 texpr = gnm_expr_parse_str
1240 (name, &pp, GNM_EXPR_PARSE_DEFAULT,
1241 sheet_get_conventions (sheet), NULL);
1242 g_free (name);
1243
1244 if (texpr == NULL) {
1245 g_object_unref (undo);
1246 g_free (text);
1247 return TRUE;
1248 }
1249
1250 redo = gnm_cell_set_array_formula_undo (sr, texpr);
1251 gnm_expr_top_unref (texpr);
1252
1253 result = cmd_generic (wbc, text, undo, redo);
1254 g_free (text);
1255 return result;
1256 }
1257
1258 /******************************************************************/
1259
1260 #define CMD_INS_DEL_COLROW_TYPE (cmd_ins_del_colrow_get_type ())
1261 #define CMD_INS_DEL_COLROW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_INS_DEL_COLROW_TYPE, CmdInsDelColRow))
1262
1263 typedef struct {
1264 GnmCommand cmd;
1265
1266 Sheet *sheet;
1267 gboolean is_insert;
1268 gboolean is_cols;
1269 gboolean is_cut;
1270 int index;
1271 int count;
1272 GnmRange *cutcopied;
1273 SheetView *cut_copy_view;
1274
1275 gboolean (*redo_action) (Sheet *sheet, int col, int count,
1276 GOUndo **pundo, GOCmdContext *cc);
1277
1278 gboolean (*repeat_action) (WorkbookControl *wbc, Sheet *sheet,
1279 int start, int count);
1280
1281 GOUndo *undo;
1282 } CmdInsDelColRow;
1283
1284 static void
cmd_ins_del_colrow_repeat(GnmCommand const * cmd,WorkbookControl * wbc)1285 cmd_ins_del_colrow_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1286 {
1287 CmdInsDelColRow const *orig = (CmdInsDelColRow const *) cmd;
1288 SheetView *sv = wb_control_cur_sheet_view (wbc);
1289 Sheet *sheet = sv_sheet (sv);
1290 GnmRange const *r = selection_first_range (sv,
1291 GO_CMD_CONTEXT (wbc), _("Ins/Del Column/Row"));
1292 int start, count;
1293
1294 if (r == NULL)
1295 return;
1296
1297 if (orig->is_cols)
1298 start = r->start.col, count = range_width (r);
1299 else
1300 start = r->start.row, count = range_height (r);
1301
1302 orig->repeat_action (wbc, sheet, start, count);
1303 }
1304
MAKE_GNM_COMMAND(CmdInsDelColRow,cmd_ins_del_colrow,cmd_ins_del_colrow_repeat)1305 MAKE_GNM_COMMAND (CmdInsDelColRow, cmd_ins_del_colrow, cmd_ins_del_colrow_repeat)
1306
1307 static gboolean
1308 cmd_ins_del_colrow_undo (GnmCommand *cmd, WorkbookControl *wbc)
1309 {
1310 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1311
1312 if (me->undo) {
1313 go_undo_undo (me->undo);
1314 g_object_unref (me->undo);
1315 me->undo = NULL;
1316 }
1317
1318 /* Ins/Del Row/Col re-ants things completely to account
1319 * for the shift of col/rows.
1320 */
1321 if (me->cutcopied != NULL && me->cut_copy_view != NULL)
1322 gnm_app_clipboard_cut_copy (wbc, me->is_cut, me->cut_copy_view,
1323 me->cutcopied, FALSE);
1324
1325 return FALSE;
1326 }
1327
1328 static gboolean
cmd_ins_del_colrow_redo(GnmCommand * cmd,WorkbookControl * wbc)1329 cmd_ins_del_colrow_redo (GnmCommand *cmd, WorkbookControl *wbc)
1330 {
1331 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1332 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
1333 int idx = me->index;
1334 int count = me->count;
1335
1336 if (me->redo_action (me->sheet, idx, count, &me->undo, cc)) {
1337 /* Trouble. */
1338 return TRUE;
1339 }
1340
1341 /* Ins/Del Row/Col re-ants things completely to account
1342 * for the shift of col/rows. */
1343 if (me->cutcopied != NULL && me->cut_copy_view != NULL) {
1344 if (me->is_cut) {
1345 GnmRange s = *me->cutcopied;
1346 int key = me->is_insert ? count : -count;
1347 int threshold = me->is_insert ? idx : idx + 1;
1348
1349 /*
1350 * Really only applies if the regions that are
1351 * inserted/deleted are above the cut/copied region.
1352 */
1353 if (me->is_cols) {
1354 if (threshold <= s.start.col) {
1355 s.start.col += key;
1356 s.end.col += key;
1357 }
1358 } else if (threshold <= s.start.row) {
1359 s.start.row += key;
1360 s.end.row += key;
1361 }
1362
1363 gnm_app_clipboard_cut_copy (wbc, me->is_cut,
1364 me->cut_copy_view,
1365 &s, FALSE);
1366 } else
1367 gnm_app_clipboard_unant ();
1368 }
1369
1370 return FALSE;
1371 }
1372
1373 static void
cmd_ins_del_colrow_finalize(GObject * cmd)1374 cmd_ins_del_colrow_finalize (GObject *cmd)
1375 {
1376 CmdInsDelColRow *me = CMD_INS_DEL_COLROW (cmd);
1377
1378 if (me->undo)
1379 g_object_unref (me->undo);
1380
1381 g_free (me->cutcopied);
1382
1383 gnm_sheet_view_weak_unref (&(me->cut_copy_view));
1384
1385 gnm_command_finalize (cmd);
1386 }
1387
1388 static gboolean
cmd_ins_del_colrow(WorkbookControl * wbc,Sheet * sheet,gboolean is_cols,gboolean is_insert,char const * descriptor,int index,int count)1389 cmd_ins_del_colrow (WorkbookControl *wbc,
1390 Sheet *sheet,
1391 gboolean is_cols, gboolean is_insert,
1392 char const *descriptor, int index, int count)
1393 {
1394 CmdInsDelColRow *me;
1395 int first, last;
1396 GnmRange r;
1397
1398 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
1399 g_return_val_if_fail (count > 0, TRUE);
1400
1401 me = g_object_new (CMD_INS_DEL_COLROW_TYPE, NULL);
1402
1403 me->sheet = sheet;
1404 me->is_cols = is_cols;
1405 me->is_insert = is_insert;
1406 me->index = index;
1407 me->count = count;
1408 me->redo_action = me->is_insert
1409 ? (me->is_cols ? sheet_insert_cols : sheet_insert_rows)
1410 : (me->is_cols ? sheet_delete_cols : sheet_delete_rows);
1411 me->repeat_action = me->is_insert
1412 ? (me->is_cols ? cmd_insert_cols : cmd_insert_rows)
1413 : (me->is_cols ? cmd_delete_cols : cmd_delete_rows);
1414
1415 /* Range that will get deleted. */
1416 first = me->is_insert
1417 ? colrow_max (is_cols, sheet) - count
1418 : index;
1419 last = first + count - 1;
1420 (is_cols ? range_init_cols : range_init_rows) (&r, sheet, first, last);
1421
1422 /* Note: redo_action checks for array subdivision. */
1423
1424 /* Check for locks */
1425 if (cmd_cell_range_is_locked_effective (sheet, &r, wbc, descriptor)) {
1426 g_object_unref (me);
1427 return TRUE;
1428 }
1429
1430 /* We store the cut or/copied range if applicable */
1431 if (!gnm_app_clipboard_is_empty () &&
1432 gnm_app_clipboard_area_get () &&
1433 sheet == gnm_app_clipboard_sheet_get ()) {
1434 me->cutcopied = gnm_range_dup (gnm_app_clipboard_area_get ());
1435 me->is_cut = gnm_app_clipboard_is_cut ();
1436 gnm_sheet_view_weak_ref (gnm_app_clipboard_sheet_view_get (),
1437 &(me->cut_copy_view));
1438 } else
1439 me->cutcopied = NULL;
1440
1441 me->cmd.sheet = sheet;
1442 me->cmd.size = count * 10; /* FIXME? */
1443 me->cmd.cmd_descriptor = descriptor;
1444
1445 return gnm_command_push_undo (wbc, G_OBJECT (me));
1446 }
1447
1448 gboolean
cmd_insert_cols(WorkbookControl * wbc,Sheet * sheet,int start_col,int count)1449 cmd_insert_cols (WorkbookControl *wbc,
1450 Sheet *sheet, int start_col, int count)
1451 {
1452 char *mesg;
1453 GnmRange r;
1454
1455 range_init_full_sheet (&r, sheet);
1456 r.start.col = r.end.col - count + 1;
1457
1458 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1459 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1460 ngettext ("Inserting %i column before column %s would push data off the sheet. "
1461 "Please enlarge the sheet first.",
1462 "Inserting %i columns before column %s would push data off the sheet. "
1463 "Please enlarge the sheet first.",
1464 count),
1465 count, col_name (start_col));
1466 return TRUE;
1467 }
1468
1469 mesg = g_strdup_printf
1470 (ngettext ("Inserting %d column before %s",
1471 "Inserting %d columns before %s",
1472 count),
1473 count, col_name (start_col));
1474 return cmd_ins_del_colrow (wbc, sheet, TRUE, TRUE, mesg, start_col, count);
1475 }
1476
1477 gboolean
cmd_insert_rows(WorkbookControl * wbc,Sheet * sheet,int start_row,int count)1478 cmd_insert_rows (WorkbookControl *wbc,
1479 Sheet *sheet, int start_row, int count)
1480 {
1481 char *mesg;
1482 GnmRange r;
1483
1484 range_init_full_sheet (&r, sheet);
1485 r.start.row = r.end.row - count + 1;
1486
1487 if (!sheet_range_trim (sheet, &r, FALSE, FALSE)) {
1488 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
1489 ngettext ("Inserting %i row before row %s would push data off the sheet. "
1490 "Please enlarge the sheet first.",
1491 "Inserting %i rows before row %s would push data off the sheet. "
1492 "Please enlarge the sheet first.",
1493 count),
1494 count, row_name (start_row));
1495 return TRUE;
1496 }
1497
1498 mesg = g_strdup_printf
1499 (ngettext ("Inserting %d row before %s",
1500 "Inserting %d rows before %s",
1501 count),
1502 count, row_name (start_row));
1503 return cmd_ins_del_colrow (wbc, sheet, FALSE, TRUE, mesg, start_row, count);
1504 }
1505
1506 gboolean
cmd_delete_cols(WorkbookControl * wbc,Sheet * sheet,int start_col,int count)1507 cmd_delete_cols (WorkbookControl *wbc,
1508 Sheet *sheet, int start_col, int count)
1509 {
1510 char *mesg = g_strdup_printf ((count > 1)
1511 ? _("Deleting columns %s")
1512 : _("Deleting column %s"),
1513 cols_name (start_col, start_col + count - 1));
1514 return cmd_ins_del_colrow (wbc, sheet, TRUE, FALSE, mesg, start_col, count);
1515 }
1516
1517 gboolean
cmd_delete_rows(WorkbookControl * wbc,Sheet * sheet,int start_row,int count)1518 cmd_delete_rows (WorkbookControl *wbc,
1519 Sheet *sheet, int start_row, int count)
1520 {
1521 char *mesg = g_strdup_printf ((count > 1)
1522 ? _("Deleting rows %s")
1523 : _("Deleting row %s"),
1524 rows_name (start_row, start_row + count - 1));
1525 return cmd_ins_del_colrow (wbc, sheet, FALSE, FALSE, mesg, start_row, count);
1526 }
1527
1528 /******************************************************************/
1529
1530 typedef struct {
1531 GSList *selection;
1532 GnmRange const *r;
1533 } cmd_selection_clear_row_handler_t;
1534
1535 static gboolean
cmd_selection_clear_row_handler(GnmColRowIter const * iter,cmd_selection_clear_row_handler_t * data)1536 cmd_selection_clear_row_handler (GnmColRowIter const *iter,
1537 cmd_selection_clear_row_handler_t *data)
1538 {
1539 if ((!iter->cri->in_filter) || iter->cri->visible) {
1540 GnmRange *r = gnm_range_dup (data->r);
1541 r->start.row = r->end.row = iter->pos;
1542 data->selection = g_slist_prepend (data->selection, r);
1543 }
1544 return FALSE;
1545 }
1546
1547 gboolean
cmd_selection_clear(WorkbookControl * wbc,int clear_flags)1548 cmd_selection_clear (WorkbookControl *wbc, int clear_flags)
1549 {
1550 char *names, *descriptor;
1551 GString *types;
1552 SheetView *sv = wb_control_cur_sheet_view (wbc);
1553 GSList *selection = selection_get_ranges (sv, FALSE /* No intersection */);
1554 Sheet *sheet = sv_sheet (sv);
1555 gboolean result;
1556 int size;
1557 GOUndo *undo = NULL;
1558 GOUndo *redo = NULL;
1559 GSList *ranges;
1560
1561 if ((clear_flags & CLEAR_FILTERED_ONLY) != 0 && sheet->filters != NULL) {
1562 /* We need to modify the selection to only include filtered rows. */
1563 cmd_selection_clear_row_handler_t data;
1564 data.selection = selection;
1565 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1566 GnmFilter *filter;
1567 data.r = ranges->data;
1568 filter = gnm_sheet_filter_intersect_rows
1569 (sheet, data.r->start.row, data.r->end.row);
1570 if (filter) {
1571 sheet_colrow_foreach (sheet, FALSE,
1572 data.r->start.row,
1573 data.r->end.row,
1574 (ColRowHandler) cmd_selection_clear_row_handler,
1575 &data);
1576 g_free (ranges->data);
1577 ranges->data = NULL;
1578 }
1579 }
1580 selection = g_slist_remove_all (data.selection, NULL);
1581 }
1582
1583 /* We should first determine whether we break anything by clearing */
1584 /* Check for array subdivision *//* Check for locked cells */
1585 if (sheet_ranges_split_region (sheet, selection,
1586 GO_CMD_CONTEXT (wbc), _("Clear")) ||
1587 cmd_selection_is_locked_effective (sheet, selection, wbc, _("Clear"))) {
1588 range_fragment_free (selection);
1589 return TRUE;
1590 }
1591
1592
1593 /* We now need to build the descriptor */
1594 /* Collect clear types for descriptor */
1595 if (clear_flags != (CLEAR_VALUES | CLEAR_FORMATS | CLEAR_COMMENTS)) {
1596 GSList *m, *l = NULL;
1597 types = g_string_new (NULL);
1598 if (clear_flags & CLEAR_VALUES)
1599 l = g_slist_append (l, g_string_new (_("contents")));
1600 if (clear_flags & CLEAR_FORMATS)
1601 l = g_slist_append (l, g_string_new (_("formats")));
1602 if (clear_flags & CLEAR_COMMENTS)
1603 l = g_slist_append (l, g_string_new (_("comments")));
1604 /* Using a list for this may seem overkill, but is really the only
1605 * right way to do this
1606 */
1607 for (m = l; m != NULL; m = m->next) {
1608 GString *s = m->data;
1609
1610 g_string_append_len (types, s->str, s->len);
1611 g_string_free (s, TRUE);
1612
1613 if (m->next)
1614 g_string_append (types, ", ");
1615 }
1616 g_slist_free (l);
1617 } else
1618 types = g_string_new (_("all"));
1619 /* The range name string will automatically be truncated, we don't
1620 * need to truncate the "types" list because it will not grow
1621 * indefinitely
1622 */
1623 names = undo_range_list_name (sheet, selection);
1624 descriptor = g_strdup_printf (_("Clearing %s in %s"), types->str, names);
1625 g_free (names);
1626 g_string_free (types, TRUE);
1627 size = g_slist_length (selection);
1628
1629 clear_flags |= CLEAR_NOCHECKARRAY;
1630
1631 if (clear_flags & (CLEAR_VALUES | CLEAR_FORMATS))
1632 clear_flags |= CLEAR_RECALC_DEPS;
1633
1634 /* We are now ready to build the redo and undo items */
1635 for (ranges = selection; ranges != NULL ; ranges = ranges->next) {
1636 GnmRange const *r = ranges->data;
1637 GnmSheetRange *sr = gnm_sheet_range_new (sheet, r);
1638
1639 undo = go_undo_combine (undo, clipboard_copy_range_undo (sheet, r));
1640 redo = go_undo_combine
1641 (redo, sheet_clear_region_undo
1642 (sr, clear_flags));
1643 }
1644
1645 range_fragment_free (selection);
1646
1647 result = cmd_generic_with_size (wbc, descriptor, size, undo, redo);
1648 g_free (descriptor);
1649
1650 return result;
1651 }
1652
1653 /******************************************************************/
1654
1655 #define CMD_FORMAT_TYPE (cmd_format_get_type ())
1656 #define CMD_FORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FORMAT_TYPE, CmdFormat))
1657
1658 typedef struct {
1659 GnmCellPos pos;
1660 GnmStyleList *styles;
1661 ColRowIndexList *rows;
1662 ColRowStateGroup *old_heights;
1663 } CmdFormatOldStyle;
1664
1665 typedef struct {
1666 GnmCommand cmd;
1667 GSList *selection;
1668 GSList *old_styles;
1669 GnmStyle *new_style;
1670 GnmBorder **borders;
1671 } CmdFormat;
1672
1673 static void
cmd_format_repeat(GnmCommand const * cmd,WorkbookControl * wbc)1674 cmd_format_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
1675 {
1676 CmdFormat const *orig = (CmdFormat const *) cmd;
1677 int i;
1678
1679 if (orig->new_style)
1680 gnm_style_ref (orig->new_style);
1681 if (orig->borders)
1682 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1683 gnm_style_border_ref (orig->borders [i]);
1684
1685 cmd_selection_format (wbc, orig->new_style, orig->borders, NULL);
1686 }
MAKE_GNM_COMMAND(CmdFormat,cmd_format,cmd_format_repeat)1687 MAKE_GNM_COMMAND (CmdFormat, cmd_format, cmd_format_repeat)
1688
1689 static gboolean
1690 cmd_format_undo (GnmCommand *cmd,
1691 G_GNUC_UNUSED WorkbookControl *wbc)
1692 {
1693 CmdFormat *me = CMD_FORMAT (cmd);
1694
1695 g_return_val_if_fail (me != NULL, TRUE);
1696
1697 if (me->old_styles) {
1698 GSList *rstyles = g_slist_reverse (g_slist_copy (me->old_styles));
1699 GSList *rsel = g_slist_reverse (g_slist_copy (me->selection));
1700 GSList *l1, *l2;
1701
1702 for (l1 = rstyles, l2 = rsel; l1; l1 = l1->next, l2 = l2->next) {
1703 CmdFormatOldStyle *os = l1->data;
1704 GnmRange const *r = l2->data;
1705 GnmSpanCalcFlags flags = sheet_style_set_list
1706 (me->cmd.sheet,
1707 &os->pos, os->styles, NULL, NULL);
1708
1709 if (os->old_heights) {
1710 colrow_restore_state_group (me->cmd.sheet, FALSE,
1711 os->rows,
1712 os->old_heights);
1713 colrow_state_group_destroy (os->old_heights);
1714 os->old_heights = NULL;
1715 colrow_index_list_destroy (os->rows);
1716 os->rows = NULL;
1717 }
1718
1719 sheet_range_calc_spans (me->cmd.sheet, r, flags);
1720 sheet_flag_style_update_range (me->cmd.sheet, r);
1721 }
1722
1723 sheet_redraw_all (me->cmd.sheet, FALSE);
1724 g_slist_free (rstyles);
1725 g_slist_free (rsel);
1726 }
1727
1728 select_selection (me->cmd.sheet, me->selection, wbc);
1729
1730 return FALSE;
1731 }
1732
1733 static gboolean
cmd_format_redo(GnmCommand * cmd,WorkbookControl * wbc)1734 cmd_format_redo (GnmCommand *cmd, WorkbookControl *wbc)
1735 {
1736 CmdFormat *me = CMD_FORMAT (cmd);
1737 GSList *l1, *l2;
1738 gboolean re_fit_height;
1739
1740 g_return_val_if_fail (me != NULL, TRUE);
1741
1742 /* Check for locked cells */
1743 if (cmd_selection_is_locked_effective (me->cmd.sheet, me->selection,
1744 wbc, _("Changing Format")))
1745 return TRUE;
1746
1747 re_fit_height = me->new_style &&
1748 (GNM_SPANCALC_ROW_HEIGHT & gnm_style_required_spanflags (me->new_style));
1749
1750 for (l1 = me->old_styles, l2 = me->selection; l2; l1 = l1->next, l2 = l2->next) {
1751 CmdFormatOldStyle *os = l1->data;
1752 GnmRange const *r = l2->data;
1753
1754 if (me->borders)
1755 sheet_apply_border (me->cmd.sheet, r, me->borders);
1756 if (me->new_style) {
1757 gnm_style_ref (me->new_style);
1758 sheet_apply_style (me->cmd.sheet, r, me->new_style);
1759 if (re_fit_height)
1760 colrow_autofit (me->cmd.sheet, r, FALSE, FALSE,
1761 TRUE, FALSE,
1762 &os->rows, &os->old_heights);
1763 }
1764
1765 sheet_flag_style_update_range (me->cmd.sheet, r);
1766 }
1767 sheet_redraw_all (me->cmd.sheet, FALSE);
1768 sheet_mark_dirty (me->cmd.sheet);
1769
1770 select_selection (me->cmd.sheet, me->selection, wbc);
1771
1772 return FALSE;
1773 }
1774
1775 static void
cmd_format_finalize(GObject * cmd)1776 cmd_format_finalize (GObject *cmd)
1777 {
1778 CmdFormat *me = CMD_FORMAT (cmd);
1779 int i;
1780
1781 if (me->new_style)
1782 gnm_style_unref (me->new_style);
1783 me->new_style = NULL;
1784
1785 if (me->borders) {
1786 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1787 gnm_style_border_unref (me->borders [i]);
1788 g_free (me->borders);
1789 me->borders = NULL;
1790 }
1791
1792 if (me->old_styles != NULL) {
1793 GSList *l;
1794
1795 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
1796 CmdFormatOldStyle *os = l->data;
1797
1798 style_list_free (os->styles);
1799 colrow_index_list_destroy (os->rows);
1800 colrow_state_group_destroy (os->old_heights);
1801 g_free (os);
1802 }
1803 me->old_styles = NULL;
1804 }
1805
1806 range_fragment_free (me->selection);
1807 me->selection = NULL;
1808
1809 gnm_command_finalize (cmd);
1810 }
1811
1812 /**
1813 * cmd_format: (skip)
1814 * @wbc: the workbook control.
1815 * @sheet: the sheet
1816 * @style: (transfer full): style to apply to the selection
1817 * @borders: (nullable) (transfer full): borders to apply to the selection
1818 * @opt_translated_name: An optional name to use in place of 'Format Cells'
1819 *
1820 * If borders is non-%NULL, then the GnmBorder references are passed,
1821 * the GnmStyle reference is also passed.
1822 *
1823 * Returns: %TRUE if there was a problem, %FALSE otherwise.
1824 **/
1825 gboolean
cmd_selection_format(WorkbookControl * wbc,GnmStyle * style,GnmBorder ** borders,char const * opt_translated_name)1826 cmd_selection_format (WorkbookControl *wbc,
1827 GnmStyle *style, GnmBorder **borders,
1828 char const *opt_translated_name)
1829 {
1830 CmdFormat *me;
1831 GSList *l;
1832 SheetView *sv = wb_control_cur_sheet_view (wbc);
1833
1834 me = g_object_new (CMD_FORMAT_TYPE, NULL);
1835
1836 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
1837 me->new_style = style;
1838
1839 me->cmd.sheet = sv_sheet (sv);
1840 me->cmd.size = 1; /* Updated below. */
1841
1842 me->old_styles = NULL;
1843 for (l = me->selection; l; l = l->next) {
1844 GnmRange const *sel_r = l->data;
1845 GnmRange range = *sel_r;
1846 CmdFormatOldStyle *os;
1847
1848 /* Store the containing range to handle borders */
1849 if (borders != NULL) {
1850 if (range.start.col > 0) range.start.col--;
1851 if (range.start.row > 0) range.start.row--;
1852 if (range.end.col < gnm_sheet_get_last_col (me->cmd.sheet)) range.end.col++;
1853 if (range.end.row < gnm_sheet_get_last_row (me->cmd.sheet)) range.end.row++;
1854 }
1855
1856 os = g_new (CmdFormatOldStyle, 1);
1857
1858 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
1859 os->pos = range.start;
1860 os->rows = NULL;
1861 os->old_heights = NULL;
1862
1863 me->cmd.size += g_slist_length (os->styles);
1864 me->old_styles = g_slist_append (me->old_styles, os);
1865 }
1866
1867 if (borders) {
1868 int i;
1869
1870 me->borders = g_new (GnmBorder *, GNM_STYLE_BORDER_EDGE_MAX);
1871 for (i = GNM_STYLE_BORDER_TOP; i < GNM_STYLE_BORDER_EDGE_MAX; i++)
1872 me->borders [i] = borders [i];
1873 } else
1874 me->borders = NULL;
1875
1876 if (opt_translated_name == NULL) {
1877 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
1878
1879 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing format of %s"), names);
1880 g_free (names);
1881 } else
1882 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
1883
1884 return gnm_command_push_undo (wbc, G_OBJECT (me));
1885 }
1886
1887 /******************************************************************/
1888
1889 static gboolean
cmd_selection_format_toggle_font_style_filter(PangoAttribute * attribute,PangoAttrType * pt)1890 cmd_selection_format_toggle_font_style_filter (PangoAttribute *attribute, PangoAttrType *pt)
1891 {
1892 return ((attribute->klass->type == *pt) ||
1893 ((PANGO_ATTR_RISE == *pt) && (attribute->klass->type == PANGO_ATTR_SCALE)));
1894 }
1895
1896 typedef struct {
1897 GOUndo *undo;
1898 PangoAttrType pt;
1899 } csftfs;
1900
1901 static GnmValue *
cmd_selection_format_toggle_font_style_cb(GnmCellIter const * iter,csftfs * closure)1902 cmd_selection_format_toggle_font_style_cb (GnmCellIter const *iter, csftfs *closure)
1903 {
1904 if (iter->cell && iter->cell->value && VALUE_IS_STRING (iter->cell->value)) {
1905 const GOFormat *fmt = VALUE_FMT (iter->cell->value);
1906 if (fmt && go_format_is_markup (fmt)) {
1907 const PangoAttrList *old_markup =
1908 go_format_get_markup (fmt);
1909 PangoAttrList *new_markup = pango_attr_list_copy ((PangoAttrList *)old_markup);
1910 PangoAttrList *other = pango_attr_list_filter
1911 (new_markup,
1912 (PangoAttrFilterFunc) cmd_selection_format_toggle_font_style_filter,
1913 &closure->pt);
1914 if (other != NULL) {
1915 GnmSheetRange *sr;
1916 GnmRange r;
1917 range_init_cellpos (&r, &iter->pp.eval);
1918 sr = gnm_sheet_range_new (iter->pp.sheet, &r);
1919 closure->undo = go_undo_combine (closure->undo,
1920 sheet_range_set_markup_undo (sr, new_markup));
1921 }
1922 pango_attr_list_unref (new_markup);
1923 pango_attr_list_unref (other);
1924 }
1925 }
1926 return NULL;
1927 }
1928
1929 gboolean
cmd_selection_format_toggle_font_style(WorkbookControl * wbc,GnmStyle * style,GnmStyleElement t)1930 cmd_selection_format_toggle_font_style (WorkbookControl *wbc,
1931 GnmStyle *style, GnmStyleElement t)
1932 {
1933 SheetView *sv = wb_control_cur_sheet_view (wbc);
1934 Sheet *sheet = sv->sheet;
1935 GSList *selection = selection_get_ranges (sv, FALSE), *l;
1936 gboolean result;
1937 char *text, *name;
1938 GOUndo *undo = NULL;
1939 GOUndo *redo = NULL;
1940 PangoAttrType pt;
1941
1942
1943 switch (t) {
1944 case MSTYLE_FONT_BOLD:
1945 pt = PANGO_ATTR_WEIGHT;
1946 break;
1947 case MSTYLE_FONT_ITALIC:
1948 pt = PANGO_ATTR_STYLE;
1949 break;
1950 case MSTYLE_FONT_UNDERLINE:
1951 pt = PANGO_ATTR_UNDERLINE;
1952 break;
1953 case MSTYLE_FONT_STRIKETHROUGH:
1954 pt = PANGO_ATTR_STRIKETHROUGH;
1955 break;
1956 case MSTYLE_FONT_SCRIPT:
1957 pt = PANGO_ATTR_RISE; /* and PANGO_ATTR_SCALE (see ) */
1958 break;
1959 default:
1960 pt = PANGO_ATTR_INVALID;
1961 break;
1962 }
1963
1964
1965 name = undo_range_list_name (sheet, selection);
1966 text = g_strdup_printf (_("Setting Font Style of %s"), name);
1967 g_free (name);
1968
1969 for (l = selection; l != NULL; l = l->next) {
1970 GnmSheetRange *sr;
1971 undo = go_undo_combine
1972 (undo, clipboard_copy_range_undo (sheet, l->data));
1973 sr = gnm_sheet_range_new (sheet, l->data);
1974 redo = go_undo_combine
1975 (redo, sheet_apply_style_undo (sr, style));
1976 if (pt != PANGO_ATTR_INVALID) {
1977 csftfs closure;
1978 closure.undo = NULL;
1979 closure.pt = pt;
1980 sheet_foreach_cell_in_range
1981 (sheet, CELL_ITER_IGNORE_BLANK, &sr->range,
1982 (CellIterFunc) cmd_selection_format_toggle_font_style_cb,
1983 &closure);
1984 redo = go_undo_combine (redo, closure.undo);
1985 }
1986 }
1987 gnm_style_unref (style);
1988 result = cmd_generic (wbc, text, undo, redo);
1989 g_free (text);
1990 range_fragment_free (selection);
1991
1992 return result;
1993 }
1994
1995
1996 /******************************************************************/
1997
1998
1999 gboolean
cmd_resize_colrow(WorkbookControl * wbc,Sheet * sheet,gboolean is_cols,ColRowIndexList * selection,int new_size)2000 cmd_resize_colrow (WorkbookControl *wbc, Sheet *sheet,
2001 gboolean is_cols, ColRowIndexList *selection,
2002 int new_size)
2003 {
2004 int size = 1;
2005 char *text;
2006 GOUndo *undo = NULL;
2007 GOUndo *redo = NULL;
2008 gboolean is_single, result;
2009 GString *list;
2010 ColRowStateGroup *saved_state;
2011
2012 list = colrow_index_list_to_string (selection, is_cols, &is_single);
2013 gnm_cmd_trunc_descriptor (list, NULL);
2014
2015 if (is_single) {
2016 if (new_size < 0)
2017 text = is_cols
2018 ? g_strdup_printf (_("Autofitting column %s"), list->str)
2019 : g_strdup_printf (_("Autofitting row %s"), list->str);
2020 else if (new_size > 0)
2021 text = is_cols
2022 ? g_strdup_printf (ngettext ("Setting width of column %s to %d pixel",
2023 "Setting width of column %s to %d pixels",
2024 new_size),
2025 list->str, new_size)
2026 : g_strdup_printf (ngettext ("Setting height of row %s to %d pixel",
2027 "Setting height of row %s to %d pixels",
2028 new_size),
2029 list->str, new_size);
2030 else text = is_cols
2031 ? g_strdup_printf (_("Setting width of column %s to default"),
2032 list->str)
2033 : g_strdup_printf (
2034 _("Setting height of row %s to default"), list->str);
2035 } else {
2036 if (new_size < 0)
2037 text = is_cols
2038 ? g_strdup_printf (_("Autofitting columns %s"), list->str)
2039 : g_strdup_printf (_("Autofitting rows %s"), list->str);
2040 else if (new_size > 0)
2041 text = is_cols
2042 ? g_strdup_printf (ngettext("Setting width of columns %s to %d pixel",
2043 "Setting width of columns %s to %d pixels",
2044 new_size),
2045 list->str, new_size)
2046 : g_strdup_printf (ngettext("Setting height of rows %s to %d pixel",
2047 "Setting height of rows %s to %d pixels",
2048 new_size),
2049 list->str, new_size);
2050 else text = is_cols
2051 ? g_strdup_printf (
2052 _("Setting width of columns %s to default"), list->str)
2053 : g_strdup_printf (
2054 _("Setting height of rows %s to default"), list->str);
2055 }
2056 g_string_free (list, TRUE);
2057
2058 saved_state = colrow_get_sizes (sheet, is_cols, selection, new_size);
2059 undo = gnm_undo_colrow_restore_state_group_new
2060 (sheet, is_cols, colrow_index_list_copy (selection), saved_state);
2061
2062 redo = gnm_undo_colrow_set_sizes_new (sheet, is_cols, selection, new_size, NULL);
2063
2064 result = cmd_generic_with_size (wbc, text, size, undo, redo);
2065 g_free (text);
2066
2067 return result;
2068 }
2069
2070 gboolean
cmd_autofit_selection(WorkbookControl * wbc,SheetView * sv,Sheet * sheet,gboolean fit_width,ColRowIndexList * selectionlist)2071 cmd_autofit_selection (WorkbookControl *wbc, SheetView *sv, Sheet *sheet, gboolean fit_width,
2072 ColRowIndexList *selectionlist)
2073 {
2074 GOUndo *undo = NULL;
2075 GOUndo *redo = NULL;
2076 gboolean result;
2077 ColRowStateGroup *saved_state;
2078 GSList *l, *selection = selection_get_ranges (sv, TRUE);
2079 gchar *names = undo_range_list_name (sheet, selection);
2080 gchar const *format = fit_width ?
2081 N_("Autofitting width of %s") : N_("Autofitting height of %s");
2082 gchar *text = g_strdup_printf (_(format), names);
2083
2084 g_free (names);
2085
2086 saved_state = colrow_get_sizes (sheet, fit_width, selectionlist, -1);
2087 undo = gnm_undo_colrow_restore_state_group_new
2088 (sheet, fit_width, colrow_index_list_copy (selectionlist), saved_state);
2089
2090 for (l = selection; l != NULL; l = l->next)
2091 redo = go_undo_combine
2092 (redo, gnm_undo_colrow_set_sizes_new
2093 (sheet, fit_width, NULL, -1, l->data));
2094
2095 result = cmd_generic (wbc, text, undo, redo);
2096 g_free (text);
2097 return result;
2098 }
2099
2100
2101 /******************************************************************/
2102
2103 #define CMD_SORT_TYPE (cmd_sort_get_type ())
2104 #define CMD_SORT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SORT_TYPE, CmdSort))
2105
2106 typedef struct {
2107 GnmCommand cmd;
2108
2109 GnmSortData *data;
2110 int *perm;
2111 GnmCellRegion *old_contents;
2112 } CmdSort;
2113
MAKE_GNM_COMMAND(CmdSort,cmd_sort,NULL)2114 MAKE_GNM_COMMAND (CmdSort, cmd_sort, NULL)
2115
2116 static void
2117 cmd_sort_finalize (GObject *cmd)
2118 {
2119 CmdSort *me = CMD_SORT (cmd);
2120
2121 if (me->data != NULL)
2122 gnm_sort_data_destroy (me->data);
2123 g_free (me->perm);
2124 if (me->old_contents != NULL)
2125 cellregion_unref (me->old_contents);
2126
2127 gnm_command_finalize (cmd);
2128 }
2129
2130 static gboolean
cmd_sort_undo(GnmCommand * cmd,WorkbookControl * wbc)2131 cmd_sort_undo (GnmCommand *cmd, WorkbookControl *wbc)
2132 {
2133 CmdSort *me = CMD_SORT (cmd);
2134 GnmSortData *data = me->data;
2135 GnmPasteTarget pt;
2136
2137 paste_target_init (&pt, data->sheet, data->range,
2138 PASTE_CONTENTS | PASTE_FORMATS | PASTE_COMMENTS |
2139 (data->retain_formats ? PASTE_FORMATS : 0));
2140 clipboard_paste_region (me->old_contents,
2141 &pt,
2142 GO_CMD_CONTEXT (wbc));
2143
2144 return FALSE;
2145 }
2146
2147 static gboolean
cmd_sort_redo(GnmCommand * cmd,WorkbookControl * wbc)2148 cmd_sort_redo (GnmCommand *cmd, WorkbookControl *wbc)
2149 {
2150 CmdSort *me = CMD_SORT (cmd);
2151 GnmSortData *data = me->data;
2152
2153 /* Check for locks */
2154 if (cmd_cell_range_is_locked_effective
2155 (data->sheet, data->range, wbc, _("Sorting")))
2156 return TRUE;
2157
2158 if (me->perm)
2159 gnm_sort_position (data, me->perm, GO_CMD_CONTEXT (wbc));
2160 else {
2161 me->old_contents =
2162 clipboard_copy_range (data->sheet, data->range);
2163 me->cmd.size = cellregion_cmd_size (me->old_contents);
2164 me->perm = gnm_sort_contents (data, GO_CMD_CONTEXT (wbc));
2165 }
2166
2167 return FALSE;
2168 }
2169
2170 gboolean
cmd_sort(WorkbookControl * wbc,GnmSortData * data)2171 cmd_sort (WorkbookControl *wbc, GnmSortData *data)
2172 {
2173 CmdSort *me;
2174 char *desc;
2175
2176 g_return_val_if_fail (data != NULL, TRUE);
2177
2178 desc = g_strdup_printf (_("Sorting %s"), range_as_string (data->range));
2179 if (sheet_range_contains_merges_or_arrays (data->sheet, data->range, GO_CMD_CONTEXT (wbc), desc, TRUE, TRUE)) {
2180 gnm_sort_data_destroy (data);
2181 g_free (desc);
2182 return TRUE;
2183 }
2184
2185 me = g_object_new (CMD_SORT_TYPE, NULL);
2186
2187 me->data = data;
2188 me->perm = NULL;
2189 me->cmd.sheet = data->sheet;
2190 me->cmd.size = 1; /* Changed in initial redo. */
2191 me->cmd.cmd_descriptor = desc;
2192
2193 return gnm_command_push_undo (wbc, G_OBJECT (me));
2194 }
2195
2196 /******************************************************************/
2197
2198 #define CMD_COLROW_HIDE_TYPE (cmd_colrow_hide_get_type ())
2199 #define CMD_COLROW_HIDE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_HIDE_TYPE, CmdColRowHide))
2200
2201 typedef struct {
2202 GnmCommand cmd;
2203
2204 gboolean is_cols;
2205 ColRowVisList *hide, *show;
2206 } CmdColRowHide;
2207
2208 static void
cmd_colrow_hide_repeat(GnmCommand const * cmd,WorkbookControl * wbc)2209 cmd_colrow_hide_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2210 {
2211 CmdColRowHide const *orig = (CmdColRowHide const *) cmd;
2212 cmd_selection_colrow_hide (wbc, orig->is_cols, orig->show != NULL);
2213 }
MAKE_GNM_COMMAND(CmdColRowHide,cmd_colrow_hide,cmd_colrow_hide_repeat)2214 MAKE_GNM_COMMAND (CmdColRowHide, cmd_colrow_hide, cmd_colrow_hide_repeat)
2215
2216 /**
2217 * cmd_colrow_hide_correct_selection:
2218 *
2219 * Try to ensure that the selection/cursor is set to a visible row/col
2220 *
2221 * Added to fix bug 38179
2222 * Removed because the result is irritating and the bug is actually XL
2223 * compatibile
2224 **/
2225 static void
2226 cmd_colrow_hide_correct_selection (G_GNUC_UNUSED CmdColRowHide *me, G_GNUC_UNUSED WorkbookControl *wbc)
2227 {
2228 #if 0
2229 int x, y, index;
2230 SheetView *sv = sheet_get_view (me->cmd.sheet,
2231 wb_control_view (wbc));
2232
2233 index = colrow_find_adjacent_visible (me->cmd.sheet, me->is_cols,
2234 me->is_cols ? sv->edit_pos.col : sv->edit_pos.row,
2235 TRUE);
2236
2237 x = me->is_cols ? sv->edit_pos.row : index;
2238 y = me->is_cols ? index : sv->edit_pos.col;
2239
2240 if (index >= 0) {
2241 sv_selection_reset (sv);
2242 if (me->is_cols)
2243 sv_selection_add_full (sv, y, x, y, 0,
2244 y, gnm_sheet_get_last_row (sheet),
2245 GNM_SELECTION_MODE_ADD);
2246 else
2247 sv_selection_add_full (sv, y, x, 0, x,
2248 gnm_sheet_get_last_col (sheet), x,
2249 GNM_SELECTION_MODE_ADD);
2250 }
2251 #endif
2252 }
2253
2254 static gboolean
cmd_colrow_hide_undo(GnmCommand * cmd,WorkbookControl * wbc)2255 cmd_colrow_hide_undo (GnmCommand *cmd, WorkbookControl *wbc)
2256 {
2257 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2258
2259 g_return_val_if_fail (me != NULL, TRUE);
2260
2261 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2262 TRUE, me->hide);
2263 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2264 FALSE, me->show);
2265
2266 if (me->show != NULL)
2267 cmd_colrow_hide_correct_selection (me, wbc);
2268
2269 return FALSE;
2270 }
2271
2272 static gboolean
cmd_colrow_hide_redo(GnmCommand * cmd,WorkbookControl * wbc)2273 cmd_colrow_hide_redo (GnmCommand *cmd, WorkbookControl *wbc)
2274 {
2275 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2276
2277 g_return_val_if_fail (me != NULL, TRUE);
2278
2279 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2280 FALSE, me->hide);
2281 colrow_set_visibility_list (me->cmd.sheet, me->is_cols,
2282 TRUE, me->show);
2283
2284 if (me->hide != NULL)
2285 cmd_colrow_hide_correct_selection (me, wbc);
2286
2287 return FALSE;
2288 }
2289
2290 static void
cmd_colrow_hide_finalize(GObject * cmd)2291 cmd_colrow_hide_finalize (GObject *cmd)
2292 {
2293 CmdColRowHide *me = CMD_COLROW_HIDE (cmd);
2294 colrow_vis_list_destroy (me->hide);
2295 me->hide = NULL;
2296 colrow_vis_list_destroy (me->show);
2297 me->show = NULL;
2298 gnm_command_finalize (cmd);
2299 }
2300
2301 gboolean
cmd_selection_colrow_hide(WorkbookControl * wbc,gboolean is_cols,gboolean visible)2302 cmd_selection_colrow_hide (WorkbookControl *wbc,
2303 gboolean is_cols, gboolean visible)
2304 {
2305 CmdColRowHide *me;
2306 SheetView *sv = wb_control_cur_sheet_view (wbc);
2307 int n;
2308 Sheet *sheet;
2309 GSList *show = NULL, *hide = NULL;
2310
2311 if (visible)
2312 show = colrow_get_visibility_toggle (sv, is_cols, TRUE);
2313 else
2314 hide = colrow_get_visibility_toggle (sv, is_cols, FALSE);
2315 n = colrow_vis_list_length (hide) + colrow_vis_list_length (show);
2316 sheet = sv_sheet (sv);
2317
2318 if (!visible) {
2319 /* If these are the last colrows to hide, check with the user */
2320 int count = 0;
2321 if (is_cols) {
2322 int i, max = gnm_sheet_get_max_cols (sheet);
2323 ColRowInfo *ci;
2324 for (i = 0 ; i < max ; i++)
2325 if (NULL ==
2326 (ci = sheet_col_get (sheet, i)) ||
2327 (ci->visible))
2328 count++;
2329 } else {
2330 int i, max = gnm_sheet_get_max_rows (sheet);
2331 ColRowInfo *ci;
2332 for (i = 0 ; i < max ; i++)
2333 if (NULL ==
2334 (ci = sheet_row_get (sheet, i)) ||
2335 (ci->visible))
2336 count++;
2337 }
2338 if (count <= n) {
2339 gchar const *text = is_cols ?
2340 _("Are you sure that you want to hide all columns? "
2341 "If you do so you can unhide them with the "
2342 "'Format\342\206\222Column\342\206\222Unhide' "
2343 "menu item.") :
2344 _("Are you sure that you want to hide all rows? "
2345 "If you do so you can unhide them with the "
2346 "'Format\342\206\222Row\342\206\222Unhide' "
2347 "menu item.");
2348 if (!go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)),
2349 FALSE, "%s", text)) {
2350 colrow_vis_list_destroy (show);
2351 colrow_vis_list_destroy (hide);
2352 return TRUE;
2353 }
2354 }
2355 }
2356
2357 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2358 me->show = show;
2359 me->hide = hide;
2360 me->is_cols = is_cols;
2361 me->cmd.sheet = sheet;
2362 me->cmd.size = 1 + g_slist_length (hide) + g_slist_length (show);
2363 me->cmd.cmd_descriptor = g_strdup (is_cols
2364 ? (visible ? _("Unhide columns") : _("Hide columns"))
2365 : (visible ? _("Unhide rows") : _("Hide rows")));
2366
2367 return gnm_command_push_undo (wbc, G_OBJECT (me));
2368 }
2369
2370 gboolean
cmd_selection_outline_change(WorkbookControl * wbc,gboolean is_cols,int index,int depth)2371 cmd_selection_outline_change (WorkbookControl *wbc,
2372 gboolean is_cols, int index, int depth)
2373 {
2374 CmdColRowHide *me;
2375 ColRowInfo const *cri;
2376 int first = -1, last = -1;
2377 gboolean visible = FALSE;
2378 int d;
2379 Sheet *sheet = wb_control_cur_sheet (wbc);
2380 SheetView *sv = wb_control_cur_sheet_view (wbc);
2381
2382 cri = sheet_colrow_get_info (sheet, index, is_cols);
2383
2384 d = cri->outline_level;
2385 if (depth > d)
2386 depth = d;
2387
2388 /* Nodes only collapse when selected directly, selecting at a lower
2389 * level is a standard toggle. */
2390 if (depth == d) {
2391 if ((is_cols ? sheet->outline_symbols_right : sheet->outline_symbols_below)) {
2392 if (index > 0) {
2393 ColRowInfo const *prev =
2394 sheet_colrow_get (sheet, index-1, is_cols);
2395
2396 if (prev != NULL && prev->outline_level > d) {
2397 visible = (depth == d && cri->is_collapsed);
2398 last = index - 1;
2399 first = colrow_find_outline_bound (sheet, is_cols,
2400 last, d+1, FALSE);
2401 }
2402 }
2403 } else if (index+1 < colrow_max (is_cols, sheet)) {
2404 ColRowInfo const *next =
2405 sheet_colrow_get (sheet, index+1, is_cols);
2406
2407 if (next != NULL && next->outline_level > d) {
2408 visible = (depth == d && cri->is_collapsed);
2409 first = index + 1;
2410 last = colrow_find_outline_bound (sheet, is_cols,
2411 first, d+1, TRUE);
2412 }
2413 }
2414 }
2415
2416 /* If nothing done yet do a simple collapse */
2417 if (first < 0 && cri->outline_level > 0) {
2418 if (depth < d)
2419 ++depth;
2420 first = colrow_find_outline_bound (sheet, is_cols, index, depth, FALSE);
2421 last = colrow_find_outline_bound (sheet, is_cols, index, depth, TRUE);
2422 visible = FALSE;
2423
2424 if (first == last && depth > cri->outline_level)
2425 return TRUE;
2426 }
2427
2428 if (first < 0 || last < 0)
2429 return TRUE;
2430
2431 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2432
2433 me->is_cols = is_cols;
2434 me->hide = me->show = NULL;
2435 if (visible)
2436 me->show = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2437 TRUE, first, last);
2438 else
2439 me->hide = colrow_get_outline_toggle (sv_sheet (sv), is_cols,
2440 FALSE, first, last);
2441
2442 me->cmd.sheet = sv_sheet (sv);
2443 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2444 me->cmd.cmd_descriptor = g_strdup (is_cols
2445 ? (visible ? _("Expand columns") : _("Collapse columns"))
2446 : (visible ? _("Expand rows") : _("Collapse rows")));
2447
2448 return gnm_command_push_undo (wbc, G_OBJECT (me));
2449 }
2450
2451 gboolean
cmd_global_outline_change(WorkbookControl * wbc,gboolean is_cols,int depth)2452 cmd_global_outline_change (WorkbookControl *wbc, gboolean is_cols, int depth)
2453 {
2454 CmdColRowHide *me;
2455 ColRowVisList *hide, *show;
2456 SheetView *sv = wb_control_cur_sheet_view (wbc);
2457
2458 colrow_get_global_outline (sv_sheet (sv), is_cols, depth, &show, &hide);
2459
2460 if (show == NULL && hide == NULL)
2461 return TRUE;
2462
2463 me = g_object_new (CMD_COLROW_HIDE_TYPE, NULL);
2464 me->is_cols = is_cols;
2465 me->hide = hide;
2466 me->show = show;
2467 me->cmd.sheet = sv_sheet (sv);
2468 me->cmd.size = 1 + g_slist_length (me->show) + g_slist_length (me->hide);
2469 me->cmd.cmd_descriptor = g_strdup_printf (is_cols
2470 ? _("Show column outline %d") : _("Show row outline %d"), depth);
2471
2472 return gnm_command_push_undo (wbc, G_OBJECT (me));
2473 }
2474
2475 /******************************************************************/
2476
2477 #define CMD_GROUP_TYPE (cmd_group_get_type ())
2478 #define CMD_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GROUP_TYPE, CmdGroup))
2479
2480 typedef struct {
2481 GnmCommand cmd;
2482
2483 GnmRange range;
2484 gboolean is_cols;
2485 gboolean group;
2486 } CmdGroup;
2487
2488 static void
cmd_group_repeat(GnmCommand const * cmd,WorkbookControl * wbc)2489 cmd_group_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2490 {
2491 CmdGroup const *orig = (CmdGroup const *) cmd;
2492 cmd_selection_group (wbc, orig->is_cols, orig->group);
2493 }
MAKE_GNM_COMMAND(CmdGroup,cmd_group,cmd_group_repeat)2494 MAKE_GNM_COMMAND (CmdGroup, cmd_group, cmd_group_repeat)
2495
2496 static gboolean
2497 cmd_group_undo (GnmCommand *cmd,
2498 G_GNUC_UNUSED WorkbookControl *wbc)
2499 {
2500 CmdGroup const *me = CMD_GROUP (cmd);
2501 sheet_colrow_group_ungroup (me->cmd.sheet,
2502 &me->range, me->is_cols, !me->group);
2503 return FALSE;
2504 }
2505
2506 static gboolean
cmd_group_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)2507 cmd_group_redo (GnmCommand *cmd,
2508 G_GNUC_UNUSED WorkbookControl *wbc)
2509 {
2510 CmdGroup const *me = CMD_GROUP (cmd);
2511 sheet_colrow_group_ungroup (me->cmd.sheet,
2512 &me->range, me->is_cols, me->group);
2513 return FALSE;
2514 }
2515
2516 static void
cmd_group_finalize(GObject * cmd)2517 cmd_group_finalize (GObject *cmd)
2518 {
2519 gnm_command_finalize (cmd);
2520 }
2521
2522 gboolean
cmd_selection_group(WorkbookControl * wbc,gboolean is_cols,gboolean group)2523 cmd_selection_group (WorkbookControl *wbc,
2524 gboolean is_cols, gboolean group)
2525 {
2526 CmdGroup *me;
2527 SheetView *sv;
2528 GnmRange r;
2529
2530 g_return_val_if_fail (wbc != NULL, TRUE);
2531
2532 sv = wb_control_cur_sheet_view (wbc);
2533 r = *selection_first_range (sv, NULL, NULL);
2534
2535 /* Check if this really is possible and display an error if it's not */
2536 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2537 if (group) {
2538 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2539 ? _("Those columns are already grouped")
2540 : _("Those rows are already grouped"));
2541 return TRUE;
2542 }
2543
2544 /* see if the user selected the col/row with the marker too */
2545 if (is_cols) {
2546 if (r.start.col != r.end.col) {
2547 if (sv->sheet->outline_symbols_right)
2548 r.end.col--;
2549 else
2550 r.start.col++;
2551 }
2552 } else {
2553 if (r.start.row != r.end.row) {
2554 if (sv->sheet->outline_symbols_below)
2555 r.end.row--;
2556 else
2557 r.start.row++;
2558 }
2559 }
2560
2561 if (sheet_colrow_can_group (sv->sheet, &r, is_cols) != group) {
2562 go_cmd_context_error_system (GO_CMD_CONTEXT (wbc), is_cols
2563 ? _("Those columns are not grouped, you can't ungroup them")
2564 : _("Those rows are not grouped, you can't ungroup them"));
2565 return TRUE;
2566 }
2567 }
2568
2569 me = g_object_new (CMD_GROUP_TYPE, NULL);
2570 me->is_cols = is_cols;
2571 me->group = group;
2572 me->range = r;
2573
2574 me->cmd.sheet = sv->sheet;
2575 me->cmd.size = 1;
2576 me->cmd.cmd_descriptor = is_cols
2577 ? g_strdup_printf (group ? _("Group columns %s") : _("Ungroup columns %s"),
2578 cols_name (me->range.start.col, me->range.end.col))
2579 : g_strdup_printf (group ? _("Group rows %d:%d") : _("Ungroup rows %d:%d"),
2580 me->range.start.row + 1, me->range.end.row + 1);
2581
2582 return gnm_command_push_undo (wbc, G_OBJECT (me));
2583 }
2584
2585 /******************************************************************/
2586
2587 #define CMD_PASTE_CUT_TYPE (cmd_paste_cut_get_type ())
2588 #define CMD_PASTE_CUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_CUT_TYPE, CmdPasteCut))
2589
2590 typedef struct {
2591 GnmCommand cmd;
2592
2593 GnmExprRelocateInfo info;
2594 GSList *paste_contents;
2595 GOUndo *reloc_undo;
2596 gboolean move_selection;
2597 ColRowStateList *saved_sizes;
2598
2599 /* handle redo-ing an undo with contents from a deleted sheet */
2600 GnmCellRegion *deleted_sheet_contents;
2601 } CmdPasteCut;
2602
2603 MAKE_GNM_COMMAND (CmdPasteCut, cmd_paste_cut, NULL)
2604
2605 typedef struct {
2606 GnmPasteTarget pt;
2607 GnmCellRegion *contents;
2608 } PasteContent;
2609
2610 /**
2611 * cmd_paste_cut_update:
2612 *
2613 * Utility routine to update things when we are transfering between sheets and
2614 * workbooks.
2615 */
2616 static void
cmd_paste_cut_update(GnmExprRelocateInfo const * info,G_GNUC_UNUSED WorkbookControl * wbc)2617 cmd_paste_cut_update (GnmExprRelocateInfo const *info,
2618 G_GNUC_UNUSED WorkbookControl *wbc)
2619 {
2620 Sheet *o = info->origin_sheet;
2621 Sheet *t = info->target_sheet;
2622
2623 /* Dirty and update both sheets */
2624 sheet_mark_dirty (t);
2625 sheet_update (t);
2626
2627 if (IS_SHEET (o) && o != t) {
2628 sheet_mark_dirty (o);
2629 sheet_update (o);
2630 }
2631 }
2632
2633 static gboolean
cmd_paste_cut_undo(GnmCommand * cmd,WorkbookControl * wbc)2634 cmd_paste_cut_undo (GnmCommand *cmd, WorkbookControl *wbc)
2635 {
2636 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2637 GnmExprRelocateInfo reverse;
2638
2639 g_return_val_if_fail (me != NULL, TRUE);
2640 g_return_val_if_fail (me->paste_contents != NULL, TRUE);
2641 g_return_val_if_fail (me->deleted_sheet_contents == NULL, TRUE);
2642
2643 reverse.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
2644 reverse.target_sheet = me->info.origin_sheet;
2645 reverse.origin_sheet = me->info.target_sheet;
2646 reverse.origin = me->info.origin;
2647 range_translate (&reverse.origin,
2648 me->info.origin_sheet, /* FIXME: What sheet? */
2649 me->info.col_offset,
2650 me->info.row_offset);
2651 reverse.col_offset = -me->info.col_offset;
2652 reverse.row_offset = -me->info.row_offset;
2653
2654 /* Move things back being careful NOT to invalidate the src region */
2655 if (IS_SHEET (me->info.origin_sheet))
2656 sheet_move_range (&reverse, NULL, GO_CMD_CONTEXT (wbc));
2657 else
2658 me->deleted_sheet_contents = clipboard_copy_range (
2659 reverse.origin_sheet, &reverse.origin);
2660
2661 /* Restore the original row heights */
2662 colrow_set_states (me->info.target_sheet, FALSE,
2663 reverse.origin.start.row, me->saved_sizes);
2664 colrow_state_list_destroy (me->saved_sizes);
2665 me->saved_sizes = NULL;
2666
2667 if (me->reloc_undo) {
2668 go_undo_undo (me->reloc_undo);
2669 g_object_unref (me->reloc_undo);
2670 me->reloc_undo = NULL;
2671 }
2672
2673 while (me->paste_contents) {
2674 PasteContent *pc = me->paste_contents->data;
2675 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2676
2677 clipboard_paste_region (pc->contents, &pc->pt, GO_CMD_CONTEXT (wbc));
2678 cellregion_unref (pc->contents);
2679 g_free (pc);
2680 }
2681
2682 /* Force update of the status area */
2683 sheet_flag_status_update_range (me->info.target_sheet, NULL);
2684
2685 cmd_paste_cut_update (&me->info, wbc);
2686
2687 /* Select the original region */
2688 if (me->move_selection && IS_SHEET (me->info.origin_sheet))
2689 select_range (me->info.origin_sheet,
2690 &me->info.origin,
2691 wbc);
2692
2693 return FALSE;
2694 }
2695
2696 static gboolean
cmd_paste_cut_redo(GnmCommand * cmd,WorkbookControl * wbc)2697 cmd_paste_cut_redo (GnmCommand *cmd, WorkbookControl *wbc)
2698 {
2699 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2700 GnmRange tmp;
2701
2702 g_return_val_if_fail (me != NULL, TRUE);
2703 g_return_val_if_fail (me->paste_contents == NULL, TRUE);
2704
2705 tmp = me->info.origin;
2706 range_translate (&tmp, me->info.origin_sheet, /* FIXME: What sheet? */
2707 me->info.col_offset, me->info.row_offset);
2708 range_normalize (&tmp);
2709
2710 g_return_val_if_fail (range_is_sane (&tmp), TRUE);
2711
2712 if (me->info.origin_sheet != me->info.target_sheet ||
2713 !range_overlap (&me->info.origin, &tmp)) {
2714 PasteContent *pc = g_new (PasteContent, 1);
2715 paste_target_init (&pc->pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2716 pc->contents = clipboard_copy_range (me->info.target_sheet, &tmp);
2717 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2718 } else {
2719 /* need to store any portions of the paste target
2720 * that do not overlap with the source.
2721 */
2722 GSList *ptr, *frag = range_split_ranges (&me->info.origin, &tmp);
2723 for (ptr = frag ; ptr != NULL ; ptr = ptr->next) {
2724 GnmRange *r = ptr->data;
2725
2726 if (!range_overlap (&me->info.origin, r)) {
2727 PasteContent *pc = g_new (PasteContent, 1);
2728 paste_target_init (&pc->pt, me->info.target_sheet, r, PASTE_ALL_SHEET);
2729 pc->contents = clipboard_copy_range (me->info.target_sheet, r);
2730 me->paste_contents = g_slist_prepend (me->paste_contents, pc);
2731 }
2732 g_free (r);
2733 }
2734 g_slist_free (frag);
2735 }
2736
2737 /* rare corner case. If the origin sheet has been deleted */
2738 if (!IS_SHEET (me->info.origin_sheet)) {
2739 GnmPasteTarget pt;
2740 paste_target_init (&pt, me->info.target_sheet, &tmp, PASTE_ALL_SHEET);
2741 sheet_clear_region (pt.sheet,
2742 tmp.start.col, tmp.start.row, tmp.end.col, tmp.end.row,
2743 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
2744 GO_CMD_CONTEXT (wbc));
2745 clipboard_paste_region (me->deleted_sheet_contents,
2746 &pt, GO_CMD_CONTEXT (wbc));
2747 cellregion_unref (me->deleted_sheet_contents);
2748 me->deleted_sheet_contents = NULL;
2749 } else
2750 sheet_move_range (&me->info, &me->reloc_undo, GO_CMD_CONTEXT (wbc));
2751
2752 cmd_paste_cut_update (&me->info, wbc);
2753
2754 /* Backup row heights and adjust row heights to fit */
2755 me->saved_sizes = colrow_get_states (me->info.target_sheet, FALSE, tmp.start.row, tmp.end.row);
2756 rows_height_update (me->info.target_sheet, &tmp, FALSE);
2757
2758 /* Make sure the destination is selected */
2759 if (me->move_selection)
2760 select_range (me->info.target_sheet, &tmp, wbc);
2761
2762 return FALSE;
2763 }
2764
2765 static void
cmd_paste_cut_finalize(GObject * cmd)2766 cmd_paste_cut_finalize (GObject *cmd)
2767 {
2768 CmdPasteCut *me = CMD_PASTE_CUT (cmd);
2769
2770 if (me->saved_sizes)
2771 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
2772 while (me->paste_contents) {
2773 PasteContent *pc = me->paste_contents->data;
2774 me->paste_contents = g_slist_remove (me->paste_contents, pc);
2775 cellregion_unref (pc->contents);
2776 g_free (pc);
2777 }
2778 if (me->reloc_undo) {
2779 g_object_unref (me->reloc_undo);
2780 me->reloc_undo = NULL;
2781 }
2782 if (me->deleted_sheet_contents) {
2783 cellregion_unref (me->deleted_sheet_contents);
2784 me->deleted_sheet_contents = NULL;
2785 }
2786
2787 gnm_command_finalize (cmd);
2788 }
2789
2790 gboolean
cmd_paste_cut(WorkbookControl * wbc,GnmExprRelocateInfo const * info,gboolean move_selection,char * descriptor)2791 cmd_paste_cut (WorkbookControl *wbc, GnmExprRelocateInfo const *info,
2792 gboolean move_selection, char *descriptor)
2793 {
2794 CmdPasteCut *me;
2795 GnmRange r;
2796 char *where;
2797
2798 g_return_val_if_fail (info != NULL, TRUE);
2799
2800 /* This is vacuous */
2801 if (info->origin_sheet == info->target_sheet &&
2802 info->col_offset == 0 && info->row_offset == 0)
2803 return TRUE;
2804
2805 /* FIXME: Do we want to show the destination range as well ? */
2806 where = undo_range_name (info->origin_sheet, &info->origin);
2807 if (descriptor == NULL)
2808 descriptor = g_strdup_printf (_("Moving %s"), where);
2809 g_free (where);
2810
2811 g_return_val_if_fail (info != NULL, TRUE);
2812
2813 r = info->origin;
2814 if (range_translate (&r, info->target_sheet,
2815 info->col_offset, info->row_offset)) {
2816
2817 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), descriptor,
2818 _("is beyond sheet boundaries"));
2819 g_free (descriptor);
2820 return TRUE;
2821 }
2822
2823 /* Check array subdivision & merged regions */
2824 if (sheet_range_splits_region (info->target_sheet, &r,
2825 (info->origin_sheet == info->target_sheet)
2826 ? &info->origin : NULL, GO_CMD_CONTEXT (wbc), descriptor)) {
2827 g_free (descriptor);
2828 return TRUE;
2829 }
2830
2831 me = g_object_new (CMD_PASTE_CUT_TYPE, NULL);
2832
2833 me->info = *info;
2834 me->paste_contents = NULL;
2835 me->deleted_sheet_contents = NULL;
2836 me->reloc_undo = NULL;
2837 me->move_selection = move_selection;
2838 me->saved_sizes = NULL;
2839
2840 me->cmd.sheet = NULL; /* we have potentially two different. */
2841 me->cmd.size = 1; /* FIXME? */
2842 me->cmd.cmd_descriptor = descriptor;
2843
2844 /* NOTE : if the destination workbook is different from the source
2845 * workbook should we have undo elements in both menus ?? It seems
2846 * poor form to hit undo in 1 window and effect another...
2847 *
2848 * Maybe queue it as two different commands, as a clear in one book
2849 * and a paste in the other. This is not symmetric though. What
2850 * happens to the cells in the original sheet that now reference the
2851 * cells in the other? When do they reset to the original?
2852 *
2853 * Probably when the clear in the original is undone.
2854 */
2855
2856 return gnm_command_push_undo (wbc, G_OBJECT (me));
2857 }
2858
2859 /******************************************************************/
2860
2861 static void
warn_if_date_trouble(WorkbookControl * wbc,GnmCellRegion * cr)2862 warn_if_date_trouble (WorkbookControl *wbc, GnmCellRegion *cr)
2863 {
2864 Workbook *wb = wb_control_get_workbook (wbc);
2865 const GODateConventions *wb_date_conv = workbook_date_conv (wb);
2866
2867 if (cr->date_conv == NULL)
2868 return;
2869 if (go_date_conv_equal (cr->date_conv, wb_date_conv))
2870 return;
2871
2872 /* We would like to show a warning, but it seems we cannot via a context. */
2873 {
2874 GError *err;
2875 err = g_error_new (go_error_invalid(), 0,
2876 _("Copying between files with different date conventions.\n"
2877 "It is possible that some dates could be copied\n"
2878 "incorrectly."));
2879 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
2880 g_error_free (err);
2881 }
2882 }
2883
2884
2885 #define CMD_PASTE_COPY_TYPE (cmd_paste_copy_get_type ())
2886 #define CMD_PASTE_COPY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PASTE_COPY_TYPE, CmdPasteCopy))
2887
2888 typedef struct {
2889 GnmCommand cmd;
2890
2891 GnmCellRegion *contents;
2892 GSList *pasted_objects,*orig_contents_objects;
2893 GnmPasteTarget dst;
2894 gboolean has_been_through_cycle;
2895 gboolean only_objects;
2896 gboolean single_merge_to_single_merge;
2897 } CmdPasteCopy;
2898
2899 static void
cmd_paste_copy_repeat(GnmCommand const * cmd,WorkbookControl * wbc)2900 cmd_paste_copy_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
2901 {
2902 CmdPasteCopy const *orig = (CmdPasteCopy const *) cmd;
2903 GnmPasteTarget new_dst;
2904 SheetView *sv = wb_control_cur_sheet_view (wbc);
2905 GnmRange const *r = selection_first_range (sv,
2906 GO_CMD_CONTEXT (wbc), _("Paste Copy"));
2907 GnmCellRegion *newcr;
2908
2909 if (r == NULL)
2910 return;
2911
2912 paste_target_init (&new_dst, sv_sheet (sv), r, orig->dst.paste_flags);
2913 newcr = clipboard_copy_range (orig->dst.sheet, &orig->dst.range);
2914 cmd_paste_copy (wbc, &new_dst, newcr);
2915 cellregion_unref (newcr);
2916 }
MAKE_GNM_COMMAND(CmdPasteCopy,cmd_paste_copy,cmd_paste_copy_repeat)2917 MAKE_GNM_COMMAND (CmdPasteCopy, cmd_paste_copy, cmd_paste_copy_repeat)
2918
2919 static int
2920 by_addr (gconstpointer a, gconstpointer b)
2921 {
2922 if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b))
2923 return -1;
2924 if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b))
2925 return +1;
2926 return 0;
2927 }
2928
2929 /**
2930 * get_new_objects:
2931 * @sheet: #Sheet to query
2932 * @old: (element-type SheetObject): list of objects to disregard
2933 *
2934 * Returns: (transfer full) (element-type SheetObject): A list of new objects
2935 * in sheet since @old was collected.
2936 */
2937 static GSList *
get_new_objects(Sheet * sheet,GSList * old)2938 get_new_objects (Sheet *sheet, GSList *old)
2939 {
2940 GSList *objs =
2941 g_slist_sort (g_slist_copy_deep (sheet->sheet_objects,
2942 (GCopyFunc)g_object_ref,
2943 NULL),
2944 by_addr);
2945 GSList *p = objs, *last = NULL;
2946
2947 while (old) {
2948 int c = -1;
2949 while (p && (c = by_addr (p->data, old->data)) < 0) {
2950 last = p;
2951 p = p->next;
2952 }
2953
2954 old = old->next;
2955
2956 if (c == 0) {
2957 GSList *next = p->next;
2958 if (last)
2959 last->next = next;
2960 else
2961 objs = next;
2962 g_object_unref (p->data);
2963 g_slist_free_1 (p);
2964 p = next;
2965 }
2966 }
2967
2968 return objs;
2969 }
2970
2971 static void
cmd_paste_copy_select_obj(SheetObject * so,SheetControlGUI * scg)2972 cmd_paste_copy_select_obj (SheetObject *so, SheetControlGUI *scg)
2973 {
2974 scg_object_select (scg, so);
2975 }
2976
2977 static gboolean
cmd_paste_copy_impl(GnmCommand * cmd,WorkbookControl * wbc,gboolean is_undo)2978 cmd_paste_copy_impl (GnmCommand *cmd, WorkbookControl *wbc,
2979 gboolean is_undo)
2980 {
2981 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
2982 GnmCellRegion *contents;
2983 GSList *old_objects;
2984
2985 g_return_val_if_fail (me != NULL, TRUE);
2986 g_return_val_if_fail (me->contents != NULL, TRUE);
2987
2988 g_slist_foreach (me->pasted_objects,
2989 (GFunc)sheet_object_clear_sheet,
2990 NULL);
2991 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
2992 me->pasted_objects = NULL;
2993 old_objects = get_new_objects (me->dst.sheet, NULL);
2994
2995 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
2996 if (me->has_been_through_cycle)
2997 me->dst.paste_flags =
2998 PASTE_CONTENTS |
2999 PASTE_COLUMN_WIDTHS | PASTE_ROW_HEIGHTS |
3000 (me->dst.paste_flags & PASTE_ALL_SHEET);
3001
3002 if (clipboard_paste_region (me->contents, &me->dst,
3003 GO_CMD_CONTEXT (wbc))) {
3004 /* There was a problem, avoid leaking */
3005 cellregion_unref (contents);
3006 g_slist_free_full (old_objects, g_object_unref);
3007 return TRUE;
3008 }
3009
3010 me->pasted_objects = get_new_objects (me->dst.sheet, old_objects);
3011 g_slist_free_full (old_objects, g_object_unref);
3012
3013 if (!is_undo && !me->has_been_through_cycle) {
3014 colrow_autofit (me->dst.sheet, &me->dst.range, FALSE, FALSE,
3015 TRUE, FALSE,
3016 NULL, NULL);
3017 colrow_autofit (me->dst.sheet, &me->dst.range, TRUE, TRUE,
3018 TRUE, FALSE,
3019 NULL, NULL);
3020 }
3021
3022 if (is_undo) {
3023 // We cannot use the random set of objects at the target
3024 // location. http://bugzilla.gnome.org/show_bug.cgi?id=308300
3025 g_slist_free_full (contents->objects, g_object_unref);
3026 contents->objects = g_slist_copy_deep
3027 (me->orig_contents_objects,
3028 (GCopyFunc)sheet_object_dup, NULL);
3029 } else {
3030 GSList *l;
3031 for (l = contents->objects; l; l = l->next) {
3032 SheetObject *so = l->data;
3033 if (sheet_object_get_sheet (so)) {
3034 g_object_unref (so);
3035 l->data = NULL;
3036 } else {
3037 // Object got deleted by paste, so keep it for
3038 // undo. See bugzilla 732653
3039 }
3040 }
3041 contents->objects =
3042 g_slist_remove_all (contents->objects, NULL);
3043 }
3044
3045 cellregion_unref (me->contents);
3046 me->contents = contents;
3047 me->has_been_through_cycle = TRUE;
3048
3049 /* Select the newly pasted contents (this queues a redraw) */
3050 if (me->only_objects && GNM_IS_WBC_GTK (wbc)) {
3051 SheetControlGUI *scg =
3052 wbcg_get_nth_scg (WBC_GTK (wbc),
3053 cmd->sheet->index_in_wb);
3054 scg_object_unselect (scg, NULL);
3055 g_slist_foreach (me->pasted_objects,
3056 (GFunc) cmd_paste_copy_select_obj, scg);
3057 }
3058 select_range (me->dst.sheet, &me->dst.range, wbc);
3059
3060 return FALSE;
3061 }
3062
3063 static gboolean
cmd_paste_copy_undo(GnmCommand * cmd,WorkbookControl * wbc)3064 cmd_paste_copy_undo (GnmCommand *cmd, WorkbookControl *wbc)
3065 {
3066 return cmd_paste_copy_impl (cmd, wbc, TRUE);
3067 }
3068
3069 static gboolean
cmd_paste_copy_redo(GnmCommand * cmd,WorkbookControl * wbc)3070 cmd_paste_copy_redo (GnmCommand *cmd, WorkbookControl *wbc)
3071 {
3072 return cmd_paste_copy_impl (cmd, wbc, FALSE);
3073 }
3074
3075 static void
cmd_paste_copy_finalize(GObject * cmd)3076 cmd_paste_copy_finalize (GObject *cmd)
3077 {
3078 CmdPasteCopy *me = CMD_PASTE_COPY (cmd);
3079
3080 if (me->contents) {
3081 cellregion_unref (me->contents);
3082 me->contents = NULL;
3083 }
3084 g_slist_free_full (me->pasted_objects, (GDestroyNotify)g_object_unref);
3085 g_slist_free_full (me->orig_contents_objects, (GDestroyNotify)g_object_unref);
3086 gnm_command_finalize (cmd);
3087 }
3088
3089 /*
3090 * cmd_paste_copy:
3091 * @wbc:
3092 * @pt:
3093 * @cr: (transfer none):
3094 *
3095 * Returns: %TRUE if there was a problem, %FALSE otherwise.
3096 */
3097 gboolean
cmd_paste_copy(WorkbookControl * wbc,GnmPasteTarget const * pt,GnmCellRegion * cr)3098 cmd_paste_copy (WorkbookControl *wbc,
3099 GnmPasteTarget const *pt, GnmCellRegion *cr)
3100 {
3101 CmdPasteCopy *me;
3102 int n_r = 1, n_c = 1;
3103 char *range_name;
3104 GnmRange const *merge_src;
3105
3106 g_return_val_if_fail (pt != NULL, TRUE);
3107 g_return_val_if_fail (IS_SHEET (pt->sheet), TRUE);
3108 g_return_val_if_fail (cr != NULL, TRUE);
3109
3110 cellregion_ref (cr);
3111
3112 me = g_object_new (CMD_PASTE_COPY_TYPE, NULL);
3113
3114 me->cmd.sheet = pt->sheet;
3115 me->cmd.size = 1; /* FIXME? */
3116
3117 range_name = undo_range_name (pt->sheet, &pt->range);
3118 me->cmd.cmd_descriptor = g_strdup_printf (_("Pasting into %s"),
3119 range_name);
3120 g_free (range_name);
3121
3122 me->dst = *pt;
3123 me->contents = cr;
3124 me->has_been_through_cycle = FALSE;
3125 me->only_objects = (cr->cols < 1 || cr->rows < 1);
3126 me->pasted_objects = NULL;
3127 me->orig_contents_objects =
3128 g_slist_copy_deep (cr->objects,
3129 (GCopyFunc)sheet_object_dup, NULL);
3130 me->single_merge_to_single_merge = FALSE;
3131
3132 /* If the input is only objects ignore all this range stuff */
3133 if (!me->only_objects) {
3134 /* see if we need to do any tiling */
3135 GnmRange *r = &me->dst.range;
3136 if (g_slist_length (cr->merged) == 1 &&
3137 (NULL != (merge_src = cr->merged->data)) &&
3138 range_height (merge_src) == cr->rows &&
3139 range_width (merge_src) == cr->cols) {
3140 /* We are copying from a single merge */
3141 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3142 if (merge != NULL && range_equal (r, merge)) {
3143 /* To a single merge */
3144 me->single_merge_to_single_merge = TRUE;
3145 n_c = n_r = 1;
3146 me->dst.paste_flags |= PASTE_DONT_MERGE;
3147 goto copy_ready;
3148 }
3149 }
3150
3151 if (pt->paste_flags & PASTE_TRANSPOSE) {
3152 n_c = range_width (r) / cr->rows;
3153 if (n_c < 1) n_c = 1;
3154 r->end.col = r->start.col + n_c * cr->rows - 1;
3155
3156 n_r = range_height (r) / cr->cols;
3157 if (n_r < 1) n_r = 1;
3158 r->end.row = r->start.row + n_r * cr->cols - 1;
3159 } else {
3160 /* Before looking for tiling if we are not transposing,
3161 * allow pasting a full col or row from a single cell */
3162 n_c = range_width (r);
3163 if (n_c == 1 && cr->cols == gnm_sheet_get_max_cols (me->cmd.sheet)) {
3164 r->start.col = 0;
3165 r->end.col = gnm_sheet_get_last_col (me->cmd.sheet);
3166 } else {
3167 n_c /= cr->cols;
3168 if (n_c < 1) n_c = 1;
3169 r->end.col = r->start.col + n_c * cr->cols - 1;
3170 }
3171
3172 n_r = range_height (r);
3173 if (n_r == 1 && cr->rows == gnm_sheet_get_max_rows (me->cmd.sheet)) {
3174 r->start.row = 0;
3175 r->end.row = gnm_sheet_get_last_row (me->cmd.sheet);
3176 } else {
3177 n_r /= cr->rows;
3178 if (n_r < 1) n_r = 1;
3179 r->end.row = r->start.row + n_r * cr->rows - 1;
3180 }
3181 }
3182
3183 if (cr->cols != 1 || cr->rows != 1) {
3184 /* Note: when the source is a single cell, a single target merge is special */
3185 /* see clipboard.c (clipboard_paste_region) */
3186 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
3187 if (merge != NULL && range_equal (r, merge)) {
3188 /* destination is a single merge */
3189 /* enlarge it such that the source fits */
3190 if (pt->paste_flags & PASTE_TRANSPOSE) {
3191 if ((r->end.col - r->start.col + 1) < cr->rows)
3192 r->end.col = r->start.col + cr->rows - 1;
3193 if ((r->end.row - r->start.row + 1) < cr->cols)
3194 r->end.row = r->start.row + cr->cols - 1;
3195 } else {
3196 if ((r->end.col - r->start.col + 1) < cr->cols)
3197 r->end.col = r->start.col + cr->cols - 1;
3198 if ((r->end.row - r->start.row + 1) < cr->rows)
3199 r->end.row = r->start.row + cr->rows - 1;
3200 }
3201 }
3202 }
3203 }
3204
3205 if (n_c * (gnm_float)n_r > 10000.) {
3206 char *number = g_strdup_printf ("%0.0" GNM_FORMAT_f,
3207 (gnm_float)n_c * (gnm_float)n_r);
3208 gboolean result = go_gtk_query_yes_no (wbcg_toplevel (WBC_GTK (wbc)), FALSE,
3209 _("Do you really want to paste "
3210 "%s copies?"), number);
3211 g_free (number);
3212 if (!result) {
3213 g_object_unref (me);
3214 return TRUE;
3215 }
3216 }
3217
3218 copy_ready:
3219 /* Use translate to do a quiet sanity check */
3220 if (range_translate (&me->dst.range, pt->sheet, 0, 0)) {
3221 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
3222 me->cmd.cmd_descriptor,
3223 _("is beyond sheet boundaries"));
3224 g_object_unref (me);
3225 return TRUE;
3226 }
3227
3228 /* no need to test if all we have are objects or are copying from */
3229 /*a single merge to a single merge*/
3230 if ((!me->only_objects) && (!me->single_merge_to_single_merge)&&
3231 sheet_range_splits_region (pt->sheet, &me->dst.range,
3232 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
3233 g_object_unref (me);
3234 return TRUE;
3235 }
3236
3237 warn_if_date_trouble (wbc, cr);
3238
3239 return gnm_command_push_undo (wbc, G_OBJECT (me));
3240 }
3241
3242 /******************************************************************/
3243
3244 #define CMD_AUTOFILL_TYPE (cmd_autofill_get_type ())
3245 #define CMD_AUTOFILL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFILL_TYPE, CmdAutofill))
3246
3247 typedef struct {
3248 GnmCommand cmd;
3249
3250 GnmCellRegion *contents;
3251 GnmPasteTarget dst;
3252 GnmRange src;
3253 int base_col, base_row, w, h, end_col, end_row;
3254 gboolean default_increment;
3255 gboolean inverse_autofill;
3256 ColRowIndexList *columns;
3257 ColRowStateGroup *old_widths;
3258 } CmdAutofill;
3259
3260 static void
cmd_autofill_repeat(GnmCommand const * cmd,WorkbookControl * wbc)3261 cmd_autofill_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3262 {
3263 CmdAutofill const *orig = (CmdAutofill const *) cmd;
3264 SheetView *sv = wb_control_cur_sheet_view (wbc);
3265 GnmRange const *r = selection_first_range (sv,
3266 GO_CMD_CONTEXT (wbc), _("Autofill"));
3267
3268 if (r == NULL)
3269 return;
3270
3271 cmd_autofill (wbc, sv_sheet (sv), orig->default_increment,
3272 r->start.col, r->start.row, range_width (r), range_height (r),
3273 r->start.col + (orig->end_col - orig->base_col),
3274 r->start.row + (orig->end_row - orig->base_row),
3275 orig->inverse_autofill);
3276 }
MAKE_GNM_COMMAND(CmdAutofill,cmd_autofill,cmd_autofill_repeat)3277 MAKE_GNM_COMMAND (CmdAutofill, cmd_autofill, cmd_autofill_repeat)
3278
3279 static gboolean
3280 cmd_autofill_undo (GnmCommand *cmd, WorkbookControl *wbc)
3281 {
3282 CmdAutofill *me = CMD_AUTOFILL (cmd);
3283 gboolean res;
3284
3285 g_return_val_if_fail (wbc != NULL, TRUE);
3286 g_return_val_if_fail (me != NULL, TRUE);
3287 g_return_val_if_fail (me->contents != NULL, TRUE);
3288
3289 res = clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc));
3290 cellregion_unref (me->contents);
3291 me->contents = NULL;
3292
3293 if (me->old_widths) {
3294 colrow_restore_state_group (me->cmd.sheet, TRUE,
3295 me->columns,
3296 me->old_widths);
3297 colrow_state_group_destroy (me->old_widths);
3298 me->old_widths = NULL;
3299 colrow_index_list_destroy (me->columns);
3300 me->columns = NULL;
3301 }
3302
3303 if (res)
3304 return TRUE;
3305
3306 select_range (me->dst.sheet, &me->src, wbc);
3307
3308 return FALSE;
3309 }
3310
3311 static gboolean
cmd_autofill_redo(GnmCommand * cmd,WorkbookControl * wbc)3312 cmd_autofill_redo (GnmCommand *cmd, WorkbookControl *wbc)
3313 {
3314 CmdAutofill *me = CMD_AUTOFILL (cmd);
3315 GnmRange r;
3316
3317 g_return_val_if_fail (me != NULL, TRUE);
3318 g_return_val_if_fail (me->contents == NULL, TRUE);
3319
3320 me->contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
3321
3322 g_return_val_if_fail (me->contents != NULL, TRUE);
3323
3324 /* FIXME : when we split autofill to support hints and better validation
3325 * move this in there.
3326 */
3327 /* MW: May 2006: we support hints now. What's this about? */
3328 sheet_clear_region (me->dst.sheet,
3329 me->dst.range.start.col, me->dst.range.start.row,
3330 me->dst.range.end.col, me->dst.range.end.row,
3331 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3332 GO_CMD_CONTEXT (wbc));
3333
3334 if (me->cmd.size == 1)
3335 me->cmd.size += cellregion_cmd_size (me->contents);
3336 if (me->inverse_autofill)
3337 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3338 me->end_col, me->end_row, me->w, me->h,
3339 me->base_col, me->base_row);
3340 else
3341 gnm_autofill_fill (me->dst.sheet, me->default_increment,
3342 me->base_col, me->base_row, me->w, me->h,
3343 me->end_col, me->end_row);
3344
3345 colrow_autofit (me->cmd.sheet, &me->dst.range, TRUE, TRUE,
3346 TRUE, FALSE,
3347 &me->columns, &me->old_widths);
3348
3349 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3350 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3351 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3352
3353 r = range_union (&me->dst.range, &me->src);
3354 select_range (me->dst.sheet, &r, wbc);
3355
3356 return FALSE;
3357 }
3358
3359 static void
cmd_autofill_finalize(GObject * cmd)3360 cmd_autofill_finalize (GObject *cmd)
3361 {
3362 CmdAutofill *me = CMD_AUTOFILL (cmd);
3363
3364 if (me->contents) {
3365 cellregion_unref (me->contents);
3366 me->contents = NULL;
3367 }
3368 colrow_index_list_destroy (me->columns);
3369 colrow_state_group_destroy (me->old_widths);
3370 gnm_command_finalize (cmd);
3371 }
3372
3373 gboolean
cmd_autofill(WorkbookControl * wbc,Sheet * sheet,gboolean default_increment,int base_col,int base_row,int w,int h,int end_col,int end_row,gboolean inverse_autofill)3374 cmd_autofill (WorkbookControl *wbc, Sheet *sheet,
3375 gboolean default_increment,
3376 int base_col, int base_row,
3377 int w, int h, int end_col, int end_row,
3378 gboolean inverse_autofill)
3379 {
3380 CmdAutofill *me;
3381 GnmRange target, src;
3382
3383 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3384
3385 /* This would be meaningless */
3386 if (base_col+w-1 == end_col && base_row+h-1 == end_row)
3387 return FALSE;
3388
3389 if (inverse_autofill) {
3390 if (end_col != base_col + w - 1) {
3391 range_init (&target, base_col, base_row,
3392 end_col - w, end_row);
3393 range_init (&src, end_col - w + 1, base_row,
3394 end_col, end_row);
3395 } else {
3396 range_init (&target, base_col, base_row,
3397 end_col, end_row - h);
3398 range_init (&src, base_col, end_row - h + 1,
3399 end_col, end_row);
3400 }
3401 } else {
3402 if (end_col != base_col + w - 1) {
3403 range_init (&target, base_col + w, base_row,
3404 end_col, end_row);
3405 range_init (&src, base_col, base_row,
3406 base_col + w - 1, end_row);
3407 } else {
3408 range_init (&target, base_col, base_row + h,
3409 end_col, end_row);
3410 range_init (&src, base_col, base_row,
3411 end_col, base_row + h - 1);
3412 }
3413 }
3414
3415 /* We don't support clearing regions, when a user uses the autofill
3416 * cursor to 'shrink' a selection
3417 */
3418 if (target.start.col > target.end.col || target.start.row > target.end.row)
3419 return TRUE;
3420
3421 /* Check arrays or merged regions in src or target regions */
3422 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")) ||
3423 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), _("Autofill")))
3424 return TRUE;
3425
3426 me = g_object_new (CMD_AUTOFILL_TYPE, NULL);
3427
3428 me->contents = NULL;
3429 me->dst.sheet = sheet;
3430 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3431 me->dst.range = target;
3432 me->src = src;
3433
3434 me->base_col = base_col;
3435 me->base_row = base_row,
3436 me->w = w;
3437 me->h = h;
3438 me->end_col = end_col;
3439 me->end_row = end_row;
3440 me->default_increment = default_increment;
3441 me->inverse_autofill = inverse_autofill;
3442
3443 me->cmd.sheet = sheet;
3444 me->cmd.size = 1; /* Changed in initial redo. */
3445 me->cmd.cmd_descriptor = g_strdup_printf (_("Autofilling %s"),
3446 range_as_string (&me->dst.range));
3447
3448 return gnm_command_push_undo (wbc, G_OBJECT (me));
3449 }
3450
3451 /******************************************************************/
3452
3453 #define CMD_COPYREL_TYPE (cmd_copyrel_get_type ())
3454 #define CMD_COPYREL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COPYREL_TYPE, CmdCopyRel))
3455
3456 typedef struct {
3457 GnmCommand cmd;
3458
3459 GOUndo *undo;
3460 GnmPasteTarget dst, src;
3461 int dx, dy;
3462 char const *name;
3463 } CmdCopyRel;
3464
3465 static void
cmd_copyrel_repeat(GnmCommand const * cmd,WorkbookControl * wbc)3466 cmd_copyrel_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3467 {
3468 CmdCopyRel const *orig = (CmdCopyRel const *) cmd;
3469 cmd_copyrel (wbc, orig->dx, orig->dy, orig->name);
3470 }
MAKE_GNM_COMMAND(CmdCopyRel,cmd_copyrel,cmd_copyrel_repeat)3471 MAKE_GNM_COMMAND (CmdCopyRel, cmd_copyrel, cmd_copyrel_repeat)
3472
3473 static gboolean
3474 cmd_copyrel_undo (GnmCommand *cmd, WorkbookControl *wbc)
3475 {
3476 CmdCopyRel *me = CMD_COPYREL (cmd);
3477
3478 g_return_val_if_fail (wbc != NULL, TRUE);
3479 g_return_val_if_fail (me != NULL, TRUE);
3480 g_return_val_if_fail (me->undo != NULL, TRUE);
3481
3482 go_undo_undo (me->undo);
3483
3484 /* Select the newly pasted contents (this queues a redraw) */
3485 select_range (me->dst.sheet, &me->dst.range, wbc);
3486
3487 return FALSE;
3488 }
3489
3490 static gboolean
cmd_copyrel_redo(GnmCommand * cmd,WorkbookControl * wbc)3491 cmd_copyrel_redo (GnmCommand *cmd, WorkbookControl *wbc)
3492 {
3493 CmdCopyRel *me = CMD_COPYREL (cmd);
3494 GnmCellRegion *contents;
3495 gboolean res;
3496
3497 g_return_val_if_fail (me != NULL, TRUE);
3498
3499 sheet_clear_region (me->dst.sheet,
3500 me->dst.range.start.col, me->dst.range.start.row,
3501 me->dst.range.end.col, me->dst.range.end.row,
3502 CLEAR_VALUES | CLEAR_MERGES | CLEAR_NOCHECKARRAY | CLEAR_RECALC_DEPS,
3503 GO_CMD_CONTEXT (wbc));
3504
3505 contents = clipboard_copy_range (me->src.sheet, &me->src.range);
3506 res = clipboard_paste_region (contents, &me->dst, GO_CMD_CONTEXT (wbc));
3507 cellregion_unref (contents);
3508 if (res)
3509 return TRUE;
3510
3511 sheet_region_queue_recalc (me->dst.sheet, &me->dst.range);
3512 sheet_range_calc_spans (me->dst.sheet, &me->dst.range, GNM_SPANCALC_RENDER);
3513 sheet_flag_status_update_range (me->dst.sheet, &me->dst.range);
3514
3515 /* Select the newly pasted contents (this queues a redraw) */
3516 select_range (me->dst.sheet, &me->dst.range, wbc);
3517
3518 return FALSE;
3519 }
3520
3521 static void
cmd_copyrel_finalize(GObject * cmd)3522 cmd_copyrel_finalize (GObject *cmd)
3523 {
3524 CmdCopyRel *me = CMD_COPYREL (cmd);
3525
3526 if (me->undo)
3527 g_object_unref (me->undo);
3528
3529 gnm_command_finalize (cmd);
3530 }
3531
3532 gboolean
cmd_copyrel(WorkbookControl * wbc,int dx,int dy,char const * name)3533 cmd_copyrel (WorkbookControl *wbc,
3534 int dx, int dy,
3535 char const *name)
3536 {
3537 CmdCopyRel *me;
3538 GnmRange target, src;
3539 SheetView *sv = wb_control_cur_sheet_view (wbc);
3540 Sheet *sheet = sv->sheet;
3541 GnmRange const *selr =
3542 selection_first_range (sv, GO_CMD_CONTEXT (wbc), name);
3543
3544 g_return_val_if_fail (dx == 0 || dy == 0, TRUE);
3545
3546 if (!selr)
3547 return FALSE;
3548
3549 target = *selr;
3550 range_normalize (&target);
3551 src.start = src.end = target.start;
3552
3553 if (dy) {
3554 src.end.col = target.end.col;
3555 if (target.start.row != target.end.row)
3556 target.start.row++;
3557 else
3558 src.start.row = src.end.row = (target.start.row + dy);
3559 }
3560
3561 if (dx) {
3562 src.end.row = target.end.row;
3563 if (target.start.col != target.end.col)
3564 target.start.col++;
3565 else
3566 src.start.col = src.end.col = (target.start.col + dx);
3567 }
3568
3569 if (src.start.col < 0 || src.start.col >= gnm_sheet_get_max_cols (sheet) ||
3570 src.start.row < 0 || src.start.row >= gnm_sheet_get_max_rows (sheet))
3571 return FALSE;
3572
3573 /* Check arrays or merged regions in src or target regions */
3574 if (sheet_range_splits_region (sheet, &target, NULL, GO_CMD_CONTEXT (wbc), name) ||
3575 sheet_range_splits_region (sheet, &src, NULL, GO_CMD_CONTEXT (wbc), name))
3576 return TRUE;
3577
3578 me = g_object_new (CMD_COPYREL_TYPE, NULL);
3579
3580 me->dst.sheet = sheet;
3581 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3582 me->dst.range = target;
3583 me->src.sheet = sheet;
3584 me->src.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
3585 me->src.range = src;
3586 me->dx = dx;
3587 me->dy = dy;
3588 me->name = name;
3589 me->undo = clipboard_copy_range_undo (me->dst.sheet, &me->dst.range);
3590
3591 me->cmd.sheet = sheet;
3592 me->cmd.size = 1;
3593 me->cmd.cmd_descriptor = g_strdup (name);
3594
3595 return gnm_command_push_undo (wbc, G_OBJECT (me));
3596 }
3597
3598 /******************************************************************/
3599
3600
3601 #define CMD_AUTOFORMAT_TYPE (cmd_autoformat_get_type ())
3602 #define CMD_AUTOFORMAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_AUTOFORMAT_TYPE, CmdAutoFormat))
3603
3604 typedef struct {
3605 GnmCellPos pos;
3606 GnmStyleList *styles;
3607 } CmdAutoFormatOldStyle;
3608
3609 typedef struct {
3610 GnmCommand cmd;
3611
3612 GSList *selection; /* Selections on the sheet */
3613 GSList *old_styles; /* Older styles, one style_list per selection range*/
3614
3615 GnmFT *ft; /* Template that has been applied */
3616 } CmdAutoFormat;
3617
3618 static void
cmd_autoformat_repeat(GnmCommand const * cmd,WorkbookControl * wbc)3619 cmd_autoformat_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3620 {
3621 CmdAutoFormat const *orig = (CmdAutoFormat const *) cmd;
3622 cmd_selection_autoformat (wbc, gnm_ft_clone (orig->ft));
3623 }
MAKE_GNM_COMMAND(CmdAutoFormat,cmd_autoformat,cmd_autoformat_repeat)3624 MAKE_GNM_COMMAND (CmdAutoFormat, cmd_autoformat, cmd_autoformat_repeat)
3625
3626 static gboolean
3627 cmd_autoformat_undo (GnmCommand *cmd,
3628 G_GNUC_UNUSED WorkbookControl *wbc)
3629 {
3630 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3631
3632 g_return_val_if_fail (me != NULL, TRUE);
3633
3634 if (me->old_styles) {
3635 GSList *l1 = me->old_styles;
3636 GSList *l2 = me->selection;
3637
3638 for (; l1; l1 = l1->next, l2 = l2->next) {
3639 GnmRange *r;
3640 CmdAutoFormatOldStyle *os = l1->data;
3641 GnmSpanCalcFlags flags = sheet_style_set_list (me->cmd.sheet,
3642 &os->pos, os->styles, NULL, NULL);
3643
3644 g_return_val_if_fail (l2 && l2->data, TRUE);
3645
3646 r = l2->data;
3647 sheet_range_calc_spans (me->cmd.sheet, r, flags);
3648 if (flags != GNM_SPANCALC_SIMPLE)
3649 rows_height_update (me->cmd.sheet, r, TRUE);
3650 }
3651 }
3652
3653 return FALSE;
3654 }
3655
3656 static gboolean
cmd_autoformat_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)3657 cmd_autoformat_redo (GnmCommand *cmd,
3658 G_GNUC_UNUSED WorkbookControl *wbc)
3659 {
3660 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3661
3662 g_return_val_if_fail (me != NULL, TRUE);
3663
3664 gnm_ft_apply_to_sheet_regions (me->ft,
3665 me->cmd.sheet, me->selection);
3666
3667 return FALSE;
3668 }
3669
3670 static void
cmd_autoformat_finalize(GObject * cmd)3671 cmd_autoformat_finalize (GObject *cmd)
3672 {
3673 CmdAutoFormat *me = CMD_AUTOFORMAT (cmd);
3674
3675 if (me->old_styles != NULL) {
3676 GSList *l;
3677
3678 for (l = me->old_styles ; l != NULL ; l = g_slist_remove (l, l->data)) {
3679 CmdAutoFormatOldStyle *os = l->data;
3680
3681 if (os->styles)
3682 style_list_free (os->styles);
3683
3684 g_free (os);
3685 }
3686
3687 me->old_styles = NULL;
3688 }
3689
3690 range_fragment_free (me->selection);
3691 me->selection = NULL;
3692
3693 gnm_ft_free (me->ft);
3694
3695 gnm_command_finalize (cmd);
3696 }
3697
3698 /**
3699 * cmd_selection_autoformat:
3700 * @wbc: the context.
3701 * @ft: The format template that was applied
3702 *
3703 * Returns: %TRUE if there was a problem, %FALSE otherwise.
3704 **/
3705 gboolean
cmd_selection_autoformat(WorkbookControl * wbc,GnmFT * ft)3706 cmd_selection_autoformat (WorkbookControl *wbc, GnmFT *ft)
3707 {
3708 CmdAutoFormat *me;
3709 char *names;
3710 GSList *l;
3711 SheetView *sv = wb_control_cur_sheet_view (wbc);
3712
3713 me = g_object_new (CMD_AUTOFORMAT_TYPE, NULL);
3714
3715 me->selection = selection_get_ranges (sv, FALSE); /* Regions may overlap */
3716 me->ft = ft;
3717 me->cmd.sheet = sv_sheet (sv);
3718 me->cmd.size = 1; /* FIXME? */
3719
3720 if (!gnm_ft_check_valid (ft, me->selection, GO_CMD_CONTEXT (wbc))) {
3721 g_object_unref (me);
3722 return TRUE;
3723 }
3724
3725 me->old_styles = NULL;
3726 for (l = me->selection; l; l = l->next) {
3727 CmdFormatOldStyle *os;
3728 GnmRange range = *((GnmRange const *) l->data);
3729
3730 /* Store the containing range to handle borders */
3731 if (range.start.col > 0) range.start.col--;
3732 if (range.start.row > 0) range.start.row--;
3733 if (range.end.col < gnm_sheet_get_last_col (sv->sheet)) range.end.col++;
3734 if (range.end.row < gnm_sheet_get_last_row (sv->sheet)) range.end.row++;
3735
3736 os = g_new (CmdFormatOldStyle, 1);
3737
3738 os->styles = sheet_style_get_range (me->cmd.sheet, &range);
3739 os->pos = range.start;
3740
3741 me->old_styles = g_slist_append (me->old_styles, os);
3742 }
3743
3744 names = undo_range_list_name (me->cmd.sheet, me->selection);
3745 me->cmd.cmd_descriptor = g_strdup_printf (_("Autoformatting %s"),
3746 names);
3747 g_free (names);
3748
3749 return gnm_command_push_undo (wbc, G_OBJECT (me));
3750 }
3751
3752 /******************************************************************/
3753
3754 #define CMD_UNMERGE_CELLS_TYPE (cmd_unmerge_cells_get_type ())
3755 #define CMD_UNMERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_UNMERGE_CELLS_TYPE, CmdUnmergeCells))
3756
3757 typedef struct {
3758 GnmCommand cmd;
3759
3760 Sheet *sheet;
3761 GArray *unmerged_regions;
3762 GArray *ranges;
3763 } CmdUnmergeCells;
3764
3765 static void
cmd_unmerge_cells_repeat(G_GNUC_UNUSED GnmCommand const * cmd,WorkbookControl * wbc)3766 cmd_unmerge_cells_repeat (G_GNUC_UNUSED GnmCommand const *cmd, WorkbookControl *wbc)
3767 {
3768 SheetView *sv = wb_control_cur_sheet_view (wbc);
3769 GSList *range_list = selection_get_ranges (sv, FALSE);
3770 cmd_unmerge_cells (wbc, sv_sheet (sv), range_list);
3771 range_fragment_free (range_list);
3772 }
MAKE_GNM_COMMAND(CmdUnmergeCells,cmd_unmerge_cells,cmd_unmerge_cells_repeat)3773 MAKE_GNM_COMMAND (CmdUnmergeCells, cmd_unmerge_cells, cmd_unmerge_cells_repeat)
3774
3775 static gboolean
3776 cmd_unmerge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3777 {
3778 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3779 unsigned i;
3780
3781 g_return_val_if_fail (me != NULL, TRUE);
3782 g_return_val_if_fail (me->unmerged_regions != NULL, TRUE);
3783
3784 for (i = 0 ; i < me->unmerged_regions->len ; ++i) {
3785 GnmRange const *tmp = &(g_array_index (me->unmerged_regions, GnmRange, i));
3786 sheet_redraw_range (me->cmd.sheet, tmp);
3787 gnm_sheet_merge_add (me->cmd.sheet, tmp, TRUE, GO_CMD_CONTEXT (wbc));
3788 sheet_range_calc_spans (me->cmd.sheet, tmp, GNM_SPANCALC_RE_RENDER);
3789 }
3790
3791 g_array_free (me->unmerged_regions, TRUE);
3792 me->unmerged_regions = NULL;
3793
3794 return FALSE;
3795 }
3796
3797 static gboolean
cmd_unmerge_cells_redo(GnmCommand * cmd,WorkbookControl * wbc)3798 cmd_unmerge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3799 {
3800 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3801 unsigned i;
3802
3803 g_return_val_if_fail (me != NULL, TRUE);
3804 g_return_val_if_fail (me->unmerged_regions == NULL, TRUE);
3805
3806 me->unmerged_regions = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3807 for (i = 0 ; i < me->ranges->len ; ++i) {
3808 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (me->cmd.sheet,
3809 &(g_array_index (me->ranges, GnmRange, i)));
3810 for (ptr = merged ; ptr != NULL ; ptr = ptr->next) {
3811 GnmRange const *pr = ptr->data;
3812 GnmRange const tmp = *pr;
3813 g_array_append_val (me->unmerged_regions, tmp);
3814 gnm_sheet_merge_remove (me->cmd.sheet, &tmp);
3815 sheet_range_calc_spans (me->cmd.sheet, &tmp,
3816 GNM_SPANCALC_RE_RENDER);
3817 }
3818 g_slist_free (merged);
3819 }
3820
3821 return FALSE;
3822 }
3823
3824 static void
cmd_unmerge_cells_finalize(GObject * cmd)3825 cmd_unmerge_cells_finalize (GObject *cmd)
3826 {
3827 CmdUnmergeCells *me = CMD_UNMERGE_CELLS (cmd);
3828
3829 if (me->unmerged_regions != NULL) {
3830 g_array_free (me->unmerged_regions, TRUE);
3831 me->unmerged_regions = NULL;
3832 }
3833 if (me->ranges != NULL) {
3834 g_array_free (me->ranges, TRUE);
3835 me->ranges = NULL;
3836 }
3837
3838 gnm_command_finalize (cmd);
3839 }
3840
3841 /**
3842 * cmd_unmerge_cells:
3843 * @wbc: the context.
3844 * @sheet: #Sheet
3845 * @selection: (element-type GnmRange): selection.
3846 *
3847 * Returns: %TRUE if there was a problem, %FALSE otherwise.
3848 **/
3849 gboolean
cmd_unmerge_cells(WorkbookControl * wbc,Sheet * sheet,GSList const * selection)3850 cmd_unmerge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection)
3851 {
3852 CmdUnmergeCells *me;
3853 char *names;
3854
3855 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
3856
3857 me = g_object_new (CMD_UNMERGE_CELLS_TYPE, NULL);
3858
3859 me->cmd.sheet = sheet;
3860 me->cmd.size = 1;
3861
3862 names = undo_range_list_name (sheet, selection);
3863 me->cmd.cmd_descriptor = g_strdup_printf (_("Unmerging %s"), names);
3864 g_free (names);
3865
3866 me->unmerged_regions = NULL;
3867 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
3868 for ( ; selection != NULL ; selection = selection->next) {
3869 GSList *merged = gnm_sheet_merge_get_overlap (sheet, selection->data);
3870 if (merged != NULL) {
3871 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
3872 g_slist_free (merged);
3873 }
3874 }
3875
3876 if (me->ranges->len <= 0) {
3877 g_object_unref (me);
3878 return TRUE;
3879 }
3880
3881 return gnm_command_push_undo (wbc, G_OBJECT (me));
3882 }
3883
3884 /******************************************************************/
3885
3886 #define CMD_MERGE_CELLS_TYPE (cmd_merge_cells_get_type ())
3887 #define CMD_MERGE_CELLS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_CELLS_TYPE, CmdMergeCells))
3888
3889 typedef struct {
3890 GnmCommand cmd;
3891 GArray *ranges;
3892 GSList *old_contents;
3893 gboolean center;
3894 } CmdMergeCells;
3895
3896 static void
3897 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc);
3898
MAKE_GNM_COMMAND(CmdMergeCells,cmd_merge_cells,cmd_merge_cells_repeat)3899 MAKE_GNM_COMMAND (CmdMergeCells, cmd_merge_cells, cmd_merge_cells_repeat)
3900
3901 static void
3902 cmd_merge_cells_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
3903 {
3904 SheetView *sv = wb_control_cur_sheet_view (wbc);
3905 GSList *range_list = selection_get_ranges (sv, FALSE);
3906 cmd_merge_cells (wbc, sv_sheet (sv), range_list,
3907 CMD_MERGE_CELLS (cmd)->center);
3908 range_fragment_free (range_list);
3909 }
3910
3911 static gboolean
cmd_merge_cells_undo(GnmCommand * cmd,WorkbookControl * wbc)3912 cmd_merge_cells_undo (GnmCommand *cmd, WorkbookControl *wbc)
3913 {
3914 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3915 unsigned i, flags;
3916
3917 g_return_val_if_fail (me != NULL, TRUE);
3918
3919 for (i = 0 ; i < me->ranges->len ; ++i) {
3920 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3921 gnm_sheet_merge_remove (me->cmd.sheet, r);
3922 }
3923
3924 /* Avoid pasting comments that are at 0,0. Redo copies the target
3925 * region (including all comments) . If there was a comment in the top
3926 * left we would end up duplicating it. */
3927 flags = PASTE_CONTENTS | PASTE_FORMATS | PASTE_COMMENTS |
3928 PASTE_IGNORE_COMMENTS_AT_ORIGIN;
3929 if (me->center)
3930 flags |= PASTE_FORMATS;
3931 for (i = 0 ; i < me->ranges->len ; ++i) {
3932 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3933 GnmPasteTarget pt;
3934 GnmCellRegion * c;
3935
3936 g_return_val_if_fail (me->old_contents != NULL, TRUE);
3937
3938 c = me->old_contents->data;
3939 clipboard_paste_region (c,
3940 paste_target_init (&pt, me->cmd.sheet, r, flags),
3941 GO_CMD_CONTEXT (wbc));
3942 cellregion_unref (c);
3943 me->old_contents = g_slist_remove (me->old_contents, c);
3944 }
3945 g_return_val_if_fail (me->old_contents == NULL, TRUE);
3946
3947 return FALSE;
3948 }
3949
3950 static gboolean
cmd_merge_cells_redo(GnmCommand * cmd,WorkbookControl * wbc)3951 cmd_merge_cells_redo (GnmCommand *cmd, WorkbookControl *wbc)
3952 {
3953 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3954 GnmStyle *align_center = NULL;
3955 Sheet *sheet;
3956 unsigned i;
3957
3958 g_return_val_if_fail (me != NULL, TRUE);
3959
3960 if (me->center) {
3961 align_center = gnm_style_new ();
3962 gnm_style_set_align_h (align_center, GNM_HALIGN_CENTER);
3963 }
3964 sheet = me->cmd.sheet;
3965 for (i = 0 ; i < me->ranges->len ; ++i) {
3966 GnmRange const *r = &(g_array_index (me->ranges, GnmRange, i));
3967 GSList *ptr, *merged = gnm_sheet_merge_get_overlap (sheet, r);
3968
3969 /* save contents before removing contained merged regions */
3970 me->old_contents = g_slist_prepend (me->old_contents,
3971 clipboard_copy_range (sheet, r));
3972 for (ptr = merged ; ptr != NULL ; ptr = ptr->next)
3973 gnm_sheet_merge_remove (sheet, ptr->data);
3974 g_slist_free (merged);
3975
3976 gnm_sheet_merge_add (sheet, r, TRUE, GO_CMD_CONTEXT (wbc));
3977 if (me->center)
3978 sheet_apply_style (me->cmd.sheet, r, align_center);
3979 }
3980
3981 if (me->center)
3982 gnm_style_unref (align_center);
3983 me->old_contents = g_slist_reverse (me->old_contents);
3984 return FALSE;
3985 }
3986
3987 static void
cmd_merge_cells_finalize(GObject * cmd)3988 cmd_merge_cells_finalize (GObject *cmd)
3989 {
3990 CmdMergeCells *me = CMD_MERGE_CELLS (cmd);
3991
3992 if (me->old_contents != NULL) {
3993 GSList *l;
3994 for (l = me->old_contents ; l != NULL ; l = g_slist_remove (l, l->data))
3995 cellregion_unref (l->data);
3996 me->old_contents = NULL;
3997 }
3998
3999 if (me->ranges != NULL) {
4000 g_array_free (me->ranges, TRUE);
4001 me->ranges = NULL;
4002 }
4003
4004 gnm_command_finalize (cmd);
4005 }
4006
4007 /**
4008 * cmd_merge_cells:
4009 * @wbc: the context.
4010 * @sheet: #Sheet
4011 * @selection: (element-type GnmRange): selection.
4012 * @center:
4013 *
4014 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4015 **/
4016 gboolean
cmd_merge_cells(WorkbookControl * wbc,Sheet * sheet,GSList const * selection,gboolean center)4017 cmd_merge_cells (WorkbookControl *wbc, Sheet *sheet, GSList const *selection,
4018 gboolean center)
4019 {
4020 CmdMergeCells *me;
4021 char *names;
4022
4023 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4024
4025 me = g_object_new (CMD_MERGE_CELLS_TYPE, NULL);
4026
4027 me->cmd.sheet = sheet;
4028 me->cmd.size = 1;
4029
4030 names = undo_range_list_name (sheet, selection);
4031 me->cmd.cmd_descriptor =
4032 g_strdup_printf ((center ? _("Merge and Center %s") :_("Merging %s")), names);
4033 g_free (names);
4034
4035 me->center = center;
4036 me->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
4037 for ( ; selection != NULL ; selection = selection->next) {
4038 GnmRange const *exist;
4039 GnmRange const *r = selection->data;
4040 if (range_is_singleton (selection->data))
4041 continue;
4042 if (NULL != (exist = gnm_sheet_merge_is_corner (sheet, &r->start)) &&
4043 range_equal (r, exist))
4044 continue;
4045 g_array_append_val (me->ranges, *(GnmRange *)selection->data);
4046 }
4047
4048 if (me->ranges->len <= 0) {
4049 g_object_unref (me);
4050 return TRUE;
4051 }
4052
4053 return gnm_command_push_undo (wbc, G_OBJECT (me));
4054 }
4055
4056 /******************************************************************/
4057
4058 #define CMD_SEARCH_REPLACE_TYPE (cmd_search_replace_get_type())
4059 #define CMD_SEARCH_REPLACE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SEARCH_REPLACE_TYPE, CmdSearchReplace))
4060
4061 typedef struct {
4062 GnmCommand cmd;
4063 GnmSearchReplace *sr;
4064
4065 /*
4066 * Undo/redo use this list of SearchReplaceItems to do their
4067 * work. Note, that it is possible for a cell to occur
4068 * multiple times in the list.
4069 */
4070 GList *cells;
4071 } CmdSearchReplace;
4072
4073 MAKE_GNM_COMMAND (CmdSearchReplace, cmd_search_replace, NULL)
4074
4075 typedef enum { SRI_text, SRI_comment } SearchReplaceItemType;
4076
4077 typedef struct {
4078 GnmEvalPos pos;
4079 SearchReplaceItemType old_type, new_type;
4080 union {
4081 char *text;
4082 char *comment;
4083 } old, new;
4084 } SearchReplaceItem;
4085
4086
4087 static void
cmd_search_replace_update_after_action(CmdSearchReplace * me,WorkbookControl * wbc)4088 cmd_search_replace_update_after_action (CmdSearchReplace *me,
4089 WorkbookControl *wbc)
4090 {
4091 GList *tmp;
4092 Sheet *last_sheet = NULL;
4093
4094 for (tmp = me->cells; tmp; tmp = tmp->next) {
4095 SearchReplaceItem *sri = tmp->data;
4096 if (sri->pos.sheet != last_sheet) {
4097 last_sheet = sri->pos.sheet;
4098 update_after_action (last_sheet, wbc);
4099 }
4100 }
4101 }
4102
4103
4104 static gboolean
cmd_search_replace_undo(GnmCommand * cmd,WorkbookControl * wbc)4105 cmd_search_replace_undo (GnmCommand *cmd,
4106 WorkbookControl *wbc)
4107 {
4108 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4109 GList *tmp;
4110
4111 /* Undo does replacements backwards. */
4112 for (tmp = g_list_last (me->cells); tmp; tmp = tmp->prev) {
4113 SearchReplaceItem *sri = tmp->data;
4114 switch (sri->old_type) {
4115 case SRI_text:
4116 {
4117 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4118 sri->pos.eval.col,
4119 sri->pos.eval.row);
4120 sheet_cell_set_text (cell, sri->old.text, NULL);
4121 break;
4122 }
4123 case SRI_comment:
4124 {
4125 GnmComment *comment =
4126 sheet_get_comment (sri->pos.sheet,
4127 &sri->pos.eval);
4128 if (comment) {
4129 cell_comment_text_set (comment, sri->old.comment);
4130 } else {
4131 g_warning ("Undo/redo broken.");
4132 }
4133 }
4134 break;
4135 }
4136 }
4137 cmd_search_replace_update_after_action (me, wbc);
4138
4139 return FALSE;
4140 }
4141
4142 static gboolean
cmd_search_replace_redo(GnmCommand * cmd,WorkbookControl * wbc)4143 cmd_search_replace_redo (GnmCommand *cmd,
4144 WorkbookControl *wbc)
4145 {
4146 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4147 GList *tmp;
4148
4149 /* Redo does replacements forward. */
4150 for (tmp = me->cells; tmp; tmp = tmp->next) {
4151 SearchReplaceItem *sri = tmp->data;
4152 switch (sri->new_type) {
4153 case SRI_text:
4154 {
4155 GnmCell *cell = sheet_cell_get (sri->pos.sheet,
4156 sri->pos.eval.col,
4157 sri->pos.eval.row);
4158 sheet_cell_set_text (cell, sri->new.text, NULL);
4159 break;
4160 }
4161 case SRI_comment:
4162 {
4163 GnmComment *comment =
4164 sheet_get_comment (sri->pos.sheet,
4165 &sri->pos.eval);
4166 if (comment) {
4167 cell_comment_text_set (comment, sri->new.comment);
4168 } else {
4169 g_warning ("Undo/redo broken.");
4170 }
4171 }
4172 break;
4173 }
4174 }
4175 cmd_search_replace_update_after_action (me, wbc);
4176
4177 return FALSE;
4178 }
4179
4180 static gboolean
cmd_search_replace_do_cell(CmdSearchReplace * me,GnmEvalPos * ep,gboolean test_run)4181 cmd_search_replace_do_cell (CmdSearchReplace *me, GnmEvalPos *ep,
4182 gboolean test_run)
4183 {
4184 GnmSearchReplace *sr = me->sr;
4185
4186 GnmSearchReplaceCellResult cell_res;
4187 GnmSearchReplaceCommentResult comment_res;
4188
4189 if (gnm_search_replace_cell (sr, ep, TRUE, &cell_res)) {
4190 GnmExprTop const *texpr;
4191 GnmValue *val;
4192 gboolean err;
4193 GnmParsePos pp;
4194
4195 parse_pos_init_evalpos (&pp, ep);
4196 parse_text_value_or_expr (&pp, cell_res.new_text, &val, &texpr);
4197
4198 /*
4199 * FIXME: this is a hack, but parse_text_value_or_expr
4200 * does not have a better way of signaling an error.
4201 */
4202 err = (val &&
4203 gnm_expr_char_start_p (cell_res.new_text) &&
4204 !go_format_is_text (gnm_cell_get_format (cell_res.cell)));
4205 value_release (val);
4206 if (texpr) gnm_expr_top_unref (texpr);
4207
4208 if (err) {
4209 if (test_run) {
4210 gnm_search_replace_query_fail (sr, &cell_res);
4211 g_free (cell_res.old_text);
4212 g_free (cell_res.new_text);
4213 return TRUE;
4214 } else {
4215 switch (sr->error_behaviour) {
4216 case GNM_SRE_ERROR: {
4217 GnmExprTop const *ee =
4218 gnm_expr_top_new
4219 (gnm_expr_new_funcall1
4220 (gnm_func_lookup ("ERROR", NULL),
4221 gnm_expr_new_constant
4222 (value_new_string_nocopy (cell_res.new_text))));
4223 GnmConventionsOut out;
4224
4225 out.accum = g_string_new ("=");
4226 out.pp = &pp;
4227 out.convs = pp.sheet->convs;
4228 gnm_expr_top_as_gstring (ee, &out);
4229 gnm_expr_top_unref (ee);
4230 cell_res.new_text = g_string_free (out.accum, FALSE);
4231 err = FALSE;
4232 break;
4233 }
4234 case GNM_SRE_STRING: {
4235 GString *s = g_string_new ("'");
4236 g_string_append (s, cell_res.new_text);
4237 g_free (cell_res.new_text);
4238 cell_res.new_text = g_string_free (s, FALSE);
4239 err = FALSE;
4240 break;
4241 }
4242 case GNM_SRE_FAIL:
4243 g_assert_not_reached ();
4244 case GNM_SRE_SKIP:
4245 default:
4246 ; /* Nothing */
4247 }
4248 }
4249 }
4250
4251 if (!err && !test_run) {
4252 int res = gnm_search_replace_query_cell
4253 (sr, &cell_res);
4254 gboolean doit = (res == GTK_RESPONSE_YES);
4255
4256 if (res == GTK_RESPONSE_CANCEL) {
4257 g_free (cell_res.old_text);
4258 g_free (cell_res.new_text);
4259 return TRUE;
4260 }
4261
4262 if (doit) {
4263 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4264
4265 sheet_cell_set_text (cell_res.cell, cell_res.new_text, NULL);
4266
4267 sri->pos = *ep;
4268 sri->old_type = sri->new_type = SRI_text;
4269 sri->old.text = cell_res.old_text;
4270 sri->new.text = cell_res.new_text;
4271 me->cells = g_list_prepend (me->cells, sri);
4272
4273 cell_res.old_text = cell_res.new_text = NULL;
4274 }
4275 }
4276
4277 g_free (cell_res.new_text);
4278 g_free (cell_res.old_text);
4279 }
4280
4281 if (!test_run &&
4282 gnm_search_replace_comment (sr, ep, TRUE, &comment_res)) {
4283 int res = gnm_search_replace_query_comment
4284 (sr, ep, &comment_res);
4285 gboolean doit = (res == GTK_RESPONSE_YES);
4286
4287 if (doit) {
4288 SearchReplaceItem *sri = g_new (SearchReplaceItem, 1);
4289 sri->pos = *ep;
4290 sri->old_type = sri->new_type = SRI_comment;
4291 sri->old.comment = g_strdup (comment_res.old_text);
4292 sri->new.comment = comment_res.new_text;
4293 me->cells = g_list_prepend (me->cells, sri);
4294
4295 cell_comment_text_set (comment_res.comment, comment_res.new_text);
4296 } else {
4297 g_free (comment_res.new_text);
4298 if (res == GTK_RESPONSE_CANCEL)
4299 return TRUE;
4300 }
4301 }
4302
4303 return FALSE;
4304 }
4305
4306
4307 static gboolean
cmd_search_replace_do(CmdSearchReplace * me,gboolean test_run,WorkbookControl * wbc)4308 cmd_search_replace_do (CmdSearchReplace *me, gboolean test_run,
4309 WorkbookControl *wbc)
4310 {
4311 GnmSearchReplace *sr = me->sr;
4312 GPtrArray *cells;
4313 gboolean result = FALSE;
4314 unsigned i;
4315
4316 if (test_run) {
4317 switch (sr->error_behaviour) {
4318 case GNM_SRE_SKIP:
4319 case GNM_SRE_QUERY:
4320 case GNM_SRE_ERROR:
4321 case GNM_SRE_STRING:
4322 /* An error is not a problem. */
4323 return FALSE;
4324
4325 case GNM_SRE_FAIL:
4326 ; /* Nothing. */
4327 }
4328 }
4329
4330 cells = gnm_search_collect_cells (sr);
4331
4332 for (i = 0; i < cells->len; i++) {
4333 GnmEvalPos *ep = g_ptr_array_index (cells, i);
4334
4335 if (cmd_search_replace_do_cell (me, ep, test_run)) {
4336 result = TRUE;
4337 break;
4338 }
4339 }
4340
4341 gnm_search_collect_cells_free (cells);
4342
4343 if (!test_run) {
4344 /* Cells were added in the wrong order. Correct. */
4345 me->cells = g_list_reverse (me->cells);
4346
4347 cmd_search_replace_update_after_action (me, wbc);
4348 }
4349
4350 return result;
4351 }
4352
4353
4354 static void
cmd_search_replace_finalize(GObject * cmd)4355 cmd_search_replace_finalize (GObject *cmd)
4356 {
4357 CmdSearchReplace *me = CMD_SEARCH_REPLACE (cmd);
4358 GList *tmp;
4359
4360 for (tmp = me->cells; tmp; tmp = tmp->next) {
4361 SearchReplaceItem *sri = tmp->data;
4362 switch (sri->old_type) {
4363 case SRI_text:
4364 g_free (sri->old.text);
4365 break;
4366 case SRI_comment:
4367 g_free (sri->old.comment);
4368 break;
4369 }
4370 switch (sri->new_type) {
4371 case SRI_text:
4372 g_free (sri->new.text);
4373 break;
4374 case SRI_comment:
4375 g_free (sri->new.comment);
4376 break;
4377 }
4378 g_free (sri);
4379 }
4380 g_list_free (me->cells);
4381 g_object_unref (me->sr);
4382
4383 gnm_command_finalize (cmd);
4384 }
4385
4386 gboolean
cmd_search_replace(WorkbookControl * wbc,GnmSearchReplace * sr)4387 cmd_search_replace (WorkbookControl *wbc, GnmSearchReplace *sr)
4388 {
4389 CmdSearchReplace *me;
4390
4391 g_return_val_if_fail (sr != NULL, TRUE);
4392
4393 me = g_object_new (CMD_SEARCH_REPLACE_TYPE, NULL);
4394
4395 me->cells = NULL;
4396 me->sr = g_object_ref (sr);
4397
4398 me->cmd.sheet = NULL;
4399 me->cmd.size = 1; /* Corrected below. */
4400 me->cmd.cmd_descriptor = g_strdup (_("Search and Replace"));
4401
4402 if (cmd_search_replace_do (me, TRUE, wbc)) {
4403 /* There was an error and nothing was done. */
4404 g_object_unref (me);
4405 return TRUE;
4406 }
4407
4408 cmd_search_replace_do (me, FALSE, wbc);
4409 me->cmd.size += g_list_length (me->cells);
4410
4411 command_register_undo (wbc, G_OBJECT (me));
4412 return FALSE;
4413 }
4414
4415 /******************************************************************/
4416
4417 #define CMD_COLROW_STD_SIZE_TYPE (cmd_colrow_std_size_get_type ())
4418 #define CMD_COLROW_STD_SIZE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_COLROW_STD_SIZE_TYPE, CmdColRowStdSize))
4419
4420 typedef struct {
4421 GnmCommand cmd;
4422
4423 Sheet *sheet;
4424 gboolean is_cols;
4425 double new_default;
4426 double old_default;
4427 } CmdColRowStdSize;
4428
MAKE_GNM_COMMAND(CmdColRowStdSize,cmd_colrow_std_size,NULL)4429 MAKE_GNM_COMMAND (CmdColRowStdSize, cmd_colrow_std_size, NULL)
4430
4431 static gboolean
4432 cmd_colrow_std_size_undo (GnmCommand *cmd,
4433 G_GNUC_UNUSED WorkbookControl *wbc)
4434 {
4435 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4436
4437 g_return_val_if_fail (me != NULL, TRUE);
4438 g_return_val_if_fail (me->old_default != 0, TRUE);
4439
4440 if (me->is_cols)
4441 sheet_col_set_default_size_pts (me->sheet, me->old_default);
4442 else
4443 sheet_row_set_default_size_pts (me->sheet, me->old_default);
4444
4445 me->old_default = 0;
4446
4447 return FALSE;
4448 }
4449
4450 static gboolean
cmd_colrow_std_size_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)4451 cmd_colrow_std_size_redo (GnmCommand *cmd,
4452 G_GNUC_UNUSED WorkbookControl *wbc)
4453 {
4454 CmdColRowStdSize *me = CMD_COLROW_STD_SIZE (cmd);
4455
4456 g_return_val_if_fail (me != NULL, TRUE);
4457 g_return_val_if_fail (me->old_default == 0, TRUE);
4458
4459 if (me->is_cols) {
4460 me->old_default = sheet_col_get_default_size_pts (me->sheet);
4461 sheet_col_set_default_size_pts (me->sheet, me->new_default);
4462 } else {
4463 me->old_default = sheet_row_get_default_size_pts (me->sheet);
4464 sheet_row_set_default_size_pts (me->sheet, me->new_default);
4465 }
4466
4467 return FALSE;
4468 }
4469 static void
cmd_colrow_std_size_finalize(GObject * cmd)4470 cmd_colrow_std_size_finalize (GObject *cmd)
4471 {
4472 gnm_command_finalize (cmd);
4473 }
4474
4475 gboolean
cmd_colrow_std_size(WorkbookControl * wbc,Sheet * sheet,gboolean is_cols,double new_default)4476 cmd_colrow_std_size (WorkbookControl *wbc, Sheet *sheet,
4477 gboolean is_cols, double new_default)
4478 {
4479 CmdColRowStdSize *me;
4480
4481 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
4482
4483 me = g_object_new (CMD_COLROW_STD_SIZE_TYPE, NULL);
4484
4485 me->sheet = sheet;
4486 me->is_cols = is_cols;
4487 me->new_default = new_default;
4488 me->old_default = 0;
4489
4490 me->cmd.sheet = sheet;
4491 me->cmd.size = 1; /* Changed in initial redo. */
4492 me->cmd.cmd_descriptor = is_cols
4493 ? g_strdup_printf (_("Setting default width of columns to %.2fpts"), new_default)
4494 : g_strdup_printf (_("Setting default height of rows to %.2fpts"), new_default);
4495
4496 return gnm_command_push_undo (wbc, G_OBJECT (me));
4497 }
4498
4499 /******************************************************************/
4500
4501 #define CMD_ZOOM_TYPE (cmd_zoom_get_type ())
4502 #define CMD_ZOOM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ZOOM_TYPE, CmdZoom))
4503
4504 typedef struct {
4505 GnmCommand cmd;
4506
4507 GSList *sheets;
4508 double new_factor;
4509 double *old_factors;
4510 } CmdZoom;
4511
MAKE_GNM_COMMAND(CmdZoom,cmd_zoom,NULL)4512 MAKE_GNM_COMMAND (CmdZoom, cmd_zoom, NULL)
4513
4514 static gboolean
4515 cmd_zoom_undo (GnmCommand *cmd,
4516 G_GNUC_UNUSED WorkbookControl *wbc)
4517 {
4518 CmdZoom *me = CMD_ZOOM (cmd);
4519 GSList *l;
4520 int i;
4521
4522 g_return_val_if_fail (me != NULL, TRUE);
4523 g_return_val_if_fail (me->sheets != NULL, TRUE);
4524 g_return_val_if_fail (me->old_factors != NULL, TRUE);
4525
4526 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4527 Sheet *sheet = l->data;
4528 g_object_set (sheet, "zoom-factor", me->old_factors[i], NULL);
4529 }
4530
4531 return FALSE;
4532 }
4533
4534 static gboolean
cmd_zoom_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)4535 cmd_zoom_redo (GnmCommand *cmd,
4536 G_GNUC_UNUSED WorkbookControl *wbc)
4537 {
4538 CmdZoom *me = CMD_ZOOM (cmd);
4539 GSList *l;
4540
4541 g_return_val_if_fail (me != NULL, TRUE);
4542 g_return_val_if_fail (me->sheets != NULL, TRUE);
4543
4544 for (l = me->sheets; l != NULL; l = l->next) {
4545 Sheet *sheet = l->data;
4546 g_object_set (sheet, "zoom-factor", me->new_factor, NULL);
4547 }
4548
4549 return FALSE;
4550 }
4551
4552 static void
cmd_zoom_finalize(GObject * cmd)4553 cmd_zoom_finalize (GObject *cmd)
4554 {
4555 CmdZoom *me = CMD_ZOOM (cmd);
4556
4557 g_slist_free (me->sheets);
4558 g_free (me->old_factors);
4559
4560 gnm_command_finalize (cmd);
4561 }
4562
4563 /**
4564 * cmd_zoom:
4565 * @wbc: #WorkbookControl
4566 * @sheets: (element-type Sheet) (transfer container):
4567 * @factor:
4568 *
4569 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4570 **/
4571 gboolean
cmd_zoom(WorkbookControl * wbc,GSList * sheets,double factor)4572 cmd_zoom (WorkbookControl *wbc, GSList *sheets, double factor)
4573 {
4574 CmdZoom *me;
4575 GString *namelist;
4576 GSList *l;
4577 int i;
4578
4579 g_return_val_if_fail (wbc != NULL, TRUE);
4580 g_return_val_if_fail (sheets != NULL, TRUE);
4581
4582 me = g_object_new (CMD_ZOOM_TYPE, NULL);
4583
4584 me->sheets = sheets;
4585 me->old_factors = g_new0 (double, g_slist_length (sheets));
4586 me->new_factor = factor;
4587
4588 /* Make a list of all sheets to zoom and save zoom factor for each */
4589 namelist = g_string_new (NULL);
4590 for (i = 0, l = me->sheets; l != NULL; l = l->next, i++) {
4591 Sheet *sheet = l->data;
4592
4593 g_string_append (namelist, sheet->name_unquoted);
4594 me->old_factors[i] = sheet->last_zoom_factor_used;
4595
4596 if (l->next)
4597 g_string_append (namelist, ", ");
4598 }
4599
4600 /* Make sure the string doesn't get overly wide */
4601 gnm_cmd_trunc_descriptor (namelist, NULL);
4602
4603 me->cmd.sheet = NULL;
4604 me->cmd.size = 1;
4605 me->cmd.cmd_descriptor =
4606 g_strdup_printf (_("Zoom %s to %.0f%%"), namelist->str, factor * 100);
4607
4608 g_string_free (namelist, TRUE);
4609
4610 return gnm_command_push_undo (wbc, G_OBJECT (me));
4611 }
4612
4613 /******************************************************************/
4614
4615 #define CMD_OBJECTS_DELETE_TYPE (cmd_objects_delete_get_type ())
4616 #define CMD_OBJECTS_DELETE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECTS_DELETE_TYPE, CmdObjectsDelete))
4617
4618 typedef struct {
4619 GnmCommand cmd;
4620 GSList *objects;
4621 GArray *location;
4622 } CmdObjectsDelete;
4623
MAKE_GNM_COMMAND(CmdObjectsDelete,cmd_objects_delete,NULL)4624 MAKE_GNM_COMMAND (CmdObjectsDelete, cmd_objects_delete, NULL)
4625
4626 static gboolean
4627 cmd_objects_delete_redo (GnmCommand *cmd,
4628 G_GNUC_UNUSED WorkbookControl *wbc)
4629 {
4630 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4631 g_slist_foreach (me->objects, (GFunc) sheet_object_clear_sheet, NULL);
4632 return FALSE;
4633 }
4634
4635 static void
cmd_objects_restore_location(SheetObject * so,gint location)4636 cmd_objects_restore_location (SheetObject *so, gint location)
4637 {
4638 gint loc = sheet_object_get_stacking (so);
4639 if (loc != location)
4640 sheet_object_adjust_stacking(so, location - loc);
4641 }
4642
4643 static gboolean
cmd_objects_delete_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)4644 cmd_objects_delete_undo (GnmCommand *cmd,
4645 G_GNUC_UNUSED WorkbookControl *wbc)
4646 {
4647 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4648 GSList *l;
4649 gint i;
4650
4651 g_slist_foreach (me->objects,
4652 (GFunc) sheet_object_set_sheet, me->cmd.sheet);
4653
4654 for (l = me->objects, i = 0; l; l = l->next, i++)
4655 cmd_objects_restore_location (GNM_SO (l->data),
4656 g_array_index(me->location,
4657 gint, i));
4658 return FALSE;
4659 }
4660
4661 static void
cmd_objects_delete_finalize(GObject * cmd)4662 cmd_objects_delete_finalize (GObject *cmd)
4663 {
4664 CmdObjectsDelete *me = CMD_OBJECTS_DELETE (cmd);
4665 g_slist_free_full (me->objects, g_object_unref);
4666 if (me->location) {
4667 g_array_free (me->location, TRUE);
4668 me->location = NULL;
4669 }
4670 gnm_command_finalize (cmd);
4671 }
4672
4673 static void
cmd_objects_store_location(SheetObject * so,GArray * location)4674 cmd_objects_store_location (SheetObject *so, GArray *location)
4675 {
4676 gint loc = sheet_object_get_stacking (so);
4677 g_array_append_val (location, loc);
4678 }
4679
4680 /**
4681 * cmd_objects_delete:
4682 * @wbc: #WorkbookControl
4683 * @objects: (element-type SheetObject) (transfer container): the objects to
4684 * delete.
4685 * @name:
4686 *
4687 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4688 **/
4689 gboolean
cmd_objects_delete(WorkbookControl * wbc,GSList * objects,char const * name)4690 cmd_objects_delete (WorkbookControl *wbc, GSList *objects,
4691 char const *name)
4692 {
4693 CmdObjectsDelete *me;
4694
4695 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4696 g_return_val_if_fail (objects != NULL, TRUE);
4697
4698 me = g_object_new (CMD_OBJECTS_DELETE_TYPE, NULL);
4699
4700 me->objects = objects;
4701 g_slist_foreach (me->objects, (GFunc) g_object_ref, NULL);
4702
4703 me->location = g_array_new (FALSE, FALSE, sizeof (gint));
4704 g_slist_foreach (me->objects, (GFunc) cmd_objects_store_location,
4705 me->location);
4706
4707 me->cmd.sheet = sheet_object_get_sheet (objects->data);
4708 me->cmd.size = 1;
4709 me->cmd.cmd_descriptor = g_strdup (name ? name : _("Delete Object"));
4710
4711 return gnm_command_push_undo (wbc, G_OBJECT (me));
4712 }
4713
4714 /******************************************************************/
4715
4716 /**
4717 * cmd_objects_move:
4718 * @wbc: #WorkbookControl
4719 * @objects: (element-type SheetObject) (transfer container): the objects to move.
4720 * @anchors: (element-type SheetObjectAnchor) (transfer full): the anchors for the objects.
4721 * @objects_created:
4722 * @name:
4723 *
4724 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4725 **/
4726 gboolean
cmd_objects_move(WorkbookControl * wbc,GSList * objects,GSList * anchors,gboolean objects_created,char const * name)4727 cmd_objects_move (WorkbookControl *wbc, GSList *objects, GSList *anchors,
4728 gboolean objects_created, char const *name)
4729 {
4730 GOUndo *undo = NULL;
4731 GOUndo *redo = NULL;
4732 gboolean result = TRUE;
4733
4734 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
4735
4736 undo = sheet_object_move_undo (objects, objects_created);
4737 redo = sheet_object_move_do (objects, anchors, objects_created);
4738
4739 result = cmd_generic (wbc, name, undo, redo);
4740
4741 g_slist_free (objects);
4742 g_slist_free_full (anchors, g_free);
4743
4744 return result;
4745 }
4746
4747 /******************************************************************/
4748
4749 #define CMD_REORGANIZE_SHEETS_TYPE (cmd_reorganize_sheets_get_type ())
4750 #define CMD_REORGANIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REORGANIZE_SHEETS_TYPE, CmdReorganizeSheets))
4751
4752 typedef struct {
4753 GnmCommand cmd;
4754 Workbook *wb;
4755 WorkbookSheetState *old;
4756 WorkbookSheetState *new;
4757 gboolean first;
4758 Sheet *undo_sheet;
4759 Sheet *redo_sheet;
4760 } CmdReorganizeSheets;
4761
MAKE_GNM_COMMAND(CmdReorganizeSheets,cmd_reorganize_sheets,NULL)4762 MAKE_GNM_COMMAND (CmdReorganizeSheets, cmd_reorganize_sheets, NULL)
4763
4764 static gboolean
4765 cmd_reorganize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4766 {
4767 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4768 workbook_sheet_state_restore (me->wb, me->old);
4769 if (me->undo_sheet) {
4770 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4771 wb_control_sheet_focus (control, me->undo_sheet););
4772 }
4773 return FALSE;
4774 }
4775
4776 static gboolean
cmd_reorganize_sheets_redo(GnmCommand * cmd,WorkbookControl * wbc)4777 cmd_reorganize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4778 {
4779 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4780
4781 if (me->first)
4782 me->first = FALSE;
4783 else {
4784 workbook_sheet_state_restore (me->wb, me->new);
4785 if (me->redo_sheet) {
4786 WORKBOOK_VIEW_FOREACH_CONTROL (wb_control_view (wbc), control,
4787 wb_control_sheet_focus (control, me->redo_sheet););
4788 }
4789 }
4790
4791 return FALSE;
4792 }
4793
4794 static void
cmd_reorganize_sheets_finalize(GObject * cmd)4795 cmd_reorganize_sheets_finalize (GObject *cmd)
4796 {
4797 CmdReorganizeSheets *me = CMD_REORGANIZE_SHEETS (cmd);
4798
4799 if (me->old)
4800 workbook_sheet_state_unref (me->old);
4801 if (me->new)
4802 workbook_sheet_state_unref (me->new);
4803
4804 gnm_command_finalize (cmd);
4805 }
4806
4807 gboolean
cmd_reorganize_sheets(WorkbookControl * wbc,WorkbookSheetState * old_state,Sheet * undo_sheet)4808 cmd_reorganize_sheets (WorkbookControl *wbc,
4809 WorkbookSheetState *old_state,
4810 Sheet *undo_sheet)
4811 {
4812 CmdReorganizeSheets *me;
4813 Workbook *wb = wb_control_get_workbook (wbc);
4814
4815 me = g_object_new (CMD_REORGANIZE_SHEETS_TYPE, NULL);
4816 me->wb = wb;
4817 me->old = old_state;
4818 me->new = workbook_sheet_state_new (me->wb);
4819 me->first = TRUE;
4820 me->undo_sheet = undo_sheet;
4821 me->redo_sheet = wb_control_cur_sheet (wbc);
4822
4823 me->cmd.sheet = NULL;
4824 me->cmd.size = workbook_sheet_state_size (me->old) +
4825 workbook_sheet_state_size (me->new);
4826 me->cmd.cmd_descriptor =
4827 workbook_sheet_state_diff (me->old, me->new);
4828
4829 if (me->cmd.cmd_descriptor)
4830 return gnm_command_push_undo (wbc, G_OBJECT (me));
4831
4832 /* No change. */
4833 g_object_unref (me);
4834 return FALSE;
4835 }
4836
4837 /******************************************************************/
4838
4839 gboolean
cmd_rename_sheet(WorkbookControl * wbc,Sheet * sheet,char const * new_name)4840 cmd_rename_sheet (WorkbookControl *wbc,
4841 Sheet *sheet,
4842 char const *new_name)
4843 {
4844 WorkbookSheetState *old_state;
4845 Sheet *collision;
4846
4847 g_return_val_if_fail (new_name != NULL, TRUE);
4848 g_return_val_if_fail (sheet != NULL, TRUE);
4849
4850 if (*new_name == 0) {
4851 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), _("Sheet names must be non-empty."));
4852 return TRUE;
4853 }
4854
4855 collision = workbook_sheet_by_name (sheet->workbook, new_name);
4856 if (collision && collision != sheet) {
4857 GError *err = g_error_new (go_error_invalid(), 0,
4858 _("A workbook cannot have two sheets with the same name."));
4859 go_cmd_context_error (GO_CMD_CONTEXT (wbc), err);
4860 g_error_free (err);
4861 return TRUE;
4862 }
4863
4864 old_state = workbook_sheet_state_new (sheet->workbook);
4865 g_object_set (sheet, "name", new_name, NULL);
4866 return cmd_reorganize_sheets (wbc, old_state, sheet);
4867 }
4868
4869 /******************************************************************/
4870
4871 #define CMD_RESIZE_SHEETS_TYPE (cmd_resize_sheets_get_type ())
4872 #define CMD_RESIZE_SHEETS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESIZE_SHEETS_TYPE, CmdResizeSheets))
4873
4874 typedef struct {
4875 GnmCommand cmd;
4876 GSList *sheets;
4877 int cols, rows;
4878 GOUndo *undo;
4879 } CmdResizeSheets;
4880
MAKE_GNM_COMMAND(CmdResizeSheets,cmd_resize_sheets,NULL)4881 MAKE_GNM_COMMAND (CmdResizeSheets, cmd_resize_sheets, NULL)
4882
4883 static gboolean
4884 cmd_resize_sheets_undo (GnmCommand *cmd, WorkbookControl *wbc)
4885 {
4886 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4887 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4888
4889 go_undo_undo_with_data (me->undo, cc);
4890 g_object_unref (me->undo);
4891 me->undo = NULL;
4892
4893 return FALSE;
4894 }
4895
4896 static gboolean
cmd_resize_sheets_redo(GnmCommand * cmd,WorkbookControl * wbc)4897 cmd_resize_sheets_redo (GnmCommand *cmd, WorkbookControl *wbc)
4898 {
4899 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4900 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
4901 GSList *l;
4902
4903 for (l = me->sheets; l; l = l->next) {
4904 Sheet *sheet = l->data;
4905 gboolean err;
4906 GOUndo *u = gnm_sheet_resize (sheet, me->cols, me->rows,
4907 cc, &err);
4908 me->undo = go_undo_combine (me->undo, u);
4909
4910 if (err) {
4911 if (me->undo)
4912 go_undo_undo_with_data (me->undo, cc);
4913 return TRUE;
4914 }
4915 }
4916
4917 return FALSE;
4918 }
4919
4920 static void
cmd_resize_sheets_finalize(GObject * cmd)4921 cmd_resize_sheets_finalize (GObject *cmd)
4922 {
4923 CmdResizeSheets *me = CMD_RESIZE_SHEETS (cmd);
4924
4925 g_slist_free (me->sheets);
4926 if (me->undo) {
4927 g_object_unref (me->undo);
4928 me->undo = NULL;
4929 }
4930
4931 gnm_command_finalize (cmd);
4932 }
4933
4934 /**
4935 * cmd_resize_sheets:
4936 * @wbc: #WorkbookControl
4937 * @sheets: (element-type Sheet) (transfer container): the sheets to resize.
4938 * @cols: new columns number.
4939 * @rows: new rows number.
4940 *
4941 * Returns: %TRUE if there was a problem, %FALSE otherwise.
4942 **/
4943 gboolean
cmd_resize_sheets(WorkbookControl * wbc,GSList * sheets,int cols,int rows)4944 cmd_resize_sheets (WorkbookControl *wbc,
4945 GSList *sheets,
4946 int cols, int rows)
4947 {
4948 CmdResizeSheets *me;
4949
4950 me = g_object_new (CMD_RESIZE_SHEETS_TYPE, NULL);
4951 me->sheets = sheets;
4952 me->cols = cols;
4953 me->rows = rows;
4954 me->cmd.sheet = sheets ? sheets->data : NULL;
4955 me->cmd.size = 1;
4956 me->cmd.cmd_descriptor = g_strdup (_("Resizing sheet"));
4957
4958 if (sheets &&
4959 gnm_sheet_valid_size (cols, rows))
4960 return gnm_command_push_undo (wbc, G_OBJECT (me));
4961
4962 /* No change. */
4963 g_object_unref (me);
4964 return FALSE;
4965 }
4966
4967 /******************************************************************/
4968
4969 #define CMD_SET_COMMENT_TYPE (cmd_set_comment_get_type ())
4970 #define CMD_SET_COMMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SET_COMMENT_TYPE, CmdSetComment))
4971
4972 typedef struct {
4973 GnmCommand cmd;
4974
4975 Sheet *sheet;
4976 GnmCellPos pos;
4977 gchar *new_text;
4978 gchar *old_text;
4979 gchar *new_author;
4980 gchar *old_author;
4981 PangoAttrList *old_attributes;
4982 PangoAttrList *new_attributes;
4983 } CmdSetComment;
4984
MAKE_GNM_COMMAND(CmdSetComment,cmd_set_comment,NULL)4985 MAKE_GNM_COMMAND (CmdSetComment, cmd_set_comment, NULL)
4986
4987 static gboolean
4988 cmd_set_comment_apply (Sheet *sheet, GnmCellPos *pos,
4989 char const *text, PangoAttrList *attributes,
4990 char const *author)
4991 {
4992 GnmComment *comment;
4993 Workbook *wb = sheet->workbook;
4994
4995 comment = sheet_get_comment (sheet, pos);
4996 if (comment) {
4997 if (text)
4998 g_object_set (G_OBJECT (comment), "text", text,
4999 "author", author,
5000 "markup", attributes, NULL);
5001 else {
5002 GnmRange const *mr;
5003
5004 mr = gnm_sheet_merge_contains_pos (sheet, pos);
5005
5006 if (mr)
5007 sheet_objects_clear (sheet, mr, GNM_CELL_COMMENT_TYPE, NULL);
5008 else {
5009 GnmRange r;
5010 r.start = r.end = *pos;
5011 sheet_objects_clear (sheet, &r, GNM_CELL_COMMENT_TYPE, NULL);
5012 }
5013 }
5014 } else if (text && (strlen (text) > 0)) {
5015 cell_set_comment (sheet, pos, author, text, attributes);
5016 }
5017 sheet_mark_dirty (sheet);
5018
5019 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
5020 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
5021
5022 return FALSE;
5023 }
5024
5025 static gboolean
cmd_set_comment_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)5026 cmd_set_comment_undo (GnmCommand *cmd,
5027 G_GNUC_UNUSED WorkbookControl *wbc)
5028 {
5029 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5030
5031 return cmd_set_comment_apply (me->sheet, &me->pos,
5032 me->old_text, me->old_attributes,
5033 me->old_author);
5034 }
5035
5036 static gboolean
cmd_set_comment_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)5037 cmd_set_comment_redo (GnmCommand *cmd,
5038 G_GNUC_UNUSED WorkbookControl *wbc)
5039 {
5040 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5041
5042 return cmd_set_comment_apply (me->sheet, &me->pos,
5043 me->new_text, me->new_attributes,
5044 me->new_author);
5045 }
5046
5047 static void
cmd_set_comment_finalize(GObject * cmd)5048 cmd_set_comment_finalize (GObject *cmd)
5049 {
5050 CmdSetComment *me = CMD_SET_COMMENT (cmd);
5051
5052 g_free (me->new_text);
5053 me->new_text = NULL;
5054
5055 g_free (me->old_text);
5056 me->old_text = NULL;
5057
5058 g_free (me->new_author);
5059 me->new_author = NULL;
5060
5061 g_free (me->old_author);
5062 me->old_author = NULL;
5063
5064 if (me->old_attributes != NULL) {
5065 pango_attr_list_unref (me->old_attributes);
5066 me->old_attributes = NULL;
5067 }
5068
5069 if (me->new_attributes != NULL) {
5070 pango_attr_list_unref (me->new_attributes);
5071 me->new_attributes = NULL;
5072 }
5073
5074 gnm_command_finalize (cmd);
5075 }
5076
5077 gboolean
cmd_set_comment(WorkbookControl * wbc,Sheet * sheet,GnmCellPos const * pos,char const * new_text,PangoAttrList * attr,char const * new_author)5078 cmd_set_comment (WorkbookControl *wbc,
5079 Sheet *sheet, GnmCellPos const *pos,
5080 char const *new_text,
5081 PangoAttrList *attr,
5082 char const *new_author)
5083 {
5084 CmdSetComment *me;
5085 GnmComment *comment;
5086 char *where;
5087
5088 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5089 g_return_val_if_fail (new_text != NULL, TRUE);
5090
5091 me = g_object_new (CMD_SET_COMMENT_TYPE, NULL);
5092
5093 me->cmd.sheet = sheet;
5094 me->cmd.size = 1;
5095 if (strlen (new_text) < 1)
5096 me->new_text = NULL;
5097 else
5098 me->new_text = g_strdup (new_text);
5099 if (strlen (new_author) < 1)
5100 me->new_author = NULL;
5101 else
5102 me->new_author = g_strdup (new_author);
5103 if (attr != NULL)
5104 pango_attr_list_ref (attr);
5105 me->new_attributes = attr;
5106 where = undo_cell_pos_name (sheet, pos);
5107 me->cmd.cmd_descriptor =
5108 g_strdup_printf (me->new_text == NULL ?
5109 _("Clearing comment of %s") :
5110 _("Setting comment of %s"),
5111 where);
5112 g_free (where);
5113 me->old_text = NULL;
5114 me->old_author = NULL;
5115 me->old_attributes = NULL;
5116 me->pos = *pos;
5117 me->sheet = sheet;
5118 comment = sheet_get_comment (sheet, pos);
5119 if (comment) {
5120 g_object_get (G_OBJECT (comment),
5121 "text", &(me->old_text),
5122 "author", &(me->old_author),
5123 "markup", &(me->old_attributes), NULL);
5124 if (me->old_attributes != NULL)
5125 pango_attr_list_ref (me->old_attributes);
5126 me->old_text = g_strdup (me->old_text);
5127 me->old_author = g_strdup (me->old_author);
5128 }
5129
5130 /* Register the command object */
5131 return gnm_command_push_undo (wbc, G_OBJECT (me));
5132 }
5133
5134 /******************************************************************/
5135
5136 #define CMD_ANALYSIS_TOOL_TYPE (cmd_analysis_tool_get_type ())
5137 #define CMD_ANALYSIS_TOOL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_ANALYSIS_TOOL_TYPE, CmdAnalysis_Tool))
5138
5139 typedef struct {
5140 GnmCommand cmd;
5141
5142 data_analysis_output_t *dao;
5143 gpointer specs;
5144 gboolean specs_owned;
5145 analysis_tool_engine engine;
5146 data_analysis_output_type_t type;
5147
5148 ColRowStateList *col_info;
5149 ColRowStateList *row_info;
5150 GnmRange old_range;
5151 GnmCellRegion *old_contents;
5152 GSList *newSheetObjects;
5153 } CmdAnalysis_Tool;
5154
MAKE_GNM_COMMAND(CmdAnalysis_Tool,cmd_analysis_tool,NULL)5155 MAKE_GNM_COMMAND (CmdAnalysis_Tool, cmd_analysis_tool, NULL)
5156
5157 static gboolean
5158 cmd_analysis_tool_undo (GnmCommand *cmd, WorkbookControl *wbc)
5159 {
5160 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5161 GnmPasteTarget pt;
5162
5163 g_return_val_if_fail (me != NULL, TRUE);
5164
5165 /* The old view might not exist anymore */
5166 me->dao->wbc = wbc;
5167
5168 switch (me->type) {
5169 case NewSheetOutput:
5170 if (!command_undo_sheet_delete (me->dao->sheet))
5171 return TRUE;
5172 me->dao->sheet = NULL;
5173 break;
5174 case NewWorkbookOutput:
5175 g_warning ("How did we get here?");
5176 return TRUE;
5177 break;
5178 case RangeOutput:
5179 default:
5180 sheet_clear_region (me->dao->sheet,
5181 me->old_range.start.col, me->old_range.start.row,
5182 me->old_range.end.col, me->old_range.end.row,
5183 CLEAR_COMMENTS | CLEAR_FORMATS | CLEAR_NOCHECKARRAY |
5184 CLEAR_RECALC_DEPS | CLEAR_VALUES | CLEAR_MERGES,
5185 GO_CMD_CONTEXT (wbc));
5186 clipboard_paste_region (me->old_contents,
5187 paste_target_init (&pt, me->dao->sheet, &me->old_range, PASTE_ALL_SHEET),
5188 GO_CMD_CONTEXT (wbc));
5189 cellregion_unref (me->old_contents);
5190 me->old_contents = NULL;
5191 if (me->col_info) {
5192 dao_set_colrow_state_list (me->dao, TRUE, me->col_info);
5193 me->col_info = colrow_state_list_destroy (me->col_info);
5194 }
5195 if (me->row_info) {
5196 dao_set_colrow_state_list (me->dao, FALSE, me->row_info);
5197 me->row_info = colrow_state_list_destroy (me->row_info);
5198 }
5199 if (me->newSheetObjects == NULL)
5200 me->newSheetObjects = dao_surrender_so (me->dao);
5201 g_slist_foreach (me->newSheetObjects, (GFunc)sheet_object_clear_sheet, NULL);
5202 sheet_update (me->dao->sheet);
5203 }
5204
5205 return FALSE;
5206 }
5207
5208 static void
cmd_analysis_tool_draw_old_so(SheetObject * so,data_analysis_output_t * dao)5209 cmd_analysis_tool_draw_old_so (SheetObject *so, data_analysis_output_t *dao)
5210 {
5211 g_object_ref (so);
5212 dao_set_sheet_object (dao, 0, 1, so);
5213 }
5214
5215 static gboolean
cmd_analysis_tool_redo(GnmCommand * cmd,WorkbookControl * wbc)5216 cmd_analysis_tool_redo (GnmCommand *cmd, WorkbookControl *wbc)
5217 {
5218 gpointer continuity = NULL;
5219 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5220 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5221
5222 g_return_val_if_fail (me != NULL, TRUE);
5223
5224 /* The old view might not exist anymore */
5225 me->dao->wbc = wbc;
5226
5227 if (me->col_info)
5228 me->col_info = colrow_state_list_destroy (me->col_info);
5229 me->col_info = dao_get_colrow_state_list (me->dao, TRUE);
5230 if (me->row_info)
5231 me->row_info = colrow_state_list_destroy (me->row_info);
5232 me->row_info = dao_get_colrow_state_list (me->dao, FALSE);
5233
5234 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PREPARE_OUTPUT_RANGE, NULL)
5235 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5236 &me->cmd.cmd_descriptor)
5237 || cmd_dao_is_locked_effective (me->dao, wbc, me->cmd.cmd_descriptor)
5238 || me->engine (cc, me->dao, me->specs, TOOL_ENGINE_LAST_VALIDITY_CHECK, &continuity))
5239 return TRUE;
5240
5241 switch (me->type) {
5242 case NewSheetOutput:
5243 me->old_contents = NULL;
5244 break;
5245 case NewWorkbookOutput:
5246 /* No undo in this case (see below) */
5247 me->old_contents = NULL;
5248 break;
5249 case RangeOutput:
5250 default:
5251 range_init (&me->old_range, me->dao->start_col, me->dao->start_row,
5252 me->dao->start_col + me->dao->cols - 1,
5253 me->dao->start_row + me->dao->rows - 1);
5254 me->old_contents = clipboard_copy_range (me->dao->sheet, &me->old_range);
5255 break;
5256 }
5257
5258 if (me->newSheetObjects != NULL)
5259 dao_set_omit_so (me->dao, TRUE);
5260
5261 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_FORMAT_OUTPUT_RANGE, NULL))
5262 return TRUE;
5263
5264 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_PERFORM_CALC, &continuity)) {
5265 if (me->type == RangeOutput) {
5266 g_warning ("This is too late for failure! The target region has "
5267 "already been formatted!");
5268 } else
5269 return TRUE;
5270 }
5271 if (me->newSheetObjects != NULL)
5272 {
5273 GSList *l = g_slist_reverse
5274 (g_slist_copy (me->newSheetObjects));
5275
5276 dao_set_omit_so (me->dao, FALSE);
5277 g_slist_foreach (l,
5278 (GFunc) cmd_analysis_tool_draw_old_so,
5279 me->dao);
5280 g_slist_free (l);
5281 }
5282
5283 if (continuity) {
5284 g_warning ("There shouldn't be any data left in here!");
5285 }
5286
5287 dao_autofit_columns (me->dao);
5288 sheet_mark_dirty (me->dao->sheet);
5289 sheet_update (me->dao->sheet);
5290
5291 /* The concept of an undo if we create a new worksheet is extremely strange,
5292 * since we have separate undo/redo queues per worksheet.
5293 * Users can simply delete the worksheet if they so desire.
5294 */
5295
5296 return (me->type == NewWorkbookOutput);
5297 }
5298
5299 static void
cmd_analysis_tool_finalize(GObject * cmd)5300 cmd_analysis_tool_finalize (GObject *cmd)
5301 {
5302 CmdAnalysis_Tool *me = CMD_ANALYSIS_TOOL (cmd);
5303
5304 if (me->col_info)
5305 me->col_info = colrow_state_list_destroy (me->col_info);
5306 if (me->row_info)
5307 me->row_info = colrow_state_list_destroy (me->row_info);
5308
5309 me->engine (NULL, me->dao, me->specs, TOOL_ENGINE_CLEAN_UP, NULL);
5310
5311 if (me->specs_owned) {
5312 g_free (me->specs);
5313 dao_free (me->dao);
5314 }
5315 if (me->old_contents)
5316 cellregion_unref (me->old_contents);
5317
5318 g_slist_free_full (me->newSheetObjects, g_object_unref);
5319
5320 gnm_command_finalize (cmd);
5321 }
5322
5323 /**
5324 * cmd_analysis_tool: (skip)
5325 * Note: this takes ownership of specs and dao if the command
5326 * succeeds.
5327 *
5328 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5329 **/
5330 gboolean
cmd_analysis_tool(WorkbookControl * wbc,G_GNUC_UNUSED Sheet * sheet,data_analysis_output_t * dao,gpointer specs,analysis_tool_engine engine,gboolean always_take_ownership)5331 cmd_analysis_tool (WorkbookControl *wbc, G_GNUC_UNUSED Sheet *sheet,
5332 data_analysis_output_t *dao, gpointer specs,
5333 analysis_tool_engine engine, gboolean always_take_ownership)
5334 {
5335 CmdAnalysis_Tool *me;
5336 gboolean trouble;
5337 GOCmdContext *cc = GO_CMD_CONTEXT (wbc);
5338
5339 g_return_val_if_fail (dao != NULL, TRUE);
5340 g_return_val_if_fail (specs != NULL, TRUE);
5341 g_return_val_if_fail (engine != NULL, TRUE);
5342
5343 me = g_object_new (CMD_ANALYSIS_TOOL_TYPE, NULL);
5344
5345 dao->wbc = wbc;
5346
5347 /* Store the specs for the object */
5348 me->specs = specs;
5349 me->specs_owned = always_take_ownership;
5350 me->dao = dao;
5351 me->engine = engine;
5352 me->cmd.cmd_descriptor = NULL;
5353 if (me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DAO, NULL)) {
5354 g_object_unref (me);
5355 return TRUE;
5356 }
5357 me->engine (cc, me->dao, me->specs, TOOL_ENGINE_UPDATE_DESCRIPTOR,
5358 &me->cmd.cmd_descriptor);
5359 me->cmd.sheet = NULL;
5360 me->type = dao->type;
5361 me->row_info = NULL;
5362 me->col_info = NULL;
5363
5364 /* We divide by 2 since many cells will be empty*/
5365 me->cmd.size = 1 + dao->rows * dao->cols / 2;
5366
5367 /* Register the command object */
5368 trouble = gnm_command_push_undo (wbc, G_OBJECT (me));
5369
5370 if (!trouble)
5371 me->specs_owned = TRUE;
5372
5373 return trouble;
5374 }
5375
5376 /******************************************************************/
5377
5378 #define CMD_MERGE_DATA_TYPE (cmd_merge_data_get_type ())
5379 #define CMD_MERGE_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_MERGE_DATA_TYPE, CmdMergeData))
5380
5381 typedef struct {
5382 GnmCommand cmd;
5383 GnmValue *merge_zone;
5384 GSList *merge_fields;
5385 GSList *merge_data;
5386 GSList *sheet_list;
5387 Sheet *sheet;
5388 gint n;
5389 } CmdMergeData;
5390
MAKE_GNM_COMMAND(CmdMergeData,cmd_merge_data,NULL)5391 MAKE_GNM_COMMAND (CmdMergeData, cmd_merge_data, NULL)
5392
5393 static void
5394 cmd_merge_data_delete_sheets (gpointer data, gpointer success)
5395 {
5396 Sheet *sheet = data;
5397
5398 if (!command_undo_sheet_delete (sheet))
5399 *(gboolean *)success = FALSE;
5400 }
5401
5402 static gboolean
cmd_merge_data_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)5403 cmd_merge_data_undo (GnmCommand *cmd,
5404 G_GNUC_UNUSED WorkbookControl *wbc)
5405 {
5406 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5407 gboolean success = TRUE;
5408
5409 g_slist_foreach (me->sheet_list, cmd_merge_data_delete_sheets, &success);
5410 g_slist_free (me->sheet_list);
5411 me->sheet_list = NULL;
5412
5413 return FALSE;
5414 }
5415
5416 static gboolean
cmd_merge_data_redo(GnmCommand * cmd,WorkbookControl * wbc)5417 cmd_merge_data_redo (GnmCommand *cmd, WorkbookControl *wbc)
5418 {
5419 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5420 int i;
5421 GnmCellRegion *merge_contents;
5422 GnmRangeRef *cell = &me->merge_zone->v_range.cell;
5423 GnmPasteTarget pt;
5424 GSList *this_field = me->merge_fields;
5425 GSList *this_data = me->merge_data;
5426 Sheet *source_sheet = cell->a.sheet;
5427 GSList *target_sheet;
5428 GnmRange target_range;
5429 ColRowStateList *state_col;
5430 ColRowStateList *state_row;
5431
5432 range_init (&target_range, cell->a.col, cell->a.row,
5433 cell->b.col, cell->b.row);
5434 merge_contents = clipboard_copy_range (source_sheet, &target_range);
5435 state_col = colrow_get_states (source_sheet, TRUE, target_range.start.col,
5436 target_range.end.col);
5437 state_row = colrow_get_states (source_sheet, FALSE, target_range.start.row,
5438 target_range.end.row);
5439
5440 for (i = 0; i < me->n; i++) {
5441 Sheet *new_sheet;
5442
5443 new_sheet = workbook_sheet_add (me->sheet->workbook, -1,
5444 gnm_sheet_get_max_cols (me->sheet),
5445 gnm_sheet_get_max_rows (me->sheet));
5446 me->sheet_list = g_slist_prepend (me->sheet_list, new_sheet);
5447
5448 colrow_set_states (new_sheet, TRUE, target_range.start.col, state_col);
5449 colrow_set_states (new_sheet, FALSE, target_range.start.row, state_row);
5450 sheet_objects_dup (source_sheet, new_sheet, &target_range);
5451 clipboard_paste_region (merge_contents,
5452 paste_target_init (&pt, new_sheet, &target_range, PASTE_ALL_SHEET),
5453 GO_CMD_CONTEXT (wbc));
5454 }
5455 cellregion_unref (merge_contents);
5456 me->sheet_list = g_slist_reverse (me->sheet_list);
5457 colrow_state_list_destroy (state_col);
5458 colrow_state_list_destroy (state_row);
5459
5460 while (this_field) {
5461 int col_source, row_source;
5462 int col_target, row_target;
5463
5464 g_return_val_if_fail (this_data != NULL, TRUE);
5465 cell = &((GnmValue *)this_field->data)->v_range.cell;
5466 col_target = cell->a.col;
5467 row_target = cell->a.row;
5468
5469 cell = &((GnmValue *)this_data->data)->v_range.cell;
5470 col_source = cell->a.col;
5471 row_source = cell->a.row;
5472 source_sheet = cell->a.sheet;
5473
5474 target_sheet = me->sheet_list;
5475 while (target_sheet) {
5476 GnmCell *source_cell = sheet_cell_get (source_sheet,
5477 col_source, row_source);
5478 if (source_cell == NULL) {
5479 GnmCell *target_cell = sheet_cell_get ((Sheet *)target_sheet->data,
5480 col_target, row_target);
5481 if (target_cell != NULL)
5482 gnm_cell_set_value (target_cell,
5483 value_new_empty ());
5484 } else {
5485 GnmCell *target_cell = sheet_cell_fetch ((Sheet *)target_sheet->data,
5486 col_target, row_target);
5487 gnm_cell_set_value (target_cell,
5488 value_dup (source_cell->value));
5489 }
5490 target_sheet = target_sheet->next;
5491 row_source++;
5492 }
5493
5494 this_field = this_field->next;
5495 this_data = this_data->next;
5496 }
5497
5498 return FALSE;
5499 }
5500
5501 static void
cmd_merge_data_finalize(GObject * cmd)5502 cmd_merge_data_finalize (GObject *cmd)
5503 {
5504 CmdMergeData *me = CMD_MERGE_DATA (cmd);
5505
5506 value_release (me->merge_zone);
5507 me->merge_zone = NULL;
5508 range_list_destroy (me->merge_data);
5509 me->merge_data = NULL;
5510 range_list_destroy (me->merge_fields);
5511 me->merge_fields = NULL;
5512 g_slist_free (me->sheet_list);
5513 me->sheet_list = NULL;
5514 me->n = 0;
5515
5516 gnm_command_finalize (cmd);
5517 }
5518
5519 /**
5520 * cmd_merge_data:
5521 * @wbc: #WorkbookControl
5522 * @sheet: #Sheet
5523 * @merge_zone: (transfer full): #GnmValue
5524 * @merge_fields: (element-type GnmRange) (transfer full):
5525 * @merge_data: (element-type GnmRange) (transfer full):
5526 *
5527 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5528 **/
5529 gboolean
cmd_merge_data(WorkbookControl * wbc,Sheet * sheet,GnmValue * merge_zone,GSList * merge_fields,GSList * merge_data)5530 cmd_merge_data (WorkbookControl *wbc, Sheet *sheet,
5531 GnmValue *merge_zone, GSList *merge_fields, GSList *merge_data)
5532 {
5533 CmdMergeData *me;
5534 GnmRangeRef *cell;
5535
5536 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
5537 g_return_val_if_fail (merge_zone != NULL, TRUE);
5538 g_return_val_if_fail (merge_fields != NULL, TRUE);
5539 g_return_val_if_fail (merge_data != NULL, TRUE);
5540
5541 me = g_object_new (CMD_MERGE_DATA_TYPE, NULL);
5542
5543 me->cmd.sheet = sheet;
5544 me->sheet = sheet;
5545 me->cmd.size = 1 + g_slist_length (merge_fields);
5546 me->cmd.cmd_descriptor =
5547 g_strdup_printf (_("Merging data into %s"), value_peek_string (merge_zone));
5548
5549 me->merge_zone = merge_zone;
5550 me->merge_fields = merge_fields;
5551 me->merge_data = merge_data;
5552 me->sheet_list = NULL;
5553
5554 cell = &((GnmValue *)merge_data->data)->v_range.cell;
5555 me->n = cell->b.row - cell->a.row + 1;
5556
5557 /* Register the command object */
5558 return gnm_command_push_undo (wbc, G_OBJECT (me));
5559 }
5560
5561 /******************************************************************/
5562
5563 #define CMD_CHANGE_META_DATA_TYPE (cmd_change_summary_get_type ())
5564 #define CMD_CHANGE_META_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_CHANGE_META_DATA_TYPE, CmdChangeMetaData))
5565
5566 typedef struct {
5567 GnmCommand cmd;
5568 GSList *changed_props;
5569 GSList *removed_names;
5570 } CmdChangeMetaData;
5571
MAKE_GNM_COMMAND(CmdChangeMetaData,cmd_change_summary,NULL)5572 MAKE_GNM_COMMAND (CmdChangeMetaData, cmd_change_summary, NULL)
5573
5574 static gboolean
5575 cmd_change_summary_undo (GnmCommand *cmd, WorkbookControl *wbc)
5576 {
5577 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5578 GsfDocMetaData *meta = go_doc_get_meta_data (wb_control_get_doc (wbc));
5579 GSList *ptr, *old_vals = NULL, *dropped = NULL;
5580 GsfDocProp *prop;
5581 char const *name;
5582
5583 for (ptr = me->removed_names; ptr != NULL ; ptr = ptr->next) {
5584 if (NULL != (prop = gsf_doc_meta_data_steal (meta, ptr->data)))
5585 old_vals = g_slist_prepend (old_vals, prop);
5586 g_free (ptr->data);
5587 }
5588 g_slist_free (me->removed_names);
5589
5590 for (ptr = me->changed_props; ptr != NULL ; ptr = ptr->next) {
5591 name = gsf_doc_prop_get_name (ptr->data);
5592 if (NULL != (prop = gsf_doc_meta_data_steal (meta, name)))
5593 old_vals = g_slist_prepend (old_vals, prop);
5594 else
5595 dropped = g_slist_prepend (old_vals, g_strdup (name));
5596 gsf_doc_meta_data_store (meta, ptr->data);
5597 }
5598 g_slist_free (me->changed_props);
5599
5600 me->removed_names = dropped;
5601 me->changed_props = old_vals;
5602 go_doc_update_meta_data (wb_control_get_doc (wbc));
5603
5604 return FALSE;
5605 }
5606
5607 static gboolean
cmd_change_summary_redo(GnmCommand * cmd,WorkbookControl * wbc)5608 cmd_change_summary_redo (GnmCommand *cmd, WorkbookControl *wbc)
5609 {
5610 return cmd_change_summary_undo (cmd, wbc);
5611 }
5612
5613 static void
cmd_change_summary_finalize(GObject * cmd)5614 cmd_change_summary_finalize (GObject *cmd)
5615 {
5616 CmdChangeMetaData *me = CMD_CHANGE_META_DATA (cmd);
5617
5618 g_slist_free_full (me->changed_props, (GDestroyNotify)gsf_doc_prop_free);
5619 me->changed_props = NULL;
5620 g_slist_free_full (me->removed_names, g_free);
5621 me->removed_names = NULL;
5622
5623 gnm_command_finalize (cmd);
5624 }
5625
5626 /**
5627 * cmd_change_meta_data:
5628 * @wbc: #WorkbookControl
5629 * @changes: (element-type GsfDocMetaData) (transfer full): the changed metadata.
5630 * @removed: (element-type GsfDocMetaData) (transfer full): the removed metadata.
5631 *
5632 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5633 **/
5634 gboolean
cmd_change_meta_data(WorkbookControl * wbc,GSList * changes,GSList * removed)5635 cmd_change_meta_data (WorkbookControl *wbc, GSList *changes, GSList *removed)
5636 {
5637 CmdChangeMetaData *me = g_object_new (CMD_CHANGE_META_DATA_TYPE, NULL);
5638
5639 me->changed_props = changes;
5640 me->removed_names = removed;
5641 me->cmd.sheet = NULL;
5642
5643 me->cmd.size = g_slist_length (changes) + g_slist_length (removed);
5644 me->cmd.cmd_descriptor = g_strdup_printf (
5645 _("Changing workbook properties"));
5646 return gnm_command_push_undo (wbc, G_OBJECT (me));
5647 }
5648
5649 /******************************************************************/
5650
5651 #define CMD_OBJECT_RAISE_TYPE (cmd_object_raise_get_type ())
5652 #define CMD_OBJECT_RAISE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_OBJECT_RAISE_TYPE, CmdObjectRaise))
5653
5654 typedef struct {
5655 GnmCommand cmd;
5656 SheetObject *so;
5657 CmdObjectRaiseSelector dir;
5658 gint changed_positions;
5659 } CmdObjectRaise;
5660
MAKE_GNM_COMMAND(CmdObjectRaise,cmd_object_raise,NULL)5661 MAKE_GNM_COMMAND (CmdObjectRaise, cmd_object_raise, NULL)
5662
5663 static gboolean
5664 cmd_object_raise_redo (GnmCommand *cmd,
5665 G_GNUC_UNUSED WorkbookControl *wbc)
5666 {
5667 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5668 switch (me->dir) {
5669 case cmd_object_pull_to_front:
5670 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MAXINT/2);
5671 break;
5672 case cmd_object_pull_forward:
5673 me->changed_positions = sheet_object_adjust_stacking (me->so, 1);
5674 break;
5675 case cmd_object_push_backward:
5676 me->changed_positions = sheet_object_adjust_stacking (me->so, -1);
5677 break;
5678 case cmd_object_push_to_back:
5679 me->changed_positions = sheet_object_adjust_stacking (me->so, G_MININT/2);
5680 break;
5681 }
5682 return FALSE;
5683 }
5684
5685 static gboolean
cmd_object_raise_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)5686 cmd_object_raise_undo (GnmCommand *cmd,
5687 G_GNUC_UNUSED WorkbookControl *wbc)
5688 {
5689 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5690 if (me->changed_positions != 0)
5691 sheet_object_adjust_stacking (me->so, - me->changed_positions);
5692 return FALSE;
5693 }
5694
5695 static void
cmd_object_raise_finalize(GObject * cmd)5696 cmd_object_raise_finalize (GObject *cmd)
5697 {
5698 CmdObjectRaise *me = CMD_OBJECT_RAISE (cmd);
5699 g_object_unref (me->so);
5700 gnm_command_finalize (cmd);
5701 }
5702
5703 gboolean
cmd_object_raise(WorkbookControl * wbc,SheetObject * so,CmdObjectRaiseSelector dir)5704 cmd_object_raise (WorkbookControl *wbc, SheetObject *so, CmdObjectRaiseSelector dir)
5705 {
5706 CmdObjectRaise *me;
5707
5708 g_return_val_if_fail (GNM_IS_SO (so), TRUE);
5709
5710 me = g_object_new (CMD_OBJECT_RAISE_TYPE, NULL);
5711
5712 me->so = so;
5713 g_object_ref (so);
5714
5715 me->cmd.sheet = sheet_object_get_sheet (so);
5716 me->cmd.size = 1;
5717 switch (dir) {
5718 case cmd_object_pull_to_front:
5719 me->cmd.cmd_descriptor = g_strdup (_("Pull Object to the Front"));
5720 break;
5721 case cmd_object_pull_forward:
5722 me->cmd.cmd_descriptor = g_strdup (_("Pull Object Forward"));
5723 break;
5724 case cmd_object_push_backward:
5725 me->cmd.cmd_descriptor = g_strdup (_("Push Object Backward"));
5726 break;
5727 case cmd_object_push_to_back:
5728 me->cmd.cmd_descriptor = g_strdup (_("Push Object to the Back"));
5729 break;
5730 }
5731 me->dir = dir;
5732 me->changed_positions = 0;
5733
5734 return gnm_command_push_undo (wbc, G_OBJECT (me));
5735 }
5736
5737 /******************************************************************/
5738
5739 #define CMD_PRINT_SETUP_TYPE (cmd_print_setup_get_type ())
5740 #define CMD_PRINT_SETUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_PRINT_SETUP_TYPE, CmdPrintSetup))
5741
5742 typedef struct {
5743 GnmCommand cmd;
5744
5745 GSList *old_pi;
5746 GnmPrintInformation *new_pi;
5747 } CmdPrintSetup;
5748
MAKE_GNM_COMMAND(CmdPrintSetup,cmd_print_setup,NULL)5749 MAKE_GNM_COMMAND (CmdPrintSetup, cmd_print_setup, NULL)
5750
5751 static void
5752 update_sheet_graph_cb (Sheet *sheet)
5753 {
5754 g_return_if_fail (IS_SHEET (sheet) && sheet->sheet_type == GNM_SHEET_OBJECT);
5755
5756 sheet_object_graph_ensure_size (GNM_SO (sheet->sheet_objects->data));
5757 }
5758
5759 static gboolean
cmd_print_setup_undo(GnmCommand * cmd,WorkbookControl * wbc)5760 cmd_print_setup_undo (GnmCommand *cmd, WorkbookControl *wbc)
5761 {
5762 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5763 guint n, i;
5764 Workbook *book;
5765 GSList *infos;
5766
5767 g_return_val_if_fail (me->old_pi != NULL, TRUE);
5768
5769 if (me->cmd.sheet) {
5770 GnmPrintInformation *pi = me->old_pi->data;
5771 gnm_print_info_free (me->cmd.sheet->print_info);
5772 me->cmd.sheet->print_info = gnm_print_info_dup (pi);
5773 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5774 update_sheet_graph_cb (me->cmd.sheet);
5775 } else {
5776 book = wb_control_get_workbook(wbc);
5777 n = workbook_sheet_count (book);
5778 infos = me->old_pi;
5779 g_return_val_if_fail (g_slist_length (infos) == n, TRUE);
5780
5781 for (i = 0 ; i < n ; i++) {
5782 GnmPrintInformation *pi = infos->data;
5783 Sheet *sheet = workbook_sheet_by_index (book, i);
5784
5785 g_return_val_if_fail (infos != NULL, TRUE);
5786
5787 gnm_print_info_free (sheet->print_info);
5788 sheet->print_info = gnm_print_info_dup (pi);
5789 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5790 update_sheet_graph_cb (sheet);
5791 infos = infos->next;
5792 }
5793 }
5794 return FALSE;
5795 }
5796
5797 static gboolean
cmd_print_setup_redo(GnmCommand * cmd,WorkbookControl * wbc)5798 cmd_print_setup_redo (GnmCommand *cmd, WorkbookControl *wbc)
5799 {
5800 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5801 int n, i;
5802 Workbook *book;
5803 gboolean save_pis = (me->old_pi == NULL);
5804
5805 if (me->cmd.sheet) {
5806 if (save_pis)
5807 me->old_pi = g_slist_append (me->old_pi, me->cmd.sheet->print_info);
5808 else
5809 gnm_print_info_free (me->cmd.sheet->print_info);
5810 me->cmd.sheet->print_info = gnm_print_info_dup (me->new_pi);
5811 if (me->cmd.sheet->sheet_type == GNM_SHEET_OBJECT)
5812 update_sheet_graph_cb (me->cmd.sheet);
5813 } else {
5814 book = wb_control_get_workbook(wbc);
5815 n = workbook_sheet_count (book);
5816 for (i = 0 ; i < n ; i++) {
5817 Sheet *sheet = workbook_sheet_by_index (book, i);
5818 sheet_mark_dirty (sheet);
5819 if (save_pis)
5820 me->old_pi = g_slist_prepend (me->old_pi, sheet->print_info);
5821 else
5822 gnm_print_info_free (sheet->print_info);
5823 sheet->print_info = gnm_print_info_dup (me->new_pi);
5824 if (sheet->sheet_type == GNM_SHEET_OBJECT)
5825 update_sheet_graph_cb (sheet);
5826 }
5827 if (save_pis)
5828 me->old_pi = g_slist_reverse (me->old_pi);
5829 }
5830 return FALSE;
5831 }
5832
5833 static void
cmd_print_setup_finalize(GObject * cmd)5834 cmd_print_setup_finalize (GObject *cmd)
5835 {
5836 CmdPrintSetup *me = CMD_PRINT_SETUP (cmd);
5837 GSList *list = me->old_pi;
5838
5839 if (me->new_pi)
5840 gnm_print_info_free (me->new_pi);
5841 for (; list; list = list->next)
5842 gnm_print_info_free ((GnmPrintInformation *) list->data);
5843 g_slist_free (me->old_pi);
5844 gnm_command_finalize (cmd);
5845 }
5846
5847 gboolean
cmd_print_setup(WorkbookControl * wbc,Sheet * sheet,GnmPrintInformation const * pi)5848 cmd_print_setup (WorkbookControl *wbc, Sheet *sheet, GnmPrintInformation const *pi)
5849 {
5850 CmdPrintSetup *me;
5851
5852 me = g_object_new (CMD_PRINT_SETUP_TYPE, NULL);
5853
5854 me->cmd.sheet = sheet;
5855 me->cmd.size = 10;
5856 if (sheet)
5857 me->cmd.cmd_descriptor =
5858 g_strdup_printf (_("Page Setup For %s"), sheet->name_unquoted);
5859 else
5860 me->cmd.cmd_descriptor = g_strdup (_("Page Setup For All Sheets"));
5861 me->old_pi = NULL;
5862 me->new_pi = gnm_print_info_dup (pi);
5863
5864 return gnm_command_push_undo (wbc, G_OBJECT (me));
5865 }
5866
5867 /******************************************************************/
5868
5869 #define CMD_DEFINE_NAME_TYPE (cmd_define_name_get_type ())
5870 #define CMD_DEFINE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DEFINE_NAME_TYPE, CmdDefineName))
5871
5872 typedef struct {
5873 GnmCommand cmd;
5874
5875 GnmParsePos pp;
5876 char *name;
5877 GnmExprTop const *texpr;
5878 gboolean new_name;
5879 gboolean placeholder;
5880 } CmdDefineName;
5881
MAKE_GNM_COMMAND(CmdDefineName,cmd_define_name,NULL)5882 MAKE_GNM_COMMAND (CmdDefineName, cmd_define_name, NULL)
5883
5884 static gboolean
5885 cmd_define_name_undo (GnmCommand *cmd,
5886 WorkbookControl *wbc)
5887 {
5888 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5889 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5890 GnmExprTop const *texpr = nexpr->texpr;
5891
5892 gnm_expr_top_ref (texpr);
5893 if (me->new_name)
5894 expr_name_remove (nexpr);
5895 else if (me->placeholder)
5896 expr_name_downgrade_to_placeholder (nexpr);
5897 else
5898 expr_name_set_expr (nexpr, me->texpr); /* restore old def */
5899
5900 me->texpr = texpr;
5901
5902 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5903 wb_view_menus_update (each_wbv);
5904 });
5905 return FALSE;
5906 }
5907
5908 static gboolean
cmd_define_name_redo(GnmCommand * cmd,WorkbookControl * wbc)5909 cmd_define_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
5910 {
5911 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5912 GnmNamedExpr *nexpr = expr_name_lookup (&(me->pp), me->name);
5913
5914 me->new_name = (nexpr == NULL);
5915 me->placeholder = (nexpr != NULL)
5916 && expr_name_is_placeholder (nexpr);
5917
5918 if (me->new_name || me->placeholder) {
5919 char *err = NULL;
5920 nexpr = expr_name_add (&me->pp, me->name, me->texpr, &err, TRUE, NULL);
5921 if (nexpr == NULL) {
5922 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Name"), err);
5923 g_free (err);
5924 return TRUE;
5925 }
5926 me->texpr = NULL;
5927 } else { /* changing the definition */
5928 GnmExprTop const *tmp = nexpr->texpr;
5929 gnm_expr_top_ref (tmp);
5930 expr_name_set_expr (nexpr, me->texpr);
5931 me->texpr = tmp; /* store the old definition */
5932 }
5933 WORKBOOK_FOREACH_VIEW (wb_control_get_workbook (wbc), each_wbv, {
5934 wb_view_menus_update (each_wbv);
5935 });
5936
5937 return FALSE;
5938 }
5939
5940 static void
cmd_define_name_finalize(GObject * cmd)5941 cmd_define_name_finalize (GObject *cmd)
5942 {
5943 CmdDefineName *me = CMD_DEFINE_NAME (cmd);
5944
5945 g_free (me->name); me->name = NULL;
5946
5947 if (me->texpr) {
5948 gnm_expr_top_unref (me->texpr);
5949 me->texpr = NULL;
5950 }
5951
5952 gnm_command_finalize (cmd);
5953 }
5954
5955 /**
5956 * cmd_define_name:
5957 * @wbc:
5958 * @name:
5959 * @pp:
5960 * @texpr: (transfer full): #GnmExprTop
5961 * @descriptor: optional descriptor.
5962 *
5963 * If the @name has never been defined in context @pp create a new name
5964 * If it is a placeholder, assign @texpr to it and make it real
5965 * If it already exists as a real name just assign @expr.
5966 *
5967 * Returns: %TRUE if there was a problem, %FALSE otherwise.
5968 **/
5969 gboolean
cmd_define_name(WorkbookControl * wbc,char const * name,GnmParsePos const * pp,GnmExprTop const * texpr,char const * descriptor)5970 cmd_define_name (WorkbookControl *wbc, char const *name,
5971 GnmParsePos const *pp, GnmExprTop const *texpr,
5972 char const *descriptor)
5973 {
5974 CmdDefineName *me;
5975 GnmNamedExpr *nexpr;
5976 Sheet *sheet;
5977
5978 g_return_val_if_fail (name != NULL, TRUE);
5979 g_return_val_if_fail (pp != NULL, TRUE);
5980 g_return_val_if_fail (texpr != NULL, TRUE);
5981
5982 if (name[0] == '\0') {
5983 go_cmd_context_error_invalid
5984 (GO_CMD_CONTEXT (wbc), _("Defined Name"),
5985 _("An empty string is not allowed as defined name."));
5986 gnm_expr_top_unref (texpr);
5987 return TRUE;
5988 }
5989
5990 sheet = wb_control_cur_sheet (wbc);
5991 if (!expr_name_validate (name)) {
5992 gchar *err = g_strdup_printf
5993 (_("'%s' is not allowed as defined name."), name);
5994 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
5995 _("Defined Name"), err);
5996 g_free (err);
5997 gnm_expr_top_unref (texpr);
5998 return TRUE;
5999 }
6000
6001 if (expr_name_check_for_loop (name, texpr)) {
6002 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), name,
6003 _("has a circular reference"));
6004 gnm_expr_top_unref (texpr);
6005 return TRUE;
6006 }
6007 nexpr = expr_name_lookup (pp, name);
6008 if (nexpr != NULL && !expr_name_is_placeholder (nexpr) &&
6009 gnm_expr_top_equal (texpr, nexpr->texpr)) {
6010 gnm_expr_top_unref (texpr);
6011 return FALSE; /* expr is not changing, do nothing */
6012 }
6013
6014 me = g_object_new (CMD_DEFINE_NAME_TYPE, NULL);
6015 me->name = g_strdup (name);
6016 me->pp = *pp;
6017 me->texpr = texpr;
6018
6019 me->cmd.sheet = sheet;
6020 me->cmd.size = 1;
6021
6022 if (descriptor == NULL) {
6023 char const *tmp;
6024 GString *res;
6025
6026 /* Underscores need to be doubled. */
6027 res = g_string_new (NULL);
6028 for (tmp = name; *tmp; tmp++) {
6029 if (*tmp == '_')
6030 g_string_append_c (res, '_');
6031 g_string_append_c (res, *tmp);
6032 }
6033
6034 nexpr = expr_name_lookup (pp, name);
6035 if (nexpr == NULL || expr_name_is_placeholder (nexpr))
6036 me->cmd.cmd_descriptor =
6037 g_strdup_printf (_("Define Name %s"), res->str);
6038 else
6039 me->cmd.cmd_descriptor =
6040 g_strdup_printf (_("Update Name %s"), res->str);
6041 g_string_free (res, TRUE);
6042 } else
6043 me->cmd.cmd_descriptor = g_strdup (descriptor);
6044
6045 return gnm_command_push_undo (wbc, G_OBJECT (me));
6046 }
6047
6048 /******************************************************************/
6049
6050 #define CMD_REMOVE_NAME_TYPE (cmd_remove_name_get_type ())
6051 #define CMD_REMOVE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_REMOVE_NAME_TYPE, CmdRemoveName))
6052
6053 typedef struct {
6054 GnmCommand cmd;
6055
6056 GnmParsePos pp;
6057 GnmNamedExpr *nexpr;
6058 const GnmExprTop *texpr;
6059 } CmdRemoveName;
6060
MAKE_GNM_COMMAND(CmdRemoveName,cmd_remove_name,NULL)6061 MAKE_GNM_COMMAND (CmdRemoveName, cmd_remove_name, NULL)
6062
6063 static gboolean
6064 cmd_remove_name_undo (GnmCommand *cmd,
6065 G_GNUC_UNUSED WorkbookControl *wbc)
6066 {
6067 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6068 GnmNamedExpr *nexpr =
6069 expr_name_add (&me->nexpr->pos, expr_name_name (me->nexpr),
6070 me->texpr, NULL, TRUE, NULL);
6071 if (nexpr) {
6072 me->texpr = NULL;
6073 expr_name_ref (nexpr);
6074 expr_name_unref (me->nexpr);
6075 me->nexpr = nexpr;
6076 return FALSE;
6077 } else {
6078 g_warning ("Redefining name failed.");
6079 return TRUE;
6080 }
6081 }
6082
6083 static gboolean
cmd_remove_name_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6084 cmd_remove_name_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6085 {
6086 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6087
6088 me->texpr = me->nexpr->texpr;
6089 gnm_expr_top_ref (me->texpr);
6090 expr_name_downgrade_to_placeholder (me->nexpr);
6091
6092 return FALSE;
6093 }
6094
6095 static void
cmd_remove_name_finalize(GObject * cmd)6096 cmd_remove_name_finalize (GObject *cmd)
6097 {
6098 CmdRemoveName *me = CMD_REMOVE_NAME (cmd);
6099
6100 expr_name_unref (me->nexpr);
6101
6102 if (me->texpr) {
6103 gnm_expr_top_unref (me->texpr);
6104 me->texpr = NULL;
6105 }
6106
6107 gnm_command_finalize (cmd);
6108 }
6109
6110 /**
6111 * cmd_remove_name:
6112 * @wbc:
6113 * @nexpr: name to remove.
6114 *
6115 * Returns TRUE on error
6116 **/
6117 gboolean
cmd_remove_name(WorkbookControl * wbc,GnmNamedExpr * nexpr)6118 cmd_remove_name (WorkbookControl *wbc, GnmNamedExpr *nexpr)
6119 {
6120 CmdRemoveName *me;
6121
6122 g_return_val_if_fail (wbc != NULL, TRUE);
6123 g_return_val_if_fail (nexpr != NULL, TRUE);
6124 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6125
6126 expr_name_ref (nexpr);
6127
6128 me = g_object_new (CMD_REMOVE_NAME_TYPE, NULL);
6129 me->nexpr = nexpr;
6130 me->texpr = NULL;
6131 me->cmd.sheet = wb_control_cur_sheet (wbc);
6132 me->cmd.size = 1;
6133 me->cmd.cmd_descriptor = g_strdup_printf (_("Remove Name %s"),
6134 expr_name_name (nexpr));
6135
6136 return gnm_command_push_undo (wbc, G_OBJECT (me));
6137 }
6138
6139 /******************************************************************/
6140
6141 #define CMD_RESCOPE_NAME_TYPE (cmd_rescope_name_get_type ())
6142 #define CMD_RESCOPE_NAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_RESCOPE_NAME_TYPE, CmdRescopeName))
6143
6144 typedef struct {
6145 GnmCommand cmd;
6146 GnmNamedExpr *nexpr;
6147 Sheet *scope;
6148 } CmdRescopeName;
6149
MAKE_GNM_COMMAND(CmdRescopeName,cmd_rescope_name,NULL)6150 MAKE_GNM_COMMAND (CmdRescopeName, cmd_rescope_name, NULL)
6151
6152 static gboolean
6153 cmd_rescope_name_redo (GnmCommand *cmd, WorkbookControl *wbc)
6154 {
6155 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6156 Sheet *old_scope = me->nexpr->pos.sheet;
6157 char *err;
6158 GnmParsePos pp = me->nexpr->pos;
6159
6160 pp.sheet = me->scope;
6161 err = expr_name_set_pos (me->nexpr, &pp);
6162
6163 if (err != NULL) {
6164 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc), _("Change Scope of Name"), err);
6165 g_free (err);
6166 return TRUE;
6167 }
6168
6169 me->scope = old_scope;
6170 return FALSE;
6171 }
6172
6173 static gboolean
cmd_rescope_name_undo(GnmCommand * cmd,WorkbookControl * wbc)6174 cmd_rescope_name_undo (GnmCommand *cmd, WorkbookControl *wbc)
6175 {
6176 return cmd_rescope_name_redo (cmd, wbc);
6177 }
6178
6179
6180 static void
cmd_rescope_name_finalize(GObject * cmd)6181 cmd_rescope_name_finalize (GObject *cmd)
6182 {
6183 CmdRescopeName *me = CMD_RESCOPE_NAME (cmd);
6184
6185 expr_name_unref (me->nexpr);
6186 gnm_command_finalize (cmd);
6187 }
6188
6189 /**
6190 * cmd_rescope_name:
6191 * @wbc:
6192 * @nexpr: name to rescope.
6193 * @scope:
6194 *
6195 * Returns: %TRUE if there was a problem, %FALSE otherwise.
6196 **/
6197 gboolean
cmd_rescope_name(WorkbookControl * wbc,GnmNamedExpr * nexpr,Sheet * scope)6198 cmd_rescope_name (WorkbookControl *wbc, GnmNamedExpr *nexpr, Sheet *scope)
6199 {
6200 CmdRescopeName *me;
6201
6202 g_return_val_if_fail (wbc != NULL, TRUE);
6203 g_return_val_if_fail (nexpr != NULL, TRUE);
6204 g_return_val_if_fail (!expr_name_is_placeholder (nexpr), TRUE);
6205
6206 expr_name_ref (nexpr);
6207
6208 me = g_object_new (CMD_RESCOPE_NAME_TYPE, NULL);
6209 me->nexpr = nexpr;
6210 me->scope = scope;
6211 me->cmd.sheet = wb_control_cur_sheet (wbc);
6212 me->cmd.size = 1;
6213 me->cmd.cmd_descriptor = g_strdup_printf (_("Change Scope of Name %s"),
6214 expr_name_name (nexpr));
6215
6216 return gnm_command_push_undo (wbc, G_OBJECT (me));
6217 }
6218 /******************************************************************/
6219
6220 #define CMD_SCENARIO_ADD_TYPE (cmd_scenario_add_get_type ())
6221 #define CMD_SCENARIO_ADD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_ADD_TYPE, CmdScenarioAdd))
6222
6223 typedef struct {
6224 GnmCommand cmd;
6225 GnmScenario *scenario;
6226 } CmdScenarioAdd;
6227
MAKE_GNM_COMMAND(CmdScenarioAdd,cmd_scenario_add,NULL)6228 MAKE_GNM_COMMAND (CmdScenarioAdd, cmd_scenario_add, NULL)
6229
6230 static gboolean
6231 cmd_scenario_add_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6232 {
6233 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6234 GnmScenario *sc = g_object_ref (me->scenario);
6235 gnm_sheet_scenario_add (sc->sheet, sc);
6236 return FALSE;
6237 }
6238
6239 static gboolean
cmd_scenario_add_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6240 cmd_scenario_add_undo (GnmCommand *cmd,
6241 G_GNUC_UNUSED WorkbookControl *wbc)
6242 {
6243 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6244 GnmScenario *sc = me->scenario;
6245 gnm_sheet_scenario_remove (sc->sheet, sc);
6246 return FALSE;
6247 }
6248
6249 static void
cmd_scenario_add_finalize(GObject * cmd)6250 cmd_scenario_add_finalize (GObject *cmd)
6251 {
6252 CmdScenarioAdd *me = CMD_SCENARIO_ADD (cmd);
6253
6254 g_object_unref (me->scenario);
6255 gnm_command_finalize (cmd);
6256 }
6257
6258 /**
6259 * cmd_scenario_add: (skip)
6260 * @wbc:
6261 * @s: (transfer full):
6262 * @sheet:
6263 *
6264 * Returns: %TRUE if there was a problem, %FALSE otherwise.
6265 */
6266 gboolean
cmd_scenario_add(WorkbookControl * wbc,GnmScenario * s,Sheet * sheet)6267 cmd_scenario_add (WorkbookControl *wbc, GnmScenario *s, Sheet *sheet)
6268 {
6269 CmdScenarioAdd *me;
6270
6271 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6272 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6273
6274 me = g_object_new (CMD_SCENARIO_ADD_TYPE, NULL);
6275
6276 me->scenario = s; /* Take ownership */
6277 me->cmd.sheet = sheet;
6278 me->cmd.size = 1;
6279 me->cmd.cmd_descriptor = g_strdup (_("Add scenario"));
6280
6281 return gnm_command_push_undo (wbc, G_OBJECT (me));
6282 }
6283
6284 /******************************************************************/
6285
6286 #define CMD_SCENARIO_MNGR_TYPE (cmd_scenario_mngr_get_type ())
6287 #define CMD_SCENARIO_MNGR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SCENARIO_MNGR_TYPE, CmdScenarioMngr))
6288
6289 typedef struct {
6290 GnmCommand cmd;
6291 GnmScenario *sc;
6292 GOUndo *undo;
6293 } CmdScenarioMngr;
6294
MAKE_GNM_COMMAND(CmdScenarioMngr,cmd_scenario_mngr,NULL)6295 MAKE_GNM_COMMAND (CmdScenarioMngr, cmd_scenario_mngr, NULL)
6296
6297 static gboolean
6298 cmd_scenario_mngr_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6299 {
6300 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6301 if (!me->undo)
6302 me->undo = gnm_scenario_apply (me->sc);
6303 return FALSE;
6304 }
6305
6306 static gboolean
cmd_scenario_mngr_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6307 cmd_scenario_mngr_undo (GnmCommand *cmd,
6308 G_GNUC_UNUSED WorkbookControl *wbc)
6309 {
6310 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6311 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6312 g_object_unref (me->undo);
6313 me->undo = NULL;
6314 return FALSE;
6315 }
6316
6317 static void
cmd_scenario_mngr_finalize(GObject * cmd)6318 cmd_scenario_mngr_finalize (GObject *cmd)
6319 {
6320 CmdScenarioMngr *me = CMD_SCENARIO_MNGR (cmd);
6321
6322 g_object_unref (me->sc);
6323 if (me->undo)
6324 g_object_unref (me->undo);
6325
6326 gnm_command_finalize (cmd);
6327 }
6328
6329 gboolean
cmd_scenario_mngr(WorkbookControl * wbc,GnmScenario * sc,GOUndo * undo)6330 cmd_scenario_mngr (WorkbookControl *wbc, GnmScenario *sc, GOUndo *undo)
6331 {
6332 CmdScenarioMngr *me;
6333
6334 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6335 g_return_val_if_fail (GNM_IS_SCENARIO (sc), TRUE);
6336
6337 me = g_object_new (CMD_SCENARIO_MNGR_TYPE, NULL);
6338
6339 me->sc = g_object_ref (sc);
6340 me->undo = g_object_ref (undo);
6341 me->cmd.sheet = sc->sheet;
6342 me->cmd.size = 1;
6343 me->cmd.cmd_descriptor = g_strdup (_("Scenario Show"));
6344
6345 return gnm_command_push_undo (wbc, G_OBJECT (me));
6346 }
6347
6348 /******************************************************************/
6349
6350 #define CMD_DATA_SHUFFLE_TYPE (cmd_data_shuffle_get_type ())
6351 #define CMD_DATA_SHUFFLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_DATA_SHUFFLE_TYPE, CmdDataShuffle))
6352
6353 typedef struct {
6354 GnmCommand cmd;
6355 data_shuffling_t *ds;
6356 } CmdDataShuffle;
6357
MAKE_GNM_COMMAND(CmdDataShuffle,cmd_data_shuffle,NULL)6358 MAKE_GNM_COMMAND (CmdDataShuffle, cmd_data_shuffle, NULL)
6359
6360 static gboolean
6361 cmd_data_shuffle_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6362 {
6363 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6364
6365 data_shuffling_redo (me->ds);
6366 return FALSE;
6367 }
6368
6369 static gboolean
cmd_data_shuffle_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6370 cmd_data_shuffle_undo (GnmCommand *cmd,
6371 G_GNUC_UNUSED WorkbookControl *wbc)
6372 {
6373 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6374
6375 data_shuffling_redo (me->ds);
6376 return FALSE;
6377 }
6378
6379 static void
cmd_data_shuffle_finalize(GObject * cmd)6380 cmd_data_shuffle_finalize (GObject *cmd)
6381 {
6382 CmdDataShuffle *me = CMD_DATA_SHUFFLE (cmd);
6383
6384 data_shuffling_free (me->ds);
6385 gnm_command_finalize (cmd);
6386 }
6387
6388 gboolean
cmd_data_shuffle(WorkbookControl * wbc,data_shuffling_t * sc,Sheet * sheet)6389 cmd_data_shuffle (WorkbookControl *wbc, data_shuffling_t *sc, Sheet *sheet)
6390 {
6391 CmdDataShuffle *me;
6392
6393 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6394 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
6395
6396 me = g_object_new (CMD_DATA_SHUFFLE_TYPE, NULL);
6397
6398 me->ds = sc;
6399 me->cmd.sheet = sheet;
6400 me->cmd.size = 1;
6401 me->cmd.cmd_descriptor = g_strdup (_("Shuffle Data"));
6402
6403 return gnm_command_push_undo (wbc, G_OBJECT (me));
6404 }
6405
6406 /******************************************************************/
6407
6408 #define CMD_TEXT_TO_COLUMNS_TYPE (cmd_text_to_columns_get_type ())
6409 #define CMD_TEXT_TO_COLUMNS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TEXT_TO_COLUMNS_TYPE, CmdTextToColumns))
6410
6411 typedef struct {
6412 GnmCommand cmd;
6413
6414 GnmCellRegion *contents;
6415 GnmPasteTarget dst;
6416 GnmRange src;
6417 Sheet *src_sheet;
6418 ColRowStateList *saved_sizes;
6419 } CmdTextToColumns;
6420
MAKE_GNM_COMMAND(CmdTextToColumns,cmd_text_to_columns,NULL)6421 MAKE_GNM_COMMAND (CmdTextToColumns, cmd_text_to_columns, NULL)
6422
6423 static gboolean
6424 cmd_text_to_columns_impl (GnmCommand *cmd, WorkbookControl *wbc,
6425 gboolean is_undo)
6426 {
6427 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6428 GnmCellRegion *contents;
6429
6430 g_return_val_if_fail (me != NULL, TRUE);
6431 g_return_val_if_fail (me->contents != NULL, TRUE);
6432
6433 contents = clipboard_copy_range (me->dst.sheet, &me->dst.range);
6434 if (clipboard_paste_region (me->contents, &me->dst, GO_CMD_CONTEXT (wbc))) {
6435 /* There was a problem, avoid leaking */
6436 cellregion_unref (contents);
6437 return TRUE;
6438 }
6439
6440 cellregion_unref (me->contents);
6441
6442 if (is_undo) {
6443 colrow_set_states (me->dst.sheet, FALSE,
6444 me->dst.range.start.row, me->saved_sizes);
6445 colrow_state_list_destroy (me->saved_sizes);
6446 me->saved_sizes = NULL;
6447 } else {
6448 me->saved_sizes = colrow_get_states (me->dst.sheet,
6449 FALSE, me->dst.range.start.row, me->dst.range.end.row);
6450 rows_height_update (me->dst.sheet, &me->dst.range, FALSE);
6451 }
6452
6453 me->contents = contents;
6454
6455 /* Select the newly pasted contents (this queues a redraw) */
6456 select_range (me->dst.sheet, &me->dst.range, wbc);
6457
6458 return FALSE;
6459 }
6460
6461 static gboolean
cmd_text_to_columns_undo(GnmCommand * cmd,WorkbookControl * wbc)6462 cmd_text_to_columns_undo (GnmCommand *cmd, WorkbookControl *wbc)
6463 {
6464 return cmd_text_to_columns_impl (cmd, wbc, TRUE);
6465 }
6466
6467 static gboolean
cmd_text_to_columns_redo(GnmCommand * cmd,WorkbookControl * wbc)6468 cmd_text_to_columns_redo (GnmCommand *cmd, WorkbookControl *wbc)
6469 {
6470 return cmd_text_to_columns_impl (cmd, wbc, FALSE);
6471 }
6472
6473 static void
cmd_text_to_columns_finalize(GObject * cmd)6474 cmd_text_to_columns_finalize (GObject *cmd)
6475 {
6476 CmdTextToColumns *me = CMD_TEXT_TO_COLUMNS (cmd);
6477
6478 if (me->saved_sizes)
6479 me->saved_sizes = colrow_state_list_destroy (me->saved_sizes);
6480 if (me->contents) {
6481 cellregion_unref (me->contents);
6482 me->contents = NULL;
6483 }
6484 gnm_command_finalize (cmd);
6485 }
6486
6487 gboolean
cmd_text_to_columns(WorkbookControl * wbc,GnmRange const * src,Sheet * src_sheet,GnmRange const * target,Sheet * target_sheet,GnmCellRegion * contents)6488 cmd_text_to_columns (WorkbookControl *wbc,
6489 GnmRange const *src, Sheet *src_sheet,
6490 GnmRange const *target, Sheet *target_sheet,
6491 GnmCellRegion *contents)
6492 {
6493 CmdTextToColumns *me;
6494 char *src_range_name, *target_range_name;
6495
6496 g_return_val_if_fail (contents != NULL, TRUE);
6497
6498 src_range_name = undo_range_name (src_sheet, src);
6499 target_range_name = undo_range_name (target_sheet, target);
6500
6501 me = g_object_new (CMD_TEXT_TO_COLUMNS_TYPE, NULL);
6502
6503 me->cmd.sheet = (src_sheet == target_sheet ? src_sheet : NULL);
6504 me->cmd.size = 1; /* FIXME? */
6505 me->cmd.cmd_descriptor = g_strdup_printf (_("Text (%s) to Columns (%s)"),
6506 src_range_name,
6507 target_range_name);
6508 me->dst.range = *target;
6509 me->dst.sheet = target_sheet;
6510 me->dst.paste_flags = PASTE_CONTENTS | PASTE_FORMATS;
6511 me->src = *src;
6512 me->src_sheet = src_sheet;
6513 me->contents = contents;
6514 me->saved_sizes = NULL;
6515
6516 g_free (src_range_name);
6517 g_free (target_range_name);
6518
6519 /* Check array subdivision & merged regions */
6520 if (sheet_range_splits_region (target_sheet, &me->dst.range,
6521 NULL, GO_CMD_CONTEXT (wbc), me->cmd.cmd_descriptor)) {
6522 g_object_unref (me);
6523 return TRUE;
6524 }
6525
6526 return gnm_command_push_undo (wbc, G_OBJECT (me));
6527 }
6528
6529 /******************************************************************/
6530
6531 #define CMD_GENERIC_TYPE (cmd_generic_get_type ())
6532 #define CMD_GENERIC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GENERIC_TYPE, CmdGeneric))
6533
6534 typedef struct {
6535 GnmCommand cmd;
6536 GOUndo *undo, *redo;
6537 } CmdGeneric;
6538
MAKE_GNM_COMMAND(CmdGeneric,cmd_generic,NULL)6539 MAKE_GNM_COMMAND (CmdGeneric, cmd_generic, NULL)
6540
6541 static gboolean
6542 cmd_generic_undo (GnmCommand *cmd, WorkbookControl *wbc)
6543 {
6544 CmdGeneric *me = CMD_GENERIC (cmd);
6545 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
6546 return FALSE;
6547 }
6548
6549 static gboolean
cmd_generic_redo(GnmCommand * cmd,WorkbookControl * wbc)6550 cmd_generic_redo (GnmCommand *cmd, WorkbookControl *wbc)
6551 {
6552 CmdGeneric *me = CMD_GENERIC (cmd);
6553 go_undo_undo_with_data (me->redo, GO_CMD_CONTEXT (wbc));
6554 return FALSE;
6555 }
6556
6557 static void
cmd_generic_finalize(GObject * cmd)6558 cmd_generic_finalize (GObject *cmd)
6559 {
6560 CmdGeneric *me = CMD_GENERIC (cmd);
6561
6562 g_object_unref (me->undo);
6563 g_object_unref (me->redo);
6564
6565 gnm_command_finalize (cmd);
6566 }
6567
6568 gboolean
cmd_generic_with_size(WorkbookControl * wbc,const char * txt,int size,GOUndo * undo,GOUndo * redo)6569 cmd_generic_with_size (WorkbookControl *wbc, const char *txt,
6570 int size,
6571 GOUndo *undo, GOUndo *redo)
6572 {
6573 CmdGeneric *me;
6574
6575 g_return_val_if_fail (GO_IS_UNDO (undo), TRUE);
6576 g_return_val_if_fail (GO_IS_UNDO (redo), TRUE);
6577
6578 me = g_object_new (CMD_GENERIC_TYPE, NULL);
6579
6580 me->cmd.sheet = wb_control_cur_sheet (wbc);
6581 me->cmd.size = size;
6582 me->cmd.cmd_descriptor = g_strdup (txt);
6583
6584 me->undo = undo;
6585 me->redo = redo;
6586
6587 return gnm_command_push_undo (wbc, G_OBJECT (me));
6588 }
6589
6590 gboolean
cmd_generic(WorkbookControl * wbc,const char * txt,GOUndo * undo,GOUndo * redo)6591 cmd_generic (WorkbookControl *wbc, const char *txt, GOUndo *undo, GOUndo *redo)
6592 {
6593 return cmd_generic_with_size (wbc, txt, 1, undo, redo);
6594 }
6595
6596 /******************************************************************/
6597
6598 #define CMD_GOAL_SEEK_TYPE (cmd_goal_seek_get_type ())
6599 #define CMD_GOAL_SEEK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_GOAL_SEEK_TYPE, CmdGoalSeek))
6600
6601 typedef struct {
6602 GnmCommand cmd;
6603
6604 GnmCell *cell;
6605 GnmValue *ov;
6606 GnmValue *nv;
6607 } CmdGoalSeek;
6608
MAKE_GNM_COMMAND(CmdGoalSeek,cmd_goal_seek,NULL)6609 MAKE_GNM_COMMAND (CmdGoalSeek, cmd_goal_seek, NULL)
6610
6611 static gboolean
6612 cmd_goal_seek_impl (GnmCell *cell, GnmValue *value)
6613 {
6614 sheet_cell_set_value (cell, value_dup(value));
6615 return FALSE;
6616 }
6617
6618
6619 static gboolean
cmd_goal_seek_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6620 cmd_goal_seek_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6621 {
6622 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6623
6624 return cmd_goal_seek_impl (me->cell, me->ov);
6625 }
6626
6627 static gboolean
cmd_goal_seek_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6628 cmd_goal_seek_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6629 {
6630 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6631
6632 return cmd_goal_seek_impl (me->cell, me->nv);
6633 }
6634
6635 static void
cmd_goal_seek_finalize(GObject * cmd)6636 cmd_goal_seek_finalize (GObject *cmd)
6637 {
6638 CmdGoalSeek *me = CMD_GOAL_SEEK (cmd);
6639
6640 value_release (me->ov);
6641 me->ov = NULL;
6642 value_release (me->nv);
6643 me->nv = NULL;
6644
6645 gnm_command_finalize (cmd);
6646 }
6647
6648 gboolean
cmd_goal_seek(WorkbookControl * wbc,GnmCell * cell,GnmValue * ov,GnmValue * nv)6649 cmd_goal_seek (WorkbookControl *wbc, GnmCell *cell, GnmValue *ov, GnmValue *nv)
6650 {
6651 CmdGoalSeek *me;
6652 GnmRange range;
6653
6654 g_return_val_if_fail (cell != NULL, TRUE);
6655 g_return_val_if_fail (ov != NULL || nv != NULL, TRUE);
6656
6657 me = g_object_new (CMD_GOAL_SEEK_TYPE, NULL);
6658
6659 me->cmd.sheet = cell->base.sheet;
6660 me->cmd.size = 1;
6661 range_init_cellpos (&range, &cell->pos);
6662 me->cmd.cmd_descriptor = g_strdup_printf
6663 (_("Goal Seek (%s)"), undo_range_name (cell->base.sheet, &range));
6664
6665 me->cell = cell;
6666 me->ov = ov;
6667 me->nv = nv;
6668
6669 if (me->ov == NULL)
6670 me->ov = value_dup (cell->value);
6671 if (me->nv == NULL)
6672 me->nv = value_dup (cell->value);
6673
6674 return gnm_command_push_undo (wbc, G_OBJECT (me));
6675 }
6676
6677 /******************************************************************/
6678
6679 #if 0
6680 #define CMD_FREEZE_PANES_TYPE (cmd_freeze_panes_get_type ())
6681 #define CMD_FREEZE_PANES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_FREEZE_PANES_TYPE, CmdFreezePanes))
6682
6683 typedef struct {
6684 GnmCommand cmd;
6685
6686 SheetView *sv;
6687 GnmCellPos pos;
6688 } CmdFreezePanes;
6689
6690 MAKE_GNM_COMMAND (CmdFreezePanes, cmd_freeze_panes, NULL)
6691
6692 static gboolean
6693 cmd_freeze_panes_undo (GnmCommand *cmd, WorkbookControl *wbc)
6694 {
6695 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6696
6697 return FALSE;
6698 }
6699
6700 static gboolean
6701 cmd_freeze_panes_redo (GnmCommand *cmd, WorkbookControl *wbc)
6702 {
6703 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6704
6705 return FALSE;
6706 }
6707
6708 static void
6709 cmd_freeze_panes_finalize (GObject *cmd)
6710 {
6711 CmdFreezePanes *me = CMD_FREEZE_PANES (cmd);
6712
6713 gnm_command_finalize (cmd);
6714 }
6715
6716 /**
6717 * cmd_freeze_panes:
6718 * @wbc: where to report errors
6719 * @sv: the view to freeze
6720 * @frozen:
6721 * @unfrozen:
6722 *
6723 * Returns: %TRUE if there was a problem, %FALSE otherwise.
6724 **/
6725 gboolean
6726 cmd_freeze_panes (WorkbookControl *wbc, SheetView *sv,
6727 GnmCellPos const *frozen, GnmCellPos const *unfrozen)
6728 {
6729 CmdFreezePanes *me;
6730
6731 g_return_val_if_fail (name != NULL, TRUE);
6732 g_return_val_if_fail (pp != NULL, TRUE);
6733 g_return_val_if_fail (expr != NULL, TRUE);
6734
6735 me = g_object_new (CMD_FREEZE_PANES_TYPE, NULL);
6736 me->sv = sv;
6737 me->frozen = f;
6738 me->unfrozen = expr;
6739 return gnm_command_push_undo (wbc, G_OBJECT (me));
6740 }
6741
6742 #endif
6743
6744
6745 /******************************************************************/
6746
6747
6748 #define CMD_TABULATE_TYPE (cmd_tabulate_get_type ())
6749 #define CMD_TABULATE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TABULATE_TYPE, CmdTabulate))
6750
6751 typedef struct {
6752 GnmCommand cmd;
6753 GSList *sheet_idx;
6754 GnmTabulateInfo *data;
6755 } CmdTabulate;
6756
MAKE_GNM_COMMAND(CmdTabulate,cmd_tabulate,NULL)6757 MAKE_GNM_COMMAND (CmdTabulate, cmd_tabulate, NULL)
6758
6759 static gint
6760 cmd_tabulate_cmp_f (gconstpointer a,
6761 gconstpointer b)
6762 {
6763 guint const a_val = GPOINTER_TO_INT (a);
6764 guint const b_val = GPOINTER_TO_INT (b);
6765
6766 if (a_val > b_val)
6767 return -1;
6768 if (a_val < b_val)
6769 return 1;
6770 return 0;
6771 }
6772
6773 static gboolean
cmd_tabulate_undo(GnmCommand * cmd,WorkbookControl * wbc)6774 cmd_tabulate_undo (GnmCommand *cmd, WorkbookControl *wbc)
6775 {
6776 CmdTabulate *me = CMD_TABULATE (cmd);
6777 GSList *l;
6778 gboolean res = TRUE;
6779
6780 me->sheet_idx = g_slist_sort (me->sheet_idx,
6781 cmd_tabulate_cmp_f);
6782
6783 for (l = me->sheet_idx; l != NULL; l = l->next) {
6784 int i = GPOINTER_TO_INT (l->data);
6785 Sheet *new_sheet =
6786 workbook_sheet_by_index (wb_control_get_workbook (wbc),
6787 i);
6788 res = res && command_undo_sheet_delete (new_sheet);
6789 }
6790 return !res;
6791 }
6792
6793 static gboolean
cmd_tabulate_redo(GnmCommand * cmd,WorkbookControl * wbc)6794 cmd_tabulate_redo (GnmCommand *cmd, WorkbookControl *wbc)
6795 {
6796 CmdTabulate *me = CMD_TABULATE (cmd);
6797
6798 g_slist_free (me->sheet_idx);
6799 me->sheet_idx = do_tabulation (wbc, me->data);
6800
6801 return (me->sheet_idx == NULL);
6802 }
6803
6804 static void
cmd_tabulate_finalize(GObject * cmd)6805 cmd_tabulate_finalize (GObject *cmd)
6806 {
6807 CmdTabulate *me = CMD_TABULATE (cmd);
6808
6809 g_free (me->data->cells);
6810 g_free (me->data->minima);
6811 g_free (me->data->maxima);
6812 g_free (me->data->steps);
6813 g_free (me->data);
6814 gnm_command_finalize (cmd);
6815 }
6816
6817 gboolean
cmd_tabulate(WorkbookControl * wbc,gpointer data)6818 cmd_tabulate (WorkbookControl *wbc, gpointer data)
6819 {
6820 CmdTabulate *me;
6821
6822 g_return_val_if_fail (data != NULL, TRUE);
6823
6824 me = g_object_new (CMD_TABULATE_TYPE, NULL);
6825
6826 me->cmd.sheet = NULL;
6827 me->cmd.size = 1;
6828 me->cmd.cmd_descriptor =
6829 g_strdup_printf (_("Tabulating Dependencies"));
6830 me->data = data;
6831 me->sheet_idx = NULL;
6832
6833 return gnm_command_push_undo (wbc, G_OBJECT (me));
6834 }
6835
6836 /******************************************************************/
6837
6838 #define CMD_SO_GRAPH_CONFIG_TYPE (cmd_so_graph_config_get_type ())
6839 #define CMD_SO_GRAPH_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_GRAPH_CONFIG_TYPE, CmdSOGraphConfig))
6840
6841 typedef struct {
6842 GnmCommand cmd;
6843 SheetObject *so;
6844 GogGraph *new_graph;
6845 GogGraph *old_graph;
6846 } CmdSOGraphConfig;
6847
MAKE_GNM_COMMAND(CmdSOGraphConfig,cmd_so_graph_config,NULL)6848 MAKE_GNM_COMMAND (CmdSOGraphConfig, cmd_so_graph_config, NULL)
6849
6850 static gboolean
6851 cmd_so_graph_config_redo (GnmCommand *cmd,
6852 G_GNUC_UNUSED WorkbookControl *wbc)
6853 {
6854 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6855 sheet_object_graph_set_gog (me->so, me->new_graph);
6856 return FALSE;
6857 }
6858
6859 static gboolean
cmd_so_graph_config_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6860 cmd_so_graph_config_undo (GnmCommand *cmd,
6861 G_GNUC_UNUSED WorkbookControl *wbc)
6862 {
6863 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6864 sheet_object_graph_set_gog (me->so, me->old_graph);
6865 return FALSE;
6866 }
6867
6868 static void
cmd_so_graph_config_finalize(GObject * cmd)6869 cmd_so_graph_config_finalize (GObject *cmd)
6870 {
6871 CmdSOGraphConfig *me = CMD_SO_GRAPH_CONFIG (cmd);
6872
6873 g_object_unref (me->so);
6874 g_object_unref (me->new_graph);
6875 g_object_unref (me->old_graph);
6876
6877 gnm_command_finalize (cmd);
6878 }
6879
6880 gboolean
cmd_so_graph_config(WorkbookControl * wbc,SheetObject * so,GObject * n_graph,GObject * o_graph)6881 cmd_so_graph_config (WorkbookControl *wbc, SheetObject *so,
6882 GObject *n_graph, GObject *o_graph)
6883 {
6884 CmdSOGraphConfig *me;
6885
6886 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6887 g_return_val_if_fail (GNM_IS_SO_GRAPH (so), TRUE);
6888 g_return_val_if_fail (GOG_IS_GRAPH (n_graph), TRUE);
6889 g_return_val_if_fail (GOG_IS_GRAPH (o_graph), TRUE);
6890
6891 me = g_object_new (CMD_SO_GRAPH_CONFIG_TYPE, NULL);
6892
6893 me->so = so;
6894 g_object_ref (so);
6895
6896 me->new_graph = GOG_GRAPH (n_graph);
6897 g_object_ref (me->new_graph);
6898 me->old_graph = GOG_GRAPH (o_graph);
6899 g_object_ref (me->old_graph);
6900
6901 me->cmd.sheet = sheet_object_get_sheet (so);
6902 me->cmd.size = 10;
6903 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Graph"));
6904
6905 return gnm_command_push_undo (wbc, G_OBJECT (me));
6906 }
6907
6908 /******************************************************************/
6909
6910 #define CMD_SO_COMPONENT_CONFIG_TYPE (cmd_so_component_config_get_type ())
6911 #define CMD_SO_COMPONENT_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_COMPONENT_CONFIG_TYPE, CmdSOComponentConfig))
6912
6913 typedef struct {
6914 GnmCommand cmd;
6915 SheetObject *so;
6916 GOComponent *new_obj;
6917 GOComponent *old_obj;
6918 } CmdSOComponentConfig;
6919
MAKE_GNM_COMMAND(CmdSOComponentConfig,cmd_so_component_config,NULL)6920 MAKE_GNM_COMMAND (CmdSOComponentConfig, cmd_so_component_config, NULL)
6921
6922 static gboolean
6923 cmd_so_component_config_redo (GnmCommand *cmd,
6924 G_GNUC_UNUSED WorkbookControl *wbc)
6925 {
6926 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6927 sheet_object_component_set_component (me->so, me->new_obj);
6928 return FALSE;
6929 }
6930
6931 static gboolean
cmd_so_component_config_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6932 cmd_so_component_config_undo (GnmCommand *cmd,
6933 G_GNUC_UNUSED WorkbookControl *wbc)
6934 {
6935 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6936 sheet_object_component_set_component (me->so, me->old_obj);
6937 return FALSE;
6938 }
6939
6940 static void
cmd_so_component_config_finalize(GObject * cmd)6941 cmd_so_component_config_finalize (GObject *cmd)
6942 {
6943 CmdSOComponentConfig *me = CMD_SO_COMPONENT_CONFIG (cmd);
6944
6945 g_object_unref (me->so);
6946 g_object_unref (me->new_obj);
6947 g_object_unref (me->old_obj);
6948
6949 gnm_command_finalize (cmd);
6950 }
6951
6952 gboolean
cmd_so_component_config(WorkbookControl * wbc,SheetObject * so,GObject * n_obj,GObject * o_obj)6953 cmd_so_component_config (WorkbookControl *wbc, SheetObject *so,
6954 GObject *n_obj, GObject *o_obj)
6955 {
6956 CmdSOComponentConfig *me;
6957
6958 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
6959 g_return_val_if_fail (GNM_IS_SO_COMPONENT (so), TRUE);
6960 g_return_val_if_fail (GO_IS_COMPONENT (n_obj), TRUE);
6961 g_return_val_if_fail (GO_IS_COMPONENT (o_obj), TRUE);
6962
6963 me = g_object_new (CMD_SO_COMPONENT_CONFIG_TYPE, NULL);
6964
6965 me->so = so;
6966 g_object_ref (so);
6967
6968 me->new_obj = GO_COMPONENT (g_object_ref (n_obj));
6969 me->old_obj = GO_COMPONENT (g_object_ref (o_obj));
6970
6971 me->cmd.sheet = sheet_object_get_sheet (so);
6972 me->cmd.size = 10;
6973 me->cmd.cmd_descriptor = g_strdup (_("Reconfigure Object"));
6974
6975 return gnm_command_push_undo (wbc, G_OBJECT (me));
6976 }
6977
6978 /******************************************************************/
6979
6980 #define CMD_TOGGLE_RTL_TYPE (cmd_toggle_rtl_get_type ())
6981 #define CMD_TOGGLE_RTL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_TOGGLE_RTL_TYPE, CmdToggleRTL))
6982
6983 typedef GnmCommand CmdToggleRTL;
6984
MAKE_GNM_COMMAND(CmdToggleRTL,cmd_toggle_rtl,NULL)6985 MAKE_GNM_COMMAND (CmdToggleRTL, cmd_toggle_rtl, NULL)
6986
6987 static gboolean
6988 cmd_toggle_rtl_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6989 {
6990 go_object_toggle (cmd->sheet, "text-is-rtl");
6991 return FALSE;
6992 }
6993
6994 static gboolean
cmd_toggle_rtl_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)6995 cmd_toggle_rtl_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
6996 {
6997 return cmd_toggle_rtl_redo (cmd, wbc);
6998 }
6999
7000 static void
cmd_toggle_rtl_finalize(GObject * cmd)7001 cmd_toggle_rtl_finalize (GObject *cmd)
7002 {
7003 gnm_command_finalize (cmd);
7004 }
7005
7006 gboolean
cmd_toggle_rtl(WorkbookControl * wbc,Sheet * sheet)7007 cmd_toggle_rtl (WorkbookControl *wbc, Sheet *sheet)
7008 {
7009 CmdToggleRTL *me;
7010
7011 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7012 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
7013
7014 me = g_object_new (CMD_TOGGLE_RTL_TYPE, NULL);
7015 me->sheet = sheet;
7016 me->size = 1;
7017 me->cmd_descriptor = g_strdup (sheet->text_is_rtl ? _("Left to Right") : _("Right to Left"));
7018
7019 return gnm_command_push_undo (wbc, G_OBJECT (me));
7020 }
7021
7022 /******************************************************************/
7023
7024 #define CMD_SO_SET_VALUE_TYPE (cmd_so_set_value_get_type ())
7025 #define CMD_SO_SET_VALUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_VALUE_TYPE, CmdSOSetValue))
7026
7027 typedef struct {
7028 GnmCommand cmd;
7029 GnmCellRef ref;
7030 GnmValue *val;
7031 GOUndo *undo;
7032 } CmdSOSetValue;
7033
MAKE_GNM_COMMAND(CmdSOSetValue,cmd_so_set_value,NULL)7034 MAKE_GNM_COMMAND (CmdSOSetValue, cmd_so_set_value, NULL)
7035
7036 static gboolean
7037 cmd_so_set_value_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7038 {
7039 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7040 Sheet *sheet = me->ref.sheet;
7041 GnmCell *cell = sheet_cell_fetch (sheet, me->ref.col, me->ref.row);
7042
7043 sheet_cell_set_value (cell, value_dup (me->val));
7044 sheet_update (sheet);
7045
7046 return FALSE;
7047 }
7048
7049 static gboolean
cmd_so_set_value_undo(GnmCommand * cmd,WorkbookControl * wbc)7050 cmd_so_set_value_undo (GnmCommand *cmd, WorkbookControl *wbc)
7051 {
7052 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7053
7054 go_undo_undo_with_data (me->undo, GO_CMD_CONTEXT (wbc));
7055
7056 return FALSE;
7057 }
7058
7059 static void
cmd_so_set_value_finalize(GObject * cmd)7060 cmd_so_set_value_finalize (GObject *cmd)
7061 {
7062 CmdSOSetValue *me = CMD_SO_SET_VALUE (cmd);
7063
7064 value_release (me->val);
7065 g_object_unref (me->undo);
7066
7067 gnm_command_finalize (cmd);
7068 }
7069
7070 gboolean
cmd_so_set_value(WorkbookControl * wbc,const char * text,const GnmCellRef * pref,GnmValue * new_val,Sheet * sheet)7071 cmd_so_set_value (WorkbookControl *wbc,
7072 const char *text,
7073 const GnmCellRef *pref,
7074 GnmValue *new_val,
7075 Sheet *sheet)
7076 {
7077 CmdSOSetValue *me;
7078 GnmRange r;
7079
7080 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7081
7082 r.start.col = r.end.col = pref->col;
7083 r.start.row = r.end.row = pref->row;
7084
7085 me = g_object_new (CMD_SO_SET_VALUE_TYPE, NULL);
7086 me->cmd.sheet = sheet;
7087 me->cmd.size = 1;
7088 me->cmd.cmd_descriptor = g_strdup (text);
7089 me->ref = *pref;
7090 me->val = new_val;
7091 me->undo = clipboard_copy_range_undo (pref->sheet, &r);
7092
7093 return gnm_command_push_undo (wbc, G_OBJECT (me));
7094 }
7095
7096 /******************************************************************/
7097
7098 #define CMD_HYPERLINK_TYPE (cmd_hyperlink_get_type ())
7099 #define CMD_HYPERLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_HYPERLINK_TYPE, CmdHyperlink))
7100
7101 typedef struct {
7102 GnmCommand cmd;
7103 GSList *selection;
7104 GnmStyle *new_style;
7105 char *opt_content;
7106 GOUndo *undo;
7107 gboolean update_size;
7108 } CmdHyperlink;
7109
7110 static void
cmd_hyperlink_repeat(GnmCommand const * cmd,WorkbookControl * wbc)7111 cmd_hyperlink_repeat (GnmCommand const *cmd, WorkbookControl *wbc)
7112 {
7113 CmdHyperlink const *orig = (CmdHyperlink const *) cmd;
7114
7115 if (orig->new_style)
7116 gnm_style_ref (orig->new_style);
7117
7118 cmd_selection_hyperlink (wbc, orig->new_style, NULL,
7119 g_strdup (orig->opt_content));
7120 }
MAKE_GNM_COMMAND(CmdHyperlink,cmd_hyperlink,cmd_hyperlink_repeat)7121 MAKE_GNM_COMMAND (CmdHyperlink, cmd_hyperlink, cmd_hyperlink_repeat)
7122
7123 static gboolean
7124 cmd_hyperlink_undo (GnmCommand *cmd, WorkbookControl *wbc)
7125 {
7126 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7127 Workbook *wb = wb_control_get_workbook (wbc);
7128
7129 if (me->undo) {
7130 go_undo_undo (me->undo);
7131 g_clear_object (&me->undo);
7132 }
7133
7134 select_selection (me->cmd.sheet, me->selection, wbc);
7135
7136 WORKBOOK_FOREACH_CONTROL (wb, view, ctl, {
7137 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS);
7138 });
7139
7140 return FALSE;
7141 }
7142
7143 static GnmValue *
cb_hyperlink_set_text(GnmCellIter const * iter,gpointer user)7144 cb_hyperlink_set_text (GnmCellIter const *iter, gpointer user)
7145 {
7146 CmdHyperlink *me = user;
7147 GnmCell *cell = iter->cell;
7148
7149 if (cell == NULL)
7150 cell = sheet_cell_fetch (iter->pp.sheet,
7151 iter->pp.eval.col,
7152 iter->pp.eval.row);
7153
7154 /* We skip non-empty cells. */
7155 if (gnm_cell_is_empty (cell) &&
7156 !gnm_cell_is_nonsingleton_array (cell)) {
7157 sheet_cell_set_value (cell, value_new_string (me->opt_content));
7158 if (me->update_size)
7159 me->cmd.size++;
7160 }
7161
7162 return NULL;
7163 }
7164
7165 static gboolean
cmd_hyperlink_redo(GnmCommand * cmd,WorkbookControl * wbc)7166 cmd_hyperlink_redo (GnmCommand *cmd, WorkbookControl *wbc)
7167 {
7168 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7169 GSList *l;
7170 Workbook *wb = wb_control_get_workbook (wbc);
7171 Sheet *sheet;
7172
7173 g_return_val_if_fail (me != NULL, TRUE);
7174
7175 sheet = me->cmd.sheet;
7176
7177 /* Check for locked cells */
7178 if (cmd_selection_is_locked_effective (sheet, me->selection,
7179 wbc, _("Changing Hyperlink")))
7180 return TRUE;
7181
7182 me->undo = clipboard_copy_ranges_undo (sheet, me->selection);
7183
7184 for (l = me->selection; l; l = l->next) {
7185 GnmRange const *r = l->data;
7186
7187 if (me->new_style) {
7188 gnm_style_ref (me->new_style);
7189 sheet_apply_style (sheet, r, me->new_style);
7190 sheet_flag_style_update_range (sheet, r);
7191 }
7192
7193 if (me->opt_content) {
7194 sheet_foreach_cell_in_range (sheet, CELL_ITER_ALL, r,
7195 cb_hyperlink_set_text,
7196 me);
7197 }
7198 }
7199 me->update_size = FALSE;
7200
7201 sheet_redraw_all (sheet, FALSE);
7202 sheet_mark_dirty (sheet);
7203
7204 select_selection (sheet, me->selection, wbc);
7205
7206 WORKBOOK_FOREACH_CONTROL (wb, view, ctl,
7207 wb_control_menu_state_update (ctl, MS_COMMENT_LINKS););
7208
7209 return FALSE;
7210 }
7211
7212 static void
cmd_hyperlink_finalize(GObject * cmd)7213 cmd_hyperlink_finalize (GObject *cmd)
7214 {
7215 CmdHyperlink *me = CMD_HYPERLINK (cmd);
7216
7217 g_clear_object (&me->undo);
7218
7219 if (me->new_style)
7220 gnm_style_unref (me->new_style);
7221 me->new_style = NULL;
7222
7223 range_fragment_free (me->selection);
7224 me->selection = NULL;
7225
7226 g_free (me->opt_content);
7227
7228 gnm_command_finalize (cmd);
7229 }
7230
7231 /**
7232 * cmd_selection_hyperlink:
7233 * @wbc: the workbook control.
7234 * @style: (transfer full): style to apply to the selection
7235 * @opt_translated_name: An optional name to use in place of 'Hyperlink Cells'
7236 * @opt_content: optional content for otherwise empty cells.
7237 *
7238 * Returns: %TRUE if there was a problem, %FALSE otherwise.
7239 **/
7240 gboolean
cmd_selection_hyperlink(WorkbookControl * wbc,GnmStyle * style,char const * opt_translated_name,char * opt_content)7241 cmd_selection_hyperlink (WorkbookControl *wbc,
7242 GnmStyle *style,
7243 char const *opt_translated_name,
7244 char *opt_content)
7245 {
7246 CmdHyperlink *me;
7247 SheetView *sv = wb_control_cur_sheet_view (wbc);
7248
7249 me = g_object_new (CMD_HYPERLINK_TYPE, NULL);
7250
7251 me->selection = selection_get_ranges (sv, FALSE); /* TRUE ? */
7252 me->new_style = style;
7253
7254 me->cmd.sheet = sv_sheet (sv);
7255 me->cmd.size = 1; /* Updated later. */
7256 me->update_size = TRUE;
7257
7258 me->opt_content = opt_content;
7259
7260 if (opt_translated_name == NULL) {
7261 char *names = undo_range_list_name (me->cmd.sheet, me->selection);
7262
7263 me->cmd.cmd_descriptor = g_strdup_printf (_("Changing hyperlink of %s"), names);
7264 g_free (names);
7265 } else
7266 me->cmd.cmd_descriptor = g_strdup (opt_translated_name);
7267
7268
7269 return gnm_command_push_undo (wbc, G_OBJECT (me));
7270 }
7271
7272 /******************************************************************/
7273
7274
7275 #define CMD_SO_SET_LINKS_TYPE (cmd_so_set_links_get_type ())
7276 #define CMD_SO_SET_LINKS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_LINKS_TYPE, CmdSOSetLink))
7277
7278 typedef struct {
7279 GnmCommand cmd;
7280 SheetObject *so;
7281 GnmExprTop const *output;
7282 GnmExprTop const *content;
7283 gboolean as_index;
7284 } CmdSOSetLink;
7285
MAKE_GNM_COMMAND(CmdSOSetLink,cmd_so_set_links,NULL)7286 MAKE_GNM_COMMAND (CmdSOSetLink, cmd_so_set_links, NULL)
7287
7288 static gboolean
7289 cmd_so_set_links_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7290 {
7291 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7292 GnmExprTop const *old_output;
7293 GnmExprTop const *old_content;
7294 gboolean old_as_index;
7295
7296 old_content = sheet_widget_list_base_get_content_link (me->so);
7297 old_output = sheet_widget_list_base_get_result_link (me->so);
7298 old_as_index = sheet_widget_list_base_result_type_is_index (me->so);
7299
7300 sheet_widget_list_base_set_links
7301 (me->so, me->output, me->content);
7302 if (old_as_index != me->as_index) {
7303 sheet_widget_list_base_set_result_type (me->so, me->as_index);
7304 me->as_index = old_as_index;
7305 }
7306 if (me->output)
7307 gnm_expr_top_unref (me->output);
7308 if (me->content)
7309 gnm_expr_top_unref (me->content);
7310 me->output = old_output;
7311 me->content = old_content;
7312
7313 return FALSE;
7314 }
7315
7316 static gboolean
cmd_so_set_links_undo(GnmCommand * cmd,WorkbookControl * wbc)7317 cmd_so_set_links_undo (GnmCommand *cmd, WorkbookControl *wbc)
7318 {
7319 return cmd_so_set_links_redo (cmd, wbc);
7320 }
7321
7322 static void
cmd_so_set_links_finalize(GObject * cmd)7323 cmd_so_set_links_finalize (GObject *cmd)
7324 {
7325 CmdSOSetLink *me = CMD_SO_SET_LINKS (cmd);
7326
7327 if (me->output)
7328 gnm_expr_top_unref (me->output);
7329 if (me->content)
7330 gnm_expr_top_unref (me->content);
7331 gnm_command_finalize (cmd);
7332 }
7333
7334 gboolean
cmd_so_set_links(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * output,GnmExprTop const * content,gboolean as_index)7335 cmd_so_set_links (WorkbookControl *wbc,
7336 SheetObject *so,
7337 GnmExprTop const *output,
7338 GnmExprTop const *content,
7339 gboolean as_index)
7340 {
7341 CmdSOSetLink *me;
7342
7343 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7344
7345 me = g_object_new (CMD_SO_SET_LINKS_TYPE, NULL);
7346 me->cmd.sheet = sheet_object_get_sheet (so);
7347 me->cmd.size = 1;
7348 me->cmd.cmd_descriptor = g_strdup (_("Configure List"));
7349 me->so = so;
7350 me->output = output;
7351 me->content = content;
7352 me->as_index = as_index;
7353
7354 return gnm_command_push_undo (wbc, G_OBJECT (me));
7355 }
7356
7357 /******************************************************************/
7358
7359
7360
7361 #define CMD_SO_SET_FRAME_LABEL_TYPE (cmd_so_set_frame_label_get_type ())
7362 #define CMD_SO_SET_FRAME_LABEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_FRAME_LABEL_TYPE, CmdSOSetFrameLabel))
7363
7364 typedef struct {
7365 GnmCommand cmd;
7366 SheetObject *so;
7367 char *old_label;
7368 char *new_label;
7369 } CmdSOSetFrameLabel;
7370
MAKE_GNM_COMMAND(CmdSOSetFrameLabel,cmd_so_set_frame_label,NULL)7371 MAKE_GNM_COMMAND (CmdSOSetFrameLabel, cmd_so_set_frame_label, NULL)
7372
7373 static gboolean
7374 cmd_so_set_frame_label_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7375 {
7376 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7377
7378 sheet_widget_frame_set_label (me->so, me->new_label);
7379
7380 return FALSE;
7381 }
7382
7383 static gboolean
cmd_so_set_frame_label_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7384 cmd_so_set_frame_label_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7385 {
7386 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7387
7388 sheet_widget_frame_set_label (me->so, me->old_label);
7389
7390 return FALSE;
7391 }
7392
7393 static void
cmd_so_set_frame_label_finalize(GObject * cmd)7394 cmd_so_set_frame_label_finalize (GObject *cmd)
7395 {
7396 CmdSOSetFrameLabel *me = CMD_SO_SET_FRAME_LABEL (cmd);
7397
7398 g_free (me->old_label);
7399 me->old_label = NULL;
7400
7401 g_free (me->new_label);
7402 me->new_label = NULL;
7403
7404 gnm_command_finalize (cmd);
7405 }
7406
7407 gboolean
cmd_so_set_frame_label(WorkbookControl * wbc,SheetObject * so,char * old_label,char * new_label)7408 cmd_so_set_frame_label (WorkbookControl *wbc,
7409 SheetObject *so,
7410 char *old_label, char *new_label )
7411 {
7412 CmdSOSetFrameLabel *me;
7413
7414 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7415
7416 me = g_object_new (CMD_SO_SET_FRAME_LABEL_TYPE, NULL);
7417 me->cmd.sheet = sheet_object_get_sheet (so);
7418 me->cmd.size = 1;
7419 me->cmd.cmd_descriptor = g_strdup (_("Set Frame Label"));
7420 me->so = so;
7421 me->old_label = old_label;
7422 me->new_label = new_label;
7423
7424 return gnm_command_push_undo (wbc, G_OBJECT (me));
7425 }
7426
7427 /******************************************************************/
7428 #define CMD_SO_SET_BUTTON_TYPE (cmd_so_set_button_get_type ())
7429 #define CMD_SO_SET_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_BUTTON_TYPE, CmdSOSetButton))
7430
7431 typedef struct {
7432 GnmCommand cmd;
7433 SheetObject *so;
7434 GnmExprTop const *new_link;
7435 GnmExprTop const *old_link;
7436 char *old_label;
7437 char *new_label;
7438 } CmdSOSetButton;
7439
MAKE_GNM_COMMAND(CmdSOSetButton,cmd_so_set_button,NULL)7440 MAKE_GNM_COMMAND (CmdSOSetButton, cmd_so_set_button, NULL)
7441
7442 static gboolean
7443 cmd_so_set_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7444 {
7445 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7446
7447 sheet_widget_button_set_link (me->so, me->new_link);
7448 sheet_widget_button_set_label (me->so, me->new_label);
7449
7450 return FALSE;
7451 }
7452
7453 static gboolean
cmd_so_set_button_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7454 cmd_so_set_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7455 {
7456 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7457
7458 sheet_widget_button_set_link (me->so, me->old_link);
7459 sheet_widget_button_set_label (me->so, me->old_label);
7460
7461 return FALSE;
7462 }
7463
7464 static void
cmd_so_set_button_finalize(GObject * cmd)7465 cmd_so_set_button_finalize (GObject *cmd)
7466 {
7467 CmdSOSetButton *me = CMD_SO_SET_BUTTON (cmd);
7468
7469 if (me->new_link)
7470 gnm_expr_top_unref (me->new_link);
7471 if (me->old_link)
7472 gnm_expr_top_unref (me->old_link);
7473 g_free (me->old_label);
7474 g_free (me->new_label);
7475 gnm_command_finalize (cmd);
7476 }
7477
7478 gboolean
cmd_so_set_button(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * lnk,char * old_label,char * new_label)7479 cmd_so_set_button (WorkbookControl *wbc,
7480 SheetObject *so, GnmExprTop const *lnk,
7481 char *old_label, char *new_label)
7482 {
7483 CmdSOSetButton *me;
7484
7485 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7486
7487 me = g_object_new (CMD_SO_SET_BUTTON_TYPE, NULL);
7488 me->cmd.sheet = sheet_object_get_sheet (so);
7489 me->cmd.size = 1;
7490 me->cmd.cmd_descriptor = g_strdup (_("Configure Button"));
7491 me->so = so;
7492 me->new_link = lnk;
7493 me->old_label = old_label;
7494 me->new_label = new_label;
7495
7496 me->old_link = sheet_widget_button_get_link (so);
7497
7498 return gnm_command_push_undo (wbc, G_OBJECT (me));
7499 }
7500
7501 /******************************************************************/
7502 #define CMD_SO_SET_RADIO_BUTTON_TYPE (cmd_so_set_radio_button_get_type ())
7503 #define CMD_SO_SET_RADIO_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_RADIO_BUTTON_TYPE, CmdSOSetRadioButton))
7504
7505 typedef struct {
7506 GnmCommand cmd;
7507 SheetObject *so;
7508 GnmExprTop const *new_link;
7509 GnmExprTop const *old_link;
7510 char *old_label;
7511 char *new_label;
7512 GnmValue *old_value;
7513 GnmValue *new_value;
7514 } CmdSOSetRadioButton;
7515
MAKE_GNM_COMMAND(CmdSOSetRadioButton,cmd_so_set_radio_button,NULL)7516 MAKE_GNM_COMMAND (CmdSOSetRadioButton, cmd_so_set_radio_button, NULL)
7517
7518 static gboolean
7519 cmd_so_set_radio_button_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7520 {
7521 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7522
7523 sheet_widget_radio_button_set_link (me->so, me->new_link);
7524 sheet_widget_radio_button_set_label (me->so, me->new_label);
7525 sheet_widget_radio_button_set_value (me->so, me->new_value);
7526
7527 return FALSE;
7528 }
7529
7530 static gboolean
cmd_so_set_radio_button_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7531 cmd_so_set_radio_button_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7532 {
7533 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7534
7535 sheet_widget_radio_button_set_link (me->so, me->old_link);
7536 sheet_widget_radio_button_set_label (me->so, me->old_label);
7537 sheet_widget_radio_button_set_value (me->so, me->old_value);
7538
7539 return FALSE;
7540 }
7541
7542 static void
cmd_so_set_radio_button_finalize(GObject * cmd)7543 cmd_so_set_radio_button_finalize (GObject *cmd)
7544 {
7545 CmdSOSetRadioButton *me = CMD_SO_SET_RADIO_BUTTON (cmd);
7546
7547 if (me->new_link)
7548 gnm_expr_top_unref (me->new_link);
7549 if (me->old_link)
7550 gnm_expr_top_unref (me->old_link);
7551 g_free (me->old_label);
7552 g_free (me->new_label);
7553 value_release (me->old_value);
7554 value_release (me->new_value);
7555 gnm_command_finalize (cmd);
7556 }
7557
7558 gboolean
cmd_so_set_radio_button(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * lnk,char * old_label,char * new_label,GnmValue * old_value,GnmValue * new_value)7559 cmd_so_set_radio_button (WorkbookControl *wbc,
7560 SheetObject *so, GnmExprTop const *lnk,
7561 char *old_label, char *new_label,
7562 GnmValue *old_value, GnmValue *new_value)
7563 {
7564 CmdSOSetRadioButton *me;
7565
7566 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7567
7568 me = g_object_new (CMD_SO_SET_RADIO_BUTTON_TYPE, NULL);
7569 me->cmd.sheet = sheet_object_get_sheet (so);
7570 me->cmd.size = 1;
7571 me->cmd.cmd_descriptor = g_strdup (_("Configure Radio Button"));
7572 me->so = so;
7573 me->new_link = lnk;
7574 me->old_label = old_label;
7575 me->new_label = new_label;
7576 me->old_value = old_value;
7577 me->new_value = new_value;
7578
7579 me->old_link = sheet_widget_radio_button_get_link (so);
7580
7581 return gnm_command_push_undo (wbc, G_OBJECT (me));
7582 }
7583
7584 /******************************************************************/
7585 #define CMD_SO_SET_CHECKBOX_TYPE (cmd_so_set_checkbox_get_type ())
7586 #define CMD_SO_SET_CHECKBOX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_CHECKBOX_TYPE, CmdSOSetCheckbox))
7587
7588 typedef struct {
7589 GnmCommand cmd;
7590 SheetObject *so;
7591 GnmExprTop const *new_link;
7592 GnmExprTop const *old_link;
7593 char *old_label;
7594 char *new_label;
7595 } CmdSOSetCheckbox;
7596
MAKE_GNM_COMMAND(CmdSOSetCheckbox,cmd_so_set_checkbox,NULL)7597 MAKE_GNM_COMMAND (CmdSOSetCheckbox, cmd_so_set_checkbox, NULL)
7598
7599 static gboolean
7600 cmd_so_set_checkbox_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7601 {
7602 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7603
7604 sheet_widget_checkbox_set_link (me->so, me->new_link);
7605 sheet_widget_checkbox_set_label (me->so, me->new_label);
7606
7607 return FALSE;
7608 }
7609
7610 static gboolean
cmd_so_set_checkbox_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7611 cmd_so_set_checkbox_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7612 {
7613 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7614
7615 sheet_widget_checkbox_set_link (me->so, me->old_link);
7616 sheet_widget_checkbox_set_label (me->so, me->old_label);
7617
7618 return FALSE;
7619 }
7620
7621 static void
cmd_so_set_checkbox_finalize(GObject * cmd)7622 cmd_so_set_checkbox_finalize (GObject *cmd)
7623 {
7624 CmdSOSetCheckbox *me = CMD_SO_SET_CHECKBOX (cmd);
7625
7626 if (me->new_link)
7627 gnm_expr_top_unref (me->new_link);
7628 if (me->old_link)
7629 gnm_expr_top_unref (me->old_link);
7630 g_free (me->old_label);
7631 g_free (me->new_label);
7632 gnm_command_finalize (cmd);
7633 }
7634
7635 gboolean
cmd_so_set_checkbox(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * lnk,char * old_label,char * new_label)7636 cmd_so_set_checkbox (WorkbookControl *wbc,
7637 SheetObject *so, GnmExprTop const *lnk,
7638 char *old_label, char *new_label)
7639 {
7640 CmdSOSetCheckbox *me;
7641
7642 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7643
7644 me = g_object_new (CMD_SO_SET_CHECKBOX_TYPE, NULL);
7645 me->cmd.sheet = sheet_object_get_sheet (so);
7646 me->cmd.size = 1;
7647 me->cmd.cmd_descriptor = g_strdup (_("Configure Checkbox"));
7648 me->so = so;
7649 me->new_link = lnk;
7650 me->old_label = old_label;
7651 me->new_label = new_label;
7652
7653 me->old_link = sheet_widget_checkbox_get_link (so);
7654
7655 return gnm_command_push_undo (wbc, G_OBJECT (me));
7656 }
7657
7658 /******************************************************************/
7659
7660 #define CMD_SO_SET_ADJUSTMENT_TYPE (cmd_so_set_adjustment_get_type ())
7661 #define CMD_SO_SET_ADJUSTMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_SO_SET_ADJUSTMENT_TYPE, CmdSOSetAdjustment))
7662
7663 typedef struct {
7664 GnmCommand cmd;
7665 SheetObject *so;
7666 GnmExprTop const *new_link;
7667 GnmExprTop const *old_link;
7668 double old_lower;
7669 double old_upper;
7670 double old_step;
7671 double old_page;
7672 gboolean old_horizontal;
7673 } CmdSOSetAdjustment;
7674
MAKE_GNM_COMMAND(CmdSOSetAdjustment,cmd_so_set_adjustment,NULL)7675 MAKE_GNM_COMMAND (CmdSOSetAdjustment, cmd_so_set_adjustment, NULL)
7676
7677 static void
7678 cmd_so_set_adjustment_adj (CmdSOSetAdjustment *me)
7679 {
7680 GtkAdjustment *adj = sheet_widget_adjustment_get_adjustment (me->so);
7681
7682 double old_lower = gtk_adjustment_get_lower (adj);
7683 double old_upper = gtk_adjustment_get_upper (adj);
7684 double old_step = gtk_adjustment_get_step_increment (adj);
7685 double old_page = gtk_adjustment_get_page_increment (adj);
7686 gboolean old_horizontal;
7687 g_object_get (G_OBJECT (me->so), "horizontal", &old_horizontal, NULL);
7688
7689 gtk_adjustment_configure (adj,
7690 gtk_adjustment_get_value (adj),
7691 me->old_lower,
7692 me->old_upper,
7693 me->old_step,
7694 me->old_page,
7695 gtk_adjustment_get_page_size (adj));
7696 g_object_set (G_OBJECT (me->so), "horizontal", me->old_horizontal, NULL);
7697
7698 me->old_lower = old_lower;
7699 me->old_upper = old_upper;
7700 me->old_step = old_step;
7701 me->old_page = old_page;
7702 me->old_horizontal = old_horizontal;
7703 }
7704
7705 static gboolean
cmd_so_set_adjustment_redo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7706 cmd_so_set_adjustment_redo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7707 {
7708 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7709
7710 sheet_widget_adjustment_set_link (me->so, me->new_link);
7711 cmd_so_set_adjustment_adj (me);
7712 return FALSE;
7713 }
7714
7715 static gboolean
cmd_so_set_adjustment_undo(GnmCommand * cmd,G_GNUC_UNUSED WorkbookControl * wbc)7716 cmd_so_set_adjustment_undo (GnmCommand *cmd, G_GNUC_UNUSED WorkbookControl *wbc)
7717 {
7718 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7719
7720 sheet_widget_adjustment_set_link (me->so, me->old_link);
7721 cmd_so_set_adjustment_adj (me);
7722
7723 return FALSE;
7724 }
7725
7726 static void
cmd_so_set_adjustment_finalize(GObject * cmd)7727 cmd_so_set_adjustment_finalize (GObject *cmd)
7728 {
7729 CmdSOSetAdjustment *me = CMD_SO_SET_ADJUSTMENT (cmd);
7730
7731 if (me->new_link)
7732 gnm_expr_top_unref (me->new_link);
7733 if (me->old_link)
7734 gnm_expr_top_unref (me->old_link);
7735 gnm_command_finalize (cmd);
7736 }
7737
7738 gboolean
cmd_so_set_adjustment(WorkbookControl * wbc,SheetObject * so,GnmExprTop const * lnk,gboolean horizontal,int lower,int upper,int step,int page,char const * undo_label)7739 cmd_so_set_adjustment (WorkbookControl *wbc,
7740 SheetObject *so, GnmExprTop const *lnk,
7741 gboolean horizontal,
7742 int lower, int upper,
7743 int step, int page,
7744 char const *undo_label)
7745 {
7746 CmdSOSetAdjustment *me;
7747
7748 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7749
7750 me = g_object_new (CMD_SO_SET_ADJUSTMENT_TYPE, NULL);
7751 me->cmd.sheet = sheet_object_get_sheet (so);
7752 me->cmd.size = 1;
7753 me->cmd.cmd_descriptor = g_strdup ((undo_label == NULL) ?
7754 _("Configure Adjustment") : _(undo_label));
7755 me->so = so;
7756 me->new_link = lnk;
7757 me->old_lower = lower;
7758 me->old_upper = upper;
7759 me->old_step = step;
7760 me->old_page = page;
7761 me->old_horizontal = horizontal;
7762
7763 me->old_link = sheet_widget_adjustment_get_link (so);
7764
7765 return gnm_command_push_undo (wbc, G_OBJECT (me));
7766 }
7767
7768 /******************************************************************/
7769
7770 gboolean
cmd_autofilter_add_remove(WorkbookControl * wbc)7771 cmd_autofilter_add_remove (WorkbookControl *wbc)
7772 {
7773 SheetView *sv = wb_control_cur_sheet_view (wbc);
7774 GnmFilter *f = gnm_sheet_view_editpos_in_filter (sv);
7775 gboolean add = (f == NULL);
7776 char *descr = NULL, *name = NULL;
7777 GOUndo *undo = NULL;
7778 GOUndo *redo = NULL;
7779 gboolean result;
7780
7781
7782 if (add) {
7783 GnmRange region;
7784 GnmRange const *src = selection_first_range (sv,
7785 GO_CMD_CONTEXT (wbc), _("Add Filter"));
7786 GnmFilter *f_old = NULL;
7787
7788 if (src == NULL)
7789 return TRUE;
7790
7791 f_old = gnm_sheet_filter_intersect_rows
7792 (sv->sheet, src->start.row, src->end.row);
7793
7794 if (f_old != NULL) {
7795 GnmRange *r = gnm_sheet_filter_can_be_extended
7796 (sv->sheet, f_old, src);
7797 if (r == NULL) {
7798 char *error;
7799 name = undo_range_name (sv->sheet, &(f_old->r));
7800 error = g_strdup_printf
7801 (_("Auto Filter blocked by %s"),
7802 name);
7803 g_free(name);
7804 go_cmd_context_error_invalid
7805 (GO_CMD_CONTEXT (wbc),
7806 _("AutoFilter"), error);
7807 g_free (error);
7808 return TRUE;
7809 }
7810 /* extending existing filter. */
7811 undo = go_undo_binary_new
7812 (gnm_filter_ref (f_old), sv->sheet,
7813 (GOUndoBinaryFunc) gnm_filter_attach,
7814 (GFreeFunc) gnm_filter_unref,
7815 NULL);
7816 redo = go_undo_unary_new
7817 (gnm_filter_ref (f_old),
7818 (GOUndoUnaryFunc) gnm_filter_remove,
7819 (GFreeFunc) gnm_filter_unref);
7820 gnm_filter_remove (f_old);
7821 region = *r;
7822 g_free (r);
7823 } else {
7824 /* if only one row is selected
7825 * assume that the user wants to
7826 * filter the region below this row. */
7827 region = *src;
7828 if (src->start.row == src->end.row)
7829 gnm_sheet_guess_region (sv->sheet, ®ion);
7830 if (region.start.row == region.end.row) {
7831 go_cmd_context_error_invalid
7832 (GO_CMD_CONTEXT (wbc),
7833 _("AutoFilter"),
7834 _("Requires more than 1 row"));
7835 return TRUE;
7836 }
7837 }
7838 f = gnm_filter_new (sv->sheet, ®ion, FALSE);
7839 if (f == NULL) {
7840 go_cmd_context_error_invalid
7841 (GO_CMD_CONTEXT (wbc),
7842 _("AutoFilter"),
7843 _("Unable to create Autofilter"));
7844 if (f_old)
7845 gnm_filter_attach (f_old, sv->sheet);
7846 return TRUE;
7847 }
7848
7849 if (f_old)
7850 gnm_filter_attach (f_old, sv->sheet);
7851
7852 redo = go_undo_combine (go_undo_binary_new
7853 (gnm_filter_ref (f), sv->sheet,
7854 (GOUndoBinaryFunc) gnm_filter_attach,
7855 (GFreeFunc) gnm_filter_unref,
7856 NULL), redo);
7857 undo = go_undo_combine (undo,
7858 go_undo_unary_new
7859 (f,
7860 (GOUndoUnaryFunc) gnm_filter_remove,
7861 (GFreeFunc) gnm_filter_unref));
7862
7863 name = undo_range_name (sv->sheet, &(f->r));
7864 descr = g_strdup_printf
7865 ((f_old == NULL) ? _("Add Autofilter to %s")
7866 : _("Extend Autofilter to %s"),
7867 name);
7868 } else {
7869 undo = go_undo_binary_new
7870 (gnm_filter_ref (f), sv->sheet,
7871 (GOUndoBinaryFunc) gnm_filter_attach,
7872 (GFreeFunc) gnm_filter_unref,
7873 NULL);
7874 redo = go_undo_unary_new
7875 (gnm_filter_ref (f),
7876 (GOUndoUnaryFunc) gnm_filter_remove,
7877 (GFreeFunc) gnm_filter_unref);
7878 name = undo_range_name (sv->sheet, &(f->r));
7879 descr = g_strdup_printf (_("Remove Autofilter from %s"),
7880 name);
7881 }
7882 result = cmd_generic (wbc, descr, undo, redo);
7883 g_free (name);
7884 g_free (descr);
7885
7886 return result;
7887 }
7888
7889
7890 /******************************************************************/
7891
cmd_autofilter_set_condition(WorkbookControl * wbc,GnmFilter * filter,unsigned i,GnmFilterCondition * cond)7892 gboolean cmd_autofilter_set_condition (WorkbookControl *wbc,
7893 GnmFilter *filter, unsigned i,
7894 GnmFilterCondition *cond)
7895 {
7896 char *descr = NULL, *name = NULL;
7897 GOUndo *undo = NULL;
7898 GOUndo *redo = NULL;
7899 gboolean result;
7900
7901 undo = gnm_undo_filter_set_condition_new (filter, i,
7902 NULL, TRUE);
7903 g_return_val_if_fail (undo != NULL, TRUE);
7904 redo = gnm_undo_filter_set_condition_new (filter, i,
7905 cond, FALSE);
7906 g_return_val_if_fail (redo != NULL, TRUE);
7907
7908 name = undo_range_name (filter->sheet, &(filter->r));
7909 descr = g_strdup_printf (_("Change filter condition for %s"),
7910 name);
7911
7912 result = cmd_generic (wbc, descr, undo, redo);
7913 g_free (name);
7914 g_free (descr);
7915
7916 return result;
7917 }
7918
7919
7920 /******************************************************************/
7921
7922 static void
cmd_page_breaks_set_breaks(Sheet * sheet,GnmPageBreaks const * breaks)7923 cmd_page_breaks_set_breaks (Sheet *sheet,
7924 GnmPageBreaks const *breaks)
7925 {
7926 print_info_set_breaks (sheet->print_info, gnm_page_breaks_dup (breaks));
7927
7928 SHEET_FOREACH_CONTROL (sheet, sv, sc, wb_control_menu_state_update (sc_wbc (sc), MS_PAGE_BREAKS););
7929 }
7930
7931 gboolean
cmd_page_breaks_clear(WorkbookControl * wbc,Sheet * sheet)7932 cmd_page_breaks_clear (WorkbookControl *wbc, Sheet *sheet)
7933 {
7934 GOUndo *undo = NULL;
7935 GOUndo *redo = NULL;
7936
7937 g_return_val_if_fail (GNM_IS_WBC (wbc), TRUE);
7938 g_return_val_if_fail (sheet != NULL, TRUE);
7939
7940 if (sheet->print_info->page_breaks.v != NULL) {
7941 redo = go_undo_binary_new
7942 (sheet,
7943 gnm_page_breaks_new (TRUE),
7944 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7945 NULL,
7946 (GFreeFunc) gnm_page_breaks_free);
7947 undo = go_undo_binary_new
7948 (sheet,
7949 gnm_page_breaks_dup
7950 (sheet->print_info->page_breaks.v),
7951 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7952 NULL,
7953 (GFreeFunc) gnm_page_breaks_free);
7954 }
7955
7956 if (sheet->print_info->page_breaks.h != NULL) {
7957 redo = go_undo_combine
7958 (redo,
7959 go_undo_binary_new
7960 (sheet,
7961 gnm_page_breaks_new (FALSE),
7962 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7963 NULL,
7964 (GFreeFunc) gnm_page_breaks_free));
7965
7966 undo = go_undo_combine
7967 (undo,
7968 go_undo_binary_new
7969 (sheet,
7970 gnm_page_breaks_dup
7971 (sheet->print_info->page_breaks.h),
7972 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
7973 NULL,
7974 (GFreeFunc) gnm_page_breaks_free));
7975 }
7976
7977 if (undo != NULL)
7978 return cmd_generic (wbc, _("Clear All Page Breaks"), undo, redo);
7979 else
7980 return TRUE;
7981 }
7982
7983 gboolean
cmd_page_break_toggle(WorkbookControl * wbc,Sheet * sheet,gboolean is_vert)7984 cmd_page_break_toggle (WorkbookControl *wbc, Sheet *sheet, gboolean is_vert)
7985 {
7986 SheetView const *sv = wb_control_cur_sheet_view (wbc);
7987 gint col = sv->edit_pos.col;
7988 gint row = sv->edit_pos.row;
7989 int rc = is_vert ? col : row;
7990 GnmPageBreaks *old, *new, *target;
7991 GnmPageBreakType type;
7992 char const *label;
7993 GOUndo *undo;
7994 GOUndo *redo;
7995
7996 target = is_vert ? sheet->print_info->page_breaks.v
7997 : sheet->print_info->page_breaks.h;
7998
7999 old = (target == NULL) ? gnm_page_breaks_new (is_vert)
8000 : gnm_page_breaks_dup (target);
8001 new = gnm_page_breaks_dup (old);
8002
8003 if (gnm_page_breaks_get_break (new, rc) != GNM_PAGE_BREAK_MANUAL) {
8004 type = GNM_PAGE_BREAK_MANUAL;
8005 label = is_vert ? _("Remove Column Page Break") : _("Remove Row Page Break");
8006 } else {
8007 type = GNM_PAGE_BREAK_NONE;
8008 label = is_vert ? _("Add Column Page Break") : _("Add Row Page Break");
8009 }
8010
8011 gnm_page_breaks_set_break (new, rc, type);
8012
8013 redo = go_undo_binary_new
8014 (sheet, new,
8015 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8016 NULL,
8017 (GFreeFunc) gnm_page_breaks_free);
8018 undo = go_undo_binary_new
8019 (sheet, old,
8020 (GOUndoBinaryFunc) cmd_page_breaks_set_breaks,
8021 NULL,
8022 (GFreeFunc) gnm_page_breaks_free);
8023
8024 return cmd_generic (wbc, label, undo, redo);
8025 }
8026
8027 /******************************************************************/
8028