1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2000 Red Hat, Inc.
3 * Copyright (C) 2004 Nokia Corporation
4 * Copyright (C) 2006-2008 Imendio AB
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
26 #import <Cocoa/Cocoa.h>
28 #include "gtkclipboard.h"
29 #include "gtkinvisible.h"
31 #include "gtkmarshalers.h"
33 #include "gtktextbuffer.h"
34 #include "gtkquartz.h"
42 typedef struct _GtkClipboardClass GtkClipboardClass;
46 GObject parent_instance;
48 NSPasteboard *pasteboard;
52 GtkClipboardGetFunc get_func;
53 GtkClipboardClearFunc clear_func;
56 GtkTargetList *target_list;
58 gboolean have_selection;
61 GdkAtom *cached_targets;
62 gint n_cached_targets;
64 guint notify_signal_id;
65 gboolean storing_selection;
66 GMainLoop *store_loop;
68 gint n_storable_targets;
69 GdkAtom *storable_targets;
72 struct _GtkClipboardClass
74 GObjectClass parent_class;
76 void (*owner_change) (GtkClipboard *clipboard,
77 GdkEventOwnerChange *event);
80 @interface GtkClipboardOwner : NSObject {
81 GtkClipboard *clipboard;
83 GtkClipboardGetFunc get_func;
84 GtkClipboardClearFunc clear_func;
91 @implementation GtkClipboardOwner
92 -(void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
94 GtkSelectionData selection_data;
97 if (!clipboard->target_list)
100 memset (&selection_data, 0, sizeof (GtkSelectionData));
102 selection_data.selection = clipboard->selection;
103 selection_data.target = _gtk_quartz_pasteboard_type_to_atom (type);
104 selection_data.display = gdk_display_get_default ();
105 selection_data.length = -1;
107 if (gtk_target_list_find (clipboard->target_list, selection_data.target, &info))
109 clipboard->get_func (clipboard, &selection_data,
111 clipboard->user_data);
113 _gtk_quartz_set_selection_data_for_pasteboard (clipboard->pasteboard,
116 g_free (selection_data.data);
120 - (void)pasteboardChangedOwner:(NSPasteboard *)sender
123 clear_func (clipboard, user_data);
128 - (id)initWithClipboard:(GtkClipboard *)aClipboard
134 clipboard = aClipboard;
142 static void gtk_clipboard_class_init (GtkClipboardClass *class);
143 static void gtk_clipboard_finalize (GObject *object);
144 static void gtk_clipboard_owner_change (GtkClipboard *clipboard,
145 GdkEventOwnerChange *event);
147 static void clipboard_unset (GtkClipboard *clipboard);
148 static GtkClipboard *clipboard_peek (GdkDisplay *display,
150 gboolean only_if_exists);
152 static const gchar clipboards_owned_key[] = "gtk-clipboards-owned";
153 static GQuark clipboards_owned_key_id = 0;
155 static GObjectClass *parent_class;
156 static guint clipboard_signals[LAST_SIGNAL] = { 0 };
159 gtk_clipboard_get_type (void)
161 static GType clipboard_type = 0;
165 const GTypeInfo clipboard_info =
167 sizeof (GtkClipboardClass),
168 NULL, /* base_init */
169 NULL, /* base_finalize */
170 (GClassInitFunc) gtk_clipboard_class_init,
171 NULL, /* class_finalize */
172 NULL, /* class_data */
173 sizeof (GtkClipboard),
175 (GInstanceInitFunc) NULL,
178 clipboard_type = g_type_register_static (G_TYPE_OBJECT, I_("GtkClipboard"),
182 return clipboard_type;
186 gtk_clipboard_class_init (GtkClipboardClass *class)
188 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
190 parent_class = g_type_class_peek_parent (class);
192 gobject_class->finalize = gtk_clipboard_finalize;
194 class->owner_change = gtk_clipboard_owner_change;
196 clipboard_signals[OWNER_CHANGE] =
197 g_signal_new (I_("owner-change"),
198 G_TYPE_FROM_CLASS (gobject_class),
200 G_STRUCT_OFFSET (GtkClipboardClass, owner_change),
202 _gtk_marshal_VOID__BOXED,
204 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
208 gtk_clipboard_finalize (GObject *object)
210 GtkClipboard *clipboard;
213 clipboard = GTK_CLIPBOARD (object);
215 clipboards = g_object_get_data (G_OBJECT (clipboard->display), "gtk-clipboard-list");
216 if (g_slist_index (clipboards, clipboard) >= 0)
217 g_warning ("GtkClipboard prematurely finalized");
219 clipboard_unset (clipboard);
221 clipboards = g_object_get_data (G_OBJECT (clipboard->display), "gtk-clipboard-list");
222 clipboards = g_slist_remove (clipboards, clipboard);
223 g_object_set_data (G_OBJECT (clipboard->display), I_("gtk-clipboard-list"), clipboards);
225 if (clipboard->store_loop && g_main_loop_is_running (clipboard->store_loop))
226 g_main_loop_quit (clipboard->store_loop);
228 if (clipboard->store_timeout != 0)
229 g_source_remove (clipboard->store_timeout);
231 g_free (clipboard->storable_targets);
233 G_OBJECT_CLASS (parent_class)->finalize (object);
237 clipboard_display_closed (GdkDisplay *display,
239 GtkClipboard *clipboard)
243 clipboards = g_object_get_data (G_OBJECT (display), "gtk-clipboard-list");
244 g_object_run_dispose (G_OBJECT (clipboard));
245 clipboards = g_slist_remove (clipboards, clipboard);
246 g_object_set_data (G_OBJECT (display), I_("gtk-clipboard-list"), clipboards);
247 g_object_unref (clipboard);
251 gtk_clipboard_get_for_display (GdkDisplay *display,
254 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
255 g_return_val_if_fail (!display->closed, NULL);
257 return clipboard_peek (display, selection, FALSE);
261 gtk_clipboard_get (GdkAtom selection)
263 return gtk_clipboard_get_for_display (gdk_display_get_default (), selection);
267 clipboard_owner_destroyed (gpointer data)
269 GSList *clipboards = data;
272 tmp_list = clipboards;
275 GtkClipboard *clipboard = tmp_list->data;
277 clipboard->get_func = NULL;
278 clipboard->clear_func = NULL;
279 clipboard->user_data = NULL;
280 clipboard->have_owner = FALSE;
282 if (clipboard->target_list)
284 gtk_target_list_unref (clipboard->target_list);
285 clipboard->target_list = NULL;
288 gtk_clipboard_clear (clipboard);
290 tmp_list = tmp_list->next;
293 g_slist_free (clipboards);
297 clipboard_add_owner_notify (GtkClipboard *clipboard)
299 if (!clipboards_owned_key_id)
300 clipboards_owned_key_id = g_quark_from_static_string (clipboards_owned_key);
302 if (clipboard->have_owner)
303 g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id,
304 g_slist_prepend (g_object_steal_qdata (clipboard->user_data,
305 clipboards_owned_key_id),
307 clipboard_owner_destroyed);
311 clipboard_remove_owner_notify (GtkClipboard *clipboard)
313 if (clipboard->have_owner)
314 g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id,
315 g_slist_remove (g_object_steal_qdata (clipboard->user_data,
316 clipboards_owned_key_id),
318 clipboard_owner_destroyed);
322 gtk_clipboard_set_contents (GtkClipboard *clipboard,
323 const GtkTargetEntry *targets,
325 GtkClipboardGetFunc get_func,
326 GtkClipboardClearFunc clear_func,
330 GtkClipboardOwner *owner;
332 NSAutoreleasePool *pool;
334 pool = [[NSAutoreleasePool alloc] init];
336 owner = [[GtkClipboardOwner alloc] initWithClipboard:clipboard];
338 if (!(clipboard->have_owner && have_owner) ||
339 clipboard->user_data != user_data)
341 clipboard_unset (clipboard);
343 if (clipboard->get_func)
345 /* Calling unset() caused the clipboard contents to be reset!
346 * Avoid leaking and return
348 if (!(clipboard->have_owner && have_owner) ||
349 clipboard->user_data != user_data)
351 (*clear_func) (clipboard, user_data);
363 clipboard->user_data = user_data;
364 clipboard->have_owner = have_owner;
366 clipboard_add_owner_notify (clipboard);
367 clipboard->get_func = get_func;
368 clipboard->clear_func = clear_func;
370 if (clipboard->target_list)
371 gtk_target_list_unref (clipboard->target_list);
372 clipboard->target_list = gtk_target_list_new (targets, n_targets);
374 types = _gtk_quartz_target_entries_to_pasteboard_types (targets, n_targets);
376 [clipboard->pasteboard declareTypes:[types allObjects] owner:owner];
384 gtk_clipboard_set_with_data (GtkClipboard *clipboard,
385 const GtkTargetEntry *targets,
387 GtkClipboardGetFunc get_func,
388 GtkClipboardClearFunc clear_func,
391 g_return_val_if_fail (clipboard != NULL, FALSE);
392 g_return_val_if_fail (targets != NULL, FALSE);
393 g_return_val_if_fail (get_func != NULL, FALSE);
395 return gtk_clipboard_set_contents (clipboard, targets, n_targets,
396 get_func, clear_func, user_data,
401 gtk_clipboard_set_with_owner (GtkClipboard *clipboard,
402 const GtkTargetEntry *targets,
404 GtkClipboardGetFunc get_func,
405 GtkClipboardClearFunc clear_func,
408 g_return_val_if_fail (clipboard != NULL, FALSE);
409 g_return_val_if_fail (targets != NULL, FALSE);
410 g_return_val_if_fail (get_func != NULL, FALSE);
411 g_return_val_if_fail (G_IS_OBJECT (owner), FALSE);
413 return gtk_clipboard_set_contents (clipboard, targets, n_targets,
414 get_func, clear_func, owner,
419 gtk_clipboard_get_owner (GtkClipboard *clipboard)
421 g_return_val_if_fail (clipboard != NULL, NULL);
423 if (clipboard->have_owner)
424 return clipboard->user_data;
430 clipboard_unset (GtkClipboard *clipboard)
432 GtkClipboardClearFunc old_clear_func;
434 gboolean old_have_owner;
435 gint old_n_storable_targets;
437 old_clear_func = clipboard->clear_func;
438 old_data = clipboard->user_data;
439 old_have_owner = clipboard->have_owner;
440 old_n_storable_targets = clipboard->n_storable_targets;
444 clipboard_remove_owner_notify (clipboard);
445 clipboard->have_owner = FALSE;
448 clipboard->n_storable_targets = -1;
449 g_free (clipboard->storable_targets);
450 clipboard->storable_targets = NULL;
452 clipboard->get_func = NULL;
453 clipboard->clear_func = NULL;
454 clipboard->user_data = NULL;
457 old_clear_func (clipboard, old_data);
459 if (clipboard->target_list)
461 gtk_target_list_unref (clipboard->target_list);
462 clipboard->target_list = NULL;
465 /* If we've transferred the clipboard data to the manager,
468 if (old_have_owner &&
469 old_n_storable_targets != -1)
470 g_object_unref (old_data);
474 gtk_clipboard_clear (GtkClipboard *clipboard)
476 [clipboard->pasteboard declareTypes:nil owner:nil];
480 text_get_func (GtkClipboard *clipboard,
481 GtkSelectionData *selection_data,
485 gtk_selection_data_set_text (selection_data, data, -1);
489 text_clear_func (GtkClipboard *clipboard,
496 gtk_clipboard_set_text (GtkClipboard *clipboard,
500 GtkTargetEntry target = { "UTF8_STRING", 0, 0 };
502 g_return_if_fail (clipboard != NULL);
503 g_return_if_fail (text != NULL);
508 gtk_clipboard_set_with_data (clipboard,
510 text_get_func, text_clear_func,
511 g_strndup (text, len));
512 gtk_clipboard_set_can_store (clipboard, NULL, 0);
517 pixbuf_get_func (GtkClipboard *clipboard,
518 GtkSelectionData *selection_data,
522 gtk_selection_data_set_pixbuf (selection_data, data);
526 pixbuf_clear_func (GtkClipboard *clipboard,
529 g_object_unref (data);
533 gtk_clipboard_set_image (GtkClipboard *clipboard,
538 GtkTargetEntry *targets;
541 g_return_if_fail (clipboard != NULL);
542 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
544 list = gtk_target_list_new (NULL, 0);
545 gtk_target_list_add_image_targets (list, 0, TRUE);
547 n_targets = g_list_length (list->list);
548 targets = g_new0 (GtkTargetEntry, n_targets);
549 for (l = list->list, i = 0; l; l = l->next, i++)
551 GtkTargetPair *pair = (GtkTargetPair *)l->data;
552 targets[i].target = gdk_atom_name (pair->target);
555 gtk_clipboard_set_with_data (clipboard,
557 pixbuf_get_func, pixbuf_clear_func,
558 g_object_ref (pixbuf));
559 gtk_clipboard_set_can_store (clipboard, NULL, 0);
561 for (i = 0; i < n_targets; i++)
562 g_free (targets[i].target);
564 gtk_target_list_unref (list);
568 gtk_clipboard_request_contents (GtkClipboard *clipboard,
570 GtkClipboardReceivedFunc callback,
573 GtkSelectionData *data;
575 data = gtk_clipboard_wait_for_contents (clipboard, target);
577 callback (clipboard, data, user_data);
579 gtk_selection_data_free (data);
583 gtk_clipboard_request_text (GtkClipboard *clipboard,
584 GtkClipboardTextReceivedFunc callback,
587 gchar *data = gtk_clipboard_wait_for_text (clipboard);
589 callback (clipboard, data, user_data);
595 gtk_clipboard_request_rich_text (GtkClipboard *clipboard,
596 GtkTextBuffer *buffer,
597 GtkClipboardRichTextReceivedFunc callback,
600 /* FIXME: Implement */
605 gtk_clipboard_wait_for_rich_text (GtkClipboard *clipboard,
606 GtkTextBuffer *buffer,
610 /* FIXME: Implement */
615 gtk_clipboard_request_image (GtkClipboard *clipboard,
616 GtkClipboardImageReceivedFunc callback,
619 GdkPixbuf *pixbuf = gtk_clipboard_wait_for_image (clipboard);
621 callback (clipboard, pixbuf, user_data);
624 g_object_unref (pixbuf);
628 gtk_clipboard_request_uris (GtkClipboard *clipboard,
629 GtkClipboardURIReceivedFunc callback,
632 gchar **uris = gtk_clipboard_wait_for_uris (clipboard);
634 callback (clipboard, uris, user_data);
640 gtk_clipboard_request_targets (GtkClipboard *clipboard,
641 GtkClipboardTargetsReceivedFunc callback,
647 gtk_clipboard_wait_for_targets (clipboard, &targets, &n_targets);
649 callback (clipboard, targets, n_targets, user_data);
654 gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
657 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
658 GtkSelectionData *selection_data = NULL;
660 if (target == gdk_atom_intern_static_string ("TARGETS"))
662 NSArray *types = [clipboard->pasteboard types];
664 GList *atom_list, *l;
667 length = [types count] * sizeof (GdkAtom);
669 selection_data = g_slice_new0 (GtkSelectionData);
670 selection_data->selection = clipboard->selection;
671 selection_data->target = target;
673 atoms = g_malloc (length);
675 atom_list = _gtk_quartz_pasteboard_types_to_atom_list (types);
676 for (l = atom_list, i = 0; l ; l = l->next, i++)
677 atoms[i] = GDK_POINTER_TO_ATOM (l->data);
678 g_list_free (atom_list);
680 gtk_selection_data_set (selection_data,
681 GDK_SELECTION_TYPE_ATOM, 32,
682 (guchar *)atoms, length);
686 return selection_data;
689 selection_data = _gtk_quartz_get_selection_data_from_pasteboard (clipboard->pasteboard,
691 clipboard->selection);
695 return selection_data;
699 gtk_clipboard_wait_for_text (GtkClipboard *clipboard)
701 GtkSelectionData *data;
704 data = gtk_clipboard_wait_for_contents (clipboard,
705 gdk_atom_intern_static_string ("UTF8_STRING"));
707 result = (gchar *)gtk_selection_data_get_text (data);
709 gtk_selection_data_free (data);
715 gtk_clipboard_wait_for_image (GtkClipboard *clipboard)
717 const gchar *priority[] = { "image/png", "image/tiff", "image/jpeg", "image/gif", "image/bmp" };
719 GtkSelectionData *data;
721 for (i = 0; i < G_N_ELEMENTS (priority); i++)
723 data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string (priority[i]));
727 GdkPixbuf *pixbuf = gtk_selection_data_get_pixbuf (data);
729 gtk_selection_data_free (data);
739 gtk_clipboard_wait_for_uris (GtkClipboard *clipboard)
741 GtkSelectionData *data;
743 data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("text/uri-list"));
748 uris = gtk_selection_data_get_uris (data);
749 gtk_selection_data_free (data);
758 gtk_clipboard_get_display (GtkClipboard *clipboard)
760 g_return_val_if_fail (clipboard != NULL, NULL);
762 return clipboard->display;
766 gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard)
768 GtkSelectionData *data;
769 gboolean result = FALSE;
771 data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS"));
774 result = gtk_selection_data_targets_include_text (data);
775 gtk_selection_data_free (data);
782 gtk_clipboard_wait_is_rich_text_available (GtkClipboard *clipboard,
783 GtkTextBuffer *buffer)
785 GtkSelectionData *data;
786 gboolean result = FALSE;
788 g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE);
789 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
791 data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS"));
794 result = gtk_selection_data_targets_include_rich_text (data, buffer);
795 gtk_selection_data_free (data);
802 gtk_clipboard_wait_is_image_available (GtkClipboard *clipboard)
804 GtkSelectionData *data;
805 gboolean result = FALSE;
807 data = gtk_clipboard_wait_for_contents (clipboard,
808 gdk_atom_intern_static_string ("TARGETS"));
811 result = gtk_selection_data_targets_include_image (data, FALSE);
812 gtk_selection_data_free (data);
819 gtk_clipboard_wait_is_uris_available (GtkClipboard *clipboard)
821 GtkSelectionData *data;
822 gboolean result = FALSE;
824 data = gtk_clipboard_wait_for_contents (clipboard,
825 gdk_atom_intern_static_string ("TARGETS"));
828 result = gtk_selection_data_targets_include_uri (data);
829 gtk_selection_data_free (data);
836 gtk_clipboard_wait_for_targets (GtkClipboard *clipboard,
840 GtkSelectionData *data;
841 gboolean result = FALSE;
843 g_return_val_if_fail (clipboard != NULL, FALSE);
845 /* If the display supports change notification we cache targets */
846 if (gdk_display_supports_selection_notification (gtk_clipboard_get_display (clipboard)) &&
847 clipboard->n_cached_targets != -1)
850 *n_targets = clipboard->n_cached_targets;
853 *targets = g_memdup (clipboard->cached_targets,
854 clipboard->n_cached_targets * sizeof (GdkAtom));
865 data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS"));
869 GdkAtom *tmp_targets;
872 result = gtk_selection_data_get_targets (data, &tmp_targets, &tmp_n_targets);
874 if (gdk_display_supports_selection_notification (gtk_clipboard_get_display (clipboard)))
876 clipboard->n_cached_targets = tmp_n_targets;
877 clipboard->cached_targets = g_memdup (tmp_targets,
878 tmp_n_targets * sizeof (GdkAtom));
882 *n_targets = tmp_n_targets;
885 *targets = tmp_targets;
887 g_free (tmp_targets);
889 gtk_selection_data_free (data);
895 static GtkClipboard *
896 clipboard_peek (GdkDisplay *display,
898 gboolean only_if_exists)
900 GtkClipboard *clipboard = NULL;
904 if (selection == GDK_NONE)
905 selection = GDK_SELECTION_CLIPBOARD;
907 clipboards = g_object_get_data (G_OBJECT (display), "gtk-clipboard-list");
909 tmp_list = clipboards;
912 clipboard = tmp_list->data;
913 if (clipboard->selection == selection)
916 tmp_list = tmp_list->next;
919 if (!tmp_list && !only_if_exists)
921 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
922 NSString *pasteboard_name;
923 clipboard = g_object_new (GTK_TYPE_CLIPBOARD, NULL);
925 if (selection == GDK_SELECTION_CLIPBOARD)
926 pasteboard_name = NSGeneralPboard;
929 char *atom_string = gdk_atom_name (selection);
931 pasteboard_name = [NSString stringWithFormat:@"_GTK_%@",
932 [NSString stringWithUTF8String:atom_string]];
933 g_free (atom_string);
936 clipboard->pasteboard = [NSPasteboard pasteboardWithName:pasteboard_name];
940 clipboard->selection = selection;
941 clipboard->display = display;
942 clipboard->n_cached_targets = -1;
943 clipboard->n_storable_targets = -1;
944 clipboards = g_slist_prepend (clipboards, clipboard);
945 g_object_set_data (G_OBJECT (display), I_("gtk-clipboard-list"), clipboards);
946 g_signal_connect (display, "closed",
947 G_CALLBACK (clipboard_display_closed), clipboard);
948 gdk_display_request_selection_notification (display, selection);
955 gtk_clipboard_owner_change (GtkClipboard *clipboard,
956 GdkEventOwnerChange *event)
958 if (clipboard->n_cached_targets != -1)
960 clipboard->n_cached_targets = -1;
961 g_free (clipboard->cached_targets);
966 gtk_clipboard_wait_is_target_available (GtkClipboard *clipboard,
971 gboolean retval = FALSE;
973 if (!gtk_clipboard_wait_for_targets (clipboard, &targets, &n_targets))
976 for (i = 0; i < n_targets; i++)
978 if (targets[i] == target)
991 _gtk_clipboard_handle_event (GdkEventOwnerChange *event)
996 gtk_clipboard_set_can_store (GtkClipboard *clipboard,
997 const GtkTargetEntry *targets,
1000 /* FIXME: Implement */
1004 gtk_clipboard_store (GtkClipboard *clipboard)
1006 /* FIXME: Implement */
1010 _gtk_clipboard_store_all (void)
1012 /* FIXME: Implement */