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 <dialogs/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"
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 // ----------------------------------------------------------------------------
74 ATOM_TEXT_HTML_WINDOWS
,
94 ATOM_GNOME_COPIED_FILES
,
100 static const char *const atom_names
[] = {
101 "application/x-gnumeric",
102 "application/x-goffice-graph",
111 "application/x-openoffice-biff-8;windows_formatname=\"Biff8\"",
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)\"",
128 "x-special/gnome-copied-files",
129 "application/x-kde-cutselection",
134 static GdkAtom atoms
[G_N_ELEMENTS(atom_names
)];
147 static GtkTargetList
*generic_text_targets
;
148 static GtkTargetList
*image_targets
;
150 // ----------------------------------------------------------------------------
154 GnmPasteTarget
*paste_target
;
155 } GnmGtkClipboardCtxt
;
158 gnm_gtk_clipboard_context_free (GnmGtkClipboardCtxt
*ctxt
)
160 g_free (ctxt
->paste_target
);
166 * (x-get-selection-internal 'CLIPBOARD 'TARGETS)
170 has_file_opener (const char *id
)
172 return go_file_opener_for_id (id
) != NULL
;
176 has_file_saver (const char *id
)
178 return go_file_saver_for_id (id
) != NULL
;
184 paste_from_gnumeric (GtkSelectionData
*selection_data
, GdkAtom target
,
185 gconstpointer data
, gssize size
)
190 if (debug_clipboard_dump
) {
191 g_file_set_contents ("paste-from-gnumeric.dat",
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
);
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
);
215 if (debug_clipboard
) {
217 char *name
= gdk_atom_name (target
);
218 g_printerr ("Received %d bytes of %s for target %s\n",
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. */
239 text_is_single_cell (gchar
const *data
, int data_len
)
243 for (i
= 0; i
< data_len
; i
++)
244 if (data
[i
] == '\n' || data
[i
] == '\t')
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
;
260 char *data_converted
= NULL
;
264 * See Redhat #1160975.
266 * I'm unsure why someone gets NULL here, but this is better
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
,
281 NULL
, &bytes_written
, NULL
);
282 if (data_converted
) {
283 data
= data_converted
;
284 data_len
= bytes_written
;
286 /* Force STF import since we don't know the charset. */
288 fixed_encoding
= FALSE
;
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
);
304 cc
->val
= value_new_string_nocopy (tmp
);
307 cr
->cols
= cr
->rows
= 1;
309 dialogresult
= stf_dialog (wbcg
, opt_encoding
, fixed_encoding
,
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
);
322 cr
= gnm_cell_region_new (NULL
);
329 text_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
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? */
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
);
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
);
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
);
376 utf8_content_received (GtkClipboard
*clipboard
, const gchar
*text
,
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) {
389 content
= text_to_cell_region (wbcg
, text
, strlen(text
), "UTF-8", TRUE
);
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
;
420 GnmCellRegion
*ret
= NULL
;
421 const GOFileOpener
*reader
= go_file_opener_for_id (reader_id
);
426 // Likely cause: plugin not loaded
427 g_warning ("No file opener for %s", reader_id
);
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
);
439 wb
= wb_view_get_workbook (wb_view
);
440 if (workbook_sheet_count (wb
) > 0) {
442 Sheet
*tmpsheet
= workbook_sheet_by_index (wb
, 0);
443 GnmRange
*rp
= g_object_get_data (G_OBJECT (tmpsheet
),
448 // File format didn't tell us the range being
449 // pasted. Looking at you, LibreOffice!
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
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
);
480 g_object_unref (wb_view
);
483 g_object_unref (ioc
);
484 g_object_unref (input
);
490 image_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
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");
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
);
509 urilist_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
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");
520 char *text
= g_strndup (gtk_selection_data_get_data (sel
), sel_len
);
521 GSList
*uris
= go_file_split_urls (text
);
525 for (l
= uris
; l
; l
= l
->next
) {
526 const char *uri
= l
->data
;
533 if (g_str_equal (uri
, "copy"))
535 mime
= go_get_mime_type (uri
);
536 qimage
= (strncmp (mime
, "image/", 6) == 0);
541 input
= go_file_open (uri
, NULL
);
544 size
= gsf_input_size (input
);
545 data
= gsf_input_read (input
, size
, NULL
);
547 scg_paste_image (wbcg_cur_scg (wbcg
), &pt
->range
,
549 g_object_unref (input
);
552 g_slist_free_full (uris
, g_free
);
556 gnm_gtk_clipboard_context_free (ctxt
);
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
;
571 while (i
< limit
&& data
[i
] != '<') {
574 for (j
= i
; j
< limit
; j
++) {
575 if (data
[j
] == ':') {
576 key
= g_strndup (data
+ i
, j
- i
);
579 if (g_ascii_isspace (data
[j
]))
586 for (k
= j
; k
< limit
; k
++) {
587 if (data
[k
] == '\n' || data
[k
] == '\r') {
588 value
= g_strndup (data
+ j
, k
- j
);
594 while (g_ascii_isspace (data
[k
]))
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
);
611 v
= g_hash_table_lookup (headers
, "StartFragment");
612 sf
= v
? strtol (v
, NULL
, 10) : -1;
613 if (sf
< (long)limit
)
616 v
= g_hash_table_lookup (headers
, "EndFragment");
617 ef
= v
? strtol (v
, NULL
, 10) : -1;
618 if (ef
< sf
|| ef
> (long)length
)
632 g_hash_table_destroy (headers
);
636 table_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
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? */
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
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
,
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
]) {
674 parse_ms_headers (buffer
, sel_len
, &start
, &end
);
677 content
= table_cellregion_read (wbc
, HTML_FILE_OPENER
,
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
,
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
);
707 find_in_table (GdkAtom
*targets
, int n
, GdkAtom a
)
710 for (i
= 0; i
< n
; i
++)
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
727 x_targets_received (GtkClipboard
*clipboard
, GdkAtom
*targets
,
728 gint n_targets
, gpointer closure
)
730 GnmGtkClipboardCtxt
*ctxt
= closure
;
734 // In order of preference
735 static const struct {
737 const char *opener_id
;
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
[] = {
754 ATOM_GNOME_COPIED_FILES
,
758 // In order of preference
759 static const int string_fmts
[] = {
765 // Nothing on clipboard? Try text.
766 if (targets
== NULL
|| n_targets
== 0) {
767 gtk_clipboard_request_text (clipboard
, utf8_content_received
,
772 if (debug_clipboard
) {
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",
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
,
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
,
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
,
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
,
830 gnm_gtk_clipboard_context_free (ctxt
);
833 /* Cheezy implementation: paste into a temporary workbook, save that. */
835 table_cellregion_write (GOCmdContext
*ctx
, GnmCellRegion
*cr
,
836 const char *saver_id
, int *size
)
839 const GOFileSaver
*saver
;
843 WorkbookView
*wb_view
;
848 if (debug_clipboard_undump
) {
851 if (g_file_get_contents ("paste-from-gnumeric.dat", &contents
,
853 g_printerr ("Sending %d prepackaged bytes.\n",
856 return (guchar
*)contents
;
862 saver
= go_file_saver_for_id (saver_id
);
864 // Likely cause: plugin not loaded
865 g_printerr ("Failed to get saver for %s for clipboard use.\n",
870 output
= gsf_output_memory_new ();
871 ioc
= go_io_context_new (ctx
);
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
);
897 if (*size
== osize
) {
898 ret
= g_memdup (data
, *size
);
900 g_warning ("Overflow"); /* Far fetched! */
904 if (!gsf_output_is_closed (output
))
905 gsf_output_close (output
);
906 g_object_unref (wb_view
);
908 g_object_unref (ioc
);
909 g_object_unref (output
);
915 image_write (GnmCellRegion
*cr
, gchar
const *mime_type
, int *size
)
918 SheetObject
*so
= NULL
;
921 GsfOutputMemory
*omem
;
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
);
938 // This shouldn't happen
939 g_warning ("non-imageable object requested as image\n");
943 format
= go_mime_to_image_format (mime_type
);
945 // This shouldn't happen
946 g_warning ("No image format for %s\n", mime_type
);
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
);
956 if (*size
== osize
) {
957 ret
= g_malloc (*size
);
958 memcpy (ret
, gsf_output_memory_get_bytes (omem
), *size
);
960 g_warning ("Overflow"); /* Far fetched! */
962 gsf_output_close (output
);
963 g_object_unref (output
);
970 object_write (GnmCellRegion
*cr
, gchar
const *mime_type
, int *size
)
973 SheetObject
*so
= NULL
;
975 GsfOutputMemory
*omem
;
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
);
992 g_warning ("non exportable object requested\n");
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
);
1003 ret
= g_memdup (gsf_output_memory_get_bytes (omem
), *size
);
1005 g_warning ("Overflow"); /* Far fetched! */
1006 gsf_output_close (output
);
1007 g_object_unref (output
);
1013 * x_clipboard_get_cb
1015 * Callback invoked when another application requests we render the selection.
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:
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
)
1056 /* What format does the other application want? */
1057 if (target
== atoms
[ATOM_GNUMERIC
]) {
1058 GsfOutputMemory
*output
= gnm_cellregion_to_xml (clipboard
);
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
,
1065 g_object_unref (output
);
1068 } else if (info
== INFO_HTML
) {
1070 guchar
*buffer
= table_cellregion_write (ctx
, clipboard
,
1073 paste_from_gnumeric (selection_data
, target
, buffer
, size
);
1075 } else if (info
== INFO_EXCEL
) {
1077 guchar
*buffer
= table_cellregion_write (ctx
, clipboard
,
1080 paste_from_gnumeric (selection_data
, target
, buffer
, size
);
1082 } else if (target
== atoms
[ATOM_GOFFICE_GRAPH
] ||
1083 g_slist_find_custom (go_components_get_mime_types (), target_name
, (GCompareFunc
) strcmp
) != NULL
) {
1085 guchar
*buffer
= object_write (clipboard
, target_name
, &size
);
1086 paste_from_gnumeric (selection_data
, target
, buffer
, size
);
1088 } else if (info
== INFO_IMAGE
) {
1090 guchar
*buffer
= image_write (clipboard
, target_name
, &size
);
1091 paste_from_gnumeric (selection_data
, target
, buffer
, size
);
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
));
1101 if (debug_clipboard
)
1102 g_message ("clipboard text of %d bytes",
1104 gtk_selection_data_set_text (selection_data
,
1105 res
->str
, res
->len
);
1106 g_string_free (res
, TRUE
);
1108 if (debug_clipboard
)
1109 g_message ("clipboard empty text");
1110 gtk_selection_data_set_text (selection_data
, "", 0);
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
1120 if (content_needs_free
) {
1121 /* If the other app was a gnumeric, emulate a cut */
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
);
1131 redo
= sheet_clear_region_undo
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
);
1138 cmd_generic (wbc
, text
, undo
, redo
);
1140 gnm_app_clipboard_clear (TRUE
);
1143 cellregion_unref (clipboard
);
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.
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
);
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
1172 gnm_conf_get_cut_and_paste_prefer_clipboard ()
1173 ? GDK_SELECTION_CLIPBOARD
1174 : GDK_SELECTION_PRIMARY
);
1176 ctxt
= g_new (GnmGtkClipboardCtxt
, 1);
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
);
1186 cb_clear_target_entry (gpointer te_
)
1188 GtkTargetEntry
*te
= te_
;
1189 g_free (te
->target
);
1193 add_target (GArray
*targets
, const char *target
, int flags
, AtomInfoType info
)
1196 t
.target
= g_strdup (target
);
1199 g_array_append_val (targets
, t
);
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. */
1219 set_clipman_targets (GdkDisplay
*disp
, GArray
*targets
)
1221 GArray
*allowed
= g_array_new (FALSE
, FALSE
, sizeof (GtkTargetEntry
));
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),
1238 g_array_free (allowed
, TRUE
);
1242 add_target_list (GArray
*targets
, GtkTargetList
*src
, AtomInfoType info
)
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
);
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
));
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
);
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
;
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
)) {
1289 add_target (targets
, atom_names
[ATOM_TEXT_HTML_WINDOWS
], 0, INFO_HTML
);
1291 add_target (targets
, atom_names
[ATOM_TEXT_HTML
], 0, INFO_HTML
);
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
);
1301 sheet_object_exportable_get_target_list (exportable
);
1302 add_target_list (targets
, tl
, INFO_OBJECT
);
1303 gtk_target_list_unref (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
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
,
1319 x_clipboard_clear_cb
,
1322 if (debug_clipboard
) {
1324 g_printerr ("Clipboard successfully claimed.\n");
1325 g_printerr ("Clipboard targets offered: ");
1326 for (ui
= 0; ui
< targets
->len
; ui
++) {
1329 g_array_index(targets
,GtkTargetEntry
,ui
).target
);
1334 g_object_set_data_full (app
, APP_CLIP_DISP_KEY
,
1335 g_slist_prepend (g_object_steal_data (app
, APP_CLIP_DISP_KEY
),
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
,
1348 if (debug_clipboard
)
1349 g_printerr ("Failed to claim clipboard.\n");
1352 g_array_free (targets
, TRUE
);
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
);
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
,
1369 gtk_selection_owner_set_for_display (display
, NULL
,
1370 GDK_SELECTION_CLIPBOARD
,
1373 g_slist_free (displays
);
1376 /* Hand clipboard off to clipboard manager. To be called before workbook
1377 * object is destroyed.
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
);
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
);
1409 gui_clipboard_test (const char *fmt
)
1411 GtkClipboard
*gclipboard
= NULL
;
1412 gpointer app
= NULL
;
1413 GtkSelectionData
*selection_data
;
1416 GdkAtom atom
= NULL
;
1421 for (ui
= 0; ui
< G_N_ELEMENTS (atom_names
); ui
++) {
1422 if (g_str_equal (fmt
, atom_names
[ui
])) {
1432 info
= INFO_GNUMERIC
;
1434 case ATOM_UTF8_STRING
:
1436 case ATOM_COMPOUND_TEXT
:
1437 info
= INFO_GENERIC_TEXT
;
1439 case ATOM_TEXT_HTML
:
1440 case ATOM_TEXT_HTML_WINDOWS
:
1445 case ATOM_BIFF8_CITRIX
:
1451 case ATOM_OOO_WINDOWS
:
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
:
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
);
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
);
1488 * gui_clipboard_init: (skip)
1491 gui_clipboard_init (void)
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)
1513 gui_clipboard_shutdown (void)
1515 gtk_target_list_unref (generic_text_targets
);
1516 gtk_target_list_unref (image_targets
);