Clipboard: check mime type before pasting image.
[gnumeric.git] / src / cmd-edit.c
blob48e93da75b3a5f4e001f095cc0eda3164c083ac8
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * cmd-edit.c: Various commands to be used by the edit menu.
5 * Copyright (C) 2000 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
24 #include "gnumeric.h"
25 #include "cmd-edit.h"
27 #include "application.h"
28 #include "command-context.h"
29 #include "workbook-control.h"
30 #include "workbook.h"
31 #include "sheet.h"
32 #include "sheet-view.h"
33 #include "cell.h"
34 #include "expr.h"
35 #include "dependent.h"
36 #include "selection.h"
37 #include "parse-util.h"
38 #include "ranges.h"
39 #include "commands.h"
40 #include "clipboard.h"
41 #include "value.h"
42 #include "wbc-gtk.h"
44 /**
45 * sv_select_cur_row:
46 * @sv: The sheet
48 * Selects an entire row
50 void
51 sv_select_cur_row (SheetView *sv)
53 GnmRange const *sel = selection_first_range (sv, NULL, NULL);
54 if (sel != NULL) {
55 GnmRange r = *sel;
56 sv_selection_reset (sv);
57 sv_selection_add_full
58 (sv,
59 sv->edit_pos.col, sv->edit_pos.row,
60 0, r.start.row, gnm_sheet_get_last_col (sv->sheet), r.end.row,
61 GNM_SELECTION_MODE_ADD);
62 sheet_update (sv->sheet);
66 /**
67 * sv_select_cur_col:
68 * @sv: The sheet
70 * Selects an entire column
72 void
73 sv_select_cur_col (SheetView *sv)
75 GnmRange const *sel = selection_first_range (sv, NULL, NULL);
76 if (sel != NULL) {
77 GnmRange r = *sel;
78 sv_selection_reset (sv);
79 sv_selection_add_full
80 (sv,
81 sv->edit_pos.col, sv->edit_pos.row,
82 r.start.col, 0, r.end.col, gnm_sheet_get_last_row (sv->sheet),
83 GNM_SELECTION_MODE_ADD);
84 sheet_update (sv->sheet);
88 /**
89 * sv_select_cur_array :
90 * @sv: The sheet
92 * If the editpos is part of an array clear the selection and select the array.
93 **/
94 void
95 sv_select_cur_array (SheetView *sv)
97 GnmRange a;
98 int const c = sv->edit_pos.col;
99 int const r = sv->edit_pos.row;
101 if (!gnm_cell_array_bound (sheet_cell_get (sv->sheet, c, r), &a))
102 return;
104 /* leave the edit pos where it is, select the entire array. */
105 sv_selection_reset (sv);
106 sv_selection_add_full (sv, c, r,
107 a.start.col, a.start.row, a.end.col, a.end.row,
108 GNM_SELECTION_MODE_ADD);
109 sheet_update (sv->sheet);
112 static gint
113 cb_compare_deps (gconstpointer a, gconstpointer b)
115 GnmCell const *cell_a = a;
116 GnmCell const *cell_b = b;
117 int tmp;
119 if (cell_a->base.sheet != cell_b->base.sheet)
120 return cell_a->base.sheet->index_in_wb - cell_b->base.sheet->index_in_wb;
122 tmp = cell_a->pos.row - cell_b->pos.row;
123 if (tmp != 0)
124 return tmp;
125 return cell_a->pos.col - cell_b->pos.col;
128 static void
129 cb_collect_deps (GnmDependent *dep, gpointer user)
131 if (dependent_is_cell (dep)) {
132 GList **list = (GList **)user;
133 *list = g_list_prepend (*list, dep);
138 * sv_select_cur_depends :
139 * @sv: The sheet
141 * Select all cells that depend on the expression in the current cell.
143 void
144 sv_select_cur_depends (SheetView *sv)
146 GnmCell *cur_cell, dummy;
147 GList *deps = NULL, *ptr = NULL;
149 g_return_if_fail (GNM_IS_SV (sv));
151 cur_cell = sheet_cell_get (sv->sheet,
152 sv->edit_pos.col, sv->edit_pos.row);
153 if (cur_cell == NULL) {
154 dummy.base.sheet = sv_sheet (sv);
155 dummy.pos = sv->edit_pos;
156 cur_cell = &dummy;
159 cell_foreach_dep (cur_cell, cb_collect_deps, &deps);
160 if (deps == NULL)
161 return;
163 sv_selection_reset (sv);
165 /* Short circuit */
166 if (g_list_length (deps) == 1) {
167 GnmCell *cell = deps->data;
168 sv_selection_add_pos (sv, cell->pos.col, cell->pos.row,
169 GNM_SELECTION_MODE_ADD);
170 } else {
171 GnmRange *cur = NULL;
172 ptr = NULL;
174 /* Merge the sorted list of cells into rows */
175 for (deps = g_list_sort (deps, &cb_compare_deps) ; deps ; ) {
176 GnmCell *cell = deps->data;
178 if (cur == NULL ||
179 cur->end.row != cell->pos.row ||
180 cur->end.col+1 != cell->pos.col) {
181 if (cur)
182 ptr = g_list_prepend (ptr, cur);
183 cur = g_new (GnmRange, 1);
184 cur->start.row = cur->end.row = cell->pos.row;
185 cur->start.col = cur->end.col = cell->pos.col;
186 } else
187 cur->end.col = cell->pos.col;
189 deps = g_list_remove (deps, cell);
191 if (cur)
192 ptr = g_list_prepend (ptr, cur);
194 /* Merge the coalesced rows into ranges */
195 deps = ptr;
196 for (ptr = NULL ; deps ; ) {
197 GnmRange *r1 = deps->data;
198 GList *fwd;
200 for (fwd = deps->next ; fwd ; ) {
201 GnmRange *r2 = fwd->data;
203 if (r1->start.col == r2->start.col &&
204 r1->end.col == r2->end.col &&
205 r1->start.row-1 == r2->end.row) {
206 r1->start.row = r2->start.row;
207 g_free (fwd->data);
208 fwd = g_list_remove (fwd, r2);
209 } else
210 fwd = fwd->next;
213 ptr = g_list_prepend (ptr, r1);
214 deps = g_list_remove (deps, r1);
217 /* now select the ranges */
218 while (ptr) {
219 sv_selection_add_range (sv, ptr->data);
220 g_free (ptr->data);
221 ptr = g_list_remove (ptr, ptr->data);
224 sheet_update (sv->sheet);
228 * sv_select_cur_inputs :
229 * @sv: The sheet
231 * Select all cells that are direct potential inputs to the
232 * current cell.
234 void
235 sv_select_cur_inputs (SheetView *sv)
237 GnmCell *cell;
238 GSList *ranges, *ptr;
239 GnmEvalPos ep;
241 g_return_if_fail (GNM_IS_SV (sv));
243 cell = sheet_cell_get (sv->sheet,
244 sv->edit_pos.col, sv->edit_pos.row);
245 if (cell == NULL || !gnm_cell_has_expr (cell))
246 return;
247 ranges = gnm_expr_top_get_ranges (cell->base.texpr);
248 if (ranges == NULL)
249 return;
251 ep.eval = sv->edit_pos;
252 ep.sheet = sv->sheet;
253 ep.dep = NULL;
255 sv_selection_reset (sv);
256 for (ptr = ranges ; ptr != NULL ; ptr = ptr->next) {
257 GnmValue *v = ptr->data;
258 GnmRangeRef const *r = value_get_rangeref (v);
260 #warning "FIXME: What do we do in these 3D cases?"
261 if ((r->a.sheet == r->b.sheet) &&
262 (r->a.sheet == NULL || r->a.sheet == sv->sheet)) {
263 gint row, col;
264 row = gnm_cellref_get_row (&r->a, &ep);
265 col = gnm_cellref_get_col (&r->a, &ep);
266 sv_selection_add_full
267 (sv, col, row, col, row,
268 gnm_cellref_get_col (&r->b, &ep),
269 gnm_cellref_get_row (&r->b, &ep),
270 GNM_SELECTION_MODE_ADD);
272 value_release (v);
274 g_slist_free (ranges);
276 sheet_update (sv->sheet);
280 * cmd_paste :
282 * Pastes the current cut buffer, copy buffer, or X selection to
283 * the destination sheet range.
285 * When pasting a cut the destination MUST be the same size as the src.
287 * When pasting a copy the destination can be a singleton, or an integer
288 * multiple of the size of the source. This is not tested here.
289 * Full undo support.
291 void
292 cmd_paste (WorkbookControl *wbc, GnmPasteTarget const *pt)
294 GnmCellRegion *content;
295 GnmRange const *src_range;
296 GnmRange dst;
298 g_return_if_fail (pt != NULL);
299 g_return_if_fail (IS_SHEET (pt->sheet));
301 dst = pt->range;
303 /* Check for locks */
304 if (cmd_cell_range_is_locked_effective (pt->sheet, &dst, wbc,
305 _("Paste")))
306 return ;
308 src_range = gnm_app_clipboard_area_get ();
309 content = gnm_app_clipboard_contents_get ();
311 if (content == NULL && src_range != NULL) {
312 /* Pasting a Cut */
313 GnmExprRelocateInfo rinfo;
314 Sheet *src_sheet = gnm_app_clipboard_sheet_get ();
316 /* Validate the size & shape of the target here. */
317 int const cols = (src_range->end.col - src_range->start.col);
318 int const rows = (src_range->end.row - src_range->start.row);
320 if (range_is_singleton (&dst)) {
321 dst.end.col = dst.start.col + cols;
322 dst.end.row = dst.start.row + rows;
323 } else if ((dst.end.col - dst.start.col) != cols ||
324 (dst.end.row - dst.start.row) != rows) {
326 char *msg = g_strdup_printf (
327 _("destination has a different shape (%dRx%dC) than the original (%dRx%dC)\n\n"
328 "Try selecting a single cell or an area of the same shape and size."),
329 (dst.end.row - dst.start.row)+1,
330 (dst.end.col - dst.start.col)+1,
331 rows+1, cols+1);
332 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc),
333 _("Unable to paste into selection"), msg);
334 g_free (msg);
335 return;
338 rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
339 rinfo.origin = *src_range;
340 rinfo.col_offset = dst.start.col - rinfo.origin.start.col;
341 rinfo.row_offset = dst.start.row - rinfo.origin.start.row;
342 rinfo.origin_sheet = src_sheet;
343 rinfo.target_sheet = pt->sheet;
345 if (!cmd_paste_cut (wbc, &rinfo, TRUE, NULL))
346 gnm_app_clipboard_clear (TRUE);
348 /* If this application has marked a selection use it */
349 } else if (content != NULL) {
350 cmd_paste_copy (wbc, pt, content);
351 /* We don't own the contents, so don't unref it. */
352 } else {
353 /* See if the control has access to information to paste */
354 wb_control_paste_from_selection (wbc, pt);
359 * cmd_paste_to_selection :
360 * @dest_sv: The sheet into which things should be pasted
361 * @paste_flags: special paste flags (eg transpose)
363 * Using the current selection as a target
364 * Full undo support.
366 void
367 cmd_paste_to_selection (WorkbookControl *wbc, SheetView *dest_sv, int paste_flags)
369 GnmRange const *r;
370 GnmPasteTarget pt;
372 r = selection_first_range (dest_sv, GO_CMD_CONTEXT (wbc), _("Paste"));
373 if (!r)
374 return;
376 pt.sheet = dest_sv->sheet;
377 pt.range = *r;
378 pt.paste_flags = paste_flags;
379 cmd_paste (wbc, &pt);
383 * cmd_shift_rows:
384 * @wbc: The error context.
385 * @sheet the sheet
386 * @col column marking the start of the shift
387 * @start_row first row
388 * @end_row end row
389 * @count numbers of columns to shift. negative numbers will
390 * delete count columns, positive number will insert
391 * count columns.
393 * Takes the cells in the region (col,start_row):(MAX_COL,end_row)
394 * and copies them @count units (possibly negative) to the right.
396 void
397 cmd_shift_rows (WorkbookControl *wbc, Sheet *sheet,
398 int col, int start_row, int end_row, int count)
400 GnmExprRelocateInfo rinfo;
401 char *desc;
403 rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
404 rinfo.col_offset = count;
405 rinfo.row_offset = 0;
406 rinfo.origin_sheet = rinfo.target_sheet = sheet;
407 rinfo.origin.start.row = start_row;
408 rinfo.origin.start.col = col;
409 rinfo.origin.end.row = end_row;
410 rinfo.origin.end.col = gnm_sheet_get_last_col (sheet);
413 if (count > 0) {
414 GnmRange r = rinfo.origin;
415 r.start.col = r.end.col - count + 1;
417 if (!sheet_is_region_empty (sheet, &r)) {
418 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
419 _("Inserting these cells would push data off the sheet. "
420 "Please enlarge the sheet first."));
421 return;
423 rinfo.origin.end.col -= count;
426 desc = g_strdup_printf ((start_row != end_row)
427 ? _("Shift rows %s")
428 : _("Shift row %s"),
429 rows_name (start_row, end_row));
430 cmd_paste_cut (wbc, &rinfo, FALSE, desc);
434 * cmd_shift_cols:
435 * @wbc: The error context.
436 * @sheet: the sheet
437 * @start_col: first column
438 * @end_col: end column
439 * @row: row marking the start of the shift
440 * @count: numbers of rows to shift. a negative numbers will
441 * delete count rows, positive number will insert
442 * count rows.
444 * Takes the cells in the region (start_col,row):(end_col,MAX_ROW)
445 * and copies them @count units (possibly negative) downwards.
447 void
448 cmd_shift_cols (WorkbookControl *wbc, Sheet *sheet,
449 int start_col, int end_col, int row, int count)
451 GnmExprRelocateInfo rinfo;
452 char *desc;
454 rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
455 rinfo.col_offset = 0;
456 rinfo.row_offset = count;
457 rinfo.origin_sheet = rinfo.target_sheet = sheet;
458 rinfo.origin.start.col = start_col;
459 rinfo.origin.start.row = row;
460 rinfo.origin.end.col = end_col;
461 rinfo.origin.end.row = gnm_sheet_get_last_row (sheet);
462 if (count > 0) {
463 GnmRange r = rinfo.origin;
464 r.start.row = r.end.row - count + 1;
466 if (!sheet_is_region_empty (sheet, &r)) {
467 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (wbc)), GTK_MESSAGE_ERROR,
468 _("Inserting these cells would push data off the sheet. "
469 "Please enlarge the sheet first."));
470 return;
472 rinfo.origin.end.row -= count;
475 desc = g_strdup_printf ((start_col != end_col)
476 ? _("Shift columns %s")
477 : _("Shift column %s"),
478 cols_name (start_col, end_col));
479 cmd_paste_cut (wbc, &rinfo, FALSE, desc);