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
21 #include <gnumeric-config.h>
22 #include <glib/gi18n-lib.h>
26 #include <application.h>
27 #include <command-context.h>
28 #include <workbook-control.h>
31 #include <sheet-view.h>
34 #include <dependent.h>
35 #include <selection.h>
36 #include <parse-util.h>
39 #include <clipboard.h>
47 * Selects an entire row
50 sv_select_cur_row (SheetView
*sv
)
52 GnmRange
const *sel
= selection_first_range (sv
, NULL
, NULL
);
55 sv_selection_reset (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
);
69 * Selects an entire column
72 sv_select_cur_col (SheetView
*sv
)
74 GnmRange
const *sel
= selection_first_range (sv
, NULL
, NULL
);
77 sv_selection_reset (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
);
88 * sv_select_cur_array:
91 * If the editpos is part of an array clear the selection and select the array.
94 sv_select_cur_array (SheetView
*sv
)
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
))
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
);
112 cb_compare_deps (gconstpointer a
, gconstpointer b
)
114 GnmCell
const *cell_a
= a
;
115 GnmCell
const *cell_b
= b
;
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
;
124 return cell_a
->pos
.col
- cell_b
->pos
.col
;
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:
140 * Select all cells that depend on the expression in the current cell.
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
;
158 cell_foreach_dep (cur_cell
, cb_collect_deps
, &deps
);
162 sv_selection_reset (sv
);
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
);
170 GnmRange
*cur
= 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
;
178 cur
->end
.row
!= cell
->pos
.row
||
179 cur
->end
.col
+1 != cell
->pos
.col
) {
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
;
186 cur
->end
.col
= cell
->pos
.col
;
188 deps
= g_list_remove (deps
, cell
);
191 ptr
= g_list_prepend (ptr
, cur
);
193 /* Merge the coalesced rows into ranges */
195 for (ptr
= NULL
; deps
; ) {
196 GnmRange
*r1
= deps
->data
;
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
;
207 fwd
= g_list_remove (fwd
, r2
);
212 ptr
= g_list_prepend (ptr
, r1
);
213 deps
= g_list_remove (deps
, r1
);
216 /* now select the ranges */
218 sv_selection_add_range (sv
, ptr
->data
);
220 ptr
= g_list_remove (ptr
, ptr
->data
);
223 sheet_update (sv
->sheet
);
227 * sv_select_cur_inputs:
230 * Select all cells that are direct potential inputs to the
234 sv_select_cur_inputs (SheetView
*sv
)
237 GSList
*ranges
, *ptr
;
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
))
246 ranges
= gnm_expr_top_get_ranges (cell
->base
.texpr
);
250 ep
.eval
= sv
->edit_pos
;
251 ep
.sheet
= sv
->sheet
;
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
)) {
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
);
273 g_slist_free (ranges
);
275 sheet_update (sv
->sheet
);
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.
291 cmd_paste (WorkbookControl
*wbc
, GnmPasteTarget
const *pt
)
293 GnmCellRegion
*content
;
294 GnmRange
const *src_range
;
297 g_return_if_fail (pt
!= NULL
);
298 g_return_if_fail (IS_SHEET (pt
->sheet
));
302 /* Check for locks */
303 if (cmd_cell_range_is_locked_effective (pt
->sheet
, &dst
, wbc
,
307 src_range
= gnm_app_clipboard_area_get ();
308 content
= gnm_app_clipboard_contents_get ();
310 if (content
== NULL
&& src_range
!= NULL
) {
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,
331 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc
),
332 _("Unable to paste into selection"), msg
);
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. */
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
366 cmd_paste_to_selection (WorkbookControl
*wbc
, SheetView
*dest_sv
, int paste_flags
)
371 r
= selection_first_range (dest_sv
, GO_CMD_CONTEXT (wbc
), _("Paste"));
375 pt
.sheet
= dest_sv
->sheet
;
377 pt
.paste_flags
= paste_flags
;
378 cmd_paste (wbc
, &pt
);
383 * @wbc: The error context.
385 * @col column marking the start of the shift
386 * @start_row first row
388 * @count numbers of columns to shift. negative numbers will
389 * delete count columns, positive number will insert
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.
396 cmd_shift_rows (WorkbookControl
*wbc
, Sheet
*sheet
,
397 int col
, int start_row
, int end_row
, int count
)
399 GnmExprRelocateInfo rinfo
;
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
);
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."));
422 rinfo
.origin
.end
.col
-= count
;
425 desc
= g_strdup_printf ((start_row
!= end_row
)
428 rows_name (start_row
, end_row
));
429 cmd_paste_cut (wbc
, &rinfo
, FALSE
, desc
);
434 * @wbc: The error context.
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
443 * Takes the cells in the region (start_col,row):(end_col,MAX_ROW)
444 * and copies them @count units (possibly negative) downwards.
447 cmd_shift_cols (WorkbookControl
*wbc
, Sheet
*sheet
,
448 int start_col
, int end_col
, int row
, int count
)
450 GnmExprRelocateInfo rinfo
;
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
);
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."));
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
);