cmd-edit.c 11 KB
Newer Older
Jody Goldberg's avatar
Jody Goldberg committed
1
/* vim: set sw=8: */
Jody Goldberg's avatar
Jody Goldberg committed
2 3 4
/*
 * cmd-edit.c: Various commands to be used by the edit menu.
 *
Jody Goldberg's avatar
Jody Goldberg committed
5
 * Copyright (C) 2000 Jody Goldberg (jody@gnome.org)
Jody Goldberg's avatar
Jody Goldberg committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */
22
#include <gnumeric-config.h>
23
#include <glib/gi18n.h>
24
#include "gnumeric.h"
Jody Goldberg's avatar
Jody Goldberg committed
25
#include "cmd-edit.h"
26

27
#include "application.h"
28 29
#include "command-context.h"
#include "workbook-control.h"
30
#include "workbook.h"
Jody Goldberg's avatar
Jody Goldberg committed
31
#include "sheet.h"
32
#include "sheet-view.h"
Jody Goldberg's avatar
Jody Goldberg committed
33 34
#include "cell.h"
#include "expr.h"
35
#include "expr-impl.h"
36
#include "dependent.h"
37
#include "selection.h"
Jody Goldberg's avatar
Jody Goldberg committed
38
#include "parse-util.h"
39 40 41 42
#include "ranges.h"
#include "commands.h"
#include "clipboard.h"

Jody Goldberg's avatar
Jody Goldberg committed
43
#include <gtk/gtk.h>
Jody Goldberg's avatar
Jody Goldberg committed
44 45

/**
46
 * sv_select_cur_row:
Jody Goldberg's avatar
Jody Goldberg committed
47
 * @sv: The sheet
Jody Goldberg's avatar
Jody Goldberg committed
48 49 50 51
 *
 * Selects an entire row
 */
void
52
sv_select_cur_row (SheetView *sv)
Jody Goldberg's avatar
Jody Goldberg committed
53
{
Jody Goldberg's avatar
Jody Goldberg committed
54
	GnmRange const *sel = selection_first_range (sv,  NULL, NULL);
Jody Goldberg's avatar
Jody Goldberg committed
55
	if (sel != NULL) {
Jody Goldberg's avatar
Jody Goldberg committed
56
		GnmRange r = *sel;
Jody Goldberg's avatar
Jody Goldberg committed
57 58 59
		sv_selection_reset (sv);
		sv_selection_add_range (sv,
			sv->edit_pos.col, sv->edit_pos.row,
Jody Goldberg's avatar
Jody Goldberg committed
60
			0, r.start.row, SHEET_MAX_COLS-1, r.end.row);
Jody Goldberg's avatar
Jody Goldberg committed
61
		sheet_update (sv->sheet);
Jody Goldberg's avatar
Jody Goldberg committed
62
	}
Jody Goldberg's avatar
Jody Goldberg committed
63 64 65
}

/**
66
 * sv_select_cur_col:
Jody Goldberg's avatar
Jody Goldberg committed
67
 * @sv: The sheet
Jody Goldberg's avatar
Jody Goldberg committed
68 69 70 71
 *
 * Selects an entire column
 */
void
72
sv_select_cur_col (SheetView *sv)
Jody Goldberg's avatar
Jody Goldberg committed
73
{
Jody Goldberg's avatar
Jody Goldberg committed
74
	GnmRange const *sel = selection_first_range (sv,  NULL, NULL);
Jody Goldberg's avatar
Jody Goldberg committed
75
	if (sel != NULL) {
Jody Goldberg's avatar
Jody Goldberg committed
76
		GnmRange r = *sel;
Jody Goldberg's avatar
Jody Goldberg committed
77 78 79
		sv_selection_reset (sv);
		sv_selection_add_range (sv,
			sv->edit_pos.col, sv->edit_pos.row,
Jody Goldberg's avatar
Jody Goldberg committed
80
			r.start.col, 0, r.end.col, SHEET_MAX_ROWS-1);
Jody Goldberg's avatar
Jody Goldberg committed
81
		sheet_update (sv->sheet);
Jody Goldberg's avatar
Jody Goldberg committed
82
	}
Jody Goldberg's avatar
Jody Goldberg committed
83 84 85
}

/**
86
 * sv_select_cur_array :
Jody Goldberg's avatar
Jody Goldberg committed
87
 * @sv: The sheet
Jody Goldberg's avatar
Jody Goldberg committed
88 89 90 91 92
 *
 * if the current cell is part of an array select
 * the entire array.
 */
void
93
sv_select_cur_array (SheetView *sv)
Jody Goldberg's avatar
Jody Goldberg committed
94
{
95
	GnmExprArray const *array;
Jody Goldberg's avatar
Jody Goldberg committed
96 97
	int col, row;

Jody Goldberg's avatar
Jody Goldberg committed
98 99 100
	col = sv->edit_pos.col;
	row = sv->edit_pos.row;
	array = cell_is_array (sheet_cell_get (sv->sheet, col, row));
Jody Goldberg's avatar
Jody Goldberg committed
101 102 103 104

	if (array == NULL)
		return;

Jody Goldberg's avatar
Jody Goldberg committed
105
	sv_selection_reset (sv);
Jody Goldberg's avatar
Jody Goldberg committed
106 107 108 109
	/*
	 * leave the edit cell where it is,
	 * select the entire array.
	 */
Jody Goldberg's avatar
Jody Goldberg committed
110
	sv_selection_add_range (sv, col, row,
Jody Goldberg's avatar
Jody Goldberg committed
111
				   col - array->x, row - array->y,
Jody Goldberg's avatar
Jody Goldberg committed
112 113
				   col - array->x + array->cols - 1,
				   row - array->y + array->rows - 1);
Jody Goldberg's avatar
Jody Goldberg committed
114

Jody Goldberg's avatar
Jody Goldberg committed
115
	sheet_update (sv->sheet);
Jody Goldberg's avatar
Jody Goldberg committed
116 117 118 119 120
}

static gint
cb_compare_deps (gconstpointer a, gconstpointer b)
{
121 122
	GnmCell const *cell_a = a;
	GnmCell const *cell_b = b;
Jody Goldberg's avatar
Jody Goldberg committed
123 124
	int tmp;

125 126 127
	if (cell_a->base.sheet != cell_b->base.sheet)
		return cell_a->base.sheet->index_in_wb - cell_b->base.sheet->index_in_wb;

128
	tmp = cell_a->pos.row - cell_b->pos.row;
Jody Goldberg's avatar
Jody Goldberg committed
129 130
	if (tmp != 0)
		return tmp;
131
	return cell_a->pos.col - cell_b->pos.col;
Jody Goldberg's avatar
Jody Goldberg committed
132 133
}

Jody Goldberg's avatar
Jody Goldberg committed
134
static void
135
cb_collect_deps (GnmDependent *dep, gpointer user)
Jody Goldberg's avatar
Jody Goldberg committed
136
{
137
	if (dependent_is_cell (dep)) {
Jody Goldberg's avatar
Jody Goldberg committed
138 139 140 141 142
		GList **list = (GList **)user;
		*list = g_list_prepend (*list, dep);
	}
}

Jody Goldberg's avatar
Jody Goldberg committed
143
/**
144
 * sv_select_cur_depends :
Jody Goldberg's avatar
Jody Goldberg committed
145
 * @sv: The sheet
Jody Goldberg's avatar
Jody Goldberg committed
146 147 148 149
 *
 * Select all cells that depend on the expression in the current cell.
 */
void
150
sv_select_cur_depends (SheetView *sv)
Jody Goldberg's avatar
Jody Goldberg committed
151
{
152
	GnmCell  *cur_cell, dummy;
Jody Goldberg's avatar
Jody Goldberg committed
153
	GList *deps = NULL, *ptr = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
154

Jody Goldberg's avatar
Jody Goldberg committed
155
	g_return_if_fail (IS_SHEET_VIEW (sv));
Jody Goldberg's avatar
Jody Goldberg committed
156

Jody Goldberg's avatar
Jody Goldberg committed
157
	cur_cell = sheet_cell_get (sv->sheet,
158 159 160 161 162 163
		sv->edit_pos.col, sv->edit_pos.row);
	if (cur_cell == NULL) {
		dummy.base.sheet = sv_sheet (sv);
		dummy.pos = sv->edit_pos;
		cur_cell = &dummy;
	}
Jody Goldberg's avatar
Jody Goldberg committed
164

Jody Goldberg's avatar
Jody Goldberg committed
165
	cell_foreach_dep (cur_cell, cb_collect_deps, &deps);
Jody Goldberg's avatar
Jody Goldberg committed
166 167 168
	if (deps == NULL)
		return;

Jody Goldberg's avatar
Jody Goldberg committed
169
	sv_selection_reset (sv);
Jody Goldberg's avatar
Jody Goldberg committed
170 171 172

	/* Short circuit */
	if (g_list_length (deps) == 1) {
173
		GnmCell *cell = deps->data;
Jody Goldberg's avatar
Jody Goldberg committed
174
		sv_selection_add_pos (sv, cell->pos.col, cell->pos.row);
Jody Goldberg's avatar
Jody Goldberg committed
175
	} else {
Jody Goldberg's avatar
Jody Goldberg committed
176
		GnmRange *cur = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
177 178 179 180
		ptr = NULL;

		/* Merge the sorted list of cells into rows */
		for (deps = g_list_sort (deps, &cb_compare_deps) ; deps ; ) {
181
			GnmCell *cell = deps->data;
Jody Goldberg's avatar
Jody Goldberg committed
182 183

			if (cur == NULL ||
184 185
			    cur->end.row != cell->pos.row ||
			    cur->end.col+1 != cell->pos.col) {
Jody Goldberg's avatar
Jody Goldberg committed
186 187
				if (cur)
					ptr = g_list_prepend (ptr, cur);
Jody Goldberg's avatar
Jody Goldberg committed
188
				cur = g_new (GnmRange, 1);
189 190
				cur->start.row = cur->end.row = cell->pos.row;
				cur->start.col = cur->end.col = cell->pos.col;
Jody Goldberg's avatar
Jody Goldberg committed
191
			} else
192
				cur->end.col = cell->pos.col;
Jody Goldberg's avatar
Jody Goldberg committed
193 194 195 196 197 198 199 200 201

			deps = g_list_remove (deps, cell);
		}
		if (cur)
			ptr = g_list_prepend (ptr, cur);

		/* Merge the coalesced rows into ranges */
		deps = ptr;
		for (ptr = NULL ; deps ; ) {
Jody Goldberg's avatar
Jody Goldberg committed
202
			GnmRange *r1 = deps->data;
Jody Goldberg's avatar
Jody Goldberg committed
203 204 205
			GList *fwd;

			for (fwd = deps->next ; fwd ; ) {
Jody Goldberg's avatar
Jody Goldberg committed
206
				GnmRange *r2 = fwd->data;
Jody Goldberg's avatar
Jody Goldberg committed
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

				if (r1->start.col == r2->start.col &&
				    r1->end.col == r2->end.col &&
				    r1->start.row-1 == r2->end.row) {
					r1->start.row = r2->start.row;
					g_free (fwd->data);
					fwd = g_list_remove (fwd, r2);
				} else
					fwd = fwd->next;
			}

			ptr = g_list_prepend (ptr, r1);
			deps = g_list_remove (deps, r1);
		}

		/* now select the ranges */
		while (ptr) {
Jody Goldberg's avatar
Jody Goldberg committed
224
			GnmRange *r = ptr->data;
Jody Goldberg's avatar
Jody Goldberg committed
225 226 227 228
			sv_selection_add_range (sv,
						r->start.col, r->start.row,
						r->start.col, r->start.row,
						r->end.col, r->end.row);
Jody Goldberg's avatar
Jody Goldberg committed
229 230 231 232
			g_free (ptr->data);
			ptr = g_list_remove (ptr, r);
		}
	}
Jody Goldberg's avatar
Jody Goldberg committed
233
	sheet_update (sv->sheet);
Jody Goldberg's avatar
Jody Goldberg committed
234 235 236
}

/**
237
 * sv_select_cur_inputs :
Jody Goldberg's avatar
Jody Goldberg committed
238
 * @sv: The sheet
Jody Goldberg's avatar
Jody Goldberg committed
239 240 241
 *
 * Select all cells that are direct potential inputs to the
 * current cell.
242
 **/
Jody Goldberg's avatar
Jody Goldberg committed
243
void
244
sv_select_cur_inputs (SheetView *sv)
Jody Goldberg's avatar
Jody Goldberg committed
245
{
246 247 248
	GnmCell  *cell;
	GnmRange *r;
	GSList   *ranges, *ptr;
Jody Goldberg's avatar
Jody Goldberg committed
249

Jody Goldberg's avatar
Jody Goldberg committed
250
	g_return_if_fail (IS_SHEET_VIEW (sv));
Jody Goldberg's avatar
Jody Goldberg committed
251

Jody Goldberg's avatar
Jody Goldberg committed
252
	cell = sheet_cell_get (sv->sheet,
253
		sv->edit_pos.col, sv->edit_pos.row);
Jody Goldberg's avatar
Jody Goldberg committed
254 255
	if (cell == NULL || !cell_has_expr (cell))
		return;
256 257 258 259 260 261 262 263 264 265 266 267 268
	ranges = gnm_expr_get_ranges (cell->base.expression);
	if (ranges == NULL)
		return;

#if 0
	sv_selection_reset (sv);
	for (ptr = ranges ; ptr != NULL ; ptr = ptr->next)
		sv_selection_add_range (sv,
					r->start.col, r->start.row,
					r->start.col, r->start.row,
					r->end.col, r->end.row);
	g_slist_free (ranges);
#endif
Jody Goldberg's avatar
Jody Goldberg committed
269 270

	/* TODO : finish this */
Jody Goldberg's avatar
Jody Goldberg committed
271
	sheet_update (sv->sheet);
Jody Goldberg's avatar
Jody Goldberg committed
272
}
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289

/**
 * cmd_paste :
 * @sheet: The destination sheet
 * @range : The range to paste to within the destination sheet.
 * @flags: Any paste special flags.
 *
 * Pastes the current cut buffer, copy buffer, or X selection to
 * the destination sheet range.
 *
 * When pasting a cut the destination MUST be the same size as the src.
 *
 * When pasting a copy the destination can be a singleton, or an integer
 * multiple of the size of the source.  This is not tested here.
 * Full undo support.
 */
void
Morten Welinder's avatar
Morten Welinder committed
290
cmd_paste (WorkbookControl *wbc, GnmPasteTarget const *pt)
291
{
Morten Welinder's avatar
Morten Welinder committed
292
	GnmCellRegion  *content;
Jody Goldberg's avatar
Jody Goldberg committed
293
	GnmRange const *src_range;
294

295
	g_return_if_fail (pt != NULL);
296 297
	g_return_if_fail (IS_SHEET (pt->sheet));

Jody Goldberg's avatar
Jody Goldberg committed
298 299
	src_range = gnm_app_clipboard_area_get ();
	content = gnm_app_clipboard_contents_get ();
300 301 302

	if (content == NULL && src_range != NULL) {
		/* Pasting a Cut */
303
		GnmExprRelocateInfo rinfo;
Jody Goldberg's avatar
Jody Goldberg committed
304
		Sheet *src_sheet = gnm_app_clipboard_sheet_get ();
305 306 307

		/* Validate the size & shape of the target here. */
		int const cols = (src_range->end.col - src_range->start.col);
308
		int const rows = (src_range->end.row - src_range->start.row);
309

Jody Goldberg's avatar
Jody Goldberg committed
310
		GnmRange dst = pt->range;
311 312 313 314 315 316 317

		if (range_is_singleton (&dst)) {
			dst.end.col = dst.start.col + cols;
			dst.end.row = dst.start.row + rows;
		} else if ((dst.end.col - dst.start.col) != cols ||
			   (dst.end.row - dst.start.row) != rows) {

Miguel de Icaza's avatar
Miguel de Icaza committed
318
			char *msg = g_strdup_printf (
319 320 321 322 323
				_("destination has a different shape (%dRx%dC) than the original (%dRx%dC)\n\n"
				  "Try selecting a single cell or an area of the same shape and size."),
				(dst.end.row - dst.start.row)+1,
				(dst.end.col - dst.start.col)+1,
				rows+1, cols+1);
Jody Goldberg's avatar
Jody Goldberg committed
324
			gnm_cmd_context_error_invalid (GNM_CMD_CONTEXT (wbc),
325
				_("Unable to paste into selection"), msg);
326 327 328 329 330 331 332 333 334 335
			g_free (msg);
			return;
		}

		rinfo.origin = *src_range;
		rinfo.col_offset = dst.start.col - rinfo.origin.start.col;
		rinfo.row_offset = dst.start.row - rinfo.origin.start.row;
		rinfo.origin_sheet = src_sheet;
		rinfo.target_sheet = pt->sheet;

336
		if (!cmd_paste_cut (wbc, &rinfo, TRUE, NULL))
Jody Goldberg's avatar
Jody Goldberg committed
337
			gnm_app_clipboard_clear (TRUE);
338 339 340 341 342 343 344

	/* If this application has marked a selection use it */
	} else if (content != NULL)
		cmd_paste_copy (wbc, pt, content);

	/* See if the control has access to information to paste */
	else
345
		wb_control_paste_from_selection (wbc, pt);
346 347 348 349
}

/**
 * cmd_paste_to_selection :
Jody Goldberg's avatar
Jody Goldberg committed
350
 * @dest_sv: The sheet into which things should be pasted
351 352 353 354 355 356
 * @flags: special paste flags (eg transpose)
 *
 * Using the current selection as a target
 * Full undo support.
 */
void
Jody Goldberg's avatar
Jody Goldberg committed
357
cmd_paste_to_selection (WorkbookControl *wbc, SheetView *dest_sv, int paste_flags)
358
{
Jody Goldberg's avatar
Jody Goldberg committed
359
	GnmRange const *r;
Morten Welinder's avatar
Morten Welinder committed
360
	GnmPasteTarget pt;
361

362
	if (!(r = selection_first_range (dest_sv, GNM_CMD_CONTEXT (wbc), _("Paste"))))
363 364
		return;

365
	g_return_if_fail (r !=NULL);
366

Jody Goldberg's avatar
Jody Goldberg committed
367
	pt.sheet = dest_sv->sheet;
368
	pt.range = *r;
369
	pt.paste_flags = paste_flags;
370
	cmd_paste (wbc, &pt);
371
}
372 373 374

/**
 * cmd_shift_rows:
375
 * @wbc : 	The error context.
376 377 378 379 380 381 382 383 384 385 386 387 388
 * @sheet	the sheet
 * @col		column marking the start of the shift
 * @start_row	first row
 * @end_row	end row
 * @count	numbers of columns to shift.  negative numbers will
 *		delete count columns, positive number will insert
 *		count columns.
 *
 * Takes the cells in the region (col,start_row):(MAX_COL,end_row)
 * and copies them @count units (possibly negative) to the right.
 */

void
389
cmd_shift_rows (WorkbookControl *wbc, Sheet *sheet,
390 391
		int col, int start_row, int end_row, int count)
{
392
	GnmExprRelocateInfo rinfo;
393
	char *desc;
394 395 396 397 398 399 400 401

	rinfo.col_offset = count;
	rinfo.row_offset = 0;
	rinfo.origin_sheet = rinfo.target_sheet = sheet;
	rinfo.origin.start.row = start_row;
	rinfo.origin.start.col = col;
	rinfo.origin.end.row = end_row;
	rinfo.origin.end.col = SHEET_MAX_COLS-1;
Jody Goldberg's avatar
typo.  
Jody Goldberg committed
402 403
	if (count > 0)
		rinfo.origin.end.col -= count;
404

405 406 407 408
	desc = g_strdup_printf ((start_row != end_row)
				? _("Shift rows %s")
				: _("Shift row %s"),
				rows_name (start_row, end_row));
409
	cmd_paste_cut (wbc, &rinfo, FALSE, desc);
410 411 412 413
}

/**
 * cmd_shift_cols:
414 415 416 417 418 419
 * @wbc: 	The error context.
 * @sheet:	the sheet
 * @start_col:	first column
 * @end_col:	end column
 * @row:	row marking the start of the shift
 * @count:	numbers of rows to shift.  a negative numbers will
420 421 422 423 424 425 426
 *		delete count rows, positive number will insert
 *		count rows.
 *
 * Takes the cells in the region (start_col,row):(end_col,MAX_ROW)
 * and copies them @count units (possibly negative) downwards.
 */
void
427
cmd_shift_cols (WorkbookControl *wbc, Sheet *sheet,
428 429
		int start_col, int end_col, int row, int count)
{
430
	GnmExprRelocateInfo rinfo;
431
	char *desc;
432 433 434 435 436 437 438 439

	rinfo.col_offset = 0;
	rinfo.row_offset = count;
	rinfo.origin_sheet = rinfo.target_sheet = sheet;
	rinfo.origin.start.col = start_col;
	rinfo.origin.start.row = row;
	rinfo.origin.end.col = end_col;
	rinfo.origin.end.row = SHEET_MAX_ROWS-1;
Jody Goldberg's avatar
typo.  
Jody Goldberg committed
440 441
	if (count > 0)
		rinfo.origin.end.row -= count;
442

443 444 445 446
	desc = g_strdup_printf ((start_col != end_col)
				? _("Shift columns %s")
				: _("Shift column %s"),
				cols_name (start_col, end_col));
447
	cmd_paste_cut (wbc, &rinfo, FALSE, desc);
448
}