Update Spanish translation
[gnumeric.git] / src / cmd-edit.c
blob2df3360bdc4d853dd16598f66c7bce406d6fce03
1 /*
2 * cmd-edit.c: Various commands to be used by the edit menu.
4 * Copyright (C) 2000 Jody Goldberg (jody@gnome.org)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
21 #include <gnumeric-config.h>
22 #include <glib/gi18n-lib.h>
23 #include <gnumeric.h>
24 #include <cmd-edit.h>
26 #include <application.h>
27 #include <command-context.h>
28 #include <workbook-control.h>
29 #include <workbook.h>
30 #include <sheet.h>
31 #include <sheet-view.h>
32 #include <cell.h>
33 #include <expr.h>
34 #include <dependent.h>
35 #include <selection.h>
36 #include <parse-util.h>
37 #include <ranges.h>
38 #include <commands.h>
39 #include <clipboard.h>
40 #include <value.h>
41 #include <wbc-gtk.h>
43 /**
44 * sv_select_cur_row:
45 * @sv: The sheet
47 * Selects an entire row
49 void
50 sv_select_cur_row (SheetView *sv)
52 GnmRange const *sel = selection_first_range (sv, NULL, NULL);
53 if (sel != NULL) {
54 GnmRange r = *sel;
55 sv_selection_reset (sv);
56 sv_selection_add_full
57 (sv,
58 sv->edit_pos.col, sv->edit_pos.row,
59 0, r.start.row, gnm_sheet_get_last_col (sv->sheet), r.end.row,
60 GNM_SELECTION_MODE_ADD);
61 sheet_update (sv->sheet);
65 /**
66 * sv_select_cur_col:
67 * @sv: The sheet
69 * Selects an entire column
71 void
72 sv_select_cur_col (SheetView *sv)
74 GnmRange const *sel = selection_first_range (sv, NULL, NULL);
75 if (sel != NULL) {
76 GnmRange r = *sel;
77 sv_selection_reset (sv);
78 sv_selection_add_full
79 (sv,
80 sv->edit_pos.col, sv->edit_pos.row,
81 r.start.col, 0, r.end.col, gnm_sheet_get_last_row (sv->sheet),
82 GNM_SELECTION_MODE_ADD);
83 sheet_update (sv->sheet);
87 /**
88 * sv_select_cur_array:
89 * @sv: The sheet
91 * If the editpos is part of an array clear the selection and select the array.
92 **/
93 void
94 sv_select_cur_array (SheetView *sv)
96 GnmRange a;
97 int const c = sv->edit_pos.col;
98 int const r = sv->edit_pos.row;
100 if (!gnm_cell_array_bound (sheet_cell_get (sv->sheet, c, r), &a))
101 return;
103 /* leave the edit pos where it is, select the entire array. */
104 sv_selection_reset (sv);
105 sv_selection_add_full (sv, c, r,
106 a.start.col, a.start.row, a.end.col, a.end.row,
107 GNM_SELECTION_MODE_ADD);
108 sheet_update (sv->sheet);
111 static gint
112 cb_compare_deps (gconstpointer a, gconstpointer b)
114 GnmCell const *cell_a = a;
115 GnmCell const *cell_b = b;
116 int tmp;
118 if (cell_a->base.sheet != cell_b->base.sheet)
119 return cell_a->base.sheet->index_in_wb - cell_b->base.sheet->index_in_wb;
121 tmp = cell_a->pos.row - cell_b->pos.row;
122 if (tmp != 0)
123 return tmp;
124 return cell_a->pos.col - cell_b->pos.col;
127 static void
128 cb_collect_deps (GnmDependent *dep, gpointer user)
130 if (dependent_is_cell (dep)) {
131 GList **list = (GList **)user;
132 *list = g_list_prepend (*list, dep);
137 * sv_select_cur_depends:
138 * @sv: The sheet
140 * Select all cells that depend on the expression in the current cell.
142 void
143 sv_select_cur_depends (SheetView *sv)
145 GnmCell *cur_cell, dummy;
146 GList *deps = NULL, *ptr = NULL;
148 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
150 cur_cell = sheet_cell_get (sv->sheet,
151 sv->edit_pos.col, sv->edit_pos.row);
152 if (cur_cell == NULL) {
153 dummy.base.sheet = sv_sheet (sv);
154 dummy.pos = sv->edit_pos;
155 cur_cell = &dummy;
158 cell_foreach_dep (cur_cell, cb_collect_deps, &deps);
159 if (deps == NULL)
160 return;
162 sv_selection_reset (sv);
164 /* Short circuit */
165 if (g_list_length (deps) == 1) {
166 GnmCell *cell = deps->data;
167 sv_selection_add_pos (sv, cell->pos.col, cell->pos.row,
168 GNM_SELECTION_MODE_ADD);
169 } else {
170 GnmRange *cur = NULL;
171 ptr = NULL;
173 /* Merge the sorted list of cells into rows */
174 for (deps = g_list_sort (deps, &cb_compare_deps) ; deps ; ) {
175 GnmCell *cell = deps->data;
177 if (cur == NULL ||
178 cur->end.row != cell->pos.row ||
179 cur->end.col+1 != cell->pos.col) {
180 if (cur)
181 ptr = g_list_prepend (ptr, cur);
182 cur = g_new (GnmRange, 1);
183 cur->start.row = cur->end.row = cell->pos.row;
184 cur->start.col = cur->end.col = cell->pos.col;
185 } else
186 cur->end.col = cell->pos.col;
188 deps = g_list_remove (deps, cell);
190 if (cur)
191 ptr = g_list_prepend (ptr, cur);
193 /* Merge the coalesced rows into ranges */
194 deps = ptr;
195 for (ptr = NULL ; deps ; ) {
196 GnmRange *r1 = deps->data;
197 GList *fwd;
199 for (fwd = deps->next ; fwd ; ) {
200 GnmRange *r2 = fwd->data;
202 if (r1->start.col == r2->start.col &&
203 r1->end.col == r2->end.col &&
204 r1->start.row-1 == r2->end.row) {
205 r1->start.row = r2->start.row;
206 g_free (fwd->data);
207 fwd = g_list_remove (fwd, r2);
208 } else
209 fwd = fwd->next;
212 ptr = g_list_prepend (ptr, r1);
213 deps = g_list_remove (deps, r1);
216 /* now select the ranges */
217 while (ptr) {
218 sv_selection_add_range (sv, ptr->data);
219 g_free (ptr->data);
220 ptr = g_list_remove (ptr, ptr->data);
223 sheet_update (sv->sheet);
227 * sv_select_cur_inputs:
228 * @sv: The sheet
230 * Select all cells that are direct potential inputs to the
231 * current cell.
233 void
234 sv_select_cur_inputs (SheetView *sv)
236 GnmCell *cell;
237 GSList *ranges, *ptr;
238 GnmEvalPos ep;
240 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
242 cell = sheet_cell_get (sv->sheet,
243 sv->edit_pos.col, sv->edit_pos.row);
244 if (cell == NULL || !gnm_cell_has_expr (cell))
245 return;
246 ranges = gnm_expr_top_get_ranges (cell->base.texpr);
247 if (ranges == NULL)
248 return;
250 ep.eval = sv->edit_pos;
251 ep.sheet = sv->sheet;
252 ep.dep = NULL;
254 sv_selection_reset (sv);
255 for (ptr = ranges ; ptr != NULL ; ptr = ptr->next) {
256 GnmValue *v = ptr->data;
257 GnmRangeRef const *r = value_get_rangeref (v);
259 #warning "FIXME: What do we do in these 3D cases?"
260 if ((r->a.sheet == r->b.sheet) &&
261 (r->a.sheet == NULL || r->a.sheet == sv->sheet)) {
262 gint row, col;
263 row = gnm_cellref_get_row (&r->a, &ep);
264 col = gnm_cellref_get_col (&r->a, &ep);
265 sv_selection_add_full
266 (sv, col, row, col, row,
267 gnm_cellref_get_col (&r->b, &ep),
268 gnm_cellref_get_row (&r->b, &ep),
269 GNM_SELECTION_MODE_ADD);
271 value_release (v);
273 g_slist_free (ranges);
275 sheet_update (sv->sheet);
279 * cmd_paste:
281 * Pastes the current cut buffer, copy buffer, or X selection to
282 * the destination sheet range.
284 * When pasting a cut the destination MUST be the same size as the src.
286 * When pasting a copy the destination can be a singleton, or an integer
287 * multiple of the size of the source. This is not tested here.
288 * Full undo support.
290 void
291 cmd_paste (WorkbookControl *wbc, GnmPasteTarget const *pt)
293 GnmCellRegion *content;
294 GnmRange const *src_range;
295 GnmRange dst;
297 g_return_if_fail (pt != NULL);
298 g_return_if_fail (IS_SHEET (pt->sheet));
300 dst = pt->range;
302 /* Check for locks */
303 if (cmd_cell_range_is_locked_effective (pt->sheet, &dst, wbc,
304 _("Paste")))
305 return ;
307 src_range = gnm_app_clipboard_area_get ();
308 content = gnm_app_clipboard_contents_get ();
310 if (content == NULL && src_range != NULL) {
311 /* Pasting a Cut */
312 GnmExprRelocateInfo rinfo;
313 Sheet *src_sheet = gnm_app_clipboard_sheet_get ();
315 /* Validate the size & shape of the target here. */
316 int const cols = (src_range->end.col - src_range->start.col);
317 int const rows = (src_range->end.row - src_range->start.row);
319 if (range_is_singleton (&dst)) {
320 dst.end.col = dst.start.col + cols;
321 dst.end.row = dst.start.row + rows;
322 } else if ((dst.end.col - dst.start.col) != cols ||
323 (dst.end.row - dst.start.row) != rows) {
325 char *msg = g_strdup_printf (
326 _("destination has a different shape (%dRx%dC) than the original (%dRx%dC)\n\n"
327 "Try selecting a single cell or an area of the same shape and size."),
328 (dst.end.row - dst.start.row)+1,
329 (dst.end.col - dst.start.col)+1,
330 rows+1, cols+1);
331 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
332 _("Unable to paste into selection"), msg);
333 g_free (msg);
334 return;
337 rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
338 rinfo.origin = *src_range;
339 rinfo.col_offset = dst.start.col - rinfo.origin.start.col;
340 rinfo.row_offset = dst.start.row - rinfo.origin.start.row;
341 rinfo.origin_sheet = src_sheet;
342 rinfo.target_sheet = pt->sheet;
344 if (!cmd_paste_cut (wbc, &rinfo, TRUE, NULL))
345 gnm_app_clipboard_clear (TRUE);
347 /* If this application has marked a selection use it */
348 } else if (content != NULL) {
349 cmd_paste_copy (wbc, pt, content);
350 /* We don't own the contents, so don't unref it. */
351 } else {
352 /* See if the control has access to information to paste */
353 wb_control_paste_from_selection (wbc, pt);
358 * cmd_paste_to_selection:
359 * @dest_sv: The sheet into which things should be pasted
360 * @paste_flags: special paste flags (eg transpose)
362 * Using the current selection as a target
363 * Full undo support.
365 void
366 cmd_paste_to_selection (WorkbookControl *wbc, SheetView *dest_sv, int paste_flags)
368 GnmRange const *r;
369 GnmPasteTarget pt;
371 r = selection_first_range (dest_sv, GO_CMD_CONTEXT (wbc), _("Paste"));
372 if (!r)
373 return;
375 pt.sheet = dest_sv->sheet;
376 pt.range = *r;
377 pt.paste_flags = paste_flags;
378 cmd_paste (wbc, &pt);
382 * cmd_shift_rows:
383 * @wbc: The error context.
384 * @sheet the sheet
385 * @col column marking the start of the shift
386 * @start_row first row
387 * @end_row end row
388 * @count numbers of columns to shift. negative numbers will
389 * delete count columns, positive number will insert
390 * count columns.
392 * Takes the cells in the region (col,start_row):(MAX_COL,end_row)
393 * and copies them @count units (possibly negative) to the right.
395 void
396 cmd_shift_rows (WorkbookControl *wbc, Sheet *sheet,
397 int col, int start_row, int end_row, int count)
399 GnmExprRelocateInfo rinfo;
400 char *desc;
402 rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
403 rinfo.col_offset = count;
404 rinfo.row_offset = 0;
405 rinfo.origin_sheet = rinfo.target_sheet = sheet;
406 rinfo.origin.start.row = start_row;
407 rinfo.origin.start.col = col;
408 rinfo.origin.end.row = end_row;
409 rinfo.origin.end.col = gnm_sheet_get_last_col (sheet);
412 if (count > 0) {
413 GnmRange r = rinfo.origin;
414 r.start.col = r.end.col - count + 1;
416 if (!sheet_is_region_empty (sheet, &r)) {
417 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
418 _("Inserting these cells would push data off the sheet. "
419 "Please enlarge the sheet first."));
420 return;
422 rinfo.origin.end.col -= count;
425 desc = g_strdup_printf ((start_row != end_row)
426 ? _("Shift rows %s")
427 : _("Shift row %s"),
428 rows_name (start_row, end_row));
429 cmd_paste_cut (wbc, &rinfo, FALSE, desc);
433 * cmd_shift_cols:
434 * @wbc: The error context.
435 * @sheet: the sheet
436 * @start_col: first column
437 * @end_col: end column
438 * @row: row marking the start of the shift
439 * @count: numbers of rows to shift. a negative numbers will
440 * delete count rows, positive number will insert
441 * count rows.
443 * Takes the cells in the region (start_col,row):(end_col,MAX_ROW)
444 * and copies them @count units (possibly negative) downwards.
446 void
447 cmd_shift_cols (WorkbookControl *wbc, Sheet *sheet,
448 int start_col, int end_col, int row, int count)
450 GnmExprRelocateInfo rinfo;
451 char *desc;
453 rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
454 rinfo.col_offset = 0;
455 rinfo.row_offset = count;
456 rinfo.origin_sheet = rinfo.target_sheet = sheet;
457 rinfo.origin.start.col = start_col;
458 rinfo.origin.start.row = row;
459 rinfo.origin.end.col = end_col;
460 rinfo.origin.end.row = gnm_sheet_get_last_row (sheet);
461 if (count > 0) {
462 GnmRange r = rinfo.origin;
463 r.start.row = r.end.row - count + 1;
465 if (!sheet_is_region_empty (sheet, &r)) {
466 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
467 _("Inserting these cells would push data off the sheet. "
468 "Please enlarge the sheet first."));
469 return;
471 rinfo.origin.end.row -= count;
474 desc = g_strdup_printf ((start_col != end_col)
475 ? _("Shift columns %s")
476 : _("Shift column %s"),
477 cols_name (start_col, end_col));
478 cmd_paste_cut (wbc, &rinfo, FALSE, desc);