1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * gui-clipboard.c: Implements the X11 based copy/paste operations
6 * Miguel de Icaza (miguel@gnu.org)
7 * Jody Goldberg (jody@gnome.org)
9 #include <gnumeric-config.h>
11 #include "gui-clipboard.h"
14 #include "clipboard.h"
15 #include "command-context-stderr.h"
16 #include "selection.h"
17 #include "application.h"
18 #include "workbook-control.h"
20 #include "workbook-priv.h"
22 #include "workbook-view.h"
25 #include "sheet-style.h"
26 #include "sheet-object.h"
27 #include "sheet-control-gui.h"
28 #include "sheet-view.h"
31 #include "number-match.h"
32 #include "dialog-stf.h"
33 #include "stf-parse.h"
35 #include "gnm-format.h"
36 #include "gnumeric-conf.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>
50 #define APP_CLIP_DISP_KEY "clipboard-displays"
53 debug_clipboard (void)
55 static gboolean d_clipboard
;
56 static gboolean inited
= FALSE
;
60 d_clipboard
= gnm_debug_flag ("clipboard");
68 GnmPasteTarget
*paste_target
;
71 } GnmGtkClipboardCtxt
;
73 /* The name of our clipboard atom and the 'magic' info number */
74 #define GNUMERIC_ATOM_NAME "application/x-gnumeric"
75 #define GNUMERIC_ATOM_INFO 2001
76 #define GOFFICE_GRAPH_ATOM_NAME "application/x-goffice-graph"
80 * (x-get-selection-internal 'CLIPBOARD 'TARGETS)
84 #define BIFF8_ATOM_NAME "Biff8"
85 #define BIFF8_ATOM_NAME_CITRIX "_CITRIX_Biff8"
86 #define BIFF5_ATOM_NAME "Biff5"
87 #define BIFF4_ATOM_NAME "Biff4"
88 #define BIFF3_ATOM_NAME "Biff3"
89 #define BIFF_ATOM_NAME "Biff"
91 #define HTML_ATOM_NAME_UNIX "text/html"
92 #define HTML_ATOM_NAME_WINDOWS "HTML Format"
94 #define HTML_ATOM_NAME HTML_ATOM_NAME_WINDOWS
96 #define HTML_ATOM_NAME HTML_ATOM_NAME_UNIX
99 #define OOO_ATOM_NAME "application/x-openoffice;windows_formatname=\"Star Embed Source (XML)\""
100 #define OOO11_ATOM_NAME "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
101 #define OOO_ATOM_NAME_WINDOWS "Star Embed Source (XML)"
103 #define UTF8_ATOM_NAME "UTF8_STRING"
104 #define CTEXT_ATOM_NAME "COMPOUND_TEXT"
105 #define STRING_ATOM_NAME "STRING"
107 /* See if this is a "single line + line end", a "multiline" or a "tab separated"
108 * string. If this is _not_ the case we won't invoke the STF, it is
109 * unlikely that the user will actually need it in this case. */
111 text_is_single_cell (gchar
const *data
, int data_len
)
115 for (i
= 0; i
< data_len
; i
++)
116 if (data
[i
] == '\n' || data
[i
] == '\t')
122 static GnmCellRegion
*
123 text_to_cell_region (WBCGtk
*wbcg
,
124 gchar
const *data
, int data_len
,
125 char const *opt_encoding
,
126 gboolean fixed_encoding
)
128 Workbook
*wb
= wb_control_get_workbook (GNM_WBC (wbcg
));
129 DialogStfResult_t
*dialogresult
;
130 GnmCellRegion
*cr
= NULL
;
132 char *data_converted
= NULL
;
136 * See Redhat #1160975.
138 * I'm unsure why someone get NULL here, but this is better
145 oneline
= text_is_single_cell (data
, data_len
);
147 if (oneline
&& (opt_encoding
== NULL
|| strcmp (opt_encoding
, "UTF-8") != 0)) {
148 size_t bytes_written
;
149 char const *enc
= opt_encoding
? opt_encoding
: "ASCII";
151 data_converted
= g_convert (data
, data_len
,
153 NULL
, &bytes_written
, NULL
);
154 if (data_converted
) {
155 data
= data_converted
;
156 data_len
= bytes_written
;
158 /* Force STF import since we don't know the charset. */
160 fixed_encoding
= FALSE
;
165 GODateConventions
const *date_conv
= workbook_date_conv (wb
);
166 GnmCellCopy
*cc
= gnm_cell_copy_new (
167 (cr
= gnm_cell_region_new (NULL
)), 0, 0);
168 char *tmp
= g_strndup (data
, data_len
);
170 g_free (data_converted
);
172 cc
->val
= format_match (tmp
, NULL
, date_conv
);
176 cc
->val
= value_new_string_nocopy (tmp
);
179 cr
->cols
= cr
->rows
= 1;
181 dialogresult
= stf_dialog (wbcg
, opt_encoding
, fixed_encoding
,
183 _("clipboard"), data
, data_len
);
185 if (dialogresult
!= NULL
) {
186 cr
= stf_parse_region (dialogresult
->parseoptions
,
187 dialogresult
->text
, NULL
, wb
);
188 g_return_val_if_fail (cr
!= NULL
, gnm_cell_region_new (NULL
));
190 stf_dialog_result_attach_formats_to_cr (dialogresult
, cr
);
192 stf_dialog_result_free (dialogresult
);
194 cr
= gnm_cell_region_new (NULL
);
201 text_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
204 GnmGtkClipboardCtxt
*ctxt
= closure
;
205 WBCGtk
*wbcg
= ctxt
->wbcg
;
206 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
207 GnmPasteTarget
*pt
= ctxt
->paste_target
;
208 GnmCellRegion
*content
= NULL
;
209 GdkAtom target
= gtk_selection_data_get_target (sel
);
211 if (debug_clipboard ()) {
213 char *name
= gdk_atom_name (gtk_selection_data_get_target (sel
));
214 g_printerr ("Received %d bytes of text for target %s\n",
215 gtk_selection_data_get_length (sel
),
218 if (gtk_selection_data_get_length (sel
) > 0) {
219 gsf_mem_dump (gtk_selection_data_get_data (sel
), MIN (gtk_selection_data_get_length (sel
), maxlen
));
220 if (gtk_selection_data_get_length (sel
) > maxlen
)
221 g_printerr ("...\n");
225 /* Nothing on clipboard? */
226 if (gtk_selection_data_get_length (sel
) < 0) {
228 } else if (target
== gdk_atom_intern (UTF8_ATOM_NAME
, FALSE
)) {
229 content
= text_to_cell_region (wbcg
, (const char *)gtk_selection_data_get_data (sel
),
230 gtk_selection_data_get_length (sel
), "UTF-8", TRUE
);
231 } else if (target
== gdk_atom_intern (CTEXT_ATOM_NAME
, FALSE
)) {
232 /* COMPOUND_TEXT is icky. Just let GTK+ do the work. */
233 char *data_utf8
= (char *)gtk_selection_data_get_text (sel
);
234 content
= text_to_cell_region (wbcg
, data_utf8
, strlen (data_utf8
), "UTF-8", TRUE
);
236 } else if (target
== gdk_atom_intern (STRING_ATOM_NAME
, FALSE
)) {
237 char const *locale_encoding
;
238 g_get_charset (&locale_encoding
);
240 content
= text_to_cell_region (wbcg
, (const char *)gtk_selection_data_get_data (sel
),
241 gtk_selection_data_get_length (sel
), locale_encoding
, FALSE
);
245 * if the conversion from the X selection -> a cellregion
246 * was canceled this may have content sized -1,-1
248 if (content
->cols
> 0 && content
->rows
> 0)
249 cmd_paste_copy (wbc
, pt
, content
);
251 /* Release the resources we used */
252 cellregion_unref (content
);
254 g_free (ctxt
->paste_target
);
259 utf8_content_received (GtkClipboard
*clipboard
, const gchar
*text
,
262 GnmGtkClipboardCtxt
*ctxt
= closure
;
263 WBCGtk
*wbcg
= ctxt
->wbcg
;
264 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
265 GnmPasteTarget
*pt
= ctxt
->paste_target
;
266 GnmCellRegion
*content
= NULL
;
268 /* Nothing on clipboard? */
269 if (!text
|| strlen(text
) == 0) {
272 content
= text_to_cell_region (wbcg
, text
, strlen(text
), "UTF-8", TRUE
);
276 * if the conversion from the X selection -> a cellregion
277 * was canceled this may have content sized -1,-1
279 if (content
->cols
> 0 && content
->rows
> 0)
280 cmd_paste_copy (wbc
, pt
, content
);
282 /* Release the resources we used */
283 cellregion_unref (content
);
285 g_free (ctxt
->paste_target
);
290 * Use the file_opener plugin service to read into a temporary workbook, in
291 * order to copy from it to the paste target. A temporary sheet would do just
292 * as well, but the file_opener service makes workbooks, not sheets.
294 * We use the file_opener service by wrapping the selection data in a GsfInput,
295 * and calling workbook_view_new_from_input.
297 static GnmCellRegion
*
298 table_cellregion_read (WorkbookControl
*wbc
, char const *reader_id
,
299 GnmPasteTarget
*pt
, const guchar
*buffer
, int length
)
301 WorkbookView
*wb_view
= NULL
;
303 GnmCellRegion
*ret
= NULL
;
304 const GOFileOpener
*reader
= go_file_opener_for_id (reader_id
);
309 g_warning ("No file opener for %s", reader_id
);
313 ioc
= go_io_context_new (GO_CMD_CONTEXT (wbc
));
314 input
= gsf_input_memory_new (buffer
, length
, FALSE
);
315 wb_view
= workbook_view_new_from_input (input
, NULL
, reader
, ioc
, NULL
);
316 if (go_io_error_occurred (ioc
) || wb_view
== NULL
) {
317 go_io_error_display (ioc
);
321 wb
= wb_view_get_workbook (wb_view
);
322 if (workbook_sheet_count (wb
) > 0) {
324 Sheet
*tmpsheet
= workbook_sheet_by_index (wb
, 0);
325 GnmRange
*rp
= g_object_get_data (G_OBJECT (tmpsheet
),
332 r
.end
.col
= tmpsheet
->cols
.max_used
;
333 r
.end
.row
= tmpsheet
->rows
.max_used
;
335 ret
= clipboard_copy_range (tmpsheet
, &r
);
338 /* This isn't particularly right, but we are going to delete
339 the workbook shortly. See #490479. */
340 WORKBOOK_FOREACH_SHEET (wb
, sheet
, {
341 cellregion_invalidate_sheet (ret
, sheet
);
346 g_object_unref (wb_view
);
349 g_object_unref (ioc
);
350 g_object_unref (input
);
356 image_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
359 GnmGtkClipboardCtxt
*ctxt
= closure
;
360 WBCGtk
*wbcg
= ctxt
->wbcg
;
361 GnmPasteTarget
*pt
= ctxt
->paste_target
;
363 if (debug_clipboard ()) {
365 char *name
= gdk_atom_name (gtk_selection_data_get_target (sel
));
366 g_printerr ("Received %d bytes of image for target %s\n",
367 gtk_selection_data_get_length (sel
),
370 if (gtk_selection_data_get_length (sel
) > 0) {
371 gsf_mem_dump (gtk_selection_data_get_data (sel
), MIN (gtk_selection_data_get_length (sel
), maxlen
));
372 if (gtk_selection_data_get_length (sel
) > maxlen
)
373 g_printerr ("...\n");
377 if (gtk_selection_data_get_length (sel
) > 0) {
378 scg_paste_image (wbcg_cur_scg (wbcg
), &pt
->range
,
379 gtk_selection_data_get_data (sel
), gtk_selection_data_get_length (sel
));
380 g_free (ctxt
->paste_target
);
382 } else if (ctxt
->string_atom
!= GDK_NONE
) {
383 gtk_clipboard_request_contents (clipboard
, ctxt
->string_atom
,
384 text_content_received
, ctxt
);
386 g_free (ctxt
->paste_target
);
392 parse_ms_headers (const char *data
, size_t length
, size_t *start
, size_t *end
)
394 GHashTable
*headers
= g_hash_table_new_full
395 (g_str_hash
, g_str_equal
, g_free
, g_free
);
396 size_t limit
= length
;
403 while (i
< limit
&& data
[i
] != '<') {
406 for (j
= i
; j
< limit
; j
++) {
407 if (data
[j
] == ':') {
408 key
= g_strndup (data
+ i
, j
- i
);
411 if (g_ascii_isspace (data
[j
]))
418 for (k
= j
; k
< limit
; k
++) {
419 if (data
[k
] == '\n' || data
[k
] == '\r') {
420 value
= g_strndup (data
+ j
, k
- j
);
426 while (g_ascii_isspace (data
[k
]))
431 if (debug_clipboard ())
432 g_printerr ("MS HTML Header [%s] => [%s]\n", key
, value
);
434 if (strcmp (key
, "StartHTML") == 0) {
435 long l
= strtol (value
, NULL
, 10);
436 limit
= MIN (limit
, (size_t)MAX (0, l
));
439 g_hash_table_replace (headers
, key
, value
);
443 v
= g_hash_table_lookup (headers
, "StartFragment");
444 sf
= v
? strtol (v
, NULL
, 10) : -1;
445 if (sf
< (long)limit
)
448 v
= g_hash_table_lookup (headers
, "EndFragment");
449 ef
= v
? strtol (v
, NULL
, 10) : -1;
450 if (ef
< sf
|| ef
> (long)length
)
464 g_hash_table_destroy (headers
);
469 table_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
472 GnmGtkClipboardCtxt
*ctxt
= closure
;
473 WBCGtk
*wbcg
= ctxt
->wbcg
;
474 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
475 GnmPasteTarget
*pt
= ctxt
->paste_target
;
476 GnmCellRegion
*content
= NULL
;
477 GdkAtom target
= gtk_selection_data_get_target (sel
);
479 if (debug_clipboard ()) {
481 char *name
= gdk_atom_name (gtk_selection_data_get_target (sel
));
482 g_printerr ("Received %d bytes of table for target %s\n",
483 gtk_selection_data_get_length (sel
),
486 if (gtk_selection_data_get_length (sel
) > 0) {
487 gsf_mem_dump (gtk_selection_data_get_data (sel
), MIN (gtk_selection_data_get_length (sel
), maxlen
));
488 if (gtk_selection_data_get_length (sel
) > maxlen
)
489 g_printerr ("...\n");
493 /* Nothing on clipboard? */
494 if (gtk_selection_data_get_length (sel
) < 0) {
496 } else if (target
== gdk_atom_intern (GNUMERIC_ATOM_NAME
, FALSE
)) {
497 /* The data is the gnumeric specific XML interchange format */
498 GOIOContext
*io_context
=
499 go_io_context_new (GO_CMD_CONTEXT (wbcg
));
500 content
= gnm_xml_cellregion_read
503 (const char *)gtk_selection_data_get_data (sel
), gtk_selection_data_get_length (sel
));
504 g_object_unref (io_context
);
505 } else if (target
== gdk_atom_intern (OOO_ATOM_NAME
, FALSE
) ||
506 target
== gdk_atom_intern (OOO_ATOM_NAME_WINDOWS
, FALSE
) ||
507 target
== gdk_atom_intern (OOO11_ATOM_NAME
, FALSE
)) {
508 content
= table_cellregion_read (wbc
, "Gnumeric_OpenCalc:openoffice",
509 pt
, gtk_selection_data_get_data (sel
),
510 gtk_selection_data_get_length (sel
));
511 } else if (target
== gdk_atom_intern (HTML_ATOM_NAME_UNIX
, FALSE
) ||
512 target
== gdk_atom_intern (HTML_ATOM_NAME_WINDOWS
, FALSE
)) {
513 size_t length
= gtk_selection_data_get_length (sel
);
514 size_t start
= 0, end
= length
;
516 if (target
== gdk_atom_intern (HTML_ATOM_NAME_WINDOWS
, FALSE
)) {
518 parse_ms_headers (gtk_selection_data_get_data (sel
), length
, &start
, &end
);
521 content
= table_cellregion_read (wbc
, "Gnumeric_html:html",
523 gtk_selection_data_get_data (sel
) + start
,
525 } else if ((target
== gdk_atom_intern ( BIFF8_ATOM_NAME
, FALSE
)) ||
526 (target
== gdk_atom_intern ( BIFF8_ATOM_NAME_CITRIX
, FALSE
)) ||
527 (target
== gdk_atom_intern ( BIFF5_ATOM_NAME
, FALSE
)) ||
528 (target
== gdk_atom_intern ( BIFF4_ATOM_NAME
, FALSE
)) ||
529 (target
== gdk_atom_intern ( BIFF3_ATOM_NAME
, FALSE
)) ||
530 (target
== gdk_atom_intern ( BIFF_ATOM_NAME
, FALSE
))) {
531 content
= table_cellregion_read (wbc
, "Gnumeric_Excel:excel",
532 pt
, gtk_selection_data_get_data (sel
),
533 gtk_selection_data_get_length (sel
));
537 * if the conversion from the X selection -> a cellregion
538 * was canceled this may have content sized -1,-1
540 if ((content
->cols
> 0 && content
->rows
> 0) ||
541 content
->objects
!= NULL
)
542 cmd_paste_copy (wbc
, pt
, content
);
544 /* Release the resources we used */
545 cellregion_unref (content
);
546 g_free (ctxt
->paste_target
);
548 } else if (ctxt
->image_atom
!= GDK_NONE
) {
549 gtk_clipboard_request_contents (clipboard
, ctxt
->image_atom
,
550 image_content_received
, ctxt
);
551 } else if (ctxt
->string_atom
!= GDK_NONE
) {
552 gtk_clipboard_request_contents (clipboard
, ctxt
->string_atom
,
553 text_content_received
, ctxt
);
555 g_free (ctxt
->paste_target
);
561 * x_targets_received:
563 * Invoked when the selection has been received by our application.
564 * This is triggered by a call we do to gtk_clipboard_request_contents.
566 * We try to import a spreadsheet/table, next an image, and finally fall back
567 * to a string format if the others fail, e.g. for html which does not
571 x_targets_received (GtkClipboard
*clipboard
, GdkAtom
*targets
,
572 gint n_targets
, gpointer closure
)
574 GnmGtkClipboardCtxt
*ctxt
= closure
;
575 GdkAtom table_atom
= GDK_NONE
;
578 /* in order of preference */
579 static char const *table_fmts
[] = {
583 BIFF8_ATOM_NAME_CITRIX
,
591 OOO_ATOM_NAME_WINDOWS
,
594 HTML_ATOM_NAME_WINDOWS
,
597 static char const *string_fmts
[] = {
604 /* Nothing on clipboard? */
605 if (targets
== NULL
|| n_targets
== 0) {
606 gtk_clipboard_request_text (clipboard
, utf8_content_received
,
611 if (debug_clipboard ()) {
614 for (j
= 0; j
< n_targets
; j
++)
615 g_printerr ("Clipboard target %d is %s\n",
616 j
, gdk_atom_name (targets
[j
]));
619 /* The data is a list of atoms */
620 /* Find the best table format offered */
621 for (i
= 0 ; table_fmts
[i
] && table_atom
== GDK_NONE
; i
++) {
622 /* Look for one we can use */
623 GdkAtom atom
= gdk_atom_intern (table_fmts
[i
], FALSE
);
624 /* is it on offer? */
625 for (j
= 0; j
< n_targets
&& table_atom
== GDK_NONE
; j
++) {
626 if (targets
[j
] == atom
)
631 if (table_atom
== GDK_NONE
) {
632 GtkTargetList
*tl
= gtk_target_list_new (NULL
, 0);
633 gboolean found
= FALSE
;
635 gtk_target_list_add_image_targets (tl
, 0, FALSE
);
637 /* Find an image format */
638 for (i
= 0 ; i
< n_targets
&& !found
; i
++) {
639 if (gtk_target_list_find (tl
, targets
[i
], NULL
)) {
640 ctxt
->image_atom
= targets
[i
];
644 gtk_target_list_unref (tl
);
647 /* Find a string format to fall back to */
648 for (i
= 0 ; string_fmts
[i
] && ctxt
->string_atom
== GDK_NONE
; i
++) {
649 /* Look for one we can use */
650 GdkAtom atom
= gdk_atom_intern (string_fmts
[i
], FALSE
);
651 /* is it on offer? */
652 for (j
= 0; j
< n_targets
&& ctxt
->string_atom
== GDK_NONE
;
654 if (targets
[j
] == atom
)
655 ctxt
->string_atom
= atom
;
657 if (ctxt
->string_atom
!= GDK_NONE
)
661 if (table_atom
!= GDK_NONE
)
662 gtk_clipboard_request_contents (clipboard
, table_atom
,
663 table_content_received
, ctxt
);
664 else if (ctxt
->image_atom
!= GDK_NONE
)
665 gtk_clipboard_request_contents (clipboard
, ctxt
->image_atom
,
666 image_content_received
, ctxt
);
667 else if (ctxt
->string_atom
!= GDK_NONE
)
668 gtk_clipboard_request_contents (clipboard
, ctxt
->string_atom
,
669 text_content_received
, ctxt
);
671 g_free (ctxt
->paste_target
);
676 /* Cheezy implementation: paste into a temporary workbook, save that. */
678 table_cellregion_write (GOCmdContext
*ctx
, GnmCellRegion
*cr
,
679 char * saver_id
, int *size
)
682 const GOFileSaver
*saver
= go_file_saver_for_id (saver_id
);
686 WorkbookView
*wb_view
;
695 output
= gsf_output_memory_new ();
696 ioc
= go_io_context_new (ctx
);
701 gnm_sheet_suggest_size (&cols
, &rows
);
702 wb
= workbook_new ();
703 workbook_sheet_add (wb
, -1, cols
, rows
);
706 wb_view
= workbook_view_new (wb
);
708 sheet
= workbook_sheet_by_index (wb
, 0);
709 memset (&r
, 0, sizeof r
);
710 r
.end
.col
= cr
->cols
- 1;
711 r
.end
.row
= cr
->rows
- 1;
713 paste_target_init (&pt
, sheet
, &r
,
714 PASTE_AS_VALUES
| PASTE_FORMATS
|
715 PASTE_COMMENTS
| PASTE_OBJECTS
);
716 if (clipboard_paste_region (cr
, &pt
, ctx
) == FALSE
) {
717 go_file_saver_save (saver
, ioc
, GO_VIEW (wb_view
), output
);
718 if (!go_io_error_occurred (ioc
)) {
719 GsfOutputMemory
*omem
= GSF_OUTPUT_MEMORY (output
);
720 gsf_off_t osize
= gsf_output_size (output
);
723 if (*size
== osize
) {
724 ret
= g_malloc (*size
);
726 gsf_output_memory_get_bytes (omem
),
729 g_warning ("Overflow"); /* Far fetched! */
733 gsf_output_close (output
);
734 g_object_unref (wb_view
);
736 g_object_unref (ioc
);
737 g_object_unref (output
);
743 image_write (GnmCellRegion
*cr
, gchar
const *mime_type
, int *size
)
746 SheetObject
*so
= NULL
;
749 GsfOutputMemory
*omem
;
755 g_return_val_if_fail (cr
->objects
!= NULL
, NULL
);
756 so
= GNM_SO (cr
->objects
->data
);
757 g_return_val_if_fail (so
!= NULL
, NULL
);
759 if (strncmp (mime_type
, "image/", 6) != 0)
761 for (l
= cr
->objects
; l
!= NULL
; l
= l
->next
) {
762 if (GNM_IS_SO_IMAGEABLE (GNM_SO (l
->data
))) {
763 so
= GNM_SO (l
->data
);
768 g_warning ("non imageable object requested as image\n");
772 format
= go_mime_to_image_format (mime_type
);
774 g_warning ("No image format for %s\n", mime_type
);
778 output
= gsf_output_memory_new ();
779 omem
= GSF_OUTPUT_MEMORY (output
);
780 sheet_object_write_image (so
, format
, 150.0, output
, NULL
);
781 osize
= gsf_output_size (output
);
784 if (*size
== osize
) {
785 ret
= g_malloc (*size
);
786 memcpy (ret
, gsf_output_memory_get_bytes (omem
), *size
);
788 g_warning ("Overflow"); /* Far fetched! */
790 gsf_output_close (output
);
791 g_object_unref (output
);
798 object_write (GnmCellRegion
*cr
, gchar
const *mime_type
, int *size
)
801 SheetObject
*so
= NULL
;
803 GsfOutputMemory
*omem
;
809 g_return_val_if_fail (cr
->objects
!= NULL
, NULL
);
810 so
= GNM_SO (cr
->objects
->data
);
811 g_return_val_if_fail (so
!= NULL
, NULL
);
813 for (l
= cr
->objects
; l
!= NULL
; l
= l
->next
) {
814 if (GNM_IS_SO_EXPORTABLE (GNM_SO (l
->data
))) {
815 so
= GNM_SO (l
->data
);
820 g_warning ("non exportable object requested\n");
823 output
= gsf_output_memory_new ();
824 omem
= GSF_OUTPUT_MEMORY (output
);
825 sheet_object_write_object (so
, mime_type
, output
, NULL
,
826 gnm_conventions_default
);
827 osize
= gsf_output_size (output
);
831 ret
= g_memdup (gsf_output_memory_get_bytes (omem
), *size
);
833 g_warning ("Overflow"); /* Far fetched! */
834 gsf_output_close (output
);
835 g_object_unref (output
);
843 * Callback invoked when another application requests we render the selection.
846 x_clipboard_get_cb (GtkClipboard
*gclipboard
, GtkSelectionData
*selection_data
,
847 guint info
, GObject
*app
)
849 gboolean to_gnumeric
= FALSE
, content_needs_free
= FALSE
;
850 GnmCellRegion
*clipboard
= gnm_app_clipboard_contents_get ();
851 Sheet
*sheet
= gnm_app_clipboard_sheet_get ();
852 GnmRange
const *a
= gnm_app_clipboard_area_get ();
853 GOCmdContext
*ctx
= gnm_cmd_context_stderr_new ();
854 GdkAtom target
= gtk_selection_data_get_target (selection_data
);
855 gchar
*target_name
= gdk_atom_name (target
);
857 if (debug_clipboard ())
858 g_printerr ("clipboard target=%s\n", target_name
);
861 * There are 4 cases. What variables are valid depends on case:
863 * a cut: clipboard NULL, sheet, area non NULL.
864 * a copy: clipboard, sheet, area all non NULL.
865 * a cut, source closed: clipboard, sheet, area all NULL.
866 * a copy, source closed: clipboard non NULL, sheet, area non NULL.
868 * If the source is a cut, we copy it for pasting. We
869 * postpone clearing it until after the selection has been
870 * rendered to the requested format.
872 if (clipboard
== NULL
&& sheet
!= NULL
) {
873 content_needs_free
= TRUE
;
874 clipboard
= clipboard_copy_range (sheet
, a
);
877 if (clipboard
== NULL
)
880 /* What format does the other application want? */
881 if (target
== gdk_atom_intern (GNUMERIC_ATOM_NAME
, FALSE
)) {
882 GsfOutputMemory
*output
= gnm_cellregion_to_xml (clipboard
);
884 gsf_off_t size
= gsf_output_size (GSF_OUTPUT (output
));
885 if (debug_clipboard ())
886 g_printerr ("clipboard .gnumeric of %d bytes\n",
888 gtk_selection_data_set
889 (selection_data
, target
, 8,
890 gsf_output_memory_get_bytes (output
),
892 g_object_unref (output
);
895 } else if (target
== gdk_atom_intern (HTML_ATOM_NAME
, FALSE
)) {
896 char *saver_id
= (char *) "Gnumeric_html:xhtml_range";
898 guchar
*buffer
= table_cellregion_write (ctx
, clipboard
,
901 if (debug_clipboard ())
902 g_message ("clipboard html of %d bytes",
904 gtk_selection_data_set (selection_data
,
906 (guchar
*) buffer
, buffer_size
);
908 } else if (strcmp (target_name
, "application/x-goffice-graph") == 0 ||
909 g_slist_find_custom (go_components_get_mime_types (), target_name
, (GCompareFunc
) strcmp
) != NULL
) {
911 guchar
*buffer
= object_write (clipboard
, target_name
,
913 if (debug_clipboard ())
914 g_message ("clipboard graph of %d bytes",
916 gtk_selection_data_set (selection_data
,
918 (guchar
*) buffer
, buffer_size
);
920 } else if (strncmp (target_name
, "image/", 6) == 0) {
922 guchar
*buffer
= image_write (clipboard
, target_name
,
924 if (debug_clipboard ())
925 g_message ("clipboard image of %d bytes",
927 gtk_selection_data_set (selection_data
,
929 (guchar
*) buffer
, buffer_size
);
931 } else if (strcmp (target_name
, "SAVE_TARGETS") == 0) {
932 /* We implicitly registered this when calling
933 * gtk_clipboard_set_can_store. We're supposed to
935 } else if (clipboard
->origin_sheet
) {
936 Workbook
*wb
= clipboard
->origin_sheet
->workbook
;
937 GString
*res
= cellregion_to_string (clipboard
,
938 TRUE
, workbook_date_conv (wb
));
940 if (debug_clipboard ())
941 g_message ("clipboard text of %d bytes",
943 gtk_selection_data_set_text (selection_data
,
945 g_string_free (res
, TRUE
);
947 if (debug_clipboard ())
948 g_message ("clipboard empty text");
949 gtk_selection_data_set_text (selection_data
, "", 0);
952 gtk_selection_data_set_text (selection_data
, "", 0);
955 * If this was a CUT operation we need to clear the content that
956 * was pasted into another application and release the stuff on
959 if (content_needs_free
) {
961 /* If the other app was a gnumeric, emulate a cut */
964 GnmSheetRange
*sr
= gnm_sheet_range_new (sheet
, a
);
965 SheetView
const *sv
= gnm_app_clipboard_sheet_view_get ();
966 SheetControl
*sc
= g_ptr_array_index (sv
->controls
, 0);
967 WorkbookControl
*wbc
= sc_wbc (sc
);
971 redo
= sheet_clear_region_undo
973 CLEAR_VALUES
|CLEAR_COMMENTS
|CLEAR_RECALC_DEPS
);
974 undo
= clipboard_copy_range_undo (sheet
, a
);
975 name
= undo_range_name (sheet
, a
);
976 text
= g_strdup_printf (_("Cut of %s"), name
);
978 cmd_generic (wbc
, text
, undo
, redo
);
980 gnm_app_clipboard_clear (TRUE
);
983 cellregion_unref (clipboard
);
986 g_free (target_name
);
987 g_object_unref (ctx
);
991 * x_clipboard_clear_cb:
993 * Callback for the "we lost the X selection" signal.
996 x_clipboard_clear_cb (GtkClipboard
*clipboard
,
999 if (debug_clipboard ())
1000 g_printerr ("Lost clipboard ownership.\n");
1002 gnm_app_clipboard_clear (FALSE
);
1008 gnm_x_request_clipboard (WBCGtk
*wbcg
, GnmPasteTarget
const *pt
)
1010 GnmGtkClipboardCtxt
*ctxt
;
1011 GdkDisplay
*display
= gtk_widget_get_display (GTK_WIDGET (wbcg_toplevel (wbcg
)));
1012 GtkClipboard
*clipboard
=
1013 gtk_clipboard_get_for_display
1015 gnm_conf_get_cut_and_paste_prefer_clipboard ()
1016 ? GDK_SELECTION_CLIPBOARD
1017 : GDK_SELECTION_PRIMARY
);
1019 ctxt
= g_new (GnmGtkClipboardCtxt
, 1);
1021 ctxt
->paste_target
= g_new (GnmPasteTarget
, 1);
1022 *ctxt
->paste_target
= *pt
;
1023 ctxt
->image_atom
= GDK_NONE
;
1024 ctxt
->string_atom
= GDK_NONE
;
1026 /* Query the formats, This will callback x_targets_received */
1027 gtk_clipboard_request_targets (clipboard
,
1028 x_targets_received
, ctxt
);
1031 /* Restrict the set of formats offered to clipboard manager. */
1032 /* We include bmp in the whitelist because that's the only image format
1033 * we share with OOo over clipboard (!) */
1035 set_clipman_targets (GdkDisplay
*disp
, GtkTargetEntry
*targets
, guint n_targets
)
1037 static GtkTargetEntry clipman_whitelist
[] = {
1038 { (char *) GNUMERIC_ATOM_NAME
, 0, GNUMERIC_ATOM_INFO
},
1039 { (char *) HTML_ATOM_NAME
, 0, 0 },
1040 { (char *)"UTF8_STRING", 0, 0 },
1041 { (char *)"application/x-goffice-graph", 0, 0 },
1042 { (char *)"image/svg+xml", 0, 0 },
1043 { (char *)"image/x-wmf", 0, 0 },
1044 { (char *)"image/x-emf", 0, 0 },
1045 { (char *)"image/png", 0, 0 },
1046 { (char *)"image/jpeg", 0, 0 },
1047 { (char *)"image/bmp", 0, 0 },
1049 guint n_whitelist
= G_N_ELEMENTS (clipman_whitelist
);
1051 GtkTargetList
*tl
= gtk_target_list_new (NULL
, 0);
1052 GtkTargetEntry
*t
, *wt
, *t_allowed
;
1054 for (t
= targets
; t
< targets
+ n_targets
; t
++) {
1055 for (wt
= clipman_whitelist
;
1056 wt
< clipman_whitelist
+ n_whitelist
; wt
++) {
1057 if (strcmp(t
->target
, wt
->target
) == 0) {
1059 (tl
, gdk_atom_intern (t
->target
, FALSE
),
1066 t_allowed
= gtk_target_table_new_from_list (tl
, &n_allowed
);
1067 gtk_target_list_unref (tl
);
1069 gtk_clipboard_set_can_store (
1070 gtk_clipboard_get_for_display (
1071 disp
, GDK_SELECTION_CLIPBOARD
),
1072 t_allowed
, n_allowed
);
1073 gtk_target_table_free (t_allowed
, n_allowed
);
1078 gnm_x_claim_clipboard (GdkDisplay
*display
)
1080 GnmCellRegion
*content
= gnm_app_clipboard_contents_get ();
1081 SheetObject
*imageable
= NULL
, *exportable
= NULL
;
1082 GtkTargetEntry
*targets
= NULL
;
1085 GObject
*app
= gnm_app_get_app ();
1087 static GtkTargetEntry
const table_targets
[] = {
1088 { (char *) GNUMERIC_ATOM_NAME
, 0, GNUMERIC_ATOM_INFO
},
1089 { (char *) HTML_ATOM_NAME
, 0, 0 },
1090 { (char *)"UTF8_STRING", 0, 0 },
1091 { (char *)"COMPOUND_TEXT", 0, 0 },
1092 { (char *)"STRING", 0, 0 },
1095 targets
= (GtkTargetEntry
*) table_targets
;
1096 n_targets
= G_N_ELEMENTS (table_targets
);
1098 (content
->cols
<= 0 || content
->rows
<= 0)) {
1100 for (ptr
= content
->objects
; ptr
!= NULL
;
1102 SheetObject
*candidate
= GNM_SO (ptr
->data
);
1103 if (exportable
== NULL
&& GNM_IS_SO_EXPORTABLE (candidate
))
1104 exportable
= candidate
;
1105 if (imageable
== NULL
&& GNM_IS_SO_IMAGEABLE (candidate
))
1106 imageable
= candidate
;
1108 /* Currently, we can't render sheet objects as text or html */
1113 sheet_object_exportable_get_target_list (exportable
);
1114 /* _add_table prepends to target_list */
1115 gtk_target_list_add_table (tl
, table_targets
, 1);
1116 targets
= gtk_target_table_new_from_list (tl
, &n_targets
);
1117 gtk_target_list_unref (tl
);
1121 sheet_object_get_target_list (imageable
);
1122 /* _add_table prepends to target_list */
1123 gtk_target_list_add_table (tl
, targets
, (exportable
)? n_targets
: 1);
1124 targets
= gtk_target_table_new_from_list (tl
, &n_targets
);
1125 gtk_target_list_unref (tl
);
1127 /* Register a x_clipboard_clear_cb only for CLIPBOARD, not for
1129 ret
= gtk_clipboard_set_with_owner (
1130 gtk_clipboard_get_for_display (display
, GDK_SELECTION_CLIPBOARD
),
1132 (GtkClipboardGetFunc
) x_clipboard_get_cb
,
1133 (GtkClipboardClearFunc
) x_clipboard_clear_cb
,
1136 if (debug_clipboard ())
1137 g_printerr ("Clipboard successfully claimed.\n");
1139 g_object_set_data_full (app
, APP_CLIP_DISP_KEY
,
1140 g_slist_prepend (g_object_steal_data (app
, APP_CLIP_DISP_KEY
),
1142 (GDestroyNotify
)g_slist_free
);
1145 set_clipman_targets (display
, targets
, n_targets
);
1146 (void)gtk_clipboard_set_with_owner (
1147 gtk_clipboard_get_for_display (display
,
1148 GDK_SELECTION_PRIMARY
),
1150 (GtkClipboardGetFunc
) x_clipboard_get_cb
,
1154 if (debug_clipboard ())
1155 g_printerr ("Failed to claim clipboard.\n");
1157 if (exportable
|| imageable
)
1158 gtk_target_table_free (targets
, n_targets
);
1164 gnm_x_disown_clipboard (void)
1166 GObject
*app
= gnm_app_get_app ();
1167 GSList
*displays
= g_object_steal_data (app
, APP_CLIP_DISP_KEY
);
1170 for (l
= displays
; l
; l
= l
->next
) {
1171 GdkDisplay
*display
= l
->data
;
1172 gtk_selection_owner_set_for_display (display
, NULL
,
1173 GDK_SELECTION_PRIMARY
,
1175 gtk_selection_owner_set_for_display (display
, NULL
,
1176 GDK_SELECTION_CLIPBOARD
,
1179 g_slist_free (displays
);
1182 /* Hand clipboard off to clipboard manager. To be called before workbook
1183 * object is destroyed.
1186 gnm_x_store_clipboard_if_needed (Workbook
*wb
)
1188 Sheet
*sheet
= gnm_app_clipboard_sheet_get ();
1189 WBCGtk
*wbcg
= NULL
;
1191 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
1193 if (sheet
&& sheet
->workbook
== wb
) {
1194 WORKBOOK_FOREACH_CONTROL (wb
, view
, control
, {
1195 if (GNM_IS_WBC_GTK (control
)) {
1196 wbcg
= WBC_GTK (control
);
1201 GtkClipboard
*clip
= gtk_clipboard_get_for_display
1202 (gtk_widget_get_display
1203 (GTK_WIDGET (wbcg_toplevel (wbcg
))),
1204 GDK_SELECTION_CLIPBOARD
);
1205 if (gtk_clipboard_get_owner (clip
) == gnm_app_get_app ()) {
1206 if (debug_clipboard ())
1207 g_printerr ("Handing off clipboard\n");
1208 gtk_clipboard_store (clip
);