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