]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkclipboard-quartz.c
gdk/gdkwindow.c, gtk/gtkwidget.c: Include fallback-c89.c
[~andy/gtk] / gtk / gtkclipboard-quartz.c
index 3a765d3d1f7027256fd346a3f9765aee3e5e2d82..482f6aa13518de0977cab81706e0423b2cd309c9 100644 (file)
 #include "gtktextbuffer.h"
 #include "gtkselectionprivate.h"
 #include "gtkquartz.h"
-
+#include "../gdk/quartz/gdkquartz.h"
 
 enum {
   OWNER_CHANGE,
   LAST_SIGNAL
 };
 
+@interface GtkClipboardOwner : NSObject {
+  GtkClipboard *clipboard;
+  @public
+  gboolean setting_same_owner;
+}
+
+@end
+
 typedef struct _GtkClipboardClass GtkClipboardClass;
 
-struct _GtkClipboard 
+struct _GtkClipboard
 {
   GObject parent_instance;
 
   NSPasteboard *pasteboard;
+  GtkClipboardOwner *owner;
+  NSInteger change_count;
 
   GdkAtom selection;
 
@@ -76,16 +86,15 @@ struct _GtkClipboardClass
                        GdkEventOwnerChange *event);
 };
 
-@interface GtkClipboardOwner : NSObject {
-  GtkClipboard *clipboard;
-
-  GtkClipboardGetFunc get_func;
-  GtkClipboardClearFunc clear_func;
-  gpointer user_data;
-  
-}
+static void gtk_clipboard_class_init   (GtkClipboardClass   *class);
+static void gtk_clipboard_finalize     (GObject             *object);
+static void gtk_clipboard_owner_change (GtkClipboard        *clipboard,
+                                       GdkEventOwnerChange *event);
 
-@end
+static void          clipboard_unset      (GtkClipboard     *clipboard);
+static GtkClipboard *clipboard_peek       (GdkDisplay       *display,
+                                          GdkAtom           selection,
+                                          gboolean          only_if_exists);
 
 @implementation GtkClipboardOwner
 -(void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
@@ -117,12 +126,16 @@ struct _GtkClipboardClass
     }
 }
 
+/*  pasteboardChangedOwner is not called immediately, and it's not called
+ *  reliably. It is somehow documented in the apple api docs, but the docs
+ *  suck and don't really give clear instructions. Therefore we track
+ *  changeCount in several places below and clear the clipboard if it
+ *  changed.
+ */
 - (void)pasteboardChangedOwner:(NSPasteboard *)sender
 {
-  if (clear_func)
-    clear_func (clipboard, user_data);
-
-  [self release];
+  if (! setting_same_owner)
+    clipboard_unset (clipboard);
 }
 
 - (id)initWithClipboard:(GtkClipboard *)aClipboard
@@ -132,6 +145,7 @@ struct _GtkClipboardClass
   if (self) 
     {
       clipboard = aClipboard;
+      setting_same_owner = FALSE;
     }
 
   return self;
@@ -139,15 +153,6 @@ struct _GtkClipboardClass
 
 @end
 
-static void gtk_clipboard_class_init   (GtkClipboardClass   *class);
-static void gtk_clipboard_finalize     (GObject             *object);
-static void gtk_clipboard_owner_change (GtkClipboard        *clipboard,
-                                       GdkEventOwnerChange *event);
-
-static void          clipboard_unset      (GtkClipboard     *clipboard);
-static GtkClipboard *clipboard_peek       (GdkDisplay       *display,
-                                          GdkAtom           selection,
-                                          gboolean          only_if_exists);
 
 static const gchar clipboards_owned_key[] = "gtk-clipboards-owned";
 static GQuark clipboards_owned_key_id = 0;
@@ -247,11 +252,6 @@ clipboard_display_closed (GdkDisplay   *display,
   g_object_unref (clipboard);
 }
 
-/**
- * gtk_clipboard_get_for_display:
- * @display: the display for which the clipboard is to be retrieved or created
- * @selection: a #GdkAtom which identifies the clipboard to use.
- */
 GtkClipboard *
 gtk_clipboard_get_for_display (GdkDisplay *display,
                               GdkAtom     selection)
@@ -262,10 +262,6 @@ gtk_clipboard_get_for_display (GdkDisplay *display,
   return clipboard_peek (display, selection, FALSE);
 }
 
-/**
- * gtk_clipboard_get:
- * @selection: a #GdkAtom which identifies the clipboard to use
- */
 GtkClipboard *
 gtk_clipboard_get (GdkAtom selection)
 {
@@ -340,10 +336,6 @@ gtk_clipboard_set_contents (GtkClipboard         *clipboard,
   NSSet *types;
   NSAutoreleasePool *pool;
 
-  pool = [[NSAutoreleasePool alloc] init];
-
-  owner = [[GtkClipboardOwner alloc] initWithClipboard:clipboard];
-
   if (!(clipboard->have_owner && have_owner) ||
       clipboard->user_data != user_data)
     {
@@ -358,17 +350,44 @@ gtk_clipboard_set_contents (GtkClipboard         *clipboard,
               clipboard->user_data != user_data)
             {
               (*clear_func) (clipboard, user_data);
-              [pool release];
               return FALSE;
             }
           else
             {
-              [pool release];
               return TRUE;
             }
         }
     }
 
+  pool = [[NSAutoreleasePool alloc] init];
+
+  types = _gtk_quartz_target_entries_to_pasteboard_types (targets, n_targets);
+
+  /*  call declareTypes before setting the clipboard members because
+   *  declareTypes might clear the clipboard
+   */
+  if (user_data && user_data == clipboard->user_data)
+    {
+      owner = [clipboard->owner retain];
+
+      owner->setting_same_owner = TRUE;
+      clipboard->change_count = [clipboard->pasteboard declareTypes: [types allObjects]
+                                                              owner: owner];
+      owner->setting_same_owner = FALSE;
+    }
+  else
+    {
+      owner = [[GtkClipboardOwner alloc] initWithClipboard:clipboard];
+
+      clipboard->change_count = [clipboard->pasteboard declareTypes: [types allObjects]
+                                                              owner: owner];
+    }
+
+  [owner release];
+  [types release];
+  [pool release];
+
+  clipboard->owner = owner;
   clipboard->user_data = user_data;
   clipboard->have_owner = have_owner;
   if (have_owner)
@@ -380,27 +399,9 @@ gtk_clipboard_set_contents (GtkClipboard         *clipboard,
     gtk_target_list_unref (clipboard->target_list);
   clipboard->target_list = gtk_target_list_new (targets, n_targets);
 
-  types = _gtk_quartz_target_entries_to_pasteboard_types (targets, n_targets);
-
-  [clipboard->pasteboard declareTypes:[types allObjects] owner:owner];
-  [types release];
-  [pool release];
-
   return TRUE;
 }
 
-/**
- * gtk_clipboard_set_with_data: (skip)
- * @clipboard: a #GtkClipboard
- * @targets: (array length=n_targets): array containing information
- *     about the available forms for the clipboard data
- * @n_targets: number of elements in @targets
- * @get_func: (scope async): function to call to get the actual clipboard data
- * @clear_func: (scope async): when the clipboard contents are set again,
- *     this function will be called, and @get_func will not be subsequently
- *     called.
- * @user_data: user data to pass to @get_func and @clear_func.
- */
 gboolean
 gtk_clipboard_set_with_data (GtkClipboard          *clipboard,
                             const GtkTargetEntry  *targets,
@@ -418,19 +419,6 @@ gtk_clipboard_set_with_data (GtkClipboard          *clipboard,
                                     FALSE);
 }
 
-/**
- * gtk_clipboard_set_with_owner: (skip)
- * @clipboard: a #GtkClipboard
- * @targets: (array length=n_targets): array containing information
- *     about the available forms for the clipboard data
- * @n_targets: number of elements in @targets
- * @get_func: (scope async): function to call to get the actual clipboard data
- * @clear_func: (scope async): when the clipboard contents are set again,
- *     this function will be called, and @get_func will not be subsequently
- *     called
- * @owner: an object that "owns" the data. This object will be passed
- *     to the callbacks when called
- */
 gboolean
 gtk_clipboard_set_with_owner (GtkClipboard          *clipboard,
                              const GtkTargetEntry  *targets,
@@ -449,15 +437,17 @@ gtk_clipboard_set_with_owner (GtkClipboard          *clipboard,
                                     TRUE);
 }
 
-/**
- * gtk_clipboard_get_owner:
- * @clipboard: a #GtkClipboard
- */
 GObject *
 gtk_clipboard_get_owner (GtkClipboard *clipboard)
 {
   g_return_val_if_fail (clipboard != NULL, NULL);
 
+  if (clipboard->change_count < [clipboard->pasteboard changeCount])
+    {
+      clipboard_unset (clipboard);
+      clipboard->change_count = [clipboard->pasteboard changeCount];
+    }
+
   if (clipboard->have_owner)
     return clipboard->user_data;
   else
@@ -486,7 +476,8 @@ clipboard_unset (GtkClipboard *clipboard)
   clipboard->n_storable_targets = -1;
   g_free (clipboard->storable_targets);
   clipboard->storable_targets = NULL;
-      
+
+  clipboard->owner = NULL;
   clipboard->get_func = NULL;
   clipboard->clear_func = NULL;
   clipboard->user_data = NULL;
@@ -508,13 +499,11 @@ clipboard_unset (GtkClipboard *clipboard)
     g_object_unref (old_data);
 }
 
-/**
- * gtk_clipboard_clear:
- * @clipboard:  a #GtkClipboard
- */
 void
 gtk_clipboard_clear (GtkClipboard *clipboard)
 {
+  clipboard_unset (clipboard);
+
   [clipboard->pasteboard declareTypes:nil owner:nil];
 }
 
@@ -534,13 +523,6 @@ text_clear_func (GtkClipboard *clipboard,
   g_free (data);
 }
 
-/**
- * gtk_clipboard_set_text:
- * @clipboard: a #GtkClipboard object
- * @text:      a UTF-8 string.
- * @len:       length of @text, in bytes, or -1, in which case
- *             the length will be determined with <function>strlen()</function>.
- */
 void 
 gtk_clipboard_set_text (GtkClipboard *clipboard,
                        const gchar  *text,
@@ -578,11 +560,6 @@ pixbuf_clear_func (GtkClipboard *clipboard,
   g_object_unref (data);
 }
 
-/**
- * gtk_clipboard_set_image:
- * @clipboard: a #GtkClipboard object
- * @pixbuf:    a #GdkPixbuf 
- */
 void
 gtk_clipboard_set_image (GtkClipboard *clipboard,
                         GdkPixbuf    *pixbuf)
@@ -618,16 +595,6 @@ gtk_clipboard_set_image (GtkClipboard *clipboard,
   gtk_target_list_unref (list);
 }
 
-/**
- * gtk_clipboard_request_contents:
- * @clipboard: a #GtkClipboard
- * @target: an atom representing the form into which the clipboard
- *     owner should convert the selection.
- * @callback: (scope async): A function to call when the results are received
- *     (or the retrieval fails). If the retrieval fails the length field of
- *     @selection_data will be negative.
- * @user_data: user data to pass to @callback
- */
 void 
 gtk_clipboard_request_contents (GtkClipboard            *clipboard,
                                GdkAtom                  target,
@@ -643,13 +610,6 @@ gtk_clipboard_request_contents (GtkClipboard            *clipboard,
   gtk_selection_data_free (data);
 }
 
-/**
- * gtk_clipboard_request_text:
- * @clipboard: a #GtkClipboard
- * @callback: (scope async): a function to call when the text is received,
- *     or the retrieval fails. (It will always be called one way or the other.)
- * @user_data: user data to pass to @callback.
- */
 void 
 gtk_clipboard_request_text (GtkClipboard                *clipboard,
                            GtkClipboardTextReceivedFunc callback,
@@ -662,14 +622,6 @@ gtk_clipboard_request_text (GtkClipboard                *clipboard,
   g_free (data);
 }
 
-/**
- * gtk_clipboard_request_rich_text:
- * @clipboard: a #GtkClipboard
- * @buffer: a #GtkTextBuffer
- * @callback: (scope async): a function to call when the text is received,
- *     or the retrieval fails. (It will always be called one way or the other.)
- * @user_data: user data to pass to @callback.
- */
 void
 gtk_clipboard_request_rich_text (GtkClipboard                    *clipboard,
                                  GtkTextBuffer                   *buffer,
@@ -690,13 +642,6 @@ gtk_clipboard_wait_for_rich_text (GtkClipboard  *clipboard,
   return NULL;
 }
 
-/**
- * gtk_clipboard_request_image:
- * @clipboard: a #GtkClipboard
- * @callback: (scope async): a function to call when the image is received,
- *     or the retrieval fails. (It will always be called one way or the other.)
- * @user_data: user data to pass to @callback.
- */
 void 
 gtk_clipboard_request_image (GtkClipboard                  *clipboard,
                             GtkClipboardImageReceivedFunc  callback,
@@ -710,13 +655,6 @@ gtk_clipboard_request_image (GtkClipboard                  *clipboard,
     g_object_unref (pixbuf);
 }
 
-/**
- * gtk_clipboard_request_uris:
- * @clipboard: a #GtkClipboard
- * @callback: (scope async): a function to call when the URIs are received,
- *     or the retrieval fails. (It will always be called one way or the other.)
- * @user_data: user data to pass to @callback.
- */
 void 
 gtk_clipboard_request_uris (GtkClipboard                *clipboard,
                            GtkClipboardURIReceivedFunc  callback,
@@ -729,14 +667,6 @@ gtk_clipboard_request_uris (GtkClipboard                *clipboard,
   g_strfreev (uris);
 }
 
-/**
- * gtk_clipboard_request_targets:
- * @clipboard: a #GtkClipboard
- * @callback: (scope async): a function to call when the targets are
- *     received, or the retrieval fails. (It will always be called
- *     one way or the other.)
- * @user_data: user data to pass to @callback.
- */
 void 
 gtk_clipboard_request_targets (GtkClipboard                *clipboard,
                               GtkClipboardTargetsReceivedFunc callback,
@@ -750,13 +680,6 @@ gtk_clipboard_request_targets (GtkClipboard                *clipboard,
   callback (clipboard, targets, n_targets, user_data);
 }
 
-
-/**
- * gtk_clipboard_wait_for_contents:
- * @clipboard: a #GtkClipboard
- * @target: an atom representing the form into which the clipboard
- *          owner should convert the selection.
- */
 GtkSelectionData *
 gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
                                 GdkAtom       target)
@@ -764,6 +687,12 @@ gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   GtkSelectionData *selection_data = NULL;
 
+  if (clipboard->change_count < [clipboard->pasteboard changeCount])
+    {
+      clipboard_unset (clipboard);
+      clipboard->change_count = [clipboard->pasteboard changeCount];
+    }
+
   if (target == gdk_atom_intern_static_string ("TARGETS")) 
     {
       NSArray *types = [clipboard->pasteboard types];
@@ -802,10 +731,6 @@ gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
   return selection_data;
 }
 
-/**
- * gtk_clipboard_wait_for_text:
- * @clipboard: a #GtkClipboard
- */
 gchar *
 gtk_clipboard_wait_for_text (GtkClipboard *clipboard)
 {
@@ -822,10 +747,6 @@ gtk_clipboard_wait_for_text (GtkClipboard *clipboard)
   return result;
 }
 
-/**
- * gtk_clipboard_wait_for_image:
- * @clipboard: a #GtkClipboard
- */
 GdkPixbuf *
 gtk_clipboard_wait_for_image (GtkClipboard *clipboard)
 {
@@ -850,10 +771,6 @@ gtk_clipboard_wait_for_image (GtkClipboard *clipboard)
   return NULL;
 }
 
-/**
- * gtk_clipboard_wait_for_uris:
- * @clipboard: a #GtkClipboard
- */
 gchar **
 gtk_clipboard_wait_for_uris (GtkClipboard *clipboard)
 {
@@ -873,10 +790,6 @@ gtk_clipboard_wait_for_uris (GtkClipboard *clipboard)
   return NULL;
 }
 
-/**
- * gtk_clipboard_get_display:
- * @clipboard: a #GtkClipboard
- */
 GdkDisplay *
 gtk_clipboard_get_display (GtkClipboard *clipboard)
 {
@@ -885,10 +798,6 @@ gtk_clipboard_get_display (GtkClipboard *clipboard)
   return clipboard->display;
 }
 
-/**
- * gtk_clipboard_wait_is_text_available:
- * @clipboard: a #GtkClipboard
- */
 gboolean
 gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard)
 {
@@ -905,11 +814,6 @@ gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard)
   return result;
 }
 
-/**
- * gtk_clipboard_wait_is_rich_text_available:
- * @clipboard: a #GtkClipboard
- * @buffer: a #GtkTextBuffer
- */
 gboolean
 gtk_clipboard_wait_is_rich_text_available (GtkClipboard  *clipboard,
                                            GtkTextBuffer *buffer)
@@ -930,10 +834,6 @@ gtk_clipboard_wait_is_rich_text_available (GtkClipboard  *clipboard,
   return result;
 }
 
-/**
- * gtk_clipboard_wait_is_image_available:
- * @clipboard: a #GtkClipboard
- */
 gboolean
 gtk_clipboard_wait_is_image_available (GtkClipboard *clipboard)
 {
@@ -951,10 +851,6 @@ gtk_clipboard_wait_is_image_available (GtkClipboard *clipboard)
   return result;
 }
 
-/**
- * gtk_clipboard_wait_is_uris_available:
- * @clipboard: a #GtkClipboard
- */
 gboolean
 gtk_clipboard_wait_is_uris_available (GtkClipboard *clipboard)
 {
@@ -972,14 +868,6 @@ gtk_clipboard_wait_is_uris_available (GtkClipboard *clipboard)
   return result;
 }
 
-/**
- * gtk_clipboard_wait_for_targets:
- * @clipboard: a #GtkClipboard
- * @targets: (out) (array length=n_targets) (transfer container): location
- *           to store an array of targets. The result stored here must
- *           be freed with g_free().
- * @n_targets: location to store number of items in @targets.
- */
 gboolean
 gtk_clipboard_wait_for_targets (GtkClipboard  *clipboard, 
                                GdkAtom      **targets,
@@ -1110,11 +998,6 @@ gtk_clipboard_owner_change (GtkClipboard        *clipboard,
     }
 }
 
-/**
- * gtk_clipboard_wait_is_target_available:
- * @clipboard: a #GtkClipboard
- * @target:    A #GdkAtom indicating which target to look for.
- */
 gboolean
 gtk_clipboard_wait_is_target_available (GtkClipboard *clipboard,
                                        GdkAtom       target)
@@ -1140,23 +1023,11 @@ gtk_clipboard_wait_is_target_available (GtkClipboard *clipboard,
   return retval;
 }
 
-/**
- * _gtk_clipboard_handle_event:
- * @event: a owner change event
- */
 void 
 _gtk_clipboard_handle_event (GdkEventOwnerChange *event)
 {
 }
 
-/**
- * gtk_clipboard_set_can_store:
- * @clipboard: a #GtkClipboard
- * @targets: (allow-none) (array length=n_targets): array containing
- *           information about which forms should be stored or %NULL
- *           to indicate that all forms should be stored.
- * @n_targets: number of elements in @targets
- */
 void
 gtk_clipboard_set_can_store (GtkClipboard         *clipboard,
                             const GtkTargetEntry *targets,
@@ -1165,18 +1036,77 @@ gtk_clipboard_set_can_store (GtkClipboard         *clipboard,
   /* FIXME: Implement */
 }
 
-/**
- * gtk_clipboard_store:
- * @clipboard: a #GtkClipboard
- */
 void
 gtk_clipboard_store (GtkClipboard *clipboard)
 {
-  /* FIXME: Implement */
+  int i;
+  int n_targets = 0;
+  GtkTargetEntry *targets;
+
+  g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
+
+  if (!clipboard->target_list || !clipboard->get_func)
+    return;
+
+  /* We simply store all targets into the OS X clipboard. We should be
+   * using the functions gdk_display_supports_clipboard_persistence() and
+   * gdk_display_store_clipboard(), but since for OS X the clipboard support
+   * was implemented in GTK+ and not through GdkSelections, we do it this
+   * way. Doing this properly could be worthwhile to implement in the future.
+   */
+
+  targets = gtk_target_table_new_from_list (clipboard->target_list,
+                                            &n_targets);
+  for (i = 0; i < n_targets; i++)
+    {
+      GtkSelectionData selection_data;
+
+      /* in each loop iteration, check if the content is still
+       * there, because calling get_func() can do anything to
+       * the clipboard
+       */
+      if (!clipboard->target_list || !clipboard->get_func)
+        break;
+
+      memset (&selection_data, 0, sizeof (GtkSelectionData));
+
+      selection_data.selection = clipboard->selection;
+      selection_data.target = gdk_atom_intern_static_string (targets[i].target);
+      selection_data.display = gdk_display_get_default ();
+      selection_data.length = -1;
+
+      clipboard->get_func (clipboard, &selection_data,
+                           targets[i].info, clipboard->user_data);
+
+      if (selection_data.length >= 0)
+        _gtk_quartz_set_selection_data_for_pasteboard (clipboard->pasteboard,
+                                                       &selection_data);
+
+      g_free (selection_data.data);
+    }
+
+  if (targets)
+    gtk_target_table_free (targets, n_targets);
 }
 
 void
 _gtk_clipboard_store_all (void)
 {
-  /* FIXME: Implement */
+  GtkClipboard *clipboard;
+  GSList *displays, *list;
+
+  displays = gdk_display_manager_list_displays (gdk_display_manager_get ());
+
+  list = displays;
+  while (list)
+    {
+      GdkDisplay *display = list->data;
+
+      clipboard = clipboard_peek (display, GDK_SELECTION_CLIPBOARD, TRUE);
+
+      if (clipboard)
+        gtk_clipboard_store (clipboard);
+
+      list = list->next;
+    }
 }