Update Spanish translation
[gnumeric.git] / src / gui-clipboard.c
blob554a8d29e68142852790ee8dd90aea80fef2961f
1 /* VIM: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * gui-clipboard.c: Implements the X11 based copy/paste operations
5 * Author:
6 * Miguel de Icaza (miguel@gnu.org)
7 * Jody Goldberg (jody@gnome.org)
8 */
9 #include <gnumeric-config.h>
10 #include <gnumeric.h>
11 #include <gui-clipboard.h>
13 #include <gui-util.h>
14 #include <clipboard.h>
15 #include <command-context-stderr.h>
16 #include <selection.h>
17 #include <application.h>
18 #include <workbook-control.h>
19 #include <wbc-gtk.h>
20 #include <workbook-priv.h>
21 #include <workbook.h>
22 #include <workbook-view.h>
23 #include <ranges.h>
24 #include <sheet.h>
25 #include <sheet-style.h>
26 #include <sheet-object.h>
27 #include <sheet-control-gui.h>
28 #include <sheet-view.h>
29 #include <commands.h>
30 #include <value.h>
31 #include <number-match.h>
32 #include <dialogs/dialog-stf.h>
33 #include <stf-parse.h>
34 #include <mstyle.h>
35 #include <gnm-format.h>
36 #include <gnumeric-conf.h>
37 #include <xml-sax.h>
38 #include <gutils.h>
40 #include <goffice/goffice.h>
41 #include <gsf/gsf-input-memory.h>
42 #include <gsf/gsf-output-memory.h>
43 #include <gsf/gsf-utils.h>
44 #include <glib/gi18n-lib.h>
45 #include <libxml/globals.h>
46 #include <locale.h>
47 #include <string.h>
48 #include <unistd.h>
50 #define APP_CLIP_DISP_KEY "clipboard-displays"
52 #define EXCEL_FILE_OPENER "Gnumeric_Excel:excel"
53 #define EXCEL_FILE_SAVER "Gnumeric_Excel:excel_biff8"
54 #define HTML_FILE_OPENER "Gnumeric_html:html"
55 #define HTML_FILE_SAVER "Gnumeric_html:xhtml_range"
56 #define OOO_FILE_OPENER "Gnumeric_OpenCalc:openoffice"
58 // ----------------------------------------------------------------------------
60 static gboolean debug_clipboard;
61 static gboolean debug_clipboard_dump;
62 static gboolean debug_clipboard_undump;
64 // ----------------------------------------------------------------------------
66 enum {
67 ATOM_GNUMERIC,
68 ATOM_GOFFICE_GRAPH,
69 // ----------
70 ATOM_UTF8_STRING,
71 ATOM_STRING,
72 ATOM_COMPOUND_TEXT,
73 ATOM_TEXT_HTML,
74 ATOM_TEXT_HTML_WINDOWS,
75 // ----------
76 ATOM_BIFF8,
77 ATOM_BIFF8_OO,
78 ATOM_BIFF8_CITRIX,
79 ATOM_BIFF5,
80 ATOM_BIFF,
81 // ----------
82 ATOM_OOO,
83 ATOM_OOO_WINDOWS,
84 ATOM_OOO11,
85 // ----------
86 ATOM_IMAGE_SVGXML,
87 ATOM_IMAGE_XWMF,
88 ATOM_IMAGE_XEMF,
89 ATOM_IMAGE_PNG,
90 ATOM_IMAGE_JPEG,
91 ATOM_IMAGE_BMP,
92 // ----------
93 ATOM_TEXT_URI_LIST,
94 ATOM_GNOME_COPIED_FILES,
95 ATOM_KDE_CUT_FILES,
96 // ----------
97 ATOM_SAVE_TARGETS,
100 static const char *const atom_names[] = {
101 "application/x-gnumeric",
102 "application/x-goffice-graph",
103 // ----------
104 "UTF8_STRING",
105 "STRING",
106 "COMPOUND_TEXT",
107 "text/html",
108 "HTML Format",
109 // ----------
110 "Biff8",
111 "application/x-openoffice-biff-8;windows_formatname=\"Biff8\"",
112 "_CITRIX_Biff8",
113 "Biff5",
114 "Biff",
115 // ----------
116 "application/x-openoffice;windows_formatname=\"Star Embed Source (XML)\"",
117 "Star Embed Source (XML)",
118 "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"",
119 // ----------
120 "image/svg+xml",
121 "image/x-wmf",
122 "image/x-emf",
123 "image/png",
124 "image/jpeg",
125 "image/bmp",
126 // ----------
127 "text/uri-list",
128 "x-special/gnome-copied-files",
129 "application/x-kde-cutselection",
130 // ----------
131 "SAVE_TARGETS",
134 static GdkAtom atoms[G_N_ELEMENTS(atom_names)];
136 typedef enum {
137 INFO_UNKNOWN,
138 INFO_GNUMERIC,
139 INFO_EXCEL,
140 INFO_OOO,
141 INFO_GENERIC_TEXT,
142 INFO_HTML,
143 INFO_OBJECT,
144 INFO_IMAGE,
145 } AtomInfoType;
147 static GtkTargetList *generic_text_targets;
148 static GtkTargetList *image_targets;
150 // ----------------------------------------------------------------------------
152 typedef struct {
153 WBCGtk *wbcg;
154 GnmPasteTarget *paste_target;
155 } GnmGtkClipboardCtxt;
157 static void
158 gnm_gtk_clipboard_context_free (GnmGtkClipboardCtxt *ctxt)
160 g_free (ctxt->paste_target);
161 g_free (ctxt);
165 * Emacs hack:
166 * (x-get-selection-internal 'CLIPBOARD 'TARGETS)
169 static gboolean
170 has_file_opener (const char *id)
172 return go_file_opener_for_id (id) != NULL;
175 static gboolean
176 has_file_saver (const char *id)
178 return go_file_saver_for_id (id) != NULL;
183 static void
184 paste_from_gnumeric (GtkSelectionData *selection_data, GdkAtom target,
185 gconstpointer data, gssize size)
187 if (size < 0)
188 size = 0;
190 if (debug_clipboard_dump) {
191 g_file_set_contents ("paste-from-gnumeric.dat",
192 data, size, NULL);
195 if (debug_clipboard) {
196 char *target_name = gdk_atom_name (target);
197 g_printerr ("clipboard %s of %d bytes\n",
198 target_name, (int)size);
199 g_free (target_name);
202 gtk_selection_data_set (selection_data, target, 8, data, size);
205 static void
206 paste_to_gnumeric (GtkSelectionData *sel, const char *typ)
208 GdkAtom target = gtk_selection_data_get_target (sel);
209 gconstpointer buffer = gtk_selection_data_get_data (sel);
210 int sel_len = gtk_selection_data_get_length (sel);
212 if (sel_len < 0)
213 sel_len = 0;
215 if (debug_clipboard) {
216 int maxlen = 1024;
217 char *name = gdk_atom_name (target);
218 g_printerr ("Received %d bytes of %s for target %s\n",
219 sel_len, typ, name);
220 g_free (name);
221 if (sel_len > 0) {
222 gsf_mem_dump (buffer, MIN (sel_len, maxlen));
223 if (sel_len > maxlen)
224 g_printerr ("...\n");
228 if (debug_clipboard_dump) {
229 g_file_set_contents ("paste-to-gnumeric.dat",
230 buffer, sel_len, NULL);
235 /* See if this is a "single line + line end", a "multiline" or a "tab separated"
236 * string. If this is _not_ the case we won't invoke the STF, it is
237 * unlikely that the user will actually need it in this case. */
238 static gboolean
239 text_is_single_cell (gchar const *data, int data_len)
241 int i;
243 for (i = 0; i < data_len; i++)
244 if (data[i] == '\n' || data[i] == '\t')
245 return FALSE;
246 return TRUE;
250 static GnmCellRegion *
251 text_to_cell_region (WBCGtk *wbcg,
252 gchar const *data, int data_len,
253 char const *opt_encoding,
254 gboolean fixed_encoding)
256 Workbook *wb = wb_control_get_workbook (GNM_WBC (wbcg));
257 DialogStfResult_t *dialogresult;
258 GnmCellRegion *cr = NULL;
259 gboolean oneline;
260 char *data_converted = NULL;
262 if (!data) {
264 * See Redhat #1160975.
266 * I'm unsure why someone gets NULL here, but this is better
267 * than a crash.
269 data = "";
270 data_len = 0;
273 oneline = text_is_single_cell (data, data_len);
275 if (oneline && (opt_encoding == NULL || strcmp (opt_encoding, "UTF-8") != 0)) {
276 size_t bytes_written;
277 char const *enc = opt_encoding ? opt_encoding : "ASCII";
279 data_converted = g_convert (data, data_len,
280 "UTF-8", enc,
281 NULL, &bytes_written, NULL);
282 if (data_converted) {
283 data = data_converted;
284 data_len = bytes_written;
285 } else {
286 /* Force STF import since we don't know the charset. */
287 oneline = FALSE;
288 fixed_encoding = FALSE;
292 if (oneline) {
293 GODateConventions const *date_conv = workbook_date_conv (wb);
294 GnmCellCopy *cc = gnm_cell_copy_new (
295 (cr = gnm_cell_region_new (NULL)), 0, 0);
296 char *tmp = g_strndup (data, data_len);
298 g_free (data_converted);
300 cc->val = format_match (tmp, NULL, date_conv);
301 if (cc->val)
302 g_free (tmp);
303 else
304 cc->val = value_new_string_nocopy (tmp);
305 cc->texpr = NULL;
307 cr->cols = cr->rows = 1;
308 } else {
309 dialogresult = stf_dialog (wbcg, opt_encoding, fixed_encoding,
310 NULL, FALSE,
311 _("clipboard"), data, data_len);
313 if (dialogresult != NULL) {
314 cr = stf_parse_region (dialogresult->parseoptions,
315 dialogresult->text, NULL, wb);
316 g_return_val_if_fail (cr != NULL, gnm_cell_region_new (NULL));
318 stf_dialog_result_attach_formats_to_cr (dialogresult, cr);
320 stf_dialog_result_free (dialogresult);
321 } else
322 cr = gnm_cell_region_new (NULL);
325 return cr;
328 static void
329 text_content_received (GtkClipboard *clipboard, GtkSelectionData *sel,
330 gpointer closure)
332 GnmGtkClipboardCtxt *ctxt = closure;
333 WBCGtk *wbcg = ctxt->wbcg;
334 WorkbookControl *wbc = GNM_WBC (wbcg);
335 GnmPasteTarget *pt = ctxt->paste_target;
336 GnmCellRegion *content = NULL;
337 GdkAtom target = gtk_selection_data_get_target (sel);
338 int sel_len = gtk_selection_data_get_length (sel);
340 paste_to_gnumeric (sel, "text");
342 /* Nothing on clipboard? */
343 if (sel_len < 0) {
345 } else if (target == atoms[ATOM_UTF8_STRING]) {
346 content = text_to_cell_region (wbcg, (const char *)gtk_selection_data_get_data (sel),
347 sel_len, "UTF-8", TRUE);
348 } else if (target == atoms[ATOM_COMPOUND_TEXT]) {
349 /* COMPOUND_TEXT is icky. Just let GTK+ do the work. */
350 char *data_utf8 = (char *)gtk_selection_data_get_text (sel);
351 content = text_to_cell_region (wbcg, data_utf8, strlen (data_utf8), "UTF-8", TRUE);
352 g_free (data_utf8);
353 } else if (target == atoms[ATOM_STRING]) {
354 char const *locale_encoding;
355 g_get_charset (&locale_encoding);
357 content = text_to_cell_region (wbcg, (const char *)gtk_selection_data_get_data (sel),
358 sel_len, locale_encoding, FALSE);
360 if (content) {
362 * if the conversion from the X selection -> a cellregion
363 * was canceled this may have content sized -1,-1
365 if (content->cols > 0 && content->rows > 0)
366 cmd_paste_copy (wbc, pt, content);
368 /* Release the resources we used */
369 cellregion_unref (content);
372 gnm_gtk_clipboard_context_free (ctxt);
375 static void
376 utf8_content_received (GtkClipboard *clipboard, const gchar *text,
377 gpointer closure)
379 GnmGtkClipboardCtxt *ctxt = closure;
380 WBCGtk *wbcg = ctxt->wbcg;
381 WorkbookControl *wbc = GNM_WBC (wbcg);
382 GnmPasteTarget *pt = ctxt->paste_target;
383 GnmCellRegion *content = NULL;
385 /* Nothing on clipboard? */
386 if (!text || strlen(text) == 0) {
388 } else {
389 content = text_to_cell_region (wbcg, text, strlen(text), "UTF-8", TRUE);
391 if (content) {
393 * if the conversion from the X selection -> a cellregion
394 * was canceled this may have content sized -1,-1
396 if (content->cols > 0 && content->rows > 0)
397 cmd_paste_copy (wbc, pt, content);
399 /* Release the resources we used */
400 cellregion_unref (content);
403 gnm_gtk_clipboard_context_free (ctxt);
407 * Use the file_opener plugin service to read into a temporary workbook, in
408 * order to copy from it to the paste target. A temporary sheet would do just
409 * as well, but the file_opener service makes workbooks, not sheets.
411 * We use the file_opener service by wrapping the selection data in a GsfInput,
412 * and calling workbook_view_new_from_input.
414 static GnmCellRegion *
415 table_cellregion_read (WorkbookControl *wbc, char const *reader_id,
416 GnmPasteTarget *pt, const guchar *buffer, int length)
418 WorkbookView *wb_view = NULL;
419 Workbook *wb = NULL;
420 GnmCellRegion *ret = NULL;
421 const GOFileOpener *reader = go_file_opener_for_id (reader_id);
422 GOIOContext *ioc;
423 GsfInput *input;
425 if (!reader) {
426 // Likely cause: plugin not loaded
427 g_warning ("No file opener for %s", reader_id);
428 return NULL;
431 ioc = go_io_context_new (GO_CMD_CONTEXT (wbc));
432 input = gsf_input_memory_new (buffer, length, FALSE);
433 wb_view = workbook_view_new_from_input (input, NULL, reader, ioc, NULL);
434 if (go_io_error_occurred (ioc) || wb_view == NULL) {
435 go_io_error_display (ioc);
436 goto out;
439 wb = wb_view_get_workbook (wb_view);
440 if (workbook_sheet_count (wb) > 0) {
441 GnmRange r;
442 Sheet *tmpsheet = workbook_sheet_by_index (wb, 0);
443 GnmRange *rp = g_object_get_data (G_OBJECT (tmpsheet),
444 "DIMENSION");
445 if (rp) {
446 r = *rp;
447 } else {
448 // File format didn't tell us the range being
449 // pasted. Looking at you, LibreOffice!
450 // Make a guess.
452 GnmRange fullr;
453 GnmStyle **col_defaults =
454 sheet_style_most_common (tmpsheet, TRUE);
456 range_init_full_sheet (&fullr, tmpsheet);
458 r = sheet_get_cells_extent (tmpsheet);
459 sheet_style_get_nondefault_extent
460 (tmpsheet, &r, &fullr, col_defaults);
462 g_free (col_defaults);
464 // Just in case there was absolutely nothing in
465 // tmpsheet:
466 if (r.start.col > r.end.col)
467 range_init (&r, 0, 0, 0, 0);
469 ret = clipboard_copy_range (tmpsheet, &r);
472 /* This isn't particularly right, but we are going to delete
473 the workbook shortly. See #490479. */
474 WORKBOOK_FOREACH_SHEET (wb, sheet, {
475 cellregion_invalidate_sheet (ret, sheet);
478 out:
479 if (wb_view)
480 g_object_unref (wb_view);
481 if (wb)
482 g_object_unref (wb);
483 g_object_unref (ioc);
484 g_object_unref (input);
486 return ret;
489 static void
490 image_content_received (GtkClipboard *clipboard, GtkSelectionData *sel,
491 gpointer closure)
493 GnmGtkClipboardCtxt *ctxt = closure;
494 WBCGtk *wbcg = ctxt->wbcg;
495 GnmPasteTarget *pt = ctxt->paste_target;
496 int sel_len = gtk_selection_data_get_length (sel);
498 paste_to_gnumeric (sel, "image");
500 if (sel_len > 0) {
501 scg_paste_image (wbcg_cur_scg (wbcg), &pt->range,
502 gtk_selection_data_get_data (sel), sel_len);
505 gnm_gtk_clipboard_context_free (ctxt);
508 static void
509 urilist_content_received (GtkClipboard *clipboard, GtkSelectionData *sel,
510 gpointer closure)
512 GnmGtkClipboardCtxt *ctxt = closure;
513 WBCGtk *wbcg = ctxt->wbcg;
514 GnmPasteTarget *pt = ctxt->paste_target;
515 int sel_len = gtk_selection_data_get_length (sel);
517 paste_to_gnumeric (sel, "urilist");
519 if (sel_len > 0) {
520 char *text = g_strndup (gtk_selection_data_get_data (sel), sel_len);
521 GSList *uris = go_file_split_urls (text);
522 GSList *l;
523 g_free (text);
525 for (l = uris; l; l = l->next) {
526 const char *uri = l->data;
527 GsfInput *input;
528 gsf_off_t size;
529 gconstpointer data;
530 char *mime;
531 gboolean qimage;
533 if (g_str_equal (uri, "copy"))
534 continue;
535 mime = go_get_mime_type (uri);
536 qimage = (strncmp (mime, "image/", 6) == 0);
537 g_free (mime);
538 if (!qimage)
539 continue;
541 input = go_file_open (uri, NULL);
542 if (!input)
543 continue;
544 size = gsf_input_size (input);
545 data = gsf_input_read (input, size, NULL);
546 if (data)
547 scg_paste_image (wbcg_cur_scg (wbcg), &pt->range,
548 data, size);
549 g_object_unref (input);
552 g_slist_free_full (uris, g_free);
556 gnm_gtk_clipboard_context_free (ctxt);
559 static void
560 parse_ms_headers (const char *data, size_t length, size_t *start, size_t *end)
562 GHashTable *headers = g_hash_table_new_full
563 (g_str_hash, g_str_equal, g_free, g_free);
564 size_t limit = length;
565 size_t i = 0;
566 char *key = NULL;
567 char *value = NULL;
568 long sf, ef;
569 const char *v;
571 while (i < limit && data[i] != '<') {
572 size_t j, k;
574 for (j = i; j < limit; j++) {
575 if (data[j] == ':') {
576 key = g_strndup (data + i, j - i);
577 break;
579 if (g_ascii_isspace (data[j]))
580 goto bad;
582 if (j >= limit)
583 goto bad;
584 j++;
586 for (k = j; k < limit; k++) {
587 if (data[k] == '\n' || data[k] == '\r') {
588 value = g_strndup (data + j, k - j);
589 break;
592 if (k >= limit)
593 goto bad;
594 while (g_ascii_isspace (data[k]))
595 k++;
597 i = k;
599 if (debug_clipboard)
600 g_printerr ("MS HTML Header [%s] => [%s]\n", key, value);
602 if (strcmp (key, "StartHTML") == 0) {
603 long l = strtol (value, NULL, 10);
604 limit = MIN (limit, (size_t)MAX (0, l));
607 g_hash_table_replace (headers, key, value);
608 key = value = NULL;
611 v = g_hash_table_lookup (headers, "StartFragment");
612 sf = v ? strtol (v, NULL, 10) : -1;
613 if (sf < (long)limit)
614 goto bad;
616 v = g_hash_table_lookup (headers, "EndFragment");
617 ef = v ? strtol (v, NULL, 10) : -1;
618 if (ef < sf || ef > (long)length)
619 goto bad;
621 *start = sf;
622 *end = ef;
623 goto out;
625 bad:
626 g_free (key);
627 g_free (value);
628 *start = 0;
629 *end = length;
631 out:
632 g_hash_table_destroy (headers);
635 static void
636 table_content_received (GtkClipboard *clipboard, GtkSelectionData *sel,
637 gpointer closure)
639 GnmGtkClipboardCtxt *ctxt = closure;
640 WBCGtk *wbcg = ctxt->wbcg;
641 WorkbookControl *wbc = GNM_WBC (wbcg);
642 GnmPasteTarget *pt = ctxt->paste_target;
643 GnmCellRegion *content = NULL;
644 GdkAtom target = gtk_selection_data_get_target (sel);
645 const guint8 *buffer = gtk_selection_data_get_data (sel);
646 int sel_len = gtk_selection_data_get_length (sel);
648 paste_to_gnumeric (sel, "table");
650 /* Nothing on clipboard? */
651 if (sel_len < 0) {
653 } else if (target == atoms[ATOM_GNUMERIC]) {
654 /* The data is the gnumeric specific XML interchange format */
655 GOIOContext *io_context =
656 go_io_context_new (GO_CMD_CONTEXT (wbcg));
657 content = gnm_xml_cellregion_read
658 (wbc, io_context,
659 pt->sheet,
660 (const char *)buffer, sel_len);
661 g_object_unref (io_context);
662 } else if (target == atoms[ATOM_OOO] ||
663 target == atoms[ATOM_OOO_WINDOWS] ||
664 target == atoms[ATOM_OOO11]) {
665 content = table_cellregion_read (wbc, OOO_FILE_OPENER,
666 pt, buffer,
667 sel_len);
668 } else if (target == atoms[ATOM_TEXT_HTML] ||
669 target == atoms[ATOM_TEXT_HTML_WINDOWS]) {
670 size_t start = 0, end = sel_len;
672 if (target == atoms[ATOM_TEXT_HTML_WINDOWS]) {
673 /* See bug 143084 */
674 parse_ms_headers (buffer, sel_len, &start, &end);
677 content = table_cellregion_read (wbc, HTML_FILE_OPENER,
679 buffer + start,
680 end - start);
681 } else if (target == atoms[ATOM_BIFF8] ||
682 target == atoms[ATOM_BIFF8_CITRIX] ||
683 target == atoms[ATOM_BIFF8_OO] ||
684 target == atoms[ATOM_BIFF5] ||
685 target == atoms[ATOM_BIFF]) {
686 content = table_cellregion_read (wbc, EXCEL_FILE_OPENER,
687 pt, buffer,
688 sel_len);
690 if (content) {
692 * if the conversion from the X selection -> a cellregion
693 * was canceled this may have content sized -1,-1
695 if ((content->cols > 0 && content->rows > 0) ||
696 content->objects != NULL)
697 cmd_paste_copy (wbc, pt, content);
699 /* Release the resources we used */
700 cellregion_unref (content);
703 gnm_gtk_clipboard_context_free (ctxt);
706 static gboolean
707 find_in_table (GdkAtom *targets, int n, GdkAtom a)
709 int i;
710 for (i = 0; i < n; i++)
711 if (targets[i] == a)
712 return TRUE;
713 return FALSE;
717 * x_targets_received:
719 * Invoked when the selection has been received by our application.
720 * This is triggered by a call we do to gtk_clipboard_request_contents.
722 * We try to import a spreadsheet/table, next an image, and finally fall back
723 * to a string format if the others fail, e.g. for html which does not
724 * contain a table.
726 static void
727 x_targets_received (GtkClipboard *clipboard, GdkAtom *targets,
728 gint n_targets, gpointer closure)
730 GnmGtkClipboardCtxt *ctxt = closure;
731 int i;
732 unsigned ui;
734 // In order of preference
735 static const struct {
736 int a;
737 const char *opener_id;
738 } table_fmts[] = {
739 { ATOM_GNUMERIC, NULL },
740 { ATOM_BIFF8, EXCEL_FILE_OPENER },
741 { ATOM_BIFF8_CITRIX, EXCEL_FILE_OPENER },
742 { ATOM_OOO, OOO_FILE_OPENER },
743 { ATOM_OOO11, OOO_FILE_OPENER },
744 { ATOM_OOO_WINDOWS, OOO_FILE_OPENER },
745 { ATOM_BIFF5, EXCEL_FILE_OPENER },
746 { ATOM_BIFF, EXCEL_FILE_OPENER },
747 { ATOM_TEXT_HTML, HTML_FILE_OPENER },
748 { ATOM_TEXT_HTML_WINDOWS, HTML_FILE_OPENER },
751 // In order of preference
752 static const int uri_list_fmts[] = {
753 ATOM_TEXT_URI_LIST,
754 ATOM_GNOME_COPIED_FILES,
755 ATOM_KDE_CUT_FILES,
758 // In order of preference
759 static const int string_fmts[] = {
760 ATOM_UTF8_STRING,
761 ATOM_STRING,
762 ATOM_COMPOUND_TEXT
765 // Nothing on clipboard? Try text.
766 if (targets == NULL || n_targets == 0) {
767 gtk_clipboard_request_text (clipboard, utf8_content_received,
768 ctxt);
769 return;
772 if (debug_clipboard) {
773 int j;
775 for (j = 0; j < n_targets; j++) {
776 char *name = gdk_atom_name (targets[j]);
777 g_printerr ("Clipboard target %d is %s\n",
778 j, name);
779 g_free (name);
783 // First look for anything that can be considered a spreadsheet
784 for (ui = 0; ui < G_N_ELEMENTS(table_fmts); ui++) {
785 GdkAtom atom = atoms[table_fmts[ui].a];
786 const char *opener = table_fmts[ui].opener_id;
787 if ((opener == NULL || has_file_opener (opener)) &&
788 find_in_table (targets, n_targets, atom)) {
789 gtk_clipboard_request_contents (clipboard, atom,
790 table_content_received,
791 ctxt);
792 return;
796 // Try an image format
797 for (i = 0; i < n_targets; i++) {
798 GdkAtom atom = targets[i];
799 if (gtk_target_list_find (image_targets, atom, NULL)) {
800 gtk_clipboard_request_contents (clipboard, atom,
801 image_content_received,
802 ctxt);
803 return;
807 // Try a uri list format
808 for (ui = 0; ui < G_N_ELEMENTS (uri_list_fmts); ui++) {
809 GdkAtom atom = atoms[uri_list_fmts[ui]];
810 if (find_in_table (targets, n_targets, atom)) {
811 gtk_clipboard_request_contents (clipboard, atom,
812 urilist_content_received,
813 ctxt);
814 return;
818 // Try a string format
819 for (ui = 0; ui < G_N_ELEMENTS (string_fmts); ui++) {
820 GdkAtom atom = atoms[string_fmts[ui]];
821 if (find_in_table (targets, n_targets, atom)) {
822 gtk_clipboard_request_contents (clipboard, atom,
823 text_content_received,
824 ctxt);
825 return;
829 // Give up
830 gnm_gtk_clipboard_context_free (ctxt);
833 /* Cheezy implementation: paste into a temporary workbook, save that. */
834 static guchar *
835 table_cellregion_write (GOCmdContext *ctx, GnmCellRegion *cr,
836 const char *saver_id, int *size)
838 guchar *ret = NULL;
839 const GOFileSaver *saver;
840 GsfOutput *output;
841 GOIOContext *ioc;
842 Workbook *wb;
843 WorkbookView *wb_view;
844 Sheet *sheet;
845 GnmPasteTarget pt;
846 GnmRange r;
848 if (debug_clipboard_undump) {
849 gsize siz;
850 gchar *contents;
851 if (g_file_get_contents ("paste-from-gnumeric.dat", &contents,
852 &siz, NULL)) {
853 g_printerr ("Sending %d prepackaged bytes.\n",
854 (int)siz);
855 *size = siz;
856 return (guchar *)contents;
860 *size = 0;
862 saver = go_file_saver_for_id (saver_id);
863 if (!saver) {
864 // Likely cause: plugin not loaded
865 g_printerr ("Failed to get saver for %s for clipboard use.\n",
866 saver_id);
867 return NULL;
870 output = gsf_output_memory_new ();
871 ioc = go_io_context_new (ctx);
874 int cols = cr->cols;
875 int rows = cr->rows;
876 gnm_sheet_suggest_size (&cols, &rows);
877 wb = workbook_new ();
878 workbook_sheet_add (wb, -1, cols, rows);
881 wb_view = workbook_view_new (wb);
883 sheet = workbook_sheet_by_index (wb, 0);
884 range_init (&r, 0, 0, cr->cols - 1, cr->rows - 1);
886 paste_target_init (&pt, sheet, &r,
887 PASTE_AS_VALUES | PASTE_FORMATS |
888 PASTE_COMMENTS | PASTE_OBJECTS);
889 if (clipboard_paste_region (cr, &pt, ctx) == FALSE) {
890 go_file_saver_save (saver, ioc, GO_VIEW (wb_view), output);
891 if (!go_io_error_occurred (ioc)) {
892 GsfOutputMemory *omem = GSF_OUTPUT_MEMORY (output);
893 gsf_off_t osize = gsf_output_size (output);
894 const guint8 *data = gsf_output_memory_get_bytes (omem);
896 *size = osize;
897 if (*size == osize) {
898 ret = g_memdup (data, *size);
899 } else {
900 g_warning ("Overflow"); /* Far fetched! */
904 if (!gsf_output_is_closed (output))
905 gsf_output_close (output);
906 g_object_unref (wb_view);
907 g_object_unref (wb);
908 g_object_unref (ioc);
909 g_object_unref (output);
911 return ret;
914 static guchar *
915 image_write (GnmCellRegion *cr, gchar const *mime_type, int *size)
917 guchar *ret = NULL;
918 SheetObject *so = NULL;
919 char *format;
920 GsfOutput *output;
921 GsfOutputMemory *omem;
922 gsf_off_t osize;
923 GSList *l;
925 *size = -1;
927 g_return_val_if_fail (cr->objects != NULL, NULL);
928 so = GNM_SO (cr->objects->data);
929 g_return_val_if_fail (so != NULL, NULL);
931 for (l = cr->objects; l != NULL; l = l->next) {
932 if (GNM_IS_SO_IMAGEABLE (GNM_SO (l->data))) {
933 so = GNM_SO (l->data);
934 break;
937 if (so == NULL) {
938 // This shouldn't happen
939 g_warning ("non-imageable object requested as image\n");
940 return ret;
943 format = go_mime_to_image_format (mime_type);
944 if (!format) {
945 // This shouldn't happen
946 g_warning ("No image format for %s\n", mime_type);
947 return ret;
950 output = gsf_output_memory_new ();
951 omem = GSF_OUTPUT_MEMORY (output);
952 sheet_object_write_image (so, format, 150.0, output, NULL);
953 osize = gsf_output_size (output);
955 *size = osize;
956 if (*size == osize) {
957 ret = g_malloc (*size);
958 memcpy (ret, gsf_output_memory_get_bytes (omem), *size);
959 } else {
960 g_warning ("Overflow"); /* Far fetched! */
962 gsf_output_close (output);
963 g_object_unref (output);
964 g_free (format);
966 return ret;
969 static guchar *
970 object_write (GnmCellRegion *cr, gchar const *mime_type, int *size)
972 guchar *ret = NULL;
973 SheetObject *so = NULL;
974 GsfOutput *output;
975 GsfOutputMemory *omem;
976 gsf_off_t osize;
977 GSList *l;
979 *size = -1;
981 g_return_val_if_fail (cr->objects != NULL, NULL);
982 so = GNM_SO (cr->objects->data);
983 g_return_val_if_fail (so != NULL, NULL);
985 for (l = cr->objects; l != NULL; l = l->next) {
986 if (GNM_IS_SO_EXPORTABLE (GNM_SO (l->data))) {
987 so = GNM_SO (l->data);
988 break;
991 if (so == NULL) {
992 g_warning ("non exportable object requested\n");
993 return ret;
995 output = gsf_output_memory_new ();
996 omem = GSF_OUTPUT_MEMORY (output);
997 sheet_object_write_object (so, mime_type, output, NULL,
998 gnm_conventions_default);
999 osize = gsf_output_size (output);
1001 *size = osize;
1002 if (*size == osize)
1003 ret = g_memdup (gsf_output_memory_get_bytes (omem), *size);
1004 else
1005 g_warning ("Overflow"); /* Far fetched! */
1006 gsf_output_close (output);
1007 g_object_unref (output);
1009 return ret;
1013 * x_clipboard_get_cb
1015 * Callback invoked when another application requests we render the selection.
1017 static void
1018 x_clipboard_get_cb (GtkClipboard *gclipboard,
1019 GtkSelectionData *selection_data,
1020 guint info_, G_GNUC_UNUSED gpointer app)
1022 gboolean to_gnumeric = FALSE, content_needs_free = FALSE;
1023 GnmCellRegion *clipboard = gnm_app_clipboard_contents_get ();
1024 Sheet *sheet = gnm_app_clipboard_sheet_get ();
1025 GnmRange const *a = gnm_app_clipboard_area_get ();
1026 GOCmdContext *ctx = gnm_cmd_context_stderr_new ();
1027 GdkAtom target = gclipboard
1028 ? gtk_selection_data_get_target (selection_data)
1029 : gtk_selection_data_get_data_type (selection_data); // testing
1030 AtomInfoType info = info_;
1031 gchar *target_name = gdk_atom_name (target);
1033 if (debug_clipboard)
1034 g_printerr ("clipboard requested, target=%s\n", target_name);
1037 * There are 4 cases. What variables are valid depends on case:
1038 * source is
1039 * a cut: clipboard NULL, sheet, area non-NULL.
1040 * a copy: clipboard, sheet, area all non-NULL.
1041 * a cut, source closed: clipboard, sheet, area all NULL.
1042 * a copy, source closed: clipboard non-NULL, sheet, area non-NULL.
1044 * If the source is a cut, we copy it for pasting. We
1045 * postpone clearing it until after the selection has been
1046 * rendered to the requested format.
1048 if (clipboard == NULL && sheet != NULL) {
1049 content_needs_free = TRUE;
1050 clipboard = clipboard_copy_range (sheet, a);
1053 if (clipboard == NULL)
1054 goto out;
1056 /* What format does the other application want? */
1057 if (target == atoms[ATOM_GNUMERIC]) {
1058 GsfOutputMemory *output = gnm_cellregion_to_xml (clipboard);
1059 if (output) {
1060 gsf_off_t size = gsf_output_size (GSF_OUTPUT (output));
1061 gconstpointer data = gsf_output_memory_get_bytes (output);
1063 paste_from_gnumeric (selection_data, target,
1064 data, size);
1065 g_object_unref (output);
1066 to_gnumeric = TRUE;
1068 } else if (info == INFO_HTML) {
1069 int size;
1070 guchar *buffer = table_cellregion_write (ctx, clipboard,
1071 HTML_FILE_SAVER,
1072 &size);
1073 paste_from_gnumeric (selection_data, target, buffer, size);
1074 g_free (buffer);
1075 } else if (info == INFO_EXCEL) {
1076 int size;
1077 guchar *buffer = table_cellregion_write (ctx, clipboard,
1078 EXCEL_FILE_SAVER,
1079 &size);
1080 paste_from_gnumeric (selection_data, target, buffer, size);
1081 g_free (buffer);
1082 } else if (target == atoms[ATOM_GOFFICE_GRAPH] ||
1083 g_slist_find_custom (go_components_get_mime_types (), target_name, (GCompareFunc) strcmp) != NULL) {
1084 int size;
1085 guchar *buffer = object_write (clipboard, target_name, &size);
1086 paste_from_gnumeric (selection_data, target, buffer, size);
1087 g_free (buffer);
1088 } else if (info == INFO_IMAGE) {
1089 int size;
1090 guchar *buffer = image_write (clipboard, target_name, &size);
1091 paste_from_gnumeric (selection_data, target, buffer, size);
1092 g_free (buffer);
1093 } else if (target == atoms[ATOM_SAVE_TARGETS]) {
1094 // We implicitly registered this target when calling
1095 // gtk_clipboard_set_can_store. We're supposed to ignore it.
1096 } else if (info == INFO_GENERIC_TEXT) {
1097 Workbook *wb = clipboard->origin_sheet->workbook;
1098 GString *res = cellregion_to_string (clipboard,
1099 TRUE, workbook_date_conv (wb));
1100 if (res != NULL) {
1101 if (debug_clipboard)
1102 g_message ("clipboard text of %d bytes",
1103 (int)res->len);
1104 gtk_selection_data_set_text (selection_data,
1105 res->str, res->len);
1106 g_string_free (res, TRUE);
1107 } else {
1108 if (debug_clipboard)
1109 g_message ("clipboard empty text");
1110 gtk_selection_data_set_text (selection_data, "", 0);
1112 } else
1113 gtk_selection_data_set_text (selection_data, "", 0);
1116 * If this was a CUT operation we need to clear the content that
1117 * was pasted into another application and release the stuff on
1118 * the clipboard
1120 if (content_needs_free) {
1121 /* If the other app was a gnumeric, emulate a cut */
1122 if (to_gnumeric) {
1123 GOUndo *redo, *undo;
1124 GnmSheetRange *sr = gnm_sheet_range_new (sheet, a);
1125 SheetView const *sv = gnm_app_clipboard_sheet_view_get ();
1126 SheetControl *sc = g_ptr_array_index (sv->controls, 0);
1127 WorkbookControl *wbc = sc_wbc (sc);
1128 char *name;
1129 char *text;
1131 redo = sheet_clear_region_undo
1132 (sr,
1133 CLEAR_VALUES|CLEAR_COMMENTS|CLEAR_RECALC_DEPS);
1134 undo = clipboard_copy_range_undo (sheet, a);
1135 name = undo_range_name (sheet, a);
1136 text = g_strdup_printf (_("Cut of %s"), name);
1137 g_free (name);
1138 cmd_generic (wbc, text, undo, redo);
1139 g_free (text);
1140 gnm_app_clipboard_clear (TRUE);
1143 cellregion_unref (clipboard);
1145 out:
1146 g_free (target_name);
1147 g_object_unref (ctx);
1151 * x_clipboard_clear_cb:
1153 * Callback for the "we lost the X selection" signal.
1155 static void
1156 x_clipboard_clear_cb (GtkClipboard *clipboard, gpointer app_)
1158 if (debug_clipboard)
1159 g_printerr ("Lost clipboard ownership.\n");
1161 gnm_app_clipboard_clear (FALSE);
1164 void
1165 gnm_x_request_clipboard (WBCGtk *wbcg, GnmPasteTarget const *pt)
1167 GnmGtkClipboardCtxt *ctxt;
1168 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (wbcg_toplevel (wbcg)));
1169 GtkClipboard *clipboard =
1170 gtk_clipboard_get_for_display
1171 (display,
1172 gnm_conf_get_cut_and_paste_prefer_clipboard ()
1173 ? GDK_SELECTION_CLIPBOARD
1174 : GDK_SELECTION_PRIMARY);
1176 ctxt = g_new (GnmGtkClipboardCtxt, 1);
1177 ctxt->wbcg = wbcg;
1178 ctxt->paste_target = g_memdup (pt, sizeof (*pt));
1180 /* Query the formats, This will callback x_targets_received */
1181 gtk_clipboard_request_targets (clipboard,
1182 x_targets_received, ctxt);
1185 static void
1186 cb_clear_target_entry (gpointer te_)
1188 GtkTargetEntry *te = te_;
1189 g_free (te->target);
1192 static void
1193 add_target (GArray *targets, const char *target, int flags, AtomInfoType info)
1195 GtkTargetEntry t;
1196 t.target = g_strdup (target);
1197 t.flags = flags;
1198 t.info = info;
1199 g_array_append_val (targets, t);
1202 static gboolean
1203 is_clipman_target (const char *target)
1205 return (g_str_equal (target, atom_names[ATOM_GNUMERIC]) ||
1206 g_str_equal (target, atom_names[ATOM_GOFFICE_GRAPH]) ||
1207 g_str_equal (target, atom_names[ATOM_TEXT_HTML]) ||
1208 g_str_equal (target, atom_names[ATOM_UTF8_STRING]) ||
1209 g_str_equal (target, atom_names[ATOM_BIFF8_OO]) ||
1210 g_str_equal (target, atom_names[ATOM_IMAGE_SVGXML]) ||
1211 g_str_equal (target, atom_names[ATOM_IMAGE_XWMF]) ||
1212 g_str_equal (target, atom_names[ATOM_IMAGE_XEMF]) ||
1213 g_str_equal (target, atom_names[ATOM_IMAGE_PNG]) ||
1214 g_str_equal (target, atom_names[ATOM_IMAGE_JPEG]));
1217 /* Restrict the set of formats offered to clipboard manager. */
1218 static void
1219 set_clipman_targets (GdkDisplay *disp, GArray *targets)
1221 GArray *allowed = g_array_new (FALSE, FALSE, sizeof (GtkTargetEntry));
1222 unsigned ui;
1224 g_array_set_clear_func (allowed, cb_clear_target_entry);
1226 for (ui = 0; ui < targets->len; ui++) {
1227 GtkTargetEntry *te = &g_array_index (targets, GtkTargetEntry, ui);
1228 if (is_clipman_target (te->target))
1229 add_target (allowed, te->target, te->flags, te->info);
1232 gtk_clipboard_set_can_store
1233 (gtk_clipboard_get_for_display
1234 (disp, GDK_SELECTION_CLIPBOARD),
1235 &g_array_index (allowed, GtkTargetEntry, 0),
1236 allowed->len);
1238 g_array_free (allowed, TRUE);
1241 static void
1242 add_target_list (GArray *targets, GtkTargetList *src, AtomInfoType info)
1244 int i, n;
1245 GtkTargetEntry *entries = gtk_target_table_new_from_list (src, &n);
1247 for (i = 0; i < n; i++) {
1248 GtkTargetEntry *te = entries + i;
1249 add_target (targets, te->target, te->flags,
1250 info == INFO_UNKNOWN ? te->info : info);
1253 gtk_target_table_free (entries, n);
1256 gboolean
1257 gnm_x_claim_clipboard (GdkDisplay *display)
1259 GnmCellRegion *content = gnm_app_clipboard_contents_get ();
1260 SheetObject *imageable = NULL, *exportable = NULL;
1261 GArray *targets = g_array_new (FALSE, FALSE, sizeof (GtkTargetEntry));
1262 gboolean ret;
1263 GObject *app = gnm_app_get_app ();
1264 gboolean no_cells = (!content) || (content->cols <= 0 || content->rows <= 0);
1266 g_array_set_clear_func (targets, cb_clear_target_entry);
1268 if (no_cells) {
1269 GSList *ptr = content ? content->objects : NULL;
1271 add_target (targets, atom_names[ATOM_GNUMERIC], 0, INFO_GNUMERIC);
1273 for (; ptr != NULL; ptr = ptr->next) {
1274 SheetObject *candidate = GNM_SO (ptr->data);
1275 if (exportable == NULL && GNM_IS_SO_EXPORTABLE (candidate))
1276 exportable = candidate;
1277 if (imageable == NULL && GNM_IS_SO_IMAGEABLE (candidate))
1278 imageable = candidate;
1280 } else {
1281 add_target (targets, atom_names[ATOM_GNUMERIC], 0, INFO_GNUMERIC);
1282 if (has_file_saver (EXCEL_FILE_SAVER)) {
1283 add_target (targets, atom_names[ATOM_BIFF8], 0, INFO_EXCEL);
1284 add_target (targets, atom_names[ATOM_BIFF8_CITRIX], 0, INFO_EXCEL);
1285 add_target (targets, atom_names[ATOM_BIFF8_OO], 0, INFO_EXCEL);
1287 if (has_file_saver (HTML_FILE_SAVER)) {
1288 #ifdef G_OS_WIN32
1289 add_target (targets, atom_names[ATOM_TEXT_HTML_WINDOWS], 0, INFO_HTML);
1290 #else
1291 add_target (targets, atom_names[ATOM_TEXT_HTML], 0, INFO_HTML);
1292 #endif
1294 add_target (targets, atom_names[ATOM_UTF8_STRING], 0, INFO_GENERIC_TEXT);
1295 add_target (targets, atom_names[ATOM_COMPOUND_TEXT], 0, INFO_GENERIC_TEXT);
1296 add_target (targets, atom_names[ATOM_STRING], 0, INFO_GENERIC_TEXT);
1299 if (exportable) {
1300 GtkTargetList *tl =
1301 sheet_object_exportable_get_target_list (exportable);
1302 add_target_list (targets, tl, INFO_OBJECT);
1303 gtk_target_list_unref (tl);
1306 if (imageable) {
1307 GtkTargetList *tl =
1308 sheet_object_get_target_list (imageable);
1309 add_target_list (targets, tl, INFO_IMAGE);
1310 gtk_target_list_unref (tl);
1313 /* Register a x_clipboard_clear_cb only for CLIPBOARD, not for
1314 * PRIMARY */
1315 ret = gtk_clipboard_set_with_owner (
1316 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD),
1317 &g_array_index(targets,GtkTargetEntry,0), targets->len,
1318 x_clipboard_get_cb,
1319 x_clipboard_clear_cb,
1320 app);
1321 if (ret) {
1322 if (debug_clipboard) {
1323 unsigned ui;
1324 g_printerr ("Clipboard successfully claimed.\n");
1325 g_printerr ("Clipboard targets offered: ");
1326 for (ui = 0; ui < targets->len; ui++) {
1327 g_printerr ("%s%s",
1328 (ui ? ", " : ""),
1329 g_array_index(targets,GtkTargetEntry,ui).target);
1331 g_printerr ("\n");
1334 g_object_set_data_full (app, APP_CLIP_DISP_KEY,
1335 g_slist_prepend (g_object_steal_data (app, APP_CLIP_DISP_KEY),
1336 display),
1337 (GDestroyNotify)g_slist_free);
1339 set_clipman_targets (display, targets);
1340 (void)gtk_clipboard_set_with_owner (
1341 gtk_clipboard_get_for_display (display,
1342 GDK_SELECTION_PRIMARY),
1343 &g_array_index(targets,GtkTargetEntry,0), targets->len,
1344 x_clipboard_get_cb,
1345 NULL,
1346 app);
1347 } else {
1348 if (debug_clipboard)
1349 g_printerr ("Failed to claim clipboard.\n");
1352 g_array_free (targets, TRUE);
1354 return ret;
1357 void
1358 gnm_x_disown_clipboard (void)
1360 GObject *app = gnm_app_get_app ();
1361 GSList *displays = g_object_steal_data (app, APP_CLIP_DISP_KEY);
1362 GSList *l;
1364 for (l = displays; l; l = l->next) {
1365 GdkDisplay *display = l->data;
1366 gtk_selection_owner_set_for_display (display, NULL,
1367 GDK_SELECTION_PRIMARY,
1368 GDK_CURRENT_TIME);
1369 gtk_selection_owner_set_for_display (display, NULL,
1370 GDK_SELECTION_CLIPBOARD,
1371 GDK_CURRENT_TIME);
1373 g_slist_free (displays);
1376 /* Hand clipboard off to clipboard manager. To be called before workbook
1377 * object is destroyed.
1379 void
1380 gnm_x_store_clipboard_if_needed (Workbook *wb)
1382 Sheet *sheet = gnm_app_clipboard_sheet_get ();
1383 WBCGtk *wbcg = NULL;
1385 g_return_if_fail (GNM_IS_WORKBOOK (wb));
1387 if (sheet && sheet->workbook == wb) {
1388 WORKBOOK_FOREACH_CONTROL (wb, view, control, {
1389 if (GNM_IS_WBC_GTK (control)) {
1390 wbcg = WBC_GTK (control);
1394 if (wbcg) {
1395 GtkClipboard *clip = gtk_clipboard_get_for_display
1396 (gtk_widget_get_display
1397 (GTK_WIDGET (wbcg_toplevel (wbcg))),
1398 GDK_SELECTION_CLIPBOARD);
1399 if (gtk_clipboard_get_owner (clip) == gnm_app_get_app ()) {
1400 if (debug_clipboard)
1401 g_printerr ("Handing off clipboard\n");
1402 gtk_clipboard_store (clip);
1408 GBytes *
1409 gui_clipboard_test (const char *fmt)
1411 GtkClipboard *gclipboard = NULL;
1412 gpointer app = NULL;
1413 GtkSelectionData *selection_data;
1414 guint info;
1415 unsigned ui;
1416 GdkAtom atom = NULL;
1417 const guchar *data;
1418 gint len;
1419 GBytes *res;
1421 for (ui = 0; ui < G_N_ELEMENTS (atom_names); ui++) {
1422 if (g_str_equal (fmt, atom_names[ui])) {
1423 atom = atoms[ui];
1424 break;
1427 if (!atom)
1428 return NULL;
1430 switch (ui) {
1431 case ATOM_GNUMERIC:
1432 info = INFO_GNUMERIC;
1433 break;
1434 case ATOM_UTF8_STRING:
1435 case ATOM_STRING:
1436 case ATOM_COMPOUND_TEXT:
1437 info = INFO_GENERIC_TEXT;
1438 break;
1439 case ATOM_TEXT_HTML:
1440 case ATOM_TEXT_HTML_WINDOWS:
1441 info = INFO_HTML;
1442 break;
1443 case ATOM_BIFF8:
1444 case ATOM_BIFF8_OO:
1445 case ATOM_BIFF8_CITRIX:
1446 case ATOM_BIFF5:
1447 case ATOM_BIFF:
1448 info = INFO_EXCEL;
1449 break;
1450 case ATOM_OOO:
1451 case ATOM_OOO_WINDOWS:
1452 case ATOM_OOO11:
1453 info = INFO_OOO;
1454 break;
1455 case ATOM_IMAGE_SVGXML:
1456 case ATOM_IMAGE_XWMF:
1457 case ATOM_IMAGE_XEMF:
1458 case ATOM_IMAGE_PNG:
1459 case ATOM_IMAGE_JPEG:
1460 case ATOM_IMAGE_BMP:
1461 info = INFO_IMAGE;
1462 break;
1463 default:
1464 g_printerr ("Unknown info type\n");
1465 info = INFO_UNKNOWN;
1469 // This is more than a little bit dirty. There is no good
1470 // way to create a GtkSelectionData.
1471 void *empty = g_new0 (char, 1000000);
1472 selection_data = gtk_selection_data_copy (empty);
1473 g_free (empty);
1476 gtk_selection_data_set (selection_data, atom, 8, NULL, 0);
1477 // No way to set target???
1479 x_clipboard_get_cb (gclipboard, selection_data, info, app);
1480 data = gtk_selection_data_get_data_with_length (selection_data, &len);
1481 res = g_bytes_new (data, len);
1482 gtk_selection_data_free (selection_data);
1483 return res;
1488 * gui_clipboard_init: (skip)
1490 void
1491 gui_clipboard_init (void)
1493 unsigned ui;
1495 debug_clipboard = gnm_debug_flag ("clipboard");
1496 debug_clipboard_dump = gnm_debug_flag ("clipboard-dump");
1497 debug_clipboard_undump = gnm_debug_flag ("clipboard-undump");
1499 for (ui = 0; ui < G_N_ELEMENTS (atoms); ui++)
1500 atoms[ui] = gdk_atom_intern_static_string (atom_names[ui]);
1502 generic_text_targets = gtk_target_list_new (NULL, 0);
1503 gtk_target_list_add_text_targets (generic_text_targets, INFO_GENERIC_TEXT);
1505 image_targets = gtk_target_list_new (NULL, 0);
1506 gtk_target_list_add_image_targets (image_targets, 0, FALSE);
1510 * gui_clipboard_shutdown: (skip)
1512 void
1513 gui_clipboard_shutdown (void)
1515 gtk_target_list_unref (generic_text_targets);
1516 gtk_target_list_unref (image_targets);