[gtk+/quartz-integration] Bug 571582: GtkSelection implementation for quartz.Forgot to add the most important file!



commit 2e39877103a4c02c04f33f8dcd629e35d1c9b91a
Author: John Ralls <jralls ceridwen us>
Date:   Fri Dec 31 17:10:53 2010 -0800

    Bug 571582: GtkSelection implementation for quartz.Forgot to add the most important file!

 gtk/gtkselection-quartz.c |  669 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 669 insertions(+), 0 deletions(-)
---
diff --git a/gtk/gtkselection-quartz.c b/gtk/gtkselection-quartz.c
new file mode 100644
index 0000000..f7b4654
--- /dev/null
+++ b/gtk/gtkselection-quartz.c
@@ -0,0 +1,669 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 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.
+ */
+
+/* This file implements most of the work of the ICCCM selection protocol.
+ * The code was written after an intensive study of the equivalent part
+ * of John Ousterhout's Tk toolkit, and does many things in much the
+ * same way.
+ *
+ * The one thing in the ICCCM that isn't fully supported here (or in Tk)
+ * is side effects targets. For these to be handled properly, MULTIPLE
+ * targets need to be done in the order specified. This cannot be
+ * guaranteed with the way we do things, since if we are doing INCR
+ * transfers, the order will depend on the timing of the requestor.
+ *
+ * By Owen Taylor <owt1 cornell edu>	      8/16/97
+ */
+
+/* Terminology note: when not otherwise specified, the term "incr" below
+ * refers to the _sending_ part of the INCR protocol. The receiving
+ * portion is referred to just as "retrieval". (Terminology borrowed
+ * from Tk, because there is no good opposite to "retrieval" in English.
+ * "send" can't be made into a noun gracefully and we're already using
+ * "emission" for something else ....)
+ */
+
+/* The MOTIF entry widget seems to ask for the TARGETS target, then
+   (regardless of the reply) ask for the TEXT target. It's slightly
+   possible though that it somehow thinks we are responding negatively
+   to the TARGETS request, though I don't really think so ... */
+
+/*
+ * 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 "config.h"
+#include <stdarg.h>
+#include <string.h>
+#include "gdk.h"
+
+#include "gtkmain.h"
+#include "gtkselection.h"
+#include "gtkselectionprivate.h"
+#include "gtktextbufferrichtext.h"
+#include "gtkintl.h"
+#include "gdk-pixbuf/gdk-pixbuf.h"
+#include "gtkclipboard.h"
+
+#import <Cocoa/Cocoa.h>
+#include "gtkalias.h"
+
+#undef DEBUG_SELECTION
+/*
+ * DON'T USE THIS INTERFACE: USE GTKCLIPBOARD INSTEAD!
+ *
+ * This is the Quartz version of gtkselection. Unlike the other
+ * versions, it was written in 2010, after most code was rewritten to
+ * use GtkClipboard. Quartz, unlike X11, is not a remote-capable
+ * display system, so most of ICCCM is pointless. This implementation
+ * can therefore be much simpler than the X11 implementation. Text is
+ * a lot simpler, too. It's UTF8. No compound text, no legacy
+ * charsets.  There's also only one display, so instead of passing it
+ * around, we'll generally just use gdk_display_get_default() when we
+ * need it.
+ *
+ * There are two constraints: The existing code in various GtkWidgets
+ * which uses GDK_SELECTION_CLIPBOARD (which gtkclipboard-quartz sets
+ * to generalPasteboard) for <ctrl>c copies and GDK_SELECTION_PRIMARY
+ * (for which gtkclipboard-quartz creates a separate pasteboard) for
+ * X-style selection transfers, and Apple's X11 Quartz implementation
+ * which by default puts both on the generalPasteboard. We need to
+ * operate with both.
+ *
+ * IMPORTANT: There is no X11 magic in quartz. If you insist on using
+ * this interface (and you really shouldn't), your MUST connect to
+ * selection-get, selection-received, and selection-clear-event for
+ * your widget.
+ */
+
+/* Maximum size of a sent chunk, in bytes. Also the default size of
+   our buffers */
+
+
+#define IDLE_ABORT_TIME 30
+
+enum {
+  INCR,
+  MULTIPLE,
+  TARGETS,
+  TIMESTAMP,
+  SAVE_TARGETS,
+  LAST_ATOM
+};
+
+typedef struct _GtkSelectionInfo GtkSelectionInfo;
+
+struct _GtkSelectionInfo
+{
+  GdkAtom	 selection;
+  GtkWidget	*owner;	/* widget that owns selection */
+  guint32	 time;		/* time used to acquire selection */
+};
+
+
+/* Local Functions */
+static void gtk_selection_get_cb            (GtkClipboard     *clipboard,
+					     GtkSelectionData *data,
+					     guint             info,
+					     gpointer          widget);
+static void gtk_selection_clear_cb          (GtkClipboard     *clipboard,
+					     gpointer 	       widget);
+static void gtk_selection_default_handler   (GtkWidget        *widget,
+					     GtkSelectionData *data);
+static int  gtk_selection_bytes_per_item    (gint              format);
+static GtkSelectionInfo *gtk_selection_info_get (GdkAtom selection);
+static void gtk_selection_info_remove		(GdkAtom selection,
+						 GtkWidget *owner);
+static void gtk_selection_info_append 		(GdkAtom selection,
+						 GtkWidget *owner,
+						 guint32 time);
+static void gtk_selection_info_clear		(GtkWidget *owner);
+static GtkTargetList *gtk_selection_target_list_get (GtkWidget    *widget,
+						     GdkAtom       selection);
+static void gtk_selection_target_list_remove 	    (GtkWidget *widget);
+
+/* Local Data */
+static gint initialize = TRUE;
+static GList *current_selections = NULL;
+
+static GdkAtom gtk_selection_atoms[LAST_ATOM];
+static const char gtk_selection_handler_key[] = "gtk-selection-handlers";
+
+static GtkTargetEntry default_target = {"UTF8_STRING", 0, 1};
+
+/**
+ * gtk_selection_owner_set_for_display:
+ * @display: the #Gdkdisplay where the selection is set
+ * @widget: (allow-none): new selection owner (a #GdkWidget), or %NULL.
+ * @selection: an interned atom representing the selection to claim.
+ * @time_: timestamp with which to claim the selection
+ *
+ * Claim ownership of a given selection for a particular widget, or,
+ * if @widget is %NULL, release ownership of the selection.
+ *
+ * Return value: TRUE if the operation succeeded
+ *
+ * Since: 2.2
+ */
+gboolean
+gtk_selection_owner_set_for_display (GdkDisplay   *display,
+				     GtkWidget    *widget,
+				     GdkAtom       selection,
+				     guint32       time)
+{
+  GObject *old_owner;
+  GtkClipboard *clip = gtk_clipboard_get (selection);
+  GtkTargetEntry *targets = &default_target;
+  gint num_targets = 1;
+  GtkTargetList *tlist;
+
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+  g_return_val_if_fail (selection != GDK_NONE, FALSE);
+
+  old_owner = gtk_clipboard_get_owner (clip);
+  if (old_owner)
+    gtk_selection_info_remove (selection, GTK_WIDGET(old_owner));
+
+  if (widget == NULL)
+    return TRUE;
+
+  g_return_val_if_fail (gtk_widget_get_display (widget) == display, FALSE);
+
+  if ((tlist = gtk_selection_target_list_get (widget, selection)) != NULL)
+      targets = gtk_target_table_new_from_list (tlist, &num_targets);
+
+  if (gtk_clipboard_set_with_owner (clip, targets, num_targets,
+				    gtk_selection_get_cb,
+				    gtk_selection_clear_cb,
+				    G_OBJECT (widget)))
+    {
+      gtk_selection_info_append (selection, widget, GDK_CURRENT_TIME);
+      return TRUE;
+    }
+  return FALSE;
+}
+
+
+typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
+
+struct _GtkSelectionTargetList {
+  GdkAtom selection;
+  GtkTargetList *list;
+};
+
+/**
+ * gtk_selection_remove_all:
+ * @widget: a #GtkWidget
+ *
+ * Removes all handlers and unsets ownership of all
+ * selections for a widget. Called when widget is being
+ * destroyed. This function will not generally be
+ * called by applications.
+ **/
+void
+gtk_selection_remove_all (GtkWidget *widget)
+{
+  g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget));
+  gtk_selection_info_clear (widget);
+  /* Remove all selection lists */
+  gtk_selection_target_list_remove (widget);
+}
+
+
+/**
+ * gtk_selection_convert:
+ * @widget: The widget which acts as requestor
+ * @selection: Which selection to get
+ * @target: Form of information desired (e.g., STRING)
+ * @time_: Time of request (usually of triggering event)
+       In emergency, you could use #GDK_CURRENT_TIME
+ *
+ * Requests the contents of a selection. When received,
+ * a "selection-received" signal will be generated.
+ *
+ * Return value: %TRUE if requested succeeded. %FALSE if we could not process
+ *          request. (e.g., there was already a request in process for
+ *          this widget).
+ **/
+gboolean
+gtk_selection_convert (GtkWidget *widget,
+		       GdkAtom	  selection,
+		       GdkAtom	  target,
+		       guint32	  time_)
+{
+  GtkClipboard *clip = gtk_clipboard_get (selection);
+  GtkSelectionData *data;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+  g_return_val_if_fail (selection != GDK_NONE, FALSE);
+
+  data = gtk_clipboard_wait_for_contents (clip, target);
+  if (data == NULL)
+    return FALSE;
+
+  g_signal_emit_by_name (widget, "selection-received", data, time);
+
+  return TRUE;
+}
+
+
+/**
+ * _gtk_selection_clear:
+ * @widget: a #GtkWidget
+ * @event: the event
+ *
+ * The default handler for the #GtkWidget::selection-clear-event
+ * signal.
+ *
+ * Return value: %TRUE if the event was handled, otherwise false
+ **/
+gboolean
+_gtk_selection_clear (GtkWidget         *widget,
+		     GdkEventSelection *event)
+{
+  gtk_selection_clear_targets (widget, event->selection);
+  return FALSE;
+}
+
+
+/*************************************************************
+ * _gtk_selection_request:
+ *     Handler for "selection_request_event"
+ *   arguments:
+ *     widget:
+ *     event:
+ *   results:
+ *************************************************************/
+
+gboolean
+_gtk_selection_request (GtkWidget *widget,
+			GdkEventSelection *event)
+{
+  g_print ("Selection Request Events should not occur in quartz\n");
+  return TRUE;
+}
+
+/*************************************************************
+ * _gtk_selection_incr_event:
+ *     Called whenever an PropertyNotify event occurs for an
+ *     GdkWindow with user_data == NULL. These will be notifications
+ *     that a window we are sending the selection to via the
+ *     INCR protocol has deleted a property and is ready for
+ *     more data.
+ *
+ *   arguments:
+ *     window:	the requestor window
+ *     event:	the property event structure
+ *
+ *   results:
+ *************************************************************/
+
+gboolean
+_gtk_selection_incr_event (GdkWindow	   *window,
+			   GdkEventProperty *event)
+{
+  g_print ("Selection_INCR_Events should not occur in quartz\n");
+  return TRUE;
+}
+
+/*************************************************************
+ * _gtk_selection_notify:
+ *     Handler for "selection-notify-event" signals on windows
+ *     where a retrieval is currently in process. The selection
+ *     owner has responded to our conversion request.
+ *   arguments:
+ *     widget:		Widget getting signal
+ *     event:		Selection event structure
+ *     info:		Information about this retrieval
+ *   results:
+ *     was event handled?
+ *************************************************************/
+
+gboolean
+_gtk_selection_notify (GtkWidget	       *widget,
+		       GdkEventSelection *event)
+{
+  g_print ("Selection_Notifications should not occur in quartz\n");
+
+  return TRUE;
+}
+
+/*************************************************************
+ * _gtk_selection_property_notify:
+ *     Handler for "property-notify-event" signals on windows
+ *     where a retrieval is currently in process. The selection
+ *     owner has added more data.
+ *   arguments:
+ *     widget:		Widget getting signal
+ *     event:		Property event structure
+ *     info:		Information about this retrieval
+ *   results:
+ *     was event handled?
+ *************************************************************/
+
+gboolean
+_gtk_selection_property_notify (GtkWidget	*widget,
+				GdkEventProperty *event)
+{
+  g_print ("Selection_Property_Notifications should not occur in quartz\n");
+  return TRUE;
+}
+
+
+/*************************************************************
+ * gtk_selection_get_cb()
+ * @clipboard: The clipboard requesting the data
+ * @data: Pass to selection-get signal; handlers should put requested
+ * data in the structure pointed to.
+ * @info: DND uses this on Windows and X11. It can be ignored for
+ * normal selection use.
+ * @owner: The window to which the information request is sent; it's
+ * the owner set with gtk_selection_owner_set_for_display.
+ *
+ * Emits a signal to the owner window to fill in the provided data structure.
+ *************************************************************/
+/* GtkClipboardGetFunc */
+static void
+gtk_selection_get_cb (GtkClipboard* clipboard,
+		      GtkSelectionData *data,
+		      guint info,
+		      gpointer owner)
+{
+  GtkTargetList *target_list;
+  GtkWidget *widget = GTK_WIDGET (owner);
+
+
+  g_return_if_fail (widget != NULL);
+
+  target_list = gtk_selection_target_list_get (widget, data->selection);
+
+  if ( data->target == gtk_selection_atoms[TIMESTAMP] ||
+       data->target == gtk_selection_atoms[TARGETS] ||
+       data->target == gtk_selection_atoms[SAVE_TARGETS])
+    {
+      gtk_selection_default_handler (widget, data);
+      return;
+    }
+  if (target_list &&
+      gtk_target_list_find (target_list, data->target, &info))
+    {
+      g_signal_emit_by_name (widget,
+			     "selection-get",
+			     data,
+			     info, time);
+    }
+}
+
+static void
+gtk_selection_clear_cb (GtkClipboard* clipboard,
+			gpointer owner)
+{
+  GtkWidget *widget = GTK_WIDGET (owner);
+  GdkEventSelection event;
+  event.type = GDK_SELECTION_CLEAR;
+  event.selection = GDK_SELECTION_PRIMARY;
+  event.window = gtk_widget_get_window(widget);
+  g_signal_emit_by_name (widget,
+			 "selection-clear-event",
+			 &event,
+			 NULL);
+}
+
+/*************************************************************
+ * gtk_selection_default_handler:
+ *     Handles some default targets that exist for any widget
+ *     If it can't fit results into buffer, returns -1. This
+ *     won't happen in any conceivable case, since it would
+ *     require 1000 selection targets!
+ *
+ *   arguments:
+ *     widget:	    selection owner
+ *     data:	    selection data [INOUT]
+ *
+ *************************************************************/
+
+static void
+gtk_selection_default_handler (GtkWidget	*widget,
+			       GtkSelectionData *data)
+{
+  if (data->target == gtk_selection_atoms[TIMESTAMP])
+    {
+      /* Time which was used to obtain selection */
+      GList *tmp_list;
+      GtkSelectionInfo *selection_info;
+
+      tmp_list = current_selections;
+      while (tmp_list)
+	{
+	  selection_info = (GtkSelectionInfo *)tmp_list->data;
+	  if ((selection_info->owner == widget) &&
+	      (selection_info->selection == data->selection))
+	    {
+	      gulong time = selection_info->time;
+
+	      gtk_selection_data_set (data,
+				      GDK_SELECTION_TYPE_INTEGER,
+				      32,
+				      (guchar *)&time,
+				      sizeof (time));
+	      return;
+	    }
+
+	  tmp_list = tmp_list->next;
+	}
+
+      data->length = -1;
+    }
+  else if (data->target == gtk_selection_atoms[TARGETS])
+    {
+      /* List of all targets supported for this widget/selection pair */
+      GdkAtom *p;
+      guint count;
+      GList *tmp_list;
+      GtkTargetList *target_list;
+      GtkTargetPair *pair;
+
+      target_list = gtk_selection_target_list_get (widget,
+						   data->selection);
+      count = g_list_length (target_list->list) + 3;
+
+      data->type = GDK_SELECTION_TYPE_ATOM;
+      data->format = 32;
+      data->length = count * sizeof (GdkAtom);
+
+      /* selection data is always terminated by a trailing \0
+       */
+      p = g_malloc (data->length + 1);
+      data->data = (guchar *)p;
+      data->data[data->length] = '\0';
+
+      *p++ = gtk_selection_atoms[TIMESTAMP];
+      *p++ = gtk_selection_atoms[TARGETS];
+      *p++ = gtk_selection_atoms[MULTIPLE];
+
+      tmp_list = target_list->list;
+      while (tmp_list)
+	{
+	  pair = (GtkTargetPair *)tmp_list->data;
+	  *p++ = pair->target;
+
+	  tmp_list = tmp_list->next;
+	}
+    }
+  else if (data->target == gtk_selection_atoms[SAVE_TARGETS])
+    {
+      gtk_selection_data_set (data,
+			      gdk_atom_intern_static_string ("NULL"),
+			      32, NULL, 0);
+    }
+  else
+    {
+      data->length = -1;
+    }
+}
+
+static GtkSelectionInfo *
+gtk_selection_info_get (GdkAtom selection)
+{
+  GList *tmp_list;
+  GList *next;
+  GtkSelectionInfo *selection_info;
+
+  tmp_list = current_selections;
+  while (tmp_list)
+    {
+      next = tmp_list->next;
+      selection_info = (GtkSelectionInfo *)tmp_list->data;
+
+      if (selection_info->selection == selection)
+	{
+	  return selection_info;
+	}
+
+      tmp_list = next;
+    }
+  return NULL;
+}
+
+static void
+gtk_selection_info_remove (GdkAtom selection, GtkWidget *owner)
+{
+  GList *tmp_list;
+  GList *next;
+  GtkSelectionInfo *selection_info;
+
+  g_return_if_fail (GTK_IS_WIDGET (owner));
+
+  tmp_list = current_selections;
+  while (tmp_list)
+    {
+      next = tmp_list->next;
+      selection_info = (GtkSelectionInfo *)tmp_list->data;
+
+      if (selection_info->selection == selection &&
+	  selection_info->owner == owner)
+	{
+	  current_selections = g_list_remove_link (current_selections,
+						   tmp_list);
+	  g_list_free (tmp_list);
+	  g_slice_free (GtkSelectionInfo, selection_info);
+	  return;
+	}
+
+      tmp_list = next;
+    }
+}
+static void
+gtk_selection_info_append (GdkAtom selection, GtkWidget *owner, guint32 time)
+{
+  GtkSelectionInfo *selection_info;
+
+  g_return_if_fail (GTK_IS_WIDGET (owner));
+
+  selection_info = g_slice_new (GtkSelectionInfo);
+  selection_info->selection = selection;
+  selection_info->owner = owner;
+  selection_info->time = time;
+  current_selections = g_list_prepend (current_selections,
+				       selection_info);
+}
+
+static void
+gtk_selection_info_clear (GtkWidget *owner)
+{
+  GList *tmp_list;
+  GList *next;
+  GtkSelectionInfo *selection_info;
+
+  g_return_if_fail (GTK_IS_WIDGET (owner));
+
+  tmp_list = current_selections;
+  while (tmp_list)
+    {
+      next = tmp_list->next;
+      selection_info = (GtkSelectionInfo *)tmp_list->data;
+
+      if (selection_info->owner == owner)
+	{
+	  current_selections = g_list_remove_link (current_selections,
+						   tmp_list);
+	  g_list_free (tmp_list);
+	  g_slice_free (GtkSelectionInfo, selection_info);
+	}
+
+      tmp_list = next;
+    }
+}
+
+static GtkTargetList *
+gtk_selection_target_list_get (GtkWidget    *widget,
+			       GdkAtom       selection)
+{
+  GtkSelectionTargetList *sellist;
+  GList *tmp_list;
+  GList *lists;
+
+  lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
+
+  tmp_list = lists;
+  while (tmp_list)
+    {
+      sellist = tmp_list->data;
+      if (sellist->selection == selection)
+	return sellist->list;
+      tmp_list = tmp_list->next;
+    }
+
+  sellist = g_slice_new (GtkSelectionTargetList);
+  sellist->selection = selection;
+  sellist->list = gtk_target_list_new (NULL, 0);
+
+  lists = g_list_prepend (lists, sellist);
+  g_object_set_data (G_OBJECT (widget), I_(gtk_selection_handler_key), lists);
+
+  return sellist->list;
+}
+
+static void
+gtk_selection_target_list_remove (GtkWidget *widget)
+{
+  GtkSelectionTargetList *sellist;
+  GList *tmp_list;
+  GList *lists;
+
+  lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
+
+  tmp_list = lists;
+  while (tmp_list)
+    {
+      sellist = tmp_list->data;
+
+      gtk_target_list_unref (sellist->list);
+
+      g_slice_free (GtkSelectionTargetList, sellist);
+      tmp_list = tmp_list->next;
+    }
+
+  g_list_free (lists);
+  g_object_set_data (G_OBJECT (widget), I_(gtk_selection_handler_key), NULL);
+}
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]