clipboard.c 19.6 KB
Newer Older
Arturo Espinosa's avatar
Arturo Espinosa committed
1 2 3 4
/*
 * Clipboard.c: Implements the copy/paste operations
 *
 * Author:
5
 *  Miguel de Icaza (miguel@gnu.org)
Arturo Espinosa's avatar
Arturo Espinosa committed
6 7 8
 */
#include <config.h>
#include <gnome.h>
9 10
#include <locale.h>
#include <string.h>
Jody Goldberg's avatar
Jody Goldberg committed
11
#include <ctype.h>
Arturo Espinosa's avatar
Arturo Espinosa committed
12
#include "gnumeric.h"
13
#include "gnumeric-util.h"
Arturo Espinosa's avatar
Arturo Espinosa committed
14
#include "clipboard.h"
15
#include "eval.h"
16
#include "selection.h"
17
#include "application.h"
18
#include "render-ascii.h"
19
#include "workbook.h"
20
#include "workbook-view.h"
Michael Meeks's avatar
Michael Meeks committed
21
#include "ranges.h"
22
#include "cell-comment.h"
23 24
#include "command-context.h"
#include "commands.h"
25

26 27 28
#include "xml-io.h"
#include "value.h"

29 30 31
#include "dialog-stf.h"
#include "stf-parse.h"

32 33 34 35 36 37 38 39

/* The name of our clipboard atom and the 'magic' info number */
#define GNUMERIC_ATOM_NAME "GNUMERIC_CLIPBOARD_XML"
#define GNUMERIC_ATOM_INFO 2000

/* The name of the TARGETS atom (don't change unless you know what you are doing!) */
#define TARGETS_ATOM_NAME "TARGETS"

40
/**
41
 * paste_cell: Pastes a cell in the spreadsheet
Jody Goldberg's avatar
Jody Goldberg committed
42
 *
43 44 45
 * @dest_sheet:  The sheet where the pasting will be done
 * @target_col:  Column to put the cell into
 * @target_row:  Row to put the cell into.
Jody Goldberg's avatar
Jody Goldberg committed
46
 * @new_cell:    A new cell (not linked into the sheet, or wb->expr_list)
47
 * @paste_flags: Bit mask that describes the paste options.
48
 */
Jody Goldberg's avatar
Jody Goldberg committed
49
static void
50 51
paste_cell (Sheet *dest_sheet,
	    int target_col, int target_row,
Jody Goldberg's avatar
Jody Goldberg committed
52
	    ExprRewriteInfo *rwinfo,
53
	    CellCopy *c_copy, int paste_flags)
54
{
Jody Goldberg's avatar
Jody Goldberg committed
55 56 57
	if ((paste_flags & (PASTE_FORMULAS | PASTE_VALUES))){
		if (c_copy->type == CELL_COPY_TYPE_CELL) {
			Cell *new_cell = cell_copy (c_copy->u.cell);
58

59 60 61 62 63
			/* Cell can not be linked in yet, but it needs an accurate location */
			new_cell->sheet	   = dest_sheet;
			new_cell->col_info = sheet_col_fetch (dest_sheet, target_col);
			new_cell->row_info = sheet_row_fetch (dest_sheet, target_row);

Jody Goldberg's avatar
Jody Goldberg committed
64 65
			if (cell_has_expr (new_cell)) {
				if (paste_flags & PASTE_FORMULAS) {
Jody Goldberg's avatar
Jody Goldberg committed
66
					cell_relocate (new_cell, rwinfo);
67

Jody Goldberg's avatar
Jody Goldberg committed
68 69 70 71 72 73 74 75 76
					/* FIXME : do this at a range level too */
					sheet_cell_changed (new_cell);
				} else
					cell_make_value (new_cell);
			} else {
				g_return_if_fail (new_cell->value != NULL);

				cell_render_value (new_cell);
			}
77

Jody Goldberg's avatar
Jody Goldberg committed
78
			sheet_cell_insert (dest_sheet, new_cell, target_col, target_row);
79
		} else {
Jody Goldberg's avatar
Jody Goldberg committed
80 81
			Cell *new_cell = sheet_cell_new (dest_sheet,
							 target_col, target_row);
82

Jody Goldberg's avatar
Jody Goldberg committed
83
			if (c_copy->u.text)
84
				sheet_cell_set_text (new_cell, c_copy->u.text);
85 86 87

			if (c_copy->type == CELL_COPY_TYPE_TEXT_AND_COMMENT && c_copy->comment)
				cell_set_comment (new_cell, c_copy->comment);
88 89 90 91
		}
	}
}

92
/**
93
 * clipboard_paste_region:
94 95 96
 * @context : The context for error handling.
 * @pt : Where to paste the values.
 * @content : The CellRegion to paste.
97 98 99 100 101
 *
 * Pastes the supplied CellRegion (@content) into the supplied
 * PasteTarget (@pt).  This operation is not undoable.  It does not auto grow
 * the destination if the target is a singleton.  This is a simple interface to
 * paste a region.
102 103
 *
 * returns : TRUE if there was a problem.
104
 */
105
gboolean
106 107 108
clipboard_paste_region (CommandContext *context,
			PasteTarget const *pt,
			CellRegion *content)
109
{
110 111 112 113 114 115
	int tmp;
	int repeat_horizontal, repeat_vertical;
	int dst_cols = pt->range.end.col - pt->range.start.col + 1;
	int dst_rows = pt->range.end.row - pt->range.start.row + 1;
	int src_cols = content->cols;
	int src_rows = content->rows;
116

117 118 119 120 121
	if (pt->paste_flags & PASTE_TRANSPOSE) {
		int tmp = src_cols;
		src_cols = src_rows;
		src_rows = tmp;
	}
122

123
	/* calculate the tiling */
124 125 126 127 128 129 130 131
	repeat_horizontal = dst_cols/src_cols;
	if (repeat_horizontal * src_cols != dst_cols) {
		char *msg = g_strdup_printf (
			_("destination does not have an even multiple of source columns (%d vs %d)\n\n"
			  "Try selecting a single cell or an area of the same shape and size."),
			dst_cols, src_cols);
		gnumeric_error_invalid (context, _("Unable to paste"), msg);
		g_free (msg);
132
		return TRUE;
133
	}
134

135 136 137 138 139 140 141 142
	repeat_vertical = dst_rows/src_rows;
	if (repeat_vertical * src_rows != dst_rows) {
		char *msg = g_strdup_printf (
			_("destination does not have an even multiple of source rows (%d vs %d)\n\n"
			  "Try selecting a single cell or an area of the same shape and size."),
			dst_rows, src_rows);
		gnumeric_error_invalid (context, _("Unable to paste"), msg);
		g_free (msg);
143
		return TRUE;
144 145
	}

146 147
	if ((pt->range.start.col + dst_cols) > SHEET_MAX_COLS ||
	    (pt->range.start.row + dst_rows) > SHEET_MAX_ROWS) {
148 149
		gnumeric_error_invalid (context, _("Unable to paste"), 
					_("result passes the sheet boundary"));
150
		return TRUE;
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
	}

	tmp = 0;
	/* clear the region where we will paste */
	if (pt->paste_flags & (PASTE_VALUES|PASTE_FORMULAS))
		tmp = CLEAR_VALUES|CLEAR_COMMENTS;
	if (pt->paste_flags & PASTE_FORMATS)
		tmp |= CLEAR_FORMATS;
	if (tmp) {
		int const dst_col = pt->range.start.col;
		int const dst_row = pt->range.start.row;
		sheet_clear_region (context, pt->sheet,
				    dst_col, dst_row,
				    dst_col + dst_cols - 1,
				    dst_row + dst_rows - 1,
				    tmp);
	}

Jody Goldberg's avatar
Jody Goldberg committed
169
	for (tmp = repeat_vertical; repeat_horizontal-- > 0 ; repeat_vertical = tmp)
170 171 172 173
		while (repeat_vertical-- > 0) {
			int const left = repeat_horizontal * src_cols + pt->range.start.col;
			int const top = repeat_vertical * src_rows + pt->range.start.row;
			CellCopyList *l;
Jody Goldberg's avatar
Jody Goldberg committed
174 175 176 177 178 179
			ExprRewriteInfo   rwinfo;
			ExprRelocateInfo *rinfo;

			rwinfo.type = EXPR_REWRITE_RELOCATE;
			rinfo = &rwinfo.u.relocate;
			rinfo->origin_sheet = rinfo->target_sheet = pt->sheet;
180 181

			if (pt->paste_flags & PASTE_EXPR_RELOCATE) {
Jody Goldberg's avatar
Jody Goldberg committed
182 183 184 185 186 187 188 189 190 191
				rinfo->origin.start.col = content->base_col;
				rinfo->origin.end.col = content->base_col + content->cols -1;
				rinfo->origin.start.row = content->base_row;
				rinfo->origin.end.row = content->base_row + content->rows -1;
				rinfo->col_offset = left - content->base_col;
				rinfo->row_offset = top - content->base_row;
			} else {
				rinfo->origin = pt->range;
				rinfo->col_offset = 0;
				rinfo->row_offset = 0;
192
			}
193 194 195 196 197 198 199

			/* Move the styles on here so we get correct formats before recalc */
			if (pt->paste_flags & PASTE_FORMATS) {
				Range boundary = pt->range;

				boundary.start.col = left;
				boundary.start.row = top;
Jody Goldberg's avatar
Jody Goldberg committed
200 201
				boundary.end.col   = left + src_cols - 1;
				boundary.end.row   = top + src_rows - 1;
202 203 204 205 206 207 208
				sheet_style_attach_list (pt->sheet, content->styles, &boundary.start,
							 (pt->paste_flags & PASTE_TRANSPOSE));

				sheet_style_optimize (pt->sheet, boundary);
			}

			for (l = content->list; l; l = l->next) {
209
				CellCopy *c_copy = l->data;
210 211
				int target_col = left;
				int target_row = top;
212

213 214 215
				if (pt->paste_flags & PASTE_TRANSPOSE) {
					target_col += c_copy->row_offset;
					target_row += c_copy->col_offset;
216
				} else {
217 218
					target_col += c_copy->col_offset;
					target_row += c_copy->row_offset;
219
				}
220

Jody Goldberg's avatar
Jody Goldberg committed
221 222 223 224 225 226 227 228 229 230 231
				rinfo->pos.sheet = pt->sheet;
				if (pt->paste_flags & PASTE_EXPR_RELOCATE) {
					rinfo->pos.eval.col = content->base_col + c_copy->col_offset;
					rinfo->pos.eval.row = content->base_row + c_copy->row_offset;
				} else {
					rinfo->pos.eval.col = target_col;
					rinfo->pos.eval.row = target_row;
				}

				paste_cell (pt->sheet, target_col, target_row,
					    &rwinfo, c_copy, pt->paste_flags);
232 233
			}
		}
234

235 236 237 238 239 240
        if (pt->paste_flags & (PASTE_FORMULAS|PASTE_VALUES)) {
		GList *deps = sheet_region_get_deps (pt->sheet,
						     pt->range.start.col,
						     pt->range.start.row,
						     pt->range.end.col,
						     pt->range.end.row);
241 242
		if (deps)
			eval_queue_list (deps, TRUE);
243
		sheet_range_calc_spans (pt->sheet, pt->range, SPANCALC_RENDER);
244
		sheet_flag_status_update_range (pt->sheet, &pt->range);
245
	}
246 247

	return FALSE;
248 249 250 251 252
}

static Value *
clipboard_prepend_cell (Sheet *sheet, int col, int row, Cell *cell, void *user_data)
{
253
	CellRegion *c = user_data;
254 255 256 257 258 259 260 261
	CellCopy *copy;

	copy = g_new (CellCopy, 1);
	copy->type       = CELL_COPY_TYPE_CELL;
	copy->u.cell     = cell_copy (cell);
	copy->col_offset = col - c->base_col;
	copy->row_offset = row - c->base_row;

262
	c->list = g_list_prepend (c->list, copy);
263 264 265 266 267 268 269 270 271 272 273 274

	return NULL;
}

/**
 * clipboard_copy_range:
 *
 * Entry point to the clipboard copy code
 */
CellRegion *
clipboard_copy_range (Sheet *sheet, Range const *r)
{
275
	CellRegion *c;
276 277 278 279 280 281

	g_return_val_if_fail (sheet != NULL, NULL);
	g_return_val_if_fail (IS_SHEET (sheet), NULL);
	g_return_val_if_fail (r->start.col <= r->end.col, NULL);
	g_return_val_if_fail (r->start.row <= r->end.row, NULL);

282
	c = g_new0 (CellRegion, 1);
283

284 285 286 287
	c->base_col = r->start.col;
	c->base_row = r->start.row;
	c->cols = r->end.col - r->start.col + 1;
	c->rows = r->end.row - r->start.row + 1;
288 289 290 291

	sheet_cell_foreach_range ( sheet, TRUE,
		r->start.col, r->start.row,
		r->end.col, r->end.row,
292
		clipboard_prepend_cell, c);
293

294
	c->styles = sheet_get_styles_in_range (sheet, r);
295 296

	/* reverse the list so that upper left corner is first */
297
	c->list = g_list_reverse (c->list);
298

299
	return c;
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
}

/**
 * clipboard_paste :
 * @content:     a Cell Region that contains information to be pasted
 * @time:        Time at which the event happened.
 */
void
clipboard_paste (CommandContext *context, PasteTarget const *pt, guint32 time)
{
	PasteTarget *new_pt;
	CellRegion *content;

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

	content = application_clipboard_contents_get ();

	/*
	 * If we own the selection, there is no need to ask X for
	 * the selection: we do the paste from our internal buffer.
	 *
	 * This is better as we have all sorts of useful information
	 * to paste from in this case (instead of the simplistic text
	 * we get from X)
	 */
	if (content) {
		cmd_paste_copy (context, pt, content);
		return;
	}

	/*
	 * OK, we dont have the X selection, so we try to get it:
	 * Now, trigger a grab of the X selection.
	 * This will callback x_selection_received
	 */
	if (pt->sheet->workbook->clipboard_paste_callback_data != NULL)
		g_free (pt->sheet->workbook->clipboard_paste_callback_data);

	new_pt = g_new (PasteTarget, 1);
	*new_pt = *pt;
	pt->sheet->workbook->clipboard_paste_callback_data = new_pt;

	/* Query the formats */
	gtk_selection_convert (pt->sheet->workbook->toplevel, GDK_SELECTION_PRIMARY,
			       gdk_atom_intern (TARGETS_ATOM_NAME, FALSE), time);
}

/*
 * Destroys the contents of a CellRegion
 */
void
clipboard_release (CellRegion *content)
{
	CellCopyList *l;

	g_return_if_fail (content != NULL);

	for (l = content->list; l; l = l->next){
		CellCopy *this_cell = l->data;

		if (this_cell->type == CELL_COPY_TYPE_CELL) {
			/* The cell is not really in the rows or columns */
			this_cell->u.cell->sheet = NULL;
			this_cell->u.cell->row_info = NULL;
			this_cell->u.cell->col_info = NULL;
			cell_destroy (this_cell->u.cell);
		} else {

			if (this_cell->type == CELL_COPY_TYPE_TEXT_AND_COMMENT)
				if (this_cell->comment != NULL)
					g_free (this_cell->comment);

			if (this_cell->u.text)
				g_free (this_cell->u.text);
376
		}
377 378

		g_free (this_cell);
379
	}
380 381 382 383 384 385 386
	if (content->styles != NULL) {
		sheet_style_list_destroy (content->styles);
		content->styles = NULL;
	}

	g_list_free (content->list);
	content->list = NULL;
387

388 389
	g_free (content);
}
390

391 392 393 394 395 396 397
PasteTarget*
paste_target_init (PasteTarget *pt, Sheet *sheet, Range const *r, int flags)
{
	pt->sheet = sheet;
	pt->range = *r;
	pt->paste_flags = flags;
	return pt;
398 399
}

400
static CellRegion *
Morten Welinder's avatar
Morten Welinder committed
401
x_selection_to_cell_region (CommandContext *context, const char *src,
402
			    int len)
403
{
404 405 406
	DialogStfResult_t *dialogresult;
	CellRegion *cr = NULL;
	CellRegion *crerr;
Morten Welinder's avatar
Morten Welinder committed
407 408 409 410 411
	char *data;

	data = g_new (char, len + 1);
	memcpy (data, src, len);
	data[len] = 0;
Jody Goldberg's avatar
Jody Goldberg committed
412

413 414 415
	crerr         = g_new (CellRegion, 1);
	crerr->list   = NULL;
	crerr->cols   = -1;
416
	crerr->rows   = -1;
417
	crerr->styles = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
418

419
	if (!stf_parse_convert_to_unix (data)) {
Morten Welinder's avatar
Morten Welinder committed
420
		g_free (data);
421 422 423
		g_warning (_("Error while trying to pre-convert clipboard data"));
		return crerr;
	}
424

425
	if (!stf_parse_is_valid_data (data)) {
Morten Welinder's avatar
Morten Welinder committed
426
		g_free (data);
427 428 429
		g_warning (_("This data on the clipboard does not seem to be valid text"));
		return crerr;
	}
430

431
	dialogresult = stf_dialog (context, "clipboard", data);
432

433 434 435
	if (dialogresult != NULL) {
		GSList *iterator;
		int col, rowcount;
Jody Goldberg's avatar
Jody Goldberg committed
436

437
		cr = stf_parse_region (dialogresult->parseoptions, dialogresult->newstart);
Jody Goldberg's avatar
Jody Goldberg committed
438

439
		if (cr == NULL) {
Morten Welinder's avatar
Morten Welinder committed
440
			g_free (data);
441 442
			g_warning (_("Parse error while trying to parse data into cellregion"));
			return crerr;
443 444
		}

445 446 447 448
		iterator = dialogresult->formats;
		col = 0;
		rowcount = stf_parse_get_rowcount (dialogresult->parseoptions, dialogresult->newstart);
		while (iterator) {
449
			StyleRegion *content = g_new (StyleRegion, 1);
450 451 452 453 454 455 456 457 458
			MStyle *style = mstyle_new ();
			Range range;

			mstyle_set_format (style, iterator->data);

			range.start.col = col;
			range.start.row = 0;
			range.end.col   = col;
			range.end.row   = rowcount;
Jody Goldberg's avatar
Jody Goldberg committed
459

460 461
			content->style = style;
			content->range  = range;
462

463
			cr->styles = g_list_prepend (cr->styles, content);
Jody Goldberg's avatar
Jody Goldberg committed
464

465 466 467 468
			iterator = g_slist_next (iterator);

			col++;
		}
469

470
		stf_dialog_result_free (dialogresult);
Morten Welinder's avatar
Morten Welinder committed
471
	} else {
472 473
		return crerr;
	}
474

475
	g_free (crerr);
Morten Welinder's avatar
Morten Welinder committed
476
	g_free (data);
Jody Goldberg's avatar
Jody Goldberg committed
477

478 479 480
	return cr;
}

481 482 483 484 485 486 487 488 489 490
/**
 * x_selection_received:
 *
 * Invoked when the selection has been received by our application.
 * This is triggered by a call we do to gtk_selection_convert.
 */
static void
x_selection_received (GtkWidget *widget, GtkSelectionData *sel, guint time, gpointer data)
{
	Workbook       *wb = data;
491
	CommandContext *context = workbook_command_context_gui (wb);
492 493
	GdkAtom atom_targets  = gdk_atom_intern (TARGETS_ATOM_NAME, FALSE);
	GdkAtom atom_gnumeric = gdk_atom_intern (GNUMERIC_ATOM_NAME, FALSE);
Jody Goldberg's avatar
Jody Goldberg committed
494
	PasteTarget *pt = wb->clipboard_paste_callback_data;
495 496 497
	CellRegion *content = NULL;
	gboolean region_pastable = FALSE;
	gboolean free_closure = FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
498

499 500 501 502 503 504 505 506
	if (sel->target == atom_targets) { /* The data is a list of atoms */
		GdkAtom *atoms = (GdkAtom *) sel->data;
		gboolean gnumeric_format;
		int atom_count = (sel->length / sizeof (GdkAtom));
		int i;

		/* Nothing on clipboard? */
		if (sel->length < 0) {
Jody Goldberg's avatar
Jody Goldberg committed
507

508 509 510 511 512 513
			if (wb->clipboard_paste_callback_data != NULL) {
				g_free (wb->clipboard_paste_callback_data);
				wb->clipboard_paste_callback_data = NULL;
			}
			return;
		}
514

515 516 517 518 519
		/*
		 * Iterate over the atoms and try to find the gnumeric atom
		 */
		gnumeric_format = FALSE;
		for (i = 0; i < atom_count; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
520

521
			if (atoms[i] == atom_gnumeric) {
522

523 524 525 526 527 528
				/* Hooya! other app == gnumeric */
				gnumeric_format = TRUE;

				break;
			}
		}
529

530 531 532 533 534 535 536
		/* NOTE : We don't release the date resources
		 * (wb->clipboard_paste_callback_data), the
		 * reason for this is that we will actually call ourself
		 * again (indirectly trough the gtk_selection_convert
		 * and that call _will_ free the data (and also needs it).
		 * So we won't release anything.
		 */
Jody Goldberg's avatar
Jody Goldberg committed
537

538 539 540 541 542 543 544 545 546 547
		/* If another instance of gnumeric put this data on the clipboard
		 * request the data in gnumeric XML format. If not, just
		 * request it in string format
		 */
		if (gnumeric_format)
			gtk_selection_convert (wb->toplevel, GDK_SELECTION_PRIMARY,
					       atom_gnumeric, time);
		else
			gtk_selection_convert (wb->toplevel, GDK_SELECTION_PRIMARY,
					       GDK_SELECTION_TYPE_STRING, time);
Jody Goldberg's avatar
Jody Goldberg committed
548

549
	} else if (sel->target == atom_gnumeric) { /* The data is the gnumeric specific XML interchange format */
Jody Goldberg's avatar
Jody Goldberg committed
550

551 552
		if (gnumeric_xml_read_selection_clipboard (context, &content, sel->data) == 0)
			region_pastable = TRUE;
Jody Goldberg's avatar
Jody Goldberg committed
553

554 555
	} else {  /* The data is probably in String format */
		region_pastable = TRUE;
556

557
		/* Did X provide any selection? */
558 559 560
		if (sel->length < 0) {
			region_pastable = FALSE;
			free_closure = TRUE;
561 562 563
		} else
			content = x_selection_to_cell_region (context,
							      sel->data, sel->length);
564
	}
Jody Goldberg's avatar
Jody Goldberg committed
565

566
	if (region_pastable) {
567 568 569 570 571 572
		/*
		 * if the conversion from the X selection -> a cellregion
		 * was canceled this may have content sized -1,-1
		 */
		if (content->cols > 0 && content->rows > 0)
			cmd_paste_copy (context, pt, content);
573 574 575 576 577

		/* Release the resources we used */
		if (sel->length >= 0)
			clipboard_release (content);
	}
Jody Goldberg's avatar
Jody Goldberg committed
578

579 580 581 582 583 584
	if (region_pastable || free_closure) {
		/* Remove our used resources */
		if (wb->clipboard_paste_callback_data != NULL) {
			g_free (wb->clipboard_paste_callback_data);
			wb->clipboard_paste_callback_data = NULL;
		}
585
	}
586 587
}

Miguel de Icaza's avatar
Miguel de Icaza committed
588
/**
589 590 591 592
 * x_selection_handler:
 *
 * Callback invoked when another application requests we render the selection.
 */
593
static void
Tom Dyas's avatar
Tom Dyas committed
594
x_selection_handler (GtkWidget *widget, GtkSelectionData *selection_data, guint info, guint time, gpointer data)
595
{
596 597
	gboolean content_needs_free = FALSE;
	CellRegion *clipboard = application_clipboard_contents_get ();
598 599 600
	GdkAtom atom_gnumeric = gdk_atom_intern (GNUMERIC_ATOM_NAME, FALSE);
	Sheet *sheet = application_clipboard_sheet_get ();
	Range const *a = application_clipboard_area_get ();
601

602 603 604 605 606
	/*
	 * Not sure how to handle this, not sure what purpose this has has
	 * (sheet being NULL). I think it is here to indicate that the selection
	 * just has been cut.
	 */
Jody Goldberg's avatar
Jody Goldberg committed
607
	if (!sheet)
608
		return;
Jody Goldberg's avatar
Jody Goldberg committed
609

610
	/*
611
	 * If the content was marked for a cut we need to copy it for pasting
612 613 614 615
	 * we clear it later on, because if the other application (the one that
	 * requested we render the data) is another instance of gnumeric
	 * we need the selection to remain "intact" (not cleared) so we can
	 * render it to the Gnumeric XML clipboard format
616
	 */
617
	if (clipboard == NULL) {
Jody Goldberg's avatar
Jody Goldberg committed
618 619 620

		g_return_if_fail (sheet != NULL);
		g_return_if_fail (a != NULL);
Jody Goldberg's avatar
Jody Goldberg committed
621

622
		content_needs_free = TRUE;
623
		clipboard = clipboard_copy_range (sheet, a);
624
	}
625

626 627
	g_return_if_fail (clipboard != NULL);

628 629 630 631 632 633 634 635 636
	/*
	 * Check weather the other application wants gnumeric XML format
	 * in fact we only have to check the 'info' variable, however
	 * to be absolutely sure I check if the atom checks out too
	 */
	if (selection_data->target == atom_gnumeric && info == 2000) {
		CommandContext *context = workbook_command_context_gui (sheet->workbook);
		xmlChar *buffer;
		int buffer_size;
Jody Goldberg's avatar
Jody Goldberg committed
637

638 639 640 641
		gnumeric_xml_write_selection_clipboard (context, sheet, &buffer, &buffer_size);

		gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, 8,
					(char *) buffer, buffer_size);
Jody Goldberg's avatar
Jody Goldberg committed
642

643 644 645
		g_free (buffer);
	} else {
		char *rendered_selection = cell_region_render_ascii (clipboard);
Jody Goldberg's avatar
Jody Goldberg committed
646

647 648
		gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, 8,
					rendered_selection, strlen (rendered_selection));
649

650 651
		g_free (rendered_selection);
	}
652

653
	/*
654
	 * If this was a CUT operation we need to clear the content that was pasted
655 656
	 * into another application and release the stuff on the clipboard
	 */
657
	if (content_needs_free) {
Jody Goldberg's avatar
Jody Goldberg committed
658

659 660 661 662 663
		sheet_clear_region (workbook_command_context_gui (sheet->workbook),
				    sheet,
				    a->start.col, a->start.row,
				    a->end.col,   a->end.row,
				    CLEAR_VALUES|CLEAR_COMMENTS);
Jody Goldberg's avatar
Jody Goldberg committed
664

665
		clipboard_release (clipboard);
666
		application_clipboard_clear (TRUE);
667
	}
668 669
}

670
/**
671
 * x_selection_clear:
672
 *
673
 * Callback for the "we lost the X selection" signal
674
 */
675 676
static gint
x_selection_clear (GtkWidget *widget, GdkEventSelection *event, Workbook *wb)
677
{
678 679
	/* we have already lost the selection, no need to clear it */
	application_clipboard_clear (FALSE);
680

681
	return TRUE;
682 683
}

684 685
/**
 * x_clipboard_bind_workbook:
686 687
 *
 * Binds the signals related to the X selection to the Workbook
688
 * and initialized the clipboard data structures for the Workbook.
689
 */
690 691 692
void
x_clipboard_bind_workbook (Workbook *wb)
{
693
	GtkTargetEntry targets;
Jody Goldberg's avatar
Jody Goldberg committed
694

695
	wb->clipboard_paste_callback_data = NULL;
696

697 698
	gtk_signal_connect (
		GTK_OBJECT (wb->toplevel), "selection_clear_event",
699
		GTK_SIGNAL_FUNC (x_selection_clear), wb);
700

701 702
	gtk_signal_connect (
		GTK_OBJECT (wb->toplevel), "selection_received",
703
		GTK_SIGNAL_FUNC (x_selection_received), wb);
Tom Dyas's avatar
Tom Dyas committed
704 705 706

	gtk_signal_connect (
		GTK_OBJECT (wb->toplevel), "selection_get",
707
		GTK_SIGNAL_FUNC (x_selection_handler), NULL);
Tom Dyas's avatar
Tom Dyas committed
708

709 710 711 712
	gtk_signal_connect (
		GTK_OBJECT (wb->toplevel), "selection_get",
		GTK_SIGNAL_FUNC (x_selection_handler), NULL);

Tom Dyas's avatar
Tom Dyas committed
713
	gtk_selection_add_target (
714
		wb->toplevel,
Tom Dyas's avatar
Tom Dyas committed
715
		GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, 0);
716 717 718 719

	/*
	 * Our specific Gnumeric XML clipboard interchange type
	 */
720
	targets.target = GNUMERIC_ATOM_NAME;
721

722
	/* This is not useful, but we have to set it to something: */
Jody Goldberg's avatar
Jody Goldberg committed
723
	targets.flags  = GTK_TARGET_SAME_WIDGET;
724
	targets.info   = GNUMERIC_ATOM_INFO;
725 726 727

	gtk_selection_add_targets (wb->toplevel,
				   GDK_SELECTION_PRIMARY,
728
				   &targets, 1);
729 730
}