1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
27 #include "application.h"
28 #include "command-context.h"
29 #include "workbook-control.h"
32 #include "sheet-view.h"
35 #include "dependent.h"
36 #include "selection.h"
37 #include "parse-util.h"
40 #include "clipboard.h"
48 * Selects an entire row
51 sv_select_cur_row (SheetView
*sv
)
53 GnmRange
const *sel
= selection_first_range (sv
, NULL
, NULL
);
56 sv_selection_reset (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
);
70 * Selects an entire column
73 sv_select_cur_col (SheetView
*sv
)
75 GnmRange
const *sel
= selection_first_range (sv
, NULL
, NULL
);
78 sv_selection_reset (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
);
89 * sv_select_cur_array:
92 * If the editpos is part of an array clear the selection and select the array.
95 sv_select_cur_array (SheetView
*sv
)
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
))
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
);
113 cb_compare_deps (gconstpointer a
, gconstpointer b
)
115 GnmCell
const *cell_a
= a
;
116 GnmCell
const *cell_b
= b
;
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
;
125 return cell_a
->pos
.col
- cell_b
->pos
.col
;
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:
141 * Select all cells that depend on the expression in the current cell.
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_SHEET_VIEW (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
;
159 cell_foreach_dep (cur_cell
, cb_collect_deps
, &deps
);
163 sv_selection_reset (sv
);
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
);
171 GnmRange
*cur
= 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
;
179 cur
->end
.row
!= cell
->pos
.row
||
180 cur
->end
.col
+1 != cell
->pos
.col
) {
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
;
187 cur
->end
.col
= cell
->pos
.col
;
189 deps
= g_list_remove (deps
, cell
);
192 ptr
= g_list_prepend (ptr
, cur
);
194 /* Merge the coalesced rows into ranges */
196 for (ptr
= NULL
; deps
; ) {
197 GnmRange
*r1
= deps
->data
;
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
;
208 fwd
= g_list_remove (fwd
, r2
);
213 ptr
= g_list_prepend (ptr
, r1
);
214 deps
= g_list_remove (deps
, r1
);
217 /* now select the ranges */
219 sv_selection_add_range (sv
, ptr
->data
);
221 ptr
= g_list_remove (ptr
, ptr
->data
);
224 sheet_update (sv
->sheet
);
228 * sv_select_cur_inputs:
231 * Select all cells that are direct potential inputs to the
235 sv_select_cur_inputs (SheetView
*sv
)
238 GSList
*ranges
, *ptr
;
241 g_return_if_fail (GNM_IS_SHEET_VIEW (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
))
247 ranges
= gnm_expr_top_get_ranges (cell
->base
.texpr
);
251 ep
.eval
= sv
->edit_pos
;
252 ep
.sheet
= sv
->sheet
;
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
)) {
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
);
274 g_slist_free (ranges
);
276 sheet_update (sv
->sheet
);
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.
292 cmd_paste (WorkbookControl
*wbc
, GnmPasteTarget
const *pt
)
294 GnmCellRegion
*content
;
295 GnmRange
const *src_range
;
298 g_return_if_fail (pt
!= NULL
);
299 g_return_if_fail (IS_SHEET (pt
->sheet
));
303 /* Check for locks */
304 if (cmd_cell_range_is_locked_effective (pt
->sheet
, &dst
, wbc
,
308 src_range
= gnm_app_clipboard_area_get ();
309 content
= gnm_app_clipboard_contents_get ();
311 if (content
== NULL
&& src_range
!= NULL
) {
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,
332 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbc
),
333 _("Unable to paste into selection"), msg
);
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. */
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
367 cmd_paste_to_selection (WorkbookControl
*wbc
, SheetView
*dest_sv
, int paste_flags
)
372 r
= selection_first_range (dest_sv
, GO_CMD_CONTEXT (wbc
), _("Paste"));
376 pt
.sheet
= dest_sv
->sheet
;
378 pt
.paste_flags
= paste_flags
;
379 cmd_paste (wbc
, &pt
);
384 * @wbc: The error context.
386 * @col column marking the start of the shift
387 * @start_row first row
389 * @count numbers of columns to shift. negative numbers will
390 * delete count columns, positive number will insert
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.
397 cmd_shift_rows (WorkbookControl
*wbc
, Sheet
*sheet
,
398 int col
, int start_row
, int end_row
, int count
)
400 GnmExprRelocateInfo rinfo
;
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
);
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."));
423 rinfo
.origin
.end
.col
-= count
;
426 desc
= g_strdup_printf ((start_row
!= end_row
)
429 rows_name (start_row
, end_row
));
430 cmd_paste_cut (wbc
, &rinfo
, FALSE
, desc
);
435 * @wbc: The error context.
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
444 * Takes the cells in the region (start_col,row):(end_col,MAX_ROW)
445 * and copies them @count units (possibly negative) downwards.
448 cmd_shift_cols (WorkbookControl
*wbc
, Sheet
*sheet
,
449 int start_col
, int end_col
, int row
, int count
)
451 GnmExprRelocateInfo rinfo
;
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
);
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."));
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
);