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"
52 // ----------------------------------------------------------------------------
54 static gboolean debug_clipboard
;
55 static gboolean debug_clipboard_dump
;
56 static gboolean debug_clipboard_undump
;
58 // ----------------------------------------------------------------------------
68 ATOM_TEXT_HTML_WINDOWS
,
90 static const char *const atom_names
[] = {
91 "application/x-gnumeric",
92 "application/x-goffice-graph",
101 "application/x-openoffice-biff-8;windows_formatname=\"Biff8\"",
106 "application/x-openoffice;windows_formatname=\"Star Embed Source (XML)\"",
107 "Star Embed Source (XML)",
108 "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"",
120 static GdkAtom atoms
[G_N_ELEMENTS(atom_names
)];
134 static GtkTargetList
*generic_text_targets
;
135 static GtkTargetList
*image_targets
;
137 // ----------------------------------------------------------------------------
141 GnmPasteTarget
*paste_target
;
142 } GnmGtkClipboardCtxt
;
145 gnm_gtk_clipboard_context_free (GnmGtkClipboardCtxt
*ctxt
)
147 g_free (ctxt
->paste_target
);
153 * (x-get-selection-internal 'CLIPBOARD 'TARGETS)
156 /* See if this is a "single line + line end", a "multiline" or a "tab separated"
157 * string. If this is _not_ the case we won't invoke the STF, it is
158 * unlikely that the user will actually need it in this case. */
160 text_is_single_cell (gchar
const *data
, int data_len
)
164 for (i
= 0; i
< data_len
; i
++)
165 if (data
[i
] == '\n' || data
[i
] == '\t')
171 static GnmCellRegion
*
172 text_to_cell_region (WBCGtk
*wbcg
,
173 gchar
const *data
, int data_len
,
174 char const *opt_encoding
,
175 gboolean fixed_encoding
)
177 Workbook
*wb
= wb_control_get_workbook (GNM_WBC (wbcg
));
178 DialogStfResult_t
*dialogresult
;
179 GnmCellRegion
*cr
= NULL
;
181 char *data_converted
= NULL
;
185 * See Redhat #1160975.
187 * I'm unsure why someone gets NULL here, but this is better
194 oneline
= text_is_single_cell (data
, data_len
);
196 if (oneline
&& (opt_encoding
== NULL
|| strcmp (opt_encoding
, "UTF-8") != 0)) {
197 size_t bytes_written
;
198 char const *enc
= opt_encoding
? opt_encoding
: "ASCII";
200 data_converted
= g_convert (data
, data_len
,
202 NULL
, &bytes_written
, NULL
);
203 if (data_converted
) {
204 data
= data_converted
;
205 data_len
= bytes_written
;
207 /* Force STF import since we don't know the charset. */
209 fixed_encoding
= FALSE
;
214 GODateConventions
const *date_conv
= workbook_date_conv (wb
);
215 GnmCellCopy
*cc
= gnm_cell_copy_new (
216 (cr
= gnm_cell_region_new (NULL
)), 0, 0);
217 char *tmp
= g_strndup (data
, data_len
);
219 g_free (data_converted
);
221 cc
->val
= format_match (tmp
, NULL
, date_conv
);
225 cc
->val
= value_new_string_nocopy (tmp
);
228 cr
->cols
= cr
->rows
= 1;
230 dialogresult
= stf_dialog (wbcg
, opt_encoding
, fixed_encoding
,
232 _("clipboard"), data
, data_len
);
234 if (dialogresult
!= NULL
) {
235 cr
= stf_parse_region (dialogresult
->parseoptions
,
236 dialogresult
->text
, NULL
, wb
);
237 g_return_val_if_fail (cr
!= NULL
, gnm_cell_region_new (NULL
));
239 stf_dialog_result_attach_formats_to_cr (dialogresult
, cr
);
241 stf_dialog_result_free (dialogresult
);
243 cr
= gnm_cell_region_new (NULL
);
250 text_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
253 GnmGtkClipboardCtxt
*ctxt
= closure
;
254 WBCGtk
*wbcg
= ctxt
->wbcg
;
255 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
256 GnmPasteTarget
*pt
= ctxt
->paste_target
;
257 GnmCellRegion
*content
= NULL
;
258 GdkAtom target
= gtk_selection_data_get_target (sel
);
259 int sel_len
= gtk_selection_data_get_length (sel
);
261 if (debug_clipboard
) {
263 char *name
= gdk_atom_name (gtk_selection_data_get_target (sel
));
264 g_printerr ("Received %d bytes of text for target %s\n",
268 gsf_mem_dump (gtk_selection_data_get_data (sel
), MIN (sel_len
, maxlen
));
269 if (sel_len
> maxlen
)
270 g_printerr ("...\n");
274 /* Nothing on clipboard? */
277 } else if (target
== atoms
[ATOM_UTF8_STRING
]) {
278 content
= text_to_cell_region (wbcg
, (const char *)gtk_selection_data_get_data (sel
),
279 sel_len
, "UTF-8", TRUE
);
280 } else if (target
== atoms
[ATOM_COMPOUND_TEXT
]) {
281 /* COMPOUND_TEXT is icky. Just let GTK+ do the work. */
282 char *data_utf8
= (char *)gtk_selection_data_get_text (sel
);
283 content
= text_to_cell_region (wbcg
, data_utf8
, strlen (data_utf8
), "UTF-8", TRUE
);
285 } else if (target
== atoms
[ATOM_STRING
]) {
286 char const *locale_encoding
;
287 g_get_charset (&locale_encoding
);
289 content
= text_to_cell_region (wbcg
, (const char *)gtk_selection_data_get_data (sel
),
290 sel_len
, locale_encoding
, FALSE
);
294 * if the conversion from the X selection -> a cellregion
295 * was canceled this may have content sized -1,-1
297 if (content
->cols
> 0 && content
->rows
> 0)
298 cmd_paste_copy (wbc
, pt
, content
);
300 /* Release the resources we used */
301 cellregion_unref (content
);
304 gnm_gtk_clipboard_context_free (ctxt
);
308 utf8_content_received (GtkClipboard
*clipboard
, const gchar
*text
,
311 GnmGtkClipboardCtxt
*ctxt
= closure
;
312 WBCGtk
*wbcg
= ctxt
->wbcg
;
313 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
314 GnmPasteTarget
*pt
= ctxt
->paste_target
;
315 GnmCellRegion
*content
= NULL
;
317 /* Nothing on clipboard? */
318 if (!text
|| strlen(text
) == 0) {
321 content
= text_to_cell_region (wbcg
, text
, strlen(text
), "UTF-8", TRUE
);
325 * if the conversion from the X selection -> a cellregion
326 * was canceled this may have content sized -1,-1
328 if (content
->cols
> 0 && content
->rows
> 0)
329 cmd_paste_copy (wbc
, pt
, content
);
331 /* Release the resources we used */
332 cellregion_unref (content
);
335 gnm_gtk_clipboard_context_free (ctxt
);
339 * Use the file_opener plugin service to read into a temporary workbook, in
340 * order to copy from it to the paste target. A temporary sheet would do just
341 * as well, but the file_opener service makes workbooks, not sheets.
343 * We use the file_opener service by wrapping the selection data in a GsfInput,
344 * and calling workbook_view_new_from_input.
346 static GnmCellRegion
*
347 table_cellregion_read (WorkbookControl
*wbc
, char const *reader_id
,
348 GnmPasteTarget
*pt
, const guchar
*buffer
, int length
)
350 WorkbookView
*wb_view
= NULL
;
352 GnmCellRegion
*ret
= NULL
;
353 const GOFileOpener
*reader
= go_file_opener_for_id (reader_id
);
358 // Likely cause: plugin not loaded
359 g_warning ("No file opener for %s", reader_id
);
363 ioc
= go_io_context_new (GO_CMD_CONTEXT (wbc
));
364 input
= gsf_input_memory_new (buffer
, length
, FALSE
);
365 wb_view
= workbook_view_new_from_input (input
, NULL
, reader
, ioc
, NULL
);
366 if (go_io_error_occurred (ioc
) || wb_view
== NULL
) {
367 go_io_error_display (ioc
);
371 wb
= wb_view_get_workbook (wb_view
);
372 if (workbook_sheet_count (wb
) > 0) {
374 Sheet
*tmpsheet
= workbook_sheet_by_index (wb
, 0);
375 GnmRange
*rp
= g_object_get_data (G_OBJECT (tmpsheet
),
380 // File format didn't tell us the range being
381 // pasted. Looking at you, LibreOffice!
385 GnmStyle
**col_defaults
=
386 sheet_style_most_common (tmpsheet
, TRUE
);
388 range_init_full_sheet (&fullr
, tmpsheet
);
390 r
= sheet_get_cells_extent (tmpsheet
);
391 sheet_style_get_nondefault_extent
392 (tmpsheet
, &r
, &fullr
, col_defaults
);
394 g_free (col_defaults
);
396 // Just in case there was absolutely nothing in
398 if (r
.start
.col
> r
.end
.col
)
399 range_init (&r
, 0, 0, 0, 0);
401 ret
= clipboard_copy_range (tmpsheet
, &r
);
404 /* This isn't particularly right, but we are going to delete
405 the workbook shortly. See #490479. */
406 WORKBOOK_FOREACH_SHEET (wb
, sheet
, {
407 cellregion_invalidate_sheet (ret
, sheet
);
412 g_object_unref (wb_view
);
415 g_object_unref (ioc
);
416 g_object_unref (input
);
422 image_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
425 GnmGtkClipboardCtxt
*ctxt
= closure
;
426 WBCGtk
*wbcg
= ctxt
->wbcg
;
427 GnmPasteTarget
*pt
= ctxt
->paste_target
;
428 int sel_len
= gtk_selection_data_get_length (sel
);
430 if (debug_clipboard
) {
432 char *name
= gdk_atom_name (gtk_selection_data_get_target (sel
));
433 g_printerr ("Received %d bytes of image for target %s\n",
438 gsf_mem_dump (gtk_selection_data_get_data (sel
), MIN (sel_len
, maxlen
));
439 if (sel_len
> maxlen
)
440 g_printerr ("...\n");
445 scg_paste_image (wbcg_cur_scg (wbcg
), &pt
->range
,
446 gtk_selection_data_get_data (sel
), sel_len
);
449 gnm_gtk_clipboard_context_free (ctxt
);
453 parse_ms_headers (const char *data
, size_t length
, size_t *start
, size_t *end
)
455 GHashTable
*headers
= g_hash_table_new_full
456 (g_str_hash
, g_str_equal
, g_free
, g_free
);
457 size_t limit
= length
;
464 while (i
< limit
&& data
[i
] != '<') {
467 for (j
= i
; j
< limit
; j
++) {
468 if (data
[j
] == ':') {
469 key
= g_strndup (data
+ i
, j
- i
);
472 if (g_ascii_isspace (data
[j
]))
479 for (k
= j
; k
< limit
; k
++) {
480 if (data
[k
] == '\n' || data
[k
] == '\r') {
481 value
= g_strndup (data
+ j
, k
- j
);
487 while (g_ascii_isspace (data
[k
]))
493 g_printerr ("MS HTML Header [%s] => [%s]\n", key
, value
);
495 if (strcmp (key
, "StartHTML") == 0) {
496 long l
= strtol (value
, NULL
, 10);
497 limit
= MIN (limit
, (size_t)MAX (0, l
));
500 g_hash_table_replace (headers
, key
, value
);
504 v
= g_hash_table_lookup (headers
, "StartFragment");
505 sf
= v
? strtol (v
, NULL
, 10) : -1;
506 if (sf
< (long)limit
)
509 v
= g_hash_table_lookup (headers
, "EndFragment");
510 ef
= v
? strtol (v
, NULL
, 10) : -1;
511 if (ef
< sf
|| ef
> (long)length
)
525 g_hash_table_destroy (headers
);
530 table_content_received (GtkClipboard
*clipboard
, GtkSelectionData
*sel
,
533 GnmGtkClipboardCtxt
*ctxt
= closure
;
534 WBCGtk
*wbcg
= ctxt
->wbcg
;
535 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
536 GnmPasteTarget
*pt
= ctxt
->paste_target
;
537 GnmCellRegion
*content
= NULL
;
538 GdkAtom target
= gtk_selection_data_get_target (sel
);
539 const guint8
*buffer
= gtk_selection_data_get_data (sel
);
540 int sel_len
= gtk_selection_data_get_length (sel
);
542 if (debug_clipboard
) {
544 char *name
= gdk_atom_name (gtk_selection_data_get_target (sel
));
545 g_printerr ("Received %d bytes of table for target %s\n",
549 gsf_mem_dump (buffer
, MIN (sel_len
, maxlen
));
550 if (sel_len
> maxlen
)
551 g_printerr ("...\n");
555 if (gnm_debug_flag ("clipboard-dump")) {
556 g_file_set_contents ("paste-to-gnumeric.dat",
557 buffer
, sel_len
< 0 ? 0 : sel_len
, NULL
);
560 /* Nothing on clipboard? */
563 } else if (target
== atoms
[ATOM_GNUMERIC
]) {
564 /* The data is the gnumeric specific XML interchange format */
565 GOIOContext
*io_context
=
566 go_io_context_new (GO_CMD_CONTEXT (wbcg
));
567 content
= gnm_xml_cellregion_read
570 (const char *)buffer
, sel_len
);
571 g_object_unref (io_context
);
572 } else if (target
== atoms
[ATOM_OOO
] ||
573 target
== atoms
[ATOM_OOO_WINDOWS
] ||
574 target
== atoms
[ATOM_OOO11
]) {
575 content
= table_cellregion_read (wbc
, "Gnumeric_OpenCalc:openoffice",
578 } else if (target
== atoms
[ATOM_TEXT_HTML
] ||
579 target
== atoms
[ATOM_TEXT_HTML_WINDOWS
]) {
580 size_t start
= 0, end
= sel_len
;
582 if (target
== atoms
[ATOM_TEXT_HTML_WINDOWS
]) {
584 parse_ms_headers (buffer
, sel_len
, &start
, &end
);
587 content
= table_cellregion_read (wbc
, "Gnumeric_html:html",
591 } else if (target
== atoms
[ATOM_BIFF8
] ||
592 target
== atoms
[ATOM_BIFF8_CITRIX
] ||
593 target
== atoms
[ATOM_BIFF8_OO
] ||
594 target
== atoms
[ATOM_BIFF5
] ||
595 target
== atoms
[ATOM_BIFF
]) {
596 content
= table_cellregion_read (wbc
, "Gnumeric_Excel:excel",
602 * if the conversion from the X selection -> a cellregion
603 * was canceled this may have content sized -1,-1
605 if ((content
->cols
> 0 && content
->rows
> 0) ||
606 content
->objects
!= NULL
)
607 cmd_paste_copy (wbc
, pt
, content
);
609 /* Release the resources we used */
610 cellregion_unref (content
);
613 gnm_gtk_clipboard_context_free (ctxt
);
617 find_in_table (GdkAtom
*targets
, int n
, GdkAtom a
)
620 for (i
= 0; i
< n
; i
++)
627 * x_targets_received:
629 * Invoked when the selection has been received by our application.
630 * This is triggered by a call we do to gtk_clipboard_request_contents.
632 * We try to import a spreadsheet/table, next an image, and finally fall back
633 * to a string format if the others fail, e.g. for html which does not
637 x_targets_received (GtkClipboard
*clipboard
, GdkAtom
*targets
,
638 gint n_targets
, gpointer closure
)
640 GnmGtkClipboardCtxt
*ctxt
= closure
;
644 // In order of preference
645 static const int table_fmts
[] = {
647 ATOM_BIFF8
, ATOM_BIFF8_CITRIX
,
648 ATOM_OOO
, ATOM_OOO11
, ATOM_OOO_WINDOWS
,
649 ATOM_BIFF5
, ATOM_BIFF
,
650 ATOM_TEXT_HTML
, ATOM_TEXT_HTML_WINDOWS
,
653 // In order of preference
654 static const int string_fmts
[] = {
660 // Nothing on clipboard? Try text.
661 if (targets
== NULL
|| n_targets
== 0) {
662 gtk_clipboard_request_text (clipboard
, utf8_content_received
,
667 if (debug_clipboard
) {
670 for (j
= 0; j
< n_targets
; j
++) {
671 char *name
= gdk_atom_name (targets
[j
]);
672 g_printerr ("Clipboard target %d is %s\n",
678 // First look for anything that can be considered a spreadsheet
679 for (ui
= 0; ui
< G_N_ELEMENTS(table_fmts
); ui
++) {
680 GdkAtom atom
= atoms
[table_fmts
[ui
]];
681 if (find_in_table (targets
, n_targets
, atom
)) {
682 gtk_clipboard_request_contents (clipboard
, atom
,
683 table_content_received
,
689 // Try an image format
690 for (i
= 0; i
< n_targets
; i
++) {
691 GdkAtom atom
= targets
[i
];
692 if (gtk_target_list_find (image_targets
, atom
, NULL
)) {
693 gtk_clipboard_request_contents (clipboard
, atom
,
694 image_content_received
,
700 // Try a string format
701 for (ui
= 0; ui
< G_N_ELEMENTS (string_fmts
); ui
++) {
702 GdkAtom atom
= atoms
[string_fmts
[ui
]];
703 if (find_in_table (targets
, n_targets
, atom
)) {
704 gtk_clipboard_request_contents (clipboard
, atom
,
705 text_content_received
,
712 gnm_gtk_clipboard_context_free (ctxt
);
715 /* Cheezy implementation: paste into a temporary workbook, save that. */
717 table_cellregion_write (GOCmdContext
*ctx
, GnmCellRegion
*cr
,
718 const char *saver_id
, int *size
)
721 const GOFileSaver
*saver
;
725 WorkbookView
*wb_view
;
730 if (gnm_debug_flag ("clipboard-undump")) {
733 if (g_file_get_contents ("paste-from-gnumeric.dat", &contents
,
735 g_printerr ("Sending %d prepackaged bytes.\n",
738 return (guchar
*)contents
;
744 saver
= go_file_saver_for_id (saver_id
);
746 // Likely cause: plugin not loaded
747 g_printerr ("Failed to get saver for %s for clipboard use.\n",
752 output
= gsf_output_memory_new ();
753 ioc
= go_io_context_new (ctx
);
758 gnm_sheet_suggest_size (&cols
, &rows
);
759 wb
= workbook_new ();
760 workbook_sheet_add (wb
, -1, cols
, rows
);
763 wb_view
= workbook_view_new (wb
);
765 sheet
= workbook_sheet_by_index (wb
, 0);
766 range_init (&r
, 0, 0, cr
->cols
- 1, cr
->rows
- 1);
768 paste_target_init (&pt
, sheet
, &r
,
769 PASTE_AS_VALUES
| PASTE_FORMATS
|
770 PASTE_COMMENTS
| PASTE_OBJECTS
);
771 if (clipboard_paste_region (cr
, &pt
, ctx
) == FALSE
) {
772 go_file_saver_save (saver
, ioc
, GO_VIEW (wb_view
), output
);
773 if (!go_io_error_occurred (ioc
)) {
774 GsfOutputMemory
*omem
= GSF_OUTPUT_MEMORY (output
);
775 gsf_off_t osize
= gsf_output_size (output
);
776 const guint8
*data
= gsf_output_memory_get_bytes (omem
);
778 if (gnm_debug_flag ("clipboard-dump")) {
779 g_file_set_contents ("paste-from-gnumeric.dat",
784 if (*size
== osize
) {
785 ret
= g_memdup (data
, *size
);
787 g_warning ("Overflow"); /* Far fetched! */
791 if (!gsf_output_is_closed (output
))
792 gsf_output_close (output
);
793 g_object_unref (wb_view
);
795 g_object_unref (ioc
);
796 g_object_unref (output
);
802 image_write (GnmCellRegion
*cr
, gchar
const *mime_type
, int *size
)
805 SheetObject
*so
= NULL
;
808 GsfOutputMemory
*omem
;
814 g_return_val_if_fail (cr
->objects
!= NULL
, NULL
);
815 so
= GNM_SO (cr
->objects
->data
);
816 g_return_val_if_fail (so
!= NULL
, NULL
);
818 if (strncmp (mime_type
, "image/", 6) != 0)
820 for (l
= cr
->objects
; l
!= NULL
; l
= l
->next
) {
821 if (GNM_IS_SO_IMAGEABLE (GNM_SO (l
->data
))) {
822 so
= GNM_SO (l
->data
);
827 g_warning ("non imageable object requested as image\n");
831 format
= go_mime_to_image_format (mime_type
);
833 g_warning ("No image format for %s\n", mime_type
);
837 output
= gsf_output_memory_new ();
838 omem
= GSF_OUTPUT_MEMORY (output
);
839 sheet_object_write_image (so
, format
, 150.0, output
, NULL
);
840 osize
= gsf_output_size (output
);
843 if (*size
== osize
) {
844 ret
= g_malloc (*size
);
845 memcpy (ret
, gsf_output_memory_get_bytes (omem
), *size
);
847 g_warning ("Overflow"); /* Far fetched! */
849 gsf_output_close (output
);
850 g_object_unref (output
);
857 object_write (GnmCellRegion
*cr
, gchar
const *mime_type
, int *size
)
860 SheetObject
*so
= NULL
;
862 GsfOutputMemory
*omem
;
868 g_return_val_if_fail (cr
->objects
!= NULL
, NULL
);
869 so
= GNM_SO (cr
->objects
->data
);
870 g_return_val_if_fail (so
!= NULL
, NULL
);
872 for (l
= cr
->objects
; l
!= NULL
; l
= l
->next
) {
873 if (GNM_IS_SO_EXPORTABLE (GNM_SO (l
->data
))) {
874 so
= GNM_SO (l
->data
);
879 g_warning ("non exportable object requested\n");
882 output
= gsf_output_memory_new ();
883 omem
= GSF_OUTPUT_MEMORY (output
);
884 sheet_object_write_object (so
, mime_type
, output
, NULL
,
885 gnm_conventions_default
);
886 osize
= gsf_output_size (output
);
890 ret
= g_memdup (gsf_output_memory_get_bytes (omem
), *size
);
892 g_warning ("Overflow"); /* Far fetched! */
893 gsf_output_close (output
);
894 g_object_unref (output
);
902 * Callback invoked when another application requests we render the selection.
905 x_clipboard_get_cb (GtkClipboard
*gclipboard
, GtkSelectionData
*selection_data
,
906 guint info_
, gpointer app
)
908 gboolean to_gnumeric
= FALSE
, content_needs_free
= FALSE
;
909 GnmCellRegion
*clipboard
= gnm_app_clipboard_contents_get ();
910 Sheet
*sheet
= gnm_app_clipboard_sheet_get ();
911 GnmRange
const *a
= gnm_app_clipboard_area_get ();
912 GOCmdContext
*ctx
= gnm_cmd_context_stderr_new ();
913 GdkAtom target
= gtk_selection_data_get_target (selection_data
);
914 AtomInfoType info
= info_
;
915 gchar
*target_name
= gdk_atom_name (target
);
918 g_printerr ("clipboard requested, target=%s\n", target_name
);
921 * There are 4 cases. What variables are valid depends on case:
923 * a cut: clipboard NULL, sheet, area non-NULL.
924 * a copy: clipboard, sheet, area all non-NULL.
925 * a cut, source closed: clipboard, sheet, area all NULL.
926 * a copy, source closed: clipboard non-NULL, sheet, area non-NULL.
928 * If the source is a cut, we copy it for pasting. We
929 * postpone clearing it until after the selection has been
930 * rendered to the requested format.
932 if (clipboard
== NULL
&& sheet
!= NULL
) {
933 content_needs_free
= TRUE
;
934 clipboard
= clipboard_copy_range (sheet
, a
);
937 if (clipboard
== NULL
)
940 /* What format does the other application want? */
941 if (target
== atoms
[ATOM_GNUMERIC
]) {
942 GsfOutputMemory
*output
= gnm_cellregion_to_xml (clipboard
);
944 gsf_off_t size
= gsf_output_size (GSF_OUTPUT (output
));
945 gconstpointer data
= gsf_output_memory_get_bytes (output
);
946 if (gnm_debug_flag ("clipboard-dump")) {
947 g_file_set_contents ("paste-from-gnumeric.dat",
951 g_printerr ("clipboard .gnumeric of %d bytes\n",
953 gtk_selection_data_set
954 (selection_data
, target
, 8,
957 g_object_unref (output
);
960 } else if (info
== INFO_HTML
) {
961 const char *saver_id
= "Gnumeric_html:xhtml_range";
963 guchar
*buffer
= table_cellregion_write (ctx
, clipboard
,
967 g_message ("clipboard html of %d bytes",
969 gtk_selection_data_set (selection_data
,
971 buffer
, buffer_size
);
973 } else if (info
== INFO_EXCEL
) {
974 const char *saver_id
= "Gnumeric_Excel:excel_biff8";
976 guchar
*buffer
= table_cellregion_write (ctx
, clipboard
,
980 g_message ("clipboard biff8 of %d bytes",
982 gtk_selection_data_set (selection_data
,
984 buffer
, buffer_size
);
986 } else if (target
== atoms
[ATOM_GOFFICE_GRAPH
] ||
987 g_slist_find_custom (go_components_get_mime_types (), target_name
, (GCompareFunc
) strcmp
) != NULL
) {
989 guchar
*buffer
= object_write (clipboard
, target_name
,
992 g_message ("clipboard graph of %d bytes",
994 gtk_selection_data_set (selection_data
,
996 buffer
, buffer_size
);
998 } else if (info
== INFO_IMAGE
) {
1000 guchar
*buffer
= image_write (clipboard
, target_name
,
1002 if (debug_clipboard
)
1003 g_message ("clipboard image of %d bytes",
1005 gtk_selection_data_set (selection_data
,
1007 buffer
, buffer_size
);
1009 } else if (target
== atoms
[ATOM_SAVE_TARGETS
]) {
1010 /* We implicitly registered this when calling
1011 * gtk_clipboard_set_can_store. We're supposed to
1013 } else if (info
== INFO_STRING
) {
1014 Workbook
*wb
= clipboard
->origin_sheet
->workbook
;
1015 GString
*res
= cellregion_to_string (clipboard
,
1016 TRUE
, workbook_date_conv (wb
));
1018 if (debug_clipboard
)
1019 g_message ("clipboard text of %d bytes",
1021 gtk_selection_data_set_text (selection_data
,
1022 res
->str
, res
->len
);
1023 g_string_free (res
, TRUE
);
1025 if (debug_clipboard
)
1026 g_message ("clipboard empty text");
1027 gtk_selection_data_set_text (selection_data
, "", 0);
1030 gtk_selection_data_set_text (selection_data
, "", 0);
1033 * If this was a CUT operation we need to clear the content that
1034 * was pasted into another application and release the stuff on
1037 if (content_needs_free
) {
1039 /* If the other app was a gnumeric, emulate a cut */
1041 GOUndo
*redo
, *undo
;
1042 GnmSheetRange
*sr
= gnm_sheet_range_new (sheet
, a
);
1043 SheetView
const *sv
= gnm_app_clipboard_sheet_view_get ();
1044 SheetControl
*sc
= g_ptr_array_index (sv
->controls
, 0);
1045 WorkbookControl
*wbc
= sc_wbc (sc
);
1049 redo
= sheet_clear_region_undo
1051 CLEAR_VALUES
|CLEAR_COMMENTS
|CLEAR_RECALC_DEPS
);
1052 undo
= clipboard_copy_range_undo (sheet
, a
);
1053 name
= undo_range_name (sheet
, a
);
1054 text
= g_strdup_printf (_("Cut of %s"), name
);
1056 cmd_generic (wbc
, text
, undo
, redo
);
1058 gnm_app_clipboard_clear (TRUE
);
1061 cellregion_unref (clipboard
);
1064 g_free (target_name
);
1065 g_object_unref (ctx
);
1069 * x_clipboard_clear_cb:
1071 * Callback for the "we lost the X selection" signal.
1074 x_clipboard_clear_cb (GtkClipboard
*clipboard
, gpointer app_
)
1076 if (debug_clipboard
)
1077 g_printerr ("Lost clipboard ownership.\n");
1079 gnm_app_clipboard_clear (FALSE
);
1083 gnm_x_request_clipboard (WBCGtk
*wbcg
, GnmPasteTarget
const *pt
)
1085 GnmGtkClipboardCtxt
*ctxt
;
1086 GdkDisplay
*display
= gtk_widget_get_display (GTK_WIDGET (wbcg_toplevel (wbcg
)));
1087 GtkClipboard
*clipboard
=
1088 gtk_clipboard_get_for_display
1090 gnm_conf_get_cut_and_paste_prefer_clipboard ()
1091 ? GDK_SELECTION_CLIPBOARD
1092 : GDK_SELECTION_PRIMARY
);
1094 ctxt
= g_new (GnmGtkClipboardCtxt
, 1);
1096 ctxt
->paste_target
= g_new (GnmPasteTarget
, 1);
1097 *ctxt
->paste_target
= *pt
;
1099 /* Query the formats, This will callback x_targets_received */
1100 gtk_clipboard_request_targets (clipboard
,
1101 x_targets_received
, ctxt
);
1104 /* Restrict the set of formats offered to clipboard manager. */
1105 /* We include bmp in the whitelist because that's the only image format
1106 * we share with OOo over clipboard (!) */
1109 is_clipman_target (const char *target
)
1111 return (g_str_equal (target
, atom_names
[ATOM_GNUMERIC
]) ||
1112 g_str_equal (target
, atom_names
[ATOM_GOFFICE_GRAPH
]) ||
1113 g_str_equal (target
, atom_names
[ATOM_TEXT_HTML
]) ||
1114 g_str_equal (target
, atom_names
[ATOM_UTF8_STRING
]) ||
1115 g_str_equal (target
, atom_names
[ATOM_BIFF8_OO
]) ||
1116 g_str_equal (target
, atom_names
[ATOM_IMAGE_SVGXML
]) ||
1117 g_str_equal (target
, atom_names
[ATOM_IMAGE_XWMF
]) ||
1118 g_str_equal (target
, atom_names
[ATOM_IMAGE_XEMF
]) ||
1119 g_str_equal (target
, atom_names
[ATOM_IMAGE_PNG
]) ||
1120 g_str_equal (target
, atom_names
[ATOM_IMAGE_JPEG
]) ||
1121 g_str_equal (target
, atom_names
[ATOM_IMAGE_BMP
]));
1125 set_clipman_targets (GdkDisplay
*disp
, GArray
*targets
)
1127 GArray
*allowed
= g_array_new (FALSE
, FALSE
, sizeof (GtkTargetEntry
));
1130 for (ui
= 0; ui
< targets
->len
; ui
++) {
1131 GtkTargetEntry
*te
= &g_array_index (targets
, GtkTargetEntry
, ui
);
1132 if (is_clipman_target (te
->target
))
1133 g_array_append_val (allowed
, *te
);
1136 gtk_clipboard_set_can_store
1137 (gtk_clipboard_get_for_display
1138 (disp
, GDK_SELECTION_CLIPBOARD
),
1139 &g_array_index (allowed
, GtkTargetEntry
, 0),
1142 g_array_free (allowed
, TRUE
);
1146 add_target (GArray
*targets
, const char *target
, int flags
, AtomInfoType info
)
1149 t
.target
= (char *)target
;
1152 g_array_append_val (targets
, t
);
1157 add_target_list (GArray
*targets
, GtkTargetList
*src
, AtomInfoType info
)
1160 GtkTargetEntry
*entries
= gtk_target_table_new_from_list (src
, &n
);
1161 unsigned ui
= targets
->len
;
1162 g_array_append_vals (targets
, entries
, n
);
1163 if (info
!= INFO_UNKNOWN
) {
1164 for (; ui
< targets
->len
; ui
++)
1165 g_array_index (targets
, GtkTargetEntry
, ui
).info
= info
;
1167 gtk_target_table_free (entries
, n
);
1171 gnm_x_claim_clipboard (GdkDisplay
*display
)
1173 GnmCellRegion
*content
= gnm_app_clipboard_contents_get ();
1174 SheetObject
*imageable
= NULL
, *exportable
= NULL
;
1175 GArray
*targets
= g_array_new (FALSE
, FALSE
, sizeof (GtkTargetEntry
));
1177 GObject
*app
= gnm_app_get_app ();
1178 gboolean no_cells
= (!content
) || (content
->cols
<= 0 || content
->rows
<= 0);
1181 GSList
*ptr
= content
? content
->objects
: NULL
;
1183 add_target (targets
, atom_names
[ATOM_GNUMERIC
], 0, INFO_GNUMERIC
);
1185 for (; ptr
!= NULL
; ptr
= ptr
->next
) {
1186 SheetObject
*candidate
= GNM_SO (ptr
->data
);
1187 if (exportable
== NULL
&& GNM_IS_SO_EXPORTABLE (candidate
))
1188 exportable
= candidate
;
1189 if (imageable
== NULL
&& GNM_IS_SO_IMAGEABLE (candidate
))
1190 imageable
= candidate
;
1193 add_target (targets
, atom_names
[ATOM_GNUMERIC
], 0, INFO_GNUMERIC
);
1194 add_target (targets
, atom_names
[ATOM_BIFF8
], 0, INFO_EXCEL
);
1195 add_target (targets
, atom_names
[ATOM_BIFF8_CITRIX
], 0, INFO_EXCEL
);
1196 add_target (targets
, atom_names
[ATOM_BIFF8_OO
], 0, INFO_EXCEL
);
1198 add_target (targets
, atom_names
[ATOM_TEXT_HTML_WINDOWS
], 0, INFO_HTML
);
1200 add_target (targets
, atom_names
[ATOM_TEXT_HTML
], 0, INFO_HTML
);
1202 add_target (targets
, atom_names
[ATOM_UTF8_STRING
], 0, INFO_STRING
);
1203 add_target (targets
, atom_names
[ATOM_COMPOUND_TEXT
], 0, INFO_STRING
);
1204 add_target (targets
, atom_names
[ATOM_STRING
], 0, INFO_STRING
);
1209 sheet_object_exportable_get_target_list (exportable
);
1210 add_target_list (targets
, tl
, INFO_OBJECT
);
1211 gtk_target_list_unref (tl
);
1216 sheet_object_get_target_list (imageable
);
1217 add_target_list (targets
, tl
, INFO_IMAGE
);
1218 gtk_target_list_unref (tl
);
1221 /* Register a x_clipboard_clear_cb only for CLIPBOARD, not for
1223 ret
= gtk_clipboard_set_with_owner (
1224 gtk_clipboard_get_for_display (display
, GDK_SELECTION_CLIPBOARD
),
1225 &g_array_index(targets
,GtkTargetEntry
,0), targets
->len
,
1227 x_clipboard_clear_cb
,
1230 if (debug_clipboard
) {
1232 g_printerr ("Clipboard successfully claimed.\n");
1233 g_printerr ("Clipboard targets offered: ");
1234 for (ui
= 0; ui
< targets
->len
; ui
++) {
1237 g_array_index(targets
,GtkTargetEntry
,ui
).target
);
1242 g_object_set_data_full (app
, APP_CLIP_DISP_KEY
,
1243 g_slist_prepend (g_object_steal_data (app
, APP_CLIP_DISP_KEY
),
1245 (GDestroyNotify
)g_slist_free
);
1247 set_clipman_targets (display
, targets
);
1248 (void)gtk_clipboard_set_with_owner (
1249 gtk_clipboard_get_for_display (display
,
1250 GDK_SELECTION_PRIMARY
),
1251 &g_array_index(targets
,GtkTargetEntry
,0), targets
->len
,
1256 if (debug_clipboard
)
1257 g_printerr ("Failed to claim clipboard.\n");
1260 g_array_free (targets
, TRUE
);
1266 gnm_x_disown_clipboard (void)
1268 GObject
*app
= gnm_app_get_app ();
1269 GSList
*displays
= g_object_steal_data (app
, APP_CLIP_DISP_KEY
);
1272 for (l
= displays
; l
; l
= l
->next
) {
1273 GdkDisplay
*display
= l
->data
;
1274 gtk_selection_owner_set_for_display (display
, NULL
,
1275 GDK_SELECTION_PRIMARY
,
1277 gtk_selection_owner_set_for_display (display
, NULL
,
1278 GDK_SELECTION_CLIPBOARD
,
1281 g_slist_free (displays
);
1284 /* Hand clipboard off to clipboard manager. To be called before workbook
1285 * object is destroyed.
1288 gnm_x_store_clipboard_if_needed (Workbook
*wb
)
1290 Sheet
*sheet
= gnm_app_clipboard_sheet_get ();
1291 WBCGtk
*wbcg
= NULL
;
1293 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
1295 if (sheet
&& sheet
->workbook
== wb
) {
1296 WORKBOOK_FOREACH_CONTROL (wb
, view
, control
, {
1297 if (GNM_IS_WBC_GTK (control
)) {
1298 wbcg
= WBC_GTK (control
);
1303 GtkClipboard
*clip
= gtk_clipboard_get_for_display
1304 (gtk_widget_get_display
1305 (GTK_WIDGET (wbcg_toplevel (wbcg
))),
1306 GDK_SELECTION_CLIPBOARD
);
1307 if (gtk_clipboard_get_owner (clip
) == gnm_app_get_app ()) {
1308 if (debug_clipboard
)
1309 g_printerr ("Handing off clipboard\n");
1310 gtk_clipboard_store (clip
);
1317 * gui_clipboard_init: (skip)
1320 gui_clipboard_init (void)
1324 debug_clipboard
= gnm_debug_flag ("clipboard");
1325 debug_clipboard_dump
= gnm_debug_flag ("clipboard-dump");
1326 debug_clipboard_undump
= gnm_debug_flag ("clipboard-undump");
1328 for (ui
= 0; ui
< G_N_ELEMENTS (atoms
); ui
++)
1329 atoms
[ui
] = gdk_atom_intern_static_string (atom_names
[ui
]);
1331 generic_text_targets
= gtk_target_list_new (NULL
, 0);
1332 gtk_target_list_add_text_targets (generic_text_targets
, INFO_STRING
);
1334 image_targets
= gtk_target_list_new (NULL
, 0);
1335 gtk_target_list_add_image_targets (image_targets
, 0, FALSE
);
1339 * gui_clipboard_shutdown: (skip)
1342 gui_clipboard_shutdown (void)
1344 gtk_target_list_unref (generic_text_targets
);
1345 gtk_target_list_unref (image_targets
);