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
5  *
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.
10  *
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.
15  *
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.
20  *
21  */
22 
23 #include "config.h"
24 #include <string.h>
25 
26 #import <Cocoa/Cocoa.h>
27 
28 #include "gtkclipboard.h"
29 #include "gtkinvisible.h"
30 #include "gtkmain.h"
31 #include "gtkmarshalers.h"
32 #include "gtkintl.h"
33 #include "gtktextbuffer.h"
34 #include "gtkquartz.h"
35 #include "gtkalias.h"
36 #include <gdk/quartz/gdkquartz.h>
37 
38 enum {
39   OWNER_CHANGE,
40   LAST_SIGNAL
41 };
42 
43 @interface GtkClipboardOwner : NSObject {
44   GtkClipboard *clipboard;
45   @public
46   gboolean setting_same_owner;
47 }
48 
49 @end
50 
51 typedef struct _GtkClipboardClass GtkClipboardClass;
52 
53 struct _GtkClipboard
54 {
55   GObject parent_instance;
56 
57   NSPasteboard *pasteboard;
58   GtkClipboardOwner *owner;
59   NSInteger change_count;
60 
61   GdkAtom selection;
62 
63   GtkClipboardGetFunc get_func;
64   GtkClipboardClearFunc clear_func;
65   gpointer user_data;
66   gboolean have_owner;
67   GtkTargetList *target_list;
68 
69   gboolean have_selection;
70   GdkDisplay *display;
71 
72   GdkAtom *cached_targets;
73   gint     n_cached_targets;
74 
75   guint      notify_signal_id;
76   gboolean   storing_selection;
77   GMainLoop *store_loop;
78   guint      store_timeout;
79   gint       n_storable_targets;
80   GdkAtom   *storable_targets;
81 };
82 
83 struct _GtkClipboardClass
84 {
85   GObjectClass parent_class;
86 
87   void (*owner_change) (GtkClipboard        *clipboard,
88 			GdkEventOwnerChange *event);
89 };
90 
91 static void gtk_clipboard_class_init   (GtkClipboardClass   *class);
92 static void gtk_clipboard_finalize     (GObject             *object);
93 static void gtk_clipboard_owner_change (GtkClipboard        *clipboard,
94 					GdkEventOwnerChange *event);
95 
96 static void          clipboard_unset      (GtkClipboard     *clipboard);
97 static GtkClipboard *clipboard_peek       (GdkDisplay       *display,
98 					   GdkAtom           selection,
99 					   gboolean          only_if_exists);
100 
101 @implementation GtkClipboardOwner
102 -(void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
103 {
104   GtkSelectionData selection_data;
105   guint info;
106 
107   if (!clipboard->target_list)
108     return;
109 
110   memset (&selection_data, 0, sizeof (GtkSelectionData));
111 
112   selection_data.selection = clipboard->selection;
113   selection_data.target = gdk_quartz_pasteboard_type_to_atom_libgtk_only (type);
114   selection_data.display = gdk_display_get_default ();
115   selection_data.length = -1;
116 
117   if (gtk_target_list_find (clipboard->target_list, selection_data.target, &info))
118     {
119       clipboard->get_func (clipboard, &selection_data,
120                            info,
121                            clipboard->user_data);
122 
123       if (selection_data.length >= 0)
124         _gtk_quartz_set_selection_data_for_pasteboard (clipboard->pasteboard,
125                                                        &selection_data);
126 
127       g_free (selection_data.data);
128     }
129 }
130 
131 /*  pasteboardChangedOwner is not called immediately, and it's not called
132  *  reliably. It is somehow documented in the apple api docs, but the docs
133  *  suck and don't really give clear instructions. Therefore we track
134  *  changeCount in several places below and clear the clipboard if it
135  *  changed.
136  */
137 - (void)pasteboardChangedOwner:(NSPasteboard *)sender
138 {
139   if (! setting_same_owner)
140     clipboard_unset (clipboard);
141 }
142 
143 - (id)initWithClipboard:(GtkClipboard *)aClipboard
144 {
145   self = [super init];
146 
147   if (self)
148     {
149       clipboard = aClipboard;
150       setting_same_owner = FALSE;
151     }
152 
153   return self;
154 }
155 
156 @end
157 
158 
159 static const gchar clipboards_owned_key[] = "gtk-clipboards-owned";
160 static GQuark clipboards_owned_key_id = 0;
161 
162 static GObjectClass *parent_class;
163 static guint         clipboard_signals[LAST_SIGNAL] = { 0 };
164 
165 GType
gtk_clipboard_get_type(void)166 gtk_clipboard_get_type (void)
167 {
168   static GType clipboard_type = 0;
169 
170   if (!clipboard_type)
171     {
172       const GTypeInfo clipboard_info =
173       {
174 	sizeof (GtkClipboardClass),
175 	NULL,           /* base_init */
176 	NULL,           /* base_finalize */
177 	(GClassInitFunc) gtk_clipboard_class_init,
178 	NULL,           /* class_finalize */
179 	NULL,           /* class_data */
180 	sizeof (GtkClipboard),
181 	0,              /* n_preallocs */
182 	(GInstanceInitFunc) NULL,
183       };
184 
185       clipboard_type = g_type_register_static (G_TYPE_OBJECT, I_("GtkClipboard"),
186 					       &clipboard_info, 0);
187     }
188 
189   return clipboard_type;
190 }
191 
192 static void
gtk_clipboard_class_init(GtkClipboardClass * class)193 gtk_clipboard_class_init (GtkClipboardClass *class)
194 {
195   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
196 
197   parent_class = g_type_class_peek_parent (class);
198 
199   gobject_class->finalize = gtk_clipboard_finalize;
200 
201   class->owner_change = gtk_clipboard_owner_change;
202 
203   clipboard_signals[OWNER_CHANGE] =
204     g_signal_new (I_("owner-change"),
205 		  G_TYPE_FROM_CLASS (gobject_class),
206 		  G_SIGNAL_RUN_FIRST,
207 		  G_STRUCT_OFFSET (GtkClipboardClass, owner_change),
208 		  NULL, NULL,
209 		  _gtk_marshal_VOID__BOXED,
210 		  G_TYPE_NONE, 1,
211 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
212 }
213 
214 static void
gtk_clipboard_finalize(GObject * object)215 gtk_clipboard_finalize (GObject *object)
216 {
217   GtkClipboard *clipboard;
218   GSList *clipboards;
219 
220   clipboard = GTK_CLIPBOARD (object);
221 
222   clipboards = g_object_get_data (G_OBJECT (clipboard->display), "gtk-clipboard-list");
223   if (g_slist_index (clipboards, clipboard) >= 0)
224     g_warning ("GtkClipboard prematurely finalized");
225 
226   clipboard_unset (clipboard);
227 
228   clipboards = g_object_get_data (G_OBJECT (clipboard->display), "gtk-clipboard-list");
229   clipboards = g_slist_remove (clipboards, clipboard);
230   g_object_set_data (G_OBJECT (clipboard->display), I_("gtk-clipboard-list"), clipboards);
231 
232   if (clipboard->store_loop && g_main_loop_is_running (clipboard->store_loop))
233     g_main_loop_quit (clipboard->store_loop);
234 
235   if (clipboard->store_timeout != 0)
236     g_source_remove (clipboard->store_timeout);
237 
238   g_free (clipboard->storable_targets);
239 
240   G_OBJECT_CLASS (parent_class)->finalize (object);
241 }
242 
243 static void
clipboard_display_closed(GdkDisplay * display,gboolean is_error,GtkClipboard * clipboard)244 clipboard_display_closed (GdkDisplay   *display,
245 			  gboolean      is_error,
246 			  GtkClipboard *clipboard)
247 {
248   GSList *clipboards;
249 
250   clipboards = g_object_get_data (G_OBJECT (display), "gtk-clipboard-list");
251   g_object_run_dispose (G_OBJECT (clipboard));
252   clipboards = g_slist_remove (clipboards, clipboard);
253   g_object_set_data (G_OBJECT (display), I_("gtk-clipboard-list"), clipboards);
254   g_object_unref (clipboard);
255 }
256 
257 GtkClipboard *
gtk_clipboard_get_for_display(GdkDisplay * display,GdkAtom selection)258 gtk_clipboard_get_for_display (GdkDisplay *display,
259 			       GdkAtom     selection)
260 {
261   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
262   g_return_val_if_fail (!display->closed, NULL);
263 
264   return clipboard_peek (display, selection, FALSE);
265 }
266 
267 GtkClipboard *
gtk_clipboard_get(GdkAtom selection)268 gtk_clipboard_get (GdkAtom selection)
269 {
270   return gtk_clipboard_get_for_display (gdk_display_get_default (), selection);
271 }
272 
273 static void
clipboard_owner_destroyed(gpointer data)274 clipboard_owner_destroyed (gpointer data)
275 {
276   GSList *clipboards = data;
277   GSList *tmp_list;
278 
279   tmp_list = clipboards;
280   while (tmp_list)
281     {
282       GtkClipboard *clipboard = tmp_list->data;
283 
284       clipboard->get_func = NULL;
285       clipboard->clear_func = NULL;
286       clipboard->user_data = NULL;
287       clipboard->have_owner = FALSE;
288 
289       if (clipboard->target_list)
290         {
291           gtk_target_list_unref (clipboard->target_list);
292           clipboard->target_list = NULL;
293         }
294 
295       gtk_clipboard_clear (clipboard);
296 
297       tmp_list = tmp_list->next;
298     }
299 
300   g_slist_free (clipboards);
301 }
302 
303 static void
clipboard_add_owner_notify(GtkClipboard * clipboard)304 clipboard_add_owner_notify (GtkClipboard *clipboard)
305 {
306   if (!clipboards_owned_key_id)
307     clipboards_owned_key_id = g_quark_from_static_string (clipboards_owned_key);
308 
309   if (clipboard->have_owner)
310     g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id,
311 			     g_slist_prepend (g_object_steal_qdata (clipboard->user_data,
312 								    clipboards_owned_key_id),
313 					      clipboard),
314 			     clipboard_owner_destroyed);
315 }
316 
317 static void
clipboard_remove_owner_notify(GtkClipboard * clipboard)318 clipboard_remove_owner_notify (GtkClipboard *clipboard)
319 {
320   if (clipboard->have_owner)
321      g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id,
322 			      g_slist_remove (g_object_steal_qdata (clipboard->user_data,
323 								    clipboards_owned_key_id),
324 					      clipboard),
325 			      clipboard_owner_destroyed);
326 }
327 
328 static gboolean
gtk_clipboard_set_contents(GtkClipboard * clipboard,const GtkTargetEntry * targets,guint n_targets,GtkClipboardGetFunc get_func,GtkClipboardClearFunc clear_func,gpointer user_data,gboolean have_owner)329 gtk_clipboard_set_contents (GtkClipboard         *clipboard,
330 			    const GtkTargetEntry *targets,
331 			    guint                 n_targets,
332 			    GtkClipboardGetFunc   get_func,
333 			    GtkClipboardClearFunc clear_func,
334 			    gpointer              user_data,
335 			    gboolean              have_owner)
336 {
337   GtkClipboardOwner *owner;
338   NSSet *types;
339   NSAutoreleasePool *pool;
340 
341   if (!(clipboard->have_owner && have_owner) ||
342       clipboard->user_data != user_data)
343     {
344       clipboard_unset (clipboard);
345 
346       if (clipboard->get_func)
347         {
348           /* Calling unset() caused the clipboard contents to be reset!
349            * Avoid leaking and return
350            */
351           if (!(clipboard->have_owner && have_owner) ||
352               clipboard->user_data != user_data)
353             {
354               (*clear_func) (clipboard, user_data);
355               return FALSE;
356             }
357           else
358             {
359               return TRUE;
360             }
361         }
362     }
363 
364   pool = [[NSAutoreleasePool alloc] init];
365 
366   types = _gtk_quartz_target_entries_to_pasteboard_types (targets, n_targets);
367 
368   /*  call declareTypes before setting the clipboard members because
369    *  declareTypes might clear the clipboard
370    */
371   if (user_data && user_data == clipboard->user_data)
372     {
373       owner = [clipboard->owner retain];
374 
375       owner->setting_same_owner = TRUE;
376       clipboard->change_count = [clipboard->pasteboard declareTypes: [types allObjects]
377                                                               owner: owner];
378       owner->setting_same_owner = FALSE;
379     }
380   else
381     {
382       owner = [[GtkClipboardOwner alloc] initWithClipboard:clipboard];
383 
384       clipboard->change_count = [clipboard->pasteboard declareTypes: [types allObjects]
385                                                               owner: owner];
386     }
387 
388   [owner release];
389   [types release];
390   [pool release];
391 
392   clipboard->owner = owner;
393   clipboard->user_data = user_data;
394   clipboard->have_owner = have_owner;
395   if (have_owner)
396     clipboard_add_owner_notify (clipboard);
397   clipboard->get_func = get_func;
398   clipboard->clear_func = clear_func;
399 
400   if (clipboard->target_list)
401     gtk_target_list_unref (clipboard->target_list);
402   clipboard->target_list = gtk_target_list_new (targets, n_targets);
403 
404   return TRUE;
405 }
406 
407 gboolean
gtk_clipboard_set_with_data(GtkClipboard * clipboard,const GtkTargetEntry * targets,guint n_targets,GtkClipboardGetFunc get_func,GtkClipboardClearFunc clear_func,gpointer user_data)408 gtk_clipboard_set_with_data (GtkClipboard          *clipboard,
409 			     const GtkTargetEntry  *targets,
410 			     guint                  n_targets,
411 			     GtkClipboardGetFunc    get_func,
412 			     GtkClipboardClearFunc  clear_func,
413 			     gpointer               user_data)
414 {
415   g_return_val_if_fail (clipboard != NULL, FALSE);
416   g_return_val_if_fail (targets != NULL, FALSE);
417   g_return_val_if_fail (get_func != NULL, FALSE);
418 
419   return gtk_clipboard_set_contents (clipboard, targets, n_targets,
420 				     get_func, clear_func, user_data,
421 				     FALSE);
422 }
423 
424 gboolean
gtk_clipboard_set_with_owner(GtkClipboard * clipboard,const GtkTargetEntry * targets,guint n_targets,GtkClipboardGetFunc get_func,GtkClipboardClearFunc clear_func,GObject * owner)425 gtk_clipboard_set_with_owner (GtkClipboard          *clipboard,
426 			      const GtkTargetEntry  *targets,
427 			      guint                  n_targets,
428 			      GtkClipboardGetFunc    get_func,
429 			      GtkClipboardClearFunc  clear_func,
430 			      GObject               *owner)
431 {
432   g_return_val_if_fail (clipboard != NULL, FALSE);
433   g_return_val_if_fail (targets != NULL, FALSE);
434   g_return_val_if_fail (get_func != NULL, FALSE);
435   g_return_val_if_fail (G_IS_OBJECT (owner), FALSE);
436 
437   return gtk_clipboard_set_contents (clipboard, targets, n_targets,
438 				     get_func, clear_func, owner,
439 				     TRUE);
440 }
441 
442 GObject *
gtk_clipboard_get_owner(GtkClipboard * clipboard)443 gtk_clipboard_get_owner (GtkClipboard *clipboard)
444 {
445   g_return_val_if_fail (clipboard != NULL, NULL);
446 
447   if (clipboard->change_count < [clipboard->pasteboard changeCount])
448     {
449       clipboard_unset (clipboard);
450       clipboard->change_count = [clipboard->pasteboard changeCount];
451     }
452 
453   if (clipboard->have_owner)
454     return clipboard->user_data;
455   else
456     return NULL;
457 }
458 
459 static void
clipboard_unset(GtkClipboard * clipboard)460 clipboard_unset (GtkClipboard *clipboard)
461 {
462   GtkClipboardClearFunc old_clear_func;
463   gpointer old_data;
464   gboolean old_have_owner;
465   gint old_n_storable_targets;
466 
467   old_clear_func = clipboard->clear_func;
468   old_data = clipboard->user_data;
469   old_have_owner = clipboard->have_owner;
470   old_n_storable_targets = clipboard->n_storable_targets;
471 
472   if (old_have_owner)
473     {
474       clipboard_remove_owner_notify (clipboard);
475       clipboard->have_owner = FALSE;
476     }
477 
478   clipboard->n_storable_targets = -1;
479   g_free (clipboard->storable_targets);
480   clipboard->storable_targets = NULL;
481 
482   clipboard->owner = NULL;
483   clipboard->get_func = NULL;
484   clipboard->clear_func = NULL;
485   clipboard->user_data = NULL;
486 
487   if (old_clear_func)
488     old_clear_func (clipboard, old_data);
489 
490   if (clipboard->target_list)
491     {
492       gtk_target_list_unref (clipboard->target_list);
493       clipboard->target_list = NULL;
494     }
495 
496   /* If we've transferred the clipboard data to the manager,
497    * unref the owner
498    */
499   if (old_have_owner &&
500       old_n_storable_targets != -1)
501     g_object_unref (old_data);
502 }
503 
504 void
gtk_clipboard_clear(GtkClipboard * clipboard)505 gtk_clipboard_clear (GtkClipboard *clipboard)
506 {
507   clipboard_unset (clipboard);
508 
509   [clipboard->pasteboard declareTypes:nil owner:nil];
510 }
511 
512 static void
text_get_func(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer data)513 text_get_func (GtkClipboard     *clipboard,
514 	       GtkSelectionData *selection_data,
515 	       guint             info,
516 	       gpointer          data)
517 {
518   gtk_selection_data_set_text (selection_data, data, -1);
519 }
520 
521 static void
text_clear_func(GtkClipboard * clipboard,gpointer data)522 text_clear_func (GtkClipboard *clipboard,
523 		 gpointer      data)
524 {
525   g_free (data);
526 }
527 
528 void
gtk_clipboard_set_text(GtkClipboard * clipboard,const gchar * text,gint len)529 gtk_clipboard_set_text (GtkClipboard *clipboard,
530 			const gchar  *text,
531 			gint          len)
532 {
533   GtkTargetEntry target = { "UTF8_STRING", 0, 0 };
534 
535   g_return_if_fail (clipboard != NULL);
536   g_return_if_fail (text != NULL);
537 
538   if (len < 0)
539     len = strlen (text);
540 
541   gtk_clipboard_set_with_data (clipboard,
542 			       &target, 1,
543 			       text_get_func, text_clear_func,
544 			       g_strndup (text, len));
545   gtk_clipboard_set_can_store (clipboard, NULL, 0);
546 }
547 
548 
549 static void
pixbuf_get_func(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer data)550 pixbuf_get_func (GtkClipboard     *clipboard,
551 		 GtkSelectionData *selection_data,
552 		 guint             info,
553 		 gpointer          data)
554 {
555   gtk_selection_data_set_pixbuf (selection_data, data);
556 }
557 
558 static void
pixbuf_clear_func(GtkClipboard * clipboard,gpointer data)559 pixbuf_clear_func (GtkClipboard *clipboard,
560 		   gpointer      data)
561 {
562   g_object_unref (data);
563 }
564 
565 void
gtk_clipboard_set_image(GtkClipboard * clipboard,GdkPixbuf * pixbuf)566 gtk_clipboard_set_image (GtkClipboard *clipboard,
567 			 GdkPixbuf    *pixbuf)
568 {
569   GtkTargetList *list;
570   GList *l;
571   GtkTargetEntry *targets;
572   gint n_targets, i;
573 
574   g_return_if_fail (clipboard != NULL);
575   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
576 
577   list = gtk_target_list_new (NULL, 0);
578   gtk_target_list_add_image_targets (list, 0, TRUE);
579 
580   n_targets = g_list_length (list->list);
581   targets = g_new0 (GtkTargetEntry, n_targets);
582   for (l = list->list, i = 0; l; l = l->next, i++)
583     {
584       GtkTargetPair *pair = (GtkTargetPair *)l->data;
585       targets[i].target = gdk_atom_name (pair->target);
586     }
587 
588   gtk_clipboard_set_with_data (clipboard,
589 			       targets, n_targets,
590 			       pixbuf_get_func, pixbuf_clear_func,
591 			       g_object_ref (pixbuf));
592   gtk_clipboard_set_can_store (clipboard, NULL, 0);
593 
594   for (i = 0; i < n_targets; i++)
595     g_free (targets[i].target);
596   g_free (targets);
597   gtk_target_list_unref (list);
598 }
599 
600 void
gtk_clipboard_request_contents(GtkClipboard * clipboard,GdkAtom target,GtkClipboardReceivedFunc callback,gpointer user_data)601 gtk_clipboard_request_contents (GtkClipboard            *clipboard,
602 				GdkAtom                  target,
603 				GtkClipboardReceivedFunc callback,
604 				gpointer                 user_data)
605 {
606   GtkSelectionData *data;
607 
608   data = gtk_clipboard_wait_for_contents (clipboard, target);
609 
610   callback (clipboard, data, user_data);
611 
612   gtk_selection_data_free (data);
613 }
614 
615 void
gtk_clipboard_request_text(GtkClipboard * clipboard,GtkClipboardTextReceivedFunc callback,gpointer user_data)616 gtk_clipboard_request_text (GtkClipboard                *clipboard,
617 			    GtkClipboardTextReceivedFunc callback,
618 			    gpointer                     user_data)
619 {
620   gchar *data = gtk_clipboard_wait_for_text (clipboard);
621 
622   callback (clipboard, data, user_data);
623 
624   g_free (data);
625 }
626 
627 void
gtk_clipboard_request_rich_text(GtkClipboard * clipboard,GtkTextBuffer * buffer,GtkClipboardRichTextReceivedFunc callback,gpointer user_data)628 gtk_clipboard_request_rich_text (GtkClipboard                    *clipboard,
629                                  GtkTextBuffer                   *buffer,
630                                  GtkClipboardRichTextReceivedFunc callback,
631                                  gpointer                         user_data)
632 {
633   /* FIXME: Implement */
634 }
635 
636 
637 guint8 *
gtk_clipboard_wait_for_rich_text(GtkClipboard * clipboard,GtkTextBuffer * buffer,GdkAtom * format,gsize * length)638 gtk_clipboard_wait_for_rich_text (GtkClipboard  *clipboard,
639                                   GtkTextBuffer *buffer,
640                                   GdkAtom       *format,
641                                   gsize         *length)
642 {
643   /* FIXME: Implement */
644   return NULL;
645 }
646 
647 void
gtk_clipboard_request_image(GtkClipboard * clipboard,GtkClipboardImageReceivedFunc callback,gpointer user_data)648 gtk_clipboard_request_image (GtkClipboard                  *clipboard,
649 			     GtkClipboardImageReceivedFunc  callback,
650 			     gpointer                       user_data)
651 {
652   GdkPixbuf *pixbuf = gtk_clipboard_wait_for_image (clipboard);
653 
654   callback (clipboard, pixbuf, user_data);
655 
656   if (pixbuf)
657     g_object_unref (pixbuf);
658 }
659 
660 void
gtk_clipboard_request_uris(GtkClipboard * clipboard,GtkClipboardURIReceivedFunc callback,gpointer user_data)661 gtk_clipboard_request_uris (GtkClipboard                *clipboard,
662 			    GtkClipboardURIReceivedFunc  callback,
663 			    gpointer                     user_data)
664 {
665   gchar **uris = gtk_clipboard_wait_for_uris (clipboard);
666 
667   callback (clipboard, uris, user_data);
668 
669   g_strfreev (uris);
670 }
671 
672 void
gtk_clipboard_request_targets(GtkClipboard * clipboard,GtkClipboardTargetsReceivedFunc callback,gpointer user_data)673 gtk_clipboard_request_targets (GtkClipboard                *clipboard,
674 			       GtkClipboardTargetsReceivedFunc callback,
675 			       gpointer                     user_data)
676 {
677   GdkAtom *targets;
678   gint n_targets;
679 
680   gtk_clipboard_wait_for_targets (clipboard, &targets, &n_targets);
681 
682   callback (clipboard, targets, n_targets, user_data);
683 }
684 
685 
686 GtkSelectionData *
gtk_clipboard_wait_for_contents(GtkClipboard * clipboard,GdkAtom target)687 gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
688 				 GdkAtom       target)
689 {
690   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
691   GtkSelectionData *selection_data = NULL;
692 
693   if (clipboard->change_count < [clipboard->pasteboard changeCount])
694     {
695       clipboard_unset (clipboard);
696       clipboard->change_count = [clipboard->pasteboard changeCount];
697     }
698 
699   if (target == gdk_atom_intern_static_string ("TARGETS"))
700     {
701       NSArray *types = [clipboard->pasteboard types];
702       int i, length;
703       GList *atom_list, *l;
704       GdkAtom *atoms;
705 
706       length = [types count] * sizeof (GdkAtom);
707 
708       selection_data = g_slice_new0 (GtkSelectionData);
709       selection_data->selection = clipboard->selection;
710       selection_data->target = target;
711       if (!selection_data->display)
712 	selection_data->display = gdk_display_get_default ();
713 
714       atoms = g_malloc (length);
715 
716       atom_list = _gtk_quartz_pasteboard_types_to_atom_list (types);
717       for (l = atom_list, i = 0; l ; l = l->next, i++)
718 	atoms[i] = GDK_POINTER_TO_ATOM (l->data);
719       g_list_free (atom_list);
720 
721       gtk_selection_data_set (selection_data,
722                               GDK_SELECTION_TYPE_ATOM, 32,
723                               (guchar *)atoms, length);
724 
725       [pool release];
726 
727       return selection_data;
728     }
729 
730   selection_data = _gtk_quartz_get_selection_data_from_pasteboard (clipboard->pasteboard,
731 								   target,
732 								   clipboard->selection);
733 
734   [pool release];
735 
736   return selection_data;
737 }
738 
739 gchar *
gtk_clipboard_wait_for_text(GtkClipboard * clipboard)740 gtk_clipboard_wait_for_text (GtkClipboard *clipboard)
741 {
742   GtkSelectionData *data;
743   gchar *result;
744 
745   data = gtk_clipboard_wait_for_contents (clipboard,
746 					  gdk_atom_intern_static_string ("UTF8_STRING"));
747 
748   result = (gchar *)gtk_selection_data_get_text (data);
749 
750   gtk_selection_data_free (data);
751 
752   return result;
753 }
754 
755 GdkPixbuf *
gtk_clipboard_wait_for_image(GtkClipboard * clipboard)756 gtk_clipboard_wait_for_image (GtkClipboard *clipboard)
757 {
758   GdkAtom target = gdk_atom_intern_static_string("image/tiff");
759   int i;
760   GtkSelectionData *data;
761 
762   data = gtk_clipboard_wait_for_contents (clipboard, target);
763 
764   if (data && data->data)
765     {
766       GdkPixbuf *pixbuf = gtk_selection_data_get_pixbuf (data);
767       gtk_selection_data_free (data);
768       return pixbuf;
769     }
770 
771   return NULL;
772 }
773 
774 gchar **
gtk_clipboard_wait_for_uris(GtkClipboard * clipboard)775 gtk_clipboard_wait_for_uris (GtkClipboard *clipboard)
776 {
777   GtkSelectionData *data;
778 
779   data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("text/uri-list"));
780   if (data)
781     {
782       gchar **uris;
783 
784       uris = gtk_selection_data_get_uris (data);
785       gtk_selection_data_free (data);
786 
787       return uris;
788     }
789 
790   return NULL;
791 }
792 
793 GdkDisplay *
gtk_clipboard_get_display(GtkClipboard * clipboard)794 gtk_clipboard_get_display (GtkClipboard *clipboard)
795 {
796   g_return_val_if_fail (clipboard != NULL, NULL);
797 
798   return clipboard->display;
799 }
800 
801 gboolean
gtk_clipboard_wait_is_text_available(GtkClipboard * clipboard)802 gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard)
803 {
804   GtkSelectionData *data;
805   gboolean result = FALSE;
806 
807   data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS"));
808   if (data)
809     {
810       result = gtk_selection_data_targets_include_text (data);
811       gtk_selection_data_free (data);
812     }
813 
814   return result;
815 }
816 
817 gboolean
gtk_clipboard_wait_is_rich_text_available(GtkClipboard * clipboard,GtkTextBuffer * buffer)818 gtk_clipboard_wait_is_rich_text_available (GtkClipboard  *clipboard,
819                                            GtkTextBuffer *buffer)
820 {
821   GtkSelectionData *data;
822   gboolean result = FALSE;
823 
824   g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE);
825   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
826 
827   data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS"));
828   if (data)
829     {
830       result = gtk_selection_data_targets_include_rich_text (data, buffer);
831       gtk_selection_data_free (data);
832     }
833 
834   return result;
835 }
836 
837 gboolean
gtk_clipboard_wait_is_image_available(GtkClipboard * clipboard)838 gtk_clipboard_wait_is_image_available (GtkClipboard *clipboard)
839 {
840   GtkSelectionData *data;
841   gboolean result = FALSE;
842 
843   data = gtk_clipboard_wait_for_contents (clipboard,
844 					  gdk_atom_intern_static_string ("TARGETS"));
845   if (data)
846     {
847       result = gtk_selection_data_targets_include_image (data, FALSE);
848       gtk_selection_data_free (data);
849     }
850 
851   return result;
852 }
853 
854 gboolean
gtk_clipboard_wait_is_uris_available(GtkClipboard * clipboard)855 gtk_clipboard_wait_is_uris_available (GtkClipboard *clipboard)
856 {
857   GtkSelectionData *data;
858   gboolean result = FALSE;
859 
860   data = gtk_clipboard_wait_for_contents (clipboard,
861 					  gdk_atom_intern_static_string ("TARGETS"));
862   if (data)
863     {
864       result = gtk_selection_data_targets_include_uri (data);
865       gtk_selection_data_free (data);
866     }
867 
868   return result;
869 }
870 
871 gboolean
gtk_clipboard_wait_for_targets(GtkClipboard * clipboard,GdkAtom ** targets,gint * n_targets)872 gtk_clipboard_wait_for_targets (GtkClipboard  *clipboard,
873 				GdkAtom      **targets,
874 				gint          *n_targets)
875 {
876   GtkSelectionData *data;
877   gboolean result = FALSE;
878 
879   g_return_val_if_fail (clipboard != NULL, FALSE);
880 
881   /* If the display supports change notification we cache targets */
882   if (gdk_display_supports_selection_notification (gtk_clipboard_get_display (clipboard)) &&
883       clipboard->n_cached_targets != -1)
884     {
885       if (n_targets)
886  	*n_targets = clipboard->n_cached_targets;
887 
888       if (targets)
889  	*targets = g_memdup (clipboard->cached_targets,
890  			     clipboard->n_cached_targets * sizeof (GdkAtom));
891 
892        return TRUE;
893     }
894 
895   if (n_targets)
896     *n_targets = 0;
897 
898   if (targets)
899     *targets = NULL;
900 
901   data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS"));
902 
903   if (data)
904     {
905       GdkAtom *tmp_targets;
906       gint tmp_n_targets;
907 
908       result = gtk_selection_data_get_targets (data, &tmp_targets, &tmp_n_targets);
909 
910       if (gdk_display_supports_selection_notification (gtk_clipboard_get_display (clipboard)))
911  	{
912  	  clipboard->n_cached_targets = tmp_n_targets;
913  	  clipboard->cached_targets = g_memdup (tmp_targets,
914  						tmp_n_targets * sizeof (GdkAtom));
915  	}
916 
917       if (n_targets)
918  	*n_targets = tmp_n_targets;
919 
920       if (targets)
921  	*targets = tmp_targets;
922       else
923  	g_free (tmp_targets);
924 
925       gtk_selection_data_free (data);
926     }
927 
928   return result;
929 }
930 
931 static GtkClipboard *
clipboard_peek(GdkDisplay * display,GdkAtom selection,gboolean only_if_exists)932 clipboard_peek (GdkDisplay *display,
933 		GdkAtom     selection,
934 		gboolean    only_if_exists)
935 {
936   GtkClipboard *clipboard = NULL;
937   GSList *clipboards;
938   GSList *tmp_list;
939 
940   if (selection == GDK_NONE)
941     selection = GDK_SELECTION_CLIPBOARD;
942 
943   clipboards = g_object_get_data (G_OBJECT (display), "gtk-clipboard-list");
944 
945   tmp_list = clipboards;
946   while (tmp_list)
947     {
948       clipboard = tmp_list->data;
949       if (clipboard->selection == selection)
950 	break;
951 
952       tmp_list = tmp_list->next;
953     }
954 
955   if (!tmp_list && !only_if_exists)
956     {
957       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
958       NSString *pasteboard_name;
959       clipboard = g_object_new (GTK_TYPE_CLIPBOARD, NULL);
960 
961       if (selection == GDK_SELECTION_CLIPBOARD)
962 	pasteboard_name = NSGeneralPboard;
963       else
964 	{
965 	  char *atom_string = gdk_atom_name (selection);
966 
967 	  pasteboard_name = [NSString stringWithFormat:@"_GTK_%@",
968 			     [NSString stringWithUTF8String:atom_string]];
969 	  g_free (atom_string);
970 	}
971 
972       clipboard->pasteboard = [NSPasteboard pasteboardWithName:pasteboard_name];
973 
974       [pool release];
975 
976       clipboard->selection = selection;
977       clipboard->display = display;
978       clipboard->n_cached_targets = -1;
979       clipboard->n_storable_targets = -1;
980       clipboards = g_slist_prepend (clipboards, clipboard);
981       g_object_set_data (G_OBJECT (display), I_("gtk-clipboard-list"), clipboards);
982       g_signal_connect (display, "closed",
983 			G_CALLBACK (clipboard_display_closed), clipboard);
984       gdk_display_request_selection_notification (display, selection);
985     }
986 
987   return clipboard;
988 }
989 
990 static void
gtk_clipboard_owner_change(GtkClipboard * clipboard,GdkEventOwnerChange * event)991 gtk_clipboard_owner_change (GtkClipboard        *clipboard,
992 			    GdkEventOwnerChange *event)
993 {
994   if (clipboard->n_cached_targets != -1)
995     {
996       clipboard->n_cached_targets = -1;
997       g_free (clipboard->cached_targets);
998     }
999 }
1000 
1001 gboolean
gtk_clipboard_wait_is_target_available(GtkClipboard * clipboard,GdkAtom target)1002 gtk_clipboard_wait_is_target_available (GtkClipboard *clipboard,
1003 					GdkAtom       target)
1004 {
1005   GdkAtom *targets;
1006   gint i, n_targets;
1007   gboolean retval = FALSE;
1008 
1009   if (!gtk_clipboard_wait_for_targets (clipboard, &targets, &n_targets))
1010     return FALSE;
1011 
1012   for (i = 0; i < n_targets; i++)
1013     {
1014       if (targets[i] == target)
1015 	{
1016 	  retval = TRUE;
1017 	  break;
1018 	}
1019     }
1020 
1021   g_free (targets);
1022 
1023   return retval;
1024 }
1025 
1026 void
_gtk_clipboard_handle_event(GdkEventOwnerChange * event)1027 _gtk_clipboard_handle_event (GdkEventOwnerChange *event)
1028 {
1029 }
1030 
1031 void
gtk_clipboard_set_can_store(GtkClipboard * clipboard,const GtkTargetEntry * targets,gint n_targets)1032 gtk_clipboard_set_can_store (GtkClipboard         *clipboard,
1033  			     const GtkTargetEntry *targets,
1034  			     gint                  n_targets)
1035 {
1036   /* FIXME: Implement */
1037 }
1038 
1039 void
gtk_clipboard_store(GtkClipboard * clipboard)1040 gtk_clipboard_store (GtkClipboard *clipboard)
1041 {
1042   int i;
1043   int n_targets = 0;
1044   GtkTargetEntry *targets;
1045 
1046   g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
1047 
1048   if (!clipboard->target_list || !clipboard->get_func)
1049     return;
1050 
1051   /* We simply store all targets into the OS X clipboard. We should be
1052    * using the functions gdk_display_supports_clipboard_persistence() and
1053    * gdk_display_store_clipboard(), but since for OS X the clipboard support
1054    * was implemented in GTK+ and not through GdkSelections, we do it this
1055    * way. Doing this properly could be worthwhile to implement in the future.
1056    */
1057 
1058   targets = gtk_target_table_new_from_list (clipboard->target_list,
1059                                             &n_targets);
1060   for (i = 0; i < n_targets; i++)
1061     {
1062       GtkSelectionData selection_data;
1063 
1064       /* in each loop iteration, check if the content is still
1065        * there, because calling get_func() can do anything to
1066        * the clipboard
1067        */
1068       if (!clipboard->target_list || !clipboard->get_func)
1069         break;
1070 
1071       memset (&selection_data, 0, sizeof (GtkSelectionData));
1072 
1073       selection_data.selection = clipboard->selection;
1074       selection_data.target = gdk_atom_intern_static_string (targets[i].target);
1075       selection_data.display = gdk_display_get_default ();
1076       selection_data.length = -1;
1077 
1078       clipboard->get_func (clipboard, &selection_data,
1079                            targets[i].info, clipboard->user_data);
1080 
1081       if (selection_data.length >= 0)
1082         _gtk_quartz_set_selection_data_for_pasteboard (clipboard->pasteboard,
1083                                                        &selection_data);
1084 
1085       g_free (selection_data.data);
1086     }
1087 
1088   if (targets)
1089     gtk_target_table_free (targets, n_targets);
1090 }
1091 
1092 void
_gtk_clipboard_store_all(void)1093 _gtk_clipboard_store_all (void)
1094 {
1095   GtkClipboard *clipboard;
1096   GSList *displays, *list;
1097 
1098   displays = gdk_display_manager_list_displays (gdk_display_manager_get ());
1099 
1100   list = displays;
1101   while (list)
1102     {
1103       GdkDisplay *display = list->data;
1104 
1105       clipboard = clipboard_peek (display, GDK_SELECTION_CLIPBOARD, TRUE);
1106 
1107       if (clipboard)
1108         gtk_clipboard_store (clipboard);
1109 
1110       list = list->next;
1111     }
1112   g_slist_free (displays);
1113 }
1114 
1115 #define __GTK_CLIPBOARD_C__
1116 #include "gtkaliasdef.c"
1117