[gtk-osx] Replace Gtk patches with new ones for 2.24.6.
- From: John Ralls <jralls src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk-osx] Replace Gtk patches with new ones for 2.24.6.
- Date: Thu, 29 Sep 2011 22:09:51 +0000 (UTC)
commit 6c455cb011c29784c82335031deea78cf7aec52b
Author: John Ralls <jralls ceridwen us>
Date: Mon Sep 26 12:22:34 2011 -0700
Replace Gtk patches with new ones for 2.24.6.
Note that most of these are already applied in Git, but the time immediately around gtk-2.24.6's release was very busy for the quartz maintainers and a bunch of important changes got committed afterwards; we want those changes in applications, so here are the patches, some of which supersede the older patches which are removed.
...f1345-Fix-refresh-of-static-autorelease_p.patch | 42 +
...Gtk-build-fails-because-of-objective-c-el.patch | 56 +
...mplement-relocatable-paths-for-quartz-si.patch} | 67 +-
...82-GtkSelection-implementation-for-quartz.patch | 923 ++++++
...Write-to-released-memory-in-gtkdnd-quartz.patch | 52 +
...722-Drag-and-Drop-sometimes-stops-working.patch | 274 ++
...767-Drag-and-Drop-NSEvent-capture-is-racy.patch | 78 +
...GtkDragSourceOwner-pasteboardChangedOwner.patch | 39 +
...009-Implement-recent-items-in-Filechooser.patch | 880 ++++++
...ead-accents-keys-don-t-work-in-GTK-appli.patch} | 37 +-
...filechooser-Deal-with-corrupted-.gtk-book.patch | 57 +
...Option-MOD1-and-Command-SUPER-modifiers-a.patch | 835 ++++++
...406-Abstract-what-triggers-a-context-menu.patch | 329 +++
...gdk_quartz_draw_opaque_stippled_pattern-c.patch | 25 +
...gtkfilechooser-crashes-when-adding-favori.patch | 59 +
patches/gdk-quartz-cursor.patch | 48 -
patches/gdk-quartz-input-window.patch | 35 -
patches/gdk-quartz-norects.patch | 227 --
patches/gdk-quartz-version.patch | 58 -
patches/gdkeventloop.patch | 22 -
patches/gtk-borderless.patch | 62 -
patches/gtk-introspection.patch | 2967 --------------------
patches/gtk-keyhash.patch | 41 -
patches/gtk2-lion-resize.patch | 117 -
patches/gtkcups.patch | 45 -
patches/gtkdndmemory.patch | 90 -
patches/gtkselection.patch | 1177 --------
patches/gtkselection_deref.patch | 22 -
28 files changed, 3714 insertions(+), 4950 deletions(-)
---
diff --git a/patches/0001-Backport-acf1345-Fix-refresh-of-static-autorelease_p.patch b/patches/0001-Backport-acf1345-Fix-refresh-of-static-autorelease_p.patch
new file mode 100644
index 0000000..2fd1d04
--- /dev/null
+++ b/patches/0001-Backport-acf1345-Fix-refresh-of-static-autorelease_p.patch
@@ -0,0 +1,42 @@
+From e1b9400f6ebadd7e4d137ec7b416e762f44148da Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Mon, 3 Jan 2011 11:56:20 -0800
+Subject: [PATCH 01/15] (Backport acf1345) Fix refresh of static
+ autorelease_pool so that it doesn't happen in
+ gtk-nested loops.
+
+---
+ gdk/quartz/gdkeventloop-quartz.c | 17 ++++++++---------
+ 1 files changed, 8 insertions(+), 9 deletions(-)
+
+diff --git a/gdk/quartz/gdkeventloop-quartz.c b/gdk/quartz/gdkeventloop-quartz.c
+index f11d4d8..568d4c8 100644
+--- a/gdk/quartz/gdkeventloop-quartz.c
++++ b/gdk/quartz/gdkeventloop-quartz.c
+@@ -632,15 +632,14 @@ gdk_event_check (GSource *source)
+
+ GDK_THREADS_ENTER ();
+
+- /* XXX: This check isn't right it won't handle a recursive GLib main
+- * loop run within an outer CFRunLoop run. Such loops will pile up
+- * memory. Fixing this requires setting a flag *only* when we call
+- * g_main_context_check() from within the run loop iteraton code,
+- * and also maintaining our own stack of run loops... allocating and
+- * releasing NSAutoReleasePools not properly nested with CFRunLoop
+- * runs seems to cause problems.
+- */
+- if (current_loop_level == 0)
++/* Refresh the autorelease pool if we're at the base CFRunLoop level
++ * (indicated by current_loop_level) and the base g_main_loop level
++ * (indicated by g_main_depth()). Messing with the autorelease pool at
++ * any level of nesting can cause access to deallocated memory because
++ * autorelease_pool is static and releasing a pool will cause all pools
++ * allocated inside of it to be released as well.
++ */
++ if (current_loop_level == 0 && g_main_depth() == 0)
+ {
+ if (autorelease_pool)
+ [autorelease_pool release];
+--
+1.7.6.3.dirty
+
diff --git a/patches/0002-Bug-628396-Gtk-build-fails-because-of-objective-c-el.patch b/patches/0002-Bug-628396-Gtk-build-fails-because-of-objective-c-el.patch
new file mode 100644
index 0000000..940f49e
--- /dev/null
+++ b/patches/0002-Bug-628396-Gtk-build-fails-because-of-objective-c-el.patch
@@ -0,0 +1,56 @@
+From c595a1c92bf29f34f835fcd365ba7cf54a990700 Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Sun, 26 Dec 2010 15:02:36 -0800
+Subject: [PATCH 02/15] Bug 628396: Gtk build fails because of objective-c
+ elements
+
+---
+ gtk/Makefile.am | 7 ++-----
+ 1 files changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/gtk/Makefile.am b/gtk/Makefile.am
+index 6050c76..78f4684 100644
+--- a/gtk/Makefile.am
++++ b/gtk/Makefile.am
+@@ -53,7 +53,6 @@ INCLUDES = \
+ -DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED \
+ $(GTK_DEBUG_FLAGS) \
+ $(GTK_DEP_CFLAGS) \
+- $(gtk_clipboard_dnd_c_sources_CFLAGS) \
+ $(INCLUDED_IMMODULE_DEFINE)
+
+ gtarget=$(gdktarget)
+@@ -740,7 +739,7 @@ gtk_use_stub_c_sources = \
+ gtkplug-stub.c \
+ gtksocket-stub.c \
+ gtkmountoperation-stub.c
+-gtk_all_c_sources += $(gtk_use_x11_c_sources) $(gtk_use_win32_c_sources) $(gtk_use_quartz_c_sources) $(gtk_use_stub_c_sources)
++gtk_all_c_sources += $(gtk_use_x11_c_sources) $(gtk_use_win32_c_source) $(gtk_use_stub_c_sources)
+ if USE_X11
+ gtk_private_h_sources += gtkxembed.h gtktrayicon.h xembed.h
+ gtk_c_sources += $(gtk_use_x11_c_sources)
+@@ -750,9 +749,9 @@ gtk_private_h_sources += gtkwin32embed.h gtkwin32embedwidget.h
+ gtk_c_sources += $(gtk_use_win32_c_sources)
+ else
+ if USE_QUARTZ
++libgtk_quartz_2_0_la_CFLAGS = "-xobjective-c"
+ gtk_private_h_sources += gtksearchenginequartz.h
+ gtk_c_sources += $(gtk_use_quartz_c_sources)
+-gtk_use_quartz_c_sources_CFLAGS = "-xobjective-c"
+ else
+ gtk_c_sources += $(gtk_use_stub_c_sources)
+ endif
+@@ -762,10 +761,8 @@ endif
+ if USE_QUARTZ
+ gtk_clipboard_dnd_c_sources = gtkclipboard-quartz.c gtkdnd-quartz.c gtkquartz.c
+ gtk_clipboard_dnd_h_sources = gtkquartz.h
+-gtk_clipboard_dnd_c_sources_CFLAGS = "-xobjective-c"
+ else
+ gtk_clipboard_dnd_c_sources = gtkclipboard.c gtkdnd.c
+-gtk_clipboard_dnd_c_sources_CFLAGS =
+ endif
+ EXTRA_DIST += gtkquartz.h
+
+--
+1.7.6.3.dirty
+
diff --git a/patches/gtk-relocation.patch b/patches/0003-Bug-658772-Implement-relocatable-paths-for-quartz-si.patch
similarity index 59%
rename from patches/gtk-relocation.patch
rename to patches/0003-Bug-658772-Implement-relocatable-paths-for-quartz-si.patch
index 50f4ac2..08c718c 100644
--- a/patches/gtk-relocation.patch
+++ b/patches/0003-Bug-658772-Implement-relocatable-paths-for-quartz-si.patch
@@ -1,37 +1,39 @@
+From 7e0d64a32bc9d0afa855e717d5b1eacc305b3458 Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Mon, 13 Dec 2010 15:01:02 -0800
+Subject: [PATCH 03/15] Bug 658772: Implement relocatable paths for quartz,
+ similar to those in Win32.
+
+The motivation was a complaint in the Gtk OSX forum that GTK didn't seem to be able to find its translations when bundled.
+---
+ gtk/gtkprivate.h | 4 +-
+ gtk/gtkquartz.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 70 insertions(+), 2 deletions(-)
+
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
-index 7ba5a5d..b3fcf40 100644
+index 7ba5a5d..3933dc5 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
-@@ -95,6 +95,27 @@ const gchar *_gtk_get_data_prefix ();
+@@ -74,7 +74,7 @@ typedef enum
+ #define GTK_PRIVATE_SET_FLAG(wid,flag) G_STMT_START{ (GTK_PRIVATE_FLAGS (wid) |= (PRIVATE_ ## flag)); }G_STMT_END
+ #define GTK_PRIVATE_UNSET_FLAG(wid,flag) G_STMT_START{ (GTK_PRIVATE_FLAGS (wid) &= ~(PRIVATE_ ## flag)); }G_STMT_END
- #endif /* G_OS_WIN32 */
+-#ifdef G_OS_WIN32
++#if defined G_OS_WIN32 || defined GDK_WINDOWING_QUARTZ
+
+ const gchar *_gtk_get_datadir ();
+ const gchar *_gtk_get_libdir ();
+@@ -93,7 +93,7 @@ const gchar *_gtk_get_data_prefix ();
+ #undef GTK_DATA_PREFIX
+ #define GTK_DATA_PREFIX _gtk_get_data_prefix ()
+
+-#endif /* G_OS_WIN32 */
++#endif /* G_OS_WIN32 || GDK_WINDOWING_QUARTZ */
-+/* Likewise for quartz */
-+#ifdef GDK_WINDOWING_QUARTZ
-+
-+const gchar *_gtk_quartz_get_datadir ();
-+const gchar *_gtk_quartz_get_libdir ();
-+const gchar *_gtk_quartz_get_sysconfdir ();
-+const gchar *_gtk_quartz_get_localedir ();
-+const gchar *_gtk_quartz_get_data_prefix ();
-+
-+#undef GTK_DATADIR
-+#define GTK_DATADIR _gtk_quartz_get_datadir ()
-+#undef GTK_LIBDIR
-+#define GTK_LIBDIR _gtk_quartz_get_libdir ()
-+#undef GTK_LOCALEDIR
-+#define GTK_LOCALEDIR _gtk_quartz_get_localedir ()
-+#undef GTK_SYSCONFDIR
-+#define GTK_SYSCONFDIR _gtk_quartz_get_sysconfdir ()
-+#undef GTK_DATA_PREFIX
-+#define GTK_DATA_PREFIX _gtk_quartz_get_data_prefix ()
-+
-+#endif /* GDK_WINDOWING_QUARTZ */
gboolean _gtk_fnmatch (const char *pattern,
const char *string,
- gboolean no_leading_period);
diff --git a/gtk/gtkquartz.c b/gtk/gtkquartz.c
-index 265d9ff..80b9e87 100644
+index 265d9ff..5b54104 100644
--- a/gtk/gtkquartz.c
+++ b/gtk/gtkquartz.c
@@ -322,3 +322,71 @@ _gtk_quartz_set_selection_data_for_pasteboard (NSPasteboard *pasteboard,
@@ -64,7 +66,7 @@ index 265d9ff..80b9e87 100644
+}
+
+const gchar *
-+_gtk_quartz_get_datadir (void)
++_gtk_get_datadir (void)
+{
+ gchar *resource_dir = get_bundle_path();
+ gchar *retval = g_build_filename(resource_dir, "share", NULL);
@@ -73,7 +75,7 @@ index 265d9ff..80b9e87 100644
+}
+
+const gchar *
-+_gtk_quartz_get_libdir (void)
++_gtk_get_libdir (void)
+{
+ gchar *resource_dir = get_bundle_path();
+ gchar *retval = g_build_filename(resource_dir, "lib", NULL);
@@ -82,7 +84,7 @@ index 265d9ff..80b9e87 100644
+}
+
+const gchar *
-+_gtk_quartz_get_localedir (void)
++_gtk_get_localedir (void)
+{
+
+ gchar *resource_dir = get_bundle_path();
@@ -92,7 +94,7 @@ index 265d9ff..80b9e87 100644
+}
+
+const gchar *
-+_gtk_quartz_get_sysconfdir (void)
++_gtk_get_sysconfdir (void)
+{
+ gchar *resource_dir = get_bundle_path();
+ gchar *retval = g_build_filename(resource_dir, "etc", NULL);
@@ -101,8 +103,11 @@ index 265d9ff..80b9e87 100644
+}
+
+const gchar *
-+_gtk_quartz_get_data_prefix (void)
++_gtk_get_data_prefix (void)
+{
+ return get_bundle_path();
+}
+
+--
+1.7.6.3.dirty
+
diff --git a/patches/0004-Bug-571582-GtkSelection-implementation-for-quartz.patch b/patches/0004-Bug-571582-GtkSelection-implementation-for-quartz.patch
new file mode 100644
index 0000000..0d8dbe9
--- /dev/null
+++ b/patches/0004-Bug-571582-GtkSelection-implementation-for-quartz.patch
@@ -0,0 +1,923 @@
+From 6118cdb88497c5531e64886d51a96fd24a895c61 Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Sun, 26 Dec 2010 13:48:47 -0800
+Subject: [PATCH 04/15] Bug 571582: GtkSelection implementation for quartz.
+
+---
+ gdk/quartz/gdkselection-quartz.c | 36 ++-
+ gtk/Makefile.am | 10 +-
+ gtk/gtkquartz.c | 1 +
+ gtk/gtkselection-quartz.c | 670 ++++++++++++++++++++++++++++++++++++++
+ gtk/gtkselection.c | 14 +-
+ 5 files changed, 711 insertions(+), 20 deletions(-)
+ create mode 100644 gtk/gtkselection-quartz.c
+
+diff --git a/gdk/quartz/gdkselection-quartz.c b/gdk/quartz/gdkselection-quartz.c
+index c327eb9..a51f567 100644
+--- a/gdk/quartz/gdkselection-quartz.c
++++ b/gdk/quartz/gdkselection-quartz.c
+@@ -32,7 +32,8 @@ gdk_selection_owner_set_for_display (GdkDisplay *display,
+ guint32 time,
+ gint send_event)
+ {
+- /* FIXME: Implement */
++ g_print ("Not a valid interface on Quartz. Use GtkSelection.\n");
++ g_return_val_if_reached(TRUE);
+ return TRUE;
+ }
+
+@@ -40,7 +41,7 @@ GdkWindow*
+ gdk_selection_owner_get_for_display (GdkDisplay *display,
+ GdkAtom selection)
+ {
+- /* FIXME: Implement */
++ /* Quartz doesn't have an X-selection, so it doesn't have a gdk_selection. */
+ return NULL;
+ }
+
+@@ -50,7 +51,9 @@ gdk_selection_convert (GdkWindow *requestor,
+ GdkAtom target,
+ guint32 time)
+ {
+- /* FIXME: Implement */
++ g_print ("Not a valid interface on Quartz. Use GtkSelection.\n");
++ g_return_if_reached();
++
+ }
+
+ gint
+@@ -59,7 +62,8 @@ gdk_selection_property_get (GdkWindow *requestor,
+ GdkAtom *ret_type,
+ gint *ret_format)
+ {
+- /* FIXME: Implement */
++ g_print ("Quartz windows do not support properties.\n");
++ g_return_val_if_reached(-1);
+ return 0;
+ }
+
+@@ -71,7 +75,8 @@ gdk_selection_send_notify_for_display (GdkDisplay *display,
+ GdkAtom property,
+ guint32 time)
+ {
+- /* FIXME: Implement */
++ g_print ("Not a valid interface on Quartz. Use GtkSelection.\n");
++ g_return_if_reached();
+ }
+
+ gint
+@@ -82,8 +87,9 @@ gdk_text_property_to_text_list_for_display (GdkDisplay *display,
+ gint length,
+ gchar ***list)
+ {
+- /* FIXME: Implement */
+- return 0;
++ /* text and utf8 are equivalent on OSX */
++ return gdk_text_property_to_utf8_list_for_display (display, encoding, format,
++ text, length, list);
+ }
+
+ gint
+@@ -94,20 +100,21 @@ gdk_string_to_compound_text_for_display (GdkDisplay *display,
+ guchar **ctext,
+ gint *length)
+ {
+- /* FIXME: Implement */
++ *ctext = (guchar*)g_strdup (str);
++ *length = strlen (str);
+ return 0;
+ }
+
+ void gdk_free_compound_text (guchar *ctext)
+ {
+- /* FIXME: Implement */
++ g_free (ctext);
+ }
+
+ gchar *
+ gdk_utf8_to_string_target (const gchar *str)
+ {
+- /* FIXME: Implement */
+- return NULL;
++ /* UTF8 is the standard string on OSX */
++ return g_strdup (str);
+ }
+
+ gboolean
+@@ -118,8 +125,11 @@ gdk_utf8_to_compound_text_for_display (GdkDisplay *display,
+ guchar **ctext,
+ gint *length)
+ {
+- /* FIXME: Implement */
+- return 0;
++ /* We don't use compound text on OSX, just stuff a copy of the string*/
++
++ *ctext = (guchar*)g_strdup (str);
++ *length = strlen (str);
++ return TRUE;
+ }
+
+ void
+diff --git a/gtk/Makefile.am b/gtk/Makefile.am
+index 78f4684..4794835 100644
+--- a/gtk/Makefile.am
++++ b/gtk/Makefile.am
+@@ -560,7 +560,6 @@ gtk_base_c_sources = \
+ gtkscalebutton.c \
+ gtkscrollbar.c \
+ gtkscrolledwindow.c \
+- gtkselection.c \
+ gtkseparator.c \
+ gtkseparatormenuitem.c \
+ gtkseparatortoolitem.c \
+@@ -759,10 +758,15 @@ endif
+ endif
+
+ if USE_QUARTZ
+-gtk_clipboard_dnd_c_sources = gtkclipboard-quartz.c gtkdnd-quartz.c gtkquartz.c
++gtk_clipboard_dnd_c_sources = \
++ gtkselection.c \
++ gtkselection-quartz.c \
++ gtkclipboard-quartz.c \
++ gtkdnd-quartz.c \
++ gtkquartz.c
+ gtk_clipboard_dnd_h_sources = gtkquartz.h
+ else
+-gtk_clipboard_dnd_c_sources = gtkclipboard.c gtkdnd.c
++gtk_clipboard_dnd_c_sources = gtkselection.c gtkclipboard.c gtkdnd.c
+ endif
+ EXTRA_DIST += gtkquartz.h
+
+diff --git a/gtk/gtkquartz.c b/gtk/gtkquartz.c
+index 5b54104..8ffeb0b 100644
+--- a/gtk/gtkquartz.c
++++ b/gtk/gtkquartz.c
+@@ -271,6 +271,7 @@ _gtk_quartz_set_selection_data_for_pasteboard (NSPasteboard *pasteboard,
+
+ type = target_to_pasteboard_type (target);
+ g_free (target);
++ g_return_if_fail (data != NULL);
+
+ if ([type isEqualTo:NSStringPboardType])
+ [pasteboard setString:[NSString stringWithUTF8String:(const char *)data]
+diff --git a/gtk/gtkselection-quartz.c b/gtk/gtkselection-quartz.c
+new file mode 100644
+index 0000000..1ce7d55
+--- /dev/null
++++ b/gtk/gtkselection-quartz.c
+@@ -0,0 +1,670 @@
++/* 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 "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)
++ {
++ GtkClipboard *clip = gtk_clipboard_get(selection_info->selection);
++ gtk_clipboard_clear(clip);
++ 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);
++}
++
+diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c
+index c2c9d97..01774dc 100644
+--- a/gtk/gtkselection.c
++++ b/gtk/gtkselection.c
+@@ -633,6 +633,7 @@ gtk_target_table_free (GtkTargetEntry *targets,
+ g_free (targets);
+ }
+
++#ifndef GDK_WINDOWING_QUARTZ /* Quartz handled by gtkselection-quartz.c */
+ /**
+ * gtk_selection_owner_set_for_display:
+ * @display: the #Gdkdisplay where the selection is set
+@@ -735,7 +736,7 @@ gtk_selection_owner_set_for_display (GdkDisplay *display,
+ else
+ return FALSE;
+ }
+-
++#endif /* GDK_WINDOWING_QUARTZ */
+ /**
+ * gtk_selection_owner_set:
+ * @widget: (allow-none): a #GtkWidget, or %NULL.
+@@ -937,7 +938,7 @@ gtk_selection_add_targets (GtkWidget *widget,
+ #endif
+ }
+
+-
++#ifndef GDK_WINDOWING_QUARTZ /* Quartz is handled in gtkselection-quartz.c */
+ /**
+ * gtk_selection_remove_all:
+ * @widget: a #GtkWidget
+@@ -998,8 +999,9 @@ gtk_selection_remove_all (GtkWidget *widget)
+ /* Remove all selection lists */
+ gtk_selection_target_list_remove (widget);
+ }
++#endif /* GDK_WINDOWING_QUARTZ */
+
+-
++#ifndef GDK_WINDOWING_QUARTZ /* Quartz is handled in gtkselection-quartz.c */
+ /**
+ * gtk_selection_convert:
+ * @widget: The widget which acts as requestor
+@@ -1111,7 +1113,7 @@ gtk_selection_convert (GtkWidget *widget,
+
+ return TRUE;
+ }
+-
++#endif /* GDK_WINDOWING_QUARTZ */
+ /**
+ * gtk_selection_data_get_selection:
+ * @selection_data: a pointer to a #GtkSelectionData structure.
+@@ -2184,6 +2186,7 @@ gtk_selection_init (void)
+ initialize = FALSE;
+ }
+
++#ifndef GDK_WINDOWING_QUARTZ /* Quartz handled by gtkselection-quartz.c */
+ /**
+ * gtk_selection_clear:
+ * @widget: a #GtkWidget
+@@ -2622,6 +2625,7 @@ _gtk_selection_incr_event (GdkWindow *window,
+
+ return TRUE;
+ }
++#endif /* GDK_WINDOWING_QUARTZ */
+
+ /*************************************************************
+ * gtk_selection_incr_timeout:
+@@ -2676,6 +2680,7 @@ gtk_selection_incr_timeout (GtkIncrInfo *info)
+ return retval;
+ }
+
++#ifndef GDK_WINDOWING_QUARTZ /* Quartz handled by gtkselection-quartz.c */
+ /*************************************************************
+ * _gtk_selection_notify:
+ * Handler for "selection-notify-event" signals on windows
+@@ -2869,6 +2874,7 @@ _gtk_selection_property_notify (GtkWidget *widget,
+
+ return TRUE;
+ }
++#endif /* GDK_WINDOWING_QUARTZ */
+
+ /*************************************************************
+ * gtk_selection_retrieval_timeout:
+--
+1.7.6.3.dirty
+
diff --git a/patches/0005-Bug-657770-Write-to-released-memory-in-gtkdnd-quartz.patch b/patches/0005-Bug-657770-Write-to-released-memory-in-gtkdnd-quartz.patch
new file mode 100644
index 0000000..a221675
--- /dev/null
+++ b/patches/0005-Bug-657770-Write-to-released-memory-in-gtkdnd-quartz.patch
@@ -0,0 +1,52 @@
+From 1280340f539644de8e8766d76bcbd65b253bb346 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris gtk org>
+Date: Sat, 24 Sep 2011 17:32:21 -0700
+Subject: [PATCH 05/15] Bug 657770 - Write to released memory in
+ gtkdnd-quartz.c
+
+Clear the Drag paste board just before the info->context is released.
+This way the GtkDragSourceOwner is released just before the drag context
+is and thus can pasteboard:provideDataForType: not accidentally access
+an already released drag context
+---
+ gtk/gtkdnd-quartz.c | 14 ++++++++++++++
+ 1 files changed, 14 insertions(+), 0 deletions(-)
+
+diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c
+index 8dd47b9..5688568 100644
+--- a/gtk/gtkdnd-quartz.c
++++ b/gtk/gtkdnd-quartz.c
+@@ -1835,6 +1835,9 @@ gtk_drag_set_default_icon (GdkColormap *colormap,
+ static void
+ gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
+ {
++ NSPasteboard *pasteboard;
++ NSAutoreleasePool *pool;
++
+ if (info->icon_pixbuf)
+ g_object_unref (info->icon_pixbuf);
+
+@@ -1849,10 +1852,21 @@ gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
+
+ gtk_target_list_unref (info->target_list);
+
++ pool = [[NSAutoreleasePool alloc] init];
++
++ /* Empty the pasteboard, so that it will not accidentally access
++ * info->context after it has been destroyed.
++ */
++ pasteboard = [NSPasteboard pasteboardWithName: NSDragPboard];
++ [pasteboard declareTypes: nil owner: nil];
++
++ [pool release];
++
+ gtk_drag_clear_source_info (info->context);
+ g_object_unref (info->context);
+
+ g_free (info);
++ info = NULL;
+ }
+
+ static gboolean
+
+
diff --git a/patches/0006-Bug-658722-Drag-and-Drop-sometimes-stops-working.patch b/patches/0006-Bug-658722-Drag-and-Drop-sometimes-stops-working.patch
new file mode 100644
index 0000000..b0a1ef5
--- /dev/null
+++ b/patches/0006-Bug-658722-Drag-and-Drop-sometimes-stops-working.patch
@@ -0,0 +1,274 @@
+From dcd9ab00c64a1df63fd5fa58c2ca25efd9b3e09e Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Sat, 24 Sep 2011 18:14:09 -0700
+Subject: [PATCH 06/15] Bug 658722 - Drag and Drop sometimes stops working
+
+First, rather than assuming that there's already an event queued up if
+_gdk_quartz_drag_source_context isn't NULL, assume that it just didn't get
+cleaned up the last time it ran and abort it.
+
+This naturally requires implementing gdk_quartz_drag_abort(), so remove the
+code from [GdkQuartzNSWindow draggedImage:endedAt:operation:] and move it to
+gdkdnd_quartz.c as static void gdk_quartz_drag_end(). Implement both
+gdk_quartz_drag_drop() and gdk_quartz_drag_abort() by calling
+gdk_quartz_drag_end().
+
+Next, try to get rid of the memory cycle between gtk_drag_source_info.context
+and _gdk_quartz_drag_source_context (which carries the GtkQuartzDragSourceInfo
+struct as qdata). Replace gtk_drag_source_clear_info() by using a
+g_object_set_qdata_full() for context in gtk_drag_get_source_context, calling
+gtk_drag_source_info_destroy() as the destructor. This eliminates the need to
+queue a cleanup idle event. I use g_object_run_dispose() on
+_gtk_quartz_drag_source_context to force the deletion of the info stored as
+qdata, which in turn unrefs the info->context pointer. Ordinarily this gets
+fired off from draggedImage:endedAt:operation:, meaning that the special
+dragging CFRunLoop is complete and NSEvents are again flowing, so queuing a
+cleanup event isn't necessary. The advantage is that it can also be run from
+gdk_drag_abort, so if Gdk thinks there's a drag but CF doesn't all of the
+memory still gets cleaned up.
+---
+ gdk/quartz/GdkQuartzWindow.c | 16 +-------
+ gdk/quartz/gdkdnd-quartz.c | 35 +++++++++++++--
+ gtk/gtkdnd-quartz.c | 96 ++++++++++++++++-------------------------
+ 3 files changed, 69 insertions(+), 78 deletions(-)
+
+diff --git a/gdk/quartz/GdkQuartzWindow.c b/gdk/quartz/GdkQuartzWindow.c
+index dcd7250..20ed80e 100644
+--- a/gdk/quartz/GdkQuartzWindow.c
++++ b/gdk/quartz/GdkQuartzWindow.c
+@@ -560,21 +560,7 @@ update_context_from_dragging_info (id <NSDraggingInfo> sender)
+
+ - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
+ {
+- GdkEvent event;
+-
+- g_assert (_gdk_quartz_drag_source_context != NULL);
+-
+- event.dnd.type = GDK_DROP_FINISHED;
+- event.dnd.window = g_object_ref ([[self contentView] gdkWindow]);
+- event.dnd.send_event = FALSE;
+- event.dnd.context = _gdk_quartz_drag_source_context;
+-
+- (*_gdk_event_func) (&event, _gdk_event_data);
+-
+- g_object_unref (event.dnd.window);
+-
+- g_object_unref (_gdk_quartz_drag_source_context);
+- _gdk_quartz_drag_source_context = NULL;
++ gdk_drag_drop (_gdk_quartz_drag_source_context, (guint32)g_get_real_time());
+ }
+
+ @end
+diff --git a/gdk/quartz/gdkdnd-quartz.c b/gdk/quartz/gdkdnd-quartz.c
+index bb70b71..143a8ed 100644
+--- a/gdk/quartz/gdkdnd-quartz.c
++++ b/gdk/quartz/gdkdnd-quartz.c
+@@ -111,11 +111,20 @@ GdkDragContext *
+ gdk_drag_begin (GdkWindow *window,
+ GList *targets)
+ {
+- g_assert (_gdk_quartz_drag_source_context == NULL);
++ if (_gdk_quartz_drag_source_context != NULL)
++ {
++ /* Something is amiss with the existing drag, so log a message
++ and abort it */
++ g_warning ("Drag begun with existing context; aborting the preexisting drag");
++ gdk_drag_abort (_gdk_quartz_drag_source_context,
++ (guint32)g_get_real_time ());
++ }
++
+
+ /* Create fake context */
+ _gdk_quartz_drag_source_context = gdk_drag_context_new ();
+ _gdk_quartz_drag_source_context->is_source = TRUE;
++ _gdk_quartz_drag_source_context->source_window = window;
+
+ return _gdk_quartz_drag_source_context;
+ }
+@@ -155,20 +164,36 @@ gdk_drag_find_window_for_screen (GdkDragContext *context,
+ /* FIXME: Implement */
+ }
+
++static void
++gdk_quartz_drag_end (GdkDragContext *context)
++{
++ GdkEvent event;
++
++ g_assert (context != NULL);
++ event.dnd.type = GDK_DROP_FINISHED;
++ event.dnd.window = g_object_ref (context->source_window);
++ event.dnd.send_event = FALSE;
++ event.dnd.context = context;
++
++ (*_gdk_event_func) (&event, _gdk_event_data);
++
++ g_object_run_dispose (_gdk_quartz_drag_source_context);
++ _gdk_quartz_drag_source_context = NULL;
++}
++
+ void
+ gdk_drag_drop (GdkDragContext *context,
+ guint32 time)
+ {
+- /* FIXME: Implement */
++ gdk_quartz_drag_end (context);
+ }
+
+ void
+ gdk_drag_abort (GdkDragContext *context,
+ guint32 time)
+ {
+- g_return_if_fail (context != NULL);
+-
+- /* FIXME: Implement */
++ g_warning ("Gdk-quartz-drag-drop, aborting\n");
++ gdk_quartz_drag_end (context);
+ }
+
+ void
+diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c
+index 5688568..be92a22 100644
+--- a/gtk/gtkdnd-quartz.c
++++ b/gtk/gtkdnd-quartz.c
+@@ -269,6 +269,39 @@ gtk_drag_dest_info_destroy (gpointer data)
+ g_free (info);
+ }
+
++static void
++gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
++{
++ NSPasteboard *pasteboard;
++ NSAutoreleasePool *pool;
++
++ if (info->icon_pixbuf)
++ g_object_unref (info->icon_pixbuf);
++
++ g_signal_emit_by_name (info->widget, "drag-end",
++ info->context);
++
++ if (info->source_widget)
++ g_object_unref (info->source_widget);
++
++ if (info->widget)
++ g_object_unref (info->widget);
++
++ gtk_target_list_unref (info->target_list);
++
++ pool = [[NSAutoreleasePool alloc] init];
++
++ /* Empty the pasteboard, so that it will not accidentally access
++ * info->context after it has been destroyed.
++ */
++ pasteboard = [NSPasteboard pasteboardWithName: NSDragPboard];
++ [pasteboard declareTypes: nil owner: nil];
++
++ [pool release];
++
++ g_free (info);
++}
++
+ static GtkDragDestInfo *
+ gtk_drag_get_dest_info (GdkDragContext *context,
+ gboolean create)
+@@ -308,18 +341,14 @@ gtk_drag_get_source_info (GdkDragContext *context,
+ {
+ info = g_new0 (GtkDragSourceInfo, 1);
+ info->context = context;
+- g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
++ g_object_ref (info->context);
++ g_object_set_qdata_full (G_OBJECT (context), dest_info_quark,
++ info, gtk_drag_source_info_destroy);
+ }
+
+ return info;
+ }
+
+-static void
+-gtk_drag_clear_source_info (GdkDragContext *context)
+-{
+- g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
+-}
+-
+ GtkWidget *
+ gtk_drag_get_source_widget (GdkDragContext *context)
+ {
+@@ -1089,7 +1118,8 @@ gtk_drag_begin_idle (gpointer arg)
+ [owner release];
+ [types release];
+
+- if ((nswindow = get_toplevel_nswindow (info->source_widget)) == NULL)
++ if (info->source_widget == NULL
++ || (nswindow = get_toplevel_nswindow (info->source_widget)) == NULL)
+ return FALSE;
+
+ /* Ref the context. It's unreffed when the drag has been aborted */
+@@ -1108,7 +1138,6 @@ gtk_drag_begin_idle (gpointer arg)
+ source:nswindow
+ slideBack:YES];
+
+- [info->nsevent release];
+ [drag_image release];
+
+ [pool release];
+@@ -1833,61 +1862,12 @@ gtk_drag_set_default_icon (GdkColormap *colormap,
+ }
+
+ static void
+-gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
+-{
+- NSPasteboard *pasteboard;
+- NSAutoreleasePool *pool;
+-
+- if (info->icon_pixbuf)
+- g_object_unref (info->icon_pixbuf);
+-
+- g_signal_emit_by_name (info->widget, "drag-end",
+- info->context);
+-
+- if (info->source_widget)
+- g_object_unref (info->source_widget);
+-
+- if (info->widget)
+- g_object_unref (info->widget);
+-
+- gtk_target_list_unref (info->target_list);
+-
+- pool = [[NSAutoreleasePool alloc] init];
+-
+- /* Empty the pasteboard, so that it will not accidentally access
+- * info->context after it has been destroyed.
+- */
+- pasteboard = [NSPasteboard pasteboardWithName: NSDragPboard];
+- [pasteboard declareTypes: nil owner: nil];
+-
+- [pool release];
+-
+- gtk_drag_clear_source_info (info->context);
+- g_object_unref (info->context);
+-
+- g_free (info);
+- info = NULL;
+-}
+-
+-static gboolean
+-drag_drop_finished_idle_cb (gpointer data)
+-{
+- gtk_drag_source_info_destroy (data);
+- return FALSE;
+-}
+-
+-static void
+ gtk_drag_drop_finished (GtkDragSourceInfo *info)
+ {
+ if (info->success && info->delete)
+ g_signal_emit_by_name (info->source_widget, "drag-data-delete",
+ info->context);
+
+- /* Workaround for the fact that the NS API blocks until the drag is
+- * over. This way the context is still valid when returning from
+- * drag_begin, even if it will still be quite useless. See bug #501588.
+- */
+- g_idle_add (drag_drop_finished_idle_cb, info);
+ }
+
+ /*************************************************************
diff --git a/patches/0007-Bug-658767-Drag-and-Drop-NSEvent-capture-is-racy.patch b/patches/0007-Bug-658767-Drag-and-Drop-NSEvent-capture-is-racy.patch
new file mode 100644
index 0000000..2bbc098
--- /dev/null
+++ b/patches/0007-Bug-658767-Drag-and-Drop-NSEvent-capture-is-racy.patch
@@ -0,0 +1,78 @@
+From 592a11566aa832e8ad80a389fcb19aa7e634f6e5 Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Sat, 24 Sep 2011 18:19:56 -0700
+Subject: [PATCH 07/15] Bug 658767 - Drag and Drop NSEvent capture is racy
+
+Create a synthetic NSMouseLeftDown to store in the GtkQuartzDragSourceInfo
+rather than relying on the NSWindow's latest event being the right one (or the
+right kind).
+---
+ gtk/gtkdnd-quartz.c | 43 +++++++++++++++++++++++++++++++++++--------
+ 1 files changed, 35 insertions(+), 8 deletions(-)
+
+diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c
+index be92a22..084aada 100644
+--- a/gtk/gtkdnd-quartz.c
++++ b/gtk/gtkdnd-quartz.c
+@@ -1155,13 +1155,44 @@ gtk_drag_begin_internal (GtkWidget *widget,
+ {
+ GtkDragSourceInfo *info;
+ GdkDragContext *context;
+- NSWindow *nswindow;
+-
+- context = gdk_drag_begin (NULL, NULL);
++ NSWindow *nswindow = get_toplevel_nswindow (widget);
++ NSPoint point = {0, 0};
++ gdouble x, y;
++ double time = (double)g_get_real_time ();
++ NSEvent *nsevent;
++ NSTimeInterval nstime;
++
++ if (event)
++ {
++ if (gdk_event_get_coords (event, &x, &y))
++ {
++ point.x = x;
++ point.y = y;
++ }
++ time = (double)gdk_event_get_time (event);
++ }
++ nstime = [[NSDate dateWithTimeIntervalSince1970: time / 1000] timeIntervalSinceReferenceDate];
++ nsevent = [NSEvent mouseEventWithType: NSLeftMouseDown
++ location: point
++ modifierFlags: 0
++ timestamp: nstime
++ windowNumber: [nswindow windowNumber]
++ context: [nswindow graphicsContext]
++ eventNumber: 0
++ clickCount: 1
++ pressure: 0.0 ];
++
++ GdkWindow *window = [[nswindow contentView] gdkWindow];
++ g_return_val_if_fail(nsevent != NULL, NULL);
++
++ context = gdk_drag_begin (window, NULL);
++ g_return_val_if_fail( context != NULL, NULL);
+ context->is_source = TRUE;
+
+ info = gtk_drag_get_source_info (context, TRUE);
+-
++ info->nsevent = nsevent;
++ [info->nsevent retain];
++
+ info->source_widget = g_object_ref (widget);
+ info->widget = g_object_ref (widget);
+ info->target_list = target_list;
+@@ -1228,10 +1259,6 @@ gtk_drag_begin_internal (GtkWidget *widget,
+ }
+ }
+
+- nswindow = get_toplevel_nswindow (widget);
+- info->nsevent = [nswindow currentEvent];
+- [info->nsevent retain];
+-
+ /* drag will begin in an idle handler to avoid nested run loops */
+
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gtk_drag_begin_idle, context, NULL);
+
+
diff --git a/patches/0008-Implement-GtkDragSourceOwner-pasteboardChangedOwner.patch b/patches/0008-Implement-GtkDragSourceOwner-pasteboardChangedOwner.patch
new file mode 100644
index 0000000..5b9153c
--- /dev/null
+++ b/patches/0008-Implement-GtkDragSourceOwner-pasteboardChangedOwner.patch
@@ -0,0 +1,39 @@
+From 259563958047ccbf6f61578f2d724fc731218304 Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Sun, 25 Sep 2011 12:03:54 -0700
+Subject: [PATCH 08/15] Implement GtkDragSourceOwner pasteboardChangedOwner:
+
+---
+ gtk/gtkdnd-quartz.c | 11 +++++++++++
+ 1 files changed, 11 insertions(+), 0 deletions(-)
+
+diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c
+index 084aada..21ce11a 100644
+--- a/gtk/gtkdnd-quartz.c
++++ b/gtk/gtkdnd-quartz.c
+@@ -149,6 +149,8 @@ struct _GtkDragFindData
+ guint target_info;
+ GtkSelectionData selection_data;
+
++ g_return_if_fail(info->source_widget != NULL);
++ g_return_if_fail(info->target_list != NULL);
+ selection_data.selection = GDK_NONE;
+ selection_data.data = NULL;
+ selection_data.length = -1;
+@@ -171,6 +173,15 @@ struct _GtkDragFindData
+ }
+ }
+
++- (void)pasteboardChangedOwner: (NSPasteboard*)sender
++{
++ if (!info) return;
++
++ info->target_list = NULL;
++ info->widget = NULL;
++ info->source_widget = NULL;
++}
++
+ - (id)initWithInfo:(GtkDragSourceInfo *)anInfo
+ {
+ self = [super init];
+
diff --git a/patches/0009-Implement-recent-items-in-Filechooser.patch b/patches/0009-Implement-recent-items-in-Filechooser.patch
new file mode 100644
index 0000000..34b4a1c
--- /dev/null
+++ b/patches/0009-Implement-recent-items-in-Filechooser.patch
@@ -0,0 +1,880 @@
+From 7ec6fc1e4161f798de9a5e6f82855ad16c60b5d4 Mon Sep 17 00:00:00 2001
+From: Federico Mena Quintero <federico gnome org>
+Date: Tue, 26 Jul 2011 16:00:35 -0500
+Subject: [PATCH 09/15] Implement recent items in Filechooser
+
+---
+ gtk/gtkfilechooserdefault.c | 563 ++++++++++++++++---------------------------
+ gtk/gtkfilechooserprivate.h | 1 +
+ 2 files changed, 208 insertions(+), 356 deletions(-)
+
+diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
+index 2ee7903..9e636c2 100644
+--- a/gtk/gtkfilechooserdefault.c
++++ b/gtk/gtkfilechooserdefault.c
+@@ -401,22 +401,23 @@ static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
+
+ static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
+ gboolean remove_from_treeview);
++
++static void search_setup_widgets (GtkFileChooserDefault *impl);
+ static void search_stop_searching (GtkFileChooserDefault *impl,
+ gboolean remove_query);
+ static void search_clear_model (GtkFileChooserDefault *impl,
+ gboolean remove_from_treeview);
+ static gboolean search_should_respond (GtkFileChooserDefault *impl);
+-static void search_switch_to_browse_mode (GtkFileChooserDefault *impl);
+ static GSList *search_get_selected_files (GtkFileChooserDefault *impl);
+ static void search_entry_activate_cb (GtkEntry *entry,
+ gpointer data);
+ static void settings_load (GtkFileChooserDefault *impl);
+
++static void recent_start_loading (GtkFileChooserDefault *impl);
+ static void recent_stop_loading (GtkFileChooserDefault *impl);
+ static void recent_clear_model (GtkFileChooserDefault *impl,
+ gboolean remove_from_treeview);
+ static gboolean recent_should_respond (GtkFileChooserDefault *impl);
+-static void recent_switch_to_browse_mode (GtkFileChooserDefault *impl);
+ static GSList * recent_get_selected_files (GtkFileChooserDefault *impl);
+ static void set_file_system_backend (GtkFileChooserDefault *impl);
+ static void unset_file_system_backend (GtkFileChooserDefault *impl);
+@@ -2322,23 +2323,9 @@ renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
+ static GtkWidget *
+ filter_create (GtkFileChooserDefault *impl)
+ {
+- GtkCellRenderer *cell;
+- GList *cells;
+-
+ impl->filter_combo = gtk_combo_box_text_new ();
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
+
+- /* Get the combo's text renderer and set ellipsize parameters */
+- cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (impl->filter_combo));
+- g_assert (cells);
+- cell = cells->data;
+-
+- g_object_set (G_OBJECT (cell),
+- "ellipsize", PANGO_ELLIPSIZE_END,
+- NULL);
+-
+- g_list_free (cells);
+-
+ g_signal_connect (impl->filter_combo, "changed",
+ G_CALLBACK (filter_combo_changed), impl);
+
+@@ -3692,7 +3679,7 @@ key_is_left_or_right (GdkEventKey *event)
+
+ /* Handles key press events on the file list, so that we can trap Enter to
+ * activate the default button on our own. Also, checks to see if '/' has been
+- * pressed. See comment by tree_view_keybinding_cb() for more details.
++ * pressed.
+ */
+ static gboolean
+ browse_files_key_press_event_cb (GtkWidget *widget,
+@@ -4130,7 +4117,9 @@ typedef struct {
+ gint model_column;
+ } ColumnMap;
+
+-/* Sets the sort column IDs for the file list based on the operation mode */
++/* Sets the sort column IDs for the file list; needs to be done whenever we
++ * change the model on the treeview.
++ */
+ static void
+ file_list_set_sort_column_ids (GtkFileChooserDefault *impl)
+ {
+@@ -4368,6 +4357,20 @@ file_pane_create (GtkFileChooserDefault *impl,
+ return vbox;
+ }
+
++static void
++location_entry_create (GtkFileChooserDefault *impl)
++{
++ if (!impl->location_entry)
++ impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
++
++ _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
++ impl->file_system);
++ _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
++ _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
++ gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
++ gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
++}
++
+ /* Creates the widgets specific to Save mode */
+ static void
+ save_widgets_create (GtkFileChooserDefault *impl)
+@@ -4400,12 +4403,7 @@ save_widgets_create (GtkFileChooserDefault *impl)
+
+ /* Location entry */
+
+- impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
+- _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+- impl->file_system);
+- _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
+- gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
+- gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
++ location_entry_create (impl);
+ gtk_table_attach (GTK_TABLE (impl->save_widgets_table), impl->location_entry,
+ 1, 2, 0, 1,
+ GTK_EXPAND | GTK_FILL, 0,
+@@ -4457,53 +4455,6 @@ location_switch_to_path_bar (GtkFileChooserDefault *impl)
+ gtk_widget_hide (impl->location_entry_box);
+ }
+
+-/* Sets the full path of the current folder as the text in the location entry. */
+-static void
+-location_entry_set_initial_text (GtkFileChooserDefault *impl)
+-{
+- gchar *text, *filename;
+-
+- if (!impl->current_folder)
+- return;
+-
+- filename = g_file_get_path (impl->current_folder);
+-
+- if (filename)
+- {
+- text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+- g_free (filename);
+- }
+- else
+- text = g_file_get_uri (impl->current_folder);
+-
+- if (text)
+- {
+- gboolean need_slash;
+- int len;
+-
+- len = strlen (text);
+- need_slash = (text[len - 1] != G_DIR_SEPARATOR);
+-
+- if (need_slash)
+- {
+- char *slash_text;
+-
+- slash_text = g_new (char, len + 2);
+- strcpy (slash_text, text);
+- slash_text[len] = G_DIR_SEPARATOR;
+- slash_text[len + 1] = 0;
+-
+- g_free (text);
+- text = slash_text;
+- }
+-
+- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), text);
+- g_free (text);
+- }
+-
+- g_free (filename);
+-}
+-
+ /* Turns on the location entry. Can be called even if we are already in that
+ * mode.
+ */
+@@ -4519,7 +4470,10 @@ location_switch_to_filename_entry (GtkFileChooserDefault *impl)
+ return;
+
+ if (impl->location_entry)
+- gtk_widget_destroy (impl->location_entry);
++ {
++ gtk_widget_destroy (impl->location_entry);
++ impl->location_entry = NULL;
++ }
+
+ /* Box */
+
+@@ -4527,19 +4481,13 @@ location_switch_to_filename_entry (GtkFileChooserDefault *impl)
+
+ /* Entry */
+
+- impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
+- _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+- impl->file_system);
+- gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
+- _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
+-
++ location_entry_create (impl);
+ gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_entry, TRUE, TRUE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (impl->location_label), impl->location_entry);
+
+ /* Configure the entry */
+
+ _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
+- _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
+
+ /* Done */
+
+@@ -4892,9 +4840,13 @@ browse_widgets_create (GtkFileChooserDefault *impl)
+ GtkWidget *widget;
+ GtkSizeGroup *size_group;
+
+- /* size group is used by the scrolled windows of the panes */
+- size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+ impl->browse_widgets_box = gtk_vbox_new (FALSE, 12);
++ gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets_box, TRUE, TRUE, 0);
++ gtk_widget_show (impl->browse_widgets_box);
++
++ impl->browse_header_box = gtk_vbox_new (FALSE, 12);
++ gtk_box_pack_start (GTK_BOX (impl->browse_widgets_box), impl->browse_header_box, FALSE, FALSE, 0);
++ gtk_widget_show (impl->browse_header_box);
+
+ /* Path bar, info bar, and their respective machinery - the browse_path_bar_hbox will get packed elsewhere */
+ path_bar_widgets_create (impl);
+@@ -4902,12 +4854,15 @@ browse_widgets_create (GtkFileChooserDefault *impl)
+ /* Box for the location label and entry */
+
+ impl->location_entry_box = gtk_hbox_new (FALSE, 12);
+- gtk_box_pack_start (GTK_BOX (impl->browse_widgets_box), impl->location_entry_box, FALSE, FALSE, 0);
++ gtk_box_pack_start (GTK_BOX (impl->browse_header_box), impl->location_entry_box, FALSE, FALSE, 0);
+
+ impl->location_label = gtk_label_new_with_mnemonic (_("_Location:"));
+ gtk_widget_show (impl->location_label);
+ gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
+
++ /* size group is used by the scrolled windows of the panes */
++ size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
++
+ /* Paned widget */
+ hpaned = gtk_hpaned_new ();
+ gtk_widget_show (hpaned);
+@@ -4945,7 +4900,6 @@ gtk_file_chooser_default_constructor (GType type,
+
+ /* The browse widgets */
+ browse_widgets_create (impl);
+- gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets_box, TRUE, TRUE, 0);
+
+ /* Alignment to hold extra widget */
+ impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
+@@ -5116,8 +5070,8 @@ restore_path_bar (GtkFileChooserDefault *impl)
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+ || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+- gtk_box_pack_start (GTK_BOX (impl->browse_widgets_box), impl->browse_path_bar_hbox, FALSE, FALSE, 0);
+- gtk_box_reorder_child (GTK_BOX (impl->browse_widgets_box), impl->browse_path_bar_hbox, 0);
++ gtk_box_pack_start (GTK_BOX (impl->browse_header_box), impl->browse_path_bar_hbox, FALSE, FALSE, 0);
++ gtk_box_reorder_child (GTK_BOX (impl->browse_header_box), impl->browse_path_bar_hbox, 0);
+ }
+ else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+ || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+@@ -5199,6 +5153,140 @@ path_bar_update (GtkFileChooserDefault *impl)
+ path_bar_set_mode (impl, mode);
+ }
+
++static void
++operation_mode_discard_search_widgets (GtkFileChooserDefault *impl)
++{
++ if (impl->search_hbox)
++ {
++ gtk_widget_destroy (impl->search_hbox);
++
++ impl->search_hbox = NULL;
++ impl->search_entry = NULL;
++ }
++}
++
++/* Stops running operations like populating the browse model, searches, and the recent-files model */
++static void
++operation_mode_stop (GtkFileChooserDefault *impl, OperationMode mode)
++{
++ switch (mode)
++ {
++ case OPERATION_MODE_BROWSE:
++ stop_loading_and_clear_list_model (impl, TRUE);
++ break;
++
++ case OPERATION_MODE_SEARCH:
++ search_stop_searching (impl, FALSE);
++ search_clear_model (impl, TRUE);
++
++ operation_mode_discard_search_widgets (impl);
++ break;
++
++ case OPERATION_MODE_RECENT:
++ recent_stop_loading (impl);
++ recent_clear_model (impl, TRUE);
++ break;
++
++ default:
++ g_assert_not_reached ();
++ }
++}
++
++static void
++operation_mode_set_browse (GtkFileChooserDefault *impl)
++{
++ path_bar_update (impl);
++
++ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
++ impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
++ {
++ gtk_widget_show (impl->location_button);
++ location_mode_set (impl, impl->location_mode, TRUE);
++
++ if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
++ gtk_widget_show (impl->location_entry_box);
++ }
++}
++
++static void
++operation_mode_set_search (GtkFileChooserDefault *impl)
++{
++ g_assert (impl->search_hbox == NULL);
++ g_assert (impl->search_entry == NULL);
++ g_assert (impl->search_model == NULL);
++
++ search_setup_widgets (impl);
++}
++
++static void
++operation_mode_set_recent (GtkFileChooserDefault *impl)
++{
++ path_bar_update (impl);
++
++ /* Hide the location widgets temporarily */
++ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
++ impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
++ {
++ gtk_widget_hide (impl->location_button);
++ gtk_widget_hide (impl->location_entry_box);
++ }
++
++ recent_start_loading (impl);
++}
++
++/* Sometimes we need to frob the selection in the shortcuts list manually */
++static void
++shortcuts_select_item_without_activating (GtkFileChooserDefault *impl, int pos)
++{
++ GtkTreeSelection *selection;
++ GtkTreePath *path;
++
++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
++
++ g_signal_handlers_block_by_func (selection, G_CALLBACK (shortcuts_selection_changed_cb), impl);
++
++ path = gtk_tree_path_new_from_indices (pos, -1);
++ gtk_tree_selection_select_path (selection, path);
++ gtk_tree_path_free (path);
++
++ g_signal_handlers_unblock_by_func (selection, G_CALLBACK (shortcuts_selection_changed_cb), impl);
++}
++
++static void
++operation_mode_set (GtkFileChooserDefault *impl, OperationMode mode)
++{
++ ShortcutsIndex shortcut_to_select;
++
++ operation_mode_stop (impl, impl->operation_mode);
++
++ impl->operation_mode = mode;
++
++ switch (impl->operation_mode)
++ {
++ case OPERATION_MODE_BROWSE:
++ operation_mode_set_browse (impl);
++ shortcut_to_select = SHORTCUTS_CURRENT_FOLDER;
++ break;
++
++ case OPERATION_MODE_SEARCH:
++ operation_mode_set_search (impl);
++ shortcut_to_select = SHORTCUTS_SEARCH;
++ break;
++
++ case OPERATION_MODE_RECENT:
++ operation_mode_set_recent (impl);
++ shortcut_to_select = SHORTCUTS_RECENT;
++ break;
++
++ default:
++ g_assert_not_reached ();
++ return;
++ }
++
++ if (shortcut_to_select != SHORTCUTS_CURRENT_FOLDER)
++ shortcuts_select_item_without_activating (impl, shortcuts_get_index (impl, shortcut_to_select));
++}
++
+ /* This function is basically a do_all function.
+ *
+ * It sets the visibility on all the widgets based on the current state, and
+@@ -5236,7 +5324,6 @@ update_appearance (GtkFileChooserDefault *impl)
+ {
+ gtk_widget_show (impl->location_button);
+ save_widgets_destroy (impl);
+- gtk_widget_show (impl->browse_widgets_box);
+ location_mode_set (impl, impl->location_mode, TRUE);
+ }
+
+@@ -6070,6 +6157,7 @@ load_set_model (GtkFileChooserDefault *impl)
+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
+ MODEL_COL_NAME);
++ file_list_set_sort_column_ids (impl);
+ set_sort_column (impl);
+ profile_msg (" gtk_tree_view_set_model end", NULL);
+ impl->list_sort_ascending = TRUE;
+@@ -7095,17 +7183,7 @@ gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
+
+ g_object_ref (file);
+
+- switch (impl->operation_mode)
+- {
+- case OPERATION_MODE_SEARCH:
+- search_switch_to_browse_mode (impl);
+- break;
+- case OPERATION_MODE_RECENT:
+- recent_switch_to_browse_mode (impl);
+- break;
+- case OPERATION_MODE_BROWSE:
+- break;
+- }
++ operation_mode_set (impl, OPERATION_MODE_BROWSE);
+
+ if (impl->local_only && !g_file_is_native (file))
+ {
+@@ -8747,7 +8825,8 @@ gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+- if (impl->location_mode == LOCATION_MODE_PATH_BAR)
++ if (impl->location_mode == LOCATION_MODE_PATH_BAR
++ || impl->operation_mode == OPERATION_MODE_RECENT)
+ widget = impl->browse_files_tree_view;
+ else
+ widget = impl->location_entry;
+@@ -8865,6 +8944,7 @@ search_engine_finished_cb (GtkSearchEngine *engine,
+ */
+ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
+ GTK_TREE_MODEL (impl->search_model));
++ file_list_set_sort_column_ids (impl);
+ #endif
+
+ /* FMQ: if search was empty, say that we got no hits */
+@@ -8934,34 +9014,6 @@ search_stop_searching (GtkFileChooserDefault *impl,
+ }
+ }
+
+-/* Stops any pending searches, clears the file list, and switches back to OPERATION_MODE_BROWSE */
+-static void
+-search_switch_to_browse_mode (GtkFileChooserDefault *impl)
+-{
+- g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
+-
+- search_stop_searching (impl, FALSE);
+- search_clear_model (impl, TRUE);
+-
+- gtk_widget_destroy (impl->search_hbox);
+- impl->search_hbox = NULL;
+- impl->search_entry = NULL;
+-
+- impl->operation_mode = OPERATION_MODE_BROWSE;
+- path_bar_update (impl);
+-
+- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+- impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+- {
+- gtk_widget_show (impl->location_button);
+-
+- if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
+- gtk_widget_show (impl->location_entry_box);
+- }
+-
+- file_list_set_sort_column_ids (impl);
+-}
+-
+ /* Creates the search_model and puts it in the tree view */
+ static void
+ search_setup_model (GtkFileChooserDefault *impl)
+@@ -8992,6 +9044,7 @@ search_setup_model (GtkFileChooserDefault *impl)
+ */
+ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
+ GTK_TREE_MODEL (impl->search_model));
++ file_list_set_sort_column_ids (impl);
+ }
+
+ /* Creates a new query with the specified text and launches it */
+@@ -9141,79 +9194,6 @@ search_setup_widgets (GtkFileChooserDefault *impl)
+ /* FMQ: hide the filter combo? */
+ }
+
+-/* Stops running operations like populating the browse model, searches, and the recent-files model */
+-static void
+-stop_operation (GtkFileChooserDefault *impl, OperationMode mode)
+-{
+- switch (mode)
+- {
+- case OPERATION_MODE_BROWSE:
+- stop_loading_and_clear_list_model (impl, TRUE);
+- break;
+-
+- case OPERATION_MODE_SEARCH:
+- search_stop_searching (impl, FALSE);
+- search_clear_model (impl, TRUE);
+-
+- gtk_widget_destroy (impl->search_hbox);
+- impl->search_hbox = NULL;
+- impl->search_entry = NULL;
+- break;
+-
+- case OPERATION_MODE_RECENT:
+- recent_stop_loading (impl);
+- recent_clear_model (impl, TRUE);
+- break;
+- }
+-}
+-
+-/* Sometimes we need to frob the selection in the shortcuts list manually */
+-static void
+-shortcuts_select_item_without_activating (GtkFileChooserDefault *impl, int pos)
+-{
+- GtkTreeSelection *selection;
+- GtkTreePath *path;
+-
+- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
+-
+- g_signal_handlers_block_by_func (selection, G_CALLBACK (shortcuts_selection_changed_cb), impl);
+-
+- path = gtk_tree_path_new_from_indices (pos, -1);
+- gtk_tree_selection_select_path (selection, path);
+- gtk_tree_path_free (path);
+-
+- g_signal_handlers_unblock_by_func (selection, G_CALLBACK (shortcuts_selection_changed_cb), impl);
+-}
+-
+-/* Main entry point to the searching functions; this gets called when the user
+- * activates the Search shortcut.
+- */
+-static void
+-search_activate (GtkFileChooserDefault *impl)
+-{
+- OperationMode previous_mode;
+-
+- if (impl->operation_mode == OPERATION_MODE_SEARCH)
+- {
+- focus_search_entry_in_idle (impl);
+- return;
+- }
+-
+- previous_mode = impl->operation_mode;
+- impl->operation_mode = OPERATION_MODE_SEARCH;
+-
+- shortcuts_select_item_without_activating (impl, shortcuts_get_index (impl, SHORTCUTS_SEARCH));
+-
+- stop_operation (impl, previous_mode);
+-
+- g_assert (impl->search_hbox == NULL);
+- g_assert (impl->search_entry == NULL);
+- g_assert (impl->search_model == NULL);
+-
+- search_setup_widgets (impl);
+- file_list_set_sort_column_ids (impl);
+-}
+-
+ /*
+ * Recent files support
+ */
+@@ -9246,34 +9226,6 @@ recent_stop_loading (GtkFileChooserDefault *impl)
+ }
+ }
+
+-/* Stops any pending load, clears the file list, and switches
+- * back to OPERATION_MODE_BROWSE
+- */
+-static void
+-recent_switch_to_browse_mode (GtkFileChooserDefault *impl)
+-{
+- g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
+-
+- recent_stop_loading (impl);
+- recent_clear_model (impl, TRUE);
+-
+- impl->operation_mode = OPERATION_MODE_BROWSE;
+- path_bar_update (impl);
+-
+- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+- impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+- {
+- gtk_widget_show (impl->location_button);
+-
+- if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
+- gtk_widget_show (impl->location_entry_box);
+- }
+-
+- gtk_tree_view_column_set_visible (impl->list_size_column, impl->show_size_column);
+-
+- file_list_set_sort_column_ids (impl);
+-}
+-
+ static void
+ recent_setup_model (GtkFileChooserDefault *impl)
+ {
+@@ -9304,7 +9256,6 @@ typedef struct
+ {
+ GtkFileChooserDefault *impl;
+ GList *items;
+- guint needs_sorting : 1;
+ } RecentLoadData;
+
+ static void
+@@ -9315,6 +9266,8 @@ recent_idle_cleanup (gpointer data)
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
+ GTK_TREE_MODEL (impl->recent_model));
++ file_list_set_sort_column_ids (impl);
++ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->recent_model), MODEL_COL_MTIME, GTK_SORT_DESCENDING);
+
+ set_busy_cursor (impl, FALSE);
+
+@@ -9324,16 +9277,6 @@ recent_idle_cleanup (gpointer data)
+ }
+
+ static gint
+-recent_sort_mru (gconstpointer a,
+- gconstpointer b)
+-{
+- GtkRecentInfo *info_a = (GtkRecentInfo *) a;
+- GtkRecentInfo *info_b = (GtkRecentInfo *) b;
+-
+- return (gtk_recent_info_get_modified (info_b) - gtk_recent_info_get_modified (info_a));
+-}
+-
+-static gint
+ get_recent_files_limit (GtkWidget *widget)
+ {
+ GtkSettings *settings;
+@@ -9408,32 +9351,18 @@ recent_idle_load (gpointer data)
+ if (!impl->recent_manager)
+ return FALSE;
+
+- /* first iteration: load all the items */
++ load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
+ if (!load_data->items)
+- {
+- load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
+- if (!load_data->items)
+- return FALSE;
+-
+- load_data->needs_sorting = TRUE;
+-
+- return TRUE;
+- }
+-
+- /* second iteration: MRU sorting and clamping, and populating the model */
+- if (load_data->needs_sorting)
+- {
+- load_data->items = g_list_sort (load_data->items, recent_sort_mru);
++ return FALSE;
+
+- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
+- populate_model_with_recent_items (impl, load_data->items);
+- else
+- populate_model_with_folders (impl, load_data->items);
++ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
++ populate_model_with_recent_items (impl, load_data->items);
++ else
++ populate_model_with_folders (impl, load_data->items);
+
+- g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
+- g_list_free (load_data->items);
+- load_data->items = NULL;
+- }
++ g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
++ g_list_free (load_data->items);
++ load_data->items = NULL;
+
+ return FALSE;
+ }
+@@ -9453,7 +9382,6 @@ recent_start_loading (GtkFileChooserDefault *impl)
+ load_data = g_new (RecentLoadData, 1);
+ load_data->impl = impl;
+ load_data->items = NULL;
+- load_data->needs_sorting = TRUE;
+
+ /* begin lazy loading the recent files into the model */
+ impl->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
+@@ -9507,45 +9435,6 @@ recent_should_respond (GtkFileChooserDefault *impl)
+ return (gtk_tree_selection_count_selected_rows (selection) != 0);
+ }
+
+-/* Hide the location widgets temporarily */
+-static void
+-recent_hide_entry (GtkFileChooserDefault *impl)
+-{
+- path_bar_update (impl);
+-
+- /* Hide the location widgets temporarily */
+- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+- impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+- {
+- gtk_widget_hide (impl->location_button);
+- gtk_widget_hide (impl->location_entry_box);
+- }
+-}
+-
+-/* Main entry point to the recent files functions; this gets called when
+- * the user activates the Recently Used shortcut.
+- */
+-static void
+-recent_activate (GtkFileChooserDefault *impl)
+-{
+- OperationMode previous_mode;
+-
+- if (impl->operation_mode == OPERATION_MODE_RECENT)
+- return;
+-
+- previous_mode = impl->operation_mode;
+- impl->operation_mode = OPERATION_MODE_RECENT;
+-
+- shortcuts_select_item_without_activating (impl, shortcuts_get_index (impl, SHORTCUTS_RECENT));
+-
+- stop_operation (impl, previous_mode);
+-
+- recent_hide_entry (impl);
+-
+- file_list_set_sort_column_ids (impl);
+- recent_start_loading (impl);
+-}
+-
+ static void
+ set_current_filter (GtkFileChooserDefault *impl,
+ GtkFileFilter *filter)
+@@ -9719,17 +9608,7 @@ shortcuts_activate_volume (GtkFileChooserDefault *impl,
+ {
+ GFile *file;
+
+- switch (impl->operation_mode)
+- {
+- case OPERATION_MODE_BROWSE:
+- break;
+- case OPERATION_MODE_SEARCH:
+- search_switch_to_browse_mode (impl);
+- break;
+- case OPERATION_MODE_RECENT:
+- recent_switch_to_browse_mode (impl);
+- break;
+- }
++ operation_mode_set (impl, OPERATION_MODE_BROWSE);
+
+ /* We ref the file chooser since volume_mount() may run a main loop, and the
+ * user could close the file chooser window in the meantime.
+@@ -9836,7 +9715,10 @@ shortcuts_activate_iter (GtkFileChooserDefault *impl,
+ gpointer col_data;
+ ShortcutType shortcut_type;
+
+- if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY
++ /* In the Save modes, we want to preserve what the uesr typed in the filename
++ * entry, so that he may choose another folder without erasing his typed name.
++ */
++ if (impl->location_entry
+ && !(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+ || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
+ _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
+@@ -9860,6 +9742,8 @@ shortcuts_activate_iter (GtkFileChooserDefault *impl,
+
+ volume = col_data;
+
++ operation_mode_set (impl, OPERATION_MODE_BROWSE);
++
+ shortcuts_activate_volume (impl, volume);
+ }
+ else if (shortcut_type == SHORTCUT_TYPE_FILE)
+@@ -9867,6 +9751,8 @@ shortcuts_activate_iter (GtkFileChooserDefault *impl,
+ struct ShortcutsActivateData *data;
+ GtkFileSystemVolume *volume;
+
++ operation_mode_set (impl, OPERATION_MODE_BROWSE);
++
+ volume = _gtk_file_system_get_volume_for_file (impl->file_system, col_data);
+
+ data = g_new0 (struct ShortcutsActivateData, 1);
+@@ -9898,11 +9784,11 @@ shortcuts_activate_iter (GtkFileChooserDefault *impl,
+ }
+ else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
+ {
+- search_activate (impl);
++ operation_mode_set (impl, OPERATION_MODE_SEARCH);
+ }
+ else if (shortcut_type == SHORTCUT_TYPE_RECENT)
+ {
+- recent_activate (impl);
++ operation_mode_set (impl, OPERATION_MODE_RECENT);
+ }
+ }
+
+@@ -10163,21 +10049,9 @@ location_popup_handler (GtkFileChooserDefault *impl,
+ if (impl->operation_mode != OPERATION_MODE_BROWSE)
+ {
+ GtkWidget *widget_to_focus;
+-
+- /* This will give us the location widgets back */
+- switch (impl->operation_mode)
+- {
+- case OPERATION_MODE_SEARCH:
+- search_switch_to_browse_mode (impl);
+- break;
+- case OPERATION_MODE_RECENT:
+- recent_switch_to_browse_mode (impl);
+- break;
+- case OPERATION_MODE_BROWSE:
+- g_assert_not_reached ();
+- break;
+- }
+
++ operation_mode_set (impl, OPERATION_MODE_BROWSE);
++
+ if (impl->current_folder)
+ change_folder_and_display_error (impl, impl->current_folder, FALSE);
+
+@@ -10193,34 +10067,11 @@ location_popup_handler (GtkFileChooserDefault *impl,
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+- LocationMode new_mode;
+-
+- if (path != NULL)
+- {
+- /* since the user typed something, we unconditionally want to turn on the entry */
+- new_mode = LOCATION_MODE_FILENAME_ENTRY;
+- }
+- else if (impl->location_mode == LOCATION_MODE_PATH_BAR)
+- new_mode = LOCATION_MODE_FILENAME_ENTRY;
+- else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
+- new_mode = LOCATION_MODE_PATH_BAR;
+- else
+- {
+- g_assert_not_reached ();
+- return;
+- }
++ if (!path)
++ return;
+
+- location_mode_set (impl, new_mode, TRUE);
+- if (new_mode == LOCATION_MODE_FILENAME_ENTRY)
+- {
+- if (path != NULL)
+- location_set_user_text (impl, path);
+- else
+- {
+- location_entry_set_initial_text (impl);
+- gtk_editable_select_region (GTK_EDITABLE (impl->location_entry), 0, -1);
+- }
+- }
++ location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
++ location_set_user_text (impl, path);
+ }
+ else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h
+index f5159d1..53f2745 100644
+--- a/gtk/gtkfilechooserprivate.h
++++ b/gtk/gtkfilechooserprivate.h
+@@ -168,6 +168,7 @@ struct _GtkFileChooserDefault
+
+ /* The file browsing widgets */
+ GtkWidget *browse_widgets_box;
++ GtkWidget *browse_header_box;
+ GtkWidget *browse_shortcuts_tree_view;
+ GtkWidget *browse_shortcuts_add_button;
+ GtkWidget *browse_shortcuts_remove_button;
+
+
diff --git a/patches/gdk-deadkeys.patch b/patches/0010-Bug-617583-Dead-accents-keys-don-t-work-in-GTK-appli.patch
similarity index 74%
rename from patches/gdk-deadkeys.patch
rename to patches/0010-Bug-617583-Dead-accents-keys-don-t-work-in-GTK-appli.patch
index ff4b953..797f3e9 100644
--- a/patches/gdk-deadkeys.patch
+++ b/patches/0010-Bug-617583-Dead-accents-keys-don-t-work-in-GTK-appli.patch
@@ -1,11 +1,28 @@
---- a/gdk/quartz/gdkkeys-quartz.c 2010-11-27 18:04:40.b/b000000000 -0800
-+++ b/gdk/quartz/gdkkeys-quartz.c 2010-11-28 15:46:42.000000000 -0800
-@@ -182,7 +182,58 @@
+From a601126aa92db1da327298d2bcc94764a356ea52 Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Fri, 9 Sep 2011 10:12:40 +0200
+Subject: [PATCH 10/15] Bug 617583 - Dead accents keys don't work in GTK+
+ applications on OSX
+
+Handle dead keys in special_ucs_table and have them converted by
+UCKeyTranslate(), so all dead key combinations can be entered.
+Later, this should be handled in the input method, just as it's
+done for X11/Win32.
+---
+ gdk/quartz/gdkkeys-quartz.c | 77 ++++++++++++++++++++++++++++++++++++++++---
+ 1 files changed, 72 insertions(+), 5 deletions(-)
+
+diff --git a/gdk/quartz/gdkkeys-quartz.c b/gdk/quartz/gdkkeys-quartz.c
+index 52b0867..8c7e6f1 100644
+--- a/gdk/quartz/gdkkeys-quartz.c
++++ b/gdk/quartz/gdkkeys-quartz.c
+@@ -182,7 +182,60 @@ const static struct {
{ 0x001d, GDK_Right },
{ 0x001e, GDK_Up },
{ 0x001f, GDK_Down },
- { 0x007f, GDK_Delete }
+ { 0x007f, GDK_Delete },
++ { 0xf027, GDK_dead_acute },
+ { 0xf060, GDK_dead_grave },
+ { 0xf300, GDK_dead_grave },
+ { 0xf0b4, GDK_dead_acute },
@@ -27,6 +44,7 @@
+ { 0xf308, GDK_dead_diaeresis },
+ { 0xf2da, GDK_dead_abovering },
+ { 0xf30A, GDK_dead_abovering },
++ { 0xf022, GDK_dead_doubleacute },
+ { 0xf2dd, GDK_dead_doubleacute },
+ { 0xf30B, GDK_dead_doubleacute },
+ { 0xf2c7, GDK_dead_caron },
@@ -60,7 +78,7 @@
};
static void
-@@ -330,10 +381,10 @@
+@@ -330,10 +383,10 @@ maybe_update_keymap (void)
UniChar uc;
key_code = modifiers[j] | i;
@@ -73,17 +91,19 @@
&state, 4, &nChars, chars);
-@@ -346,7 +397,21 @@
+@@ -345,8 +398,22 @@ maybe_update_keymap (void)
+ {
int k;
gboolean found = FALSE;
-
+-
- uc = chars[0];
++
+ /* A few <Shift><Option>keys return two
+ * characters, the first of which is U+00a0,
+ * which isn't interesting; so we return the
+ * second. More sophisticated handling is the
+ * job of a GtkIMContext.
-+
++ *
+ * If state isn't zero, it means that it's a
+ * dead key of some sort. Some of those are
+ * enumerated in the special_ucs_table with the
@@ -91,8 +111,9 @@
+ * private use range. Here we do the same.
+ */
+ if (state != 0)
-+ chars[nChars - 1] |= 0xf000;
++ chars[nChars - 1] |= 0xf000;
+ uc = chars[nChars - 1];
for (k = 0; k < G_N_ELEMENTS (special_ucs_table); k++)
{
+
diff --git a/patches/0011-bgo-514843-filechooser-Deal-with-corrupted-.gtk-book.patch b/patches/0011-bgo-514843-filechooser-Deal-with-corrupted-.gtk-book.patch
new file mode 100644
index 0000000..a323019
--- /dev/null
+++ b/patches/0011-bgo-514843-filechooser-Deal-with-corrupted-.gtk-book.patch
@@ -0,0 +1,57 @@
+From 996c6b0cb4cfbc9cf98bafd32cbb4080452d4ffa Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Mon, 12 Sep 2011 14:25:45 -0500
+Subject: [PATCH 11/15] bgo#514843 - [filechooser] Deal with corrupted
+ .gtk-bookmarks gracefully
+
+We weren't checking for the lines in that file being valid UTF-8 strings.
+---
+ gtk/gtkfilesystem.c | 13 +++++++++----
+ 1 files changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/gtk/gtkfilesystem.c b/gtk/gtkfilesystem.c
+index 6e7be3c..f2897d2 100644
+--- a/gtk/gtkfilesystem.c
++++ b/gtk/gtkfilesystem.c
+@@ -272,6 +272,9 @@ read_bookmarks (GFile *file)
+ if (!*lines[i])
+ continue;
+
++ if (!g_utf8_validate (lines[i], -1, NULL))
++ continue;
++
+ bookmark = g_slice_new0 (GtkFileSystemBookmark);
+
+ if ((space = strchr (lines[i], ' ')) != NULL)
+@@ -297,23 +300,25 @@ save_bookmarks (GFile *bookmarks_file,
+ {
+ GError *error = NULL;
+ GString *contents;
++ GSList *l;
+
+ contents = g_string_new ("");
+
+- while (bookmarks)
++ for (l = bookmarks; l; l = l->next)
+ {
+- GtkFileSystemBookmark *bookmark;
++ GtkFileSystemBookmark *bookmark = l->data;
+ gchar *uri;
+
+- bookmark = bookmarks->data;
+ uri = g_file_get_uri (bookmark->file);
++ if (!uri)
++ continue;
++
+ g_string_append (contents, uri);
+
+ if (bookmark->label)
+ g_string_append_printf (contents, " %s", bookmark->label);
+
+ g_string_append_c (contents, '\n');
+- bookmarks = bookmarks->next;
+ g_free (uri);
+ }
+
+
+
diff --git a/patches/0012-Bug-605799-Option-MOD1-and-Command-SUPER-modifiers-a.patch b/patches/0012-Bug-605799-Option-MOD1-and-Command-SUPER-modifiers-a.patch
new file mode 100644
index 0000000..63f21f9
--- /dev/null
+++ b/patches/0012-Bug-605799-Option-MOD1-and-Command-SUPER-modifiers-a.patch
@@ -0,0 +1,835 @@
+From 2123991148d68ca1d0ea451b8bbc58306d0378c9 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch lanedo com>
+Date: Fri, 16 Sep 2011 15:39:23 +0200
+Subject: [PATCH 12/15] Bug 605799 - Option (MOD1) and Command (SUPER)
+ modifiers are switched
+
+- maps Alt/Option to MOD1 as on all other platforms
+- maps Command to MOD2
+- maps MOD2 to the virtual META
+- fixes (?) group handling in gdkkeys-quartz.c
+- fixes text input by adding GTK_NO_TEXT_INPUT_MOD_MASK instead
+ of hardcoding something that doesn't match a Mac
+- disables mnemonics
+- sets the default accel modifier to META on quartz
+- adds <Primary> as possible modifier to GtkAccelGroup which
+ automatically maps to Control or Meta, depending on the platform
+- fixes GtkCellRendererAccel to recognize virtual modifiers
+- changes default stock items to GTK_NO_TEXT_INPUT_MOD_MASK instead
+ of hardcoding GDK_CONTROL_MASK
+---
+ gdk/quartz/gdkevents-quartz.c | 8 +++--
+ gdk/quartz/gdkkeys-quartz.c | 18 +++++++---
+ gtk/gtkaccelgroup.c | 36 ++++++++++++++++++++
+ gtk/gtkcellrendereraccel.c | 5 ++-
+ gtk/gtkentry.c | 2 +-
+ gtk/gtkfilechooserdefault.c | 7 +---
+ gtk/gtkiconview.c | 46 +++++++++++++-------------
+ gtk/gtkimcontextsimple.c | 2 +-
+ gtk/gtkimmulticontext.c | 2 +-
+ gtk/gtkprivate.h | 18 ++++++++++
+ gtk/gtkrc.key.mac | 2 +
+ gtk/gtkstock.c | 23 +++++++------
+ gtk/gtktextview.c | 4 +-
+ gtk/gtktreeprivate.h | 8 ++--
+ gtk/gtktreeview.c | 74 ++++++++++++++++++++--------------------
+ 15 files changed, 161 insertions(+), 94 deletions(-)
+
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index 9e27329..4625070 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -271,8 +271,10 @@ get_keyboard_modifiers_from_ns_event (NSEvent *nsevent)
+ modifiers |= GDK_SHIFT_MASK;
+ if (nsflags & NSControlKeyMask)
+ modifiers |= GDK_CONTROL_MASK;
+- if (nsflags & NSCommandKeyMask)
++ if (nsflags & NSAlternateKeyMask)
+ modifiers |= GDK_MOD1_MASK;
++ if (nsflags & NSCommandKeyMask)
++ modifiers |= GDK_MOD2_MASK;
+
+ return modifiers;
+ }
+@@ -908,7 +910,7 @@ fill_key_event (GdkWindow *window,
+ {
+ case GDK_Meta_R:
+ case GDK_Meta_L:
+- mask = GDK_MOD1_MASK;
++ mask = GDK_MOD2_MASK;
+ break;
+ case GDK_Shift_R:
+ case GDK_Shift_L:
+@@ -919,7 +921,7 @@ fill_key_event (GdkWindow *window,
+ break;
+ case GDK_Alt_R:
+ case GDK_Alt_L:
+- mask = GDK_MOD5_MASK;
++ mask = GDK_MOD1_MASK;
+ break;
+ case GDK_Control_R:
+ case GDK_Control_L:
+diff --git a/gdk/quartz/gdkkeys-quartz.c b/gdk/quartz/gdkkeys-quartz.c
+index 8c7e6f1..9fffc70 100644
+--- a/gdk/quartz/gdkkeys-quartz.c
++++ b/gdk/quartz/gdkkeys-quartz.c
+@@ -547,8 +547,8 @@ gdk_keymap_get_entries_for_keyval (GdkKeymap *keymap,
+ (*n_keys)++;
+
+ key.keycode = i / KEYVALS_PER_KEYCODE;
+- key.group = 0;
+- key.level = i % KEYVALS_PER_KEYCODE;
++ key.group = (i % KEYVALS_PER_KEYCODE) >= 2;
++ key.level = i % 2;
+
+ g_array_append_val (keys_array, key);
+ }
+@@ -606,7 +606,7 @@ gdk_keymap_get_entries_for_keycode (GdkKeymap *keymap,
+ GdkKeymapKey key;
+
+ key.keycode = hardware_keycode;
+- key.group = i / 2;
++ key.group = i >= 2;
+ key.level = i % 2;
+
+ g_array_append_val (keys_array, key);
+@@ -666,6 +666,11 @@ translate_keysym (guint hardware_keycode,
+ tmp_keyval = upper;
+ }
+
++ if (effective_group)
++ *effective_group = group;
++ if (effective_level)
++ *effective_level = level;
++
+ return tmp_keyval;
+ }
+
+@@ -723,14 +728,17 @@ void
+ gdk_keymap_add_virtual_modifiers (GdkKeymap *keymap,
+ GdkModifierType *state)
+ {
+- /* FIXME: For now, we've mimiced the Windows backend. */
++ if (*state & GDK_MOD2_MASK)
++ *state |= GDK_META_MASK;
+ }
+
+ gboolean
+ gdk_keymap_map_virtual_modifiers (GdkKeymap *keymap,
+ GdkModifierType *state)
+ {
+- /* FIXME: For now, we've mimiced the Windows backend. */
++ if (*state & GDK_META_MASK)
++ *state |= GDK_MOD2_MASK;
++
+ return TRUE;
+ }
+
+diff --git a/gtk/gtkaccelgroup.c b/gtk/gtkaccelgroup.c
+index 4c21943..eb2a937 100644
+--- a/gtk/gtkaccelgroup.c
++++ b/gtk/gtkaccelgroup.c
+@@ -35,6 +35,7 @@
+ #include "gtkmain.h" /* For _gtk_boolean_handled_accumulator */
+ #include "gdk/gdkkeysyms.h"
+ #include "gtkmarshalers.h"
++#include "gtkprivate.h"
+ #include "gtkalias.h"
+
+ /**
+@@ -1121,6 +1122,20 @@ is_hyper (const gchar *string)
+ (string[6] == '>'));
+ }
+
++static inline gboolean
++is_primary (const gchar *string)
++{
++ return ((string[0] == '<') &&
++ (string[1] == 'p' || string[1] == 'P') &&
++ (string[2] == 'r' || string[2] == 'R') &&
++ (string[3] == 'i' || string[3] == 'I') &&
++ (string[4] == 'm' || string[4] == 'M') &&
++ (string[5] == 'a' || string[5] == 'A') &&
++ (string[6] == 'r' || string[6] == 'R') &&
++ (string[7] == 'y' || string[7] == 'Y') &&
++ (string[8] == '>'));
++}
++
+ /**
+ * gtk_accelerator_parse:
+ * @accelerator: string representing an accelerator
+@@ -1167,6 +1182,12 @@ gtk_accelerator_parse (const gchar *accelerator,
+ len -= 9;
+ mods |= GDK_RELEASE_MASK;
+ }
++ else if (len >= 9 && is_primary (accelerator))
++ {
++ accelerator += 9;
++ len -= 9;
++ mods |= GTK_DEFAULT_ACCEL_MOD_MASK;
++ }
+ else if (len >= 9 && is_control (accelerator))
+ {
+ accelerator += 9;
+@@ -1280,6 +1301,7 @@ gtk_accelerator_name (guint accelerator_key,
+ GdkModifierType accelerator_mods)
+ {
+ static const gchar text_release[] = "<Release>";
++ static const gchar text_primary[] = "<Primary>";
+ static const gchar text_shift[] = "<Shift>";
+ static const gchar text_control[] = "<Control>";
+ static const gchar text_mod1[] = "<Alt>";
+@@ -1290,6 +1312,7 @@ gtk_accelerator_name (guint accelerator_key,
+ static const gchar text_meta[] = "<Meta>";
+ static const gchar text_super[] = "<Super>";
+ static const gchar text_hyper[] = "<Hyper>";
++ GdkModifierType saved_mods;
+ guint l;
+ gchar *keyval_name;
+ gchar *accelerator;
+@@ -1300,9 +1323,15 @@ gtk_accelerator_name (guint accelerator_key,
+ if (!keyval_name)
+ keyval_name = "";
+
++ saved_mods = accelerator_mods;
+ l = 0;
+ if (accelerator_mods & GDK_RELEASE_MASK)
+ l += sizeof (text_release) - 1;
++ if (accelerator_mods & GTK_DEFAULT_ACCEL_MOD_MASK)
++ {
++ l += sizeof (text_primary) - 1;
++ accelerator_mods &= ~GTK_DEFAULT_ACCEL_MOD_MASK; /* consume the default accel */
++ }
+ if (accelerator_mods & GDK_SHIFT_MASK)
+ l += sizeof (text_shift) - 1;
+ if (accelerator_mods & GDK_CONTROL_MASK)
+@@ -1327,6 +1356,7 @@ gtk_accelerator_name (guint accelerator_key,
+
+ accelerator = g_new (gchar, l + 1);
+
++ accelerator_mods = saved_mods;
+ l = 0;
+ accelerator[l] = 0;
+ if (accelerator_mods & GDK_RELEASE_MASK)
+@@ -1334,6 +1364,12 @@ gtk_accelerator_name (guint accelerator_key,
+ strcpy (accelerator + l, text_release);
+ l += sizeof (text_release) - 1;
+ }
++ if (accelerator_mods & GTK_DEFAULT_ACCEL_MOD_MASK)
++ {
++ strcpy (accelerator + l, text_primary);
++ l += sizeof (text_primary) - 1;
++ accelerator_mods &= ~GTK_DEFAULT_ACCEL_MOD_MASK; /* consume the default accel */
++ }
+ if (accelerator_mods & GDK_SHIFT_MASK)
+ {
+ strcpy (accelerator + l, text_shift);
+diff --git a/gtk/gtkcellrendereraccel.c b/gtk/gtkcellrendereraccel.c
+index dedbfda..846b1ec 100644
+--- a/gtk/gtkcellrendereraccel.c
++++ b/gtk/gtkcellrendereraccel.c
+@@ -427,11 +427,14 @@ grab_key_callback (GtkWidget *widget,
+ event->group,
+ NULL, NULL, NULL, &consumed_modifiers);
+
++ accel_mods = event->state;
++ gdk_keymap_add_virtual_modifiers (gdk_keymap_get_for_display (display), &accel_mods);
++
+ accel_key = gdk_keyval_to_lower (event->keyval);
+ if (accel_key == GDK_ISO_Left_Tab)
+ accel_key = GDK_Tab;
+
+- accel_mods = event->state & gtk_accelerator_get_default_mod_mask ();
++ accel_mods &= gtk_accelerator_get_default_mod_mask ();
+
+ /* Filter consumed modifiers
+ */
+diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
+index 0a833cf..8e2c2b2 100644
+--- a/gtk/gtkentry.c
++++ b/gtk/gtkentry.c
+@@ -3696,7 +3696,7 @@ gtk_entry_button_press (GtkWidget *widget,
+ entry->select_words = FALSE;
+ entry->select_lines = FALSE;
+
+- if (event->state & GDK_SHIFT_MASK)
++ if (event->state & GTK_EXTEND_SELECTION_MOD_MASK)
+ {
+ _gtk_entry_reset_im_context (entry);
+
+diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
+index 9e636c2..44c2d17 100644
+--- a/gtk/gtkfilechooserdefault.c
++++ b/gtk/gtkfilechooserdefault.c
+@@ -3687,18 +3687,15 @@ browse_files_key_press_event_cb (GtkWidget *widget,
+ gpointer data)
+ {
+ GtkFileChooserDefault *impl;
+- int modifiers;
+
+ impl = (GtkFileChooserDefault *) data;
+
+- modifiers = gtk_accelerator_get_default_mod_mask ();
+-
+ if ((event->keyval == GDK_KEY_slash
+ || event->keyval == GDK_KEY_KP_Divide
+ #ifdef G_OS_UNIX
+ || event->keyval == GDK_KEY_asciitilde
+ #endif
+- ) && ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
++ ) && !(event->state & GTK_NO_TEXT_INPUT_MOD_MASK))
+ {
+ location_popup_handler (impl, event->string);
+ return TRUE;
+@@ -3715,7 +3712,7 @@ browse_files_key_press_event_cb (GtkWidget *widget,
+ || event->keyval == GDK_KEY_KP_Enter
+ || event->keyval == GDK_KEY_space
+ || event->keyval == GDK_KEY_KP_Space)
+- && ((event->state & modifiers) == 0)
++ && !(event->state & gtk_accelerator_get_default_mod_mask ())
+ && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
+ {
+diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c
+index 8ce402f..54b3f3b 100644
+--- a/gtk/gtkiconview.c
++++ b/gtk/gtkiconview.c
+@@ -183,8 +183,8 @@ struct _GtkIconViewPrivate
+ guint reorderable : 1;
+ guint empty_view_drop :1;
+
+- guint ctrl_pressed : 1;
+- guint shift_pressed : 1;
++ guint modify_selection_pressed : 1;
++ guint extend_selection_pressed : 1;
+
+ guint draw_focus : 1;
+ };
+@@ -2177,7 +2177,7 @@ gtk_icon_view_button_press (GtkWidget *widget,
+ gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell);
+ }
+ else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
+- (event->state & GDK_SHIFT_MASK))
++ (event->state & GTK_EXTEND_SELECTION_MOD_MASK))
+ {
+ gtk_icon_view_unselect_all_internal (icon_view);
+
+@@ -2194,7 +2194,7 @@ gtk_icon_view_button_press (GtkWidget *widget,
+ {
+ if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE ||
+ ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) &&
+- (event->state & GDK_CONTROL_MASK))
++ (event->state & GTK_MODIFY_SELECTION_MOD_MASK))
+ {
+ item->selected = !item->selected;
+ gtk_icon_view_queue_draw_item (icon_view, item);
+@@ -2239,7 +2239,7 @@ gtk_icon_view_button_press (GtkWidget *widget,
+ else
+ {
+ if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE &&
+- !(event->state & GDK_CONTROL_MASK))
++ !(event->state & GTK_MODIFY_SELECTION_MOD_MASK))
+ {
+ dirty = gtk_icon_view_unselect_all_internal (icon_view);
+ }
+@@ -3856,10 +3856,10 @@ gtk_icon_view_real_move_cursor (GtkIconView *icon_view,
+
+ if (gtk_get_current_event_state (&state))
+ {
+- if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
+- icon_view->priv->ctrl_pressed = TRUE;
+- if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
+- icon_view->priv->shift_pressed = TRUE;
++ if ((state & GTK_MODIFY_SELECTION_MOD_MASK) == GTK_MODIFY_SELECTION_MOD_MASK)
++ icon_view->priv->modify_selection_pressed = TRUE;
++ if ((state & GTK_EXTEND_SELECTION_MOD_MASK) == GTK_EXTEND_SELECTION_MOD_MASK)
++ icon_view->priv->extend_selection_pressed = TRUE;
+ }
+ /* else we assume not pressed */
+
+@@ -3882,8 +3882,8 @@ gtk_icon_view_real_move_cursor (GtkIconView *icon_view,
+ g_assert_not_reached ();
+ }
+
+- icon_view->priv->ctrl_pressed = FALSE;
+- icon_view->priv->shift_pressed = FALSE;
++ icon_view->priv->modify_selection_pressed = FALSE;
++ icon_view->priv->extend_selection_pressed = FALSE;
+
+ icon_view->priv->draw_focus = TRUE;
+
+@@ -4154,15 +4154,15 @@ gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view,
+ return;
+ }
+
+- if (icon_view->priv->ctrl_pressed ||
+- !icon_view->priv->shift_pressed ||
++ if (icon_view->priv->modify_selection_pressed ||
++ !icon_view->priv->extend_selection_pressed ||
+ !icon_view->priv->anchor_item ||
+ icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
+ icon_view->priv->anchor_item = item;
+
+ gtk_icon_view_set_cursor_item (icon_view, item, cell);
+
+- if (!icon_view->priv->ctrl_pressed &&
++ if (!icon_view->priv->modify_selection_pressed &&
+ icon_view->priv->selection_mode != GTK_SELECTION_NONE)
+ {
+ dirty = gtk_icon_view_unselect_all_internal (icon_view);
+@@ -4209,15 +4209,15 @@ gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view,
+ if (!item)
+ return;
+
+- if (icon_view->priv->ctrl_pressed ||
+- !icon_view->priv->shift_pressed ||
++ if (icon_view->priv->modify_selection_pressed ||
++ !icon_view->priv->extend_selection_pressed ||
+ !icon_view->priv->anchor_item ||
+ icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
+ icon_view->priv->anchor_item = item;
+
+ gtk_icon_view_set_cursor_item (icon_view, item, -1);
+
+- if (!icon_view->priv->ctrl_pressed &&
++ if (!icon_view->priv->modify_selection_pressed &&
+ icon_view->priv->selection_mode != GTK_SELECTION_NONE)
+ {
+ dirty = gtk_icon_view_unselect_all_internal (icon_view);
+@@ -4291,15 +4291,15 @@ gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view,
+ return;
+ }
+
+- if (icon_view->priv->ctrl_pressed ||
+- !icon_view->priv->shift_pressed ||
++ if (icon_view->priv->modify_selection_pressed ||
++ !icon_view->priv->extend_selection_pressed ||
+ !icon_view->priv->anchor_item ||
+ icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
+ icon_view->priv->anchor_item = item;
+
+ gtk_icon_view_set_cursor_item (icon_view, item, cell);
+
+- if (!icon_view->priv->ctrl_pressed &&
++ if (!icon_view->priv->modify_selection_pressed &&
+ icon_view->priv->selection_mode != GTK_SELECTION_NONE)
+ {
+ dirty = gtk_icon_view_unselect_all_internal (icon_view);
+@@ -4338,15 +4338,15 @@ gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view,
+ if (!item)
+ return;
+
+- if (icon_view->priv->ctrl_pressed ||
+- !icon_view->priv->shift_pressed ||
++ if (icon_view->priv->modify_selection_pressed ||
++ !icon_view->priv->extend_selection_pressed ||
+ !icon_view->priv->anchor_item ||
+ icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
+ icon_view->priv->anchor_item = item;
+
+ gtk_icon_view_set_cursor_item (icon_view, item, -1);
+
+- if (!icon_view->priv->ctrl_pressed &&
++ if (!icon_view->priv->modify_selection_pressed &&
+ icon_view->priv->selection_mode != GTK_SELECTION_NONE)
+ {
+ dirty = gtk_icon_view_unselect_all_internal (icon_view);
+diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c
+index 9ef18a9..d496166 100644
+--- a/gtk/gtkimcontextsimple.c
++++ b/gtk/gtkimcontextsimple.c
+@@ -871,7 +871,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
+ (context_simple->in_hex_sequence && !hex_keyval &&
+ !is_hex_start && !is_hex_end && !is_escape && !is_backspace))
+ {
+- if (event->state & (GDK_MOD1_MASK | GDK_CONTROL_MASK) ||
++ if (event->state & GTK_NO_TEXT_INPUT_MOD_MASK ||
+ (context_simple->in_hex_sequence && context_simple->modifiers_dropped &&
+ (event->keyval == GDK_Return ||
+ event->keyval == GDK_ISO_Enter ||
+diff --git a/gtk/gtkimmulticontext.c b/gtk/gtkimmulticontext.c
+index 6185667..34f34b6 100644
+--- a/gtk/gtkimmulticontext.c
++++ b/gtk/gtkimmulticontext.c
+@@ -330,7 +330,7 @@ gtk_im_multicontext_filter_keypress (GtkIMContext *context,
+ if (slave)
+ return gtk_im_context_filter_keypress (slave, event);
+ else if (event->type == GDK_KEY_PRESS &&
+- (event->state & (GDK_MOD1_MASK | GDK_CONTROL_MASK)) == 0)
++ (event->state & GTK_NO_TEXT_INPUT_MOD_MASK) == 0)
+ {
+ gunichar ch;
+
+diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
+index 3933dc5..00440cb 100644
+--- a/gtk/gtkprivate.h
++++ b/gtk/gtkprivate.h
+@@ -112,6 +112,24 @@ gboolean _gtk_fnmatch (const char *pattern,
+ #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
+ #endif
+
++/* When any of these modifiers are active, a key
++ * event cannot produce a symbol, so should be
++ * skipped when handling text input
++ */
++#ifndef GDK_WINDOWING_QUARTZ
++#define GTK_NO_TEXT_INPUT_MOD_MASK (GDK_MOD1_MASK | GDK_CONTROL_MASK)
++#else
++#define GTK_NO_TEXT_INPUT_MOD_MASK (GDK_MOD2_MASK | GDK_CONTROL_MASK)
++#endif
++
++#ifndef GDK_WINDOWING_QUARTZ
++#define GTK_EXTEND_SELECTION_MOD_MASK GDK_SHIFT_MASK
++#define GTK_MODIFY_SELECTION_MOD_MASK GDK_CONTROL_MASK
++#else
++#define GTK_EXTEND_SELECTION_MOD_MASK GDK_SHIFT_MASK
++#define GTK_MODIFY_SELECTION_MOD_MASK GDK_MOD2_MASK
++#endif
++
+ G_END_DECLS
+
+ #endif /* __GTK_PRIVATE_H__ */
+diff --git a/gtk/gtkrc.key.mac b/gtk/gtkrc.key.mac
+index 7b6e8d1..5389ffe 100644
+--- a/gtk/gtkrc.key.mac
++++ b/gtk/gtkrc.key.mac
+@@ -1,3 +1,5 @@
++gtk-enable-mnemonics = 0
++
+ binding "gtk-mac-alt-arrows"
+ {
+ bind "<alt>Right" { "move-cursor" (words, 1, 0) }
+diff --git a/gtk/gtkstock.c b/gtk/gtkstock.c
+index 66b0ed5..5cb64ec 100644
+--- a/gtk/gtkstock.c
++++ b/gtk/gtkstock.c
+@@ -32,6 +32,7 @@
+ #include "gtkiconfactory.h"
+ #include "gtkintl.h"
+ #include <gdk/gdkkeysyms.h>
++#include "gtkprivate.h"
+ #include "gtkalias.h"
+
+ /**
+@@ -326,18 +327,18 @@ static const GtkStockItem builtin_items [] =
+ { GTK_STOCK_CANCEL, NC_("Stock label", "_Cancel"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_CDROM, NC_("Stock label", "_CD-Rom"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_CLEAR, NC_("Stock label", "_Clear"), 0, 0, GETTEXT_PACKAGE },
+- { GTK_STOCK_CLOSE, NC_("Stock label", "_Close"), GDK_CONTROL_MASK, 'w', GETTEXT_PACKAGE },
++ { GTK_STOCK_CLOSE, NC_("Stock label", "_Close"), GTK_DEFAULT_ACCEL_MOD_MASK, 'w', GETTEXT_PACKAGE },
+ { GTK_STOCK_CONNECT, NC_("Stock label", "C_onnect"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_CONVERT, NC_("Stock label", "_Convert"), 0, 0, GETTEXT_PACKAGE },
+- { GTK_STOCK_COPY, NC_("Stock label", "_Copy"), GDK_CONTROL_MASK, 'c', GETTEXT_PACKAGE },
+- { GTK_STOCK_CUT, NC_("Stock label", "Cu_t"), GDK_CONTROL_MASK, 'x', GETTEXT_PACKAGE },
++ { GTK_STOCK_COPY, NC_("Stock label", "_Copy"), GTK_DEFAULT_ACCEL_MOD_MASK, 'c', GETTEXT_PACKAGE },
++ { GTK_STOCK_CUT, NC_("Stock label", "Cu_t"), GTK_DEFAULT_ACCEL_MOD_MASK, 'x', GETTEXT_PACKAGE },
+ { GTK_STOCK_DELETE, NC_("Stock label", "_Delete"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_DISCARD, NC_("Stock label", "_Discard"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_DISCONNECT, NC_("Stock label", "_Disconnect"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_EXECUTE, NC_("Stock label", "_Execute"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_EDIT, NC_("Stock label", "_Edit"), 0, 0, GETTEXT_PACKAGE },
+- { GTK_STOCK_FIND, NC_("Stock label", "_Find"), GDK_CONTROL_MASK, 'f', GETTEXT_PACKAGE },
+- { GTK_STOCK_FIND_AND_REPLACE, NC_("Stock label", "Find and _Replace"), GDK_CONTROL_MASK, 'r', GETTEXT_PACKAGE },
++ { GTK_STOCK_FIND, NC_("Stock label", "_Find"), GTK_DEFAULT_ACCEL_MOD_MASK, 'f', GETTEXT_PACKAGE },
++ { GTK_STOCK_FIND_AND_REPLACE, NC_("Stock label", "Find and _Replace"), GTK_DEFAULT_ACCEL_MOD_MASK, 'r', GETTEXT_PACKAGE },
+ { GTK_STOCK_FLOPPY, NC_("Stock label", "_Floppy"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_FULLSCREEN, NC_("Stock label", "_Fullscreen"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_LEAVE_FULLSCREEN, NC_("Stock label", "_Leave Fullscreen"), 0, 0, GETTEXT_PACKAGE },
+@@ -358,7 +359,7 @@ static const GtkStockItem builtin_items [] =
+ /* This is a navigation label as in "go up" */
+ { GTK_STOCK_GO_UP, NC_("Stock label, navigation", "_Up"), 0, 0, GETTEXT_PACKAGE "-navigation" },
+ { GTK_STOCK_HARDDISK, NC_("Stock label", "_Harddisk"), 0, 0, GETTEXT_PACKAGE },
+- { GTK_STOCK_HELP, NC_("Stock label", "_Help"), GDK_CONTROL_MASK, 'h', GETTEXT_PACKAGE },
++ { GTK_STOCK_HELP, NC_("Stock label", "_Help"), GTK_DEFAULT_ACCEL_MOD_MASK, 'h', GETTEXT_PACKAGE },
+ { GTK_STOCK_HOME, NC_("Stock label", "_Home"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_INDENT, NC_("Stock label", "Increase Indent"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_UNINDENT, NC_("Stock label", "Decrease Indent"), 0, 0, GETTEXT_PACKAGE },
+@@ -392,10 +393,10 @@ static const GtkStockItem builtin_items [] =
+ /* Media label */
+ { GTK_STOCK_MEDIA_STOP, NC_("Stock label, media", "_Stop"), 0, 0, GETTEXT_PACKAGE "-media" },
+ { GTK_STOCK_NETWORK, NC_("Stock label", "_Network"), 0, 0, GETTEXT_PACKAGE },
+- { GTK_STOCK_NEW, NC_("Stock label", "_New"), GDK_CONTROL_MASK, 'n', GETTEXT_PACKAGE },
++ { GTK_STOCK_NEW, NC_("Stock label", "_New"), GTK_DEFAULT_ACCEL_MOD_MASK, 'n', GETTEXT_PACKAGE },
+ { GTK_STOCK_NO, NC_("Stock label", "_No"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_OK, NC_("Stock label", "_OK"), 0, 0, GETTEXT_PACKAGE },
+- { GTK_STOCK_OPEN, NC_("Stock label", "_Open"), GDK_CONTROL_MASK, 'o', GETTEXT_PACKAGE },
++ { GTK_STOCK_OPEN, NC_("Stock label", "_Open"), GTK_DEFAULT_ACCEL_MOD_MASK, 'o', GETTEXT_PACKAGE },
+ /* Page orientation */
+ { GTK_STOCK_ORIENTATION_LANDSCAPE, NC_("Stock label", "Landscape"), 0, 0, GETTEXT_PACKAGE },
+ /* Page orientation */
+@@ -405,17 +406,17 @@ static const GtkStockItem builtin_items [] =
+ /* Page orientation */
+ { GTK_STOCK_ORIENTATION_REVERSE_PORTRAIT, NC_("Stock label", "Reverse portrait"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_PAGE_SETUP, NC_("Stock label", "Page Set_up"), 0, 0, GETTEXT_PACKAGE },
+- { GTK_STOCK_PASTE, NC_("Stock label", "_Paste"), GDK_CONTROL_MASK, 'v', GETTEXT_PACKAGE },
++ { GTK_STOCK_PASTE, NC_("Stock label", "_Paste"), GTK_DEFAULT_ACCEL_MOD_MASK, 'v', GETTEXT_PACKAGE },
+ { GTK_STOCK_PREFERENCES, NC_("Stock label", "_Preferences"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_PRINT, NC_("Stock label", "_Print"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_PRINT_PREVIEW, NC_("Stock label", "Print Pre_view"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_PROPERTIES, NC_("Stock label", "_Properties"), 0, 0, GETTEXT_PACKAGE },
+- { GTK_STOCK_QUIT, NC_("Stock label", "_Quit"), GDK_CONTROL_MASK, 'q', GETTEXT_PACKAGE },
++ { GTK_STOCK_QUIT, NC_("Stock label", "_Quit"), GTK_DEFAULT_ACCEL_MOD_MASK, 'q', GETTEXT_PACKAGE },
+ { GTK_STOCK_REDO, NC_("Stock label", "_Redo"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_REFRESH, NC_("Stock label", "_Refresh"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_REMOVE, NC_("Stock label", "_Remove"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_REVERT_TO_SAVED, NC_("Stock label", "_Revert"), 0, 0, GETTEXT_PACKAGE },
+- { GTK_STOCK_SAVE, NC_("Stock label", "_Save"), GDK_CONTROL_MASK, 's', GETTEXT_PACKAGE },
++ { GTK_STOCK_SAVE, NC_("Stock label", "_Save"), GTK_DEFAULT_ACCEL_MOD_MASK, 's', GETTEXT_PACKAGE },
+ { GTK_STOCK_SAVE_AS, NC_("Stock label", "Save _As"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_SELECT_ALL, NC_("Stock label", "Select _All"), 0, 0, GETTEXT_PACKAGE },
+ { GTK_STOCK_SELECT_COLOR, NC_("Stock label", "_Color"), 0, 0, GETTEXT_PACKAGE },
+diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
+index 57316f6..2061b5b 100644
+--- a/gtk/gtktextview.c
++++ b/gtk/gtktextview.c
+@@ -4368,7 +4368,7 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
+ if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
+ &start, &end) &&
+ gtk_text_iter_in_range (&iter, &start, &end) &&
+- !(event->state & GDK_SHIFT_MASK))
++ !(event->state & GTK_EXTEND_SELECTION_MOD_MASK))
+ {
+ text_view->drag_start_x = event->x;
+ text_view->drag_start_y = event->y;
+@@ -6295,7 +6295,7 @@ gtk_text_view_start_selection_drag (GtkTextView *text_view,
+ orig_start = ins;
+ orig_end = bound;
+
+- if (button->state & GDK_SHIFT_MASK)
++ if (button->state & GTK_EXTEND_SELECTION_MOD_MASK)
+ {
+ /* Extend selection */
+ GtkTextIter old_ins, old_bound;
+diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h
+index acd6d69..82c52da 100644
+--- a/gtk/gtktreeprivate.h
++++ b/gtk/gtktreeprivate.h
+@@ -211,8 +211,8 @@ struct _GtkTreeViewPrivate
+ gint rubber_band_status;
+ gint rubber_band_x;
+ gint rubber_band_y;
+- gint rubber_band_shift;
+- gint rubber_band_ctrl;
++ gint rubber_band_extend;
++ gint rubber_band_modify;
+
+ GtkRBNode *rubber_band_start_node;
+ GtkRBTree *rubber_band_start_tree;
+@@ -276,8 +276,8 @@ struct _GtkTreeViewPrivate
+ /* for DnD */
+ guint empty_view_drop : 1;
+
+- guint ctrl_pressed : 1;
+- guint shift_pressed : 1;
++ guint modify_selection_pressed : 1;
++ guint extend_selection_pressed : 1;
+
+ guint init_hadjust_value : 1;
+
+diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
+index 47d0282..33dd907 100644
+--- a/gtk/gtktreeview.c
++++ b/gtk/gtktreeview.c
+@@ -2773,21 +2773,21 @@ gtk_tree_view_button_press (GtkWidget *widget,
+ */
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+- if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
+- tree_view->priv->ctrl_pressed = TRUE;
+- if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
+- tree_view->priv->shift_pressed = TRUE;
++ if ((event->state & GTK_MODIFY_SELECTION_MOD_MASK) == GTK_MODIFY_SELECTION_MOD_MASK)
++ tree_view->priv->modify_selection_pressed = TRUE;
++ if ((event->state & GTK_EXTEND_SELECTION_MOD_MASK) == GTK_EXTEND_SELECTION_MOD_MASK)
++ tree_view->priv->extend_selection_pressed = TRUE;
+
+ focus_cell = _gtk_tree_view_column_get_cell_at_pos (column, event->x - background_area.x);
+ if (focus_cell)
+ gtk_tree_view_column_focus_cell (column, focus_cell);
+
+- if (event->state & GDK_CONTROL_MASK)
++ if (event->state & GTK_MODIFY_SELECTION_MOD_MASK)
+ {
+ gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
+ gtk_tree_view_real_toggle_cursor_row (tree_view);
+ }
+- else if (event->state & GDK_SHIFT_MASK)
++ else if (event->state & GTK_EXTEND_SELECTION_MOD_MASK)
+ {
+ gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
+ gtk_tree_view_real_select_cursor_row (tree_view, FALSE);
+@@ -2797,8 +2797,8 @@ gtk_tree_view_button_press (GtkWidget *widget,
+ gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
+ }
+
+- tree_view->priv->ctrl_pressed = FALSE;
+- tree_view->priv->shift_pressed = FALSE;
++ tree_view->priv->modify_selection_pressed = FALSE;
++ tree_view->priv->extend_selection_pressed = FALSE;
+ }
+
+ /* the treeview may have been scrolled because of _set_cursor,
+@@ -2830,10 +2830,10 @@ gtk_tree_view_button_press (GtkWidget *widget,
+ tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
+ tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
+
+- if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
+- tree_view->priv->rubber_band_ctrl = TRUE;
+- if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
+- tree_view->priv->rubber_band_shift = TRUE;
++ if ((event->state & GTK_MODIFY_SELECTION_MOD_MASK) == GTK_MODIFY_SELECTION_MOD_MASK)
++ tree_view->priv->rubber_band_modify = TRUE;
++ if ((event->state & GTK_EXTEND_SELECTION_MOD_MASK) == GTK_EXTEND_SELECTION_MOD_MASK)
++ tree_view->priv->rubber_band_extend = TRUE;
+ }
+ }
+
+@@ -3791,8 +3791,8 @@ gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view)
+
+ /* Clear status variables */
+ tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
+- tree_view->priv->rubber_band_shift = 0;
+- tree_view->priv->rubber_band_ctrl = 0;
++ tree_view->priv->rubber_band_extend = FALSE;
++ tree_view->priv->rubber_band_modify = FALSE;
+
+ tree_view->priv->rubber_band_start_node = NULL;
+ tree_view->priv->rubber_band_start_tree = NULL;
+@@ -3837,9 +3837,9 @@ gtk_tree_view_update_rubber_band_selection_range (GtkTreeView *tree_view,
+
+ if (select)
+ {
+- if (tree_view->priv->rubber_band_shift)
+- GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
+- else if (tree_view->priv->rubber_band_ctrl)
++ if (tree_view->priv->rubber_band_extend)
++ GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
++ else if (tree_view->priv->rubber_band_modify)
+ {
+ /* Toggle the selection state */
+ if (GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
+@@ -3853,9 +3853,9 @@ gtk_tree_view_update_rubber_band_selection_range (GtkTreeView *tree_view,
+ else
+ {
+ /* Mirror the above */
+- if (tree_view->priv->rubber_band_shift)
++ if (tree_view->priv->rubber_band_extend)
+ GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
+- else if (tree_view->priv->rubber_band_ctrl)
++ else if (tree_view->priv->rubber_band_modify)
+ {
+ /* Toggle the selection state */
+ if (GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
+@@ -8203,10 +8203,10 @@ gtk_tree_view_real_move_cursor (GtkTreeView *tree_view,
+
+ if (gtk_get_current_event_state (&state))
+ {
+- if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
+- tree_view->priv->ctrl_pressed = TRUE;
+- if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
+- tree_view->priv->shift_pressed = TRUE;
++ if ((state & GTK_MODIFY_SELECTION_MOD_MASK) == GTK_MODIFY_SELECTION_MOD_MASK)
++ tree_view->priv->modify_selection_pressed = TRUE;
++ if ((state & GTK_EXTEND_SELECTION_MOD_MASK) == GTK_EXTEND_SELECTION_MOD_MASK)
++ tree_view->priv->extend_selection_pressed = TRUE;
+ }
+ /* else we assume not pressed */
+
+@@ -8230,8 +8230,8 @@ gtk_tree_view_real_move_cursor (GtkTreeView *tree_view,
+ g_assert_not_reached ();
+ }
+
+- tree_view->priv->ctrl_pressed = FALSE;
+- tree_view->priv->shift_pressed = FALSE;
++ tree_view->priv->modify_selection_pressed = FALSE;
++ tree_view->priv->extend_selection_pressed = FALSE;
+
+ return TRUE;
+ }
+@@ -9690,7 +9690,7 @@ gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
+
+ if (selection_count == 0
+ && tree_view->priv->selection->type != GTK_SELECTION_NONE
+- && !tree_view->priv->ctrl_pressed
++ && !tree_view->priv->modify_selection_pressed
+ && selectable)
+ {
+ /* Don't move the cursor, but just select the current node */
+@@ -9759,7 +9759,7 @@ gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
+ {
+ gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
+
+- if (!tree_view->priv->shift_pressed)
++ if (!tree_view->priv->extend_selection_pressed)
+ {
+ if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
+ count < 0 ?
+@@ -10111,7 +10111,7 @@ gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
+ return FALSE;
+ }
+
+- if (!tree_view->priv->shift_pressed && start_editing &&
++ if (!tree_view->priv->extend_selection_pressed && start_editing &&
+ tree_view->priv->focus_column)
+ {
+ if (gtk_tree_view_start_editing (tree_view, cursor_path))
+@@ -10121,9 +10121,9 @@ gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
+ }
+ }
+
+- if (tree_view->priv->ctrl_pressed)
++ if (tree_view->priv->modify_selection_pressed)
+ mode |= GTK_TREE_SELECT_MODE_TOGGLE;
+- if (tree_view->priv->shift_pressed)
++ if (tree_view->priv->extend_selection_pressed)
+ mode |= GTK_TREE_SELECT_MODE_EXTEND;
+
+ _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
+@@ -10147,7 +10147,7 @@ gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
+ gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+ _gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
+
+- if (!tree_view->priv->shift_pressed)
++ if (!tree_view->priv->extend_selection_pressed)
+ gtk_tree_view_row_activated (tree_view, cursor_path,
+ tree_view->priv->focus_column);
+
+@@ -10285,8 +10285,8 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view)
+
+ if (gtk_get_current_event_state (&state))
+ {
+- if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
+- tree_view->priv->ctrl_pressed = TRUE;
++ if ((state & GTK_MODIFY_SELECTION_MOD_MASK) == GTK_MODIFY_SELECTION_MOD_MASK)
++ tree_view->priv->modify_selection_pressed = TRUE;
+ }
+
+ gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
+@@ -10296,7 +10296,7 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view)
+ gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
+ gtk_tree_path_free (cursor_path);
+
+- tree_view->priv->ctrl_pressed = FALSE;
++ tree_view->priv->modify_selection_pressed = FALSE;
+
+ return TRUE;
+ }
+@@ -12579,13 +12579,13 @@ gtk_tree_view_real_set_cursor (GtkTreeView *tree_view,
+ GtkRBTree *new_tree = NULL;
+ GtkRBNode *new_node = NULL;
+
+- if (clear_and_select && !tree_view->priv->ctrl_pressed)
++ if (clear_and_select && !tree_view->priv->modify_selection_pressed)
+ {
+ GtkTreeSelectMode mode = 0;
+
+- if (tree_view->priv->ctrl_pressed)
++ if (tree_view->priv->modify_selection_pressed)
+ mode |= GTK_TREE_SELECT_MODE_TOGGLE;
+- if (tree_view->priv->shift_pressed)
++ if (tree_view->priv->extend_selection_pressed)
+ mode |= GTK_TREE_SELECT_MODE_EXTEND;
+
+ _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
+
+
diff --git a/patches/0013-Bug-659406-Abstract-what-triggers-a-context-menu.patch b/patches/0013-Bug-659406-Abstract-what-triggers-a-context-menu.patch
new file mode 100644
index 0000000..1ae7095
--- /dev/null
+++ b/patches/0013-Bug-659406-Abstract-what-triggers-a-context-menu.patch
@@ -0,0 +1,329 @@
+From f2a96fb87408010775730a5b89c26ea5120cf2f2 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch lanedo com>
+Date: Mon, 19 Sep 2011 00:32:52 +0200
+Subject: [PATCH 13/15] Bug 659406 - Abstract what triggers a context menu
+
+Add _gtk_button_event_triggers_context_menu() and use it instead
+of checking for event->button == 3, so context menus are invoked
+correctly on the Mac.
+---
+ gtk/gtkcolorsel.c | 5 ++---
+ gtk/gtkentry.c | 18 +++++++++---------
+ gtk/gtkfilechooserdefault.c | 4 ++--
+ gtk/gtklabel.c | 23 ++++++++++++-----------
+ gtk/gtklinkbutton.c | 4 ++--
+ gtk/gtkmain.c | 21 +++++++++++++++++++++
+ gtk/gtkmountoperation.c | 3 +--
+ gtk/gtknotebook.c | 2 +-
+ gtk/gtkprivate.h | 2 ++
+ gtk/gtkrecentchooserdefault.c | 4 ++--
+ gtk/gtkstatusicon.c | 8 ++++----
+ gtk/gtktextview.c | 12 ++++++------
+ gtk/gtktoolbar.c | 2 +-
+ 13 files changed, 65 insertions(+), 43 deletions(-)
+
+diff --git a/gtk/gtkcolorsel.c b/gtk/gtkcolorsel.c
+index 6c65d9e..4981e74 100644
+--- a/gtk/gtkcolorsel.c
++++ b/gtk/gtkcolorsel.c
+@@ -1436,9 +1436,8 @@ palette_press (GtkWidget *drawing_area,
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
+
+ gtk_widget_grab_focus (drawing_area);
+-
+- if (event->button == 3 &&
+- event->type == GDK_BUTTON_PRESS)
++
++ if (_gtk_button_event_triggers_context_menu (event))
+ {
+ do_popup (colorsel, drawing_area, event->time);
+ return TRUE;
+diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
+index 8e2c2b2..2c9c5a4 100644
+--- a/gtk/gtkentry.c
++++ b/gtk/gtkentry.c
+@@ -3688,8 +3688,15 @@ gtk_entry_button_press (GtkWidget *widget,
+ }
+
+ tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
+-
+- if (event->button == 1)
++
++ if (_gtk_button_event_triggers_context_menu (event))
++ {
++ gtk_entry_do_popup (entry, event);
++ entry->button = 0; /* Don't wait for release, since the menu will gtk_grab_add */
++
++ return TRUE;
++ }
++ else if (event->button == 1)
+ {
+ gboolean have_selection = gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end);
+
+@@ -3810,13 +3817,6 @@ gtk_entry_button_press (GtkWidget *widget,
+ gtk_widget_error_bell (widget);
+ }
+ }
+- else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+- {
+- gtk_entry_do_popup (entry, event);
+- entry->button = 0; /* Don't wait for release, since the menu will gtk_grab_add */
+-
+- return TRUE;
+- }
+
+ return FALSE;
+ }
+diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
+index 44c2d17..256cc20 100644
+--- a/gtk/gtkfilechooserdefault.c
++++ b/gtk/gtkfilechooserdefault.c
+@@ -3419,7 +3419,7 @@ shortcuts_button_press_event_cb (GtkWidget *widget,
+ if (in_press)
+ return FALSE;
+
+- if (event->button != 3)
++ if (!_gtk_button_event_triggers_context_menu (event))
+ return FALSE;
+
+ in_press = TRUE;
+@@ -4097,7 +4097,7 @@ list_button_press_event_cb (GtkWidget *widget,
+ if (in_press)
+ return FALSE;
+
+- if (event->button != 3)
++ if (!_gtk_button_event_triggers_context_menu (event))
+ return FALSE;
+
+ in_press = TRUE;
+diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
+index 01225b1..c539537 100644
+--- a/gtk/gtklabel.c
++++ b/gtk/gtklabel.c
+@@ -4189,16 +4189,16 @@ gtk_label_button_press (GtkWidget *widget,
+
+ if (info->active_link)
+ {
+- if (event->button == 1)
++ if (_gtk_button_event_triggers_context_menu (event))
+ {
+ info->link_clicked = 1;
+- gtk_widget_queue_draw (widget);
++ gtk_label_do_popup (label, event);
++ return TRUE;
+ }
+- else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
++ else if (event->button == 1)
+ {
+ info->link_clicked = 1;
+- gtk_label_do_popup (label, event);
+- return TRUE;
++ gtk_widget_queue_draw (widget);
+ }
+ }
+
+@@ -4208,7 +4208,13 @@ gtk_label_button_press (GtkWidget *widget,
+ info->in_drag = FALSE;
+ info->select_words = FALSE;
+
+- if (event->button == 1)
++ if (_gtk_button_event_triggers_context_menu (event))
++ {
++ gtk_label_do_popup (label, event);
++
++ return TRUE;
++ }
++ else if (event->button == 1)
+ {
+ if (!gtk_widget_has_focus (widget))
+ {
+@@ -4271,12 +4277,7 @@ gtk_label_button_press (GtkWidget *widget,
+
+ return TRUE;
+ }
+- else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+- {
+- gtk_label_do_popup (label, event);
+
+- return TRUE;
+- }
+ return FALSE;
+ }
+
+diff --git a/gtk/gtklinkbutton.c b/gtk/gtklinkbutton.c
+index a3f246f..bae8ec3 100644
+--- a/gtk/gtklinkbutton.c
++++ b/gtk/gtklinkbutton.c
+@@ -36,8 +36,8 @@
+ #include "gtkstock.h"
+ #include "gtkshow.h"
+ #include "gtktooltip.h"
+-
+ #include "gtklinkbutton.h"
++#include "gtkprivate.h"
+
+ #include "gtkintl.h"
+ #include "gtkalias.h"
+@@ -455,7 +455,7 @@ gtk_link_button_button_press (GtkWidget *widget,
+ if (!gtk_widget_has_focus (widget))
+ gtk_widget_grab_focus (widget);
+
+- if ((event->button == 3) && (event->type == GDK_BUTTON_PRESS))
++ if (_gtk_button_event_triggers_context_menu (event))
+ {
+ gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), event);
+
+diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
+index d081f70..bc19aed 100644
+--- a/gtk/gtkmain.c
++++ b/gtk/gtkmain.c
+@@ -2632,5 +2632,26 @@ _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
+ return continue_emission;
+ }
+
++gboolean
++_gtk_button_event_triggers_context_menu (GdkEventButton *event)
++{
++ if (event->type == GDK_BUTTON_PRESS)
++ {
++ if (event->button == 3 &&
++ ! (event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK)))
++ return TRUE;
++
++#ifdef GDK_WINDOWING_QUARTZ
++ if (event->button == 1 &&
++ ! (event->state & (GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) &&
++ (event->state & GDK_CONTROL_MASK))
++ return TRUE;
++#endif
++ }
++
++ return FALSE;
++}
++
++
+ #define __GTK_MAIN_C__
+ #include "gtkaliasdef.c"
+diff --git a/gtk/gtkmountoperation.c b/gtk/gtkmountoperation.c
+index 0747ed5..6ddaabf 100644
+--- a/gtk/gtkmountoperation.c
++++ b/gtk/gtkmountoperation.c
+@@ -1171,8 +1171,7 @@ on_button_press_event_for_process_tree_view (GtkWidget *widget,
+
+ ret = FALSE;
+
+- /* Ignore double-clicks and triple-clicks */
+- if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
++ if (_gtk_button_event_triggers_context_menu (event))
+ {
+ ret = do_popup_menu_for_process_tree_view (widget, event, op);
+ }
+diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c
+index 254f6b8..a8c8057 100644
+--- a/gtk/gtknotebook.c
++++ b/gtk/gtknotebook.c
+@@ -2681,7 +2681,7 @@ gtk_notebook_button_press (GtkWidget *widget,
+ if (arrow)
+ return gtk_notebook_arrow_button_press (notebook, arrow, event->button);
+
+- if (event->button == 3 && notebook->menu)
++ if (notebook->menu && _gtk_button_event_triggers_context_menu (event))
+ {
+ gtk_menu_popup (GTK_MENU (notebook->menu), NULL, NULL,
+ NULL, NULL, 3, event->time);
+diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
+index 00440cb..27aaf1f 100644
+--- a/gtk/gtkprivate.h
++++ b/gtk/gtkprivate.h
+@@ -130,6 +130,8 @@ gboolean _gtk_fnmatch (const char *pattern,
+ #define GTK_MODIFY_SELECTION_MOD_MASK GDK_MOD2_MASK
+ #endif
+
++gboolean _gtk_button_event_triggers_context_menu (GdkEventButton *event);
++
+ G_END_DECLS
+
+ #endif /* __GTK_PRIVATE_H__ */
+diff --git a/gtk/gtkrecentchooserdefault.c b/gtk/gtkrecentchooserdefault.c
+index 676d599..1ab48e2 100644
+--- a/gtk/gtkrecentchooserdefault.c
++++ b/gtk/gtkrecentchooserdefault.c
+@@ -1893,8 +1893,8 @@ recent_view_button_press_cb (GtkWidget *widget,
+ gpointer user_data)
+ {
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+-
+- if (event->button == 3)
++
++ if (_gtk_button_event_triggers_context_menu (event))
+ {
+ GtkTreePath *path;
+ gboolean res;
+diff --git a/gtk/gtkstatusicon.c b/gtk/gtkstatusicon.c
+index bd43da1..ab8fe79 100644
+--- a/gtk/gtkstatusicon.c
++++ b/gtk/gtkstatusicon.c
+@@ -1750,14 +1750,14 @@ gtk_status_icon_button_press (GtkStatusIcon *status_icon,
+ if (handled)
+ return TRUE;
+
+- if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
++ if (_gtk_button_event_triggers_context_menu (event))
+ {
+- emit_activate_signal (status_icon);
++ emit_popup_menu_signal (status_icon, event->button, event->time);
+ return TRUE;
+ }
+- else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
++ else if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
+ {
+- emit_popup_menu_signal (status_icon, event->button, event->time);
++ emit_activate_signal (status_icon);
+ return TRUE;
+ }
+
+diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
+index 2061b5b..9ddddec 100644
+--- a/gtk/gtktextview.c
++++ b/gtk/gtktextview.c
+@@ -4352,7 +4352,12 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
+ {
+ gtk_text_view_reset_im_context (text_view);
+
+- if (event->button == 1)
++ if (_gtk_button_event_triggers_context_menu (event))
++ {
++ gtk_text_view_do_popup (text_view, event);
++ return TRUE;
++ }
++ else if (event->button == 1)
+ {
+ /* If we're in the selection, start a drag copy/move of the
+ * selection; otherwise, start creating a new selection.
+@@ -4402,11 +4407,6 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
+ text_view->editable);
+ return TRUE;
+ }
+- else if (event->button == 3)
+- {
+- gtk_text_view_do_popup (text_view, event);
+- return TRUE;
+- }
+ }
+ else if ((event->type == GDK_2BUTTON_PRESS ||
+ event->type == GDK_3BUTTON_PRESS) &&
+diff --git a/gtk/gtktoolbar.c b/gtk/gtktoolbar.c
+index 17e2f8f..1388a65 100644
+--- a/gtk/gtktoolbar.c
++++ b/gtk/gtktoolbar.c
+@@ -2699,7 +2699,7 @@ static gboolean
+ gtk_toolbar_button_press (GtkWidget *toolbar,
+ GdkEventButton *event)
+ {
+- if (event->button == 3)
++ if (_gtk_button_event_triggers_context_menu (event))
+ {
+ gboolean return_value;
+
+
+
diff --git a/patches/0014-Bug-659907-gdk_quartz_draw_opaque_stippled_pattern-c.patch b/patches/0014-Bug-659907-gdk_quartz_draw_opaque_stippled_pattern-c.patch
new file mode 100644
index 0000000..2cb9373
--- /dev/null
+++ b/patches/0014-Bug-659907-gdk_quartz_draw_opaque_stippled_pattern-c.patch
@@ -0,0 +1,25 @@
+From 86bdbe35b30cd1f10624628800db66e5d8e6673e Mon Sep 17 00:00:00 2001
+From: Steffen Gutmann <muibase yahoo com>
+Date: Fri, 23 Sep 2011 09:01:44 +0200
+Subject: [PATCH 14/15] Bug 659907 - gdk_quartz_draw_opaque_stippled_pattern
+ crashes
+
+---
+ gdk/quartz/gdkgc-quartz.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/gdk/quartz/gdkgc-quartz.c b/gdk/quartz/gdkgc-quartz.c
+index d922154..cfdde77 100644
+--- a/gdk/quartz/gdkgc-quartz.c
++++ b/gdk/quartz/gdkgc-quartz.c
+@@ -426,7 +426,7 @@ gdk_quartz_draw_opaque_stippled_pattern (void *info,
+ CGContextFillRect (context, rect);
+
+ CGContextClipToMask (context, rect, pattern_image);
+- color = _gdk_quartz_colormap_get_cgcolor_from_pixel (info,
++ color = _gdk_quartz_colormap_get_cgcolor_from_pixel (pinfo->drawable,
+ _gdk_gc_get_fg_pixel (gc));
+ CGContextSetFillColorWithColor (context, color);
+ CGColorRelease (color);
+
+
diff --git a/patches/0015-Bug-653450-gtkfilechooser-crashes-when-adding-favori.patch b/patches/0015-Bug-653450-gtkfilechooser-crashes-when-adding-favori.patch
new file mode 100644
index 0000000..b070249
--- /dev/null
+++ b/patches/0015-Bug-653450-gtkfilechooser-crashes-when-adding-favori.patch
@@ -0,0 +1,59 @@
+From c797c34e600f21b37ddbf4d2cd6e7a0f9312ced4 Mon Sep 17 00:00:00 2001
+From: John Ralls <jralls ceridwen us>
+Date: Sun, 3 Jul 2011 16:40:03 -0700
+Subject: [PATCH 15/15] Bug 653450 - gtkfilechooser crashes when adding
+ favorite
+
+Ensure that display is set during drag-and-drop, and that string lists'
+memory is zeroed after allocation to prevent g_strfreev() from
+over-running.
+---
+ gdk/quartz/gdkselection-quartz.c | 4 +---
+ gtk/gtkdnd-quartz.c | 1 +
+ gtk/gtkquartz.c | 3 ++-
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/gdk/quartz/gdkselection-quartz.c b/gdk/quartz/gdkselection-quartz.c
+index a51f567..b216871 100644
+--- a/gdk/quartz/gdkselection-quartz.c
++++ b/gdk/quartz/gdkselection-quartz.c
+@@ -189,10 +189,8 @@ make_list (const gchar *text,
+ }
+
+ if (list)
+- *list = g_new (gchar *, n_strings + 1);
++ *list = g_new0 (gchar *, n_strings + 1);
+
+- (*list)[n_strings] = NULL;
+-
+ i = n_strings;
+ tmp_list = strings;
+ while (tmp_list)
+diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c
+index 21ce11a..670b185 100644
+--- a/gtk/gtkdnd-quartz.c
++++ b/gtk/gtkdnd-quartz.c
+@@ -155,6 +155,7 @@ struct _GtkDragFindData
+ selection_data.data = NULL;
+ selection_data.length = -1;
+ selection_data.target = _gtk_quartz_pasteboard_type_to_atom (type);
++ selection_data.display = gdk_display_get_default ();
+
+ if (gtk_target_list_find (info->target_list,
+ selection_data.target,
+diff --git a/gtk/gtkquartz.c b/gtk/gtkquartz.c
+index 8ffeb0b..a675d8a 100644
+--- a/gtk/gtkquartz.c
++++ b/gtk/gtkquartz.c
+@@ -160,7 +160,8 @@ _gtk_quartz_get_selection_data_from_pasteboard (NSPasteboard *pasteboard,
+ selection_data = g_slice_new0 (GtkSelectionData);
+ selection_data->selection = selection;
+ selection_data->target = target;
+-
++ if (!selection_data->display)
++ selection_data->display = gdk_display_get_default ();
+ if (target == gdk_atom_intern_static_string ("UTF8_STRING"))
+ {
+ NSString *s = [pasteboard stringForType:NSStringPboardType];
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]