diff --git a/ChangeLog b/ChangeLog index c49a55743c..356f5e1247 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2006-03-21 Anders Carlsson + + * gtk/Makefile.am: + Add new files. + + * gtk/gtkclipboard-quartz.c: Added. + * gtk/gtkdnd-quartz.c: Added. + * gtk/gtkquartz.c: Added. + * gtk/gtkquartz.h: Added. + Add dnd and clipboard implementations. + 2006-03-21 Michael Natterer * gdk/quartz/Makefile.am: removing trailing \ fixes the build. diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index c49a55743c..356f5e1247 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,14 @@ +2006-03-21 Anders Carlsson + + * gtk/Makefile.am: + Add new files. + + * gtk/gtkclipboard-quartz.c: Added. + * gtk/gtkdnd-quartz.c: Added. + * gtk/gtkquartz.c: Added. + * gtk/gtkquartz.h: Added. + Add dnd and clipboard implementations. + 2006-03-21 Michael Natterer * gdk/quartz/Makefile.am: removing trailing \ fixes the build. diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 84d41c83eb..6a128c8141 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -142,7 +142,6 @@ gtk_public_h_sources = \ gtkcheckmenuitem.h \ gtkclipboard.h \ gtkclist.h \ - gtkclipboard.h \ gtkcolorbutton.h \ gtkcolorsel.h \ gtkcolorseldialog.h \ @@ -358,7 +357,6 @@ gtk_c_sources = \ gtkcellview.c \ gtkcheckbutton.c \ gtkcheckmenuitem.c \ - gtkclipboard.c \ gtkclist.c \ gtkcolorbutton.c \ gtkcolorsel.c \ @@ -370,7 +368,6 @@ gtk_c_sources = \ gtkctree.c \ gtkcurve.c \ gtkdialog.c \ - gtkdnd.c \ gtkdrawingarea.c \ gtkeditable.c \ gtkentry.c \ @@ -533,7 +530,8 @@ gtk_c_sources = \ gtkwidget.c \ gtkwindow-decorate.c \ gtkwindow.c \ - xembed.h + xembed.h \ + $(gtk_clipboard_dnd_c_sources) if OS_UNIX gtk_private_h_sources += gtkfilesystemunix.h @@ -561,6 +559,13 @@ gtk_c_sources += gtkplug-stub.c gtksocket-stub.c endif endif +if USE_QUARTZ +gtk_clipboard_dnd_c_sources = gtkclipboard-quartz.c gtkdnd-quartz.c gtkquartz.c gtkquartz.h +gtk_clipboard_dnd_c_sources_CFLAGS = "-xobjective-c" +else +gtk_clipboard_dnd_c_sources = gtkclipboard.c gtkdnd.c +endif + # we use our own built_sources variable rules to avoid automake's # BUILT_SOURCES oddities # we generate frequently rebuild files piggyback on a stamp file, so sources diff --git a/gtk/gtkclipboard-quartz.c b/gtk/gtkclipboard-quartz.c new file mode 100644 index 0000000000..9a79c25947 --- /dev/null +++ b/gtk/gtkclipboard-quartz.c @@ -0,0 +1,1264 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2000 Red Hat, Inc. + * Copyright (C) 2004 Nokia Corporation + * Copyright (C) 2006 Imendio AB + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#import + +#include "gtkclipboard.h" +#include "gtkinvisible.h" +#include "gtkmain.h" +#include "gtkmarshalers.h" +#include "gtkintl.h" +#include "gtkalias.h" +#include "gtktextbuffer.h" + +#include "gtkquartz.h" + +enum { + OWNER_CHANGE, + LAST_SIGNAL +}; + +typedef struct _GtkClipboardClass GtkClipboardClass; + +struct _GtkClipboard +{ + GObject parent_instance; + + NSPasteboard *pasteboard; + + GdkAtom selection; + + GtkClipboardGetFunc get_func; + GtkClipboardClearFunc clear_func; + gpointer user_data; + gboolean have_owner; + + gboolean have_selection; + GdkDisplay *display; + + GdkAtom *cached_targets; + gint n_cached_targets; + + guint notify_signal_id; + gboolean storing_selection; + GMainLoop *store_loop; + guint store_timeout; + gint n_storable_targets; + GdkAtom *storable_targets; +}; + +struct _GtkClipboardClass +{ + GObjectClass parent_class; + + void (*owner_change) (GtkClipboard *clipboard, + GdkEventOwnerChange *event); +}; + +@interface GtkClipboardOwner : NSObject { + GtkClipboard *clipboard; + + GtkClipboardGetFunc get_func; + GtkClipboardClearFunc clear_func; + gpointer user_data; + +} + +@end + +@implementation GtkClipboardOwner +-(void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type +{ + GtkSelectionData selection_data; + + selection_data.selection = clipboard->selection; + selection_data.data = NULL; + selection_data.target = _gtk_quartz_pasteboard_type_to_atom (type); + + /* FIXME: We need to find out what the info argument should be + * here by storing it in the clipboard object in + * gtk_clipboard_set_contents + */ + clipboard->get_func (clipboard, &selection_data, 0, clipboard->user_data); + + _gtk_quartz_set_selection_data_for_pasteboard (clipboard->pasteboard, &selection_data); + + g_free (selection_data.data); + + NSLog(@"Provide data for %@", type); +} + +- (void)pasteboardChangedOwner:(NSPasteboard *)sender +{ + if (clear_func) + clear_func (clipboard, user_data); + + [self release]; +} + +- (id)initWithClipboard:(GtkClipboard *)aClipboard +{ + self = [super init]; + + if (self) + { + clipboard = aClipboard; + } + + return self; +} + +@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; + +static GObjectClass *parent_class; +static guint clipboard_signals[LAST_SIGNAL] = { 0 }; + +GType +gtk_clipboard_get_type (void) +{ + static GType clipboard_type = 0; + + if (!clipboard_type) + { + static const GTypeInfo clipboard_info = + { + sizeof (GtkClipboardClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_clipboard_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkClipboard), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + clipboard_type = g_type_register_static (G_TYPE_OBJECT, I_("GtkClipboard"), + &clipboard_info, 0); + } + + return clipboard_type; +} + +static void +gtk_clipboard_class_init (GtkClipboardClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + gobject_class->finalize = gtk_clipboard_finalize; + + class->owner_change = gtk_clipboard_owner_change; + + clipboard_signals[OWNER_CHANGE] = + g_signal_new (I_("owner_change"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkClipboardClass, owner_change), + NULL, NULL, + _gtk_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); +} + +static void +gtk_clipboard_finalize (GObject *object) +{ + GtkClipboard *clipboard; + GSList *clipboards; + + clipboard = GTK_CLIPBOARD (object); + + clipboards = g_object_get_data (G_OBJECT (clipboard->display), "gtk-clipboard-list"); + if (g_slist_index (clipboards, clipboard) >= 0) + g_warning ("GtkClipboard prematurely finalized"); + + clipboard_unset (clipboard); + + clipboards = g_object_get_data (G_OBJECT (clipboard->display), "gtk-clipboard-list"); + clipboards = g_slist_remove (clipboards, clipboard); + g_object_set_data (G_OBJECT (clipboard->display), I_("gtk-clipboard-list"), clipboards); + + if (clipboard->store_loop && g_main_loop_is_running (clipboard->store_loop)) + g_main_loop_quit (clipboard->store_loop); + + if (clipboard->store_timeout != 0) + g_source_remove (clipboard->store_timeout); + + g_free (clipboard->storable_targets); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +clipboard_display_closed (GdkDisplay *display, + gboolean is_error, + GtkClipboard *clipboard) +{ + GSList *clipboards; + + clipboards = g_object_get_data (G_OBJECT (display), "gtk-clipboard-list"); + g_object_run_dispose (G_OBJECT (clipboard)); + clipboards = g_slist_remove (clipboards, clipboard); + g_object_set_data (G_OBJECT (display), I_("gtk-clipboard-list"), clipboards); + 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. + * + * Returns the clipboard object for the given selection. + * Cut/copy/paste menu items and keyboard shortcuts should use + * the default clipboard, returned by passing %GDK_SELECTION_CLIPBOARD for @selection. + * (%GDK_NONE is supported as a synonym for GDK_SELECTION_CLIPBOARD + * for backwards compatibility reasons.) + * The currently-selected object or text should be provided on the clipboard + * identified by #GDK_SELECTION_PRIMARY. Cut/copy/paste menu items + * conceptually copy the contents of the #GDK_SELECTION_PRIMARY clipboard + * to the default clipboard, i.e. they copy the selection to what the + * user sees as the clipboard. + * + * (Passing #GDK_NONE is the same as using gdk_atom_intern + * ("CLIPBOARD", FALSE). See + * http://www.freedesktop.org/Standards/clipboards-spec + * for a detailed discussion of the "CLIPBOARD" vs. "PRIMARY" + * selections under the X window system. On Win32 the + * #GDK_SELECTION_PRIMARY clipboard is essentially ignored.) + * + * It's possible to have arbitrary named clipboards; if you do invent + * new clipboards, you should prefix the selection name with an + * underscore (because the ICCCM requires that nonstandard atoms are + * underscore-prefixed), and namespace it as well. For example, + * if your application called "Foo" has a special-purpose + * clipboard, you might call it "_FOO_SPECIAL_CLIPBOARD". + * + * Return value: the appropriate clipboard object. If no + * clipboard already exists, a new one will + * be created. Once a clipboard object has + * been created, it is persistent and, since + * it is owned by GTK+, must not be freed or + * unrefd. + * + * Since: 2.2 + **/ +GtkClipboard * +gtk_clipboard_get_for_display (GdkDisplay *display, + GdkAtom selection) +{ + g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); + g_return_val_if_fail (!display->closed, NULL); + + return clipboard_peek (display, selection, FALSE); +} + + +/** + * gtk_clipboard_get(): + * @selection: a #GdkAtom which identifies the clipboard + * to use. + * + * Returns the clipboard object for the given selection. + * See gtk_clipboard_get_for_display() for complete details. + * + * Return value: the appropriate clipboard object. If no + * clipboard already exists, a new one will + * be created. Once a clipboard object has + * been created, it is persistent and, since + * it is owned by GTK+, must not be freed or + * unrefd. + **/ +GtkClipboard * +gtk_clipboard_get (GdkAtom selection) +{ + return gtk_clipboard_get_for_display (gdk_display_get_default (), selection); +} + +static void +clipboard_owner_destroyed (gpointer data) +{ + GSList *clipboards = data; + GSList *tmp_list; + + tmp_list = clipboards; + while (tmp_list) + { + GtkClipboard *clipboard = tmp_list->data; + + clipboard->get_func = NULL; + clipboard->clear_func = NULL; + clipboard->user_data = NULL; + clipboard->have_owner = FALSE; + + gtk_clipboard_clear (clipboard); + + tmp_list = tmp_list->next; + } + + g_slist_free (clipboards); +} + +static void +clipboard_add_owner_notify (GtkClipboard *clipboard) +{ + if (!clipboards_owned_key_id) + clipboards_owned_key_id = g_quark_from_static_string (clipboards_owned_key); + + if (clipboard->have_owner) + g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id, + g_slist_prepend (g_object_steal_qdata (clipboard->user_data, + clipboards_owned_key_id), + clipboard), + clipboard_owner_destroyed); +} + +static void +clipboard_remove_owner_notify (GtkClipboard *clipboard) +{ + if (clipboard->have_owner) + g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id, + g_slist_remove (g_object_steal_qdata (clipboard->user_data, + clipboards_owned_key_id), + clipboard), + clipboard_owner_destroyed); +} + +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) +{ + GtkClipboardOwner *owner; + NSArray *types; + + owner = [[GtkClipboardOwner alloc] initWithClipboard:clipboard]; + types = _gtk_quartz_target_entries_to_pasteboard_types (targets, n_targets); + + clipboard->user_data = user_data; + clipboard->have_owner = have_owner; + if (have_owner) + clipboard_add_owner_notify (clipboard); + clipboard->get_func = get_func; + clipboard->clear_func = clear_func; + + [clipboard->pasteboard declareTypes:types owner:owner]; + + return true; +} + +/** + * gtk_clipboard_set_with_data: + * @clipboard: a #GtkClipboard + * @targets: array containing information about the available forms for the + * clipboard data + * @n_targets: number of elements in @targets + * @get_func: function to call to get the actual clipboard data + * @clear_func: 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. + * + * Virtually sets the contents of the specified clipboard by providing + * a list of supported formats for the clipboard data and a function + * to call to get the actual data when it is requested. + * + * Return value: %TRUE if setting the clipboard data succeeded. If setting + * the clipboard data failed the provided callback functions + * will be ignored. + **/ +gboolean +gtk_clipboard_set_with_data (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + gpointer user_data) +{ + g_return_val_if_fail (clipboard != NULL, FALSE); + g_return_val_if_fail (targets != NULL, FALSE); + g_return_val_if_fail (get_func != NULL, FALSE); + + return gtk_clipboard_set_contents (clipboard, targets, n_targets, + get_func, clear_func, user_data, + FALSE); +} + +/** + * gtk_clipboard_set_with_owner: + * @clipboard: a #GtkClipboard + * @targets: array containing information about the available forms for the + * clipboard data + * @n_targets: number of elements in @targets + * @get_func: function to call to get the actual clipboard data + * @clear_func: 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. + * + * Virtually sets the contents of the specified clipboard by providing + * a list of supported formats for the clipboard data and a function + * to call to get the actual data when it is requested. + * + * The difference between this function and gtk_clipboard_set_with_data() + * is that instead of an generic @user_data pointer, a #GObject is passed + * in. + * + * Return value: %TRUE if setting the clipboard data succeeded. If setting + * the clipboard data failed the provided callback functions + * will be ignored. + **/ +gboolean +gtk_clipboard_set_with_owner (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + GObject *owner) +{ + g_return_val_if_fail (clipboard != NULL, FALSE); + g_return_val_if_fail (targets != NULL, FALSE); + g_return_val_if_fail (get_func != NULL, FALSE); + g_return_val_if_fail (G_IS_OBJECT (owner), FALSE); + + return gtk_clipboard_set_contents (clipboard, targets, n_targets, + get_func, clear_func, owner, + TRUE); +} + +/** + * gtk_clipboard_get_owner: + * @clipboard: a #GtkClipboard + * + * If the clipboard contents callbacks were set with + * gtk_clipboard_set_with_owner(), and the gtk_clipboard_set_with_data() or + * gtk_clipboard_clear() has not subsequently called, returns the owner set + * by gtk_clipboard_set_with_owner(). + * + * Return value: the owner of the clipboard, if any; otherwise %NULL. + **/ +GObject * +gtk_clipboard_get_owner (GtkClipboard *clipboard) +{ + g_return_val_if_fail (clipboard != NULL, NULL); + + if (clipboard->have_owner) + return clipboard->user_data; + else + return NULL; +} + +static void +clipboard_unset (GtkClipboard *clipboard) +{ + GtkClipboardClearFunc old_clear_func; + gpointer old_data; + gboolean old_have_owner; + gint old_n_storable_targets; + + old_clear_func = clipboard->clear_func; + old_data = clipboard->user_data; + old_have_owner = clipboard->have_owner; + old_n_storable_targets = clipboard->n_storable_targets; + + if (old_have_owner) + { + clipboard->have_owner = FALSE; + } + + clipboard->n_storable_targets = -1; + g_free (clipboard->storable_targets); + clipboard->storable_targets = NULL; + + clipboard->get_func = NULL; + clipboard->clear_func = NULL; + clipboard->user_data = NULL; + + if (old_clear_func) + old_clear_func (clipboard, old_data); + + /* If we've transferred the clipboard data to the manager, + * unref the owner + */ + if (old_have_owner && + old_n_storable_targets != -1) + g_object_unref (old_data); +} + +/** + * gtk_clipboard_clear: + * @clipboard: a #GtkClipboard + * + * Clears the contents of the clipboard. Generally this should only + * be called between the time you call gtk_clipboard_set_with_owner() + * or gtk_clipboard_set_with_data(), + * and when the @clear_func you supplied is called. Otherwise, the + * clipboard may be owned by someone else. + **/ +void +gtk_clipboard_clear (GtkClipboard *clipboard) +{ + [clipboard->pasteboard declareTypes:nil owner:nil]; +} + +static void +text_get_func (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer data) +{ + gtk_selection_data_set_text (selection_data, data, -1); +} + +static void +text_clear_func (GtkClipboard *clipboard, + gpointer data) +{ + 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 strlen(). + * + * Sets the contents of the clipboard to the given UTF-8 string. GTK+ will + * make a copy of the text and take responsibility for responding + * for requests for the text, and for converting the text into + * the requested format. + **/ +void +gtk_clipboard_set_text (GtkClipboard *clipboard, + const gchar *text, + gint len) +{ + GtkTargetEntry target = { "UTF8_STRING", 0, 0 }; + + g_return_if_fail (clipboard != NULL); + g_return_if_fail (text != NULL); + + if (len < 0) + len = strlen (text); + + gtk_clipboard_set_with_data (clipboard, + &target, 1, + text_get_func, text_clear_func, + g_strndup (text, len)); + gtk_clipboard_set_can_store (clipboard, NULL, 0); +} + + +static void +pixbuf_get_func (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer data) +{ + gtk_selection_data_set_pixbuf (selection_data, data); +} + +static void +pixbuf_clear_func (GtkClipboard *clipboard, + gpointer data) +{ + g_object_unref (data); +} + +/** + * gtk_clipboard_set_image: + * @clipboard: a #GtkClipboard object + * @pixbuf: a #GdkPixbuf + * + * Sets the contents of the clipboard to the given #GdkPixbuf. + * GTK+ will take responsibility for responding for requests + * for the image, and for converting the image into the + * requested format. + * + * Since: 2.6 + **/ +void +gtk_clipboard_set_image (GtkClipboard *clipboard, + GdkPixbuf *pixbuf) +{ + GtkTargetList *list; + GList *l; + GtkTargetEntry *targets; + gint n_targets, i; + + g_return_if_fail (clipboard != NULL); + g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); + + list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_image_targets (list, 0, TRUE); + + n_targets = g_list_length (list->list); + targets = g_new0 (GtkTargetEntry, n_targets); + for (l = list->list, i = 0; l; l = l->next, i++) + { + GtkTargetPair *pair = (GtkTargetPair *)l->data; + targets[i].target = gdk_atom_name (pair->target); + } + + gtk_clipboard_set_with_data (clipboard, + targets, n_targets, + pixbuf_get_func, pixbuf_clear_func, + g_object_ref (pixbuf)); + gtk_clipboard_set_can_store (clipboard, NULL, 0); + + for (i = 0; i < n_targets; i++) + g_free (targets[i].target); + g_free (targets); + 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: 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 + * + * Requests the contents of clipboard as the given target. + * When the results of the result are later received the supplied callback + * will be called. + **/ +void +gtk_clipboard_request_contents (GtkClipboard *clipboard, + GdkAtom target, + GtkClipboardReceivedFunc callback, + gpointer user_data) +{ + GtkSelectionData *data; + + data = gtk_clipboard_wait_for_contents (clipboard, target); + + callback (clipboard, data, user_data); + + gtk_selection_data_free (data); +} + +/** + * gtk_clipboard_request_text: + * @clipboard: a #GtkClipboard + * @callback: 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. + * + * Requests the contents of the clipboard as text. When the text is + * later received, it will be converted to UTF-8 if necessary, and + * @callback will be called. + * + * The @text parameter to @callback will contain the resulting text if + * the request succeeded, or %NULL if it failed. This could happen for + * various reasons, in particular if the clipboard was empty or if the + * contents of the clipboard could not be converted into text form. + **/ +void +gtk_clipboard_request_text (GtkClipboard *clipboard, + GtkClipboardTextReceivedFunc callback, + gpointer user_data) +{ + gchar *data = gtk_clipboard_wait_for_text (clipboard); + + callback (clipboard, data, user_data); + + g_free (data); +} + +void +gtk_clipboard_request_rich_text (GtkClipboard *clipboard, + GtkTextBuffer *buffer, + GtkClipboardRichTextReceivedFunc callback, + gpointer user_data) +{ + /* FIXME: Implement */ +} + +/** + * gtk_clipboard_request_image: + * @clipboard: a #GtkClipboard + * @callback: 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. + * + * Requests the contents of the clipboard as image. When the image is + * later received, it will be converted to a #GdkPixbuf, and + * @callback will be called. + * + * The @pixbuf parameter to @callback will contain the resulting + * #GdkPixbuf if the request succeeded, or %NULL if it failed. This + * could happen for various reasons, in particular if the clipboard + * was empty or if the contents of the clipboard could not be + * converted into an image. + * + * Since: 2.6 + **/ +void +gtk_clipboard_request_image (GtkClipboard *clipboard, + GtkClipboardImageReceivedFunc callback, + gpointer user_data) +{ + GdkPixbuf *pixbuf = gtk_clipboard_wait_for_image (clipboard); + + callback (clipboard, pixbuf, user_data); + + if (pixbuf) + g_object_unref (pixbuf); +} + +/** + * gtk_clipboard_request_targets: + * @clipboard: a #GtkClipboard + * @callback: 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. + * + * Requests the contents of the clipboard as list of supported targets. + * When the list is later received, @callback will be called. + * + * The @targets parameter to @callback will contain the resulting targets if + * the request succeeded, or %NULL if it failed. + * + * Since: 2.4 + **/ +void +gtk_clipboard_request_targets (GtkClipboard *clipboard, + GtkClipboardTargetsReceivedFunc callback, + gpointer user_data) +{ + GdkAtom *targets; + gint n_targets; + + gtk_clipboard_wait_for_targets (clipboard, &targets, &n_targets); + + 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. + * + * Requests the contents of the clipboard using the given target. + * This function waits for the data to be received using the main + * loop, so events, timeouts, etc, may be dispatched during the wait. + * + * Return value: a newly-allocated #GtkSelectionData object or %NULL + * if retrieving the given target failed. If non-%NULL, + * this value must be freed with gtk_selection_data_free() + * when you are finished with it. + **/ +GtkSelectionData * +gtk_clipboard_wait_for_contents (GtkClipboard *clipboard, + GdkAtom target) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + gchar *name; + NSData *data; + GtkSelectionData *selection_data = NULL; + + if (target == gdk_atom_intern_static_string ("TARGETS")) + { + NSArray *types = [clipboard->pasteboard types]; + int i, count; + GList *atom_list, *l; + GdkAtom *atoms; + + count = [types count]; + atom_list = _gtk_quartz_pasteboard_types_to_atom_list (types); + + selection_data = g_new (GtkSelectionData, 1); + selection_data->selection = clipboard->selection; + selection_data->target = target; + selection_data->type = GDK_SELECTION_TYPE_ATOM; + selection_data->format = 32; + selection_data->length = count * sizeof (GdkAtom); + + atoms = g_malloc (selection_data->length + 1); + + for (l = atom_list, i = 0; l ; l = l->next, i++) + atoms[i] = GDK_POINTER_TO_ATOM (l->data); + + selection_data->data = (guchar *)atoms; + selection_data->data[selection_data->length] = '\0'; + + [pool release]; + + g_list_free (atom_list); + return selection_data; + } + + selection_data = _gtk_quartz_get_selection_data_from_pasteboard (clipboard->pasteboard, + target, + clipboard->selection); + + [pool release]; + return selection_data; +} + +/** + * gtk_clipboard_wait_for_text: + * @clipboard: a #GtkClipboard + * + * Requests the contents of the clipboard as text and converts + * the result to UTF-8 if necessary. This function waits for + * the data to be received using the main loop, so events, + * timeouts, etc, may be dispatched during the wait. + * + * Return value: a newly-allocated UTF-8 string which must + * be freed with g_free(), or %NULL if retrieving + * the selection data failed. (This could happen + * for various reasons, in particular if the + * clipboard was empty or if the contents of the + * clipboard could not be converted into text form.) + **/ +gchar * +gtk_clipboard_wait_for_text (GtkClipboard *clipboard) +{ + GtkSelectionData *data; + gchar *result; + + data = gtk_clipboard_wait_for_contents (clipboard, + gdk_atom_intern_static_string ("UTF8_STRING")); + + result = (gchar *)gtk_selection_data_get_text (data); + + gtk_selection_data_free (data); + + return result; +} + +/** + * gtk_clipboard_wait_for_image: + * @clipboard: a #GtkClipboard + * + * Requests the contents of the clipboard as image and converts + * the result to a #GdkPixbuf. This function waits for + * the data to be received using the main loop, so events, + * timeouts, etc, may be dispatched during the wait. + * + * Return value: a newly-allocated #GdkPixbuf object which must + * be disposed with g_object_unref(), or %NULL if + * retrieving the selection data failed. (This + * could happen for various reasons, in particular + * if the clipboard was empty or if the contents of + * the clipboard could not be converted into an image.) + * + * Since: 2.6 + **/ +GdkPixbuf * +gtk_clipboard_wait_for_image (GtkClipboard *clipboard) +{ + const gchar *priority[] = { "image/png", "image/tiff", "image/jpeg", "image/gif", "image/bmp" }; + int i; + GtkSelectionData *data; + + for (i = 0; i < G_N_ELEMENTS (priority); i++) + { + data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string (priority[i])); + + if (data) + { + GdkPixbuf *pixbuf = gtk_selection_data_get_pixbuf (data); + + gtk_selection_data_free (data); + + return pixbuf; + } + } + + return NULL; +} + +/** + * gtk_clipboard_get_display: + * @clipboard: a #GtkClipboard + * + * Gets the #GdkDisplay associated with @clipboard + * + * Return value: the #GdkDisplay associated with @clipboard + * + * Since: 2.2 + **/ +GdkDisplay * +gtk_clipboard_get_display (GtkClipboard *clipboard) +{ + g_return_val_if_fail (clipboard != NULL, NULL); + + return clipboard->display; +} + +/** + * gtk_clipboard_wait_is_text_available: + * @clipboard: a #GtkClipboard + * + * Test to see if there is text available to be pasted + * This is done by requesting the TARGETS atom and checking + * if it contains any of the supported text targets. This function + * waits for the data to be received using the main loop, so events, + * timeouts, etc, may be dispatched during the wait. + * + * This function is a little faster than calling + * gtk_clipboard_wait_for_text() since it doesn't need to retrieve + * the actual text. + * + * Return value: %TRUE is there is text available, %FALSE otherwise. + **/ +gboolean +gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard) +{ + GtkSelectionData *data; + gboolean result = FALSE; + + data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS")); + if (data) + { + result = gtk_selection_data_targets_include_text (data); + gtk_selection_data_free (data); + } + + return result; +} + +gboolean +gtk_clipboard_wait_is_rich_text_available (GtkClipboard *clipboard, + GtkTextBuffer *buffer) +{ + GtkSelectionData *data; + gboolean result = FALSE; + + g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE); + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE); + + data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS")); + if (data) + { + result = gtk_selection_data_targets_include_rich_text (data, buffer); + gtk_selection_data_free (data); + } + + return result; +} + +gboolean +gtk_clipboard_wait_is_image_available (GtkClipboard *clipboard) +{ + GtkSelectionData *data; + gboolean result = FALSE; + + data = gtk_clipboard_wait_for_contents (clipboard, + gdk_atom_intern_static_string ("TARGETS")); + if (data) + { + result = gtk_selection_data_targets_include_image (data, FALSE); + gtk_selection_data_free (data); + } + + return result; +} + +/** + * gtk_clipboard_wait_for_targets + * @clipboard: a #GtkClipboard + * @targets: 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. + * + * Returns a list of targets that are present on the clipboard, or %NULL + * if there aren't any targets available. The returned list must be + * freed with g_free(). + * This function waits for the data to be received using the main + * loop, so events, timeouts, etc, may be dispatched during the wait. + * + * Return value: %TRUE if any targets are present on the clipboard, + * otherwise %FALSE. + * + * Since: 2.4 + */ +gboolean +gtk_clipboard_wait_for_targets (GtkClipboard *clipboard, + GdkAtom **targets, + gint *n_targets) +{ + GtkSelectionData *data; + gboolean result = FALSE; + + g_return_val_if_fail (clipboard != NULL, FALSE); + + /* If the display supports change notification we cache targets */ + if (gdk_display_supports_selection_notification (gtk_clipboard_get_display (clipboard)) && + clipboard->n_cached_targets != -1) + { + if (n_targets) + *n_targets = clipboard->n_cached_targets; + + if (targets) + *targets = g_memdup (clipboard->cached_targets, + clipboard->n_cached_targets * sizeof (GdkAtom)); + + return TRUE; + } + + if (n_targets) + *n_targets = 0; + + if (targets) + *targets = NULL; + + data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS")); + + if (data) + { + GdkAtom *tmp_targets; + gint tmp_n_targets; + + result = gtk_selection_data_get_targets (data, &tmp_targets, &tmp_n_targets); + + if (gdk_display_supports_selection_notification (gtk_clipboard_get_display (clipboard))) + { + clipboard->n_cached_targets = tmp_n_targets; + clipboard->cached_targets = g_memdup (tmp_targets, + tmp_n_targets * sizeof (GdkAtom)); + } + + if (n_targets) + *n_targets = tmp_n_targets; + + if (targets) + *targets = tmp_targets; + else + g_free (tmp_targets); + + gtk_selection_data_free (data); + } + + return result; +} + +static GtkClipboard * +clipboard_peek (GdkDisplay *display, + GdkAtom selection, + gboolean only_if_exists) +{ + GtkClipboard *clipboard = NULL; + GSList *clipboards; + GSList *tmp_list; + + if (selection == GDK_NONE) + selection = GDK_SELECTION_CLIPBOARD; + + clipboards = g_object_get_data (G_OBJECT (display), "gtk-clipboard-list"); + + tmp_list = clipboards; + while (tmp_list) + { + clipboard = tmp_list->data; + if (clipboard->selection == selection) + break; + + tmp_list = tmp_list->next; + } + + if (!tmp_list && !only_if_exists) + { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *pasteboard_name; + clipboard = g_object_new (GTK_TYPE_CLIPBOARD, NULL); + + if (selection == GDK_SELECTION_CLIPBOARD) + pasteboard_name = NSGeneralPboard; + else + { + char *atom_string = gdk_atom_name (selection); + + pasteboard_name = [NSString stringWithFormat:@"_GTK_%@", + [NSString stringWithUTF8String:atom_string]]; + g_free (atom_string); + } + + clipboard->pasteboard = [NSPasteboard pasteboardWithName:pasteboard_name]; + + [pool release]; + + clipboard->selection = selection; + clipboard->display = display; + clipboard->n_cached_targets = -1; + clipboard->n_storable_targets = -1; + clipboards = g_slist_prepend (clipboards, clipboard); + g_object_set_data (G_OBJECT (display), I_("gtk-clipboard-list"), clipboards); + g_signal_connect (display, "closed", + G_CALLBACK (clipboard_display_closed), clipboard); + gdk_display_request_selection_notification (display, selection); + } + + return clipboard; +} + +static void +gtk_clipboard_owner_change (GtkClipboard *clipboard, + GdkEventOwnerChange *event) +{ + if (clipboard->n_cached_targets != -1) + { + clipboard->n_cached_targets = -1; + g_free (clipboard->cached_targets); + } +} + +/** + * gtk_clipboard_wait_is_target_available: + * @clipboard: a #GtkClipboard + * @target: A #GdkAtom indicating which target to look for. + * + * Checks if a clipboard supports pasting data of a given type. This + * function can be used to determine if a "Paste" menu item should be + * insensitive or not. + * + * If you want to see if there's text available on the clipboard, use + * gtk_clipboard_wait_is_text_available () instead. + * + * Return value: %TRUE if the target is available, %FALSE otherwise. + * + * Since: 2.6 + */ +gboolean +gtk_clipboard_wait_is_target_available (GtkClipboard *clipboard, + GdkAtom target) +{ + GdkAtom *targets; + gint i, n_targets; + gboolean retval = FALSE; + + if (!gtk_clipboard_wait_for_targets (clipboard, &targets, &n_targets)) + return FALSE; + + for (i = 0; i < n_targets; i++) + { + if (targets[i] == target) + { + retval = TRUE; + break; + } + } + + g_free (targets); + + return retval; +} + +/** + * _gtk_clipboard_handle_event: + * @event: a owner change event + * + * Emits the ::owner_change signal on the appropriate @clipboard. + * + * Since: 2.6 + **/ +void +_gtk_clipboard_handle_event (GdkEventOwnerChange *event) +{ +} + + +/** + * gtk_clipboard_set_can_store: + * @clipboard: a #GtkClipboard + * @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 + * + * Hints that the clipboard data should be stored somewhere when the + * application exits or when gtk_clipboard_store () is called. + * + * This value is reset when the clipboard owner changes. + * Where the clipboard data is stored is platform dependent, + * see gdk_display_store_clipboard () for more information. + * + * Since: 2.6 + */ +void +gtk_clipboard_set_can_store (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + gint n_targets) +{ + /* FIXME: Implement */ +} + +/** + * gtk_clipboard_store: + * @clipboard: a #GtkClipboard + * + * Stores the current clipboard data somewhere so that it will stay + * around after the application has quit. + * + * Since: 2.6 + */ +void +gtk_clipboard_store (GtkClipboard *clipboard) +{ + /* FIXME: Implement */ +} + +/* Stores all clipboard selections on all displays, called from + * gtk_main_quit (). + */ +void +_gtk_clipboard_store_all (void) +{ + /* FIXME: Implement */ +} + +#define __GTK_CLIPBOARD_C__ +#include "gtkaliasdef.c" diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c new file mode 100644 index 0000000000..2000b1efe1 --- /dev/null +++ b/gtk/gtkdnd-quartz.c @@ -0,0 +1,1769 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include + +#include +#include + +#include "gdkconfig.h" + +#include "gdk/gdkkeysyms.h" + +#include "gtkdnd.h" +#include "gtkiconfactory.h" +#include "gtkicontheme.h" +#include "gtkimage.h" +#include "gtkinvisible.h" +#include "gtkmain.h" +#include "gtkplug.h" +#include "gtkstock.h" +#include "gtkwindow.h" +#include "gtkintl.h" +#include "gtkalias.h" + +#include "gtkquartz.h" +#include "gdk/quartz/gdkquartz.h" + +typedef struct _GtkDragSourceSite GtkDragSourceSite; +typedef struct _GtkDragSourceInfo GtkDragSourceInfo; +typedef struct _GtkDragDestSite GtkDragDestSite; +typedef struct _GtkDragDestInfo GtkDragDestInfo; +typedef struct _GtkDragFindData GtkDragFindData; + +static void gtk_drag_find_widget (GtkWidget *widget, + GtkDragFindData *data); +static void gtk_drag_dest_site_destroy (gpointer data); +static void gtk_drag_dest_leave (GtkWidget *widget, + GdkDragContext *context, + guint time); +static GtkDragDestInfo *gtk_drag_get_dest_info (GdkDragContext *context, + gboolean create); +static void gtk_drag_source_site_destroy (gpointer data); + +struct _GtkDragSourceSite +{ + GdkModifierType start_button_mask; + GtkTargetList *target_list; /* Targets for drag data */ + GdkDragAction actions; /* Possible actions */ + + /* Drag icon */ + GtkImageType icon_type; + union + { + GtkImagePixmapData pixmap; + GtkImagePixbufData pixbuf; + GtkImageStockData stock; + GtkImageIconNameData name; + } icon_data; + GdkBitmap *icon_mask; + + GdkColormap *colormap; /* Colormap for drag icon */ + + /* Stored button press information to detect drag beginning */ + gint state; + gint x, y; +}; + +struct _GtkDragSourceInfo +{ + GtkWidget *widget; + GtkTargetList *target_list; /* Targets for drag data */ + GdkDragAction possible_actions; /* Actions allowed by source */ + GdkDragContext *context; /* drag context */ + + gint hot_x, hot_y; /* Hot spot for drag */ + GdkPixbuf *icon_pixbuf; +}; + +struct _GtkDragDestSite +{ + GtkDestDefaults flags; + GtkTargetList *target_list; + GdkDragAction actions; + guint have_drag : 1; +}; + +struct _GtkDragDestInfo +{ + GtkWidget *widget; /* Widget in which drag is in */ + GdkDragContext *context; /* Drag context */ + guint dropped : 1; /* Set after we receive a drop */ + gint drop_x, drop_y; /* Position of drop */ +}; + +struct _GtkDragFindData +{ + gint x; + gint y; + GdkDragContext *context; + GtkDragDestInfo *info; + gboolean found; + gboolean toplevel; + gboolean (*callback) (GtkWidget *widget, GdkDragContext *context, + gint x, gint y, guint32 time); + guint32 time; +}; + + +@interface GtkDragSourceOwner : NSObject { + GtkDragSourceInfo *info; +} + +@end + +@implementation GtkDragSourceOwner +-(void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type +{ + guint target_info; + GtkSelectionData selection_data; + + selection_data.selection = GDK_NONE; + selection_data.data = NULL; + selection_data.target = _gtk_quartz_pasteboard_type_to_atom (type); + + if (gtk_target_list_find (info->target_list, + selection_data.target, + &target_info)) + { + g_signal_emit_by_name (info->widget, "drag_data_get", + info->context, + &selection_data, + target_info, + time); + + _gtk_quartz_set_selection_data_for_pasteboard (sender, &selection_data); + + g_free (selection_data.data); + } +} + +- (id)initWithInfo:(GtkDragSourceInfo *)anInfo +{ + self = [super init]; + + if (self) + { + info = anInfo; + } + + return self; +} + +@end + +void +gtk_drag_get_data (GtkWidget *widget, + GdkDragContext *context, + GdkAtom target, + guint32 time) +{ + id dragging_info = GDK_DRAG_CONTEXT_PRIVATE (context)->dragging_info; + NSPasteboard *pasteboard = [dragging_info draggingPasteboard]; + GtkSelectionData *selection_data; + GtkDragDestInfo *info; + GtkDragDestSite *site; + + info = gtk_drag_get_dest_info (context, FALSE); + site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); + + selection_data = _gtk_quartz_get_selection_data_from_pasteboard (pasteboard, + target, 0); + + if (site && site->target_list) + { + guint target_info; + + if (gtk_target_list_find (site->target_list, + selection_data->target, + &target_info)) + { + if (!(site->flags & GTK_DEST_DEFAULT_DROP) || + selection_data->length >= 0) + g_signal_emit_by_name (widget, + "drag_data_received", + context, info->drop_x, info->drop_y, + selection_data, + target_info, time); + } + } + else + { + g_signal_emit_by_name (widget, + "drag_data_received", + context, info->drop_x, info->drop_y, + selection_data, + 0, time); + } + + if (site && site->flags & GTK_DEST_DEFAULT_DROP) + { + gtk_drag_finish (context, + (selection_data->length >= 0), + (context->action == GDK_ACTION_MOVE), + time); + } +} + + +GtkWidget * +gtk_drag_get_source_widget (GdkDragContext *context) +{ + return NULL; +} + +void +gtk_drag_finish (GdkDragContext *context, + gboolean success, + gboolean del, + guint32 time) +{ +} + +static void +gtk_drag_dest_info_destroy (gpointer data) +{ + GtkDragDestInfo *info = data; + + g_free (info); +} + +static GtkDragDestInfo * +gtk_drag_get_dest_info (GdkDragContext *context, + gboolean create) +{ + GtkDragDestInfo *info; + static GQuark info_quark = 0; + if (!info_quark) + info_quark = g_quark_from_static_string ("gtk-dest-info"); + + info = g_object_get_qdata (G_OBJECT (context), info_quark); + if (!info && create) + { + info = g_new (GtkDragDestInfo, 1); + info->widget = NULL; + info->context = context; + info->dropped = FALSE; + g_object_set_qdata_full (G_OBJECT (context), info_quark, + info, gtk_drag_dest_info_destroy); + } + + return info; +} + +static GQuark dest_info_quark = 0; + +static GtkDragSourceInfo * +gtk_drag_get_source_info (GdkDragContext *context, + gboolean create) +{ + GtkDragSourceInfo *info; + if (!dest_info_quark) + dest_info_quark = g_quark_from_static_string ("gtk-source-info"); + + info = g_object_get_qdata (G_OBJECT (context), dest_info_quark); + if (!info && create) + { + info = g_new0 (GtkDragSourceInfo, 1); + info->context = context; + g_object_set_qdata (G_OBJECT (context), dest_info_quark, info); + } + + return info; +} + +static void +gtk_drag_clear_source_info (GdkDragContext *context) +{ + g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL); +} + +/************************************************************* + * gtk_drag_highlight_expose: + * Callback for expose_event for highlighted widgets. + * arguments: + * widget: + * event: + * data: + * results: + *************************************************************/ + +static gboolean +gtk_drag_highlight_expose (GtkWidget *widget, + GdkEventExpose *event, + gpointer data) +{ + gint x, y, width, height; + + if (GTK_WIDGET_DRAWABLE (widget)) + { + cairo_t *cr; + + if (GTK_WIDGET_NO_WINDOW (widget)) + { + x = widget->allocation.x; + y = widget->allocation.y; + width = widget->allocation.width; + height = widget->allocation.height; + } + else + { + x = 0; + y = 0; + gdk_drawable_get_size (widget->window, &width, &height); + } + + gtk_paint_shadow (widget->style, widget->window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, widget, "dnd", + x, y, width, height); + + cr = gdk_cairo_create (widget->window); + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ + cairo_set_line_width (cr, 1.0); + cairo_rectangle (cr, + x + 0.5, y + 0.5, + width - 1, height - 1); + cairo_stroke (cr); + cairo_destroy (cr); + } + + return FALSE; +} + +/************************************************************* + * gtk_drag_highlight: + * Highlight the given widget in the default manner. + * arguments: + * widget: + * results: + *************************************************************/ + +void +gtk_drag_highlight (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + g_signal_connect_after (widget, "expose_event", + G_CALLBACK (gtk_drag_highlight_expose), + NULL); + + gtk_widget_queue_draw (widget); +} + +/************************************************************* + * gtk_drag_unhighlight: + * Refresh the given widget to remove the highlight. + * arguments: + * widget: + * results: + *************************************************************/ + +void +gtk_drag_unhighlight (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + g_signal_handlers_disconnect_by_func (widget, + gtk_drag_highlight_expose, + NULL); + + gtk_widget_queue_draw (widget); +} + +static NSWindow * +get_toplevel_nswindow (GtkWidget *widget) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); + + if (GTK_WIDGET_TOPLEVEL (toplevel) && toplevel->window) + return [gdk_quartz_window_get_nsview (toplevel->window) window]; + else + return NULL; +} + +static void +register_types (GtkWidget *widget, GtkDragDestSite *site) +{ + if (site->target_list) + { + NSWindow *nswindow = get_toplevel_nswindow (widget); + NSArray *types; + NSAutoreleasePool *pool; + + if (!nswindow) + return; + + pool= [[NSAutoreleasePool alloc] init]; + types = _gtk_quartz_target_list_to_pasteboard_types (site->target_list); + + [nswindow registerForDraggedTypes:types]; + [pool release]; + } +} + +static void +gtk_drag_dest_realized (GtkWidget *widget, + gpointer user_data) +{ + GtkDragDestSite *site = user_data; + + register_types (widget, site); +} + +static void +gtk_drag_dest_hierarchy_changed (GtkWidget *widget, + GtkWidget *previous_toplevel, + gpointer user_data) +{ + GtkDragDestSite *site = user_data; + + register_types (widget, site); +} + +static void +gtk_drag_dest_site_destroy (gpointer data) +{ + GtkDragDestSite *site = data; + + if (site->target_list) + gtk_target_list_unref (site->target_list); + + g_free (site); +} + +void +gtk_drag_dest_set (GtkWidget *widget, + GtkDestDefaults flags, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions) +{ + GtkDragDestSite *old_site, *site; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); + if (old_site) + { + g_signal_handlers_disconnect_by_func (widget, + gtk_drag_dest_realized, + old_site); + g_signal_handlers_disconnect_by_func (widget, + gtk_drag_dest_hierarchy_changed, + old_site); + } + + site = g_new (GtkDragDestSite, 1); + site->flags = flags; + site->have_drag = FALSE; + if (targets) + site->target_list = gtk_target_list_new (targets, n_targets); + else + site->target_list = NULL; + site->actions = actions; + + if (GTK_WIDGET_REALIZED (widget)) + gtk_drag_dest_realized (widget, site); + + g_signal_connect (widget, "realize", + G_CALLBACK (gtk_drag_dest_realized), site); + g_signal_connect (widget, "hierarchy_changed", + G_CALLBACK (gtk_drag_dest_hierarchy_changed), site); + + g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"), + site, gtk_drag_dest_site_destroy); +} + +void +gtk_drag_dest_set_proxy (GtkWidget *widget, + GdkWindow *proxy_window, + GdkDragProtocol protocol, + gboolean use_coordinates) +{ + g_warning ("gtk_drag_dest_set_proxy is not supported on Mac OS X."); +} + +void +gtk_drag_dest_unset (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL); +} + +GtkTargetList* +gtk_drag_dest_get_target_list (GtkWidget *widget) +{ + GtkDragDestSite *site; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); + + return site ? site->target_list : NULL; +} + +void +gtk_drag_dest_set_target_list (GtkWidget *widget, + GtkTargetList *target_list) +{ + GtkDragDestSite *site; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); + + if (!site) + { + g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() " + "to make the widget into a drag destination"); + return; + } + + if (target_list) + gtk_target_list_ref (target_list); + + if (site->target_list) + gtk_target_list_unref (site->target_list); + + site->target_list = target_list; + + register_types (widget, site); +} + +void +gtk_drag_dest_add_text_targets (GtkWidget *widget) +{ + GtkTargetList *target_list; + + target_list = gtk_drag_dest_get_target_list (widget); + if (target_list) + gtk_target_list_ref (target_list); + else + target_list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_text_targets (target_list, 0); + gtk_drag_dest_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); +} + +void +gtk_drag_dest_add_image_targets (GtkWidget *widget) +{ + GtkTargetList *target_list; + + target_list = gtk_drag_dest_get_target_list (widget); + if (target_list) + gtk_target_list_ref (target_list); + else + target_list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_image_targets (target_list, 0, FALSE); + gtk_drag_dest_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); +} + +void +gtk_drag_dest_add_uri_targets (GtkWidget *widget) +{ + GtkTargetList *target_list; + + target_list = gtk_drag_dest_get_target_list (widget); + if (target_list) + gtk_target_list_ref (target_list); + else + target_list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_uri_targets (target_list, 0); + gtk_drag_dest_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); +} + +static void +prepend_and_ref_widget (GtkWidget *widget, + gpointer data) +{ + GSList **slist_p = data; + + *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget)); +} + +static void +gtk_drag_find_widget (GtkWidget *widget, + GtkDragFindData *data) +{ + GtkAllocation new_allocation; + gint allocation_to_window_x = 0; + gint allocation_to_window_y = 0; + gint x_offset = 0; + gint y_offset = 0; + + if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget)) + return; + + /* Note that in the following code, we only count the + * position as being inside a WINDOW widget if it is inside + * widget->window; points that are outside of widget->window + * but within the allocation are not counted. This is consistent + * with the way we highlight drag targets. + * + * data->x,y are relative to widget->parent->window (if + * widget is not a toplevel, widget->window otherwise). + * We compute the allocation of widget in the same coordinates, + * clipping to widget->window, and all intermediate + * windows. If data->x,y is inside that, then we translate + * our coordinates to be relative to widget->window and + * recurse. + */ + new_allocation = widget->allocation; + + if (widget->parent) + { + gint tx, ty; + GdkWindow *window = widget->window; + + /* Compute the offset from allocation-relative to + * window-relative coordinates. + */ + allocation_to_window_x = widget->allocation.x; + allocation_to_window_y = widget->allocation.y; + + if (!GTK_WIDGET_NO_WINDOW (widget)) + { + /* The allocation is relative to the parent window for + * window widgets, not to widget->window. + */ + gdk_window_get_position (window, &tx, &ty); + + allocation_to_window_x -= tx; + allocation_to_window_y -= ty; + } + + new_allocation.x = 0 + allocation_to_window_x; + new_allocation.y = 0 + allocation_to_window_y; + + while (window && window != widget->parent->window) + { + GdkRectangle window_rect = { 0, 0, 0, 0 }; + + gdk_drawable_get_size (window, &window_rect.width, &window_rect.height); + + gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation); + + gdk_window_get_position (window, &tx, &ty); + new_allocation.x += tx; + x_offset += tx; + new_allocation.y += ty; + y_offset += ty; + + window = gdk_window_get_parent (window); + } + + if (!window) /* Window and widget heirarchies didn't match. */ + return; + } + + if (data->toplevel || + ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) && + (data->x < new_allocation.x + new_allocation.width) && + (data->y < new_allocation.y + new_allocation.height))) + { + /* First, check if the drag is in a valid drop site in + * one of our children + */ + if (GTK_IS_CONTAINER (widget)) + { + GtkDragFindData new_data = *data; + GSList *children = NULL; + GSList *tmp_list; + + new_data.x -= x_offset; + new_data.y -= y_offset; + new_data.found = FALSE; + new_data.toplevel = FALSE; + + /* need to reference children temporarily in case the + * ::drag_motion/::drag_drop callbacks change the widget heirarchy. + */ + gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children); + for (tmp_list = children; tmp_list; tmp_list = tmp_list->next) + { + if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data)) + gtk_drag_find_widget (tmp_list->data, &new_data); + g_object_unref (tmp_list->data); + } + g_slist_free (children); + + data->found = new_data.found; + } + + /* If not, and this widget is registered as a drop site, check to + * emit "drag_motion" to check if we are actually in + * a drop site. + */ + if (!data->found && + g_object_get_data (G_OBJECT (widget), "gtk-drag-dest")) + { + data->found = data->callback (widget, + data->context, + data->x - x_offset - allocation_to_window_x, + data->y - y_offset - allocation_to_window_y, + data->time); + /* If so, send a "drag_leave" to the last widget */ + if (data->found) + { + if (data->info->widget && data->info->widget != widget) + { + gtk_drag_dest_leave (data->info->widget, data->context, data->time); + } + data->info->widget = widget; + } + } + } +} + +static void +gtk_drag_dest_leave (GtkWidget *widget, + GdkDragContext *context, + guint time) +{ + GtkDragDestSite *site; + + site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); + g_return_if_fail (site != NULL); + + if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag) + gtk_drag_unhighlight (widget); + + if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag) + g_signal_emit_by_name (widget, "drag_leave", + context, time); + + site->have_drag = FALSE; +} + +static gboolean +gtk_drag_dest_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GtkDragDestSite *site; + GdkDragAction action = 0; + gboolean retval; + + site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); + g_return_val_if_fail (site != NULL, FALSE); + + if (site->flags & GTK_DEST_DEFAULT_MOTION) + { + if (context->suggested_action & site->actions) + action = context->suggested_action; + + if (action && gtk_drag_dest_find_target (widget, context, NULL)) + { + if (!site->have_drag) + { + site->have_drag = TRUE; + if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) + gtk_drag_highlight (widget); + } + + gdk_drag_status (context, action, time); + } + else + { + gdk_drag_status (context, 0, time); + return TRUE; + } + } + + g_signal_emit_by_name (widget, "drag_motion", + context, x, y, time, &retval); + + return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval; +} + +static gboolean +gtk_drag_dest_drop (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GtkDragDestSite *site; + GtkDragDestInfo *info; + gboolean retval; + + site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"); + g_return_val_if_fail (site != NULL, FALSE); + + info = gtk_drag_get_dest_info (context, FALSE); + g_return_val_if_fail (info != NULL, FALSE); + + info->drop_x = x; + info->drop_y = y; + + if (site->flags & GTK_DEST_DEFAULT_DROP) + { + GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL); + + if (target == GDK_NONE) + { + gtk_drag_finish (context, FALSE, FALSE, time); + return TRUE; + } + else + gtk_drag_get_data (widget, context, target, time); + } + + g_signal_emit_by_name (widget, "drag_drop", + context, x, y, time, &retval); + + return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval; +} + +void +_gtk_drag_dest_handle_event (GtkWidget *toplevel, + GdkEvent *event) +{ + GtkDragDestInfo *info; + GdkDragContext *context; + + g_return_if_fail (toplevel != NULL); + g_return_if_fail (event != NULL); + + context = event->dnd.context; + + info = gtk_drag_get_dest_info (context, TRUE); + + /* Find the widget for the event */ + switch (event->type) + { + case GDK_DRAG_ENTER: + break; + + case GDK_DRAG_LEAVE: + if (info->widget) + { + gtk_drag_dest_leave (info->widget, context, event->dnd.time); + info->widget = NULL; + } + break; + + case GDK_DRAG_MOTION: + case GDK_DROP_START: + { + GtkDragFindData data; + gint tx, ty; + + if (event->type == GDK_DROP_START) + { + info->dropped = TRUE; + /* We send a leave here so that the widget unhighlights + * properly. + */ + if (info->widget) + { + gtk_drag_dest_leave (info->widget, context, event->dnd.time); + info->widget = NULL; + } + } + + gdk_window_get_position (toplevel->window, &tx, &ty); + + data.x = event->dnd.x_root - tx; + data.y = event->dnd.y_root - ty; + data.context = context; + data.info = info; + data.found = FALSE; + data.toplevel = TRUE; + data.callback = (event->type == GDK_DRAG_MOTION) ? + gtk_drag_dest_motion : gtk_drag_dest_drop; + data.time = event->dnd.time; + + gtk_drag_find_widget (toplevel, &data); + + if (info->widget && !data.found) + { + gtk_drag_dest_leave (info->widget, context, event->dnd.time); + info->widget = NULL; + } + + /* Send a reply. + */ + if (event->type == GDK_DRAG_MOTION) + { + if (!data.found) + gdk_drag_status (context, 0, event->dnd.time); + } + + break; + default: + g_assert_not_reached (); + } + } +} + + +GdkAtom +gtk_drag_dest_find_target (GtkWidget *widget, + GdkDragContext *context, + GtkTargetList *target_list) +{ + id dragging_info = GDK_DRAG_CONTEXT_PRIVATE (context)->dragging_info; + NSPasteboard *pasteboard = [dragging_info draggingPasteboard]; + GtkWidget *source_widget; + GList *tmp_target; + GList *tmp_source = NULL; + GList *source_targets; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE); + g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE); + g_return_val_if_fail (!context->is_source, GDK_NONE); + + source_widget = gtk_drag_get_source_widget (context); + + if (target_list == NULL) + target_list = gtk_drag_dest_get_target_list (widget); + + if (target_list == NULL) + return GDK_NONE; + + source_targets = _gtk_quartz_pasteboard_types_to_atom_list ([pasteboard types]); + tmp_target = target_list->list; + while (tmp_target) + { + GtkTargetPair *pair = tmp_target->data; + tmp_source = source_targets; + while (tmp_source) + { + if (tmp_source->data == GUINT_TO_POINTER (pair->target)) + { + if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) && + (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget))) + { + g_list_free (source_targets); + return pair->target; + } + else + break; + } + tmp_source = tmp_source->next; + } + tmp_target = tmp_target->next; + } + + g_list_free (source_targets); + return GDK_NONE; +} + +static GdkDragContext * +gtk_drag_begin_internal (GtkWidget *widget, + GtkDragSourceSite *site, + GtkTargetList *target_list, + GdkDragAction actions, + gint button, + GdkEvent *event) +{ + GtkDragSourceInfo *info; + GdkDragContext *context; + NSWindow *nswindow; + NSPasteboard *pasteboard; + GtkDragSourceOwner *owner; + NSEvent *nsevent; + NSPoint point; + + context = gdk_drag_begin (NULL, NULL); + context->is_source = TRUE; + + info = gtk_drag_get_source_info (context, TRUE); + + info->widget = gtk_widget_ref (widget); + info->target_list = target_list; + gtk_target_list_ref (target_list); + + info->possible_actions = actions; + + g_signal_emit_by_name (widget, "drag_begin", info->context); + + /* Ensure that we have an icon before we start the drag; the + * application may have set one in ::drag_begin, or it may + * not have set one. + */ + if (!info->icon_pixbuf) + { + if (!site || site->icon_type == GTK_IMAGE_EMPTY) + gtk_drag_set_icon_default (context); + else + switch (site->icon_type) + { + case GTK_IMAGE_PIXMAP: + gtk_drag_set_icon_pixmap (context, + site->colormap, + site->icon_data.pixmap.pixmap, + site->icon_mask, + -2, -2); + break; + case GTK_IMAGE_PIXBUF: + gtk_drag_set_icon_pixbuf (context, + site->icon_data.pixbuf.pixbuf, + -2, -2); + break; + case GTK_IMAGE_STOCK: + gtk_drag_set_icon_stock (context, + site->icon_data.stock.stock_id, + -2, -2); + break; + case GTK_IMAGE_ICON_NAME: + gtk_drag_set_icon_name (context, + site->icon_data.name.icon_name, + -2, -2); + break; + case GTK_IMAGE_EMPTY: + default: + g_assert_not_reached(); + break; + } + } + + gdk_pointer_ungrab (0); + + pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + owner = [[GtkDragSourceOwner alloc] initWithInfo:info]; + + [pasteboard declareTypes:_gtk_quartz_target_list_to_pasteboard_types (target_list) owner:owner]; + + /* Ref the context. It's unreffed when the drag has been aborted */ + g_object_ref (info->context); + + nswindow = get_toplevel_nswindow (widget); + + /* FIXME: If the event isn't a mouse event, use the global cursor position instead */ + nsevent = [nswindow currentEvent]; + point = [nsevent locationInWindow]; + + [nswindow dragImage:_gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf) + at:point + offset:NSMakeSize(0, 0) + event:nsevent + pasteboard:pasteboard + source:nswindow + slideBack:YES]; + + return info->context; +} + +GdkDragContext * +gtk_drag_begin (GtkWidget *widget, + GtkTargetList *targets, + GdkDragAction actions, + gint button, + GdkEvent *event) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL); + g_return_val_if_fail (targets != NULL, NULL); + + return gtk_drag_begin_internal (widget, NULL, targets, + actions, button, event); +} + + +static gboolean +gtk_drag_source_event_cb (GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + GtkDragSourceSite *site; + gboolean retval = FALSE; + site = (GtkDragSourceSite *)data; + + switch (event->type) + { + case GDK_BUTTON_PRESS: + if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask) + { + site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1)); + site->x = event->button.x; + site->y = event->button.y; + } + break; + + case GDK_BUTTON_RELEASE: + if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask) + site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1)); + break; + + case GDK_MOTION_NOTIFY: + if (site->state & event->motion.state & site->start_button_mask) + { + /* FIXME: This is really broken and can leave us + * with a stuck grab + */ + int i; + for (i=1; i<6; i++) + { + if (site->state & event->motion.state & + GDK_BUTTON1_MASK << (i - 1)) + break; + } + + if (gtk_drag_check_threshold (widget, site->x, site->y, + event->motion.x, event->motion.y)) + { + site->state = 0; + gtk_drag_begin_internal (widget, site, site->target_list, + site->actions, + i, event); + + retval = TRUE; + } + } + break; + + default: /* hit for 2/3BUTTON_PRESS */ + break; + } + + return retval; +} + +void +gtk_drag_source_set (GtkWidget *widget, + GdkModifierType start_button_mask, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions) +{ + GtkDragSourceSite *site; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + site = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); + + gtk_widget_add_events (widget, + gtk_widget_get_events (widget) | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK); + + if (site) + { + if (site->target_list) + gtk_target_list_unref (site->target_list); + } + else + { + site = g_new0 (GtkDragSourceSite, 1); + + site->icon_type = GTK_IMAGE_EMPTY; + + g_signal_connect (widget, "button_press_event", + G_CALLBACK (gtk_drag_source_event_cb), + site); + g_signal_connect (widget, "button_release_event", + G_CALLBACK (gtk_drag_source_event_cb), + site); + g_signal_connect (widget, "motion_notify_event", + G_CALLBACK (gtk_drag_source_event_cb), + site); + + g_object_set_data_full (G_OBJECT (widget), + I_("gtk-site-data"), + site, gtk_drag_source_site_destroy); + } + + site->start_button_mask = start_button_mask; + + site->target_list = gtk_target_list_new (targets, n_targets); + + site->actions = actions; +} + +/************************************************************* + * gtk_drag_source_unset + * Unregister this widget as a drag source. + * arguments: + * widget: + * results: + *************************************************************/ + +void +gtk_drag_source_unset (GtkWidget *widget) +{ + GtkDragSourceSite *site; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + site = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); + + if (site) + { + g_signal_handlers_disconnect_by_func (widget, + gtk_drag_source_event_cb, + site); + g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL); + } +} + +GtkTargetList * +gtk_drag_source_get_target_list (GtkWidget *widget) +{ + GtkDragSourceSite *site; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + site = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); + + return site ? site->target_list : NULL; + +} + +void +gtk_drag_source_set_target_list (GtkWidget *widget, + GtkTargetList *target_list) +{ + GtkDragSourceSite *site; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + site = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); + if (site == NULL) + { + g_warning ("gtk_drag_source_set_target_list() requires the widget " + "to already be a drag source."); + return; + } + + if (target_list) + gtk_target_list_ref (target_list); + + if (site->target_list) + gtk_target_list_unref (site->target_list); + + site->target_list = target_list; +} + +/** + * gtk_drag_source_add_text_targets: + * @widget: a #GtkWidget that's is a drag source + * + * Add the text targets supported by #GtkSelection to + * the target list of the drag source. The targets + * are added with @info = 0. If you need another value, + * use gtk_target_list_add_text_targets() and + * gtk_drag_source_set_target_list(). + * + * Since: 2.6 + **/ +void +gtk_drag_source_add_text_targets (GtkWidget *widget) +{ + GtkTargetList *target_list; + + target_list = gtk_drag_source_get_target_list (widget); + if (target_list) + gtk_target_list_ref (target_list); + else + target_list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_text_targets (target_list, 0); + gtk_drag_source_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); +} + +void +gtk_drag_source_add_image_targets (GtkWidget *widget) +{ + GtkTargetList *target_list; + + target_list = gtk_drag_source_get_target_list (widget); + if (target_list) + gtk_target_list_ref (target_list); + else + target_list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_image_targets (target_list, 0, TRUE); + gtk_drag_source_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); +} + +void +gtk_drag_source_add_uri_targets (GtkWidget *widget) +{ + GtkTargetList *target_list; + + target_list = gtk_drag_source_get_target_list (widget); + if (target_list) + gtk_target_list_ref (target_list); + else + target_list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_uri_targets (target_list, 0); + gtk_drag_source_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); +} + +static void +gtk_drag_source_unset_icon (GtkDragSourceSite *site) +{ + switch (site->icon_type) + { + case GTK_IMAGE_EMPTY: + break; + case GTK_IMAGE_PIXMAP: + if (site->icon_data.pixmap.pixmap) + g_object_unref (site->icon_data.pixmap.pixmap); + if (site->icon_mask) + g_object_unref (site->icon_mask); + break; + case GTK_IMAGE_PIXBUF: + g_object_unref (site->icon_data.pixbuf.pixbuf); + break; + case GTK_IMAGE_STOCK: + g_free (site->icon_data.stock.stock_id); + break; + case GTK_IMAGE_ICON_NAME: + g_free (site->icon_data.name.icon_name); + break; + default: + g_assert_not_reached(); + break; + } + site->icon_type = GTK_IMAGE_EMPTY; + + if (site->colormap) + g_object_unref (site->colormap); + site->colormap = NULL; +} + +static void +gtk_drag_source_site_destroy (gpointer data) +{ + GtkDragSourceSite *site = data; + + if (site->target_list) + gtk_target_list_unref (site->target_list); + + gtk_drag_source_unset_icon (site); + g_free (site); +} + +void +gtk_drag_source_set_icon (GtkWidget *widget, + GdkColormap *colormap, + GdkPixmap *pixmap, + GdkBitmap *mask) +{ + GtkDragSourceSite *site; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (GDK_IS_COLORMAP (colormap)); + g_return_if_fail (GDK_IS_PIXMAP (pixmap)); + g_return_if_fail (!mask || GDK_IS_PIXMAP (mask)); + + site = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); + g_return_if_fail (site != NULL); + + g_object_ref (colormap); + g_object_ref (pixmap); + if (mask) + g_object_ref (mask); + + gtk_drag_source_unset_icon (site); + + site->icon_type = GTK_IMAGE_PIXMAP; + + site->icon_data.pixmap.pixmap = pixmap; + site->icon_mask = mask; + site->colormap = colormap; +} + +void +gtk_drag_source_set_icon_pixbuf (GtkWidget *widget, + GdkPixbuf *pixbuf) +{ + GtkDragSourceSite *site; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); + + site = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); + g_return_if_fail (site != NULL); + g_object_ref (pixbuf); + + gtk_drag_source_unset_icon (site); + + site->icon_type = GTK_IMAGE_PIXBUF; + site->icon_data.pixbuf.pixbuf = pixbuf; +} + +/** + * gtk_drag_source_set_icon_stock: + * @widget: a #GtkWidget + * @stock_id: the ID of the stock icon to use + * + * Sets the icon that will be used for drags from a particular source + * to a stock icon. + **/ +void +gtk_drag_source_set_icon_stock (GtkWidget *widget, + const gchar *stock_id) +{ + GtkDragSourceSite *site; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (stock_id != NULL); + + site = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); + g_return_if_fail (site != NULL); + + gtk_drag_source_unset_icon (site); + + site->icon_type = GTK_IMAGE_STOCK; + site->icon_data.stock.stock_id = g_strdup (stock_id); +} + +/** + * gtk_drag_source_set_icon_name: + * @widget: a #GtkWidget + * @icon_name: name of icon to use + * + * Sets the icon that will be used for drags from a particular source + * to a themed icon. See the docs for #GtkIconTheme for more details. + * + * Since: 2.8 + **/ +void +gtk_drag_source_set_icon_name (GtkWidget *widget, + const gchar *icon_name) +{ + GtkDragSourceSite *site; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (icon_name != NULL); + + site = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); + g_return_if_fail (site != NULL); + + gtk_drag_source_unset_icon (site); + + site->icon_type = GTK_IMAGE_ICON_NAME; + site->icon_data.name.icon_name = g_strdup (icon_name); +} + + +/** + * gtk_drag_set_icon_widget: + * @context: the context for a drag. (This must be called + with a context for the source side of a drag) + * @widget: a toplevel window to use as an icon. + * @hot_x: the X offset within @widget of the hotspot. + * @hot_y: the Y offset within @widget of the hotspot. + * + * Changes the icon for a widget to a given widget. GTK+ + * will not destroy the icon, so if you don't want + * it to persist, you should connect to the "drag_end" + * signal and destroy it yourself. + **/ +void +gtk_drag_set_icon_widget (GdkDragContext *context, + GtkWidget *widget, + gint hot_x, + gint hot_y) +{ + g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); + g_return_if_fail (context->is_source); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + g_warning ("gtk_drag_set_icon_widget is not supported on Mac OS X"); +} + +static void +set_icon_stock_pixbuf (GdkDragContext *context, + const gchar *stock_id, + GdkPixbuf *pixbuf, + gint hot_x, + gint hot_y) +{ + GtkDragSourceInfo *info; + + info = gtk_drag_get_source_info (context, FALSE); + + if (stock_id) + { + pixbuf = gtk_widget_render_icon (info->widget, stock_id, + GTK_ICON_SIZE_DND, NULL); + + if (!pixbuf) + { + g_warning ("Cannot load drag icon from stock_id %s", stock_id); + return; + } + } + else + g_object_ref (pixbuf); + + if (info->icon_pixbuf) + g_object_unref (info->icon_pixbuf); + info->icon_pixbuf = pixbuf; + info->hot_x = hot_x; + info->hot_y = hot_y; +} + +/** + * gtk_drag_set_icon_pixbuf: + * @context: the context for a drag. (This must be called + * with a context for the source side of a drag) + * @pixbuf: the #GdkPixbuf to use as the drag icon. + * @hot_x: the X offset within @widget of the hotspot. + * @hot_y: the Y offset within @widget of the hotspot. + * + * Sets @pixbuf as the icon for a given drag. + **/ +void +gtk_drag_set_icon_pixbuf (GdkDragContext *context, + GdkPixbuf *pixbuf, + gint hot_x, + gint hot_y) +{ + g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); + g_return_if_fail (context->is_source); + g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); + + set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y); +} + +/** + * gtk_drag_set_icon_stock: + * @context: the context for a drag. (This must be called + * with a context for the source side of a drag) + * @stock_id: the ID of the stock icon to use for the drag. + * @hot_x: the X offset within the icon of the hotspot. + * @hot_y: the Y offset within the icon of the hotspot. + * + * Sets the icon for a given drag from a stock ID. + **/ +void +gtk_drag_set_icon_stock (GdkDragContext *context, + const gchar *stock_id, + gint hot_x, + gint hot_y) +{ + + g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); + g_return_if_fail (context->is_source); + g_return_if_fail (stock_id != NULL); + + set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y); +} + +/** + * gtk_drag_set_icon_pixmap: + * @context: the context for a drag. (This must be called + * with a context for the source side of a drag) + * @colormap: the colormap of the icon + * @pixmap: the image data for the icon + * @mask: the transparency mask for the icon + * @hot_x: the X offset within @pixmap of the hotspot. + * @hot_y: the Y offset within @pixmap of the hotspot. + * + * Sets @pixmap as the icon for a given drag. GTK+ retains + * references for the arguments, and will release them when + * they are no longer needed. In general, gtk_drag_set_icon_pixbuf() + * will be more convenient to use. + **/ +void +gtk_drag_set_icon_pixmap (GdkDragContext *context, + GdkColormap *colormap, + GdkPixmap *pixmap, + GdkBitmap *mask, + gint hot_x, + gint hot_y) +{ + g_warning ("gtk_drag_set_icon_pixmap is not supported on Mac OS X"); +} + +/** + * gtk_drag_set_icon_name: + * @context: the context for a drag. (This must be called + * with a context for the source side of a drag) + * @icon_name: name of icon to use + * @hot_x: the X offset of the hotspot within the icon + * @hot_y: the Y offset of the hotspot within the icon + * + * Sets the icon for a given drag from a named themed icon. See + * the docs for #GtkIconTheme for more details. Note that the + * size of the icon depends on the icon theme (the icon is + * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus + * @hot_x and @hot_y have to be used with care. + * + * Since: 2.8 + **/ +void +gtk_drag_set_icon_name (GdkDragContext *context, + const gchar *icon_name, + gint hot_x, + gint hot_y) +{ + GdkScreen *screen; + GtkSettings *settings; + GtkIconTheme *icon_theme; + GdkPixbuf *pixbuf; + gint width, height, icon_size; + + g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); + g_return_if_fail (context->is_source); + g_return_if_fail (icon_name != NULL); + + screen = gdk_drawable_get_screen (context->source_window); + g_return_if_fail (screen != NULL); + + settings = gtk_settings_get_for_screen (screen); + if (gtk_icon_size_lookup_for_settings (settings, + GTK_ICON_SIZE_DND, + &width, &height)) + icon_size = MAX (width, height); + else + icon_size = 32; /* default value for GTK_ICON_SIZE_DND */ + + icon_theme = gtk_icon_theme_get_for_screen (screen); + + pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name, + icon_size, 0, NULL); + if (pixbuf) + set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y); + else + g_warning ("Cannot load drag icon from icon name %s", icon_name); +} + +/** + * gtk_drag_set_icon_default: + * @context: the context for a drag. (This must be called + with a context for the source side of a drag) + * + * Sets the icon for a particular drag to the default + * icon. + **/ +void +gtk_drag_set_icon_default (GdkDragContext *context) +{ + g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); + g_return_if_fail (context->is_source); + + gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2); +} + +void +gtk_drag_set_default_icon (GdkColormap *colormap, + GdkPixmap *pixmap, + GdkBitmap *mask, + gint hot_x, + gint hot_y) +{ + g_warning ("gtk_drag_set_default_icon is not supported on Mac OS X."); +} + +static void +gtk_drag_source_info_destroy (GtkDragSourceInfo *info) +{ + if (info->icon_pixbuf) + g_object_unref (info->icon_pixbuf); + + g_signal_emit_by_name (info->widget, "drag_end", + info->context); + + if (info->widget) + g_object_unref (info->widget); + + gtk_target_list_unref (info->target_list); + + gtk_drag_clear_source_info (info->context); + g_object_unref (info->context); + + g_free (info); +} + +static void +gtk_drag_drop_finished (GtkDragSourceInfo *info) +{ + gtk_drag_source_info_destroy (info); +} + +/************************************************************* + * _gtk_drag_source_handle_event: + * Called from widget event handling code on Drag events + * for drag sources. + * + * arguments: + * toplevel: Toplevel widget that received the event + * event: + * results: + *************************************************************/ + +void +_gtk_drag_source_handle_event (GtkWidget *widget, + GdkEvent *event) +{ + GtkDragSourceInfo *info; + GdkDragContext *context; + + g_return_if_fail (widget != NULL); + g_return_if_fail (event != NULL); + + context = event->dnd.context; + info = gtk_drag_get_source_info (context, FALSE); + if (!info) + return; + + switch (event->type) + { + case GDK_DROP_FINISHED: + gtk_drag_drop_finished (info); + break; + default: + g_assert_not_reached (); + } +} + + +gboolean +gtk_drag_check_threshold (GtkWidget *widget, + gint start_x, + gint start_y, + gint current_x, + gint current_y) +{ + gint drag_threshold; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + g_object_get (gtk_widget_get_settings (widget), + "gtk-dnd-drag-threshold", &drag_threshold, + NULL); + + return (ABS (current_x - start_x) > drag_threshold || + ABS (current_y - start_y) > drag_threshold); +} + +#define __GTK_DND_C__ +#include "gtkaliasdef.c" diff --git a/gtk/gtkquartz.c b/gtk/gtkquartz.c new file mode 100644 index 0000000000..57556643cf --- /dev/null +++ b/gtk/gtkquartz.c @@ -0,0 +1,288 @@ +/* gtkquartz.c: Utility functions used by the Quartz port + * + * Copyright (C) 2006 Imendio AB + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "gtkquartz.h" + +NSImage * +_gtk_quartz_create_image_from_pixbuf (GdkPixbuf *pixbuf) +{ + CGColorSpaceRef colorspace; + CGDataProviderRef data_provider; + CGContextRef context; + CGImageRef image; + void *data; + int rowstride, pixbuf_width, pixbuf_height; + gboolean has_alpha; + NSImage *nsimage; + + pixbuf_width = gdk_pixbuf_get_width (pixbuf); + pixbuf_height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + + data = gdk_pixbuf_get_pixels (pixbuf); + + colorspace = CGColorSpaceCreateDeviceRGB (); + data_provider = CGDataProviderCreateWithData (NULL, data, pixbuf_height * rowstride, NULL); + + image = CGImageCreate (pixbuf_width, pixbuf_height, 8, + has_alpha ? 32 : 24, rowstride, + colorspace, + has_alpha ? kCGImageAlphaLast : 0, + data_provider, NULL, FALSE, + kCGRenderingIntentDefault); + + CGDataProviderRelease (data_provider); + CGColorSpaceRelease (colorspace); + + nsimage = [[NSImage alloc] initWithSize:NSMakeSize (pixbuf_width, pixbuf_height)]; + [nsimage lockFocus]; + + context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + CGContextDrawImage (context, CGRectMake (0, 0, pixbuf_width, pixbuf_height), image); + + [nsimage unlockFocus]; + + CGImageRelease (image); + + return nsimage; +} + +static NSString * +target_to_pasteboard_type (const char *target) +{ + if (strcmp (target, "UTF8_STRING") == 0) + return NSStringPboardType; + else if (strcmp (target, "image/tiff") == 0) + return NSTIFFPboardType; + else if (strcmp (target, "application/x-color") == 0) + return NSColorPboardType; + else if (strcmp (target, "text/uri-list") == 0) + return NSURLPboardType; + else + return [NSString stringWithUTF8String:target]; +} + +NSArray * +_gtk_quartz_target_list_to_pasteboard_types (GtkTargetList *target_list) +{ + NSMutableSet *set = [[NSMutableSet alloc] init]; + GList *list; + + for (list = target_list->list; list; list = list->next) + { + GtkTargetPair *pair = list->data; + gchar *target = gdk_atom_name (pair->target); + [set addObject:target_to_pasteboard_type (target)]; + g_free (target); + } + + return [set allObjects]; +} + + +NSArray * +_gtk_quartz_target_entries_to_pasteboard_types (const GtkTargetEntry *targets, + guint n_targets) +{ + NSMutableSet *set = [[NSMutableSet alloc] init]; + int i; + + for (i = 0; i < n_targets; i++) + { + [set addObject:target_to_pasteboard_type (targets[i].target)]; + } + + return [set allObjects]; +} + +GdkAtom +_gtk_quartz_pasteboard_type_to_atom (NSString *type) +{ + if ([type isEqualToString:NSStringPboardType]) + return gdk_atom_intern_static_string ("UTF8_STRING"); + else if ([type isEqualToString:NSTIFFPboardType]) + return gdk_atom_intern_static_string ("image/tiff"); + else if ([type isEqualToString:NSColorPboardType]) + return gdk_atom_intern_static_string ("application/x-color"); + else if ([type isEqualToString:NSURLPboardType]) + return gdk_atom_intern_static_string ("text/uri-list"); + else + return gdk_atom_intern ([type UTF8String], FALSE); +} + +GList * +_gtk_quartz_pasteboard_types_to_atom_list (NSArray *array) +{ + GList *result = NULL; + int i; + int count; + + count = [array count]; + + for (i = 0; i < count; i++) + { + GdkAtom atom = _gtk_quartz_pasteboard_type_to_atom ([array objectAtIndex:i]); + + result = g_list_prepend (result, GDK_ATOM_TO_POINTER (atom)); + } + + return result; +} + +GtkSelectionData * +_gtk_quartz_get_selection_data_from_pasteboard (NSPasteboard *pasteboard, + GdkAtom target, + GdkAtom selection) +{ + GtkSelectionData *selection_data = NULL; + + selection_data = g_new0 (GtkSelectionData, 1); + selection_data->selection = selection; + selection_data->target = target; + + if (target == gdk_atom_intern_static_string ("UTF8_STRING")) + { + NSString *s = [pasteboard stringForType:NSStringPboardType]; + int len = [s length]; + + if (s) + { + selection_data->type = target; + selection_data->format = 8; + selection_data->length = len; + selection_data->data = g_memdup ([s UTF8String], len + 1); + } + } + else if (target == gdk_atom_intern_static_string ("application/x-color")) + { + NSColor *nscolor = [[NSColor colorFromPasteboard:pasteboard] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + + guint16 color[4]; + + selection_data->target = target; + + color[0] = 0xffff * [nscolor redComponent]; + color[1] = 0xffff * [nscolor greenComponent]; + color[2] = 0xffff * [nscolor blueComponent]; + color[3] = 0xffff * [nscolor alphaComponent]; + + gtk_selection_data_set (selection_data, target, 16, (guchar *)color, 8); + } + else if (target == gdk_atom_intern_static_string ("text/uri-list")) + { + gchar *uris[2]; + NSURL *url = [NSURL URLFromPasteboard:pasteboard]; + + selection_data->target = gdk_atom_intern_static_string ("text/uri-list"); + + uris[0] = [[url description] UTF8String]; + uris[1] = NULL; + gtk_selection_data_set_uris (selection_data, uris); + } + else + { + NSData *data; + gchar *name; + + name = gdk_atom_name (target); + + if (strcmp (name, "image/tiff") == 0) + data = [pasteboard dataForType:NSTIFFPboardType]; + else + data = [pasteboard dataForType:[NSString stringWithUTF8String:name]]; + + g_free (name); + + if (data) + { + selection_data->type = target; + selection_data->format = 8; + selection_data->length = [data length]; + selection_data->data = g_malloc (selection_data->length + 1); + selection_data->data[selection_data->length] = '\0'; + memcpy(selection_data->data, [data bytes], selection_data->length); + } + } + + return selection_data; +} + +void +_gtk_quartz_set_selection_data_for_pasteboard (NSPasteboard *pasteboard, + GtkSelectionData *selection_data) +{ + NSString *type; + NSData *data; + gchar *target = gdk_atom_name (selection_data->target); + + type = target_to_pasteboard_type (target); + g_free (target); + + if ([type isEqualTo:NSStringPboardType]) + [pasteboard setString:[NSString stringWithUTF8String:(const char *)selection_data->data] + forType:type]; + else if ([type isEqualTo:NSColorPboardType]) + { + guint16 *color = (guint16 *)selection_data->data; + float red, green, blue, alpha; + + red = (float)color[0] / 0xffff; + green = (float)color[1] / 0xffff; + blue = (float)color[2] / 0xffff; + alpha = (float)color[3] / 0xffff; + + NSColor *nscolor = [NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha]; + + [nscolor writeToPasteboard:pasteboard]; + } + else if ([type isEqualTo:NSURLPboardType]) + { + gchar **list; + gchar **result = NULL; + NSURL *url; + + int count = gdk_text_property_to_utf8_list_for_display (selection_data->display, + gdk_atom_intern_static_string ("UTF8_STRING"), + selection_data->format, + selection_data->data, + selection_data->length, + &list); + + if (count > 0) + result = g_uri_list_extract_uris (list[0]); + g_strfreev (list); + + url = [NSURL URLWithString:[NSString stringWithUTF8String:result[0]]]; + [url writeToPasteboard:pasteboard]; + + g_strfreev (result); + + } + else + [pasteboard setData:[NSData dataWithBytesNoCopy:selection_data->data + length:selection_data->length + freeWhenDone:NO] + forType:type]; +} + + diff --git a/gtk/gtkquartz.h b/gtk/gtkquartz.h new file mode 100644 index 0000000000..cb84f275a6 --- /dev/null +++ b/gtk/gtkquartz.h @@ -0,0 +1,47 @@ +/* gtkquartz.h: Utility functions used by the Quartz port + * + * Copyright (C) 2006 Imendio AB + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_QUARTZ_H__ +#define __GTK_QUARTZ_H__ + +#import +#include + +G_BEGIN_DECLS + +NSArray *_gtk_quartz_target_list_to_pasteboard_types (GtkTargetList *target_list); +NSArray *_gtk_quartz_target_entries_to_pasteboard_types (const GtkTargetEntry *targets, + guint n_targets); + +GList *_gtk_quartz_pasteboard_types_to_atom_list (NSArray *array); +GdkAtom _gtk_quartz_pasteboard_type_to_atom (NSString *type); + +GtkSelectionData *_gtk_quartz_get_selection_data_from_pasteboard (NSPasteboard *pasteboard, + GdkAtom target, + GdkAtom selection); + +void _gtk_quartz_set_selection_data_for_pasteboard (NSPasteboard *pasteboard, + GtkSelectionData *selection_data); + +NSImage *_gtk_quartz_create_image_from_pixbuf (GdkPixbuf *pixbuf); + +G_END_DECLS + +#endif /* __GTK_QUARTZ_H__ */